summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPhilip Withnall <philip.withnall@collabora.co.uk>2014-06-23 01:00:40 +0100
committerPhilip Withnall <philip.withnall@collabora.co.uk>2014-06-27 17:36:52 +0100
commitd395a38afc6410bc9d60f501745300305e137b37 (patch)
tree190c487d09e0771cdfbd43d5ba5ee58f9f220258 /tests
parent10ba1c82bd6f58ccab99eda85416ec5600ac015d (diff)
gsignal: Correctly handle incorrect parameter counts in signal callbacks
This is a minefield. g_signal_connect_swapped() is intended to be used in situations where the number of formal parameters in the callback function differs from the number of actual parameters emitted by the signal. This allows the common idiom of (e.g.) calling gtk_widget_hide() on a signal callback from another object, with the widget being passed in the user_data. It turns out that these situations are entirely implementation defined. The C specification disavows all knowledge of how parameter passing should work when the callback function prototype isn’t known, leaving this to the calling convention to define. All calling conventions that we care about handle this sensibly, explicitly covering the case of the number of actual parameters exceeding the number of formal parameters, just like they handle varargs. However, there are a few calling conventions which won’t handle this properly, and will cause explosions should you try. For example, I believe that passing a WinAPI function (calling convention: stdcall) as a signal callback will cause problems. I haven’t verified this experimentally though. The gsignal-checker now allows for the number of formal and actual parameters to differ, but only if the calling convention for the callback function is safe. That should cover things.
Diffstat (limited to 'tests')
-rw-r--r--tests/gsignal-connect.c51
-rw-r--r--tests/gsignal.head.c15
2 files changed, 60 insertions, 6 deletions
diff --git a/tests/gsignal-connect.c b/tests/gsignal-connect.c
index f60520a..ea8ea6b 100644
--- a/tests/gsignal-connect.c
+++ b/tests/gsignal-connect.c
@@ -132,14 +132,11 @@
}
/*
- * Incorrect number of arguments in signal handler for signal ‘GObject::notify’. Expected 3 but saw 2.
- * (GCallback) object_notify_missing_parameter_cb, NULL);
- * ^
- * note: expanded from macro 'g_signal_connect'
- * g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
- * ^
+ * No error
*/
{
+ // As long as object_notify_missing_parameter_cb is using a safe calling
+ // convention, this is actually fine.
GObject *some_object = g_malloc (5); // only checking the type
g_signal_connect (some_object, "notify",
(GCallback) object_notify_missing_parameter_cb, NULL);
@@ -284,3 +281,45 @@
some_thing, NULL,
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
}
+
+/*
+ * No error
+ */
+{
+ GSettings *settings = g_malloc (5); // only checking the type
+ GApplication *app = g_malloc (5); // only checking the type
+ g_signal_connect_swapped (app, "activate",
+ G_CALLBACK (application_activate_swapped_cb),
+ settings);
+}
+
+/*
+ * Incorrect number of arguments in signal handler for signal ‘GApplication::activate’. Expected 2 but saw 4.
+ * G_CALLBACK (application_activate_swapped_excess_arguments_cb),
+ * ^
+ * note: expanded from macro 'G_CALLBACK'
+ * #define G_CALLBACK(f) ((GCallback) (f))
+ * ^
+ * note: expanded from macro 'g_signal_connect_swapped'
+ * g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED)
+ * ^
+ */
+{
+ GSettings *settings = g_malloc (5); // only checking the type
+ GApplication *app = g_malloc (5); // only checking the type
+ g_signal_connect_swapped (app, "activate",
+ G_CALLBACK (application_activate_swapped_excess_arguments_cb),
+ settings);
+}
+
+/*
+ * No error
+ */
+{
+ // A more typical use of G_CONNECT_SWAPPED, where we use an existing
+ // function and swap the arguments to avoid writing a wrapper function.
+ GApplication *app = g_malloc (5); // only checking the type
+ GObject *some_object = g_malloc (5); // only checking the type
+ g_signal_connect_swapped (app, "activate",
+ G_CALLBACK (g_object_run_dispose), some_object);
+}
diff --git a/tests/gsignal.head.c b/tests/gsignal.head.c
index f651e4f..ec6e6d0 100644
--- a/tests/gsignal.head.c
+++ b/tests/gsignal.head.c
@@ -104,6 +104,21 @@ settings_changed_swapped_stream_cb (GInputStream *stream, const gchar *key,
/* Something */
}
+static void
+application_activate_swapped_cb (GSettings *self, GApplication *app)
+{
+ /* Done */
+}
+
+static void
+application_activate_swapped_excess_arguments_cb (GSettings *self,
+ const gchar *some_random_arg,
+ guint another_random_arg,
+ gpointer user_data)
+{
+ /* Done */
+}
+
int
main (void)
{