* configure.in: Add test/name-test/Makefile to the generated
[platform/upstream/dbus.git] / glib / dbus-gobject.c
index da5da3b..0a019cc 100644 (file)
@@ -2,6 +2,7 @@
 /* dbus-gobject.c Exporting a GObject remotely
  *
  * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2005 Nokia
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -28,6 +29,7 @@
 #include "dbus-gtest.h"
 #include "dbus-gutils.h"
 #include "dbus-gobject.h"
+#include "dbus-gsignature.h"
 #include "dbus-gvalue.h"
 #include "dbus-gmarshal.h"
 #include "dbus-gvalue-utils.h"
  * @{
  */
 
+typedef struct
+{
+  char *default_iface;
+  GType code_enum;
+} DBusGErrorInfo;
+
 static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
-static GHashTable *info_hash = NULL;
 static GHashTable *marshal_table = NULL;
+static GData *error_metadata = NULL;
 
 static char*
 uscore_to_wincaps (const char *uscore)
@@ -136,28 +144,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,
-            const char **type)
+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");
+      g_warning ("invalid arg direction '%c'", *data);
+      inarg = FALSE;
       break;
     }
+  if (in)
+    *in = inarg;
+
+  if (!inarg)
+    {
+      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);
 }
@@ -180,7 +251,7 @@ method_dir_signature_from_object_info (const DBusGObjectInfo *object,
       gboolean arg_in;
       const char *type;
 
-      arg = arg_iterate (arg, &name, &arg_in, &type);
+      arg = arg_iterate (arg, &name, &arg_in, NULL, NULL, &type);
 
       if (arg_in == in)
        g_string_append (ret, type);
@@ -203,160 +274,96 @@ method_output_signature_from_object_info (const DBusGObjectInfo *object,
   return method_dir_signature_from_object_info (object, method, FALSE);
 }
 
-static void
-gobject_unregister_function (DBusConnection  *connection,
-                             void            *user_data)
+static const char *
+propsig_iterate (const char *data, const char **iface, const char **name)
 {
-  GObject *object;
-
-  object = G_OBJECT (user_data);
+  *iface = data;
 
-  /* FIXME */
+  data = string_table_next (data);
+  *name = data;
 
+  return string_table_next (data);
 }
 
-static void
-introspect_properties (GObject *object, GString *xml)
+static GQuark
+dbus_g_object_type_dbus_metadata_quark (void)
 {
-  unsigned int i;
-  unsigned int n_specs;
-  GType last_type;
-  GParamSpec **specs;
+  static GQuark quark;
 
-  last_type = G_TYPE_INVALID;
-  specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
-                                          &n_specs);
+  if (!quark)
+    quark = g_quark_from_static_string ("DBusGObjectTypeDBusMetadataQuark");
+  return quark;
+}
 
