From 8c095eea8fbe2f8c219bdb2aebcf61e4e3993f53 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 10 Jul 2005 22:54:19 +0000 Subject: 2005-07-10 Colin Walters * doc/TODO: Knock off some GLib items with this patch. * glib/dbus-gvalue-utils.c (_dbus_gtype_can_signal_error) (_dbus_gvalue_signals_error): New functions. * glib/dbus-gvalue-utils.h: Prototype them. * glib/dbus-gobject.c (arg_iterate): Update to handle return vals and change to not output const/retval flags for input args. All callers updated. (invoke_object_method): Refactor to handle return values. Add some more comments in various places. Remove debug g_print. * glib/dbus-binding-tool-glib.h (DBUS_GLIB_ANNOTATION_RETURNVAL): New. * glib/dbus-binding-tool-glib.c (dbus_g_type_get_marshal_name): Handle G_TYPE_NONE. (compute_gsignature): New function; refactored from code from compute_marshaller and compute_marshaller_name. Enhance to handle return values and async ops more cleanly. Update for async ops returning NONE instead of BOOLEAN. (compute_marshaller, compute_marshaller_name): Call compute_gsignature and output appropriate string. (generate_glue): Handle return value annotation. Also don't dump constness flag for input arguments. * glib/Makefile.am (DBUS_GLIB_INTERNALS): New variable; contains files shared between installed library and utilities. (libdbus_glib_1_la_SOURCES): Move some stuf into DBUS_GLIB_INTERNALS. (libdbus_gtool_la_SOURCES): Suck in DBUS_GLIB_INTERNALS so the binding tool can access gtype utility functions. * test/glib/test-service-glib.c: * test/glib/test-service-glib.xml: * test/glib/test-dbus-glib.c: Add some tests for return values. --- glib/dbus-gobject.c | 242 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 187 insertions(+), 55 deletions(-) (limited to 'glib/dbus-gobject.c') diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c index 58291573..1dcf4bb9 100644 --- a/glib/dbus-gobject.c +++ b/glib/dbus-gobject.c @@ -143,45 +143,91 @@ method_arg_info_from_object_info (const DBusGObjectInfo *object, return string_table_lookup (get_method_data (object, method), 3);/*RB was 2*/ } +typedef enum +{ + RETVAL_NONE, + RETVAL_NOERROR, + RETVAL_ERROR +} RetvalType; + static const char * arg_iterate (const char *data, const char **name, gboolean *in, gboolean *constval, + RetvalType *retval, const char **type) { - *name = data; + gboolean inarg; + + if (name) + *name = data; data = string_table_next (data); switch (*data) { case 'I': - *in = TRUE; + inarg = TRUE; break; case 'O': - *in = FALSE; + inarg = FALSE; break; default: g_warning ("invalid arg direction '%c'", *data); + inarg = FALSE; break; } + if (in) + *in = inarg; - data = string_table_next (data); - switch (*data) + if (!inarg) { - case 'F': - *constval = FALSE; - break; - case 'C': - *constval = TRUE; - break; - default: - g_warning ("invalid arg const value '%c'", *data); - break; + data = string_table_next (data); + switch (*data) + { + case 'F': + if (constval) + *constval = FALSE; + break; + case 'C': + if (constval) + *constval = TRUE; + break; + default: + g_warning ("invalid arg const value '%c'", *data); + break; + } + data = string_table_next (data); + switch (*data) + { + case 'N': + if (retval) + *retval = RETVAL_NONE; + break; + case 'E': + if (retval) + *retval = RETVAL_ERROR; + break; + case 'R': + if (retval) + *retval = RETVAL_NOERROR; + break; + default: + g_warning ("invalid arg ret value '%c'", *data); + break; + } + } + else + { + if (constval) + *constval = FALSE; + if (retval) + *retval = FALSE; } data = string_table_next (data); - *type = data; + if (type) + *type = data; return string_table_next (data); } @@ -202,10 +248,9 @@ method_dir_signature_from_object_info (const DBusGObjectInfo *object, { const char *name; gboolean arg_in; - gboolean constval; const char *type; - arg = arg_iterate (arg, &name, &arg_in, &constval, &type); + arg = arg_iterate (arg, &name, &arg_in, NULL, NULL, &type); if (arg_in == in) g_string_append (ret, type); @@ -340,10 +385,9 @@ write_interface (gpointer key, gpointer val, gpointer user_data) { const char *name; gboolean arg_in; - gboolean constval; const char *type; - args = arg_iterate (args, &name, &arg_in, &constval, &type); + args = arg_iterate (args, &name, &arg_in, NULL, NULL, &type); /* FIXME - handle container types */ g_string_append_printf (xml, " \n", @@ -846,15 +890,17 @@ invoke_object_method (GObject *object, GValue return_value = {0,}; GClosure closure; char *in_signature; - char *out_signature = NULL; - int current_type; - DBusSignatureIter out_signature_iter; GArray *out_param_values = NULL; GValueArray *out_param_gvalues = NULL; int out_param_count; int out_param_pos, out_param_gvalue_pos; DBusHandlerResult result; DBusMessage *reply; + gboolean have_retval; + gboolean retval_signals_error; + gboolean retval_is_synthetic; + gboolean retval_is_constant; + const char *arg_metadata; gerror = NULL; @@ -866,6 +912,11 @@ invoke_object_method (GObject *object, else call_only = FALSE; + have_retval = FALSE; + retval_signals_error = FALSE; + retval_is_synthetic = FALSE; + retval_is_constant = FALSE; + /* This is evil. We do this to work around the fact that * the generated glib marshallers check a flag in the closure object * which we don't care about. We don't need/want to create @@ -924,21 +975,72 @@ invoke_object_method (GObject *object, } else { - out_signature = method_output_signature_from_object_info (object_info, method); + RetvalType retval; + gboolean arg_in; + gboolean arg_const; + const char *argsig; - /* Count number of output parameters */ - dbus_signature_iter_init (&out_signature_iter, out_signature); + arg_metadata = method_arg_info_from_object_info (object_info, method); + + /* Count number of output parameters, and look for a return value */ out_param_count = 0; - while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID) + while (*arg_metadata) + { + arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, &arg_const, &retval, &argsig); + if (arg_in) + continue; + if (retval != RETVAL_NONE) + { + DBusSignatureIter tmp_sigiter; + /* This is the function return value */ + g_assert (!have_retval); + have_retval = TRUE; + retval_is_synthetic = FALSE; + + switch (retval) + { + case RETVAL_NONE: + g_assert_not_reached (); + break; + case RETVAL_NOERROR: + retval_signals_error = FALSE; + break; + case RETVAL_ERROR: + retval_signals_error = TRUE; + break; + } + + retval_is_constant = arg_const; + + /* Initialize our return GValue with the specified type */ + dbus_signature_iter_init (&tmp_sigiter, argsig); + g_value_init (&return_value, dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE)); + } + else + { + /* It's a regular output value */ + out_param_count++; + } + } + + /* For compatibility, if we haven't found a return value, we assume + * the function returns a gboolean for signalling an error + * (and therefore also takes a GError). We also note that it + * is a "synthetic" return value; i.e. we aren't going to be + * sending it over the bus, it's just to signal an error. + */ + if (!have_retval) { - out_param_count++; - dbus_signature_iter_next (&out_signature_iter); + have_retval = TRUE; + retval_is_synthetic = TRUE; + retval_signals_error = TRUE; + g_value_init (&return_value, G_TYPE_BOOLEAN); } - /* Create an array to store the actual values of OUT - * parameters. Then, create a GValue boxed POINTER - * to each of those values, and append to the invocation, - * so the method can return the OUT parameters. + /* Create an array to store the actual values of OUT parameters + * (other than the real function return, if any). Then, create + * a GValue boxed POINTER to each of those values, and append to + * the invocation, so the method can return the OUT parameters. */ out_param_values = g_array_sized_new (FALSE, TRUE, sizeof (GTypeCValue), out_param_count); @@ -948,16 +1050,32 @@ invoke_object_method (GObject *object, out_param_gvalues = g_value_array_new (out_param_count); out_param_pos = 0; out_param_gvalue_pos = 0; - dbus_signature_iter_init (&out_signature_iter, out_signature); - while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID) + + /* Reset argument metadata pointer */ + arg_metadata = method_arg_info_from_object_info (object_info, method); + + /* Iterate over output arguments again, this time allocating space for + * them as appopriate. + */ + while (*arg_metadata) { GValue value = {0, }; GTypeCValue storage; + DBusSignatureIter tmp_sigiter; + GType current_gtype; + + arg_metadata = arg_iterate (arg_metadata, NULL, &arg_in, NULL, &retval, &argsig); + /* Skip over input arguments and the return value, if any */ + if (arg_in || retval != RETVAL_NONE) + continue; + + dbus_signature_iter_init (&tmp_sigiter, argsig); + current_gtype = dbus_gtype_from_signature_iter (&tmp_sigiter, FALSE); g_value_init (&value, G_TYPE_POINTER); /* We special case variants to make method invocation a bit nicer */ - if (current_type != DBUS_TYPE_VARIANT) + if (current_gtype != G_TYPE_VALUE) { memset (&storage, 0, sizeof (storage)); g_array_append_val (out_param_values, storage); @@ -971,17 +1089,20 @@ invoke_object_method (GObject *object, out_param_gvalue_pos++; } g_value_array_append (value_array, &value); - dbus_signature_iter_next (&out_signature_iter); } + } - /* Append GError as final argument */ + /* Append GError as final argument if necessary */ + if (retval_signals_error) + { + g_assert (have_retval); g_value_array_append (value_array, NULL); g_value_init (g_value_array_get_nth (value_array, value_array->n_values - 1), G_TYPE_POINTER); g_value_set_pointer (g_value_array_get_nth (value_array, value_array->n_values - 1), &gerror); } + /* Actually invoke method */ - g_value_init (&return_value, G_TYPE_BOOLEAN); - method->marshaller (&closure, &return_value, + method->marshaller (&closure, have_retval ? &return_value : NULL, value_array->n_values, value_array->values, NULL, method->function); @@ -990,22 +1111,35 @@ invoke_object_method (GObject *object, result = DBUS_HANDLER_RESULT_HANDLED; goto done; } - had_error = !g_value_get_boolean (&return_value); + if (retval_signals_error) + had_error = _dbus_gvalue_signals_error (&return_value); + else + had_error = FALSE; if (!had_error) { DBusMessageIter iter; - const char *arg_metadata; - - /* Grab the argument metadata and iterate over it */ - arg_metadata = method_arg_info_from_object_info (object_info, method); reply = dbus_message_new_method_return (message); if (reply == NULL) goto nomem; - /* Append OUT arguments to reply */ + /* Append output arguments to reply */ dbus_message_iter_init_append (reply, &iter); + + /* First, append the return value, unless it's synthetic */ + if (have_retval && !retval_is_synthetic) + { + if (!dbus_gvalue_marshal (&iter, &return_value)) + goto nomem; + if (!retval_is_constant) + g_value_unset (&return_value); + } + + /* Grab the argument metadata and iterate over it */ + arg_metadata = method_arg_info_from_object_info (object_info, method); + + /* Now append any remaining return values */ out_param_pos = 0; out_param_gvalue_pos = 0; while (*arg_metadata) @@ -1014,26 +1148,26 @@ invoke_object_method (GObject *object, const char *arg_name; gboolean arg_in; gboolean constval; + RetvalType retval; const char *arg_signature; DBusSignatureIter argsigiter; do { - /* Look for constness; skip over input arguments */ - arg_metadata = arg_iterate (arg_metadata, &arg_name, &arg_in, &constval, &arg_signature); + /* Iterate over only output values; skip over input + arguments and the return value */ + arg_metadata = arg_iterate (arg_metadata, &arg_name, &arg_in, &constval, &retval, &arg_signature); } - while (arg_in && *arg_metadata); + while ((arg_in || retval != RETVAL_NONE) && *arg_metadata); - /* If the last argument we saw was input, we must be done iterating over - * output arguments. + /* If the last argument we saw was input or the return + * value, we must be done iterating over output arguments. */ - if (arg_in) + if (arg_in || retval != RETVAL_NONE) break; dbus_signature_iter_init (&argsigiter, arg_signature); - g_print ("looking at arg %s (%s)\n", arg_name, constval ? "TRUE" : "FALSE"); - g_value_init (&gvalue, dbus_gtype_from_signature_iter (&argsigiter, FALSE)); if (G_VALUE_TYPE (&gvalue) != G_TYPE_VALUE) { @@ -1070,14 +1204,12 @@ invoke_object_method (GObject *object, result = DBUS_HANDLER_RESULT_HANDLED; done: g_free (in_signature); - g_free (out_signature); if (!call_only) { g_array_free (out_param_values, TRUE); g_value_array_free (out_param_gvalues); } g_value_array_free (value_array); - g_value_unset (&return_value); return result; nomem: result = DBUS_HANDLER_RESULT_NEED_MEMORY; -- cgit