[vala/staging: 6/6] Allow to pass compatible delegates to signal.connect()
- From: Rico Tzschichholz <ricotz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/staging: 6/6] Allow to pass compatible delegates to signal.connect()
- Date: Sun, 19 Nov 2017 13:17:23 +0000 (UTC)
commit d4d6cc2abbfff21dd8978c50c2a98ce7cf11515e
Author: Rico Tzschichholz <ricotz ubuntu com>
Date: Sun Sep 10 15:49:56 2017 +0200
Allow to pass compatible delegates to signal.connect()
https://bugzilla.gnome.org/show_bug.cgi?id=787521
codegen/valagsignalmodule.vala | 32 +++++++++--
tests/Makefile.am | 1 +
tests/objects/signals-delegate.vala | 108 +++++++++++++++++++++++++++++++++++
vala/valadatatype.vala | 4 -
vala/valadelegatetype.vala | 66 +++++++++++++++++++++
5 files changed, 202 insertions(+), 9 deletions(-)
---
diff --git a/codegen/valagsignalmodule.vala b/codegen/valagsignalmodule.vala
index 306c374..5e34245 100644
--- a/codegen/valagsignalmodule.vala
+++ b/codegen/valagsignalmodule.vala
@@ -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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6f18747..d8b7c8f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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 \
diff --git a/tests/objects/signals-delegate.vala b/tests/objects/signals-delegate.vala
new file mode 100644
index 0000000..1e94603
--- /dev/null
+++ b/tests/objects/signals-delegate.vala
@@ -0,0 +1,108 @@
+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 ();
+}
diff --git a/vala/valadatatype.vala b/vala/valadatatype.vala
index 83b918d..d340554 100644
--- a/vala/valadatatype.vala
+++ b/vala/valadatatype.vala
@@ -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 ||
diff --git a/vala/valadelegatetype.vala b/vala/valadelegatetype.vala
index 2238b98..8e62d75 100644
--- a/vala/valadelegatetype.vala
+++ b/vala/valadelegatetype.vala
@@ -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;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]