diff options
author | Lorenz Wildberg <lorenz@wild-fisch.de> | 2021-12-04 19:05:42 +0000 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2022-01-04 14:51:37 +0100 |
commit | d74400bfaae61a1e2c246382e627b0a9adf8c304 (patch) | |
tree | ca0ae596870df270819adcfbcac35b9d83585280 | |
parent | de1682099945edfd8d23958cf45186aecbd0015e (diff) | |
download | vala-d74400bfaae61a1e2c246382e627b0a9adf8c304.tar.gz |
Support "emit" for explicit signal emission
If "foo" is a signal then "foo.emit (...);" is allowed now additionally to
"foo (...);". This is required to emit dynamic signals.
-rw-r--r-- | codegen/valaccodemethodcallmodule.vala | 4 | ||||
-rw-r--r-- | codegen/valagsignalmodule.vala | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/objects/signals-dynamic-emit.c-expected | 195 | ||||
-rw-r--r-- | tests/objects/signals-dynamic-emit.vala | 20 | ||||
-rw-r--r-- | tests/objects/signals-emit.c-expected | 206 | ||||
-rw-r--r-- | tests/objects/signals-emit.vala | 28 | ||||
-rw-r--r-- | vala/valamemberaccess.vala | 13 | ||||
-rw-r--r-- | vala/valasignaltype.vala | 13 |
9 files changed, 481 insertions, 2 deletions
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala index 894660c88..d7ac3f4b0 100644 --- a/codegen/valaccodemethodcallmodule.vala +++ b/codegen/valaccodemethodcallmodule.vala @@ -715,7 +715,9 @@ public class Vala.CCodeMethodCallModule : CCodeAssignmentModule { if (ellipsis) { /* ensure variable argument list ends with NULL * except when using printf-style arguments */ - if (m == null) { + if (itype is SignalType) { + // g_signal_emit*() does not require more + } else if (m == null) { in_arg_map.set (get_param_pos (-1, true), new CCodeConstant ("NULL")); } else if (!m.printf_format && !m.scanf_format && get_ccode_sentinel (m) != "" && !expr.is_constructv_chainup) { in_arg_map.set (get_param_pos (-1, true), new CCodeConstant (get_ccode_sentinel (m))); diff --git a/codegen/valagsignalmodule.vala b/codegen/valagsignalmodule.vala index cfd4a5da8..4186859cf 100644 --- a/codegen/valagsignalmodule.vala +++ b/codegen/valagsignalmodule.vala @@ -537,7 +537,7 @@ public class Vala.GSignalModule : GObjectModule { return new CCodeMemberAccess.pointer (vcast, m.name); } - if (!sig.external_package && expr.source_reference.file == sig.source_reference.file) { + if (!sig.external_package && expr.source_reference.file == sig.source_reference.file && !(sig is DynamicSignal)) { var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_signal_emit")); ccall.add_argument (pub_inst); ccall.add_argument (get_signal_id_cexpression (sig)); diff --git a/tests/Makefile.am b/tests/Makefile.am index 2b4cb2ba4..c34a84fa1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -556,9 +556,11 @@ TESTS = \ objects/signals-enum-marshal.vala \ objects/signals-delegate.vala \ objects/signals-delegate-parameter.vala \ + objects/signals-dynamic-emit.vala \ objects/signals-dynamic-invalid-disconnect.test \ objects/signals-dymanic-invalid-handler.test \ objects/signals-dynamic-lambda-handler.test \ + objects/signals-emit.vala \ objects/signals-error-marshal.vala \ objects/signals-fundamental-return.vala \ objects/signals-generic-return.vala \ diff --git a/tests/objects/signals-dynamic-emit.c-expected b/tests/objects/signals-dynamic-emit.c-expected new file mode 100644 index 000000000..5b04c9bbb --- /dev/null +++ b/tests/objects/signals-dynamic-emit.c-expected @@ -0,0 +1,195 @@ +/* objects_signals_dynamic_emit.c generated by valac, the Vala compiler + * generated from objects_signals_dynamic_emit.vala, do not modify */ + +#include <glib-object.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +#define TYPE_FOO (foo_get_type ()) +#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOO, Foo)) +#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOO, FooClass)) +#define IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOO)) +#define IS_FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOO)) +#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOO, FooClass)) + +typedef struct _Foo Foo; +typedef struct _FooClass FooClass; +typedef struct _FooPrivate FooPrivate; +enum { + FOO_0_PROPERTY, + FOO_NUM_PROPERTIES +}; +static GParamSpec* foo_properties[FOO_NUM_PROPERTIES]; +enum { + FOO_SIG_SIGNAL, + FOO_NUM_SIGNALS +}; +static guint foo_signals[FOO_NUM_SIGNALS] = {0}; +#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) +#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } +#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } +#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + +struct _Foo { + GObject parent_instance; + FooPrivate * priv; +}; + +struct _FooClass { + GObjectClass parent_class; +}; + +static gpointer foo_parent_class = NULL; +VALA_EXTERN gboolean success; +gboolean success = FALSE; + +VALA_EXTERN GType foo_get_type (void) G_GNUC_CONST ; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Foo, g_object_unref) +VALA_EXTERN Foo* foo_new (void); +VALA_EXTERN Foo* foo_construct (GType object_type); +static void g_cclosure_user_marshal_VOID__STRING_INT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, + gpointer marshal_data); +static GType foo_get_type_once (void); +VALA_EXTERN void sig_cb (GObject* o, + const gchar* s, + gint i); +static void _vala_main (void); +static void _sig_cb_dynamic_sig0_ (GObject* _sender, + const gchar* s, + gint i, + gpointer self); + +Foo* +foo_construct (GType object_type) +{ + Foo * self = NULL; + self = (Foo*) g_object_new (object_type, NULL); + return self; +} + +Foo* +foo_new (void) +{ + return foo_construct (TYPE_FOO); +} + +static void +g_cclosure_user_marshal_VOID__STRING_INT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1, const char* arg_1, gint arg_2, gpointer data2); + register GMarshalFunc_VOID__STRING_INT callback; + register GCClosure * cc; + register gpointer data1; + register gpointer data2; + cc = (GCClosure *) closure; + g_return_if_fail (n_param_values == 3); + if (G_CCLOSURE_SWAP_DATA (closure)) { + data1 = closure->data; + data2 = param_values->data[0].v_pointer; + } else { + data1 = param_values->data[0].v_pointer; + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback); + callback (data1, g_value_get_string (param_values + 1), g_value_get_int (param_values + 2), data2); +} + +static void +foo_class_init (FooClass * klass, + gpointer klass_data) +{ + foo_parent_class = g_type_class_peek_parent (klass); + foo_signals[FOO_SIG_SIGNAL] = g_signal_new ("sig", TYPE_FOO, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_user_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); +} + +static void +foo_instance_init (Foo * self, + gpointer klass) +{ +} + +static GType +foo_get_type_once (void) +{ + static const GTypeInfo g_define_type_info = { sizeof (FooClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) foo_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Foo), 0, (GInstanceInitFunc) foo_instance_init, NULL }; + GType foo_type_id; + foo_type_id = g_type_register_static (G_TYPE_OBJECT, "Foo", &g_define_type_info, 0); + return foo_type_id; +} + +GType +foo_get_type (void) +{ + static volatile gsize foo_type_id__once = 0; + if (g_once_init_enter (&foo_type_id__once)) { + GType foo_type_id; + foo_type_id = foo_get_type_once (); + g_once_init_leave (&foo_type_id__once, foo_type_id); + } + return foo_type_id__once; +} + +void +sig_cb (GObject* o, + const gchar* s, + gint i) +{ + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (o, G_TYPE_OBJECT)); + g_return_if_fail (s != NULL); + success = TRUE; + _vala_assert (g_strcmp0 (s, "foo") == 0, "s == \"foo\""); + _vala_assert (i == 42, "i == 42"); +} + +static void +_sig_cb_dynamic_sig0_ (GObject* _sender, + const gchar* s, + gint i, + gpointer self) +{ + sig_cb (_sender, s, i); +} + +static void +_vala_main (void) +{ + GObject* dfoo = NULL; + Foo* _tmp0_; + _tmp0_ = foo_new (); + dfoo = G_TYPE_CHECK_INSTANCE_CAST (_tmp0_, G_TYPE_OBJECT, GObject); + g_signal_connect (dfoo, "sig", (GCallback) _sig_cb_dynamic_sig0_, NULL); + success = FALSE; + g_signal_emit_by_name (dfoo, "sig", "foo", 42); + _vala_assert (success, "success"); + _g_object_unref0 (dfoo); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/objects/signals-dynamic-emit.vala b/tests/objects/signals-dynamic-emit.vala new file mode 100644 index 000000000..55d6fac98 --- /dev/null +++ b/tests/objects/signals-dynamic-emit.vala @@ -0,0 +1,20 @@ +class Foo : Object { + public signal void sig (string s, int i); +} + +void sig_cb (Object o, string s, int i) { + success = true; + assert (s == "foo"); + assert (i == 42); +} + +bool success = false; + +void main () { + dynamic Object dfoo = new Foo (); + dfoo.sig.connect (sig_cb); + + success = false; + dfoo.sig.emit ("foo", 42); + assert (success); +} diff --git a/tests/objects/signals-emit.c-expected b/tests/objects/signals-emit.c-expected new file mode 100644 index 000000000..e249f6fe7 --- /dev/null +++ b/tests/objects/signals-emit.c-expected @@ -0,0 +1,206 @@ +/* objects_signals_emit.c generated by valac, the Vala compiler + * generated from objects_signals_emit.vala, do not modify */ + +#include <glib-object.h> +#include <glib.h> +#include <stdlib.h> +#include <string.h> + +#if !defined(VALA_EXTERN) +#if defined(_MSC_VER) +#define VALA_EXTERN __declspec(dllexport) extern +#elif __GNUC__ >= 4 +#define VALA_EXTERN __attribute__((visibility("default"))) extern +#else +#define VALA_EXTERN extern +#endif +#endif + +#define TYPE_FOO (foo_get_type ()) +#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_FOO, Foo)) +#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_FOO, FooClass)) +#define IS_FOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_FOO)) +#define IS_FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_FOO)) +#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_FOO, FooClass)) + +typedef struct _Foo Foo; +typedef struct _FooClass FooClass; +typedef struct _FooPrivate FooPrivate; +enum { + FOO_0_PROPERTY, + FOO_NUM_PROPERTIES +}; +static GParamSpec* foo_properties[FOO_NUM_PROPERTIES]; +enum { + FOO_SIG_SIGNAL, + FOO_NUM_SIGNALS +}; +static guint foo_signals[FOO_NUM_SIGNALS] = {0}; +#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) +#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } +#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } +#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + +struct _Foo { + GObject parent_instance; + FooPrivate * priv; +}; + +struct _FooClass { + GObjectClass parent_class; +}; + +static gpointer foo_parent_class = NULL; +VALA_EXTERN gboolean success; +gboolean success = FALSE; + +VALA_EXTERN GType foo_get_type (void) G_GNUC_CONST ; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Foo, g_object_unref) +VALA_EXTERN void foo_fire (Foo* self); +VALA_EXTERN Foo* foo_new (void); +VALA_EXTERN Foo* foo_construct (GType object_type); +static void g_cclosure_user_marshal_VOID__STRING_INT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, + gpointer marshal_data); +static GType foo_get_type_once (void); +VALA_EXTERN void sig_cb (GObject* o, + const gchar* s, + gint i); +static void _vala_main (void); +static void _sig_cb_foo_sig (Foo* _sender, + const gchar* s, + gint i, + gpointer self); + +void +foo_fire (Foo* self) +{ + g_return_if_fail (IS_FOO (self)); + g_signal_emit (self, foo_signals[FOO_SIG_SIGNAL], 0, "foo", 42); +} + +Foo* +foo_construct (GType object_type) +{ + Foo * self = NULL; + self = (Foo*) g_object_new (object_type, NULL); + return self; +} + +Foo* +foo_new (void) +{ + return foo_construct (TYPE_FOO); +} + +static void +g_cclosure_user_marshal_VOID__STRING_INT (GClosure * closure, + GValue * return_value, + guint n_param_values, + const GValue * param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer data1, const char* arg_1, gint arg_2, gpointer data2); + register GMarshalFunc_VOID__STRING_INT callback; + register GCClosure * cc; + register gpointer data1; + register gpointer data2; + cc = (GCClosure *) closure; + g_return_if_fail (n_param_values == 3); + if (G_CCLOSURE_SWAP_DATA (closure)) { + data1 = closure->data; + data2 = param_values->data[0].v_pointer; + } else { + data1 = param_values->data[0].v_pointer; + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback); + callback (data1, g_value_get_string (param_values + 1), g_value_get_int (param_values + 2), data2); +} + +static void +foo_class_init (FooClass * klass, + gpointer klass_data) +{ + foo_parent_class = g_type_class_peek_parent (klass); + foo_signals[FOO_SIG_SIGNAL] = g_signal_new ("sig", TYPE_FOO, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_user_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); +} + +static void +foo_instance_init (Foo * self, + gpointer klass) +{ +} + +static GType +foo_get_type_once (void) +{ + static const GTypeInfo g_define_type_info = { sizeof (FooClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) foo_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (Foo), 0, (GInstanceInitFunc) foo_instance_init, NULL }; + GType foo_type_id; + foo_type_id = g_type_register_static (G_TYPE_OBJECT, "Foo", &g_define_type_info, 0); + return foo_type_id; +} + +GType +foo_get_type (void) +{ + static volatile gsize foo_type_id__once = 0; + if (g_once_init_enter (&foo_type_id__once)) { + GType foo_type_id; + foo_type_id = foo_get_type_once (); + g_once_init_leave (&foo_type_id__once, foo_type_id); + } + return foo_type_id__once; +} + +void +sig_cb (GObject* o, + const gchar* s, + gint i) +{ + g_return_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (o, G_TYPE_OBJECT)); + g_return_if_fail (s != NULL); + success = TRUE; + _vala_assert (g_strcmp0 (s, "foo") == 0, "s == \"foo\""); + _vala_assert (i == 42, "i == 42"); +} + +static void +_sig_cb_foo_sig (Foo* _sender, + const gchar* s, + gint i, + gpointer self) +{ + sig_cb (_sender, s, i); +} + +static void +_vala_main (void) +{ + Foo* foo = NULL; + Foo* _tmp0_; + _tmp0_ = foo_new (); + foo = _tmp0_; + g_signal_connect (foo, "sig", (GCallback) _sig_cb_foo_sig, NULL); + success = FALSE; + g_signal_emit (foo, foo_signals[FOO_SIG_SIGNAL], 0, "foo", 42); + _vala_assert (success, "success"); + success = FALSE; + foo_fire (foo); + _vala_assert (success, "success"); + _g_object_unref0 (foo); +} + +int +main (int argc, + char ** argv) +{ + _vala_main (); + return 0; +} + diff --git a/tests/objects/signals-emit.vala b/tests/objects/signals-emit.vala new file mode 100644 index 000000000..5b49cae8c --- /dev/null +++ b/tests/objects/signals-emit.vala @@ -0,0 +1,28 @@ +class Foo : Object { + public signal void sig (string s, int i); + + public void fire () { + sig.emit ("foo", 42); + } +} + +void sig_cb (Object o, string s, int i) { + success = true; + assert (s == "foo"); + assert (i == 42); +} + +bool success = false; + +void main () { + var foo = new Foo (); + foo.sig.connect (sig_cb); + + success = false; + foo.sig.emit ("foo", 42); + assert (success); + + success = false; + foo.fire (); + assert (success); +} diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala index c8b3b267b..39a7af72c 100644 --- a/vala/valamemberaccess.vala +++ b/vala/valamemberaccess.vala @@ -390,6 +390,12 @@ public class Vala.MemberAccess : Expression { } } + if (inner.value_type is SignalType && member_name == "emit") { + // transform foo.sig.emit() to foo.sig() + parent_node.replace_expression (this, inner); + return true; + } + if (inner is MemberAccess) { unowned MemberAccess ma = (MemberAccess) inner; if (ma.prototype_access) { @@ -499,6 +505,13 @@ public class Vala.MemberAccess : Expression { s.access = SymbolAccessibility.PUBLIC; dynamic_object_type.type_symbol.scope.add (null, s); symbol_reference = s; + } else if (ma.member_name == "emit") { + // dynamic signal + var s = new DynamicSignal (inner.value_type, member_name, new VoidType (), source_reference); + s.access = SymbolAccessibility.PUBLIC; + s.add_parameter (new Parameter.with_ellipsis ()); + dynamic_object_type.type_symbol.scope.add (null, s); + symbol_reference = s; } else if (ma.member_name == "disconnect") { error = true; Report.error (ma.source_reference, "Use SignalHandler.disconnect() to disconnect from dynamic signal"); diff --git a/vala/valasignaltype.vala b/vala/valasignaltype.vala index ca90186fc..93867ac54 100644 --- a/vala/valasignaltype.vala +++ b/vala/valasignaltype.vala @@ -35,6 +35,7 @@ public class Vala.SignalType : CallableType { Method? connect_method; Method? connect_after_method; Method? disconnect_method; + Method? emit_method; public SignalType (Signal signal_symbol, SourceReference? source_reference = null) { base (signal_symbol, source_reference); @@ -104,6 +105,16 @@ public class Vala.SignalType : CallableType { return disconnect_method; } + unowned Method get_emit_method () { + if (emit_method == null) { + emit_method = new Method ("emit", signal_symbol.return_type, source_reference); + emit_method.access = SymbolAccessibility.PUBLIC; + emit_method.external = true; + emit_method.owner = signal_symbol.scope; + } + return emit_method; + } + public override Symbol? get_member (string member_name) { if (member_name == "connect") { return get_connect_method (); @@ -111,6 +122,8 @@ public class Vala.SignalType : CallableType { return get_connect_after_method (); } else if (member_name == "disconnect") { return get_disconnect_method (); + } else if (member_name == "emit") { + return get_emit_method (); } return null; } |