]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
2005-07-10 Colin Walters <walters@verbum.org>
authorColin Walters <walters@verbum.org>
Sun, 10 Jul 2005 22:54:19 +0000 (22:54 +0000)
committerColin Walters <walters@verbum.org>
Sun, 10 Jul 2005 22:54:19 +0000 (22:54 +0000)
* 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.

12 files changed:
ChangeLog
doc/TODO
glib/Makefile.am
glib/dbus-binding-tool-glib.c
glib/dbus-binding-tool-glib.h
glib/dbus-gobject.c
glib/dbus-gvalue-utils.c
glib/dbus-gvalue-utils.h
glib/examples/statemachine/.cvsignore
test/glib/test-dbus-glib.c
test/glib/test-service-glib.c
test/glib/test-service-glib.xml

index 00c778800df4727227ceb1bf8ca5b8c7f178f8ec..53023dddbdbb3368f9486be7602a42d32112abb0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2005-07-10  Colin Walters  <walters@verbum.org>
+
+       * 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.
+
 2005-07-09  Colin Walters  <walters@verbum.org>
 
        * glib/dbus-gparser.c (parse_annotation): Add annotations to
index df0f9c77d1eb6f5ba05a8a136f2988b0fff3fd25..38fbe57c13dd1be93656d4b39973b45099df46d3 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -28,10 +28,6 @@ Important for 1.0
 Important for 1.0 GLib Bindings
 ===
 
- - Annotations for "do not take ownership of this return value" on server
-
- - Support a{sv}
-
  - Test point-to-point mode
 
  - Add support for getting sender
index 26724b930d5ed9c5dcf6adb3b12a66bf46cbf180..1abf68057aef39d2a6587d6df840b4ad7589fc3d 100644 (file)
@@ -11,6 +11,13 @@ BUILT_SOURCES = dbus-glib-error-switch.h
 
 CLEANFILES = $(BUILT_SOURCES)
 
+DBUS_GLIB_INTERNALS = \
+       dbus-gtype-specialized.c                \
+       dbus-gutils.c                           \
+       dbus-gutils.h                           \
+       dbus-gvalue-utils.c                     \
+       dbus-gvalue-utils.h
+
 libdbus_glib_1_la_SOURCES =                    \
        dbus-glib-error-switch.h                \
        dbus-glib.c                             \
@@ -23,13 +30,9 @@ libdbus_glib_1_la_SOURCES =                  \
        dbus-gtest.c                            \
        dbus-gtest.h                            \
        dbus-gthread.c                          \
-       dbus-gutils.c                           \
-       dbus-gutils.h                           \
        dbus-gvalue.c                           \
-       dbus-gtype-specialized.c                \
        dbus-gvalue.h                           \
-       dbus-gvalue-utils.c                     \
-       dbus-gvalue-utils.h
+       $(DBUS_GLIB_INTERNALS)
 
 libdbus_glib_HEADERS =                  \
        dbus-gtype-specialized.h
