Commit d4d6cc2a authored by Rico Tzschichholz's avatar Rico Tzschichholz

Allow to pass compatible delegates to signal.connect()

https://bugzilla.gnome.org/show_bug.cgi?id=787521
parent 801d6301
......@@ -603,7 +603,15 @@ public class Vala.GSignalModule : GObjectModule {
CCodeExpression? connect_signal (Signal sig, Expression signal_access, Expression handler, bool disconnect, bool after, CodeNode expr) {
string connect_func;
var m = (Method) handler.symbol_reference;
DelegateType? dt = null;
var p = handler.symbol_reference as Parameter;
if (p != null) {
dt = p.variable_type as DelegateType;
if (dt != null && !context.experimental) {
Report.warning (dt.source_reference, "Connecting delegates to signals is experimental");
}
}
var m = handler.symbol_reference as Method;
if (!disconnect) {
// connect
......@@ -613,9 +621,9 @@ public class Vala.GSignalModule : GObjectModule {
else
connect_func = get_dynamic_signal_connect_after_wrapper_name ((DynamicSignal) sig);
} else {
if (m.closure) {
if ((m != null && m.closure) || (dt != null && dt.value_owned)) {
connect_func = "g_signal_connect_data";
} else if (in_gobject_instance (m)) {
} else if (m != null && in_gobject_instance (m)) {
connect_func = "g_signal_connect_object";
} else if (!after) {
connect_func = "g_signal_connect";
......@@ -714,7 +722,7 @@ public class Vala.GSignalModule : GObjectModule {
// third resp. sixth argument: handler
ccall.add_argument (new CCodeCastExpression (get_cvalue (handler), "GCallback"));
if (m.closure) {
if (m != null && m.closure) {
// g_signal_connect_data
// fourth argument: user_data
......@@ -729,7 +737,7 @@ public class Vala.GSignalModule : GObjectModule {
ccall.add_argument (new CCodeConstant ("0"));
else
ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
} else if (m.binding == MemberBinding.INSTANCE) {
} else if (m != null && m.binding == MemberBinding.INSTANCE) {
// g_signal_connect_object or g_signal_handlers_disconnect_matched
// or dynamic_signal_connect or dynamic_signal_disconnect
......@@ -754,6 +762,20 @@ public class Vala.GSignalModule : GObjectModule {
else
ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
}
} else if (dt != null && dt.delegate_symbol.has_target) {
// fourth argument: user_data
CCodeExpression handler_destroy_notify;
ccall.add_argument (get_delegate_target_cexpression (handler, out handler_destroy_notify));
if (!disconnect && dt.value_owned) {
// fifth argument: destroy_notify
//FIXME handler_destroy_notify is NULL
ccall.add_argument (new CCodeCastExpression (handler_destroy_notify, "GClosureNotify"));
// sixth argument: connect_flags
if (!after)
ccall.add_argument (new CCodeConstant ("0"));
else
ccall.add_argument (new CCodeConstant ("G_CONNECT_AFTER"));
}
} else {
// g_signal_connect or g_signal_connect_after or g_signal_handlers_disconnect_matched
// or dynamic_signal_connect or dynamic_signal_disconnect
......
......@@ -180,6 +180,7 @@ TESTS = \
objects/properties.vala \
objects/regex.vala \
objects/signals.vala \
objects/signals-delegate.vala \
objects/test-025.vala \
objects/test-026.vala \
objects/test-029.vala \
......
public delegate string FooFunc (Foo foo, string s);
public class Foo : Object {
public signal string test (string s);
public void add (FooFunc func) {
test.connect (func);
}
public void add_owned (owned FooFunc func) {
test.connect (func);
}
public void add_remove (FooFunc func) {
test.connect (func);
test.disconnect (func);
}
public void add_remove_owned (owned FooFunc func) {
test.connect (func);
test.disconnect (func);
}
public void invoke_test () {
assert (test ("bar") == "foo");
}
public void invoke_test_empty () {
assert (test ("bar") == null);
}
}
public class Bar : Object {
public signal string test (string s);
int i;
public Bar (Foo foo) {
i = 42;
foo.add (instance_callback);
}
public Bar.owned (Foo foo) {
i = 42;
foo.add_owned (instance_callback);
}
public Bar.remove (Foo foo) {
i = 42;
foo.add_remove (instance_callback);
}
public Bar.remove_owned (Foo foo) {
i = 42;
foo.add_remove_owned (instance_callback);
}
string instance_callback (Foo foo, string s) {
assert (foo is Foo);
assert (this is Bar);
assert (s == "bar");
assert (i == 42);
return "foo";
}
}
string callback_static (Foo foo, string s) {
assert (foo is Foo);
assert (s == "bar");
return "foo";
}
void main () {
Foo foo;
Bar bar;
foo = new Foo ();
foo.add ((FooFunc) callback_static);
foo.invoke_test ();
foo = new Foo ();
foo.add_owned ((FooFunc) callback_static);
foo.invoke_test ();
foo = new Foo ();
foo.add_remove ((FooFunc) callback_static);
foo.invoke_test_empty ();
foo = new Foo ();
foo.add_remove_owned ((FooFunc) callback_static);
foo.invoke_test_empty ();
foo = new Foo ();
bar = new Bar (foo);
foo.invoke_test ();
foo = new Foo ();
bar = new Bar.owned (foo);
foo.invoke_test ();
foo = new Foo ();
bar = new Bar.remove (foo);
foo.invoke_test_empty ();
foo = new Foo ();
bar = new Bar.remove_owned (foo);
foo.invoke_test_empty ();
}
......@@ -285,10 +285,6 @@ public abstract class Vala.DataType : CodeNode {
}
}
if (target_type is DelegateType && this is DelegateType) {
return ((DelegateType) target_type).delegate_symbol == ((DelegateType) this).delegate_symbol;
}
if (target_type is PointerType) {
/* any reference or array type or pointer type can be cast to a generic pointer */
if (type_parameter != null ||
......
......@@ -140,6 +140,72 @@ public class Vala.DelegateType : CallableType {
return true;
}
public override bool compatible (DataType target_type) {
var dt_target = target_type as DelegateType;
if (dt_target == null) {
return false;
}
// trivial case
if (delegate_symbol == dt_target.delegate_symbol) {
return true;
}
// target-delegate is allowed to ensure stricter return type (stronger postcondition)
if (!get_return_type ().stricter (dt_target.get_return_type ().get_actual_type (dt_target, null, this))) {
return false;
}
var parameters = get_parameters ();
Iterator<Parameter> params_it = parameters.iterator ();
if (dt_target.delegate_symbol.parent_symbol is Signal && dt_target.delegate_symbol.sender_type != null && parameters.size == dt_target.get_parameters ().size + 1) {
// target-delegate has sender parameter
params_it.next ();
// target-delegate is allowed to accept arguments of looser types (weaker precondition)
var p = params_it.get ();
if (!dt_target.delegate_symbol.sender_type.stricter (p.variable_type)) {
return false;
}
}
foreach (Parameter param in dt_target.get_parameters ()) {
/* target-delegate is allowed to accept less arguments */
if (!params_it.next ()) {
break;
}
// target-delegate is allowed to accept arguments of looser types (weaker precondition)
var p = params_it.get ();
if (!param.variable_type.get_actual_type (this, null, this).stricter (p.variable_type)) {
return false;
}
}
/* target-delegate may not expect more arguments */
if (params_it.next ()) {
return false;
}
// target-delegate may throw less but not more errors than the delegate
foreach (DataType error_type in get_error_types ()) {
bool match = false;
foreach (DataType delegate_error_type in dt_target.get_error_types ()) {
if (error_type.compatible (delegate_error_type)) {
match = true;
break;
}
}
if (!match) {
return false;
}
}
return true;
}
public override bool is_disposable () {
return delegate_symbol.has_target && value_owned && !is_called_once;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment