[BUG] - varargs removal does not check correct variable
[gnome.gobject-introspection] / girepository / girparser.c
index a4581e6..1347945 100644 (file)
 #include <stdio.h>
 
 #include <glib.h>
+#include "girparser.h"
 #include "girmodule.h"
 #include "girnode.h"
 #include "gtypelib.h"
+#include "config.h"
+
+#if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)
+# include <execinfo.h>
+#endif
+
+struct _GIrParser
+{
+  gchar **includes;
+  GList *parsed_modules; /* All previously parsed modules */
+};
 
 typedef enum
 {
-  STATE_START,    
-  STATE_END,        
-  STATE_REPOSITORY, 
-  STATE_NAMESPACE,  
-  STATE_ENUM,        
-  STATE_BITFIELD,  /* 5 */  
-  STATE_FUNCTION,   
-  STATE_FUNCTION_RETURN, 
-  STATE_FUNCTION_PARAMETERS,
-  STATE_FUNCTION_PARAMETER, 
-  STATE_CLASS,   /* 10 */
+  STATE_START,
+  STATE_END,
+  STATE_REPOSITORY,
+  STATE_INCLUDE,
+  STATE_PACKAGE,
+  STATE_NAMESPACE, /* 5 */
+  STATE_ENUM,
+  STATE_BITFIELD,
+  STATE_FUNCTION,
+  STATE_FUNCTION_RETURN,
+  STATE_FUNCTION_PARAMETERS, /* 10 */
+  STATE_FUNCTION_PARAMETER,
+  STATE_CLASS,
   STATE_CLASS_FIELD,
   STATE_CLASS_PROPERTY,
-  STATE_INTERFACE,
-  STATE_INTERFACE_PROPERTY, 
-  STATE_INTERFACE_FIELD,  /* 15 */
-  STATE_IMPLEMENTS, 
-  STATE_REQUIRES,
-  STATE_BOXED,  
+  STATE_INTERFACE, /* 15 */
+  STATE_INTERFACE_PROPERTY,
+  STATE_INTERFACE_FIELD,
+  STATE_IMPLEMENTS,
+  STATE_PREREQUISITE,
+  STATE_BOXED,   /* 20 */
   STATE_BOXED_FIELD,
-  STATE_STRUCT,   /* 20 */
+  STATE_STRUCT,
   STATE_STRUCT_FIELD,
-  STATE_ERRORDOMAIN, 
-  STATE_UNION,
+  STATE_ERRORDOMAIN,
+  STATE_UNION, /* 25 */
   STATE_UNION_FIELD,
-  STATE_NAMESPACE_CONSTANT, /* 25 */
-  STATE_CLASS_CONSTANT, 
+  STATE_NAMESPACE_CONSTANT,
+  STATE_CLASS_CONSTANT,
   STATE_INTERFACE_CONSTANT,
-  STATE_ALIAS
+  STATE_ALIAS,
+  STATE_TYPE,
+  STATE_ATTRIBUTE,
+  STATE_UNKNOWN
 } ParseState;
 
 typedef struct _ParseContext ParseContext;
 struct _ParseContext
 {
+  GIrParser *parser;
+
   ParseState state;
+  int unknown_depth;
   ParseState prev_state;
 
   GList *modules;
+  GList *include_modules;
+  GList *dependencies;
   GHashTable *aliases;
+  GHashTable *disguised_structures;
 
+  const char *namespace;
+  const char *c_prefix;
   GIrModule *current_module;
-  GIrNode *current_node;
+  GSList *node_stack;
   GIrNode *current_typed;
+  gboolean is_varargs;
+  GList *type_stack;
+  GList *type_parameters;
+  int type_depth;
+  gboolean in_embedded_type;
+};
+#define CURRENT_NODE(ctx) ((GIrNode *)((ctx)->node_stack->data))
+
+static void start_element_handler (GMarkupParseContext *context,
+                                  const gchar         *element_name,
+                                  const gchar        **attribute_names,
+                                  const gchar        **attribute_values,
+                                  gpointer             user_data,
+                                  GError             **error);
+static void end_element_handler   (GMarkupParseContext *context,
+                                  const gchar         *element_name,
+                                  gpointer             user_data,
+                                  GError             **error);
+static void text_handler          (GMarkupParseContext *context,
+                                  const gchar         *text,
+                                  gsize                text_len,
+                                  gpointer             user_data,
+                                  GError             **error);
+static void cleanup               (GMarkupParseContext *context,
+                                  GError              *error,
+                                  gpointer             user_data);
+
+static GMarkupParser markup_parser =
+{
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  cleanup
 };
 