@@ -44,14 +47,12 @@ libdbus_glib_1_la_LDFLAGS= -export-symbols-regex "^[^_].*" -version-info $(LT_CU
 # convenience lib used here and by dbus-viewer
 noinst_LTLIBRARIES=libdbus-gtool.la
 
-libdbus_gtool_la_SOURCES =                     \
+libdbus_gtool_la_SOURCES = $(DBUS_GLIB_INTERNALS) \
        dbus-gidl.c                             \
        dbus-gidl.h                             \
        dbus-gloader-expat.c                    \
        dbus-gparser.c                          \
-       dbus-gparser.h                          \
-       dbus-gutils.c                           \
-       dbus-gutils.h
+       dbus-gparser.h
 
 libdbus_gtool_la_LIBADD = libdbus-glib-1.la
 
index f161c6380d1c70a1847d93344d9f78e0b48a4f05..43b23c6d9c5d5e9c7d1067c36c6a31333d77dc73 100644 (file)
@@ -62,6 +62,8 @@ dbus_g_type_get_marshal_name (GType gtype)
 {
   switch (G_TYPE_FUNDAMENTAL (gtype))
     {
+    case G_TYPE_NONE:
+      return "NONE";
     case G_TYPE_BOOLEAN:
       return "BOOLEAN";
     case G_TYPE_UCHAR:
@@ -112,148 +114,195 @@ dbus_g_type_get_c_name (GType gtype)
   return g_type_name (gtype);
 }
 
-static char *
-compute_marshaller (MethodInfo *method, GError **error)
+static gboolean
+compute_gsignature (MethodInfo *method, GType *rettype, GArray **params, GError **error)
 {
   GSList *elt;
-  GString *ret;
-  gboolean first;
+  GType retval_type;
+  GArray *ret;
+  gboolean is_async;
+  const char *arg_type;
+  gboolean retval_signals_error;
+  
+  is_async = method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL;
+  retval_signals_error = FALSE;
 
-  /* All methods required to return boolean for now;
-   * will be conditional on method info later */
-  ret = g_string_new ("BOOLEAN:");
+  ret = g_array_new (TRUE, TRUE, sizeof (GType));
 
-  first = TRUE;
-  /* Append input arguments */
-  for (elt = method_info_get_args (method); elt; elt = elt->next)
+  if (is_async)
+    retval_type = G_TYPE_NONE;
+  else
     {
-      ArgInfo *arg = elt->data;
+      gboolean found_retval;
 
-      if (arg_info_get_direction (arg) == ARG_IN)
+      /* Look for return value */
+      found_retval = FALSE;
+      for (elt = method_info_get_args (method); elt; elt = elt->next)
        {
-         const char *marshal_name;
-         GType gtype;
-
-         gtype = dbus_gtype_from_signature (arg_info_get_type (arg), FALSE);
-         if (gtype == G_TYPE_INVALID)
+         ArgInfo *arg = elt->data;
+         const char *returnval_annotation;
+      
+         returnval_annotation = arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL);
+         if (returnval_annotation != NULL)
            {
-             g_set_error (error,
-                          DBUS_BINDING_TOOL_ERROR,
-                          DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
-                          _("Unsupported conversion from D-BUS type %s to glib-genmarshal type"),
-                          arg_info_get_type (arg));
-             g_string_free (ret, TRUE);
-             return NULL;
+             arg_type = arg_info_get_type (arg);
+             retval_type = dbus_gtype_from_signature (arg_type, FALSE);
+             if (retval_type == G_TYPE_INVALID)
+               goto invalid_type;
+             found_retval = TRUE;
+             if (!strcmp (returnval_annotation, "error"))
+               retval_signals_error = TRUE;
+             break;
            }
-
-         marshal_name = dbus_g_type_get_marshal_name (gtype);
-         g_assert (marshal_name);
-
-         if (!first)
-           g_string_append (ret, ",");
-         else
-           first = FALSE;
-         g_string_append (ret, marshal_name);
+       }
+      if (!found_retval)
+       {
+         retval_type = G_TYPE_BOOLEAN;
+         retval_signals_error = TRUE;
        }
     }
 
-  if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL)
+  *rettype = retval_type;
+
+  /* Handle all input arguments */
+  for (elt = method_info_get_args (method); elt; elt = elt->next)
     {
-      if (!first)
-       g_string_append (ret, ",");
-      g_string_append (ret, "POINTER");
-      first = FALSE;
+      ArgInfo *arg = elt->data;
+      if (arg_info_get_direction (arg) == ARG_IN)
+       {
+         GType gtype;
+         
+         arg_type = arg_info_get_type (arg);
+         gtype = dbus_gtype_from_signature (arg_type, FALSE);
+         if (gtype == G_TYPE_INVALID)
+           goto invalid_type;
+         
+         g_array_append_val (ret, gtype);
+       }
     }
-  else
+
+  if (!is_async)
     {
       /* Append pointer for each out arg storage */
       for (elt = method_info_get_args (method); elt; elt = elt->next)
        {
          ArgInfo *arg = elt->data;
 
+         /* Skip return value */
+         if (arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL) != NULL)
+           continue;
+      
          if (arg_info_get_direction (arg) == ARG_OUT)
            {
-             if (!first)
-               g_string_append (ret, ",");
-             else
-               first = FALSE;
-             g_string_append (ret, "POINTER");
+             GType gtype;
+             arg_type = arg_info_get_type (arg);
+             gtype = dbus_gtype_from_signature (arg_type, FALSE);
+             if (gtype == G_TYPE_INVALID)
+               goto invalid_type;
+             /* We actually just need a pointer for the return value
+                storage */
+             gtype = G_TYPE_POINTER;
+             g_array_append_val (ret, gtype);
            }
        }
-      /* Final GError parameter */
-      if (!first)
-       g_string_append (ret, ",");
-      g_string_append (ret, "POINTER");
 
+      if (retval_signals_error)
+       {
+         /* Final GError parameter */
+         GType gtype = G_TYPE_POINTER;
+         g_array_append_val (ret, gtype);
+       }
+    }
+  else
+    {
+      /* Context pointer */
+      GType gtype = G_TYPE_POINTER;
+      g_array_append_val (ret, gtype);
     }
 