-  for (i = 0; i < n_specs; i++ )
+static const DBusGObjectInfo *
+lookup_object_info (GObject *object)
+{
+  const DBusGObjectInfo *ret;
+  GType classtype;
+  
+  ret = NULL;
+  
+  for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
     {
-      char *s;
-      const char *dbus_type;
-      gboolean can_set;
-      gboolean can_get;
-      GParamSpec *spec = specs[i];
-      
-      dbus_type = dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
-      if (dbus_type == NULL)
-       continue;
-      
-      if (spec->owner_type != last_type)
-       {
-          if (last_type != G_TYPE_INVALID)
-            g_string_append (xml, "  </interface>\n");
-
+      const DBusGObjectInfo *info;
 
-          /* FIXME what should the namespace on the interface be in
-           * general?  should people be able to set it for their
-           * objects?
-           */
-          g_string_append (xml, "  <interface name=\"org.gtk.objects.");
-          g_string_append (xml, g_type_name (spec->owner_type));
-          g_string_append (xml, "\">\n");
+      info = g_type_get_qdata (classtype, dbus_g_object_type_dbus_metadata_quark ()); 
 
-          last_type = spec->owner_type;
+      if (info != NULL && info->format_version >= 0)
+       {
+         ret = info;
+         break;
        }
-
-      can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
-                (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
-      
-      can_get = (spec->flags & G_PARAM_READABLE) != 0;
-
-      s = uscore_to_wincaps (spec->name);
-      
-      if (can_set || can_get)
-        {
-          g_string_append (xml, "    <property name=\"");
-          g_string_append (xml, s);
-          g_string_append (xml, "\" type=\"");
-          g_string_append (xml, dbus_type);
-          g_string_append (xml, "\" access=\"");
-
-          if (can_set && can_get)
-            g_string_append (xml, "readwrite");
-          else if (can_get)
-            g_string_append (xml, "read");
-          else
-            {
-              g_assert (can_set);
-              g_string_append (xml, "write");
-            }
-          
-          g_string_append (xml, "\"/>\n");
-        }
-      
-      g_free (s);
     }
 
-  if (last_type != G_TYPE_INVALID)
-    g_string_append (xml, "  </interface>\n");
-
-  g_free (specs);
+  return ret;
 }
 
 static void
-introspect_signals (GType type, GString *xml)
+gobject_unregister_function (DBusConnection  *connection,
+                             void            *user_data)
 {
-  guint i;
-  guint *ids, n_ids;
-
-  ids = g_signal_list_ids (type, &n_ids);
-  if (!n_ids)
-    return;
-
-  g_string_append (xml, "  <interface name=\"org.gtk.objects.");
-  g_string_append (xml, g_type_name (type));
-  g_string_append (xml, "\">\n");
-
-  /* FIXME: recurse to parent types ? */
-  for (i = 0; i < n_ids; i++)
-    {
-      guint arg;
-      GSignalQuery query;
-      
-      g_signal_query (ids[i], &query);
-
-      if (query.return_type != G_TYPE_NONE)
-       continue; /* FIXME: these could be listed as methods ? */
-
-      g_string_append (xml, "    <signal name=\"");
-      g_string_append (xml, query.signal_name);
-      g_string_append (xml, "\">\n");
-
-      for (arg = 0; arg < query.n_params; arg++)
-       {
-         const char *dbus_type = dbus_gtype_to_signature (query.param_types[arg]);
+  GObject *object;
 
-         if (!dbus_type)
-           continue;
-         
-          g_string_append (xml, "      <arg type=\"");
-          g_string_append (xml, dbus_type);
-          g_string_append (xml, "\"/>\n");
-       }
+  object = G_OBJECT (user_data);
 
-      g_string_append (xml, "    </signal>\n");
-    }
+  /* FIXME */
 
-  g_string_append (xml, "  </interface>\n");
 }
 
 typedef struct
 {
   GString *xml;
+  GType gtype;
   const DBusGObjectInfo *object_info;
-} DBusGlibWriteIterfaceData;
+} DBusGLibWriteIterfaceData;
+
+typedef struct
+{
+  GSList *methods;
+  GSList *signals;
+  GSList *properties;
+} DBusGLibWriteInterfaceValues;
 
 static void
 write_interface (gpointer key, gpointer val, gpointer user_data)
 {
   const char *name;
   GSList *methods;
+  GSList *signals;
+  GSList *properties;
   GString *xml;
   const DBusGObjectInfo *object_info;
-  DBusGlibWriteIterfaceData *data;
+  DBusGLibWriteIterfaceData *data;
+  DBusGLibWriteInterfaceValues *values;
 
   name = key;
-  methods = val;
+
+  values = val;
+  methods = values->methods;
+  signals = values->signals;
+  properties = values->properties;
+
   data = user_data;
   xml = data->xml;
   object_info = data->object_info;
@@ -367,8 +374,8 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
   for (; methods; methods = methods->next)
     {
       DBusGMethodInfo *method;
-      method = methods->data;
       const char *args;
+      method = methods->data;
 
       g_string_append_printf (xml, "    <method name=\"%s\">\n",
                              method_name_from_object_info (object_info, method));
@@ -381,7 +388,7 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
          gboolean arg_in;
          const char *type;
          
-         args = arg_iterate (args, &name, &arg_in, &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",
@@ -389,64 +396,180 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
 
        }
       g_string_append (xml, "    </method>\n");
+
+    }
+  g_slist_free (values->methods);
+
+  for (; signals; signals = signals->next)
+    {
+      guint id;
+      guint arg;
+      const char *signame;
+      GSignalQuery query;
+      char *s;
+
+      signame = signals->data;
+
+      s = _dbus_gutils_wincaps_to_uscore (signame);
+      
+      id = g_signal_lookup (s, data->gtype);
+      g_assert (id != 0);
+
+      g_signal_query (id, &query);
+      g_assert (query.return_type == G_TYPE_NONE);
+
+      g_string_append_printf (xml, "    <signal name=\"%s\">\n", signame);
+
+      for (arg = 0; arg < query.n_params; arg++)
+       {
+         char *dbus_type = _dbus_gtype_to_signature (query.param_types[arg]);
+
+         g_assert (dbus_type != NULL);
+
+          g_string_append (xml, "      <arg type=\"");
+          g_string_append (xml, dbus_type);
+          g_string_append (xml, "\"/>\n");
+         g_free (dbus_type);
+       }
+
+      g_string_append (xml, "    </signal>\n");
+      g_free (s);
+    }
+  g_slist_free (values->signals);
+
+  for (; properties; properties = properties->next)
+    {
+      const char *propname;
+      GParamSpec *spec;
+      char *dbus_type;
+      gboolean can_set;
+      gboolean can_get;
+      char *s;
+
+      propname = properties->data;
+      spec = NULL;
+
+      s = _dbus_gutils_wincaps_to_uscore (spec->name);
+
+      spec = g_object_class_find_property (g_type_class_peek (data->gtype), s);
+      g_assert (spec != NULL);
+      g_free (s);
+      
+      dbus_type = _dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
+      g_assert (dbus_type != NULL);
+      
+      can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
+                (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
+      
+      can_get = (spec->flags & G_PARAM_READABLE) != 0;
+      
+      if (can_set || can_get)
+       {
+         g_string_append_printf (xml, "    <property name=\"%s\" ", propname);
+         g_string_append (xml, "type=\"");
+         g_string_append (xml, dbus_type);
+         g_string_append (xml, "\" access=\"");
+
+         if (can_set && can_get)
+           g_string_append (xml, "readwrite");
+         else if (can_get)
+           g_string_append (xml, "read");
+         else
+           {
+             g_assert (can_set);
+             g_string_append (xml, "write");
+           }
+          
+         g_string_append (xml, "\"/>\n");
+       }
+      
+      g_free (dbus_type);
+      g_free (s);
+
+      g_string_append (xml, "    </property>\n");
     }
+  g_slist_free (values->properties);
 
+  g_free (values);
   g_string_append (xml, "  </interface>\n");
 }
 
+static DBusGLibWriteInterfaceValues *
+lookup_values (GHashTable *interfaces, const char *method_interface)
+{
+  DBusGLibWriteInterfaceValues *values;
+  if ((values = g_hash_table_lookup (interfaces, (gpointer) method_interface)) == NULL)
+    {
+      values = g_new0 (DBusGLibWriteInterfaceValues, 1);
+      g_hash_table_insert (interfaces, (gpointer) method_interface, values);
+    }
+  return values;
+}
+
 static void
 introspect_interfaces (GObject *object, GString *xml)
 {
-  GType classtype;
+  const DBusGObjectInfo *info;
+  DBusGLibWriteIterfaceData data;
+  int i;
+  GHashTable *interfaces;
+  DBusGLibWriteInterfaceValues *values;
+  const char *propsig;
 
-  g_static_rw_lock_reader_lock (&globals_lock);
+  info = lookup_object_info (object);
 
-  for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
+  g_assert (info != NULL);
+
+  /* Gather a list of all interfaces, indexed into their methods */
+  interfaces = g_hash_table_new (g_str_hash, g_str_equal);
+  for (i = 0; i < info->n_method_infos; i++)
     {
-      const DBusGObjectInfo *info;
-      DBusGlibWriteIterfaceData data;
+      const char *method_name;
+      const char *method_interface;
+      const char *method_args;
+      const DBusGMethodInfo *method;
 
-      info = g_hash_table_lookup (info_hash,
-                                 g_type_class_peek (classtype));
+      method = &(info->method_infos[i]);
 
-      if (info != NULL && info->format_version == 0)
-       {
-         int i;
-         GHashTable *interfaces;
+      method_interface = method_interface_from_object_info (info, method);
+      method_name = method_name_from_object_info (info, method);
+      method_args = method_arg_info_from_object_info (info, method);
 
-         /* Gather a list of all interfaces, indexed into their methods */
-         interfaces = g_hash_table_new (g_str_hash, g_str_equal);
-         for (i = 0; i < info->n_infos; i++)
-           {
-             const char *method_name;
-             const char *method_interface;
-             const char *method_args;
-             const DBusGMethodInfo *method;
-             GSList *methods;
-
-             method = &(info->infos[i]);
-
-             method_interface = method_interface_from_object_info (info, method);
-             method_name = method_name_from_object_info (info, method);
-             method_args = method_arg_info_from_object_info (info, method);
-
-             if ((methods = g_hash_table_lookup (interfaces, method_interface)) == NULL)
-                 methods = g_slist_prepend (NULL, (gpointer) method);
-             else
-                 methods = g_slist_prepend (methods, (gpointer) method);
-             g_hash_table_insert (interfaces, (gpointer) method_interface, methods);
-           }
+      values = lookup_values (interfaces, method_interface);
+      values->methods = g_slist_prepend (values->methods, (gpointer) method);
+    }
 
-         memset (&data, 0, sizeof (data));
-         data.xml = xml;
-         data.object_info = info;
-         g_hash_table_foreach (interfaces, write_interface, &data);
+  propsig = info->exported_signals;
+  while (*propsig)
+    {
+      const char *iface;
+      const char *signame;
 
-         g_hash_table_destroy (interfaces);
-       }
+      propsig = propsig_iterate (propsig, &iface, &signame);
+
+      values = lookup_values (interfaces, iface);
+      values->signals = g_slist_prepend (values->signals, (gpointer) signame);
     }
 
-  g_static_rw_lock_reader_unlock (&globals_lock);
+  propsig = info->exported_properties;
+  while (*propsig)
+    {
+      const char *iface;
+      const char *propname;
+
+      propsig = propsig_iterate (propsig, &iface, &propname);
+
+      values = lookup_values (interfaces, iface);
+      values->properties = g_slist_prepend (values->properties, (gpointer) propname);
+    }
+  
+  memset (&data, 0, sizeof (data));
+  data.xml = xml;
+  data.gtype = G_TYPE_FROM_INSTANCE (object);
+  data.object_info = info;
+  g_hash_table_foreach (interfaces, write_interface, &data);
+  
+  g_hash_table_destroy (interfaces);
 }
 
 static DBusHandlerResult
@@ -491,8 +614,6 @@ handle_introspect (DBusConnection *connection,
   g_string_append (xml, "    </method>\n");
   g_string_append (xml, "  </interface>\n");
   
-  introspect_signals (G_OBJECT_TYPE (object), xml);
-  introspect_properties (object, xml);
   introspect_interfaces (object, xml);
 
   /* Append child nodes */
@@ -539,7 +660,7 @@ set_object_property (DBusConnection  *connection,
   context.proxy = NULL;
 
   g_value_init (&value, pspec->value_type);
-  if (dbus_gvalue_demarshal (&context, &sub, &value, NULL))
+  if (_dbus_gvalue_demarshal (&context, &sub, &value, NULL))
     {
       g_object_set_property (object,
                              pspec->name,
@@ -587,7 +708,7 @@ get_object_property (DBusConnection *connection,
 
   dbus_message_iter_init_append (message, &iter);
 
-  if (!dbus_gvalue_marshal (&iter, &value))
+  if (!_dbus_gvalue_marshal (&iter, &value))
     {
       dbus_message_unref (ret);
       ret = dbus_message_new_error (message,
@@ -604,69 +725,53 @@ lookup_object_and_method (GObject      *object,
                          const DBusGObjectInfo **object_ret,
                          const DBusGMethodInfo **method_ret)
 {
-  GType classtype;
   const char *interface;
   const char *member;
   const char *signature;
   gboolean ret;
+  const DBusGObjectInfo *info;
+  int i;
 
   interface = dbus_message_get_interface (message);
   member = dbus_message_get_member (message);
   signature = dbus_message_get_signature (message);
   ret = FALSE;
 
-  g_static_rw_lock_reader_lock (&globals_lock);
-
-  if (!info_hash)
-    goto out;
+  info = lookup_object_info (object);
+  *object_ret = info;
   
-  for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
+  for (i = 0; i < info->n_method_infos; i++)
     {
-      const DBusGObjectInfo *info;
-
-      info = g_hash_table_lookup (info_hash,
-                                 g_type_class_peek (classtype));
-
-      *object_ret = info;
-
-      if (info != NULL && info->format_version == 0)
+      const char *expected_member;
+      const char *expected_interface;
+      char *expected_signature;
+      const DBusGMethodInfo *method;
+
+      method = &(info->method_infos[i]);
+
+      /* Check method interface/name and input signature */ 
+      expected_interface = method_interface_from_object_info (*object_ret, method);
+      expected_member = method_name_from_object_info (*object_ret, method);
+      expected_signature = method_input_signature_from_object_info (*object_ret, method);
+
+      if ((interface == NULL
+          || strcmp (expected_interface, interface) == 0)
+         && strcmp (expected_member, member) == 0
+         && strcmp (expected_signature, signature) == 0)
        {
-         int i;
-         for (i = 0; i < info->n_infos; i++)
-           {
-             const char *expected_member;
-             const char *expected_interface;
-             char *expected_signature;
-             const DBusGMethodInfo *method;
-
-             method = &(info->infos[i]);
-
-             /* Check method interface/name and input signature */ 
-             expected_interface = method_interface_from_object_info (*object_ret, method);
-             expected_member = method_name_from_object_info (*object_ret, method);
-             expected_signature = method_input_signature_from_object_info (*object_ret, method);
-
-             if ((interface == NULL
-                  || strcmp (expected_interface, interface) == 0)
-                 && strcmp (expected_member, member) == 0
-                 && strcmp (expected_signature, signature) == 0)
-               {
-                 g_free (expected_signature);
-                 *method_ret = method;
-                 ret = TRUE;
-                 goto out;
-               }
-             g_free (expected_signature);
-           }
+         g_free (expected_signature);
+         *method_ret = method;
+         return TRUE;
        }
+      g_free (expected_signature);
     }
- out:
-  g_static_rw_lock_reader_unlock (&globals_lock);
+
   return ret;
 }
 
 static char *
 gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info,
+                                     const char *msg_interface,
                                      GQuark domain, gint code)
 {
   const char *domain_str;
@@ -678,6 +783,36 @@ gerror_domaincode_to_dbus_error_name (const DBusGObjectInfo *object_info,
 
   if (!domain_str || !code_str)
     {
+      DBusGErrorInfo *info;
+
+      g_static_rw_lock_reader_lock (&globals_lock);
+
+      if (error_metadata != NULL)
+       info = g_datalist_id_get_data (&error_metadata, domain);
+      else
+       info = NULL;
+
+      g_static_rw_lock_reader_unlock (&globals_lock);
+
+      if (info)
+       {
+         GEnumValue *value;
+         GEnumClass *klass;
+
+         klass = g_type_class_ref (info->code_enum);
+         value = g_enum_get_value (klass, code);
+         g_type_class_unref (klass);
+
+         domain_str = info->default_iface;
+         code_str = value->value_nick;
+       }
+    }
+
+  if (!domain_str)
+    domain_str = msg_interface;
+
+  if (!domain_str || !code_str)
+    {
       /* If we can't map it sensibly, make up an error name */
       char *domain_from_quark;
       
@@ -716,10 +851,19 @@ gerror_to_dbus_error_message (const DBusGObjectInfo *object_info,
     }
   else
     {
-      char *error_name;
-      error_name = gerror_domaincode_to_dbus_error_name (object_info, error->domain, error->code);
-      reply = dbus_message_new_error (message, error_name, error->message);
-      g_free (error_name); 
+      if (error->domain == DBUS_GERROR)
+       reply = dbus_message_new_error (message,
+                                       dbus_g_error_get_name (error),
+                                       error->message);
+      else
+       {
+         char *error_name;
+         error_name = gerror_domaincode_to_dbus_error_name (object_info,
+                                                            dbus_message_get_interface (message),
+                                                            error->domain, error->code);
+         reply = dbus_message_new_error (message, error_name, error->message);
+         g_free (error_name); 
+       }
     }
   return reply;
 }
@@ -728,7 +872,7 @@ gerror_to_dbus_error_message (const DBusGObjectInfo *object_info,
  * The context of an asynchronous method call.  See dbus_g_method_return() and
  * dbus_g_method_return_error().
  */
-struct DBusGMethodInvocation {
+struct _DBusGMethodInvocation {
   DBusGConnection *connection; /**< The connection */
   DBusGMessage *message; /**< The message which generated the method call */
   const DBusGObjectInfo *object; /**< The object the method was called on */
@@ -745,28 +889,36 @@ invoke_object_method (GObject         *object,
   gboolean had_error, call_only;
   GError *gerror;
   GValueArray *value_array;
-  GValue object_value = {0,};
-  GValue error_value = {0,};
   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;
 
+  /* Determine whether or not this method should be invoked in a new
+     thread
+   */
   if (strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0)
     call_only = TRUE;
   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
@@ -787,11 +939,11 @@ invoke_object_method (GObject         *object,
     context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
     context.proxy = NULL;
 
-    types_array = dbus_gtypes_from_arg_signature (in_signature, FALSE);
+    types_array = _dbus_gtypes_from_arg_signature (in_signature, FALSE);
     n_params = types_array->len;
     types = (const GType*) types_array->data;
 
-    value_array = dbus_gvalue_demarshal_message (&context, message, n_params, types, &error);
+    value_array = _dbus_gvalue_demarshal_message (&context, message, n_params, types, &error);
     if (value_array == NULL)
       {
        g_free (in_signature); 
@@ -806,10 +958,10 @@ invoke_object_method (GObject         *object,
   }
 
   /* Prepend object as first argument */ 
-  g_value_init (&object_value, G_TYPE_OBJECT);
-  g_value_set_object (&object_value, object);
-  g_value_array_prepend (value_array, &object_value);
-
+  g_value_array_prepend (value_array, NULL);
+  g_value_init (g_value_array_get_nth (value_array, 0), G_TYPE_OBJECT);
+  g_value_set_object (g_value_array_get_nth (value_array, 0), object);
+  
   if (call_only)
     {
       GValue context_value = {0,};
@@ -825,21 +977,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);
 
@@ -849,16 +1052,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);
@@ -872,17 +1091,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 */
-      g_value_init (&error_value, G_TYPE_POINTER);
-      g_value_set_pointer (&error_value, &gerror);
-      g_value_array_append (value_array, &error_value);
+  /* 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);
@@ -891,7 +1113,10 @@ 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)
     {
@@ -901,19 +1126,54 @@ invoke_object_method (GObject         *object,
       if (reply == NULL)
        goto nomem;
 
-      /* Append OUT arguments to reply */
+      /* Append output arguments to reply */
       dbus_message_iter_init_append (reply, &iter);
-      dbus_signature_iter_init (&out_signature_iter, out_signature);
+
+      /* 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 ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
+      while (*arg_metadata)
        {
          GValue gvalue = {0, };
+         const char *arg_name;
+         gboolean arg_in;
+         gboolean constval;
+         RetvalType retval;
+         const char *arg_signature;
+         DBusSignatureIter argsigiter;
+
+         do
+           {
+             /* 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 || retval != RETVAL_NONE) && *arg_metadata);
+
+         /* If the last argument we saw was input or the return
+          * value, we must be done iterating over output arguments.
+          */
+         if (arg_in || retval != RETVAL_NONE)
+           break;
+
+         dbus_signature_iter_init (&argsigiter, arg_signature);
          
-         g_value_init (&gvalue, dbus_gtype_from_signature_iter (&out_signature_iter, FALSE));
-         if (current_type != DBUS_TYPE_VARIANT)
+         g_value_init (&gvalue, _dbus_gtype_from_signature_iter (&argsigiter, FALSE));
+         if (G_VALUE_TYPE (&gvalue) != G_TYPE_VALUE)
            {
-             if (!dbus_gvalue_take (&gvalue,
+             if (!_dbus_gvalue_take (&gvalue,
                                     &(g_array_index (out_param_values, GTypeCValue, out_param_pos))))
                g_assert_not_reached ();
              out_param_pos++;
@@ -924,13 +1184,14 @@ invoke_object_method (GObject         *object,
              out_param_gvalue_pos++;
            }
              
-         if (!dbus_gvalue_marshal (&iter, &gvalue))
+         if (!_dbus_gvalue_marshal (&iter, &gvalue))
            goto nomem;
          /* Here we actually free the allocated value; we
-          * took ownership of it with dbus_gvalue_take.
+          * took ownership of it with _dbus_gvalue_take, unless
+          * an annotation has specified this value as constant.
           */
-         g_value_unset (&gvalue);
-         dbus_signature_iter_next (&out_signature_iter);
+         if (!constval)
+           g_value_unset (&gvalue);
        }
     }
   else
@@ -945,16 +1206,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_unset (&object_value);
-      g_value_unset (&error_value);
     }
   g_value_array_free (value_array);
-  g_value_unset (&return_value);
   return result;
  nomem:
   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -1073,7 +1330,7 @@ gobject_message_function (DBusConnection  *connection,
   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-static DBusObjectPathVTable gobject_dbus_vtable = {
+static const DBusObjectPathVTable gobject_dbus_vtable = {
   gobject_unregister_function,
   gobject_message_function,
   NULL
@@ -1083,13 +1340,15 @@ typedef struct {
   GClosure         closure;
   DBusGConnection *connection;
   GObject         *object;
-  char            *signame;
+  const char      *signame;
+  const char      *sigiface;
 } DBusGSignalClosure;
 
 static GClosure *
 dbus_g_signal_closure_new (DBusGConnection *connection,
                           GObject         *object,
-                          const char      *signame)
+                          const char      *signame,
+                          const char      *sigiface)
 {
   DBusGSignalClosure *closure;
   
@@ -1097,7 +1356,8 @@ dbus_g_signal_closure_new (DBusGConnection *connection,
 
   closure->connection = dbus_g_connection_ref (connection);
   closure->object = object;
-  closure->signame = g_strdup (signame);
+  closure->signame = signame;
+  closure->sigiface = sigiface;
   return (GClosure*) closure;
 }
 
@@ -1108,7 +1368,6 @@ dbus_g_signal_closure_finalize (gpointer data,
   DBusGSignalClosure *sigclosure = (DBusGSignalClosure *) closure;
 
   dbus_g_connection_unref (sigclosure->connection);
-  g_free (sigclosure->signame);
 }
 
 static void
@@ -1134,7 +1393,7 @@ signal_emitter_marshaller (GClosure        *closure,
   g_assert (path != NULL);
 
   signal = dbus_message_new_signal (path,
-                                   "org.gtk.objects",
+                                   sigclosure->sigiface,
                                    sigclosure->signame);
   if (!signal)
     {
@@ -1147,7 +1406,7 @@ signal_emitter_marshaller (GClosure        *closure,
   /* First argument is the object itself, and we can't marshall that */
   for (i = 1; i < n_param_values; i++)
     {
-      if (!dbus_gvalue_marshal (&iter,
+      if (!_dbus_gvalue_marshal (&iter,
                                (GValue *) (&(param_values[i]))))
        {
          g_warning ("failed to marshal parameter %d for signal %s",
@@ -1162,43 +1421,96 @@ signal_emitter_marshaller (GClosure        *closure,
 }
 
 static void
-export_signals (DBusGConnection *connection, GObject *object)
+export_signals (DBusGConnection *connection, const DBusGObjectInfo *info, GObject *object)
 {
-  guint i;
-  guint *ids, n_ids;
+  GType gtype;
+  const char *sigdata;
+  const char *iface;
+  const char *signame;
 
-  ids = g_signal_list_ids (G_TYPE_FROM_INSTANCE (object), &n_ids);
-  if (!n_ids)
-    return;
+  gtype = G_TYPE_FROM_INSTANCE (object);
 
-  /* FIXME: recurse to parent types ? */
-  for (i = 0; i < n_ids; i++)
+  sigdata = info->exported_signals;
+  
+  while (*sigdata != '\0')
     {
+      guint id;
       GSignalQuery query;
       GClosure *closure;
+      char *s;
+
+      sigdata = propsig_iterate (sigdata, &iface, &signame);
       
-      g_signal_query (ids[i], &query);
+      s = _dbus_gutils_wincaps_to_uscore (signame);
+
+      id = g_signal_lookup (s, gtype);
+      if (id == 0)
+       {
+         g_warning ("signal \"%s\" (from \"%s\") exported but not found in object class \"%s\"",
+                    s, signame, g_type_name (gtype));
+         g_free (s);
+         continue;
+       }
+
+      g_signal_query (id, &query);
 
       if (query.return_type != G_TYPE_NONE)
        {
-         g_warning("Not exporting signal '%s' as it has a return type %s", query.signal_name, g_type_name (query.return_type));
+         g_warning ("Not exporting signal \"%s\" for object class \"%s\" as it has a return type \"%s\"",
+                    s, g_type_name (gtype), g_type_name (query.return_type));
+         g_free (s);
          continue; /* FIXME: these could be listed as methods ? */
        }
       
-      closure = dbus_g_signal_closure_new (connection, object, query.signal_name);
+      closure = dbus_g_signal_closure_new (connection, object, signame, (char*) iface);
       g_closure_set_marshal (closure, signal_emitter_marshaller);
 
       g_signal_connect_closure_by_id (object,
-                                     ids[i],
+                                     id,
                                      0,
                                      closure,
                                      FALSE);
 
       g_closure_add_finalize_notifier (closure, NULL,
                                       dbus_g_signal_closure_finalize);
+      g_free (s);
     }
 }
 
+#include "dbus-glib-error-switch.h"
+
+void
+dbus_set_g_error (GError    **gerror,
+                 DBusError  *error)
+{
+  int code;
+
+  code = dbus_error_to_gerror_code (error->name);
+  if (code != DBUS_GERROR_REMOTE_EXCEPTION)
+    g_set_error (gerror, DBUS_GERROR,
+                code,
+                "%s",
+                error->message);
+  else
+    g_set_error (gerror, DBUS_GERROR,
+                code,
+                "%s%c%s",
+                error->message ? error->message : "",
+                '\0',
+                error->name);
+}
+
+static void
+dbus_g_error_info_free (gpointer p)
+{
+  DBusGErrorInfo *info;
+
+  info = p;
+
+  g_free (info->default_iface);
+  g_free (info);
+}
+
 /** @} */ /* end of internals */
 
 /**
@@ -1224,24 +1536,58 @@ void
 dbus_g_object_type_install_info (GType                  object_type,
                                 const DBusGObjectInfo *info)
 {
-  GObjectClass *object_class;
-
-  g_return_if_fail (G_TYPE_IS_OBJECT (object_type));
+  g_return_if_fail (G_TYPE_IS_CLASSED (object_type));
 
-  dbus_g_value_types_init ();
+  _dbus_g_value_types_init ();
 
-  object_class = g_type_class_peek (object_type);
+  g_type_set_qdata (object_type,
+                   dbus_g_object_type_dbus_metadata_quark (),
+                   (gpointer) info);
+}
 
-  g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
+/**
+ * Register a GError domain and set of codes with D-BUS.  You must
+ * have created a GEnum for the error codes.  This function will not
+ * be needed with an introspection-capable GLib.
+ *
+ * @param domain the GError domain 
+ * @param default_iface the D-BUS interface used for error values by default, or #NULL
+ * @param code_enum a GType for a GEnum of the error codes
+ */
+void
+dbus_g_error_domain_register (GQuark                domain,
+                             const char           *default_iface,
+                             GType                 code_enum)
+{
+  DBusGErrorInfo *info;
+  
+  g_return_if_fail (g_quark_to_string (domain) != NULL);
+  g_return_if_fail (code_enum != G_TYPE_INVALID);
+  g_return_if_fail (G_TYPE_FUNDAMENTAL (code_enum) == G_TYPE_ENUM);
 
   g_static_rw_lock_writer_lock (&globals_lock);
 
-  if (info_hash == NULL)
+  if (error_metadata == NULL)
+    g_datalist_init (&error_metadata);
+
+  info = g_datalist_id_get_data (&error_metadata, domain);
+
+  if (info != NULL)
     {
-      info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
+      g_warning ("Metadata for error domain \"%s\" already registered\n",
+                g_quark_to_string (domain));
+    }
+  else
+    {
+      info = g_new0 (DBusGErrorInfo, 1);
+      info->default_iface = g_strdup (default_iface);
+      info->code_enum = code_enum;
+
+      g_datalist_id_set_data_full (&error_metadata,
+                                  domain,
+                                  info,
+                                  dbus_g_error_info_free);
     }
-
-  g_hash_table_replace (info_hash, object_class, (void*) info);
 
   g_static_rw_lock_writer_unlock (&globals_lock);
 }
@@ -1273,10 +1619,19 @@ dbus_g_connection_register_g_object (DBusGConnection       *connection,
                                      const char            *at_path,
                                      GObject               *object)
 {
+  const DBusGObjectInfo *info;
   g_return_if_fail (connection != NULL);
   g_return_if_fail (at_path != NULL);
   g_return_if_fail (G_IS_OBJECT (object));
 
+  info = lookup_object_info (object);
+  if (info == NULL)
+    {
+      g_warning ("No introspection data registered for object class \"%s\"",
+                g_type_name (G_TYPE_FROM_INSTANCE (object)));
+      return;
+    }
+
   if (!dbus_connection_register_object_path (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
                                              at_path,
                                              &gobject_dbus_vtable,
@@ -1286,7 +1641,7 @@ dbus_g_connection_register_g_object (DBusGConnection       *connection,
       return;
     }
 
-  export_signals (connection, object);
+  export_signals (connection, info, object);
 
   g_object_set_data (object, "dbus_glib_object_path", g_strdup (at_path));
   g_object_weak_ref (object, (GWeakNotify)unregister_gobject, connection);
@@ -1314,11 +1669,12 @@ funcsig_hash (gconstpointer key)
   const DBusGFuncSignature *sig = key;
   GType *types;
   guint ret;
+  guint i;
 
   ret = sig->rettype;
   types = sig->params;
 
-  while (*types != G_TYPE_INVALID)
+  for (i = 0; i < sig->n_params; i++)
     {
       ret += (int) (*types);
       types++;
@@ -1335,22 +1691,22 @@ funcsig_equal (gconstpointer aval,
   const DBusGFuncSignature *b = bval;
   const GType *atypes;
   const GType *btypes;
+  guint i;
 
-  if (a->rettype != b->rettype)
+  if (a->rettype != b->rettype
+      || a->n_params != b->n_params)
     return FALSE;
 
   atypes = a->params;
   btypes = b->params;
 
-  while (*atypes != G_TYPE_INVALID)
+  for (i = 0; i < a->n_params; i++)
     {
       if (*btypes != *atypes)
        return FALSE;
       atypes++;
       btypes++;
     }
-  if (*btypes != G_TYPE_INVALID)
-    return FALSE;
       
   return TRUE;
 }
@@ -1362,10 +1718,18 @@ _dbus_gobject_lookup_marshaller (GType        rettype,
 {
   GClosureMarshal ret;
   DBusGFuncSignature sig;
+  GType *params;
+  guint i;
+
+  /* Convert to fundamental types */
+  rettype = G_TYPE_FUNDAMENTAL (rettype);
+  params = g_new (GType, n_params);
+  for (i = 0; i < n_params; i++)
+    params[i] = G_TYPE_FUNDAMENTAL (param_types[i]);
 
   sig.rettype = rettype;
   sig.n_params = n_params;
-  sig.params = (GType*) param_types;
+  sig.params = params;
   
   g_static_rw_lock_reader_lock (&globals_lock);
 
@@ -1384,7 +1748,7 @@ _dbus_gobject_lookup_marshaller (GType        rettype,
            ret = g_cclosure_marshal_VOID__VOID;
          else if (n_params == 1)
            {
-             switch (param_types[0])
+             switch (params[0])
                {
                case G_TYPE_BOOLEAN:
                  ret = g_cclosure_marshal_VOID__BOOLEAN;
@@ -1404,38 +1768,77 @@ _dbus_gobject_lookup_marshaller (GType        rettype,
                case G_TYPE_STRING:
                  ret = g_cclosure_marshal_VOID__STRING;
                  break;
+               case G_TYPE_BOXED:
+                 ret = g_cclosure_marshal_VOID__BOXED;
+                 break;
                }
            }
-       }
-      else if (n_params == 3
-              && param_types[0] == G_TYPE_STRING
-              && param_types[1] == G_TYPE_STRING
-              && param_types[2] == G_TYPE_STRING)
-       {
-         ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
+         else if (n_params == 3
+                  && params[0] == G_TYPE_STRING
+                  && params[1] == G_TYPE_STRING
+                  && params[2] == G_TYPE_STRING)
+           {
+             ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
+           }
        }
     }
 
+  g_free (params);
   return ret;
 }
 
 /**
- * Register a GClosureMarshal to be used for signal invocations.
+ * Register a GClosureMarshal to be used for signal invocations,
+ * giving its return type and a list of parameter types,
+ * followed by G_TYPE_INVALID.
+
  * This function will not be needed once GLib includes libffi.
  *
+ * @param marshaller a GClosureMarshal to be used for invocation
+ * @param rettype a GType for the return type of the function
+ * @param ... The parameter GTypes, followed by G_TYPE_INVALID
+ */
+void
+dbus_g_object_register_marshaller (GClosureMarshal  marshaller,
+                                  GType            rettype,
+                                  ...)
+{
+  va_list args;
+  GArray *types;
+  GType gtype;
+
+  va_start (args, rettype);
+
+  types = g_array_new (TRUE, TRUE, sizeof (GType));
+
+  while ((gtype = va_arg (args, GType)) != G_TYPE_INVALID)
+    g_array_append_val (types, gtype);
+
+  dbus_g_object_register_marshaller_array (marshaller, rettype,
+                                          types->len, (GType*) types->data);
+
+  g_array_free (types, TRUE);
+  va_end (args);
+}
+
+/**
+ * Register a GClosureMarshal to be used for signal invocations.
+ * See also #dbus_g_object_register_marshaller
+ *
+ * @param marshaller a GClosureMarshal to be used for invocation
  * @param rettype a GType for the return type of the function
  * @param n_types number of function parameters
- * @param param_types a C array of GTypes values
- * @param marshaller a GClosureMarshal to be used for invocation
+ * @param types a C array of GTypes values
  */
 void
-dbus_g_object_register_marshaller (GType            rettype,
-                                  guint            n_types,
-                                  const GType      *param_types,
-                                  GClosureMarshal   marshaller)
+dbus_g_object_register_marshaller_array (GClosureMarshal  marshaller,
+                                        GType            rettype,
+                                        guint            n_types,
+                                        const GType*     types)
 {
   DBusGFuncSignature *sig;
-  
+  guint i;
+
   g_static_rw_lock_writer_lock (&globals_lock);
 
   if (marshal_table == NULL)
@@ -1444,10 +1847,11 @@ dbus_g_object_register_marshaller (GType            rettype,
                                           g_free,
                                           NULL);
   sig = g_new0 (DBusGFuncSignature, 1);
-  sig->rettype = rettype;
+  sig->rettype = G_TYPE_FUNDAMENTAL (rettype);
   sig->n_params = n_types;
   sig->params = g_new (GType, n_types);
-  memcpy (sig->params, param_types, n_types * sizeof (GType));
+  for (i = 0; i < n_types; i++)
+    sig->params[i] = G_TYPE_FUNDAMENTAL (types[i]);
 
   g_hash_table_insert (marshal_table, sig, marshaller);
 
@@ -1455,7 +1859,64 @@ dbus_g_object_register_marshaller (GType            rettype,
 }
 
 /**
+ * Get the sender of a message so we can send a
+ * "reply" later (i.e. send a message directly
+ * to a service which invoked the method at a 
+ * later time).
+ *
+ * @param context the method context
+ *
+ * @return the unique name of teh sender
+ */
+gchar *
+dbus_g_method_get_sender (DBusGMethodInvocation *context)
+{
+  const gchar *sender;
+
+  sender = dbus_message_get_sender (dbus_g_message_get_message (context->message));
+
+  if (sender == NULL)
+    return NULL;
+    
+  return strdup (sender);
+}
+
+/**
+ * Get the reply message to append reply values
+ * Used as a sidedoor when you can't generate dbus values
+ * of the correct type due to glib binding limitations
+ *
+ * @param context the method context
+ */
+DBusMessage *
+dbus_g_method_get_reply (DBusGMethodInvocation *context)
+{
+  return dbus_message_new_method_return (dbus_g_message_get_message (context->message));
+}
+
+/**
+ * Send a manually created reply message
+ * Used as a sidedoor when you can't generate dbus values
+ * of the correct type due to glib binding limitations
+ *
+ * @param context the method context
+ * @param reply the reply message, will be unreffed
+ */
+void
+dbus_g_method_send_reply (DBusGMethodInvocation *context, DBusMessage *reply)
+{
+  dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+  dbus_message_unref (reply);
+
+  dbus_g_connection_unref (context->connection);
+  dbus_g_message_unref (context->message);
+  g_free (context);
+}
+
+
+/**
  * Send a return message for a given method invocation, with arguments.
+ * This function also frees the sending context.
  *
  * @param context the method context
  */
@@ -1471,7 +1932,7 @@ dbus_g_method_return (DBusGMethodInvocation *context, ...)
 
   reply = dbus_message_new_method_return (dbus_g_message_get_message (context->message));
   out_sig = method_output_signature_from_object_info (context->object, context->method);
-  argsig = dbus_gtypes_from_arg_signature (out_sig, FALSE);
+  argsig = _dbus_gtypes_from_arg_signature (out_sig, FALSE);
 
   dbus_message_iter_init_append (reply, &iter);
 
@@ -1482,13 +1943,13 @@ dbus_g_method_return (DBusGMethodInvocation *context, ...)
       char *error;
       g_value_init (&value, g_array_index (argsig, GType, i));
       error = NULL;
-      G_VALUE_COLLECT (&value, args, 0, &error);
+      G_VALUE_COLLECT (&value, args, G_VALUE_NOCOPY_CONTENTS, &error);
       if (error)
        {
          g_warning(error);
          g_free (error);
        }
-      dbus_gvalue_marshal (&iter, &value);
+      _dbus_gvalue_marshal (&iter, &value);
     }
   va_end (args);
 
@@ -1498,10 +1959,12 @@ dbus_g_method_return (DBusGMethodInvocation *context, ...)
   dbus_g_connection_unref (context->connection);
   dbus_g_message_unref (context->message);
   g_free (context);
+  g_free (out_sig);
 }
 
 /**
  * Send a error message for a given method invocation.
+ * This function also frees the sending context.
  *
  * @param context the method context
  * @param error the error to send.
@@ -1513,6 +1976,7 @@ dbus_g_method_return_error (DBusGMethodInvocation *context, GError *error)
   reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
   dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
   dbus_message_unref (reply);
+  g_free (context);
 }
 
 /** @} */ /* end of public API */
@@ -1525,6 +1989,57 @@ const char * _dbus_gobject_get_path (GObject *obj)
 #ifdef DBUS_BUILD_TESTS
 #include <stdlib.h>
 
+static void
+_dummy_function (void)
+{
+}
+
+/* Data structures copied from one generated by current dbus-binding-tool;
+ * we need to support this layout forever
+ */
+static const DBusGMethodInfo dbus_glib_internal_test_methods[] = {
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 0 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 49 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 117 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 191 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 270 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 320 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 391 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 495 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 623 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 693 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 765 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 838 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 911 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 988 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1064 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1140 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1204 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1278 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1347 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1408 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1460 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1533 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1588 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1647 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1730 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1784 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1833 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1895 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1947 },
+  { (GCallback) _dummy_function, g_cclosure_marshal_VOID__VOID, 1999 },
+};
+
+const DBusGObjectInfo dbus_glib_internal_test_object_info = {
+  0,
+  dbus_glib_internal_test_methods,
+  30,
+"org.freedesktop.DBus.Tests.MyObject\0DoNothing\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Increment\0S\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetval\0S\0x\0I\0u\0arg1\0O\0F\0R\0u\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementRetvalError\0S\0x\0I\0u\0arg1\0O\0F\0E\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ThrowError\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Uppercase\0S\0arg0\0I\0s\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyArgs\0S\0x\0I\0u\0str\0I\0s\0trouble\0I\0d\0d_ret\0O\0F\0N\0d\0str_ret\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0ManyReturn\0S\0arg0\0O\0F\0N\0u\0arg1\0O\0F\0N\0s\0arg2\0O\0F\0N\0i\0arg3\0O\0F\0N\0u\0arg4\0O\0F\0N\0u\0arg5\0O\0C\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Stringify\0S\0val\0I\0v\0arg1\0O\0F\0N\0s\0\0org.freedesktop.DBus.Tests.MyObject\0Unstringify\0S\0val\0I\0s\0arg1\0O\0F\0N\0v\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive1\0S\0arg0\0I\0au\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0Recursive2\0S\0arg0\0I\0u\0arg1\0O\0F\0N\0au\0\0org.freedesktop.DBus.Tests.MyObject\0ManyUppercase\0S\0arg0\0I\0as\0arg1\0O\0F\0N\0as\0\0org.freedesktop.DBus.Tests.MyObject\0StrHashLen\0S\0arg0\0I\0a{ss}\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0SendCar\0S\0arg0\0I\0(suv)\0arg1\0O\0F\0N\0(uo)\0\0org.freedesktop.DBus.Tests.MyObject\0GetHash\0S\0arg0\0O\0F\0N\0a{ss}\0\0org.freedesktop.DBus.Tests.MyObject\0RecArrays\0S\0val\0I\0aas\0arg1\0O\0F\0N\0aau\0\0org.freedesktop.DBus.Tests.MyObject\0Objpath\0S\0arg0\0I\0o\0arg1\0O\0C\0N\0o\0\0org.freedesktop.DBus.Tests.MyObject\0GetObjs\0S\0arg0\0O\0F\0N\0ao\0\0org.freedesktop.DBus.Tests.MyObject\0IncrementVal\0S\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncIncrement\0A\0x\0I\0u\0arg1\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0AsyncThrowError\0A\0\0org.freedesktop.DBus.Tests.MyObject\0GetVal\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.MyObject\0ManyStringify\0S\0arg0\0I\0a{sv}\0arg1\0O\0F\0N\0a{sv}\0\0org.freedesktop.DBus.Tests.MyObject\0EmitFrobnicate\0S\0\0org.freedesktop.DBus.Tests.MyObject\0Terminate\0S\0\0org.freedesktop.DBus.Tests.FooObject\0GetValue\0S\0arg0\0O\0F\0N\0u\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignals\0S\0\0org.freedesktop.DBus.Tests.FooObject\0EmitSignal2\0S\0\0org.freedesktop.DBus.Tests.FooObject\0Terminate\0S\0\0\0",
+"org.freedesktop.DBus.Tests.MyObject\0Frobnicate\0org.freedesktop.DBus.Tests.FooObject\0Sig0\0org.freedesktop.DBus.Tests.FooObject\0Sig1\0org.freedesktop.DBus.Tests.FooObject\0Sig2\0\0",
+"\0"
+};
+
+
 /**
  * @ingroup DBusGLibInternals
  * Unit test for GLib GObject integration ("skeletons")
@@ -1534,6 +2049,16 @@ gboolean
 _dbus_gobject_test (const char *test_data_dir)
 {
   int i;
+  const char *arg;
+  const char *arg_name;
+  gboolean arg_in;
+  gboolean constval;
+  RetvalType retval;
+  const char *arg_signature;
+  const char *sigdata;
+  const char *iface;
+  const char *signame;
+  
   static struct { const char *wincaps; const char *uscore; } name_pairs[] = {
     { "SetFoo", "set_foo" },
     { "Foo", "foo" },
@@ -1544,6 +2069,95 @@ _dbus_gobject_test (const char *test_data_dir)
     /* { "FrobateUIHandler", "frobate_ui_handler" } */
   };
 
+  /* Test lookup in our hardcoded object info; if these tests fail
+   * then it likely means you changed the generated object info in an
+   * incompatible way and broke the lookup functions.  In that case
+   * you need to bump the version and use a new structure instead. */
+  /* DoNothing */
+  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+                                         &(dbus_glib_internal_test_methods[0]));
+  g_assert (*arg == '\0');
+
+  /* Increment */
+  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+                                         &(dbus_glib_internal_test_methods[1]));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (!strcmp (arg_name, "x"));
+  g_assert (arg_in == TRUE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (arg_in == FALSE);
+  g_assert (retval == RETVAL_NONE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg == '\0');
+
+  /* IncrementRetval */
+  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+                                         &(dbus_glib_internal_test_methods[2]));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (!strcmp (arg_name, "x"));
+  g_assert (arg_in == TRUE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (retval == RETVAL_NOERROR);
+  g_assert (arg_in == FALSE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg == '\0');
+
+  /* IncrementRetvalError */
+  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+                                         &(dbus_glib_internal_test_methods[3]));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (!strcmp (arg_name, "x"));
+  g_assert (arg_in == TRUE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (retval == RETVAL_ERROR);
+  g_assert (arg_in == FALSE);
+  g_assert (!strcmp (arg_signature, "u"));
+  g_assert (*arg == '\0');
+  
+  /* Stringify */
+  arg = method_arg_info_from_object_info (&dbus_glib_internal_test_object_info,
+                                         &(dbus_glib_internal_test_methods[8]));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (!strcmp (arg_name, "val"));
+  g_assert (arg_in == TRUE);
+  g_assert (!strcmp (arg_signature, "v"));
+  g_assert (*arg != '\0');
+  arg = arg_iterate (arg, &arg_name, &arg_in, &constval, &retval, &arg_signature);
+  g_assert (retval == RETVAL_NONE);
+  g_assert (arg_in == FALSE);
+  g_assert (!strcmp (arg_signature, "s"));
+  g_assert (*arg == '\0');
+
+  sigdata = dbus_glib_internal_test_object_info.exported_signals;
+  g_assert (*sigdata != '\0');
+  sigdata = propsig_iterate (sigdata, &iface, &signame);
+  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.MyObject"));
+  g_assert (!strcmp (signame, "Frobnicate"));
+  g_assert (*sigdata != '\0');
+  sigdata = propsig_iterate (sigdata, &iface, &signame);
+  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+  g_assert (!strcmp (signame, "Sig0"));
+  g_assert (*sigdata != '\0');
+  sigdata = propsig_iterate (sigdata, &iface, &signame);
+  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+  g_assert (!strcmp (signame, "Sig1"));
+  g_assert (*sigdata != '\0');
+  sigdata = propsig_iterate (sigdata, &iface, &signame);
+  g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
+  g_assert (!strcmp (signame, "Sig2"));
+  g_assert (*sigdata == '\0');
+  
+
   i = 0;
   while (i < (int) G_N_ELEMENTS (name_pairs))
     {