+static gboolean
+start_alias (GMarkupParseContext *context,
+            const gchar         *element_name,
+            const gchar        **attribute_names,
+            const gchar        **attribute_values,
+            ParseContext        *ctx,
+            GError             **error);
+
+static const gchar *find_attribute (const gchar  *name,
+                                   const gchar **attribute_names,
+                                   const gchar **attribute_values);
+
+
+GIrParser *
+g_ir_parser_new (void)
+{
+  GIrParser *parser = g_slice_new0 (GIrParser);
+
+  return parser;
+}
+
+void
+g_ir_parser_free (GIrParser *parser)
+{
+  GList *l;
+
+  if (parser->includes)
+    g_strfreev (parser->includes);
+
+  for (l = parser->parsed_modules; l; l = l->next)
+    g_ir_module_free (l->data);
+
+  g_slice_free (GIrParser, parser);
+}
+
+void
+g_ir_parser_set_includes (GIrParser          *parser,
+                         const gchar *const *includes)
+{
+  if (parser->includes)
+    g_strfreev (parser->includes);
+
+  parser->includes = g_strdupv ((char **)includes);
+}
+
+static void
+firstpass_start_element_handler (GMarkupParseContext *context,
+                                const gchar         *element_name,
+                                const gchar        **attribute_names,
+                                const gchar        **attribute_values,
+                                gpointer             user_data,
+                                GError             **error)
+{
+  ParseContext *ctx = user_data;
+
+  if (strcmp (element_name, "alias") == 0)
+    {
+      start_alias (context, element_name, attribute_names, attribute_values,
+                  ctx, error);
+    }
+  else if (strcmp (element_name, "record") == 0)
+    {
+      const gchar *name;
+      const gchar *disguised;
+
+      name = find_attribute ("name", attribute_names, attribute_values);
+      disguised = find_attribute ("disguised", attribute_names, attribute_values);
+
+      if (disguised && strcmp (disguised, "1") == 0)
+       {
+         char *key;
+
+         key = g_strdup_printf ("%s.%s", ctx->namespace, name);
+         g_hash_table_replace (ctx->disguised_structures, key, GINT_TO_POINTER (1));
+       }
+    }
+}
+
+static void
+firstpass_end_element_handler (GMarkupParseContext *context,
+                              const gchar         *element_name,
+                              gpointer             user_data,
+                              GError             **error)
+{
+}
+
+static GMarkupParser firstpass_parser =
+{
+  firstpass_start_element_handler,
+  firstpass_end_element_handler,
+  NULL,
+  NULL,
+  NULL,
+};
+
+static char *
+locate_gir (GIrParser  *parser,
+           const char *girname)
+{
+  const gchar *const *datadirs;
+  const gchar *const *dir;
+  char *path = NULL;
+
+  datadirs = g_get_system_data_dirs ();
+
+  if (parser->includes != NULL)
+    {
+      for (dir = (const gchar *const *)parser->includes; *dir; dir++)
+       {
+         path = g_build_filename (*dir, girname, NULL);
+         if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+           return path;
+         g_free (path);
+         path = NULL;
+       }
+    }
+  for (dir = datadirs; *dir; dir++)
+    {
+      path = g_build_filename (*dir, GIR_SUFFIX, girname, NULL);
+      if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+       return path;
+      g_free (path);
+      path = NULL;
+    }
+
+  path = g_build_filename (GIR_DIR, girname, NULL);
+  if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+    return path;
+  g_free (path);
+  return NULL;
+}
+
 #define MISSING_ATTRIBUTE(ctx,error,element,attribute)                         \
   do {                                                                          \
     int line_number, char_number;                                                \
@@ -88,10 +279,10 @@ struct _ParseContext
 static void
 backtrace_stderr (void)
 {
+#if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)
   void *array[50];
-  int size;
+  int size, i;
   char **strings;
-  size_t i;
 
   size = backtrace (array, 50);
   strings = (char**) backtrace_symbols (array, size);
@@ -101,23 +292,24 @@ backtrace_stderr (void)
   for (i = 0; i < size; i++)
     fprintf (stderr, "%s\n", strings[i]);
 
-  fprintf (stderr, "--- END BACKTRACE ---\n", size);
+  fprintf (stderr, "--- END BACKTRACE ---\n");
 
   free (strings);
+#endif
 }
 
 
 static const gchar *
-find_attribute (const gchar  *name, 
+find_attribute (const gchar  *name,
                const gchar **attribute_names,
                const gchar **attribute_values)
 {
   gint i;
-  
+
   for (i = 0; attribute_names[i] != NULL; i++)
     if (strcmp (attribute_names[i], name) == 0)
       return attribute_values[i];
-  
+
   return 0;
 }
 
@@ -129,28 +321,37 @@ state_switch (ParseContext *ctx, ParseState newstate)
   ctx->state = newstate;
 }
 
-static GIrNodeType * parse_type_internal (gchar *str, gchar **rest);
-
-static GIrNodeType *
-create_pointer ()
+static GIrNode *
+pop_node (ParseContext *ctx)
 {
-  char *pointer = g_strdup ("any");
-  char *pointer_rest;
-  GIrNodeType *ret = parse_type_internal (pointer, &pointer_rest);
-  g_free (pointer);
-  return ret;
+  g_assert (ctx->node_stack != 0);
+
+  GSList *top = ctx->node_stack;
+  GIrNode *node = top->data;
+
+  g_debug ("popping node %d %s", node->type, node->name);
+  ctx->node_stack = top->next;
+  g_slist_free_1 (top);
+  return node;
 }
 
-static GIrNodeType *
-parse_type_internal (gchar *str, gchar **rest)
+static void
+push_node (ParseContext *ctx, GIrNode *node)
 {
-  gint i;
+  g_debug ("pushing node %d %s", node->type, node->name);
+  ctx->node_stack = g_slist_prepend (ctx->node_stack, node);
+}
+
+static GIrNodeType * parse_type_internal (const gchar *str, gchar **next, gboolean in_glib,
+                                         gboolean in_gobject);
+
+typedef struct {
+  const gchar *str;
+  gint tag;
+  gboolean pointer;
+} BasicTypeInfo;
 
-  static struct {
-    const gchar *str;
-    gint tag;
-    gboolean pointer;
-  } basic[] = {
+static BasicTypeInfo basic_types[] = {
     { "none",     GI_TYPE_TAG_VOID,    0 },
     { "any",      GI_TYPE_TAG_VOID,    1 },
 
@@ -164,6 +365,8 @@ parse_type_internal (gchar *str, gchar **rest)
     { "uint32",   GI_TYPE_TAG_UINT32,  0 },
     { "int64",    GI_TYPE_TAG_INT64,   0 },
     { "uint64",   GI_TYPE_TAG_UINT64,  0 },
+    { "short",    GI_TYPE_TAG_SHORT,   0 },
+    { "ushort",   GI_TYPE_TAG_USHORT,  0 },
     { "int",      GI_TYPE_TAG_INT,     0 },
     { "uint",     GI_TYPE_TAG_UINT,    0 },
     { "long",     GI_TYPE_TAG_LONG,    0 },
@@ -174,259 +377,164 @@ parse_type_internal (gchar *str, gchar **rest)
     { "size",     GI_TYPE_TAG_SIZE,    0 },
     { "float",    GI_TYPE_TAG_FLOAT,   0 },
     { "double",   GI_TYPE_TAG_DOUBLE,  0 },
-    { "utf8",     GI_TYPE_TAG_UTF8,    1 },  
+    { "time_t",   GI_TYPE_TAG_TIME_T,  0 },
+    { "GType",    GI_TYPE_TAG_GTYPE,   0 },
+    { "utf8",     GI_TYPE_TAG_UTF8,    1 },
     { "filename", GI_TYPE_TAG_FILENAME,1 },
+};
+
+static const BasicTypeInfo *
+parse_basic (const char *str)
+{
+  gint i;
+  gint n_basic = G_N_ELEMENTS (basic_types);
 
-    /* FIXME: merge - do we still want this? */
-    { "string",   GI_TYPE_TAG_UTF8,  1 },
-
-    /* FIXME: Remove these */
-    { "void",     GI_TYPE_TAG_VOID,    0 },
-    { "int8_t",   GI_TYPE_TAG_INT8,    0 },
-    { "uint8_t",  GI_TYPE_TAG_UINT8,   0 },
-    { "int16_t",  GI_TYPE_TAG_INT16,   0 },
-    { "uint16_t", GI_TYPE_TAG_UINT16,  0 },
-    { "int32_t",  GI_TYPE_TAG_INT32,   0 },
-    { "uint32_t", GI_TYPE_TAG_UINT32,  0 },
-    { "int64_t",  GI_TYPE_TAG_INT64,   0 },
-    { "uint64_t", GI_TYPE_TAG_UINT64,  0 },
-    { "gpointer", GI_TYPE_TAG_VOID,    1 },
-    { "gboolean", GI_TYPE_TAG_BOOLEAN, 0 },
-    { "gchar",    GI_TYPE_TAG_INT8,    0 },
-    { "guchar",   GI_TYPE_TAG_UINT8,   0 },
-    { "gunichar", GI_TYPE_TAG_UINT32,  0 },
-    { "gint",     GI_TYPE_TAG_INT,     0 },
-    { "guint",    GI_TYPE_TAG_UINT,    0 },
-    { "gint8",    GI_TYPE_TAG_INT8,    0 },
-    { "guint8",   GI_TYPE_TAG_UINT8,   0 },
-    { "gint16",   GI_TYPE_TAG_INT16,   0 },
-    { "guint16",  GI_TYPE_TAG_UINT16,  0 },
-    { "gint32",   GI_TYPE_TAG_INT32,   0 },
-    { "guint32",  GI_TYPE_TAG_UINT32,  0 },
-    { "gint64",   GI_TYPE_TAG_INT64,   0 },
-    { "guint64",  GI_TYPE_TAG_UINT64,  0 },
-    { "glong",    GI_TYPE_TAG_LONG,    0 },
-    { "gulong",   GI_TYPE_TAG_ULONG,   0 },
-    { "gssize",   GI_TYPE_TAG_SSIZE,   0 },
-    { "gsize",    GI_TYPE_TAG_SIZE,    0 },
-    { "gfloat",   GI_TYPE_TAG_FLOAT,   0 },
-    { "gdouble",  GI_TYPE_TAG_DOUBLE,  0 },
-    { "gchar*",   GI_TYPE_TAG_UTF8,    1 }
-  };  
-
-  gint n_basic = G_N_ELEMENTS (basic);
-  gchar *start, *end;
-  
+  for (i = 0; i < n_basic; i++)
+    {
+      if (g_str_has_prefix (str, basic_types[i].str))
+       return &(basic_types[i]);
+    }
+  return NULL;
+}
+
+static GIrNodeType *
+parse_type_internal (const gchar *str, char **next, gboolean in_glib,
+                    gboolean in_gobject)
+{
+  const BasicTypeInfo *basic;
   GIrNodeType *type;
-  
+  char *temporary_type = NULL;
+
   type = (GIrNodeType *)g_ir_node_new (G_IR_NODE_TYPE);
-  
-  str = g_strstrip (str);
 
   type->unparsed = g_strdup (str);
-  *rest = str;
-  for (i = 0; i < n_basic; i++)
+
+  /* See comment below on GLib.List handling */
+  if (in_gobject && strcmp (str, "Type") == 0)
     {
-      if (g_str_has_prefix (*rest, basic[i].str))
-       {
-         type->is_basic = TRUE;
-         type->tag = basic[i].tag;
-         type->is_pointer = basic[i].pointer;
+      temporary_type = g_strdup ("GLib.Type");
+      str = temporary_type;
+    }
 
-         *rest += strlen(basic[i].str);
-         *rest = g_strchug (*rest);
-         if (**rest == '*' && !type->is_pointer)
-           {
-             type->is_pointer = TRUE;
-             (*rest)++;
-           }
+  basic = parse_basic (str);
+  if (basic != NULL)
+    {
+      type->is_basic = TRUE;
+      type->tag = basic->tag;
+      type->is_pointer = basic->pointer;
 
-         break;
+      str += strlen(basic->str);
+    }
+  else if (in_glib)
+    {
+      /* If we're inside GLib, handle "List" etc. by prefixing with
+       * "GLib." so the parsing code below doesn't have to get more
+       * special.
+       */
+      if (g_str_has_prefix (str, "List<") ||
+         strcmp (str, "List") == 0)
+       {
+         temporary_type = g_strdup_printf ("GLib.List%s", str + 4);
+         str = temporary_type;
+       }
+      else if (g_str_has_prefix (str, "SList<") ||
+         strcmp (str, "SList") == 0)
+       {
+         temporary_type = g_strdup_printf ("GLib.SList%s", str + 5);
+         str = temporary_type;
+       }
+      else if (g_str_has_prefix (str, "HashTable<") ||
+         strcmp (str, "HashTable") == 0)
+       {
+         temporary_type = g_strdup_printf ("GLib.HashTable%s", str + 9);
+         str = temporary_type;
+       }
+      else if (g_str_has_prefix (str, "Error<") ||
+         strcmp (str, "Error") == 0)
+       {
+         temporary_type = g_strdup_printf ("GLib.Error%s", str + 5);
+         str = temporary_type;
        }
     }
 
-  if (i < n_basic)
+  if (basic != NULL)
     /* found a basic type */;
-  else if (g_str_has_prefix (*rest, "GList") ||
-          g_str_has_prefix (*rest, "GSList"))
+  else if (g_str_has_prefix (str, "GLib.List") ||
+          g_str_has_prefix (str, "GLib.SList"))
     {
-      if (g_str_has_prefix (*rest, "GList"))
+      str += strlen ("GLib.");
+      if (g_str_has_prefix (str, "List"))
        {
          type->tag = GI_TYPE_TAG_GLIST;
          type->is_glist = TRUE;
          type->is_pointer = TRUE;
-         *rest += strlen ("GList");
+         str += strlen ("List");
        }
       else
        {
          type->tag = GI_TYPE_TAG_GSLIST;
          type->is_gslist = TRUE;
          type->is_pointer = TRUE;
-         *rest += strlen ("GSList");
-       }
-      
-      *rest = g_strchug (*rest);
-      
-      if (**rest == '<')
-       {
-         (*rest)++;
-         
-         type->parameter_type1 = parse_type_internal (*rest, rest);
-         if (type->parameter_type1 == NULL)
-           goto error;
-         
-         *rest = g_strchug (*rest);
-         
-         if ((*rest)[0] != '>')
-           goto error;
-         (*rest)++;
-       }
-      else
-       {
-         type->parameter_type1 = create_pointer ();
+         str += strlen ("SList");
        }
     }
-  else if (g_str_has_prefix (*rest, "GHashTable"))
+  else if (g_str_has_prefix (str, "GLib.HashTable"))
     {
+      str += strlen ("GLib.");
+
       type->tag = GI_TYPE_TAG_GHASH;
       type->is_ghashtable = TRUE;
       type->is_pointer = TRUE;
-      *rest += strlen ("GHashTable");
-      
-      *rest = g_strchug (*rest);
-      
-      if (**rest == '<')
-       {
-         (*rest)++;
-      
-         type->parameter_type1 = parse_type_internal (*rest, rest);
-         if (type->parameter_type1 == NULL)
-           goto error;
-      
-         *rest = g_strchug (*rest);
-         
-         if ((*rest)[0] != ',')
-           goto error;
-         (*rest)++;
-         
-         type->parameter_type2 = parse_type_internal (*rest, rest);
-         if (type->parameter_type2 == NULL)
-           goto error;
-      
-         if ((*rest)[0] != '>')
-           goto error;
-         (*rest)++;
-       }
-      else
-       {
-         type->parameter_type1 = create_pointer ();
-         type->parameter_type2 = create_pointer ();
-       }
-
+      str += strlen ("HashTable");
     }
-  else if (g_str_has_prefix (*rest, "GError"))
+  else if (g_str_has_prefix (str, "GLib.Error"))
     {
+      str += strlen ("GLib.");
+
       type->tag = GI_TYPE_TAG_ERROR;
       type->is_error = TRUE;
       type->is_pointer = TRUE;
-      *rest += strlen ("GError");
-      
-      *rest = g_strchug (*rest);
-      
-      if (**rest == '<')
-       {
-         (*rest)++;
-         
-         end = strchr (*rest, '>');
-         str = g_strndup (*rest, end - *rest);
-         type->errors = g_strsplit (str, ",", 0);
-         g_free (str);
-         
-         *rest = end + 1;
-       }
-    }
-  else 
-    {
-      type->tag = GI_TYPE_TAG_INTERFACE;
-      type->is_interface = TRUE; 
-      start = *rest;
+      str += strlen ("Error");
 
-      /* must be an interface type */
-      while (g_ascii_isalnum (**rest) || 
-            **rest == '.' || 
-            **rest == '-' || 
-            **rest == '_' ||
-            **rest == ':')
-       (*rest)++;
+      if (*str == '<')
+       {
+         (str)++;
+         char *tmp, *end;
 
-      type->interface = g_strndup (start, *rest - start);
+         end = strchr (str, '>');
+         tmp = g_strndup (str, end - str);
+         type->errors = g_strsplit (tmp, ",", 0);
+         g_free (tmp);
 
-      *rest = g_strchug (*rest);
-      if (**rest == '*')
-       {
-         type->is_pointer = TRUE;
-         (*rest)++;
+         str = end;
        }
     }
-  
-  *rest = g_strchug (*rest);
-  if (g_str_has_prefix (*rest, "["))
+  else
     {
-      GIrNodeType *array;
-
-      array = (GIrNodeType *)g_ir_node_new (G_IR_NODE_TYPE);
-
-      array->tag = GI_TYPE_TAG_ARRAY;
-      array->is_pointer = TRUE;
-      array->is_array = TRUE;
-      
-      array->parameter_type1 = type;
-
-      array->zero_terminated = FALSE;
-      array->has_length = FALSE;
-      array->length = 0;
-
-      if (!g_str_has_prefix (*rest, "[]"))
-       {
-         gchar *end, *str, **opts;
-         
-         end = strchr (*rest, ']');
-         str = g_strndup (*rest + 1, (end - *rest) - 1); 
-         opts = g_strsplit (str, ",", 0);
-         
-         *rest = end + 1;
-
-         for (i = 0; opts[i]; i++)
-           {
-             gchar **vals;
-             
-             vals = g_strsplit (opts[i], "=", 0);
-
-             if (strcmp (vals[0], "zero-terminated") == 0)
-               array->zero_terminated = (strcmp (vals[1], "1") == 0);
-             else if (strcmp (vals[0], "length") == 0)
-               {
-                 array->has_length = TRUE;
-                 array->length = atoi (vals[1]);
-               }
-
-             g_strfreev (vals);
-           }
+      type->tag = GI_TYPE_TAG_INTERFACE;
+      type->is_interface = TRUE;
+      const char *start = str;
 
-         g_free (str);
-         g_strfreev (opts);
-       }
-             
-      type = array;
+      /* must be an interface type */
+      while (g_ascii_isalnum (*str) ||
+            *str == '.' ||
+            *str == '-' ||
+            *str == '_' ||
+            *str == ':')
+       (str)++;
+
+      type->interface = g_strndup (start, str - start);
     }
 
+  if (next)
+    *next = (char*)str;
   g_assert (type->tag >= 0 && type->tag <= GI_TYPE_TAG_ERROR);
+  g_free (temporary_type);
   return type;
 
- error:
+/* error: */
   g_ir_node_free ((GIrNode *)type);
-  
+  g_free (temporary_type);
   return NULL;
 }
 
@@ -435,26 +543,87 @@ resolve_aliases (ParseContext *ctx, const gchar *type)
 {
   gpointer orig;
   gpointer value;
+  GSList *seen_values = NULL;
+  const gchar *lookup;
+  gchar *prefixed;
 
-  while (g_hash_table_lookup_extended (ctx->aliases, type, &orig, &value))
+  if (strchr (type, '.') == NULL)
     {
-      g_debug ("Resolved: %s => %s", type, value);
-      type = value;
+      prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type);
+      lookup = prefixed;
     }
-  return type;
+  else
+    {
+      lookup = type;
+      prefixed = NULL;
+    }
+
+  seen_values = g_slist_prepend (seen_values, (char*)lookup);
+  while (g_hash_table_lookup_extended (ctx->current_module->aliases, lookup, &orig, &value))
+    {
+      g_debug ("Resolved: %s => %s\n", lookup, (char*)value);
+      lookup = value;
+      if (g_slist_find_custom (seen_values, lookup,
+                              (GCompareFunc)strcmp) != NULL)
+       break;
+      seen_values = g_slist_prepend (seen_values, (gchar*)lookup);
+    }
+  g_slist_free (seen_values);
+
+  if (lookup == prefixed)
+    lookup = type;
+
+  g_free (prefixed);
+
+  return lookup;
+}
+
+static gboolean
+is_disguised_structure (ParseContext *ctx, const gchar *type)
+{
+  const gchar *lookup;
+  gchar *prefixed;
+  gboolean result;
+
+  if (strchr (type, '.') == NULL)
+    {
+      prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type);
+      lookup = prefixed;
+    }
+  else
+    {
+      lookup = type;
+      prefixed = NULL;
+    }
+
+  result = g_hash_table_lookup (ctx->current_module->disguised_structures,
+                               lookup) != NULL;
+
+  g_free (prefixed);
+
+  return result;
 }
 
 static GIrNodeType *
 parse_type (ParseContext *ctx, const gchar *type)
 {
-  gchar *str;
-  gchar *rest;
   GIrNodeType *node;
-  
-  type = resolve_aliases (ctx, type);
-  str = g_strdup (type);
-  node = parse_type_internal (str, &rest);
-  g_free (str);
+  const BasicTypeInfo *basic;
+  gboolean in_glib, in_gobject;
+
+  in_glib = strcmp (ctx->namespace, "GLib") == 0;
+  in_gobject = strcmp (ctx->namespace, "GObject") == 0;
+
+  /* Do not search aliases for basic types */
+  basic = parse_basic (type);
+  if (basic == NULL)
+    type = resolve_aliases (ctx, type);
+
+  node = parse_type_internal (type, NULL, in_glib, in_gobject);
+  if (node)
+    g_debug ("Parsed type: %s => %d", type, node->tag);
+  else
+    g_critical ("Failed to parse type: '%s'", type);
 
   return node;
 }
@@ -467,179 +636,238 @@ start_glib_boxed (GMarkupParseContext *context,
                  ParseContext        *ctx,
                  GError             **error)
 {
-  if (strcmp (element_name, "glib:boxed") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+  const gchar *name;
+  const gchar *typename;
+  const gchar *typeinit;
+  const gchar *deprecated;
+  GIrNodeBoxed *boxed;
+
+  if (!(strcmp (element_name, "glib:boxed") == 0 &&
+       ctx->state == STATE_NAMESPACE))
+    return FALSE;
+
+  name = find_attribute ("glib:name", attribute_names, attribute_values);
+  typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
+  typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
+  deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
+
+  if (name == NULL)
     {
-      const gchar *name;
-      const gchar *typename;
-      const gchar *typeinit;
-      const gchar *deprecated;
-      
-      name = find_attribute ("glib:name", attribute_names, attribute_values);
-      typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
-      typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
-      deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
-      if (name == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "glib:name");
-      else if (typename == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
-      else if (typeinit == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
-      else
-       {
-         GIrNodeBoxed *boxed;
+      MISSING_ATTRIBUTE (context, error, element_name, "glib:name");
+      return FALSE;
+    }
+  else if (typename == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
+      return FALSE;
+    }
+  else if (typeinit == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
+      return FALSE;
+    }
 
-         boxed = (GIrNodeBoxed *) g_ir_node_new (G_IR_NODE_BOXED);
-         
-         ((GIrNode *)boxed)->name = g_strdup (name);
-         boxed->gtype_name = g_strdup (typename);
-         boxed->gtype_init = g_strdup (typeinit);
-         if (deprecated && strcmp (deprecated, "1") == 0)
-           boxed->deprecated = TRUE;
-         else
-           boxed->deprecated = FALSE;
-         
-         ctx->current_node = (GIrNode *)boxed;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, boxed);
-         
-         state_switch (ctx, STATE_BOXED);
-       }
-         
-      return TRUE;
+  boxed = (GIrNodeBoxed *) g_ir_node_new (G_IR_NODE_BOXED);
+
+  ((GIrNode *)boxed)->name = g_strdup (name);
+  boxed->gtype_name = g_strdup (typename);
+  boxed->gtype_init = g_strdup (typeinit);
+  if (deprecated)
+    boxed->deprecated = TRUE;
+  else
+    boxed->deprecated = FALSE;
+
+  push_node (ctx, (GIrNode *)boxed);
+  ctx->current_module->entries =
+    g_list_append (ctx->current_module->entries, boxed);
+
+  state_switch (ctx, STATE_BOXED);
+
+  return TRUE;
+}
+
+static gboolean
+start_function (GMarkupParseContext *context,
+               const gchar         *element_name,
+               const gchar        **attribute_names,
+               const gchar        **attribute_values,
+               ParseContext        *ctx,
+               GError             **error)
+{
+  const gchar *name;
+  const gchar *symbol;
+  const gchar *deprecated;
+  const gchar *throws;
+  GIrNodeFunction *function;
+  gboolean found = FALSE;
+
+  switch (ctx->state)
+    {
+    case STATE_NAMESPACE:
+      found = (strcmp (element_name, "function") == 0 ||
+              strcmp (element_name, "callback") == 0);
+      break;
+    case STATE_CLASS:
+      found = strcmp (element_name, "function") == 0;
+       /* fallthrough */
+    case STATE_BOXED:
+    case STATE_STRUCT:
+    case STATE_UNION:
+      found = (found || strcmp (element_name, "constructor") == 0);
+      /* fallthrough */
+    case STATE_INTERFACE:
+      found = (found ||
+              strcmp (element_name, "method") == 0 ||
+              strcmp (element_name, "callback") == 0);
+      break;
+    case STATE_STRUCT_FIELD:
+      ctx->in_embedded_type = TRUE;
+      found = (found || strcmp (element_name, "callback") == 0);
+      break;
+    default:
+      break;
+    }
+
+  if (!found)
+    return FALSE;
+
+  name = find_attribute ("name", attribute_names, attribute_values);
+  symbol = find_attribute ("c:identifier", attribute_names, attribute_values);
+  deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
+  throws = find_attribute ("throws", attribute_names, attribute_values);
+
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
+  else if (strcmp (element_name, "callback") != 0 && symbol == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "c:identifier");
+      return FALSE;
+    }
+
+  function = (GIrNodeFunction *) g_ir_node_new (G_IR_NODE_FUNCTION);
+
+  ((GIrNode *)function)->name = g_strdup (name);
+  function->symbol = g_strdup (symbol);
+  function->parameters = NULL;
+  if (deprecated)
+    function->deprecated = TRUE;
+  else
+    function->deprecated = FALSE;
+
+  if (strcmp (element_name, "method") == 0 ||
+      strcmp (element_name, "constructor") == 0)
+    {
+      function->is_method = TRUE;
+
+      if (strcmp (element_name, "constructor") == 0)
+       function->is_constructor = TRUE;
+      else
+       function->is_constructor = FALSE;
+    }
+  else
+    {
+      function->is_method = FALSE;
+      function->is_setter = FALSE;
+      function->is_getter = FALSE;
+      function->is_constructor = FALSE;
+      if (strcmp (element_name, "callback") == 0)
+       ((GIrNode *)function)->type = G_IR_NODE_CALLBACK;
     }
 
-  return FALSE;
-}
+  if (throws && strcmp (throws, "1") == 0)
+    function->throws = TRUE;
+  else
+    function->throws = FALSE;
 
-static gboolean
-start_function (GMarkupParseContext *context,
-               const gchar         *element_name,
-               const gchar        **attribute_names,
-               const gchar        **attribute_values,
-               ParseContext        *ctx,
-               GError             **error)
-{
-  if ((ctx->state == STATE_NAMESPACE &&
-       (strcmp (element_name, "function") == 0 ||
-       strcmp (element_name, "callback") == 0)) ||
-      ((ctx->state == STATE_CLASS ||
-       ctx->state == STATE_INTERFACE ||
-       ctx->state == STATE_BOXED ||
-        ctx->state == STATE_UNION) &&
-       (strcmp (element_name, "method") == 0 || 
-       strcmp (element_name, "callback") == 0)) ||
-      ((ctx->state == STATE_CLASS ||
-       ctx->state == STATE_BOXED) &&
-       (strcmp (element_name, "constructor") == 0)) ||
-      (ctx->state == STATE_STRUCT && strcmp (element_name, "callback") == 0))
+  if (ctx->node_stack == NULL)
     {
-      const gchar *name;
-      const gchar *symbol;
-      const gchar *deprecated;
-      const gchar *type;
-      
-      name = find_attribute ("name", attribute_names, attribute_values);
-      symbol = find_attribute ("c:identifier", attribute_names, attribute_values);
-      deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      type = find_attribute ("type", attribute_names, attribute_values);
-      
-      if (name == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else if (strcmp (element_name, "callback") != 0 && symbol == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "c:identifier");
-      else
-       {
-         GIrNodeFunction *function;
-         
-         function = (GIrNodeFunction *) g_ir_node_new (G_IR_NODE_FUNCTION);
+      ctx->current_module->entries =
+       g_list_append (ctx->current_module->entries, function);
+    }
+  else if (ctx->current_typed)
+    {
+      GIrNodeField *field;
 
-         ((GIrNode *)function)->name = g_strdup (name);
-         function->symbol = g_strdup (symbol);
-         function->parameters = NULL;
-         if (deprecated && strcmp (deprecated, "1") == 0)
-           function->deprecated = TRUE;
-         else
-           function->deprecated = FALSE;
-       
-         if (strcmp (element_name, "method") == 0 ||
-             strcmp (element_name, "constructor") == 0)
-           {
-             function->is_method = TRUE;
+      field = (GIrNodeField *)ctx->current_typed;
+      field->callback = function;
+    }
+  else
+    switch (CURRENT_NODE (ctx)->type)
+      {
+      case G_IR_NODE_INTERFACE:
+      case G_IR_NODE_OBJECT:
+       {
+         GIrNodeInterface *iface;
 
-             if (type && strcmp (type, "setter") == 0)
-               function->is_setter = TRUE;
-             else if (type && strcmp (type, "getter") == 0)
-               function->is_getter = TRUE;               
+         iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
+         iface->members = g_list_append (iface->members, function);
+       }
+       break;
+      case G_IR_NODE_BOXED:
+       {
+         GIrNodeBoxed *boxed;
 
-             if (strcmp (element_name, "constructor") == 0)
-               function->is_constructor = TRUE;
-             else
-               function->is_constructor = FALSE;
-           }
-         else
-           {
-             function->is_method = FALSE;
-             function->is_setter = FALSE;
-             function->is_getter = FALSE;
-             function->is_constructor = FALSE;
-             if (strcmp (element_name, "callback") == 0)
-               ((GIrNode *)function)->type = G_IR_NODE_CALLBACK;
-           }
-         
-         if (ctx->current_node == NULL)
-           {
-             ctx->current_module->entries = 
-               g_list_append (ctx->current_module->entries, function);       
-           }
-         else
-           switch (ctx->current_node->type)
-             {
-             case G_IR_NODE_INTERFACE:
-             case G_IR_NODE_OBJECT:
-               {
-                 GIrNodeInterface *iface;
-                 
-                 iface = (GIrNodeInterface *)ctx->current_node;
-                 iface->members = g_list_append (iface->members, function);
-               }
-               break;
-             case G_IR_NODE_BOXED:
-               {
-                 GIrNodeBoxed *boxed;
+         boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx);
+         boxed->members = g_list_append (boxed->members, function);
+       }
+       break;
+      case G_IR_NODE_STRUCT:
+       {
+         GIrNodeStruct *struct_;
 
-                 boxed = (GIrNodeBoxed *)ctx->current_node;
-                 boxed->members = g_list_append (boxed->members, function);
-               }
-               break;
-             case G_IR_NODE_STRUCT:
-               {
-                 GIrNodeStruct *struct_;
-                 
-                 struct_ = (GIrNodeStruct *)ctx->current_node;
-                 struct_->members = g_list_append (struct_->members, function);                }
-               break;
-             case G_IR_NODE_UNION:
-               {
-                 GIrNodeUnion *union_;
-                 
-                 union_ = (GIrNodeUnion *)ctx->current_node;
-                 union_->members = g_list_append (union_->members, function);
-               }
-               break;
-             default:
-               g_assert_not_reached ();
-             }
-         
-         ctx->current_node = (GIrNode *)function;
-         state_switch (ctx, STATE_FUNCTION);
+         struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx);
+         struct_->members = g_list_append (struct_->members, function);                }
+       break;
+      case G_IR_NODE_UNION:
+       {
+         GIrNodeUnion *union_;
 
-         return TRUE;
+         union_ = (GIrNodeUnion *)CURRENT_NODE (ctx);
+         union_->members = g_list_append (union_->members, function);
        }
-    }
+       break;
+      default:
+       g_assert_not_reached ();
+      }
 
-  return FALSE;
+  push_node(ctx, (GIrNode *)function);
+  state_switch (ctx, STATE_FUNCTION);
+
+  return TRUE;
+}
+
+static void
+parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name)
+{
+  if (transfer == NULL)
+  {
+    if (!name)
+      g_warning ("required attribute 'transfer-ownership' missing");
+    else
+      g_warning ("required attribute 'transfer-ownership' for function '%s'", name);
+  }
+  else if (strcmp (transfer, "none") == 0)
+    {
+      param->transfer = FALSE;
+      param->shallow_transfer = FALSE;
+    }
+  else if (strcmp (transfer, "container") == 0)
+    {
+      param->transfer = FALSE;
+      param->shallow_transfer = TRUE;
+    }
+  else if (strcmp (transfer, "full") == 0)
+    {
+      param->transfer = TRUE;
+      param->shallow_transfer = FALSE;
+    }
+  else
+    {
+      g_warning ("Unknown transfer-ownership value: %s", transfer);
+    }
 }
 
 static gboolean