-  return g_string_free (ret, FALSE);
+  *params = ret;
+  return TRUE;
 
+ invalid_type:
+  g_set_error (error,
+              DBUS_BINDING_TOOL_ERROR,
+              DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
+              _("Unsupported conversion from D-BUS type %s to glib-genmarshal type"),
+              arg_type);
+  return FALSE;
 }
+  
 
 static char *
-compute_marshaller_name (MethodInfo *method, const char *prefix, GError **error)
+compute_marshaller (MethodInfo *method, GError **error)
 {
-  GSList *elt;
+  GArray *signature;
+  GType rettype;
+  const char *marshal_name;
   GString *ret;
+  guint i;
 
-  /* All methods required to return boolean for now;
-   * will be conditional on method info later */
-  ret = g_string_new (MARSHAL_PREFIX);
-  g_string_append (ret, prefix);
-  g_string_append (ret, "_BOOLEAN_");
+  if (!compute_gsignature (method, &rettype, &signature, error))
+    return NULL;
 
-  /* Append input arguments */
-  for (elt = method_info_get_args (method); elt; elt = elt->next)
+  ret = g_string_new ("");
+  marshal_name = dbus_g_type_get_marshal_name (rettype);
+  g_assert (marshal_name != NULL);
+  g_string_append (ret, marshal_name);
+  g_string_append_c (ret, ':');
+  for (i = 0; i < signature->len; i++)
     {
-      ArgInfo *arg = elt->data;
+      marshal_name = dbus_g_type_get_marshal_name (g_array_index (signature, GType, i));
+      g_assert (marshal_name != NULL);
+      g_string_append (ret, marshal_name);
+      if (i < signature->len - 1)
+       g_string_append_c (ret, ',');
+    }
+  if (signature->len == 0)
+    {
+      marshal_name = dbus_g_type_get_marshal_name (G_TYPE_NONE);
+      g_assert (marshal_name != NULL);
+      g_string_append (ret, marshal_name);
+    }
+  g_array_free (signature, TRUE);
+  return g_string_free (ret, FALSE);
+}
 
-      if (arg_info_get_direction (arg) == ARG_IN)
-       {
-         const char *marshal_name;
-         const char *type;
-         GType gtype;
+static char *
+compute_marshaller_name (MethodInfo *method, const char *prefix, GError **error)
+{
+  GString *ret;
+  GArray *signature;
+  GType rettype;
+  const char *marshal_name;
+  guint i;
 
-         type = arg_info_get_type (arg);
-         gtype = dbus_gtype_from_signature (type, FALSE);
-         if (gtype == G_TYPE_INVALID)
-           {
-             g_set_error (error,
-                          DBUS_BINDING_TOOL_ERROR,
-                          DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
-                          _("Unsupported conversion from D-BUS type %s to glib type"),
-                          type);
-             g_string_free (ret, TRUE);
-             return NULL;
-           }
-         marshal_name = dbus_g_type_get_marshal_name (gtype);
-         g_assert (marshal_name != NULL);
+  if (!compute_gsignature (method, &rettype, &signature, error))
+    return NULL;
 
-         g_string_append (ret, "_");
-         g_string_append (ret, marshal_name);
-       }
-    }
+  ret = g_string_new (MARSHAL_PREFIX);
+  g_string_append (ret, prefix);
+  g_string_append_c (ret, '_');
 
