cleanup
[platform/upstream/glib.git] / gio / gdbusintrospection.c
index fd26d40..6d3ff93 100644 (file)
@@ -1,6 +1,6 @@
 /* GDBus - GLib D-Bus Library
  *
- * Copyright (C) 2008-2009 Red Hat, Inc.
+ * Copyright (C) 2008-2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
 #include "config.h"
 
 #include <stdlib.h>
-
-#include <glib/gi18n.h>
+#include <string.h>
 
 #include "gdbusintrospection.h"
 
+#include "glibintl.h"
+
 /**
  * SECTION:gdbusintrospection
- * @title: Introspection XML
- * @short_description: Parse and Generate Introspection XML
+ * @title: D-Bus Introspection Data
+ * @short_description: Node and interface description data structures
  * @include: gio/gio.h
  *
  * Various data structures and convenience routines to parse and
- * generate D-Bus introspection XML.
+ * generate D-Bus introspection XML. Introspection information is
+ * used when registering objects with g_dbus_connection_register_object().
+ *
+ * The format of D-Bus introspection XML is specified in the
+ * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format)
  */
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* See also https://bugzilla.gnome.org/show_bug.cgi?id=449565 ... */
-#define _MY_DEFINE_BOXED_TYPE(TypeName, type_name)                     \
-  GType                                                                 \
-  type_name##_get_type (void)                                           \
-  {                                                                     \
-    static volatile gsize type_volatile = 0;                            \
-    if (g_once_init_enter (&type_volatile))                             \
-      {                                                                 \
-        GType type = g_boxed_type_register_static (g_intern_static_string (#TypeName),  \
-                                                   (GBoxedCopyFunc) type_name##_ref,    \
-                                                   (GBoxedFreeFunc) type_name##_unref); \
-        g_once_init_leave (&type_volatile, type);                       \
-      }                                                                 \
-    return (GType) type_volatile;                                       \
-  }
+#define _MY_DEFINE_BOXED_TYPE(TypeName, type_name) \
+  G_DEFINE_BOXED_TYPE (TypeName, type_name, type_name##_ref, type_name##_unref)
 
 _MY_DEFINE_BOXED_TYPE (GDBusNodeInfo,       g_dbus_node_info);
 _MY_DEFINE_BOXED_TYPE (GDBusInterfaceInfo,  g_dbus_interface_info);
 _MY_DEFINE_BOXED_TYPE (GDBusMethodInfo,     g_dbus_method_info);
+_MY_DEFINE_BOXED_TYPE (GDBusSignalInfo,     g_dbus_signal_info);
 _MY_DEFINE_BOXED_TYPE (GDBusPropertyInfo,   g_dbus_property_info);
 _MY_DEFINE_BOXED_TYPE (GDBusArgInfo,        g_dbus_arg_info);
 _MY_DEFINE_BOXED_TYPE (GDBusAnnotationInfo, g_dbus_annotation_info);
@@ -219,7 +210,7 @@ g_dbus_arg_info_ref (GDBusArgInfo *info)
 }
 
 /**
- * g_dbus_node_info_ref:
+ * g_dbus_annotation_info_ref:
  * @info: A #GDBusNodeInfo
  *
  * If @info is statically allocated does nothing. Otherwise increases
@@ -427,10 +418,10 @@ g_dbus_node_info_unref (GDBusNodeInfo *info)
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-g_dbus_annotation_info_set (ParseData                      *data,
-                            GDBusAnnotationInfo            *info,
-                            const gchar                    *key,
-                            const gchar                    *value,
+g_dbus_annotation_info_set (ParseData            *data,
+                            GDBusAnnotationInfo  *info,
+                            const gchar          *key,
+                            const gchar          *value,
                             GDBusAnnotationInfo **embedded_annotations)
 {
   info->ref_count = 1;
@@ -469,9 +460,7 @@ static void
 g_dbus_method_info_set (ParseData            *data,
                         GDBusMethodInfo      *info,
                         const gchar          *name,
-                        guint                 in_num_args,
                         GDBusArgInfo        **in_args,
-                        guint                 out_num_args,
                         GDBusArgInfo        **out_args,
                         GDBusAnnotationInfo **annotations)
 {
@@ -480,17 +469,11 @@ g_dbus_method_info_set (ParseData            *data,
   if (name != NULL)
     info->name = g_strdup (name);
 
-  if (in_num_args != 0)
-    {
-      //info->in_num_args = in_num_args;
-      info->in_args = in_args;
-    }
+  if (in_args != NULL)
+    info->in_args = in_args;
 
-  if (out_num_args != 0)
-    {
-      //info->out_num_args = out_num_args;
-      info->out_args = out_args;
-    }
+  if (out_args != NULL)
+    info->out_args = out_args;
 
   if (annotations != NULL)
     info->annotations = annotations;
@@ -500,7 +483,6 @@ static void
 g_dbus_signal_info_set (ParseData            *data,
                         GDBusSignalInfo      *info,
                         const gchar          *name,
-                        guint                 num_args,
                         GDBusArgInfo        **args,
                         GDBusAnnotationInfo **annotations)
 {
@@ -509,16 +491,11 @@ g_dbus_signal_info_set (ParseData            *data,
   if (name != NULL)
     info->name = g_strdup (name);
 
-  if (num_args != 0)
-    {
-      //info->num_args = num_args;
-      info->args = args;
-    }
+  if (args != NULL)
+    info->args = args;
 
   if (annotations != NULL)
-    {
-      info->annotations = annotations;
-    }
+    info->annotations = annotations;
 }
 
 static void
@@ -538,66 +515,44 @@ g_dbus_property_info_set (ParseData               *data,
     info->flags = flags;
 
   if (signature != NULL)
-    {
-      info->signature = g_strdup (signature);
-    }
+    info->signature = g_strdup (signature);
 
   if (annotations != NULL)
-    {
-      info->annotations = annotations;
-    }
+    info->annotations = annotations;
 }
 
 static void
 g_dbus_interface_info_set (ParseData            *data,
                            GDBusInterfaceInfo   *info,
                            const gchar          *name,
-                           guint                 num_methods,
                            GDBusMethodInfo     **methods,
-                           guint                 num_signals,
                            GDBusSignalInfo     **signals,
-                           guint                 num_properties,
                            GDBusPropertyInfo   **properties,
                            GDBusAnnotationInfo **annotations)
 {
   info->ref_count = 1;
 
   if (name != NULL)
-    {
-      info->name = g_strdup (name);
-    }
+    info->name = g_strdup (name);
 
-  if (num_methods != 0)
-    {
-      //info->num_methods    = num_methods;
-      info->methods        = methods;
-    }
+  if (methods != NULL)
+    info->methods = methods;
 
-  if (num_signals != 0)
-    {
-      //info->num_signals    = num_signals;
-      info->signals        = signals;
-    }
+  if (signals != NULL)
+    info->signals = signals;
 
-  if (num_properties != 0)
-    {
-      //info->num_properties = num_properties;
-      info->properties     = properties;
-    }
+  if (properties != NULL)
+    info->properties = properties;
 
   if (annotations != NULL)
-    {
-      info->annotations = annotations;
-    }
+    info->annotations = annotations;
 }
 
 static void
 g_dbus_node_info_set (ParseData            *data,
                       GDBusNodeInfo        *info,
                       const gchar          *path,
-                      guint                 num_interfaces,
                       GDBusInterfaceInfo  **interfaces,
-                      guint                 num_nodes,
                       GDBusNodeInfo       **nodes,
                       GDBusAnnotationInfo **annotations)
 {
@@ -609,38 +564,32 @@ g_dbus_node_info_set (ParseData            *data,
       /* TODO: relative / absolute path snafu */
     }
 
-  if (num_interfaces != 0)
-    {
-      //info->num_interfaces = num_interfaces;
-      info->interfaces     = interfaces;
-    }
+  if (interfaces != NULL)
+    info->interfaces = interfaces;
 
-  if (num_nodes != 0)
-    {
-      //info->num_nodes      = num_nodes;
-      info->nodes          = nodes;
-    }
+  if (nodes != NULL)
+    info->nodes = nodes;
 
   if (annotations != NULL)
-    {
-      info->annotations = annotations;
-    }
-
+    info->annotations = annotations;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-g_dbus_annotation_info_generate_xml (const GDBusAnnotationInfo  *info,
-                                     guint                       indent,
-                                     GString                    *string_builder)
+g_dbus_annotation_info_generate_xml (GDBusAnnotationInfo *info,
+                                     guint                indent,
+                                     GString             *string_builder)
 {
+  gchar *tmp;
   guint n;
 
-  g_string_append_printf (string_builder, "%*s<annotation name=\"%s\" value=\"%s\"",
-                          indent, "",
-                          info->key,
-                          info->value);
+  tmp = g_markup_printf_escaped ("%*s<annotation name=\"%s\" value=\"%s\"",
+                                 indent, "",
+                                 info->key,
+                                 info->value);
+  g_string_append (string_builder, tmp);
+  g_free (tmp);
 
   if (info->annotations == NULL)
     {
@@ -662,10 +611,10 @@ g_dbus_annotation_info_generate_xml (const GDBusAnnotationInfo  *info,
 }
 
 static void
-g_dbus_arg_info_generate_xml (const GDBusArgInfo  *info,
-                              guint                indent,
-                              const gchar         *extra_attributes,
-                              GString             *string_builder)
+g_dbus_arg_info_generate_xml (GDBusArgInfo *info,
+                              guint         indent,
+                              const gchar  *extra_attributes,
+                              GString      *string_builder)
 {
   guint n;
 
@@ -698,9 +647,9 @@ g_dbus_arg_info_generate_xml (const GDBusArgInfo  *info,
 }
 
 static void
-g_dbus_method_info_generate_xml (const GDBusMethodInfo  *info,
-                                 guint                   indent,
-                                 GString                *string_builder)
+g_dbus_method_info_generate_xml (GDBusMethodInfo *info,
+                                 guint            indent,
+                                 GString         *string_builder)
 {
   guint n;
 
@@ -738,9 +687,9 @@ g_dbus_method_info_generate_xml (const GDBusMethodInfo  *info,
 }
 
 static void
-g_dbus_signal_info_generate_xml (const GDBusSignalInfo  *info,
-                                 guint                   indent,
-                                 GString                *string_builder)
+g_dbus_signal_info_generate_xml (GDBusSignalInfo *info,
+                                 guint            indent,
+                                 GString         *string_builder)
 {
   guint n;
 
@@ -772,9 +721,9 @@ g_dbus_signal_info_generate_xml (const GDBusSignalInfo  *info,
 }
 
 static void
-g_dbus_property_info_generate_xml (const GDBusPropertyInfo  *info,
-                                   guint                     indent,
-                                   GString                  *string_builder)
+g_dbus_property_info_generate_xml (GDBusPropertyInfo *info,
+                                   guint              indent,
+                                   GString           *string_builder)
 {
   guint n;
   const gchar *access_string;
@@ -825,21 +774,21 @@ g_dbus_property_info_generate_xml (const GDBusPropertyInfo  *info,
  * g_dbus_interface_info_generate_xml:
  * @info: A #GDBusNodeInfo
  * @indent: Indentation level.
- * @string_builder: A #GString to to append XML data to.
+ * @string_builder: (out): A #GString to to append XML data to.
  *
  * Appends an XML representation of @info (and its children) to @string_builder.
  *
  * This function is typically used for generating introspection XML
  * documents at run-time for handling the
- * <literal>org.freedesktop.DBus.Introspectable.Introspect</literal>
+ * `org.freedesktop.DBus.Introspectable.Introspect`
  * method.
  *
  * Since: 2.26
  */
 void
-g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo  *info,
-                                    guint                      indent,
-                                    GString                   *string_builder)
+g_dbus_interface_info_generate_xml (GDBusInterfaceInfo *info,
+                                    guint               indent,
+                                    GString            *string_builder)
 {
   guint n;
 
@@ -872,29 +821,29 @@ g_dbus_interface_info_generate_xml (const GDBusInterfaceInfo  *info,
 
 /**
  * g_dbus_node_info_generate_xml:
- * @node_info: A #GDBusNodeInfo.
+ * @info: A #GDBusNodeInfo.
  * @indent: Indentation level.
- * @string_builder: A #GString to to append XML data to.
+ * @string_builder: (out): A #GString to to append XML data to.
  *
- * Appends an XML representation of @node_info (and its children) to @string_builder.
+ * Appends an XML representation of @info (and its children) to @string_builder.
  *
  * This function is typically used for generating introspection XML documents at run-time for
- * handling the <literal>org.freedesktop.DBus.Introspectable.Introspect</literal> method.
+ * handling the `org.freedesktop.DBus.Introspectable.Introspect`  method.
  *
  * Since: 2.26
  */
 void
-g_dbus_node_info_generate_xml (const GDBusNodeInfo  *node_info,
-                               guint                 indent,
-                               GString              *string_builder)
+g_dbus_node_info_generate_xml (GDBusNodeInfo *info,
+                               guint          indent,
+                               GString       *string_builder)
 {
   guint n;
 
   g_string_append_printf (string_builder, "%*s<node", indent, "");
-  if (node_info->path != NULL)
-    g_string_append_printf (string_builder, " name=\"%s\"", node_info->path);
+  if (info->path != NULL)
+    g_string_append_printf (string_builder, " name=\"%s\"", info->path);
 
-  if (node_info->interfaces == NULL && node_info->nodes == NULL && node_info->annotations == NULL)
+  if (info->interfaces == NULL && info->nodes == NULL && info->annotations == NULL)
     {
       g_string_append (string_builder, "/>\n");
     }
@@ -902,18 +851,18 @@ g_dbus_node_info_generate_xml (const GDBusNodeInfo  *node_info,
     {
       g_string_append (string_builder, ">\n");
 
-      for (n = 0; node_info->annotations != NULL && node_info->annotations[n] != NULL; n++)
-        g_dbus_annotation_info_generate_xml (node_info->annotations[n],
+      for (n = 0; info->annotations != NULL && info->annotations[n] != NULL; n++)
+        g_dbus_annotation_info_generate_xml (info->annotations[n],
                                              indent + 2,
                                              string_builder);
 
-      for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++)
-        g_dbus_interface_info_generate_xml (node_info->interfaces[n],
+      for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
+        g_dbus_interface_info_generate_xml (info->interfaces[n],
                                             indent + 2,
                                             string_builder);
 
-      for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++)
-        g_dbus_node_info_generate_xml (node_info->nodes[n],
+      for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++)
+        g_dbus_node_info_generate_xml (info->nodes[n],
                                        indent + 2,
                                        string_builder);
 
@@ -924,7 +873,8 @@ g_dbus_node_info_generate_xml (const GDBusNodeInfo  *node_info,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static GDBusAnnotationInfo **
-parse_data_steal_annotations (ParseData *data, guint *out_num_elements)
+parse_data_steal_annotations (ParseData *data,
+                              guint     *out_num_elements)
 {
   GDBusAnnotationInfo **ret;
   if (out_num_elements != NULL)
@@ -941,7 +891,8 @@ parse_data_steal_annotations (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusArgInfo **
-parse_data_steal_args (ParseData *data, guint *out_num_elements)
+parse_data_steal_args (ParseData *data,
+                       guint     *out_num_elements)
 {
   GDBusArgInfo **ret;
   if (out_num_elements != NULL)
@@ -958,7 +909,8 @@ parse_data_steal_args (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusArgInfo **
-parse_data_steal_out_args (ParseData *data, guint *out_num_elements)
+parse_data_steal_out_args (ParseData *data,
+                           guint     *out_num_elements)
 {
   GDBusArgInfo **ret;
   if (out_num_elements != NULL)
@@ -975,7 +927,8 @@ parse_data_steal_out_args (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusMethodInfo **
-parse_data_steal_methods (ParseData *data, guint *out_num_elements)
+parse_data_steal_methods (ParseData *data,
+                          guint     *out_num_elements)
 {
   GDBusMethodInfo **ret;
   if (out_num_elements != NULL)
@@ -992,7 +945,8 @@ parse_data_steal_methods (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusSignalInfo **
-parse_data_steal_signals (ParseData *data, guint *out_num_elements)
+parse_data_steal_signals (ParseData *data,
+                          guint     *out_num_elements)
 {
   GDBusSignalInfo **ret;
   if (out_num_elements != NULL)
@@ -1009,7 +963,8 @@ parse_data_steal_signals (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusPropertyInfo **
-parse_data_steal_properties (ParseData *data, guint *out_num_elements)
+parse_data_steal_properties (ParseData *data,
+                             guint     *out_num_elements)
 {
   GDBusPropertyInfo **ret;
   if (out_num_elements != NULL)
@@ -1026,7 +981,8 @@ parse_data_steal_properties (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusInterfaceInfo **
-parse_data_steal_interfaces (ParseData *data, guint *out_num_elements)
+parse_data_steal_interfaces (ParseData *data,
+                             guint     *out_num_elements)
 {
   GDBusInterfaceInfo **ret;
   if (out_num_elements != NULL)
@@ -1043,7 +999,8 @@ parse_data_steal_interfaces (ParseData *data, guint *out_num_elements)
 }
 
 static GDBusNodeInfo **
-parse_data_steal_nodes (ParseData *data, guint *out_num_elements)
+parse_data_steal_nodes (ParseData *data,
+                        guint     *out_num_elements)
 {
   GDBusNodeInfo **ret;
   if (out_num_elements != NULL)
@@ -1144,7 +1101,8 @@ parse_data_free_nodes (ParseData *data)
 /* ---------------------------------------------------------------------------------------------------- */
 
 static GDBusAnnotationInfo *
-parse_data_get_annotation (ParseData *data, gboolean create_new)
+parse_data_get_annotation (ParseData *data,
+                           gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->annotations, g_new0 (GDBusAnnotationInfo, 1));
@@ -1152,7 +1110,8 @@ parse_data_get_annotation (ParseData *data, gboolean create_new)
 }
 
 static GDBusArgInfo *
-parse_data_get_arg (ParseData *data, gboolean create_new)
+parse_data_get_arg (ParseData *data,
+                    gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->args, g_new0 (GDBusArgInfo, 1));
@@ -1160,7 +1119,8 @@ parse_data_get_arg (ParseData *data, gboolean create_new)
 }
 
 static GDBusArgInfo *
-parse_data_get_out_arg (ParseData *data, gboolean create_new)
+parse_data_get_out_arg (ParseData *data,
+                        gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->out_args, g_new0 (GDBusArgInfo, 1));
@@ -1168,7 +1128,8 @@ parse_data_get_out_arg (ParseData *data, gboolean create_new)
 }
 
 static GDBusMethodInfo *
-parse_data_get_method (ParseData *data, gboolean create_new)
+parse_data_get_method (ParseData *data,
+                       gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->methods, g_new0 (GDBusMethodInfo, 1));
@@ -1176,7 +1137,8 @@ parse_data_get_method (ParseData *data, gboolean create_new)
 }
 
 static GDBusSignalInfo *
-parse_data_get_signal (ParseData *data, gboolean create_new)
+parse_data_get_signal (ParseData *data,
+                       gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->signals, g_new0 (GDBusSignalInfo, 1));
@@ -1184,7 +1146,8 @@ parse_data_get_signal (ParseData *data, gboolean create_new)
 }
 
 static GDBusPropertyInfo *
-parse_data_get_property (ParseData *data, gboolean create_new)
+parse_data_get_property (ParseData *data,
+                         gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->properties, g_new0 (GDBusPropertyInfo, 1));
@@ -1192,7 +1155,8 @@ parse_data_get_property (ParseData *data, gboolean create_new)
 }
 
 static GDBusInterfaceInfo *
-parse_data_get_interface (ParseData *data, gboolean create_new)
+parse_data_get_interface (ParseData *data,
+                          gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->interfaces, g_new0 (GDBusInterfaceInfo, 1));
@@ -1200,7 +1164,8 @@ parse_data_get_interface (ParseData *data, gboolean create_new)
 }
 
 static GDBusNodeInfo *
-parse_data_get_node (ParseData *data, gboolean create_new)
+parse_data_get_node (ParseData *data,
+                     gboolean   create_new)
 {
   if (create_new)
     g_ptr_array_add (data->nodes, g_new0 (GDBusNodeInfo, 1));
@@ -1267,6 +1232,9 @@ parse_data_free (ParseData *data)
   parse_data_free_methods (data);
   parse_data_free_signals (data);
   parse_data_free_properties (data);
+  parse_data_free_interfaces (data);
+  parse_data_free_annotations (data);
+  parse_data_free_nodes (data);
 
   g_free (data);
 }
@@ -1274,12 +1242,12 @@ parse_data_free (ParseData *data)
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-parser_start_element (GMarkupParseContext *context,
-                      const gchar         *element_name,
-                      const gchar        **attribute_names,
-                      const gchar        **attribute_values,
-                      gpointer             user_data,
-                      GError             **error)
+parser_start_element (GMarkupParseContext  *context,
+                      const gchar          *element_name,
+                      const gchar         **attribute_names,
+                      const gchar         **attribute_values,
+                      gpointer              user_data,
+                      GError              **error)
 {
   ParseData *data = user_data;
   GSList *stack;
@@ -1322,8 +1290,8 @@ parser_start_element (GMarkupParseContext *context,
       g_dbus_node_info_set (data,
                             parse_data_get_node (data, TRUE),
                             name,
-                            0, NULL,
-                            0, NULL,
+                            NULL,
+                            NULL,
                             NULL);
 
       /* push the currently retrieved interfaces and nodes on the stack and prepare new arrays */
@@ -1353,15 +1321,17 @@ parser_start_element (GMarkupParseContext *context,
                                         attribute_values,
                                         error,
                                         G_MARKUP_COLLECT_STRING, "name", &name,
+                                        /* seen in the wild */
+                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
                                         G_MARKUP_COLLECT_INVALID))
         goto out;
 
       g_dbus_interface_info_set (data,
                                  parse_data_get_interface (data, TRUE),
                                  name,
-                                 0, NULL,
-                                 0, NULL,
-                                 0, NULL,
+                                 NULL,
+                                 NULL,
+                                 NULL,
                                  NULL);
 
     }
@@ -1382,14 +1352,16 @@ parser_start_element (GMarkupParseContext *context,
                                         attribute_values,
                                         error,
                                         G_MARKUP_COLLECT_STRING, "name", &name,
+                                        /* seen in the wild */
+                                        G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "version", NULL,
                                         G_MARKUP_COLLECT_INVALID))
         goto out;
 
       g_dbus_method_info_set (data,
                               parse_data_get_method (data, TRUE),
                               name,
-                              0, NULL,
-                              0, NULL,
+                              NULL,
+                              NULL,
                               NULL);
 
       data->num_args = 0;
@@ -1418,7 +1390,7 @@ parser_start_element (GMarkupParseContext *context,
       g_dbus_signal_info_set (data,
                               parse_data_get_signal (data, TRUE),
                               name,
-                              0, NULL,
+                              NULL,
                               NULL);
 
       data->num_args = 0;
@@ -1499,7 +1471,10 @@ parser_start_element (GMarkupParseContext *context,
                                         G_MARKUP_COLLECT_INVALID))
         goto out;
 
-      is_in = FALSE;
+      if (strcmp (stack->next->data, "method") == 0)
+        is_in = TRUE;
+      else
+        is_in = FALSE;
       if (direction != NULL)
         {
           if (strcmp (direction, "in") == 0)
@@ -1614,10 +1589,10 @@ steal_annotations (ParseData *data)
 
 
 static void
-parser_end_element (GMarkupParseContext *context,
-                    const gchar         *element_name,
-                    gpointer             user_data,
-                    GError             **error)
+parser_end_element (GMarkupParseContext  *context,
+                    const gchar          *element_name,
+                    gpointer              user_data,
+                    GError              **error)
 {
   ParseData *data = user_data;
   gboolean have_popped_annotations;
@@ -1648,9 +1623,7 @@ parser_end_element (GMarkupParseContext *context,
       g_dbus_node_info_set (data,
                             parse_data_get_node (data, FALSE),
                             NULL,
-                            num_interfaces,
                             interfaces,
-                            num_nodes,
                             nodes,
                             steal_annotations (data));
 
@@ -1671,11 +1644,8 @@ parser_end_element (GMarkupParseContext *context,
       g_dbus_interface_info_set (data,
                                  parse_data_get_interface (data, FALSE),
                                  NULL,
-                                 num_methods,
                                  methods,
-                                 num_signals,
                                  signals,
-                                 num_properties,
                                  properties,
                                  steal_annotations (data));
 
@@ -1693,9 +1663,7 @@ parser_end_element (GMarkupParseContext *context,
       g_dbus_method_info_set (data,
                               parse_data_get_method (data, FALSE),
                               NULL,
-                              in_num_args,
                               in_args,
-                              out_num_args,
                               out_args,
                               steal_annotations (data));
     }
@@ -1709,7 +1677,6 @@ parser_end_element (GMarkupParseContext *context,
       g_dbus_signal_info_set (data,
                               parse_data_get_signal (data, FALSE),
                               NULL,
-                              num_args,
                               args,
                               steal_annotations (data));
     }
@@ -1789,6 +1756,13 @@ parser_error (GMarkupParseContext *context,
  *
  * Parses @xml_data and returns a #GDBusNodeInfo representing the data.
  *
+ * The introspection XML must contain exactly one top-level
+ * <node> element.
+ *
+ * Note that this routine is using a
+ * [GMarkup][glib-Simple-XML-Subset-Parser.description]-based
+ * parser that only accepts a subset of valid XML documents.
+ *
  * Returns: A #GDBusNodeInfo structure or %NULL if @error is set. Free
  * with g_dbus_node_info_unref().
  *
@@ -1803,6 +1777,7 @@ g_dbus_node_info_new_for_xml (const gchar  *xml_data,
   GMarkupParser *parser;
   guint num_nodes;
   ParseData *data;
+  GDBusNodeInfo **ughret;
 
   ret = NULL;
   parser = NULL;
@@ -1815,7 +1790,7 @@ g_dbus_node_info_new_for_xml (const gchar  *xml_data,
 
   data = parse_data_new ();
   context = g_markup_parse_context_new (parser,
-                                        0,
+                                        G_MARKUP_IGNORE_QUALIFIED,
                                         data,
                                         (GDestroyNotify) parse_data_free);
 
@@ -1825,7 +1800,9 @@ g_dbus_node_info_new_for_xml (const gchar  *xml_data,
                                      error))
     goto out;
 
-  GDBusNodeInfo **ughret;
+  if (!g_markup_parse_context_end_parse (context, error))
+    goto out;
+
   ughret = parse_data_steal_nodes (data, &num_nodes);
 
   if (num_nodes != 1)
@@ -1841,19 +1818,16 @@ g_dbus_node_info_new_for_xml (const gchar  *xml_data,
       /* clean up */
       for (n = 0; n < num_nodes; n++)
         {
-          for (n = 0; n < num_nodes; n++)
-            g_dbus_node_info_unref (&(ret[n]));
+          g_dbus_node_info_unref (ughret[n]);
+          ughret[n] = NULL;
         }
-      g_free (ret);
-      ret = NULL;
     }
 
   ret = ughret[0];
   g_free (ughret);
 
  out:
-  if (parser != NULL)
-    g_free (parser);
+  g_free (parser);
   if (context != NULL)
     g_markup_parse_context_free (context);
 
@@ -1864,26 +1838,26 @@ g_dbus_node_info_new_for_xml (const gchar  *xml_data,
 
 /**
  * g_dbus_annotation_info_lookup:
- * @annotations: A %NULL-terminated array of annotations or %NULL.
+ * @annotations: (array zero-terminated=1) (allow-none): A %NULL-terminated array of annotations or %NULL.
  * @name: The name of the annotation to look up.
  *
  * Looks up the value of an annotation.
  *
- * This cost of this function is O(n) in number of annotations.
+ * The cost of this function is O(n) in number of annotations.
  *
  * Returns: The value or %NULL if not found. Do not free, it is owned by @annotations.
  *
  * Since: 2.26
  */
 const gchar *
-g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations,
-                               const gchar                *name)
+g_dbus_annotation_info_lookup (GDBusAnnotationInfo **annotations,
+                               const gchar          *name)
 {
   guint n;
   const gchar *ret;
 
   ret = NULL;
-  for (n = 0; annotations != NULL && annotations[n]->key != NULL; n++)
+  for (n = 0; annotations != NULL && annotations[n] != NULL; n++)
     {
       if (g_strcmp0 (annotations[n]->key, name) == 0)
         {
@@ -1898,6 +1872,37 @@ g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+G_LOCK_DEFINE_STATIC (info_cache_lock);
+
+typedef struct
+{
+  gint use_count;
+
+  /* gchar* -> GDBusMethodInfo* */
+  GHashTable *method_name_to_data;
+
+  /* gchar* -> GDBusMethodInfo* */
+  GHashTable *signal_name_to_data;
+
+  /* gchar* -> GDBusMethodInfo* */
+  GHashTable *property_name_to_data;
+} InfoCacheEntry;
+
+static void
+info_cache_free (InfoCacheEntry *cache)
+{
+  g_assert (cache->use_count == 0);
+  g_hash_table_unref (cache->method_name_to_data);
+  g_hash_table_unref (cache->signal_name_to_data);
+  g_hash_table_unref (cache->property_name_to_data);
+  g_slice_free (InfoCacheEntry, cache);
+}
+
+/* maps from GDBusInterfaceInfo* to InfoCacheEntry* */
+static GHashTable *info_cache = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 /**
  * g_dbus_interface_info_lookup_method:
  * @info: A #GDBusInterfaceInfo.
@@ -1905,22 +1910,37 @@ g_dbus_annotation_info_lookup (const GDBusAnnotationInfo **annotations,
  *
  * Looks up information about a method.
  *
- * This cost of this function is O(n) in number of methods.
+ * The cost of this function is O(n) in number of methods unless
+ * g_dbus_interface_info_cache_build() has been used on @info.
  *
- * Returns: A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info.
+ * Returns: (transfer none): A #GDBusMethodInfo or %NULL if not found. Do not free, it is owned by @info.
  *
  * Since: 2.26
  */
-const GDBusMethodInfo *
-g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info,
-                                     const gchar              *name)
+GDBusMethodInfo *
+g_dbus_interface_info_lookup_method (GDBusInterfaceInfo *info,
+                                     const gchar        *name)
 {
   guint n;
-  const GDBusMethodInfo *result;
+  GDBusMethodInfo *result;
+
+  G_LOCK (info_cache_lock);
+  if (G_LIKELY (info_cache != NULL))
+    {
+      InfoCacheEntry *cache;
+      cache = g_hash_table_lookup (info_cache, info);
+      if (G_LIKELY (cache != NULL))
+        {
+          result = g_hash_table_lookup (cache->method_name_to_data, name);
+          G_UNLOCK (info_cache_lock);
+          goto out;
+        }
+    }
+  G_UNLOCK (info_cache_lock);
 
   for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
     {
-      const GDBusMethodInfo *i = info->methods[n];
+      GDBusMethodInfo *i = info->methods[n];
 
       if (g_strcmp0 (i->name, name) == 0)
         {
@@ -1944,22 +1964,37 @@ g_dbus_interface_info_lookup_method (const GDBusInterfaceInfo *info,
  *
  * Looks up information about a signal.
  *
- * This cost of this function is O(n) in number of signals.
+ * The cost of this function is O(n) in number of signals unless
+ * g_dbus_interface_info_cache_build() has been used on @info.
  *
- * Returns: A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info.
+ * Returns: (transfer none): A #GDBusSignalInfo or %NULL if not found. Do not free, it is owned by @info.
  *
  * Since: 2.26
  */
-const GDBusSignalInfo *
-g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info,
-                                     const gchar              *name)
+GDBusSignalInfo *
+g_dbus_interface_info_lookup_signal (GDBusInterfaceInfo *info,
+                                     const gchar        *name)
 {
   guint n;
-  const GDBusSignalInfo *result;
+  GDBusSignalInfo *result;
+
+  G_LOCK (info_cache_lock);
+  if (G_LIKELY (info_cache != NULL))
+    {
+      InfoCacheEntry *cache;
+      cache = g_hash_table_lookup (info_cache, info);
+      if (G_LIKELY (cache != NULL))
+        {
+          result = g_hash_table_lookup (cache->signal_name_to_data, name);
+          G_UNLOCK (info_cache_lock);
+          goto out;
+        }
+    }
+  G_UNLOCK (info_cache_lock);
 
   for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
     {
-      const GDBusSignalInfo *i = info->signals[n];
+      GDBusSignalInfo *i = info->signals[n];
 
       if (g_strcmp0 (i->name, name) == 0)
         {
@@ -1983,22 +2018,37 @@ g_dbus_interface_info_lookup_signal (const GDBusInterfaceInfo *info,
  *
  * Looks up information about a property.
  *
- * This cost of this function is O(n) in number of properties.
+ * The cost of this function is O(n) in number of properties unless
+ * g_dbus_interface_info_cache_build() has been used on @info.
  *
- * Returns: A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info.
+ * Returns: (transfer none): A #GDBusPropertyInfo or %NULL if not found. Do not free, it is owned by @info.
  *
  * Since: 2.26
  */
-const GDBusPropertyInfo *
-g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info,
-                                       const gchar              *name)
+GDBusPropertyInfo *
+g_dbus_interface_info_lookup_property (GDBusInterfaceInfo *info,
+                                       const gchar        *name)
 {
   guint n;
-  const GDBusPropertyInfo *result;
+  GDBusPropertyInfo *result;
+
+  G_LOCK (info_cache_lock);
+  if (G_LIKELY (info_cache != NULL))
+    {
+      InfoCacheEntry *cache;
+      cache = g_hash_table_lookup (info_cache, info);
+      if (G_LIKELY (cache != NULL))
+        {
+          result = g_hash_table_lookup (cache->property_name_to_data, name);
+          G_UNLOCK (info_cache_lock);
+          goto out;
+        }
+    }
+  G_UNLOCK (info_cache_lock);
 
   for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
     {
-      const GDBusPropertyInfo *i = info->properties[n];
+      GDBusPropertyInfo *i = info->properties[n];
 
       if (g_strcmp0 (i->name, name) == 0)
         {
@@ -2016,28 +2066,117 @@ g_dbus_interface_info_lookup_property (const GDBusInterfaceInfo *info,
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
+ * g_dbus_interface_info_cache_build:
+ * @info: A #GDBusInterfaceInfo.
+ *
+ * Builds a lookup-cache to speed up
+ * g_dbus_interface_info_lookup_method(),
+ * g_dbus_interface_info_lookup_signal() and
+ * g_dbus_interface_info_lookup_property().
+ *
+ * If this has already been called with @info, the existing cache is
+ * used and its use count is increased.
+ *
+ * Note that @info cannot be modified until
+ * g_dbus_interface_info_cache_release() is called.
+ *
+ * Since: 2.30
+ */
+void
+g_dbus_interface_info_cache_build (GDBusInterfaceInfo *info)
+{
+  InfoCacheEntry *cache;
+  guint n;
+
+  G_LOCK (info_cache_lock);
+  if (info_cache == NULL)
+    info_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) info_cache_free);
+  cache = g_hash_table_lookup (info_cache, info);
+  if (cache != NULL)
+    {
+      cache->use_count += 1;
+      goto out;
+    }
+  cache = g_slice_new0 (InfoCacheEntry);
+  cache->use_count = 1;
+  cache->method_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
+  cache->signal_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
+  cache->property_name_to_data = g_hash_table_new (g_str_hash, g_str_equal);
+  for (n = 0; info->methods != NULL && info->methods[n] != NULL; n++)
+    g_hash_table_insert (cache->method_name_to_data, info->methods[n]->name, info->methods[n]);
+  for (n = 0; info->signals != NULL && info->signals[n] != NULL; n++)
+    g_hash_table_insert (cache->signal_name_to_data, info->signals[n]->name, info->signals[n]);
+  for (n = 0; info->properties != NULL && info->properties[n] != NULL; n++)
+    g_hash_table_insert (cache->property_name_to_data, info->properties[n]->name, info->properties[n]);
+  g_hash_table_insert (info_cache, info, cache);
+ out:
+  G_UNLOCK (info_cache_lock);
+}
+
+/**
+ * g_dbus_interface_info_cache_release:
+ * @info: A GDBusInterfaceInfo
+ *
+ * Decrements the usage count for the cache for @info built by
+ * g_dbus_interface_info_cache_build() (if any) and frees the
+ * resources used by the cache if the usage count drops to zero.
+ *
+ * Since: 2.30
+ */
+void
+g_dbus_interface_info_cache_release (GDBusInterfaceInfo *info)
+{
+  InfoCacheEntry *cache;
+
+  G_LOCK (info_cache_lock);
+  if (G_UNLIKELY (info_cache == NULL))
+    {
+      g_warning ("%s called for interface %s but there is no cache", info->name, G_STRFUNC);
+      goto out;
+    }
+
+  cache = g_hash_table_lookup (info_cache, info);
+  if (G_UNLIKELY (cache == NULL))
+    {
+      g_warning ("%s called for interface %s but there is no cache entry", info->name, G_STRFUNC);
+      goto out;
+    }
+  cache->use_count -= 1;
+  if (cache->use_count == 0)
+    {
+      g_hash_table_remove (info_cache, info);
+      /* could nuke info_cache itself if empty */
+    }
+ out:
+  G_UNLOCK (info_cache_lock);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
  * g_dbus_node_info_lookup_interface:
- * @node_info: A #GDBusNodeInfo.
+ * @info: A #GDBusNodeInfo.
  * @name: A D-Bus interface name.
  *
  * Looks up information about an interface.
  *
- * This cost of this function is O(n) in number of interfaces.
+ * The cost of this function is O(n) in number of interfaces.
  *
- * Returns: A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @node_info.
+ * Returns: (transfer none): A #GDBusInterfaceInfo or %NULL if not found. Do not free, it is owned by @info.
  *
  * Since: 2.26
  */
-const GDBusInterfaceInfo *
-g_dbus_node_info_lookup_interface (const GDBusNodeInfo *node_info,
-                                   const gchar         *name)
+GDBusInterfaceInfo *
+g_dbus_node_info_lookup_interface (GDBusNodeInfo *info,
+                                   const gchar   *name)
 {
   guint n;
-  const GDBusInterfaceInfo *result;
+  GDBusInterfaceInfo *result;
 
-  for (n = 0; node_info->interfaces != NULL && node_info->interfaces[n] != NULL; n++)
+  for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++)
     {
-      const GDBusInterfaceInfo *i = node_info->interfaces[n];
+      GDBusInterfaceInfo *i = info->interfaces[n];
 
       if (g_strcmp0 (i->name, name) == 0)
         {