@@ -655,10 +883,13 @@ start_parameter (GMarkupParseContext *context,
   const gchar *retval;
   const gchar *dipper;
   const gchar *optional;
-  const gchar *nullok;
+  const gchar *allow_none;
   const gchar *transfer;
+  const gchar *scope;
+  const gchar *closure;
+  const gchar *destroy;
   GIrNodeParam *param;
-      
+
   if (!(strcmp (element_name, "parameter") == 0 &&
        ctx->state == STATE_FUNCTION_PARAMETERS))
     return FALSE;
@@ -668,8 +899,11 @@ start_parameter (GMarkupParseContext *context,
   retval = find_attribute ("retval", attribute_names, attribute_values);
   dipper = find_attribute ("dipper", attribute_names, attribute_values);
   optional = find_attribute ("optional", attribute_names, attribute_values);
-  nullok = find_attribute ("null-ok", attribute_names, attribute_values);
-  transfer = find_attribute ("transfer", attribute_names, attribute_values);
+  allow_none = find_attribute ("allow-none", attribute_names, attribute_values);
+  transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
+  scope = find_attribute ("scope", attribute_names, attribute_values);
+  closure = find_attribute ("closure", attribute_names, attribute_values);
+  destroy = find_attribute ("destroy", attribute_names, attribute_values);
 
   if (name == NULL)
     name = "unknown";
@@ -712,37 +946,35 @@ start_parameter (GMarkupParseContext *context,
   else
     param->optional = FALSE;
 
-  if (nullok && strcmp (nullok, "1") == 0)
-    param->null_ok = TRUE;
+  if (allow_none && strcmp (allow_none, "1") == 0)
+    param->allow_none = TRUE;
   else
-    param->null_ok = FALSE;
+    param->allow_none = FALSE;
 
-  if (transfer && strcmp (transfer, "none") == 0)
-    {
-      param->transfer = FALSE;
-      param->shallow_transfer = FALSE;
-    }
-  else if (transfer && strcmp (transfer, "shallow") == 0)
-    {
-      param->transfer = FALSE;
-      param->shallow_transfer = TRUE;
-    }
+  parse_param_transfer (param, transfer, name);
+
+  if (scope && strcmp (scope, "call") == 0)
+    param->scope = GI_SCOPE_TYPE_CALL;
+  else if (scope && strcmp (scope, "async") == 0)
+    param->scope = GI_SCOPE_TYPE_ASYNC;
+  else if (scope && strcmp (scope, "notified") == 0)
+    param->scope = GI_SCOPE_TYPE_NOTIFIED;
   else
-    {
-      param->transfer = TRUE;
-      param->shallow_transfer = FALSE;
-    }
-         
+    param->scope = GI_SCOPE_TYPE_INVALID;
+
+  param->closure = closure ? atoi (closure) : -1;
+  param->destroy = destroy ? atoi (destroy) : -1;
+
   ((GIrNode *)param)->name = g_strdup (name);
-         
-  switch (ctx->current_node->type)
+
+  switch (CURRENT_NODE (ctx)->type)
     {
     case G_IR_NODE_FUNCTION:
     case G_IR_NODE_CALLBACK:
       {
        GIrNodeFunction *func;
 
-       func = (GIrNodeFunction *)ctx->current_node;
+       func = (GIrNodeFunction *)CURRENT_NODE (ctx);
        func->parameters = g_list_append (func->parameters, param);
       }
       break;
@@ -750,15 +982,15 @@ start_parameter (GMarkupParseContext *context,
       {
        GIrNodeSignal *signal;
 
-       signal = (GIrNodeSignal *)ctx->current_node;
+       signal = (GIrNodeSignal *)CURRENT_NODE (ctx);
        signal->parameters = g_list_append (signal->parameters, param);
       }
       break;
     case G_IR_NODE_VFUNC:
       {
        GIrNodeVFunc *vfunc;
-               
-       vfunc = (GIrNodeVFunc *)ctx->current_node;
+
+       vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx);
        vfunc->parameters = g_list_append (vfunc->parameters, param);
       }
       break;
@@ -777,123 +1009,118 @@ start_field (GMarkupParseContext *context,
             ParseContext        *ctx,
             GError             **error)
 {
-  if (strcmp (element_name, "field") == 0 &&
-      (ctx->state == STATE_CLASS ||
-       ctx->state == STATE_BOXED ||
-       ctx->state == STATE_STRUCT ||
-       ctx->state == STATE_UNION ||
-       ctx->state == STATE_INTERFACE))
+  const gchar *name;
+  const gchar *readable;
+  const gchar *writable;
+  const gchar *bits;
+  const gchar *branch;
+  GIrNodeField *field;
+
+  switch (ctx->state)
     {
-      const gchar *name;
-      const gchar *readable;
-      const gchar *writable;
-      const gchar *bits;
-      const gchar *branch;
-      const gchar *offset;
-      
-      name = find_attribute ("name", attribute_names, attribute_values);
-      readable = find_attribute ("readable", attribute_names, attribute_values);
-      writable = find_attribute ("writable", attribute_names, attribute_values);
-      bits = find_attribute ("bits", attribute_names, attribute_values);
-      branch = find_attribute ("branch", attribute_names, attribute_values);
-      offset = find_attribute ("offset", attribute_names, attribute_values);
-      
-      if (name == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else
-       {
-         GIrNodeField *field;
+    case STATE_CLASS:
+    case STATE_BOXED:
+    case STATE_STRUCT:
+    case STATE_UNION:
+    case STATE_INTERFACE:
+      break;
+    default:
+      return FALSE;
+    }
 
-         field = (GIrNodeField *)g_ir_node_new (G_IR_NODE_FIELD);
-         ctx->current_typed = (GIrNode*) field;
-         ((GIrNode *)field)->name = g_strdup (name);
-         if (readable && strcmp (readable, "1") == 0)
-           field->readable = TRUE;
-         else
-           field->readable = FALSE;
-         
-         if (writable && strcmp (writable, "1") == 0)
-           field->writable = TRUE;
-         else
-           field->writable = FALSE;
-         
-         if (bits)
-           field->bits = atoi (bits);
-         else
-           field->bits = 0;
+  if (strcmp (element_name, "field") != 0)
+    return FALSE;
 
-         if (offset)
-           field->offset = atoi (offset);
-         else
-           field->offset = 0;
-         
-         switch (ctx->current_node->type)
-           {
-           case G_IR_NODE_OBJECT:
-             {
-               GIrNodeInterface *iface;
+  name = find_attribute ("name", attribute_names, attribute_values);
+  readable = find_attribute ("readable", attribute_names, attribute_values);
+  writable = find_attribute ("writable", attribute_names, attribute_values);
+  bits = find_attribute ("bits", attribute_names, attribute_values);
+  branch = find_attribute ("branch", attribute_names, attribute_values);
 
-               iface = (GIrNodeInterface *)ctx->current_node;
-               iface->members = g_list_append (iface->members, field);
-               state_switch (ctx, STATE_CLASS_FIELD);
-             }
-             break;
-           case G_IR_NODE_INTERFACE:
-             {
-               GIrNodeInterface *iface;
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
 
-               iface = (GIrNodeInterface *)ctx->current_node;
-               iface->members = g_list_append (iface->members, field);
-               state_switch (ctx, STATE_INTERFACE_FIELD);
-             }
-             break;
-           case G_IR_NODE_BOXED:
-             {
-               GIrNodeBoxed *boxed;
+  field = (GIrNodeField *)g_ir_node_new (G_IR_NODE_FIELD);
+  ctx->current_typed = (GIrNode*) field;
+  ((GIrNode *)field)->name = g_strdup (name);
+  /* Fields are assumed to be read-only.
+   * (see also girwriter.py and generate.c)
+   */
+  field->readable = readable == NULL || strcmp (readable, "0") == 0;
+  field->writable = writable != NULL && strcmp (writable, "1") == 0;
+
+  if (bits)
+    field->bits = atoi (bits);
+  else
+    field->bits = 0;
 
-               boxed = (GIrNodeBoxed *)ctx->current_node;
+  switch (CURRENT_NODE (ctx)->type)
+    {
+    case G_IR_NODE_OBJECT:
+      {
+       GIrNodeInterface *iface;
+
+       iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
+       iface->members = g_list_append (iface->members, field);
+       state_switch (ctx, STATE_CLASS_FIELD);
+      }
+      break;
+    case G_IR_NODE_INTERFACE:
+      {
+       GIrNodeInterface *iface;
+
+       iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
+       iface->members = g_list_append (iface->members, field);
+       state_switch (ctx, STATE_INTERFACE_FIELD);
+      }
+      break;
+    case G_IR_NODE_BOXED:
+      {
+       GIrNodeBoxed *boxed;
+
+       boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx);
                boxed->members = g_list_append (boxed->members, field);
                state_switch (ctx, STATE_BOXED_FIELD);
-             }
-             break;
-           case G_IR_NODE_STRUCT:
-             {
-               GIrNodeStruct *struct_;
+      }
+      break;
+    case G_IR_NODE_STRUCT:
+      {
+       GIrNodeStruct *struct_;
 
-               struct_ = (GIrNodeStruct *)ctx->current_node;
-               struct_->members = g_list_append (struct_->members, field);
-               state_switch (ctx, STATE_STRUCT_FIELD);
-             }
-             break;
-           case G_IR_NODE_UNION:
-             {
-               GIrNodeUnion *union_;
-
-               union_ = (GIrNodeUnion *)ctx->current_node;
-               union_->members = g_list_append (union_->members, field);
-               if (branch)
-                 {
-                   GIrNodeConstant *constant;
-                   
-                   constant = (GIrNodeConstant *) g_ir_node_new (G_IR_NODE_CONSTANT);
-                   ((GIrNode *)constant)->name = g_strdup (name);
-                   constant->value = g_strdup (branch);          
-                   constant->type = union_->discriminator_type;
-                   constant->deprecated = FALSE;
-
-                   union_->discriminators = g_list_append (union_->discriminators, constant);
-                 }
-               state_switch (ctx, STATE_UNION_FIELD);
-             }
-             break;
-           default:
-             g_assert_not_reached ();
-           }
-       }
-      return TRUE;
+       struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx);
+       struct_->members = g_list_append (struct_->members, field);
+       state_switch (ctx, STATE_STRUCT_FIELD);
+      }
+      break;
+    case G_IR_NODE_UNION:
+      {
+       GIrNodeUnion *union_;
+
+       union_ = (GIrNodeUnion *)CURRENT_NODE (ctx);
+       union_->members = g_list_append (union_->members, field);
+       if (branch)
+         {
+           GIrNodeConstant *constant;
+
+           constant = (GIrNodeConstant *) g_ir_node_new (G_IR_NODE_CONSTANT);
+           ((GIrNode *)constant)->name = g_strdup (name);
+           constant->value = g_strdup (branch);
+           constant->type = union_->discriminator_type;
+           constant->deprecated = FALSE;
+
+           union_->discriminators = g_list_append (union_->discriminators, constant);
+         }
+       state_switch (ctx, STATE_UNION_FIELD);
+      }
+      break;
+    default:
+      g_assert_not_reached ();
     }
-  
-  return FALSE;
+
+  return TRUE;
 }
 
 static gboolean
@@ -906,21 +1133,37 @@ start_alias (GMarkupParseContext *context,
 {
   const gchar *name;
   const gchar *target;
-  const gchar *type;
+  char *key;
+  char *value;
 
   name = find_attribute ("name", attribute_names, attribute_values);
-  if (name == NULL) {
-    MISSING_ATTRIBUTE (context, error, element_name, "name");
-    return FALSE;
-  }
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
 
   target = find_attribute ("target", attribute_names, attribute_values);
-  if (name == NULL) {
-    MISSING_ATTRIBUTE (context, error, element_name, "target");
-    return FALSE;
-  }
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "target");
+      return FALSE;
+    }
+
+  value = g_strdup (target);
+  key = g_strdup_printf ("%s.%s", ctx->namespace, name);
+  if (!strchr (target, '.'))
+    {
+      const BasicTypeInfo *basic = parse_basic (target);
+      if (!basic)
+       {
+         g_free (value);
+         /* For non-basic types, re-qualify the interface */
+         value = g_strdup_printf ("%s.%s", ctx->namespace, target);
+       }
+    }
+  g_hash_table_replace (ctx->aliases, key, value);
 
-  g_hash_table_insert (ctx->aliases, g_strdup (name), g_strdup (target));
   return TRUE;
 }
 
@@ -939,18 +1182,18 @@ start_enum (GMarkupParseContext *context,
       const gchar *typename;
       const gchar *typeinit;
       const gchar *deprecated;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else 
-       {             
+      else
+       {
          GIrNodeEnum *enum_;
-         
+
          if (strcmp (element_name, "enumeration") == 0)
            enum_ = (GIrNodeEnum *) g_ir_node_new (G_IR_NODE_ENUM);
          else
@@ -958,18 +1201,18 @@ start_enum (GMarkupParseContext *context,
          ((GIrNode *)enum_)->name = g_strdup (name);
          enum_->gtype_name = g_strdup (typename);
          enum_->gtype_init = g_strdup (typeinit);
-         if (deprecated && strcmp (deprecated, "1") == 0)
+         if (deprecated)
            enum_->deprecated = TRUE;
          else
            enum_->deprecated = FALSE;
 
-         ctx->current_node = (GIrNode *) enum_;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, enum_);              
-         
+         push_node (ctx, (GIrNode *) enum_);
+         ctx->current_module->entries =
+           g_list_append (ctx->current_module->entries, enum_);
+
          state_switch (ctx, STATE_ENUM);
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -992,26 +1235,27 @@ start_property (GMarkupParseContext *context,
       const gchar *writable;
       const gchar *construct;
       const gchar *construct_only;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       readable = find_attribute ("readable", attribute_names, attribute_values);
       writable = find_attribute ("writable", attribute_names, attribute_values);
       construct = find_attribute ("construct", attribute_names, attribute_values);
       construct_only = find_attribute ("construct-only", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else 
-       {             
+      else
+       {
          GIrNodeProperty *property;
          GIrNodeInterface *iface;
-         
+
          property = (GIrNodeProperty *) g_ir_node_new (G_IR_NODE_PROPERTY);
          ctx->current_typed = (GIrNode*) property;
 
          ((GIrNode *)property)->name = g_strdup (name);
-         
-         if (readable && strcmp (readable, "1") == 0)
+
+         /* Assume properties are readable */
+         if (readable == NULL || strcmp (readable, "1") == 0)
            property->readable = TRUE;
          else
            property->readable = FALSE;
@@ -1028,7 +1272,7 @@ start_property (GMarkupParseContext *context,
          else
            property->construct_only = FALSE;
 
-         iface = (GIrNodeInterface *)ctx->current_node;
+         iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
          iface->members = g_list_append (iface->members, property);
 
          if (ctx->state == STATE_CLASS)
@@ -1038,7 +1282,7 @@ start_property (GMarkupParseContext *context,
          else
            g_assert_not_reached ();
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1048,7 +1292,7 @@ static gint
 parse_value (const gchar *str)
 {
   gchar *shift_op;
+
   /* FIXME just a quick hack */
   shift_op = strstr (str, "<<");
 
@@ -1058,7 +1302,7 @@ parse_value (const gchar *str)
 
       base = strtol (str, NULL, 10);
       shift = strtol (shift_op + 3, NULL, 10);
-      
+
       return base << shift;
     }
   else
@@ -1081,33 +1325,33 @@ start_member (GMarkupParseContext *context,
       const gchar *name;
       const gchar *value;
       const gchar *deprecated;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       value = find_attribute ("value", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else 
-       {             
+      else
+       {
          GIrNodeEnum *enum_;
          GIrNodeValue *value_;
 
          value_ = (GIrNodeValue *) g_ir_node_new (G_IR_NODE_VALUE);
 
          ((GIrNode *)value_)->name = g_strdup (name);
-         
+
          value_->value = parse_value (value);
-         
-         if (deprecated && strcmp (deprecated, "1") == 0)
+
+         if (deprecated)
            value_->deprecated = TRUE;
          else
            value_->deprecated = FALSE;
 
-         enum_ = (GIrNodeEnum *)ctx->current_node;
+         enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx);
          enum_->values = g_list_append (enum_->values, value_);
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1129,17 +1373,17 @@ start_constant (GMarkupParseContext *context,
       const gchar *name;
       const gchar *value;
       const gchar *deprecated;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       value = find_attribute ("value", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else if (value == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "value");
-      else 
-       {             
+      else
+       {
          GIrNodeConstant *constant;
 
          constant = (GIrNodeConstant *) g_ir_node_new (G_IR_NODE_CONSTANT);
@@ -1149,22 +1393,22 @@ start_constant (GMarkupParseContext *context,
 
          ctx->current_typed = (GIrNode*) constant;
 
-         if (deprecated && strcmp (deprecated, "1") == 0)
+         if (deprecated)
            constant->deprecated = TRUE;
          else
            constant->deprecated = FALSE;
 
          if (ctx->state == STATE_NAMESPACE)
            {
-             ctx->current_node = (GIrNode *) constant;
-             ctx->current_module->entries = 
+             push_node (ctx, (GIrNode *) constant);
+             ctx->current_module->entries =
                g_list_append (ctx->current_module->entries, constant);
            }
          else
            {
              GIrNodeInterface *iface;
 
-             iface = (GIrNodeInterface *)ctx->current_node;
+             iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
              iface->members = g_list_append (iface->members, constant);
            }
 
@@ -1184,7 +1428,7 @@ start_constant (GMarkupParseContext *context,
              break;
            }
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1205,20 +1449,20 @@ start_errordomain (GMarkupParseContext *context,
       const gchar *getquark;
       const gchar *codes;
       const gchar *deprecated;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       getquark = find_attribute ("get-quark", attribute_names, attribute_values);
       codes = find_attribute ("codes", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else if (getquark == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "getquark");
       else if (codes == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "codes");
-      else 
-       {             
+      else
+       {
          GIrNodeErrorDomain *domain;
 
          domain = (GIrNodeErrorDomain *) g_ir_node_new (G_IR_NODE_ERROR_DOMAIN);
@@ -1227,18 +1471,18 @@ start_errordomain (GMarkupParseContext *context,
          domain->getquark = g_strdup (getquark);
          domain->codes = g_strdup (codes);
 
-         if (deprecated && strcmp (deprecated, "1") == 0)
+         if (deprecated)
            domain->deprecated = TRUE;
          else
            domain->deprecated = FALSE;
 
-         ctx->current_node = (GIrNode *) domain;
-         ctx->current_module->entries = 
+         push_node (ctx, (GIrNode *) domain);
+         ctx->current_module->entries =
            g_list_append (ctx->current_module->entries, domain);
 
          state_switch (ctx, STATE_ERRORDOMAIN);
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1259,12 +1503,14 @@ start_interface (GMarkupParseContext *context,
       const gchar *typename;
       const gchar *typeinit;
       const gchar *deprecated;
-      
+      const gchar *glib_type_struct;
+
       name = find_attribute ("name", attribute_names, attribute_values);
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
+      glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else if (typename == NULL)
@@ -1279,19 +1525,20 @@ start_interface (GMarkupParseContext *context,
          ((GIrNode *)iface)->name = g_strdup (name);
          iface->gtype_name = g_strdup (typename);
          iface->gtype_init = g_strdup (typeinit);
-         if (deprecated && strcmp (deprecated, "1") == 0)
+          iface->glib_type_struct = g_strdup (glib_type_struct);
+         if (deprecated)
            iface->deprecated = TRUE;
          else
            iface->deprecated = FALSE;
-         
-         ctx->current_node = (GIrNode *) iface;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, iface);              
-         
+
+         push_node (ctx, (GIrNode *) iface);
+         ctx->current_module->entries =
+           g_list_append (ctx->current_module->entries, iface);
+
          state_switch (ctx, STATE_INTERFACE);
-         
+
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1310,16 +1557,20 @@ start_class (GMarkupParseContext *context,
     {
       const gchar *name;
       const gchar *parent;
+      const gchar *glib_type_struct;
       const gchar *typename;
       const gchar *typeinit;
       const gchar *deprecated;
-      
+      const gchar *abstract;
+
       name = find_attribute ("name", attribute_names, attribute_values);
       parent = find_attribute ("parent", attribute_names, attribute_values);
+      glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values);
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
+      abstract = find_attribute ("abstract", attribute_names, attribute_values);
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else if (typename == NULL)
@@ -1335,18 +1586,21 @@ start_class (GMarkupParseContext *context,
          iface->gtype_name = g_strdup (typename);
          iface->gtype_init = g_strdup (typeinit);
          iface->parent = g_strdup (parent);
-         if (deprecated && strcmp (deprecated, "1") == 0)
+         iface->glib_type_struct = g_strdup (glib_type_struct);
+         if (deprecated)
            iface->deprecated = TRUE;
          else
            iface->deprecated = FALSE;
-         
-         ctx->current_node = (GIrNode *) iface;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, iface);              
-         
+
+         iface->abstract = abstract && strcmp (abstract, "1") == 0;
+
+         push_node (ctx, (GIrNode *) iface);
+         ctx->current_module->entries =
+           g_list_append (ctx->current_module->entries, iface);
+
          state_switch (ctx, STATE_CLASS);
        }
-      
+
       return TRUE;
     }
   return  FALSE;
@@ -1361,23 +1615,84 @@ start_type (GMarkupParseContext *context,
            GError             **error)
 {
   const gchar *name;
+  const gchar *ctype;
+  gboolean is_array;
+  gboolean is_varargs;
+  GIrNodeType *typenode;
+
+  is_array = strcmp (element_name, "array") == 0;
+  is_varargs = strcmp (element_name, "varargs") == 0;
 
-  if (strcmp (element_name, "type") != 0 ||
-      !(ctx->state == STATE_FUNCTION_PARAMETER ||
-       ctx->state == STATE_FUNCTION_RETURN || 
-       ctx->state == STATE_STRUCT_FIELD ||
-       ctx->state == STATE_UNION_FIELD ||
-       ctx->state == STATE_CLASS_PROPERTY ||
-       ctx->state == STATE_CLASS_FIELD ||
-       ctx->state == STATE_INTERFACE_FIELD ||
-       ctx->state == STATE_INTERFACE_PROPERTY ||
-       ctx->state == STATE_BOXED_FIELD ||
-       ctx->state == STATE_NAMESPACE_CONSTANT ||
-       ctx->state == STATE_CLASS_CONSTANT ||
-       ctx->state == STATE_INTERFACE_CONSTANT
-       ))
+  if (!(is_array || is_varargs || (strcmp (element_name, "type") == 0)))
     return FALSE;
 
+  if (ctx->state == STATE_TYPE)
+    {
+      ctx->type_depth++;
+      ctx->type_stack = g_list_prepend (ctx->type_stack, ctx->type_parameters);
+      ctx->type_parameters = NULL;
+    }
+  else if (ctx->state == STATE_FUNCTION_PARAMETER ||
+          ctx->state == STATE_FUNCTION_RETURN ||
+          ctx->state == STATE_STRUCT_FIELD ||
+          ctx->state == STATE_UNION_FIELD ||
+          ctx->state == STATE_CLASS_PROPERTY ||
+          ctx->state == STATE_CLASS_FIELD ||
+          ctx->state == STATE_INTERFACE_FIELD ||
+          ctx->state == STATE_INTERFACE_PROPERTY ||
+          ctx->state == STATE_BOXED_FIELD ||
+          ctx->state == STATE_NAMESPACE_CONSTANT ||
+          ctx->state == STATE_CLASS_CONSTANT ||
+          ctx->state == STATE_INTERFACE_CONSTANT
+          )
+    {
+      state_switch (ctx, STATE_TYPE);
+      ctx->type_depth = 1;
+      if (is_varargs)
+       {
+         switch (CURRENT_NODE (ctx)->type)
+           {
+           case G_IR_NODE_FUNCTION:
+           case G_IR_NODE_CALLBACK:
+             {
+               GIrNodeFunction *func = (GIrNodeFunction *)CURRENT_NODE (ctx);
+               func->is_varargs = TRUE;
+             }
+             break;
+           case G_IR_NODE_VFUNC:
+             {
+               GIrNodeVFunc *vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx);
+               vfunc->is_varargs = TRUE;
+             }
+             break;
+           /* list others individually rather than with default: so that compiler
+            * warns if new node types are added without adding them to the switch
+            */
+           case G_IR_NODE_INVALID:
+           case G_IR_NODE_ENUM:
+           case G_IR_NODE_FLAGS:
+           case G_IR_NODE_CONSTANT:
+           case G_IR_NODE_ERROR_DOMAIN:
+           case G_IR_NODE_PARAM:
+           case G_IR_NODE_TYPE:
+           case G_IR_NODE_PROPERTY:
+           case G_IR_NODE_SIGNAL:
+           case G_IR_NODE_VALUE:
+           case G_IR_NODE_FIELD:
+           case G_IR_NODE_XREF:
+           case G_IR_NODE_STRUCT:
+           case G_IR_NODE_BOXED:
+           case G_IR_NODE_OBJECT:
+           case G_IR_NODE_INTERFACE:
+           case G_IR_NODE_UNION:
+             g_assert_not_reached ();
+             break;
+           }
+       }
+      ctx->type_stack = NULL;
+      ctx->type_parameters = NULL;
+    }
+
   if (!ctx->current_typed)
     {
       g_set_error (error,
@@ -1387,43 +1702,230 @@ start_type (GMarkupParseContext *context,
       return FALSE;
     }
 
-  name = find_attribute ("name", attribute_names, attribute_values);
+  if (is_varargs)
+    return TRUE;
+
+  if (is_array)
+    {
+      const char *zero;
+      const char *len;
+      const char *size;
+
+      typenode = (GIrNodeType *)g_ir_node_new (G_IR_NODE_TYPE);
+
+      typenode->tag = GI_TYPE_TAG_ARRAY;
+      typenode->is_pointer = TRUE;
+      typenode->is_array = TRUE;
+
+      zero = find_attribute ("zero-terminated", attribute_names, attribute_values);
+      len = find_attribute ("length", attribute_names, attribute_values);
+      size = find_attribute ("fixed-size", attribute_names, attribute_values);
+
+      typenode->zero_terminated = !(zero && strcmp (zero, "1") != 0);
+      typenode->has_length = len != NULL;
+      typenode->length = typenode->has_length ? atoi (len) : -1;
+
+      typenode->has_size = size != NULL;
+      typenode->size = typenode->has_size ? atoi (size) : -1;
+
+      if (zero)
+        typenode->zero_terminated = strcmp(zero, "1") == 0;
+      else
+        /* If neither zero-terminated nor length nor fixed-size is given, assume zero-terminated. */
+        typenode->zero_terminated = !(typenode->has_length || typenode->has_size);
+    }
+  else
+    {
+      int pointer_depth;
+      name = find_attribute ("name", attribute_names, attribute_values);
+
+      if (name == NULL)
+       MISSING_ATTRIBUTE (context, error, element_name, "name");
+
+      pointer_depth = 0;
+      ctype = find_attribute ("c:type", attribute_names, attribute_values);
+      if (ctype != NULL)
+        {
+          const char *cp = ctype + strlen(ctype) - 1;
+          while (cp > ctype && *cp-- == '*')
+            pointer_depth++;
+        }
+
+      if (ctx->current_typed->type == G_IR_NODE_PARAM &&
+          ((GIrNodeParam *)ctx->current_typed)->out &&
+          pointer_depth > 0)
+        pointer_depth--;
+
+      typenode = parse_type (ctx, name);
+
+      /* A 'disguised' structure is one where the c:type is a typedef that
+       * doesn't look like a pointer, but is internally.
+       */
+      if (typenode->tag == GI_TYPE_TAG_INTERFACE &&
+         is_disguised_structure (ctx, typenode->interface))
+       pointer_depth++;
+
+      if (pointer_depth > 0)
+       typenode->is_pointer = TRUE;
+    }
+
+  ctx->type_parameters = g_list_append (ctx->type_parameters, typenode);
+
+  return TRUE;
+}
+
+static void
+end_type_top (ParseContext *ctx)
+{
+  GIrNodeType *typenode;
+
+  if (!ctx->type_parameters)
+    goto out;
+
+  typenode = (GIrNodeType*)ctx->type_parameters->data;
+
+  /* Default to pointer for unspecified containers */
+  if (typenode->tag == GI_TYPE_TAG_ARRAY ||
+      typenode->tag == GI_TYPE_TAG_GLIST ||
+      typenode->tag == GI_TYPE_TAG_GSLIST)
+    {
+      if (typenode->parameter_type1 == NULL)
+       typenode->parameter_type1 = parse_type (ctx, "any");
+    }
+  else if (typenode->tag == GI_TYPE_TAG_GHASH)
+    {
+      if (typenode->parameter_type1 == NULL)
+       {
+         typenode->parameter_type1 = parse_type (ctx, "any");
+         typenode->parameter_type2 = parse_type (ctx, "any");
+       }
+    }
 
-  if (name == NULL)
-    MISSING_ATTRIBUTE (context, error, element_name, "name");
-  
   switch (ctx->current_typed->type)
     {
     case G_IR_NODE_PARAM:
       {
        GIrNodeParam *param = (GIrNodeParam *)ctx->current_typed;
-       param->type = parse_type (ctx, name);
+       param->type = typenode;
       }
       break;
     case G_IR_NODE_FIELD:
       {
        GIrNodeField *field = (GIrNodeField *)ctx->current_typed;
-       field->type = parse_type (ctx, name);
+       field->type = typenode;
       }
       break;
     case G_IR_NODE_PROPERTY:
       {
        GIrNodeProperty *property = (GIrNodeProperty *) ctx->current_typed;
-       property->type = parse_type (ctx, name);
+       property->type = typenode;
       }
       break;
     case G_IR_NODE_CONSTANT:
       {
        GIrNodeConstant *constant = (GIrNodeConstant *)ctx->current_typed;
-       constant->type = parse_type (ctx, name);
+       constant->type = typenode;
       }
       break;
     default:
-      g_printerr("current node is %d\n", ctx->current_node->type);
+      g_printerr("current node is %d\n", CURRENT_NODE (ctx)->type);
       g_assert_not_reached ();
     }
+  g_list_free (ctx->type_parameters);
 
+ out:
+  ctx->type_depth = 0;
+  ctx->type_parameters = NULL;
   ctx->current_typed = NULL;
+}
+
+static void
+end_type_recurse (ParseContext *ctx)
+{
+  GIrNodeType *parent;
+  GIrNodeType *param = NULL;
+
+  parent = (GIrNodeType *) ((GList*)ctx->type_stack->data)->data;
+  if (ctx->type_parameters)
+    param = (GIrNodeType *) ctx->type_parameters->data;
+
+  if (parent->tag == GI_TYPE_TAG_ARRAY ||
+      parent->tag == GI_TYPE_TAG_GLIST ||
+      parent->tag == GI_TYPE_TAG_GSLIST)
+    {
+      g_assert (param != NULL);
+
+      if (parent->parameter_type1 == NULL)
+        parent->parameter_type1 = param;
+      else
+        g_assert_not_reached ();
+    }
+  else if (parent->tag == GI_TYPE_TAG_GHASH)
+    {
+      g_assert (param != NULL);
+
+      if (parent->parameter_type1 == NULL)
+        parent->parameter_type1 = param;
+      else if (parent->parameter_type2 == NULL)
+        parent->parameter_type2 = param;
+      else
+        g_assert_not_reached ();
+    }
+  g_list_free (ctx->type_parameters);
+  ctx->type_parameters = (GList *)ctx->type_stack->data;
+  ctx->type_stack = g_list_delete_link (ctx->type_stack, ctx->type_stack);
+}
+
+static void
+end_type (ParseContext *ctx)
+{
+  if (ctx->type_depth == 1)
+    {
+      end_type_top (ctx);
+      state_switch (ctx, ctx->prev_state);
+    }
+  else
+    {
+      end_type_recurse (ctx);
+      ctx->type_depth--;
+    }
+}
+
+static gboolean
+start_attribute (GMarkupParseContext *context,
+                 const gchar         *element_name,
+                 const gchar        **attribute_names,
+                 const gchar        **attribute_values,
+                 ParseContext       *ctx,
+                 GError             **error)
+{
+  const gchar *name;
+  const gchar *value;
+  GIrNode *curnode;
+
+  if (strcmp (element_name, "attribute") != 0 || ctx->node_stack == NULL)
+    return FALSE;
+
+  name = find_attribute ("name", attribute_names, attribute_values);
+  value = find_attribute ("value", attribute_names, attribute_values);
+
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
+  if (value == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "value");
+      return FALSE;
+    }
+
+  state_switch (ctx, STATE_ATTRIBUTE);
+
+  curnode = CURRENT_NODE (ctx);
+
+  g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
+
   return TRUE;
 }
 
@@ -1439,6 +1941,7 @@ start_return_value (GMarkupParseContext *context,
       ctx->state == STATE_FUNCTION)
     {
       GIrNodeParam *param;
+      const gchar  *transfer;
 
       param = (GIrNodeParam *)g_ir_node_new (G_IR_NODE_PARAM);
       param->in = FALSE;
@@ -1449,37 +1952,70 @@ start_return_value (GMarkupParseContext *context,
 
       state_switch (ctx, STATE_FUNCTION_RETURN);
 
-      switch (ctx->current_node->type)
+      transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
+      parse_param_transfer (param, transfer, NULL);
+
+      switch (CURRENT_NODE (ctx)->type)
        {
        case G_IR_NODE_FUNCTION:
        case G_IR_NODE_CALLBACK:
          {
-           GIrNodeFunction *func = (GIrNodeFunction *)ctx->current_node;
+           GIrNodeFunction *func = (GIrNodeFunction *)CURRENT_NODE (ctx);
            func->result = param;
          }
          break;
        case G_IR_NODE_SIGNAL:
          {
-           GIrNodeSignal *signal = (GIrNodeSignal *)ctx->current_node;
+           GIrNodeSignal *signal = (GIrNodeSignal *)CURRENT_NODE (ctx);
            signal->result = param;
          }
          break;
        case G_IR_NODE_VFUNC:
          {
-           GIrNodeVFunc *vfunc = (GIrNodeVFunc *)ctx->current_node;
+           GIrNodeVFunc *vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx);
            vfunc->result = param;
          }
          break;
        default:
          g_assert_not_reached ();
        }
-      
+
       return TRUE;
     }
 
   return FALSE;
 }
 
+static gboolean
+start_implements (GMarkupParseContext *context,
+                 const gchar         *element_name,
+                 const gchar        **attribute_names,
+                 const gchar        **attribute_values,
+                 ParseContext       *ctx,
+                 GError             **error)
+{
+  GIrNodeInterface *iface;
+  const char *name;
+
+  if (strcmp (element_name, "implements") != 0 ||
+      !(ctx->state == STATE_CLASS))
+    return FALSE;
+
+  state_switch (ctx, STATE_IMPLEMENTS);
+
+  name = find_attribute ("name", attribute_names, attribute_values);
+  if (name == NULL)
+    {
+      MISSING_ATTRIBUTE (context, error, element_name, "name");
+      return FALSE;
+    }
+
+  iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
+  iface->interfaces = g_list_append (iface->interfaces, g_strdup (name));
+
+  return TRUE;
+}
+
 static gboolean
 start_glib_signal (GMarkupParseContext *context,
                   const gchar         *element_name,
@@ -1488,7 +2024,7 @@ start_glib_signal (GMarkupParseContext *context,
                   ParseContext       *ctx,
                   GError             **error)
 {
-  if (strcmp (element_name, "glib:signal") == 0 && 
+  if (strcmp (element_name, "glib:signal") == 0 &&
       (ctx->state == STATE_CLASS ||
        ctx->state == STATE_INTERFACE))
     {
@@ -1499,7 +2035,7 @@ start_glib_signal (GMarkupParseContext *context,
       const gchar *action;
       const gchar *no_hooks;
       const gchar *has_class_closure;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       when = find_attribute ("when", attribute_names, attribute_values);
       no_recurse = find_attribute ("no-recurse", attribute_names, attribute_values);
@@ -1507,7 +2043,7 @@ start_glib_signal (GMarkupParseContext *context,
       action = find_attribute ("action", attribute_names, attribute_values);
       no_hooks = find_attribute ("no-hooks", attribute_names, attribute_values);
       has_class_closure = find_attribute ("has-class-closure", attribute_names, attribute_values);
-      
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else
@@ -1516,9 +2052,9 @@ start_glib_signal (GMarkupParseContext *context,
          GIrNodeSignal *signal;
 
          signal = (GIrNodeSignal *)g_ir_node_new (G_IR_NODE_SIGNAL);
-         
+
          ((GIrNode *)signal)->name = g_strdup (name);
-         
+
          signal->run_first = FALSE;
          signal->run_last = FALSE;
          signal->run_cleanup = FALSE;
@@ -1526,9 +2062,9 @@ start_glib_signal (GMarkupParseContext *context,
            signal->run_last = TRUE;
          else if (strcmp (when, "FIRST") == 0)
            signal->run_first = TRUE;
-         else 
+         else
            signal->run_cleanup = TRUE;
-         
+
          if (no_recurse && strcmp (no_recurse, "1") == 0)
            signal->no_recurse = TRUE;
          else
@@ -1550,13 +2086,13 @@ start_glib_signal (GMarkupParseContext *context,
          else
            signal->has_class_closure = FALSE;
 
-         iface = (GIrNodeInterface *)ctx->current_node;
+         iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
          iface->members = g_list_append (iface->members, signal);
 
-         ctx->current_node = (GIrNode *)signal;
+         push_node (ctx, (GIrNode *)signal);
          state_switch (ctx, STATE_FUNCTION);
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1570,7 +2106,7 @@ start_vfunc (GMarkupParseContext *context,
             ParseContext       *ctx,
             GError             **error)
 {
-  if (strcmp (element_name, "vfunc") == 0 && 
+  if (strcmp (element_name, "virtual-method") == 0 &&
       (ctx->state == STATE_CLASS ||
        ctx->state == STATE_INTERFACE))
     {
@@ -1579,13 +2115,15 @@ start_vfunc (GMarkupParseContext *context,
       const gchar *override;
       const gchar *is_class_closure;
       const gchar *offset;
-      
+      const gchar *invoker;
+
       name = find_attribute ("name", attribute_names, attribute_values);
-      must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values);       
+      must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values);
       override = find_attribute ("override", attribute_names, attribute_values);
       is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values);
       offset = find_attribute ("offset", attribute_names, attribute_values);
-      
+      invoker = find_attribute ("invoker", attribute_names, attribute_values);
+
       if (name == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else
@@ -1594,7 +2132,7 @@ start_vfunc (GMarkupParseContext *context,
          GIrNodeVFunc *vfunc;
 
          vfunc = (GIrNodeVFunc *)g_ir_node_new (G_IR_NODE_VFUNC);
-         
+
          ((GIrNode *)vfunc)->name = g_strdup (name);
 
          if (must_chain_up && strcmp (must_chain_up, "1") == 0)
@@ -1617,24 +2155,26 @@ start_vfunc (GMarkupParseContext *context,
              vfunc->must_be_implemented = FALSE;
              vfunc->must_not_be_implemented = FALSE;
            }
-         
+
          if (is_class_closure && strcmp (is_class_closure, "1") == 0)
            vfunc->is_class_closure = TRUE;
          else
            vfunc->is_class_closure = FALSE;
-         
+
          if (offset)
            vfunc->offset = atoi (offset);
          else
            vfunc->offset = 0;
 
-         iface = (GIrNodeInterface *)ctx->current_node;
+         vfunc->invoker = g_strdup (invoker);
+
+         iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
          iface->members = g_list_append (iface->members, vfunc);
 
-         ctx->current_node = (GIrNode *)vfunc;
+         push_node (ctx, (GIrNode *)vfunc);
          state_switch (ctx, STATE_FUNCTION);
        }
-      
+
       return TRUE;
     }
   return FALSE;
@@ -1649,40 +2189,74 @@ start_struct (GMarkupParseContext *context,
              ParseContext       *ctx,
              GError             **error)
 {
-  if (strcmp (element_name, "record") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+  if (strcmp (element_name, "record") == 0 &&
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
-      
+      const gchar *disguised;
+      const gchar *gtype_name;
+      const gchar *gtype_init;
+      const gchar *gtype_struct;
+      const gchar *foreign;
+      GIrNodeStruct *struct_;
+
       name = find_attribute ("name", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
-      
-      if (name == NULL)
-       MISSING_ATTRIBUTE (context, error, element_name, "name");
-      else
+      disguised = find_attribute ("disguised", attribute_names, attribute_values);
+      gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values);
+      gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values);
+      gtype_struct = find_attribute ("glib:is-gtype-struct-for", attribute_names, attribute_values);
+      foreign = find_attribute ("foreign", attribute_names, attribute_values);
+
+      if (name == NULL && ctx->node_stack == NULL)
        {
-         GIrNodeStruct *struct_;
+         MISSING_ATTRIBUTE (context, error, element_name, "name");
+         return FALSE;
+       }
+      if ((gtype_name == NULL && gtype_init != NULL))
+       {
+         MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
+         return FALSE;
+       }
+      if ((gtype_name != NULL && gtype_init == NULL))
+       {
+         MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
+         return FALSE;
+       }
 
-         struct_ = (GIrNodeStruct *) g_ir_node_new (G_IR_NODE_STRUCT);
-         
-         ((GIrNode *)struct_)->name = g_strdup (name);
-         if (deprecated && strcmp (deprecated, "1") == 0)
-           struct_->deprecated = TRUE;
-         else
-           struct_->deprecated = FALSE;
+      struct_ = (GIrNodeStruct *) g_ir_node_new (G_IR_NODE_STRUCT);
 
-         ctx->current_node = (GIrNode *)struct_;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, struct_);
-         
-         state_switch (ctx, STATE_STRUCT);
-       }
+      ((GIrNode *)struct_)->name = g_strdup (name ? name : "");
+      if (deprecated)
+       struct_->deprecated = TRUE;
+      else
+       struct_->deprecated = FALSE;
+
+      if (disguised && strcmp (disguised, "1") == 0)
+       struct_->disguised = TRUE;
+
+      struct_->is_gtype_struct = gtype_struct != NULL;
+
+      struct_->gtype_name = g_strdup (gtype_name);
+      struct_->gtype_init = g_strdup (gtype_init);
+
+      struct_->foreign = (g_strcmp0 (foreign, "1") == 0);
+
+      if (ctx->node_stack == NULL)
+        ctx->current_module->entries =
+          g_list_append (ctx->current_module->entries, struct_);
+      push_node (ctx, (GIrNode *)struct_);
+
+      state_switch (ctx, STATE_STRUCT);
       return TRUE;
     }
   return FALSE;
 }
-  
+
 
 static gboolean
 start_union (GMarkupParseContext *context,
@@ -1692,39 +2266,43 @@ start_union (GMarkupParseContext *context,
             ParseContext       *ctx,
             GError             **error)
 {
-  if (strcmp (element_name, "union") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+  if (strcmp (element_name, "union") == 0 &&
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
       const gchar *typename;
       const gchar *typeinit;
-      
+
       name = find_attribute ("name", attribute_names, attribute_values);
       deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
-      
-      if (name == NULL)
+
+      if (name == NULL && ctx->node_stack == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else
        {
          GIrNodeUnion *union_;
 
          union_ = (GIrNodeUnion *) g_ir_node_new (G_IR_NODE_UNION);
-         
-         ((GIrNode *)union_)->name = g_strdup (name);
+
+         ((GIrNode *)union_)->name = g_strdup (name ? name : "");
          union_->gtype_name = g_strdup (typename);
          union_->gtype_init = g_strdup (typeinit);
-         if (deprecated && strcmp (deprecated, "1") == 0)
+         if (deprecated)
            union_->deprecated = TRUE;
          else
            union_->deprecated = FALSE;
 
-         ctx->current_node = (GIrNode *)union_;
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, union_);
-         
+          if (ctx->node_stack == NULL)
+            ctx->current_module->entries =
+              g_list_append (ctx->current_module->entries, union_);
+         push_node (ctx, (GIrNode *)union_);
+
          state_switch (ctx, STATE_UNION);
        }
       return TRUE;
@@ -1745,7 +2323,7 @@ start_discriminator (GMarkupParseContext *context,
     {
       const gchar *type;
       const gchar *offset;
-      
+
       type = find_attribute ("type", attribute_names, attribute_values);
       offset = find_attribute ("offset", attribute_names, attribute_values);
       if (type == NULL)
@@ -1753,18 +2331,91 @@ start_discriminator (GMarkupParseContext *context,
       else if (offset == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "offset");
        {
-         ((GIrNodeUnion *)ctx->current_node)->discriminator_type 
+         ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_type
            = parse_type (ctx, type);
-         ((GIrNodeUnion *)ctx->current_node)->discriminator_offset 
+         ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset
            = atoi (offset);
        }
-      
+
       return TRUE;
     }
 
   return FALSE;
 }
-  
+
+static gboolean
+parse_include (GMarkupParseContext *context,
+              ParseContext        *ctx,
+              const char          *name,
+              const char          *version,
+              GError             **error)
+{
+  gchar *buffer;
+  gsize length;
+  gchar *girpath, *girname;
+  gboolean success = FALSE;
+  GList *modules;
+  GList *l;
+
+  for (l = ctx->parser->parsed_modules; l; l = l->next)
+    {
+      GIrModule *m = l->data;
+
+      if (strcmp (m->name, name) == 0)
+       {
+         if (strcmp (m->version, version) == 0)
+           {
+             ctx->include_modules = g_list_prepend (ctx->include_modules, m);
+
+             return TRUE;
+           }
+         else
+           {
+             g_set_error (error,
+                          G_MARKUP_ERROR,
+                          G_MARKUP_ERROR_INVALID_CONTENT,
+                          "Module '%s' imported with conflicting versions '%s' and '%s'",
+                          name, m->version, version);
+             return FALSE;
+           }
+       }
+    }
+
+  girname = g_strdup_printf ("%s-%s.gir", name, version);
+  girpath = locate_gir (ctx->parser, girname);
+
+  if (girpath == NULL)
+    {
+      g_set_error (error,
+                  G_MARKUP_ERROR,
+                  G_MARKUP_ERROR_INVALID_CONTENT,
+                  "Could not find GIR file '%s'; check XDG_DATA_DIRS or use --includedir",
+                  girname);
+      g_free (girname);
+      return FALSE;
+    }
+  g_free (girname);
+
+  g_debug ("Parsing include %s", girpath);
+
+  if (!g_file_get_contents (girpath, &buffer, &length, error))
+    {
+      g_free (girpath);
+      return FALSE;
+    }
+  g_free (girpath);
+
+  modules = g_ir_parser_parse_string (ctx->parser, name, buffer, length, error);
+  success = error != NULL;
+
+  ctx->include_modules = g_list_concat (ctx->include_modules,
+                                       modules);
+
+  g_free (buffer);
+
+  return success;
+}
+
 extern GLogLevelFlags logged_levels;
 
 static void
@@ -1796,23 +2447,31 @@ start_element_handler (GMarkupParseContext *context,
       g_string_free (tags, TRUE);
     }
 
-  switch (element_name[0]) 
+  switch (element_name[0])
     {
     case 'a':
-      if (ctx->state == STATE_NAMESPACE && strcmp (element_name, "alias") == 0) 
+      if (ctx->state == STATE_NAMESPACE && strcmp (element_name, "alias") == 0)
        {
          state_switch (ctx, STATE_ALIAS);
          goto out;
        }
+      if (start_type (context, element_name,
+                     attribute_names, attribute_values,
+                     ctx, error))
+       goto out;
+      else if (start_attribute (context, element_name,
+                                attribute_names, attribute_values,
+                                ctx, error))
+        goto out;
       break;
     case 'b':
-      if (start_enum (context, element_name, 
+      if (start_enum (context, element_name,
                      attribute_names, attribute_values,
                      ctx, error))
        goto out;
       break;
     case 'c':
-      if (start_function (context, element_name, 
+      if (start_function (context, element_name,
                          attribute_names, attribute_values,
                          ctx, error))
        goto out;
@@ -1820,55 +2479,36 @@ start_element_handler (GMarkupParseContext *context,
                               attribute_names, attribute_values,
                               ctx, error))
        goto out;
-      else if (start_class (context, element_name, 
+      else if (start_class (context, element_name,
                            attribute_names, attribute_values,
                            ctx, error))
-       goto out;
-      else if (strcmp (element_name, "class") == 0 &&
-              ctx->state == STATE_REQUIRES)
-       {
-         const gchar *name;
-
-         name = find_attribute ("name", attribute_names, attribute_values);
-
-         if (name == NULL)
-           MISSING_ATTRIBUTE (context, error, element_name, "name");
-         else
-           {  
-             GIrNodeInterface *iface;
-
-             iface = (GIrNodeInterface *)ctx->current_node;
-             iface ->prerequisites = g_list_append (iface->prerequisites, g_strdup (name));
-           }
-
-         goto out;
-       }
+       goto out;
       break;
 
     case 'd':
-      if (start_discriminator (context, element_name, 
+      if (start_discriminator (context, element_name,
                               attribute_names, attribute_values,
                               ctx, error))
        goto out;
       break;
 
     case 'e':
-      if (start_enum (context, element_name, 
+      if (start_enum (context, element_name,
                      attribute_names, attribute_values,
                      ctx, error))
        goto out;
-      else if (start_errordomain (context, element_name, 
+      else if (start_errordomain (context, element_name,
                      attribute_names, attribute_values,
                      ctx, error))
        goto out;
       break;
 
     case 'f':
-      if (start_function (context, element_name, 
+      if (start_function (context, element_name,
                          attribute_names, attribute_values,
                          ctx, error))
        goto out;
-      else if (start_field (context, element_name, 
+      else if (start_field (context, element_name,
                            attribute_names, attribute_values,
                            ctx, error))
        goto out;
@@ -1883,70 +2523,55 @@ start_element_handler (GMarkupParseContext *context,
                             attribute_names, attribute_values,
                             ctx, error))
        goto out;
-      else if (start_glib_boxed (context, element_name,
-                                attribute_names, attribute_values,
-                                ctx, error))
-       goto out;
       break;
 
     case 'i':
-      if (start_interface (context, element_name, 
-                          attribute_names, attribute_values,
-                          ctx, error))
-       goto out;
-      if (strcmp (element_name, "implements") == 0 &&
-         ctx->state == STATE_CLASS)
-       {
-         state_switch (ctx, STATE_IMPLEMENTS);
-
-         goto out;
-       }
-      else if (strcmp (element_name, "interface") == 0 &&
-              ctx->state == STATE_IMPLEMENTS)
+      if (strcmp (element_name, "include") == 0 &&
+         ctx->state == STATE_REPOSITORY)
        {
          const gchar *name;
+         const gchar *version;
 
          name = find_attribute ("name", attribute_names, attribute_values);
+         version = find_attribute ("version", attribute_names, attribute_values);
 
          if (name == NULL)
-           MISSING_ATTRIBUTE (context, error, element_name, "name");
-         else
-           {  
-             GIrNodeInterface *iface;
-
-             iface = (GIrNodeInterface *)ctx->current_node;
-             iface ->interfaces = g_list_append (iface->interfaces, g_strdup (name));
+           {
+             MISSING_ATTRIBUTE (context, error, element_name, "name");
+             break;
+           }
+         if (version == NULL)
+           {
+             MISSING_ATTRIBUTE (context, error, element_name, "version");
+             break;
            }
 
-         goto out;
-       }
-      else if (strcmp (element_name, "interface") == 0 &&
-              ctx->state == STATE_REQUIRES)
-       {
-         const gchar *name;
-
-         name = find_attribute ("name", attribute_names, attribute_values);
+         if (!parse_include (context, ctx, name, version, error))
+           break;
 
-         if (name == NULL)
-           MISSING_ATTRIBUTE (context, error, element_name, "name");
-         else
-           {  
-             GIrNodeInterface *iface;
+         ctx->dependencies = g_list_prepend (ctx->dependencies,
+                                             g_strdup_printf ("%s-%s", name, version));
 
-             iface = (GIrNodeInterface *)ctx->current_node;
-             iface ->prerequisites = g_list_append (iface->prerequisites, g_strdup (name));
-           }
 
+         state_switch (ctx, STATE_INCLUDE);
          goto out;
        }
+      if (start_interface (context, element_name,
+                          attribute_names, attribute_values,
+                          ctx, error))
+       goto out;
+      else if (start_implements (context, element_name,
+                                attribute_names, attribute_values,
+                                ctx, error))
+       goto out;
       break;
 
     case 'm':
-      if (start_function (context, element_name, 
+      if (start_function (context, element_name,
                          attribute_names, attribute_values,
                          ctx, error))
        goto out;
-      else if (start_member (context, element_name, 
+      else if (start_member (context, element_name,
                          attribute_names, attribute_values,
                          ctx, error))
        goto out;
@@ -1955,22 +2580,56 @@ start_element_handler (GMarkupParseContext *context,
     case 'n':
       if (strcmp (element_name, "namespace") == 0 && ctx->state == STATE_REPOSITORY)
        {
-         const gchar *name, *shared_library;
-         
+         const gchar *name, *version, *shared_library, *cprefix;
+
+         if (ctx->current_module != NULL)
+           {
+             g_set_error (error,
+                          G_MARKUP_ERROR,
+                          G_MARKUP_ERROR_INVALID_CONTENT,
+                          "Only one <namespace/> element is currently allowed per <repository/>");
+             goto out;
+           }
+
          name = find_attribute ("name", attribute_names, attribute_values);
+         version = find_attribute ("version", attribute_names, attribute_values);
          shared_library = find_attribute ("shared-library", attribute_names, attribute_values);
+         cprefix = find_attribute ("c:prefix", attribute_names, attribute_values);
 
          if (name == NULL)
            MISSING_ATTRIBUTE (context, error, element_name, "name");
+         else if (version == NULL)
+           MISSING_ATTRIBUTE (context, error, element_name, "version");
          else
            {
-             ctx->current_module = g_ir_module_new (name, shared_library);
+             GList *l;
+
+             if (strcmp (name, ctx->namespace) != 0)
+               g_set_error (error,
+                            G_MARKUP_ERROR,
+                            G_MARKUP_ERROR_INVALID_CONTENT,
+                            "<namespace/> name element '%s' doesn't match file name '%s'",
+                            name, ctx->namespace);
+
+             ctx->current_module = g_ir_module_new (name, version, shared_library, cprefix);
+
+             ctx->current_module->aliases = ctx->aliases;
+             ctx->aliases = NULL;
+             ctx->current_module->disguised_structures = ctx->disguised_structures;
+             ctx->disguised_structures = NULL;
+
+             for (l = ctx->include_modules; l; l = l->next)
+               g_ir_module_add_include_module (ctx->current_module, l->data);
+
+             g_list_free (ctx->include_modules);
+             ctx->include_modules = NULL;
+
              ctx->modules = g_list_append (ctx->modules, ctx->current_module);
+             ctx->current_module->dependencies = ctx->dependencies;
 
              state_switch (ctx, STATE_NAMESPACE);
+             goto out;
            }
-
-         goto out;
        }
       break;
 
@@ -1990,7 +2649,32 @@ start_element_handler (GMarkupParseContext *context,
                                attribute_names, attribute_values,
                                ctx, error))
        goto out;
+      else if (strcmp (element_name, "prerequisite") == 0 &&
+              ctx->state == STATE_INTERFACE)
+       {
+         const gchar *name;
+
+         name = find_attribute ("name", attribute_names, attribute_values);
+
+         state_switch (ctx, STATE_PREREQUISITE);
+
+         if (name == NULL)
+           MISSING_ATTRIBUTE (context, error, element_name, "name");
+         else
+           {
+             GIrNodeInterface *iface;
 
+             iface = (GIrNodeInterface *)CURRENT_NODE(ctx);
+             iface->prerequisites = g_list_append (iface->prerequisites, g_strdup (name));
+           }
+         goto out;
+       }
+      else if (strcmp (element_name, "package") == 0 &&
+          ctx->state == STATE_REPOSITORY)
+        {
+          state_switch (ctx, STATE_PACKAGE);
+          goto out;
+        }
       break;
 
     case 'r':
@@ -1999,7 +2683,7 @@ start_element_handler (GMarkupParseContext *context,
          const gchar *version;
 
          version = find_attribute ("version", attribute_names, attribute_values);
-         
+
          if (version == NULL)
            MISSING_ATTRIBUTE (context, error, element_name, "version");
          else if (strcmp (version, "1.0") != 0)
@@ -2010,24 +2694,17 @@ start_element_handler (GMarkupParseContext *context,
                         version);
          else
            state_switch (ctx, STATE_REPOSITORY);
-         
+
          goto out;
        }
       else if (start_return_value (context, element_name,
                                   attribute_names, attribute_values,
                                   ctx, error))
-       goto out;      
-      else if (strcmp (element_name, "requires") == 0 &&
-              ctx->state == STATE_INTERFACE)
-       {
-         state_switch (ctx, STATE_REQUIRES);
-         
-         goto out;
-       }
+       goto out;
       else if (start_struct (context, element_name,
                             attribute_names, attribute_values,
                             ctx, error))
-       goto out;      
+       goto out;
       break;
 
     case 'u':
@@ -2048,21 +2725,26 @@ start_element_handler (GMarkupParseContext *context,
       if (start_vfunc (context, element_name,
                       attribute_names, attribute_values,
                       ctx, error))
-       goto out;      
+       goto out;
+      if (start_type (context, element_name,
+                     attribute_names, attribute_values,
+                     ctx, error))
+       goto out;
       break;
     }
 
-  g_markup_parse_context_get_position (context, &line_number, &char_number);
+  if (ctx->state != STATE_UNKNOWN)
+    {
+      state_switch (ctx, STATE_UNKNOWN);
+      ctx->unknown_depth = 1;
+    }
+  else
+    {
+      ctx->unknown_depth += 1;
+    }
 
-  g_set_error (error,
-              G_MARKUP_ERROR,
-              G_MARKUP_ERROR_UNKNOWN_ELEMENT,
-              "Unexpected start tag '%s' on line %d char %d; current state=%d",
-              element_name,
-              line_number, char_number, ctx->state);
-  
- out: ;
-  if (*error) 
+ out:
+  if (*error)
     {
       g_markup_parse_context_get_position (context, &line_number, &char_number);
 
@@ -2075,7 +2757,7 @@ static gboolean
 require_one_of_end_elements (GMarkupParseContext *context,
                             ParseContext        *ctx,
                             const char          *actual_name,
-                            GError             **error, 
+                            GError             **error,
                             ...)
 {
   va_list args;
@@ -2085,7 +2767,7 @@ require_one_of_end_elements (GMarkupParseContext *context,
 
   va_start (args, error);
 
-  while ((expected = va_arg (args, const char*)) != NULL) 
+  while ((expected = va_arg (args, const char*)) != NULL)
     {
       if (strcmp (expected, actual_name) == 0)
        {
@@ -2104,12 +2786,47 @@ require_one_of_end_elements (GMarkupParseContext *context,
               G_MARKUP_ERROR,
               G_MARKUP_ERROR_INVALID_CONTENT,
               "Unexpected end tag '%s' on line %d char %d; current state=%d",
-              actual_name, 
+              actual_name,
               line_number, char_number, ctx->state);
   backtrace_stderr();
   return FALSE;
 }
 
+static gboolean
+state_switch_end_struct_or_union (GMarkupParseContext *context,
+                                  ParseContext *ctx,
+                                  const gchar *element_name,
+                                  GError **error)
+{
+  pop_node (ctx);
+  if (ctx->node_stack == NULL)
+    {
+      state_switch (ctx, STATE_NAMESPACE);
+    }
+  else
+    {
+      if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT)
+        state_switch (ctx, STATE_STRUCT);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION)
+        state_switch (ctx, STATE_UNION);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT)
+        state_switch (ctx, STATE_CLASS);
+      else
+        {
+          int line_number, char_number;
+          g_markup_parse_context_get_position (context, &line_number, &char_number);
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_INVALID_CONTENT,
+                       "Unexpected end tag '%s' on line %d char %d",
+                       element_name,
+                       line_number, char_number);
+          return FALSE;
+        }
+    }
+  return TRUE;
+}
+
 static gboolean
 require_end_element (GMarkupParseContext *context,
                     ParseContext        *ctx,
@@ -2141,6 +2858,20 @@ end_element_handler (GMarkupParseContext *context,
       state_switch (ctx, STATE_END);
       break;
 
+    case STATE_INCLUDE:
+      if (require_end_element (context, ctx, "include", element_name, error))
+       {
+          state_switch (ctx, STATE_REPOSITORY);
+        }
+      break;
+
+    case STATE_PACKAGE:
+      if (require_end_element (context, ctx, "package", element_name, error))
+        {
+          state_switch (ctx, STATE_REPOSITORY);
+        }
+      break;
+
     case STATE_NAMESPACE:
       if (require_end_element (context, ctx, "namespace", element_name, error))
        {
@@ -2182,36 +2913,43 @@ end_element_handler (GMarkupParseContext *context,
       break;
 
     case STATE_FUNCTION:
-       if (ctx->current_node == g_list_last (ctx->current_module->entries)->data)
-       {
-         ctx->current_node = NULL;
-         state_switch (ctx, STATE_NAMESPACE);
-       }
-      else 
-       { 
-         ctx->current_node = g_list_last (ctx->current_module->entries)->data;
-         if (ctx->current_node->type == G_IR_NODE_INTERFACE)
-           state_switch (ctx, STATE_INTERFACE);
-         else if (ctx->current_node->type == G_IR_NODE_OBJECT)
-           state_switch (ctx, STATE_CLASS);
-         else if (ctx->current_node->type == G_IR_NODE_BOXED)
-           state_switch (ctx, STATE_BOXED);
-         else if (ctx->current_node->type == G_IR_NODE_STRUCT)
-           state_switch (ctx, STATE_STRUCT);
-         else if (ctx->current_node->type == G_IR_NODE_UNION)
-           state_switch (ctx, STATE_UNION);
-         else
-           {
-             int line_number, char_number;
-             g_markup_parse_context_get_position (context, &line_number, &char_number);
-             g_set_error (error,
-                          G_MARKUP_ERROR,
-                          G_MARKUP_ERROR_INVALID_CONTENT,
-                          "Unexpected end tag '%s' on line %d char %d",
-                          element_name,
-                          line_number, char_number);
-           }
-       }
+      {
+        pop_node (ctx);
+       if (ctx->node_stack == NULL)
+         {
+           state_switch (ctx, STATE_NAMESPACE);
+         }
+       else
+         {
+            g_debug("case STATE_FUNCTION %d", CURRENT_NODE (ctx)->type);
+            if (ctx->in_embedded_type)
+              {
+                ctx->in_embedded_type = FALSE;
+                state_switch (ctx, STATE_STRUCT_FIELD);
+              }
+           else if (CURRENT_NODE (ctx)->type == G_IR_NODE_INTERFACE)
+             state_switch (ctx, STATE_INTERFACE);
+           else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT)
+             state_switch (ctx, STATE_CLASS);
+           else if (CURRENT_NODE (ctx)->type == G_IR_NODE_BOXED)
+             state_switch (ctx, STATE_BOXED);
+           else if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT)
+             state_switch (ctx, STATE_STRUCT);
+           else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION)
+             state_switch (ctx, STATE_UNION);
+           else
+             {
+               int line_number, char_number;
+               g_markup_parse_context_get_position (context, &line_number, &char_number);
+               g_set_error (error,
+                            G_MARKUP_ERROR,
+                            G_MARKUP_ERROR_INVALID_CONTENT,
+                            "Unexpected end tag '%s' on line %d char %d",
+                            element_name,
+                            line_number, char_number);
+             }
+         }
+      }
       break;
 
     case STATE_CLASS_FIELD:
@@ -2235,7 +2973,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_CLASS:
       if (require_end_element (context, ctx, "class", element_name, error))
        {
-         ctx->current_node = NULL;
+         pop_node (ctx);
          state_switch (ctx, STATE_NAMESPACE);
        }
       break;
@@ -2243,7 +2981,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_ERRORDOMAIN:
       if (require_end_element (context, ctx, "errordomain", element_name, error))
        {
-         ctx->current_node = NULL;
+         pop_node (ctx);
          state_switch (ctx, STATE_NAMESPACE);
        }
       break;
@@ -2269,7 +3007,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_INTERFACE:
       if (require_end_element (context, ctx, "interface", element_name, error))
        {
-         ctx->current_node = NULL;
+         pop_node (ctx);
          state_switch (ctx, STATE_NAMESPACE);
        }
       break;
@@ -2277,11 +3015,11 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_ENUM:
       if (strcmp ("member", element_name) == 0)
        break;
-      else if (require_one_of_end_elements (context, ctx, 
-                                           element_name, error, "enumeration", 
+      else if (require_one_of_end_elements (context, ctx,
+                                           element_name, error, "enumeration",
                                            "bitfield", NULL))
        {
-         ctx->current_node = NULL;
+         pop_node (ctx);
          state_switch (ctx, STATE_NAMESPACE);
        }
       break;
@@ -2289,7 +3027,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_BOXED:
       if (require_end_element (context, ctx, "glib:boxed", element_name, error))
        {
-         ctx->current_node = NULL;
+         pop_node (ctx);
          state_switch (ctx, STATE_NAMESPACE);
        }
       break;
@@ -2315,8 +3053,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_STRUCT:
       if (require_end_element (context, ctx, "record", element_name, error))
        {
-         ctx->current_node = NULL;
-         state_switch (ctx, STATE_NAMESPACE);
+         state_switch_end_struct_or_union (context, ctx, element_name, error);
        }
       break;
 
@@ -2332,8 +3069,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_UNION:
       if (require_end_element (context, ctx, "union", element_name, error))
        {
-         ctx->current_node = NULL;
-         state_switch (ctx, STATE_NAMESPACE);
+         state_switch_end_struct_or_union (context, ctx, element_name, error);
        }
       break;
     case STATE_IMPLEMENTS:
@@ -2342,8 +3078,8 @@ end_element_handler (GMarkupParseContext *context,
       if (require_end_element (context, ctx, "implements", element_name, error))
         state_switch (ctx, STATE_CLASS);
       break;
-    case STATE_REQUIRES:
-      if (require_end_element (context, ctx, "requires", element_name, error))
+    case STATE_PREREQUISITE:
+      if (require_end_element (context, ctx, "prerequisite", element_name, error))
         state_switch (ctx, STATE_INTERFACE);
       break;
     case STATE_NAMESPACE_CONSTANT:
@@ -2353,10 +3089,10 @@ end_element_handler (GMarkupParseContext *context,
        break;
       if (require_end_element (context, ctx, "constant", element_name, error))
        {
-         ctx->current_node = NULL;
          switch (ctx->state)
            {
            case STATE_NAMESPACE_CONSTANT:
+                 pop_node (ctx);
              state_switch (ctx, STATE_NAMESPACE);
              break;
            case STATE_CLASS_CONSTANT:
@@ -2371,15 +3107,34 @@ end_element_handler (GMarkupParseContext *context,
            }
        }
       break;
+    case STATE_TYPE:
+      if ((strcmp ("type", element_name) == 0) || (strcmp ("array", element_name) == 0) ||
+         (strcmp ("varargs", element_name) == 0))
+       {
+         end_type (ctx);
+         break;
+       }
+    case STATE_ATTRIBUTE:
+      if (strcmp ("attribute", element_name) == 0)
+        {
+          state_switch (ctx, ctx->prev_state);
+        }
+      break;
+
+    case STATE_UNKNOWN:
+      ctx->unknown_depth -= 1;
+      if (ctx->unknown_depth == 0)
+        state_switch (ctx, ctx->prev_state);
+      break;
     default:
       g_error ("Unhandled state %d in end_element_handler\n", ctx->state);
     }
 }
 
-static void 
+static void
 text_handler (GMarkupParseContext *context,
              const gchar         *text,
-             gsize                text_len,  
+             gsize                text_len,
              gpointer             user_data,
              GError             **error)
 {
@@ -2393,73 +3148,187 @@ cleanup (GMarkupParseContext *context,
 {
   ParseContext *ctx = user_data;
   GList *m;
-  int line_number, char_number;
 
   for (m = ctx->modules; m; m = m->next)
     g_ir_module_free (m->data);
   g_list_free (ctx->modules);
   ctx->modules = NULL;
-  
+
   ctx->current_module = NULL;
 }
 
-static void
-firstpass_start_element_handler (GMarkupParseContext *context,
-                                const gchar         *element_name,
-                                const gchar        **attribute_names,
-                                const gchar        **attribute_values,
-                                gpointer             user_data,
-                                GError             **error)
+static GList *
+post_filter_toplevel_varargs_functions (GList *list,
+                                       GList **varargs_callbacks_out)
 {
-  ParseContext *ctx = user_data;
+  GList *iter;
+  GList *varargs_callbacks = *varargs_callbacks_out;
 
-  if (strcmp (element_name, "alias") == 0) 
+  iter = list;
+  while (iter)
     {
-      start_alias (context, element_name, attribute_names, attribute_values,
-                  ctx, error);
+      GList *link = iter;
+      GIrNode *node = iter->data;
+
+      iter = iter->next;
+
+      if (node->type == G_IR_NODE_FUNCTION)
+       {
+         if (((GIrNodeFunction*)node)->is_varargs)
+           {
+             list = g_list_delete_link (list, link);
+           }
+       }
+      if (node->type == G_IR_NODE_CALLBACK)
+       {
+         if (((GIrNodeFunction*)node)->is_varargs)
+           {
+             varargs_callbacks = g_list_append (varargs_callbacks,
+                                                node);
+             list = g_list_delete_link (list, link);
+           }
+       }
     }
+
+  *varargs_callbacks_out = varargs_callbacks;
+
+  return list;
 }
 
-static void
-firstpass_end_element_handler (GMarkupParseContext *context,
-                              const gchar         *element_name,
-                              gpointer             user_data,
-                              GError             **error)
+static GList *
+post_filter_varargs_functions (GList *list, GList ** varargs_callbacks_out)
 {
-  ParseContext *ctx = user_data;
+  GList *iter;
+  GList *varargs_callbacks;
 
-}
+  list = post_filter_toplevel_varargs_functions (list, varargs_callbacks_out);
 
+  varargs_callbacks = *varargs_callbacks_out;
 
-static GMarkupParser firstpass_parser = 
-{
-  firstpass_start_element_handler,
-  firstpass_end_element_handler,
-  NULL,
-  NULL,
-  NULL,
-};
+  iter = list;
+  while (iter)
+    {
+      GList *link = iter;
+      GIrNode *node = iter->data;
+
+      iter = iter->next;
 
+      if (node->type == G_IR_NODE_FUNCTION)
+       {
+         GList *param;
+         gboolean function_done = FALSE;
+
+         for (param = ((GIrNodeFunction *)node)->parameters;
+              param;
+              param = param->next)
+           {
+             GIrNodeParam *node = (GIrNodeParam *)param->data;
+
+             if (function_done)
+               break;
+
+             if (node->type->is_interface)
+               {
+                 GList *callback;
+                 for (callback = varargs_callbacks;
+                      callback;
+                      callback = callback->next)
+                   {
+                     if (!strcmp (node->type->interface,
+                                  ((GIrNode *)callback->data)->name))
+                       {
+                         list = g_list_delete_link (list, link);
+                         function_done = TRUE;
+                         break;
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+  *varargs_callbacks_out = varargs_callbacks;
 
-static GMarkupParser parser = 
+  return list;
+}
+
+static void
+post_filter (GIrModule *module)
 {
-  start_element_handler,
-  end_element_handler,
-  text_handler,
-  NULL,
-  cleanup
-};
+  GList *iter;
+  GList *varargs_callbacks = NULL;
+
+  module->entries = post_filter_varargs_functions (module->entries,
+                                                  &varargs_callbacks);
+  iter = module->entries;
+  while (iter)
+    {
+      GIrNode *node = iter->data;
+
+      iter = iter->next;
+
+      if (node->type == G_IR_NODE_OBJECT ||
+         node->type == G_IR_NODE_INTERFACE)
+       {
+         GIrNodeInterface *iface = (GIrNodeInterface*)node;
+         iface->members = post_filter_varargs_functions (iface->members,
+                                                         &varargs_callbacks);
+       }
+      else if (node->type == G_IR_NODE_BOXED)
+       {
+         GIrNodeBoxed *boxed = (GIrNodeBoxed*)node;
+         boxed->members = post_filter_varargs_functions (boxed->members,
+                                                         &varargs_callbacks);
+       }
+      else if (node->type == G_IR_NODE_STRUCT)
+       {
+         GIrNodeStruct *iface = (GIrNodeStruct*)node;
+         iface->members = post_filter_varargs_functions (iface->members,
+                                                         &varargs_callbacks);
+       }
+      else if (node->type == G_IR_NODE_UNION)
+       {
+         GIrNodeUnion *iface = (GIrNodeUnion*)node;
+         iface->members = post_filter_varargs_functions (iface->members,
+                                                         &varargs_callbacks);
+       }
+    }
+  g_list_free (varargs_callbacks);
+}
 
-GList * 
-g_ir_parse_string (const gchar  *buffer, 
-                  gssize        length,
-                  GError      **error)
+/**
+ * g_ir_parser_parse_string:
+ * @parser: a #GIrParser
+ * @namespace: the namespace of the string
+ * @buffer: the data containing the XML
+ * @length: length of the data
+ * @error: return location for a #GError, or %NULL
+ *
+ * Parse a string that holds a complete GIR XML file, and return a list of a
+ * a #GirModule for each &lt;namespace/&gt; element within the file.
+ *
+ * Returns: a newly allocated list of #GIrModule. The modules themselves
+ *  are owned by the #GIrParser and will be freed along with the parser.
+ */
+GList *
+g_ir_parser_parse_string (GIrParser           *parser,
+                         const gchar         *namespace,
+                         const gchar         *buffer,
+                         gssize               length,
+                         GError             **error)
 {
   ParseContext ctx = { 0 };
   GMarkupParseContext *context;
 
+  ctx.parser = parser;
   ctx.state = STATE_START;
+  ctx.namespace = namespace;
+  ctx.include_modules = NULL;
   ctx.aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  ctx.disguised_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  ctx.type_depth = 0;
+  ctx.dependencies = NULL;
+  ctx.current_module = NULL;
 
   context = g_markup_parse_context_new (&firstpass_parser, 0, &ctx, NULL);
 
@@ -2468,37 +3337,97 @@ g_ir_parse_string (const gchar  *buffer,
 
   if (!g_markup_parse_context_end_parse (context, error))
     goto out;
-  
-  context = g_markup_parse_context_new (&parser, 0, &ctx, NULL);
+
+  g_markup_parse_context_free (context);
+
+  context = g_markup_parse_context_new (&markup_parser, 0, &ctx, NULL);
   if (!g_markup_parse_context_parse (context, buffer, length, error))
     goto out;
 
   if (!g_markup_parse_context_end_parse (context, error))
     goto out;
 
+  parser->parsed_modules = g_list_concat (g_list_copy (ctx.modules),
+                                         parser->parsed_modules);
+
  out:
 
-  g_hash_table_destroy (ctx.aliases);
-  
+  if (ctx.modules == NULL)
+    {
+      /* An error occurred before we created a module, so we haven't
+       * transferred ownership of these hash tables to the module.
+       */
+      if (ctx.aliases != NULL)
+       g_hash_table_destroy (ctx.aliases);
+      if (ctx.disguised_structures != NULL)
+       g_hash_table_destroy (ctx.disguised_structures);
+      g_list_free (ctx.include_modules);
+    }
+
   g_markup_parse_context_free (context);
-  
+
   return ctx.modules;
 }
 
+/**
+ * g_ir_parser_parse_file:
+ * @parser: a #GIrParser
+ * @filename: filename to parse
+ * @error: return location for a #GError, or %NULL
+ *
+ * Parse GIR XML file, and return a list of a a #GirModule for each
+ * &lt;namespace/&gt; element within the file.
+ *
+ * Returns: a newly allocated list of #GIrModule. The modules themselves
+ *  are owned by the #GIrParser and will be freed along with the parser.
+ */
 GList *
-g_ir_parse_file (const gchar  *filename,
-                GError      **error)
+g_ir_parser_parse_file (GIrParser   *parser,
+                       const gchar *filename,
+                       GError     **error)
 {
   gchar *buffer;
   gsize length;
   GList *modules;
+  GList *iter;
+  const char *slash;
+  char *dash;
+  char *namespace;
+
+  if (!g_str_has_suffix (filename, ".gir"))
+    {
+      g_set_error (error,
+                  G_MARKUP_ERROR,
+                  G_MARKUP_ERROR_INVALID_CONTENT,
+                  "Expected filename to end with '.gir'");
+      return NULL;
+    }
 
   g_debug ("[parsing] filename %s", filename);
 
+  slash = g_strrstr (filename, "/");
+  if (!slash)
+    namespace = g_strdup (filename);
+  else
+    namespace = g_strdup (slash+1);
+  namespace[strlen(namespace)-4] = '\0';
+
+  /* Remove version */
+  dash = strstr (namespace, "-");
+  if (dash != NULL)
+    *dash = '\0';
+
   if (!g_file_get_contents (filename, &buffer, &length, error))
     return NULL;
-  
-  modules = g_ir_parse_string (buffer, length, error);
+
+  modules = g_ir_parser_parse_string (parser, namespace, buffer, length, error);
+
+  for (iter = modules; iter; iter = iter->next)
+    {
+      post_filter ((GIrModule*)iter->data);
+    }
+
+  g_free (namespace);
 
   g_free (buffer);