-  if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL)
+  marshal_name = dbus_g_type_get_marshal_name (rettype);
+  g_assert (marshal_name != NULL);
+  g_string_append (ret, marshal_name);
+  g_string_append (ret, "__");
+  for (i = 0; i < signature->len; i++)
     {
-      g_string_append (ret, "_POINTER");
+      marshal_name = dbus_g_type_get_marshal_name (g_array_index (signature, GType, i));
+      g_assert (marshal_name != NULL);
+      g_string_append (ret, marshal_name);
+      if (i < signature->len - 1)
+       g_string_append_c (ret, '_');
     }
-  else
+  if (signature->len == 0)
     {
-      /* Append pointer for each out arg storage */
-      for (elt = method_info_get_args (method); elt; elt = elt->next)
-       {
-         ArgInfo *arg = elt->data;
-
-         if (arg_info_get_direction (arg) == ARG_OUT)
-           {
-             g_string_append (ret, "_POINTER");
-           }
-       }
-      /* Final GError parameter */
-      g_string_append (ret, "_POINTER");
+      marshal_name = dbus_g_type_get_marshal_name (G_TYPE_NONE);
+      g_assert (marshal_name != NULL);
+      g_string_append (ret, marshal_name);
     }
-
+  g_array_free (signature, TRUE);
   return g_string_free (ret, FALSE);
 }
 
@@ -482,6 +531,7 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
          char *method_c_name;
           gboolean async = FALSE;
          GSList *args;
+         gboolean found_retval = FALSE;
 
           method = (MethodInfo *) tmp->data;
          method_c_name = g_strdup (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_C_SYMBOL));
@@ -531,6 +581,7 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
            {
              ArgInfo *arg;
              char direction;
+             const char *returnval_annotation;
 
              arg = args->data;
 
@@ -561,17 +612,86 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
                      g_set_error (error,
                                   DBUS_BINDING_TOOL_ERROR,
                                   DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION,
-                                  "Input argument \"%s\" has const annotation in method \"%s\" of interface \"%s\"\n",
+                                  "Input argument \"%s\" cannot have const annotation in method \"%s\" of interface \"%s\"\n",
                                   arg_info_get_name (arg),
                                   method_info_get_name (method),
                                   interface_info_get_name (interface));
                      return FALSE;
                    }
                  g_string_append_c (object_introspection_data_blob, 'C');
+                 g_string_append_c (object_introspection_data_blob, '\0');
+               }
+             else if (arg_info_get_direction (arg) == ARG_OUT)
+               {
+                 g_string_append_c (object_introspection_data_blob, 'F');
+                 g_string_append_c (object_introspection_data_blob, '\0');
+               }
+
+             returnval_annotation = arg_info_get_annotation (arg, DBUS_GLIB_ANNOTATION_RETURNVAL);
+             if (returnval_annotation != NULL)
+               {
+                 GType gtype;
+
+                 if (found_retval)
+                   {
+                     g_set_error (error,
+                                  DBUS_BINDING_TOOL_ERROR,
+                                  DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION,
+                                  "Multiple arguments with return value annotation in method \"%s\" of interface \"%s\"\n",
+                                  method_info_get_name (method),
+                                  interface_info_get_name (interface));
+                     return FALSE;
+                   }
+                 found_retval = TRUE;
+                 if (arg_info_get_direction (arg) == ARG_IN)
+                   {
+                     g_set_error (error,
+                                  DBUS_BINDING_TOOL_ERROR,
+                                  DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION,
+                                  "Input argument \"%s\" cannot have return value annotation in method \"%s\" of interface \"%s\"\n",
+                                  arg_info_get_name (arg),
+                                  method_info_get_name (method),
+                                  interface_info_get_name (interface));
+                     return FALSE;
+                   }
+                 if (!strcmp ("", returnval_annotation))
+                   g_string_append_c (object_introspection_data_blob, 'R');
+                 else if (!strcmp ("error", returnval_annotation))
+                   {
+                     gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+                     if (!_dbus_gtype_can_signal_error (gtype))
+                       {
+                         g_set_error (error,
+                                      DBUS_BINDING_TOOL_ERROR,
+                                      DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION,
+                                      "Output argument \"%s\" cannot signal error with type \"%s\" in method \"%s\" of interface \"%s\"\n",
+                                      arg_info_get_name (arg),
+                                      g_type_name (gtype),
+                                      method_info_get_name (method),
+                                      interface_info_get_name (interface));
+                         return FALSE;
+                       }
+                     g_string_append_c (object_introspection_data_blob, 'E');
+                   }
+                 else
+                   {
+                     g_set_error (error,
+                                  DBUS_BINDING_TOOL_ERROR,
+                                  DBUS_BINDING_TOOL_ERROR_INVALID_ANNOTATION,
+                                  "Invalid ReturnVal annotation for argument \"%s\" in method \"%s\" of interface \"%s\"\n",
+                                  arg_info_get_name (arg),
+                                  method_info_get_name (method),
+                                  interface_info_get_name (interface));
+                     return FALSE;
+                   }
+                     
+                 g_string_append_c (object_introspection_data_blob, '\0');
+               }
+             else if (arg_info_get_direction (arg) == ARG_OUT)
+               {
+                 g_string_append_c (object_introspection_data_blob, 'N');
+                 g_string_append_c (object_introspection_data_blob, '\0');
                }
-             else
-               g_string_append_c (object_introspection_data_blob, 'F');
-             g_string_append_c (object_introspection_data_blob, '\0');
 
              g_string_append (object_introspection_data_blob, arg_info_get_type (arg));
              g_string_append_c (object_introspection_data_blob, '\0');
index 62ad2b79be7b02ac08dc89691fe9265b172dd46b..bbb54f72f47483cda63044d82ac0762747146e55 100644 (file)
@@ -28,6 +28,7 @@ G_BEGIN_DECLS
 #define DBUS_GLIB_ANNOTATION_C_SYMBOL "org.freedesktop.DBus.GLib.CSymbol"
 #define DBUS_GLIB_ANNOTATION_ASYNC "org.freedesktop.DBus.GLib.Async"
 #define DBUS_GLIB_ANNOTATION_CONST "org.freedesktop.DBus.GLib.Const"
+#define DBUS_GLIB_ANNOTATION_RETURNVAL "org.freedesktop.DBus.GLib.ReturnVal"
 
 gboolean dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, gboolean ignore_unsupported, GError **error);
 gboolean dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, const char *prefix, GError **error);
index 582915732ab33a1e5f5dc7388bbcc3b8d909f0bf..1dcf4bb94eb38e23afd6820f801240d33bf5eba2 100644 (file)
@@ -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, "      <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\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;
index b17eee162faf5a766a8dc428e9a612c749763ed7..af2fff19c8120826b65b9d1b2406537f3bee4445 100644 (file)
@@ -206,6 +206,58 @@ dbus_gvalue_take (GValue          *value,
   return TRUE;
 }
 
+gboolean
+_dbus_gtype_can_signal_error (GType gtype)
+{
+  switch (gtype)
+    {
+    case G_TYPE_BOOLEAN:
+    case G_TYPE_INT:
+    case G_TYPE_UINT:
+    case G_TYPE_STRING:
+    case G_TYPE_BOXED:
+    case G_TYPE_OBJECT:
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+gboolean
+_dbus_gvalue_signals_error (const GValue *value)
+{
+  /* Hardcoded rules for return value semantics for certain
+   * types.  Perhaps in the future we'd want an annotation
+   * specifying which return values are errors, but in
+   * reality people will probably just use boolean and
+   * boxed, and there the semantics are pretty standard.
+   */
+  switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_BOOLEAN:
+      return (g_value_get_boolean (value) == FALSE);
+      break;
+    case G_TYPE_INT:
+      return (g_value_get_int (value) < 0);
+      break;
+    case G_TYPE_UINT:
+      return (g_value_get_uint (value) == 0);
+      break;
+    case G_TYPE_STRING:
+      return (g_value_get_string (value) == NULL);
+      break;
+    case G_TYPE_BOXED:
+      return (g_value_get_boxed (value) == NULL);
+      break;
+    case G_TYPE_OBJECT:
+      return (g_value_get_boxed (value) == NULL);
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+
 static gboolean
 hash_func_from_gtype (GType gtype, GHashFunc *func)
 {
@@ -1061,4 +1113,6 @@ _dbus_gvalue_utils_test (const char *datadir)
   return TRUE;
 }
 
+
+
 #endif /* DBUS_BUILD_TESTS */
index 551e42898bc38a58592f875bce05b4e62fcc4960..4f81081ee3bdbc4180be35af9f3cd1b91b91b329 100644 (file)
@@ -64,6 +64,10 @@ gboolean       dbus_gvalue_store                     (GValue          *value,
 gboolean       dbus_gvalue_take                      (GValue          *value,
                                                      GTypeCValue     *cvalue);
 
+gboolean       _dbus_gtype_can_signal_error          (GType                    gtype);
+gboolean       _dbus_gvalue_signals_error            (const GValue            *value);
+
+
 G_END_DECLS
 
 #endif
index b1f9981da33da19ea44e9007b274e4f452616f23..6bf742186767dbe98b6327a2ff590707fe018b58 100644 (file)
@@ -7,6 +7,7 @@ Makefile.in
 statemachine-client
 statemachine-server
 statemachine-glue.h
+statemachine-server-glue.h
 run-with-tmp-session-bus.conf
 sm-marshal.[ch]
 *.bb
index 4fbe797ee95a6e1077e9bbeb9a7d06d940dee427..786235deb005ecb66eb9386c849305f4762812a5 100644 (file)
@@ -504,11 +504,11 @@ main (int argc, char **argv)
                          G_TYPE_UINT, &v_UINT32_2,
                          G_TYPE_INVALID))
     lose_gerror ("Failed to complete Increment call", error);
-  g_assert (n_times_echo_cb_entered == 1);
-
   if (v_UINT32_2 != 43)
     lose ("Increment call returned %d, should be 43", v_UINT32_2);
 
+  v_UINT32_2 = 0;
+  g_print ("Calling Increment (async)\n");
   call = dbus_g_proxy_begin_call (proxy, "Increment",
                                  increment_received_cb, g_strdup ("moo"), g_free,
                                  G_TYPE_UINT, 42,
@@ -517,6 +517,30 @@ main (int argc, char **argv)
   exit_timeout = g_timeout_add (5000, timed_exit, loop);
   g_main_loop_run (loop);
 
+  g_print ("Calling IncrementRetval\n");
+  error = NULL;
+  v_UINT32_2 = 0;
+  if (!dbus_g_proxy_call (proxy, "IncrementRetval", &error,
+                         G_TYPE_UINT, 42,
+                         G_TYPE_INVALID,
+                         G_TYPE_UINT, &v_UINT32_2,
+                         G_TYPE_INVALID))
+    lose_gerror ("Failed to complete Increment call", error);
+  if (v_UINT32_2 != 43)
+    lose ("IncrementRetval call returned %d, should be 43", v_UINT32_2);
+
+  g_print ("Calling IncrementRetvalError\n");
+  error = NULL;
+  v_UINT32_2 = 0;
+  if (!dbus_g_proxy_call (proxy, "IncrementRetvalError", &error,
+                         G_TYPE_UINT, 5,
+                         G_TYPE_INVALID,
+                         G_TYPE_UINT, &v_UINT32_2,
+                         G_TYPE_INVALID))
+    lose_gerror ("Failed to complete Increment call", error);
+  if (v_UINT32_2 != 6)
+    lose ("IncrementRetval call returned %d, should be 6", v_UINT32_2);
+
   g_print ("Calling ThrowError\n");
   if (dbus_g_proxy_call (proxy, "ThrowError", &error,
                         G_TYPE_INVALID, G_TYPE_INVALID) != FALSE)
@@ -529,6 +553,19 @@ main (int argc, char **argv)
   g_print ("ThrowError failed (as expected) returned error: %s\n", error->message);
   g_clear_error (&error);
 
+  g_print ("Calling IncrementRetvalError (for error)\n");
+  error = NULL;
+  v_UINT32_2 = 0;
+  if (dbus_g_proxy_call (proxy, "IncrementRetvalError", &error,
+                        G_TYPE_UINT, 20,
+                        G_TYPE_INVALID,
+                        G_TYPE_UINT, &v_UINT32_2,
+                        G_TYPE_INVALID) != FALSE)
+    lose ("IncrementRetvalError call unexpectedly succeeded!");
+  if (!dbus_g_error_has_name (error, "org.freedesktop.DBus.Tests.MyObject.Foo"))
+    lose ("IncrementRetvalError call returned unexpected error \"%s\": %s", dbus_g_error_get_name (error), error->message);
+  g_clear_error (&error);
+
   error = NULL;
   g_print ("Calling Uppercase\n");
   if (!dbus_g_proxy_call (proxy, "Uppercase", &error,
index 853b401a1dc7ea0a363d2683055be8e29226616e..e44310f3b4d340bd6a7326479f871d491ec07d4d 100644 (file)
@@ -52,6 +52,10 @@ gboolean my_object_do_nothing (MyObject *obj, GError **error);
 
 gboolean my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error);
 
+gint32   my_object_increment_retval (MyObject *obj, gint32 x);
+
+gint32   my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error);
+
 gboolean my_object_throw_error (MyObject *obj, GError **error);
 
 gboolean my_object_uppercase (MyObject *obj, const char *str, char **ret, GError **error);
@@ -91,9 +95,9 @@ gboolean my_object_emit_frobnicate (MyObject *obj, GError **error);
 
 gboolean my_object_terminate (MyObject *obj, GError **error);
 
-gboolean my_object_async_increment (MyObject *obj, gint32 x, DBusGMethodInvocation *context);
+void my_object_async_increment (MyObject *obj, gint32 x, DBusGMethodInvocation *context);
 
-gboolean my_object_async_throw_error (MyObject *obj, DBusGMethodInvocation *context);
+void my_object_async_throw_error (MyObject *obj, DBusGMethodInvocation *context);
 
 #include "test-service-glib-glue.h"
 
@@ -283,6 +287,27 @@ my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error)
   return TRUE;
 }
 
+gint32
+my_object_increment_retval (MyObject *obj, gint32 x)
+{
+  return x + 1;
+}
+
+gint32
+my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error)
+{
+  if (x + 1 > 10)
+    {
+      g_set_error (error,
+                  MY_OBJECT_ERROR,
+                  MY_OBJECT_ERROR_FOO,
+                  "%s",
+                  "x is bigger than 9");    
+      return FALSE;
+    }
+  return x + 1;
+}
+
 gboolean
 my_object_throw_error (MyObject *obj, GError **error)
 {
@@ -559,14 +584,13 @@ do_async_increment (IncrementData *data)
   return FALSE;
 }
 
-gboolean
+void
 my_object_async_increment (MyObject *obj, gint32 x, DBusGMethodInvocation *context)
 {
   IncrementData *data = g_new0 (IncrementData, 1);
   data->x = x;
   data->context = context;
   g_idle_add ((GSourceFunc)do_async_increment, data);
-  return TRUE;
 }
 
 static gboolean
@@ -582,13 +606,12 @@ do_async_error (IncrementData *data)
   return FALSE;
 }
 
-gboolean
+void
 my_object_async_throw_error (MyObject *obj, DBusGMethodInvocation *context)
 {
   IncrementData *data = g_new0(IncrementData, 1);
   data->context = context;
   g_idle_add ((GSourceFunc)do_async_error,  data);
-  return TRUE;
 }
 
 
@@ -623,11 +646,16 @@ main (int argc, char **argv)
 
   g_printerr ("Launching test-service-glib\n");
 
-  g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
-  g_log_set_always_fatal (G_LOG_LEVEL_WARNING);
-  
   loop = g_main_loop_new (NULL, FALSE);
 
+  {
+    GLogLevelFlags fatal_mask;
+    
+    fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+    fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+    g_log_set_always_fatal (fatal_mask);
+  }
+
   error = NULL;
   connection = dbus_g_bus_get (DBUS_BUS_STARTER,
                                &error);
index 84524a109e32b58b5bd3e24a874f3c554d611cf0..4ea2b2bbe3bfa7b9651cfc6d5f2f17b7b4eb7ae3 100644 (file)
       <arg type="u" direction="out" />
     </method>
 
+    <method name="IncrementRetval">
+      <arg type="u" name="x" />
+      <arg type="u" direction="out">
+       <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/>
+      </arg>
+    </method>
+
+    <method name="IncrementRetvalError">
+      <arg type="u" name="x" />
+      <arg type="u" direction="out">
+       <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/>
+      </arg>
+    </method>
+
     <method name="ThrowError">
     </method>