[gobject-introspection/wip/transformer: 10/14] Major rewrite



commit a802a9833ea29256c664e4f91910d15d1682a215
Author: Colin Walters <walters verbum org>
Date:   Tue Jul 27 06:16:37 2010 -0400

    Major rewrite
    
    One of the first big changes in this rewrite is changing the Type
    object to have separate target_fundamental and target_giname properties,
    rather than just being strings.  Previously in the scanner, it was
    awful because we used heuristics around strings.
    
    The ast.py is refactored so that not everything is a Node - that
    was a rather useless abstraction.  Now, only things which can have
    a GIName are Node.  E.g. Type and Field are no longer Node.
    
    More things were merged from glibast.py into ast.py, since it isn't
    a very useful split.
    
    transformer.py gains more intelligence and will e.g. turn GLib.List
    into a List() object earlier.  The namespace processing is a lot
    cleaner now; since we parse the included .girs, we know the C
    prefix for each namespace, and have functions to parse both
    C type names (GtkFooBar) and symbols gtk_foo_bar into their
    symbols cleanly.  Type resolution is much, much saner because
    we know Type(target_giname=Gtk.Foo) maps to the namespace Gtk.
    
    glibtransformer.py now just handles the XML processing from the dump,
    and a few miscellaneous things.
    
    The major heavy lifting now lives in primarytransformer.py, which
    is a combination of most of annotationparser.py and half of
    glibtransformer.py.
    
    annotationparser.py now literally just parses annotations; it's
    no longer in the business of e.g. guessing transfer too.
    
    finaltransformer.py is a new file which does post-analysis for
    "introspectability" mainly.
    
    girparser.c is fixed for some introspectable=0 processing.

 gir/DBus-1.0.gir                           |    2 +-
 gir/DBusGLib-1.0.gir                       |    2 +-
 gir/GL-1.0.gir                             |    2 +-
 gir/Makefile.am                            |   12 +-
 gir/cairo-1.0.gir                          |    5 +-
 gir/fontconfig-2.0.gir                     |    2 +-
 gir/freetype2-2.0.gir                      |    2 +-
 gir/libxml2-2.0.gir                        |    2 +-
 gir/xfixes-4.0.gir                         |    2 +-
 gir/xft-2.0.gir                            |    2 +-
 gir/xlib-2.0.gir                           |    2 +-
 gir/xrandr-1.3.gir                         |    2 +-
 girepository/girmodule.c                   |    2 +-
 girepository/girparser.c                   |  109 ++-
 girepository/gitypelib.c                   |    4 +-
 giscanner/Makefile.am                      |    4 +
 giscanner/annotationparser.py              |  734 +------------------
 giscanner/ast.py                           |  740 ++++++++++++-------
 giscanner/codegen.py                       |  137 ++++
 giscanner/finaltransformer.py              |  159 ++++
 giscanner/girparser.py                     |  491 +++++++++-----
 giscanner/girwriter.py                     |  235 ++++---
 giscanner/glibast.py                       |   91 ++-
 giscanner/glibtransformer.py               | 1101 +++++-----------------------
 giscanner/primarytransformer.py            |  928 +++++++++++++++++++++++
 giscanner/scannermain.py                   |  163 ++---
 giscanner/sourcescanner.py                 |   10 +-
 giscanner/testcodegen.py                   |  124 ++++
 giscanner/transformer.py                   |  749 ++++++++++---------
 giscanner/utils.py                         |   13 +
 misc/pep8.py                               |    2 +-
 tests/scanner/Annotation-1.0-expected.gir  |  109 ++-
 tests/scanner/Bar-1.0-expected.gir         |    9 +-
 tests/scanner/Foo-1.0-expected.gir         |  125 +++-
 tests/scanner/GtkFrob-1.0-expected.gir     |    5 +-
 tests/scanner/Makefile.am                  |    2 +-
 tests/scanner/Regress-1.0-expected.gir     |  152 +++--
 tests/scanner/TestInherit-1.0-expected.gir |    6 +-
 tests/scanner/Utility-1.0-expected.gir     |   18 +-
 39 files changed, 3385 insertions(+), 2874 deletions(-)
---
diff --git a/gir/DBus-1.0.gir b/gir/DBus-1.0.gir
index 331c228..5eab4ce 100644
--- a/gir/DBus-1.0.gir
+++ b/gir/DBus-1.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
diff --git a/gir/DBusGLib-1.0.gir b/gir/DBusGLib-1.0.gir
index c71d77b..b9ba132 100644
--- a/gir/DBusGLib-1.0.gir
+++ b/gir/DBusGLib-1.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
 	    xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
diff --git a/gir/GL-1.0.gir b/gir/GL-1.0.gir
index 0defbbf..cf07454 100644
--- a/gir/GL-1.0.gir
+++ b/gir/GL-1.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="GL" version="1.0">
diff --git a/gir/Makefile.am b/gir/Makefile.am
index 82a2908..05ab955 100644
--- a/gir/Makefile.am
+++ b/gir/Makefile.am
@@ -33,7 +33,7 @@ GLIB_LIBRARY=glib-2.0
 endif
 
 GLib_2_0_gir_LIBS = $(GLIB_LIBRARY)
-GLib_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="glib.h"
+GLib_2_0_gir_SCANNERFLAGS = --reparse-validate --identifier-prefix=G --symbol-prefix=g --symbol-prefix=glib --c-include="glib.h"
 GLib_2_0_gir_PACKAGES = glib-2.0
 GLib_2_0_gir_CFLAGS = \
             -I$(GLIB_INCLUDEDIR) \
@@ -65,7 +65,7 @@ endif
 GObject-2.0.gir: GLib-2.0.gir
 
 GObject_2_0_gir_LIBS = $(GOBJECT_LIBRARY)
-GObject_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="glib-object.h" --add-include-path=.
+GObject_2_0_gir_SCANNERFLAGS = --reparse-validate --identifier-prefix=G --c-include="glib-object.h" --add-include-path=.
 GObject_2_0_gir_PACKAGES = gobject-2.0
 GObject_2_0_gir_INCLUDES = GLib-2.0
 GObject_2_0_gir_CFLAGS = \
@@ -91,7 +91,7 @@ endif
 GModule-2.0.gir: GLib-2.0.gir
 
 GModule_2_0_gir_LIBS = $(GMODULE_LIBRARY)
-GModule_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="gmodule.h" --add-include-path=.
+GModule_2_0_gir_SCANNERFLAGS = --identifier-prefix=G --c-include="gmodule.h" --add-include-path=.
 GModule_2_0_gir_PACKAGES = gmodule-2.0
 GModule_2_0_gir_INCLUDES = GLib-2.0
 GModule_2_0_gir_CFLAGS = \
@@ -122,7 +122,7 @@ endif
 Gio-2.0.gir: GObject-2.0.gir
 
 Gio_2_0_gir_LIBS = $(GIO_LIBRARY)
-Gio_2_0_gir_SCANNERFLAGS = --warn-all --strip-prefix=G --c-include="gio/gio.h" --add-include-path=.
+Gio_2_0_gir_SCANNERFLAGS = --reparse-validate --warn-all --identifier-prefix=G --c-include="gio/gio.h" --add-include-path=.
 Gio_2_0_gir_PACKAGES = gio-2.0 $(GIO_UNIX_PACKAGES)
 Gio_2_0_gir_INCLUDES = GObject-2.0
 Gio_2_0_gir_CFLAGS = \
@@ -141,7 +141,9 @@ GIRepository-2.0.gir: GObject-2.0.gir $(top_builddir)/girepository/libgireposito
 
 GIRepository_2_0_gir_LIBS = girepository-1.0
 GIRepository_2_0_gir_SCANNERFLAGS = \
-        --strip-prefix=g \
+	--warn-all \
+        --identifier-prefix=GI \
+        --symbol-prefix=g \
         --c-include="girepository.h" \
         --pkg-export gobject-introspection-1.0 \
         --add-include-path=.
diff --git a/gir/cairo-1.0.gir b/gir/cairo-1.0.gir
index 52e4e76..05f1a78 100644
--- a/gir/cairo-1.0.gir
+++ b/gir/cairo-1.0.gir
@@ -1,9 +1,10 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
-  <namespace name="cairo" version="1.0">
+  <namespace name="cairo" version="1.0"
+	     c:prefix="cairo">
     <record name="Context" c:type="cairo_t" foreign="1"/>
     <record name="Surface" c:type="cairo_surface_t" foreign="1"/>
     <record name="Matrix" c:type="cairo_matrix_t" foreign="1"/>
diff --git a/gir/fontconfig-2.0.gir b/gir/fontconfig-2.0.gir
index 024c57d..192d720 100644
--- a/gir/fontconfig-2.0.gir
+++ b/gir/fontconfig-2.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="fontconfig" version="2.0">
diff --git a/gir/freetype2-2.0.gir b/gir/freetype2-2.0.gir
index 9065bb9..239e4af 100644
--- a/gir/freetype2-2.0.gir
+++ b/gir/freetype2-2.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="freetype2" version="2.0">
diff --git a/gir/libxml2-2.0.gir b/gir/libxml2-2.0.gir
index 3305d68..cd962d0 100644
--- a/gir/libxml2-2.0.gir
+++ b/gir/libxml2-2.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="libxml2" version="2.0">
diff --git a/gir/xfixes-4.0.gir b/gir/xfixes-4.0.gir
index 4441e70..7a0fa18 100644
--- a/gir/xfixes-4.0.gir
+++ b/gir/xfixes-4.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="xfixes" version="4.0">
diff --git a/gir/xft-2.0.gir b/gir/xft-2.0.gir
index db7f77c..1117847 100644
--- a/gir/xft-2.0.gir
+++ b/gir/xft-2.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="xft" version="2.0">
diff --git a/gir/xlib-2.0.gir b/gir/xlib-2.0.gir
index 3349fb0..1081652 100644
--- a/gir/xlib-2.0.gir
+++ b/gir/xlib-2.0.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="xlib" version="2.0">
diff --git a/gir/xrandr-1.3.gir b/gir/xrandr-1.3.gir
index 30e5c5f..d8844da 100644
--- a/gir/xrandr-1.3.gir
+++ b/gir/xrandr-1.3.gir
@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";>
   <namespace name="xrandr" version="1.3">
diff --git a/girepository/girmodule.c b/girepository/girmodule.c
index 70b1d2a..ebee26c 100644
--- a/girepository/girmodule.c
+++ b/girepository/girmodule.c
@@ -309,7 +309,7 @@ g_ir_module_build_typelib (GIrModule  *module)
   /* fill in header */
   header = (Header *)data;
   memcpy (header, G_IR_MAGIC, 16);
-  header->major_version = 3;
+  header->major_version = 4;
   header->minor_version = 0;
   header->reserved = 0;
   header->n_entries = n_entries;
diff --git a/girepository/girparser.c b/girepository/girparser.c
index 570e648..bdb8781 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -33,7 +33,7 @@
 /* This is a "major" version in the sense that it's only bumped
  * for incompatible changes.
  */
-#define SUPPORTED_GIR_VERSION "1.1"
+#define SUPPORTED_GIR_VERSION "1.2"
 
 struct _GIrParser
 {
@@ -48,33 +48,33 @@ typedef enum
   STATE_REPOSITORY,
   STATE_INCLUDE,
   STATE_C_INCLUDE,
-  STATE_PACKAGE,
-  STATE_NAMESPACE, /* 5 */
+  STATE_PACKAGE,  /* 5 */
+  STATE_NAMESPACE,
   STATE_ENUM,
   STATE_BITFIELD,
   STATE_FUNCTION,
-  STATE_FUNCTION_RETURN,
-  STATE_FUNCTION_PARAMETERS, /* 10 */
+  STATE_FUNCTION_RETURN,  /* 10 */
+  STATE_FUNCTION_PARAMETERS,
   STATE_FUNCTION_PARAMETER,
   STATE_CLASS,
   STATE_CLASS_FIELD,
-  STATE_CLASS_PROPERTY,
-  STATE_INTERFACE, /* 15 */
+  STATE_CLASS_PROPERTY,  /* 15 */
+  STATE_INTERFACE,
   STATE_INTERFACE_PROPERTY,
   STATE_INTERFACE_FIELD,
   STATE_IMPLEMENTS,
-  STATE_PREREQUISITE,
-  STATE_BOXED,   /* 20 */
+  STATE_PREREQUISITE,    /* 20 */
+  STATE_BOXED,
   STATE_BOXED_FIELD,
   STATE_STRUCT,
   STATE_STRUCT_FIELD,
-  STATE_ERRORDOMAIN,
-  STATE_UNION, /* 25 */
+  STATE_ERRORDOMAIN, /* 25 */
+  STATE_UNION,
   STATE_UNION_FIELD,
   STATE_NAMESPACE_CONSTANT,
   STATE_CLASS_CONSTANT,
-  STATE_INTERFACE_CONSTANT,
-  STATE_ALIAS, /* 30 */
+  STATE_INTERFACE_CONSTANT,  /* 30 */
+  STATE_ALIAS,
   STATE_TYPE,
   STATE_ATTRIBUTE,
   STATE_DOC,
@@ -779,6 +779,7 @@ start_function (GMarkupParseContext *context,
   const gchar *throws;
   GIrNodeFunction *function;
   gboolean found = FALSE;
+  gboolean in_embedded_type;
 
   switch (ctx->state)
     {
@@ -787,15 +788,14 @@ start_function (GMarkupParseContext *context,
 	       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);
+      found = strcmp (element_name, "constructor") == 0;
       /* fallthrough */
     case STATE_INTERFACE:
       found = (found ||
+	       strcmp (element_name, "function") == 0 ||
 	       strcmp (element_name, "method") == 0 ||
 	       strcmp (element_name, "callback") == 0);
       break;
@@ -809,12 +809,13 @@ start_function (GMarkupParseContext *context,
   if (!found)
     return FALSE;
 
-  if (ctx->state == STATE_STRUCT_FIELD)
-    ctx->in_embedded_type = TRUE;
+  in_embedded_type = ctx->state == STATE_STRUCT_FIELD;
 
   if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
     return TRUE;
 
+  ctx->in_embedded_type = in_embedded_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);
@@ -964,15 +965,16 @@ parse_property_transfer (GIrNodeProperty *property,
     }
 }
 
-static void
-parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name)
+static gboolean
+parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name,
+		      GError **error)
 {
   if (transfer == NULL)
   {
-    if (!name)
-      g_warning ("required attribute 'transfer-ownership' missing");
-    else
-      g_warning ("required attribute 'transfer-ownership' for function '%s'", name);
+    g_set_error (error, G_MARKUP_ERROR,
+		 G_MARKUP_ERROR_INVALID_CONTENT,
+		 "required attribute 'transfer-ownership' missing");
+    return FALSE;
   }
   else if (strcmp (transfer, "none") == 0)
     {
@@ -991,8 +993,12 @@ parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *n
     }
   else
     {
-      g_warning ("Unknown transfer-ownership value: %s", transfer);
+      g_set_error (error, G_MARKUP_ERROR,
+		   G_MARKUP_ERROR_INVALID_CONTENT,
+		   "invalid value for 'transfer-ownership': %s", transfer);
+      return FALSE;
     }
+  return TRUE;
 }
 
 static gboolean
@@ -1078,7 +1084,8 @@ start_parameter (GMarkupParseContext *context,
   else
     param->allow_none = FALSE;
 
-  parse_param_transfer (param, transfer, name);
+  if (!parse_param_transfer (param, transfer, name, error))
+    return FALSE;
 
   if (scope && strcmp (scope, "call") == 0)
     param->scope = GI_SCOPE_TYPE_CALL;
@@ -1142,14 +1149,25 @@ start_field (GMarkupParseContext *context,
   const gchar *bits;
   const gchar *branch;
   GIrNodeField *field;
+  ParseState target_state;
+  gboolean introspectable;
 
   switch (ctx->state)
     {
     case STATE_CLASS:
+      target_state = STATE_CLASS_FIELD;
+      break;
     case STATE_BOXED:
+      target_state = STATE_BOXED_FIELD;
+      break;
     case STATE_STRUCT:
+      target_state = STATE_STRUCT_FIELD;
+      break;
     case STATE_UNION:
+      target_state = STATE_UNION_FIELD;
+      break;
     case STATE_INTERFACE:
+      target_state = STATE_INTERFACE_FIELD;
       break;
     default:
       return FALSE;
@@ -1158,6 +1176,13 @@ start_field (GMarkupParseContext *context,
   if (strcmp (element_name, "field") != 0)
     return FALSE;
 
+  g_assert (ctx->state != STATE_PASSTHROUGH);
+
+  /* We handle introspectability specially here; we replace with just gpointer
+   * for the type.
+   */
+  introspectable = introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state);
+
   name = find_attribute ("name", attribute_names, attribute_values);
   readable = find_attribute ("readable", attribute_names, attribute_values);
   writable = find_attribute ("writable", attribute_names, attribute_values);
@@ -1172,7 +1197,15 @@ start_field (GMarkupParseContext *context,
 
   field = (GIrNodeField *)g_ir_node_new (G_IR_NODE_FIELD,
 					 ctx->current_module);
-  ctx->current_typed = (GIrNode*) field;
+  if (introspectable)
+    {
+      ctx->current_typed = (GIrNode*) field;
+    }
+  else
+    {
+      field->type = parse_type (ctx, "gpointer");
+    }
+
   ((GIrNode *)field)->name = g_strdup (name);
   /* Fields are assumed to be read-only.
    * (see also girwriter.py and generate.c)
@@ -1193,7 +1226,6 @@ start_field (GMarkupParseContext *context,
 
 	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:
@@ -1202,7 +1234,6 @@ start_field (GMarkupParseContext *context,
 
 	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:
@@ -1211,7 +1242,6 @@ start_field (GMarkupParseContext *context,
 
 	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:
@@ -1220,7 +1250,6 @@ start_field (GMarkupParseContext *context,
 
 	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:
@@ -1242,7 +1271,6 @@ start_field (GMarkupParseContext *context,
 
 	    union_->discriminators = g_list_append (union_->discriminators, constant);
 	  }
-	state_switch (ctx, STATE_UNION_FIELD);
       }
       break;
     default:
@@ -2169,7 +2197,8 @@ start_return_value (GMarkupParseContext *context,
   state_switch (ctx, STATE_FUNCTION_RETURN);
 
   transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
-  parse_param_transfer (param, transfer, NULL);
+  if (!parse_param_transfer (param, transfer, NULL, error))
+    return FALSE;
 
   switch (CURRENT_NODE (ctx)->type)
     {
@@ -2979,8 +3008,9 @@ start_element_handler (GMarkupParseContext *context,
     {
       g_markup_parse_context_get_position (context, &line_number, &char_number);
       if (!g_str_has_prefix (element_name, "c:"))
-	g_printerr ("%s:%d:%d: warning: dropping to PASSTHROUGH\n",
-		    ctx->file_path, line_number, char_number);
+	g_printerr ("%s:%d:%d: warning: element %s from state %d is unknown, ignoring\n",
+		    ctx->file_path, line_number, char_number, element_name,
+		    ctx->state);
       state_switch (ctx, STATE_PASSTHROUGH);
       ctx->unknown_depth = 1;
     }
@@ -3026,9 +3056,9 @@ require_one_of_end_elements (GMarkupParseContext *context,
   g_set_error (error,
 	       G_MARKUP_ERROR,
 	       G_MARKUP_ERROR_INVALID_CONTENT,
-	       "Unexpected end tag '%s' on line %d char %d; current state=%d",
+	       "Unexpected end tag '%s' on line %d char %d; current state=%d (prev=%d)",
 	       actual_name,
-	       line_number, char_number, ctx->state);
+	       line_number, char_number, ctx->state, ctx->prev_state);
   return FALSE;
 }
 
@@ -3379,6 +3409,7 @@ end_element_handler (GMarkupParseContext *context,
 
     case STATE_PASSTHROUGH:
       ctx->unknown_depth -= 1;
+      g_assert (ctx->unknown_depth >= 0);
       if (ctx->unknown_depth == 0)
         state_switch (ctx, ctx->prev_state);
       break;
@@ -3486,7 +3517,9 @@ g_ir_parser_parse_string (GIrParser           *parser,
 
   g_markup_parse_context_free (context);
 
-  return ctx.modules->data;
+  if (ctx.modules)
+    return ctx.modules->data;
+  return NULL;
 }
 
 /**
diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c
index ef87c0e..f47a743 100644
--- a/girepository/gitypelib.c
+++ b/girepository/gitypelib.c
@@ -288,12 +288,12 @@ validate_header_basic (const guint8   *memory,
 
     }
 
-  if (header->major_version != 3 || header->minor_version != 0)
+  if (header->major_version != 4 || header->minor_version != 0)
     {
       g_set_error (error,
 		   G_TYPELIB_ERROR,
 		   G_TYPELIB_ERROR_INVALID_HEADER,
-		   "Typelib version mismatch; expected 3, found %d",
+		   "Typelib version mismatch; expected 4, found %d",
 		   header->major_version);
       return FALSE;
 
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
index 642bcbc..f208a47 100644
--- a/giscanner/Makefile.am
+++ b/giscanner/Makefile.am
@@ -36,8 +36,10 @@ pkgpyexec_PYTHON = 		\
 	annotationparser.py	\
 	ast.py			\
 	cachestore.py		\
+	codegen.py		\
 	config.py		\
 	dumper.py		\
+	finaltransformer.py	\
 	girparser.py		\
 	girwriter.py		\
 	glibast.py		\
@@ -45,9 +47,11 @@ pkgpyexec_PYTHON = 		\
 	libtoolimporter.py	\
 	minixpath.py		\
 	odict.py		\
+	primarytransformer.py	\
 	shlibs.py		\
 	scannermain.py		\
 	sourcescanner.py	\
+	testcodegen.py		\
 	transformer.py		\
 	utils.py		\
 	xmlwriter.py
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 9e4340f..48c4cd7 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -22,23 +22,7 @@
 
 import re
 
-from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
-                  Interface, List, Map, Parameter, Property, Record, Return,
-                  Type, Union, Varargs,
-                  default_array_types,
-                  BASIC_GIR_TYPES,
-                  PARAM_DIRECTION_INOUT,
-                  PARAM_DIRECTION_IN,
-                  PARAM_DIRECTION_OUT,
-                  PARAM_SCOPE_CALL,
-                  PARAM_SCOPE_ASYNC,
-                  PARAM_SCOPE_NOTIFIED,
-                  PARAM_TRANSFER_NONE,
-                  PARAM_TRANSFER_CONTAINER,
-                  PARAM_TRANSFER_FULL,
-                  TYPE_ANY, TYPE_NONE)
 from .odict import odict
-from .glibast import GLibBoxed
 
 # All gtk-doc comments needs to start with this:
 _COMMENT_HEADER = '*\n '
@@ -73,9 +57,6 @@ OPT_DESTROY = 'destroy'
 OPT_SKIP = 'skip'
 OPT_FOREIGN = 'foreign'
 
-# Specific option values
-OPT_VAL_BITFIELD = 'bitfield'
-
 # Array options - array specific annotations
 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
 OPT_ARRAY_LENGTH = 'length'
@@ -85,10 +66,6 @@ OPT_SCOPE_ASYNC = 'async'
 OPT_SCOPE_CALL = 'call'
 OPT_SCOPE_NOTIFIED = 'notified'
 
-class InvalidAnnotationError(Exception):
-    pass
-
-
 class DocBlock(object):
 
     def __init__(self, name, options):
@@ -153,16 +130,16 @@ class AnnotationParser(object):
     OPTION_RE = re.compile(r'\([A-Za-z]+[^(]*\)')
     RETURNS_RE = re.compile(r'^return(s?)( value)?:', re.IGNORECASE)
 
-    def __init__(self, namespace, source_scanner, transformer):
+    def __init__(self, source_scanner, transformer):
         self._blocks = {}
-        self._namespace = namespace
         self._transformer = transformer
-        for comment in source_scanner.get_comments():
-            self._parse_comment(comment)
+        self._source_scanner = source_scanner
+        self._namespace = transformer.namespace
 
     def parse(self):
-        aa = AnnotationApplier(self._blocks, self._transformer)
-        aa.parse(self._namespace)
+        for comment in self._source_scanner.get_comments():
+            self._parse_comment(comment)
+        return self._blocks
 
     def _parse_comment(self, comment):
         # We're looking for gtk-doc comments here, they look like this:
@@ -199,7 +176,6 @@ class AnnotationParser(object):
         else:
             block_name, block_options = block_header, {}
         block = DocBlock(block_name, block_options)
-        debug = block_name == 'annotation_object_compute_sum_n'
         comment_lines = []
         parsing_parameters = True
         last_param_tag = None
@@ -311,701 +287,3 @@ class AnnotationParser(object):
                 opened = -1
 
         return options
-
-
-class AnnotationApplier(object):
-
-    def __init__(self, blocks, transformer):
-        self._blocks = blocks
-        self._transformer = transformer
-
-    def _get_tag(self, block, tag_name):
-        if block is None:
-            return None
-
-        return block.get(tag_name)
-
-    def parse(self, namespace):
-        self._namespace = namespace
-        for node in namespace.nodes[:]:
-            self._parse_node(node)
-        del self._namespace
-
-    # Boring parsing boilerplate.
-
-    def _parse_node(self, node):
-        if isinstance(node, Function):
-            self._parse_function(node)
-        elif isinstance(node, Enum):
-            self._parse_enum(node)
-        elif isinstance(node, Bitfield):
-            self._parse_bitfield(node)
-        elif isinstance(node, Class):
-            self._parse_class(node)
-        elif isinstance(node, Interface):
-            self._parse_interface(node)
-        elif isinstance(node, Callback):
-            self._parse_callback(node)
-        elif isinstance(node, Record):
-            self._parse_record(node)
-        elif isinstance(node, Union):
-            self._parse_union(node)
-        elif isinstance(node, GLibBoxed):
-            self._parse_boxed(node)
-
-    def _parse_class(self, class_):
-        block = self._blocks.get(class_.type_name)
-        self._parse_node_common(class_, block)
-        self._parse_constructors(class_.constructors)
-        self._parse_methods(class_, class_.methods)
-        self._parse_vfuncs(class_, class_.virtual_methods)
-        self._parse_methods(class_, class_.static_methods)
-        self._parse_properties(class_, class_.properties)
-        self._parse_signals(class_, class_.signals)
-        self._parse_fields(class_, class_.fields)
-        if block:
-            class_.doc = block.comment
-        self._parse_type_instance_tags(class_, block)
-
-    def _parse_interface(self, interface):
-        block = self._blocks.get(interface.type_name)
-        self._parse_node_common(interface, block)
-        self._parse_methods(interface, interface.methods)
-        self._parse_vfuncs(interface, interface.virtual_methods)
-        self._parse_properties(interface, interface.properties)
-        self._parse_signals(interface, interface.signals)
-        self._parse_fields(interface, interface.fields)
-        if block:
-            interface.doc = block.comment
-
-    def _parse_record(self, record):
-        block = self._blocks.get(record.symbol)
-        self._parse_node_common(record, block)
-        self._parse_constructors(record.constructors)
-        self._parse_methods(record, record.methods)
-        self._parse_fields(record, record.fields, block)
-        if block:
-            record.doc = block.comment
-
-    def _parse_boxed(self, boxed):
-        block = self._blocks.get(boxed.name)
-        self._parse_node_common(boxed, block)
-        self._parse_constructors(boxed.constructors)
-        self._parse_methods(boxed, boxed.methods)
-        if block:
-            boxed.doc = block.comment
-
-    def _parse_union(self, union):
-        block = self._blocks.get(union.name)
-        self._parse_node_common(union, block)
-        self._parse_fields(union, union.fields, block)
-        self._parse_constructors(union.constructors)
-        self._parse_methods(union, union.methods)
-        if block:
-            union.doc = block.comment
-
-    def _parse_enum(self, enum):
-        block = self._blocks.get(enum.symbol)
-        self._parse_node_common(enum, block)
-        if block:
-            enum.doc = block.comment
-            type_opt = block.options.get(OPT_TYPE)
-            if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
-                # This is hack, but hey, it works :-)
-                enum.__class__ = Bitfield
-
-    def _parse_bitfield(self, bitfield):
-        block = self._blocks.get(bitfield.symbol)
-        self._parse_node_common(bitfield, block)
-        if block:
-            bitfield.doc = block.comment
-
-    def _parse_constructors(self, constructors):
-        for ctor in constructors:
-            self._parse_function(ctor)
-
-    def _parse_fields(self, parent, fields, block=None):
-        for field in fields:
-            self._parse_field(parent, field, block)
-
-    def _parse_properties(self, parent, properties):
-        for prop in properties:
-            self._parse_property(parent, prop)
-
-    def _parse_methods(self, parent, methods):
-        for method in methods:
-            self._parse_method(parent, method)
-
-    def _parse_vfuncs(self, parent, vfuncs):
-        for vfunc in vfuncs:
-            self._parse_vfunc(parent, vfunc)
-
-    def _parse_signals(self, parent, signals):
-        for signal in signals:
-            self._parse_signal(parent, signal)
-
-    def _parse_property(self, parent, prop):
-        block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
-        self._parse_node_common(prop, block)
-        if block:
-            prop.doc = block.comment
-
-        transfer_tag = self._get_tag(block, TAG_TRANSFER)
-        if transfer_tag is not None:
-            options = {OPT_TRANSFER: Option(transfer_tag.value)}
-        else:
-            options = {}
-        prop.transfer = self._extract_transfer(parent, prop, options)
-
-        type_tag = self._get_tag(block, TAG_TYPE)
-        if type_tag:
-            prop.type = self._resolve(type_tag.value, prop.type)
-
-    def _parse_callback(self, callback):
-        block = self._blocks.get(callback.ctype)
-        self._parse_node_common(callback, block)
-        self._parse_params(callback, callback.parameters, block)
-        self._parse_return(callback, callback.retval, block)
-        if block:
-            callback.doc = block.comment
-
-    def _parse_callable(self, callable, block):
-        self._parse_node_common(callable, block)
-        for i, param in enumerate(callable.parameters):
-            if (param.type.ctype != 'GDestroyNotify' and
-                param.type.name != 'GLib.DestroyNotify'):
-                continue
-            if i < 2:
-                break
-            callback_param = callable.parameters[i-2]
-            if callback_param.closure_index != -1:
-                callback_param.scope = OPT_SCOPE_NOTIFIED
-                callback_param.transfer = PARAM_TRANSFER_NONE
-
-        self._parse_params(callable, callable.parameters, block)
-        self._parse_return(callable, callable.retval, block)
-        if block:
-            callable.doc = block.comment
-
-    def _parse_function(self, func):
-        block = self._blocks.get(func.symbol)
-        self._parse_callable(func, block)
-        self._parse_rename_to_func(func, block)
-
-    def _parse_signal(self, parent, signal):
-        block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
-        self._parse_node_common(signal, block)
-        # We're only attempting to name the signal parameters if
-        # the number of parameter tags (@foo) is the same or greater
-        # than the number of signal parameters
-        if block and len(block.tags) > len(signal.parameters):
-            names = block.tags.items()
-        else:
-            names = []
-        for i, param in enumerate(signal.parameters):
-            if names:
-                name, tag = names[i+1]
-                param.name = name
-                options = getattr(tag, 'options', {})
-                param_type = options.get(OPT_TYPE)
-                if param_type:
-                    param.type = self._resolve(param_type.one(), param.type)
-            else:
-                tag = None
-            self._parse_param(signal, param, tag)
-        self._parse_return(signal, signal.retval, block)
-        if block:
-            signal.doc = block.comment
-
-    def _parse_method(self, parent, meth):
-        block = self._blocks.get(meth.symbol)
-        self._parse_function(meth)
-        virtual = self._get_tag(block, TAG_VFUNC)
-        if virtual:
-            invoker_name = virtual.value
-            matched = False
-            for vfunc in parent.virtual_methods:
-                if vfunc.name == invoker_name:
-                    matched = True
-                    vfunc.invoker = meth
-                    break
-            if not matched:
-                print "warning: unmatched virtual invoker %r for method %r" % \
-                    (invoker_name, meth.symbol)
-
-    def _parse_vfunc(self, parent, vfunc):
-        key = '%s::%s' % (parent.type_name, vfunc.name)
-        self._parse_callable(vfunc, self._blocks.get(key))
-        if vfunc.invoker:
-            # We normally expect annotations like (element-type) to be
-            # applied to the invoker.
-            block = self._blocks.get(vfunc.invoker.symbol)
-            self._parse_callable(vfunc, block)
-
-    def _parse_field(self, parent, field, block=None):
-        if isinstance(field, Callback):
-            self._parse_callback(field)
-        else:
-            if not block:
-                return
-            tag = block.get(field.name)
-            if not tag:
-                return
-            t = tag.options.get('type')
-            if not t:
-                return
-            field.type.name = self._transformer.resolve_type_name(t.one())
-
-    def _check_arg_annotations(self, parent, params, block):
-        if block is None:
-            return
-        for tag in block.tags.keys():
-            if tag == TAG_RETURNS:
-                continue
-            for param in params:
-                if param.name == tag:
-                    break
-            else:
-                return
-                #print 'WARNING: annotation for "%s" refers to unknown ' \
-                #        'argument "%s"' % (parent.name, tag)
-
-    def _parse_params(self, parent, params, block):
-        self._check_arg_annotations(parent, params, block)
-        for param in params:
-            tag = self._get_tag(block, param.name)
-            self._parse_param(parent, param, tag)
-
-    def _parse_return(self, parent, return_, block):
-        tag = self._get_tag(block, TAG_RETURNS)
-        self._parse_param_ret_common(parent, return_, tag)
-
-    def _get_parameter_index(self, parent, param_name, location_name):
-        index = parent.get_parameter_index(param_name)
-        if index is None:
-            raise InvalidAnnotationError(
-                "can't find parameter %s referenced by parameter %s of %r"
-                % (param_name, location_name, parent.name))
-
-        return index
-
-    def _parse_param(self, parent, param, tag):
-        options = getattr(tag, 'options', {})
-        if isinstance(parent, Function):
-            scope = options.get(OPT_SCOPE)
-            if scope:
-                param.scope = scope.one()
-                if param.scope not in [PARAM_SCOPE_CALL,
-                                       PARAM_SCOPE_ASYNC,
-                                       PARAM_SCOPE_NOTIFIED]:
-                    raise InvalidAnnotationError(
-                        "scope for %s of %r is invalid (%r), must be one of "
-                        "call, async, notified."
-                        % (param.name, parent.name, param.scope))
-                param.transfer = PARAM_TRANSFER_NONE
-            elif (param.type.ctype == 'GAsyncReadyCallback' or
-                  param.type.name == 'Gio.AsyncReadyCallback'):
-                param.scope = OPT_SCOPE_ASYNC
-                param.transfer = PARAM_TRANSFER_NONE
-
-            destroy = options.get(OPT_DESTROY)
-            if destroy:
-                param.destroy_index = self._get_parameter_index(parent,
-                                                                destroy.one(),
-                                                                param.name)
-                self._fixup_param_destroy(parent, param)
-            closure = options.get(OPT_CLOSURE)
-            if closure:
-                param.closure_index = self._get_parameter_index(parent,
-                                                                closure.one(),
-                                                                param.name)
-                self._fixup_param_closure(parent, param)
-        if isinstance(parent, Callback):
-            if OPT_CLOSURE in options:
-                # For callbacks, (closure) appears without an
-                # argument, and tags a parameter that is a closure. We
-                # represent it (weirdly) in the gir and typelib by
-                # setting param.closure_index to its own index.
-                param.closure_index = parent.get_parameter_index(param.name)
-                self._fixup_param_closure(parent, param)
-
-        self._parse_param_ret_common(parent, param, tag)
-
-    def _fixup_param_destroy(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.destroy_index == param.destroy_index:
-                p.destroy_index = -1
-
-    def _fixup_param_closure(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.closure_index == param.closure_index:
-                p.closure_index = -1
-
-    def _parse_param_ret_common(self, parent, node, tag):
-        options = getattr(tag, 'options', {})
-        (node.direction, node.caller_allocates) = \
-            self._extract_direction(node, options)
-        container_type = self._extract_container_type(
-            parent, node, options)
-        if container_type is not None:
-            node.type = container_type
-        if node.direction is None:
-            node.direction = self._guess_direction(node)
-            node.caller_allocates = False
-        node.transfer = self._extract_transfer(parent, node, options)
-        param_type = options.get(OPT_TYPE)
-        if param_type:
-            node.type = self._resolve(param_type.one(), node.type)
-
-        if (OPT_ALLOW_NONE in options or
-            node.type.ctype == 'GCancellable*'):
-            node.allow_none = True
-
-        assert node.transfer is not None
-        if tag is not None and tag.comment is not None:
-            node.doc = tag.comment
-
-        for key in options:
-            if '.' in key:
-                value = options.get(key)
-                if value:
-                    node.attributes.append((key, value.one()))
-
-    def _extract_direction(self, node, options):
-        caller_allocates = False
-        if (OPT_INOUT in options or
-            OPT_INOUT_ALT in options):
-            direction = PARAM_DIRECTION_INOUT
-        elif OPT_OUT in options:
-            subtype = options[OPT_OUT]
-            if subtype is not None:
-                subtype = subtype.one()
-            direction = PARAM_DIRECTION_OUT
-            if subtype in (None, ''):
-                if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
-                    caller_allocates = '**' not in node.type.ctype
-                else:
-                    caller_allocates = False
-            elif subtype == 'caller-allocates':
-                caller_allocates = True
-            elif subtype == 'callee-allocates':
-                caller_allocates = False
-            else:
-                raise InvalidAnnotationError(
-                    "out direction for %s is invalid (%r)" % (node, subtype))
-        elif OPT_IN in options:
-            direction = PARAM_DIRECTION_IN
-        else:
-            direction = node.direction
-        return (direction, caller_allocates)
-
-    def _guess_array(self, node):
-        ctype = node.type.ctype
-        if ctype is None:
-            return False
-        if not ctype.endswith('*'):
-            return False
-        if node.type.canonical in default_array_types:
-            return True
-        return False
-
-    def _is_array_type(self, node):
-        if node.type.name in ['GLib.Array', 'GLib.PtrArray',
-                              'GLib.ByteArray']:
-            return True
-        if node.type.ctype in ['GArray*', 'GPtrArray*', 'GByteArray*']:
-            return True
-        return False
-
-    def _extract_container_type(self, parent, node, options):
-        has_element_type = OPT_ELEMENT_TYPE in options
-        has_array = OPT_ARRAY in options
-
-        if not has_array:
-            has_array = self._is_array_type(node)
-
-        # FIXME: This is a hack :-(
-        if (not isinstance(node, Field) and
-            (not has_element_type and
-             (node.direction is None
-              or isinstance(node, Return)
-              or node.direction == PARAM_DIRECTION_IN))):
-            if self._guess_array(node):
-                has_array = True
-
-        if has_array:
-            container_type = self._parse_array(parent, node, options)
-        elif has_element_type:
-            container_type = self._parse_element_type(parent, node, options)
-        else:
-            container_type = None
-
-        return container_type
-
-    def _parse_array(self, parent, node, options):
-        array_opt = options.get(OPT_ARRAY)
-        if array_opt:
-            array_values = array_opt.all()
-        else:
-            array_values = {}
-
-        is_g_array = self._is_array_type(node)
-
-        element_type = options.get(OPT_ELEMENT_TYPE)
-        if element_type is not None:
-            element_type_node = self._resolve(element_type.one())
-        else:
-            if is_g_array:
-                element_type_node = None
-            else:
-                element_type_node = Type(node.type.name) # erase ctype
-
-        if is_g_array:
-            type_name = node.type.name
-        else:
-            type_name = None
-
-        container_type = Array(type_name, node.type.ctype,
-                               element_type_node)
-        container_type.is_const = node.type.is_const
-        if OPT_ARRAY_ZERO_TERMINATED in array_values:
-            container_type.zeroterminated = array_values.get(
-                OPT_ARRAY_ZERO_TERMINATED) == '1'
-        length = array_values.get(OPT_ARRAY_LENGTH)
-        if length is not None:
-            param_index = self._get_parameter_index(parent, length, node.name)
-            container_type.length_param_index = param_index
-            # For in parameters we're incorrectly deferring
-            # char/unsigned char to utf8 when a length annotation
-            # is specified.
-            if (isinstance(node, Parameter) and
-                node.type.name == 'utf8' and
-                self._guess_direction(node) == PARAM_DIRECTION_IN and
-                element_type is None):
-                # FIXME: unsigned char/guchar should be uint8
-                container_type.element_type = Type('gint8')
-        container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
-        return container_type
-
-    def _resolve(self, type_str, orig_node=None):
-        def grab_one(type_str, resolver, top_combiner, combiner):
-            """Return a complete type, and the trailing string part after it.
-            Use resolver() on each identifier, and combiner() on the parts of
-            each complete type. (top_combiner is used on the top-most type.)"""
-            bits = re.split(r'([,<>])', type_str, 1)
-            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
-            args = [resolver(first)]
-            if sep == '<':
-                while sep != '>':
-                    next, rest = grab_one(rest, resolver, combiner, combiner)
-                    args.append(next)
-                    sep, rest = rest[0], rest[1:]
-            else:
-                rest = sep + rest
-            return top_combiner(*args), rest
-        def resolver(ident):
-            return self._transformer.resolve_param_type(Type(ident))
-        def combiner(base, *rest):
-            if not rest:
-                return base
-            if (base.name in ['GLib.List', 'GLib.SList'] or
-                base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
-                return List(base.name, base.ctype, *rest)
-            if (base.name in ['GLib.HashTable'] or
-                base.ctype in ['GHashTable*']) and len(rest)==2:
-                return Map(base.name, base.ctype, *rest)
-            print "WARNING: throwing away type parameters:", type_str
-            return base
-        def top_combiner(base, *rest):
-            """For the top most type, recycle orig_node if possible."""
-            if orig_node is not None:
-                orig_node.name = base.name
-                base = orig_node # preserve other properties of orig_node
-            return combiner(base, *rest)
-
-        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
-        if rest:
-            print "WARNING: throwing away trailing part of type:", type_str
-        return result
-
-    def _parse_element_type(self, parent, node, options):
-        element_type_opt = options.get(OPT_ELEMENT_TYPE)
-        element_type = element_type_opt.flat()
-        if (node.type.name in ['GLib.List', 'GLib.SList'] or
-            node.type.ctype in ['GList*', 'GSList*']):
-            assert len(element_type) == 1
-            container_type = List(
-                node.type.name,
-                node.type.ctype,
-                self._resolve(element_type[0]))
-        elif (node.type.name in ['GLib.HashTable'] or
-              node.type.ctype in ['GHashTable*']):
-            assert len(element_type) == 2
-            container_type = Map(
-                node.type.name,
-                node.type.ctype,
-                self._resolve(element_type[0]),
-                self._resolve(element_type[1]))
-        elif self._is_array_type(node):
-            container_type = Array(node.type.name,
-                                   node.type.ctype,
-                                   self._resolve(element_type[0]))
-        else:
-            print 'FIXME: unhandled element-type container:', node
-        return container_type
-
-    def _extract_transfer(self, parent, node, options):
-        transfer_opt = options.get(OPT_TRANSFER)
-        if transfer_opt is None:
-            transfer = self._guess_transfer(node, options)
-        else:
-            transfer = transfer_opt.one()
-            if transfer is None:
-                transfer = PARAM_TRANSFER_FULL
-            if transfer not in [PARAM_TRANSFER_NONE,
-                                PARAM_TRANSFER_CONTAINER,
-                                PARAM_TRANSFER_FULL]:
-                raise InvalidAnnotationError(
-                    "transfer for %s of %r is invalid (%r), must be one of "
-                    "none, container, full." % (node, parent.name, transfer))
-        return transfer
-
-    def _parse_node_common(self, node, block):
-        self._parse_version(node, block)
-        self._parse_deprecated(node, block)
-        self._parse_attributes(node, block)
-        self._parse_skip(node, block)
-        self._parse_foreign(node, block)
-
-    def _parse_version(self, node, block):
-        since_tag = self._get_tag(block, TAG_SINCE)
-        if since_tag is None:
-            return
-        node.version = since_tag.value
-
-    def _parse_deprecated(self, node, block):
-        deprecated_tag = self._get_tag(block, TAG_DEPRECATED)
-        if deprecated_tag is None:
-            return
-        value = deprecated_tag.value
-        if ': ' in value:
-            version, desc = value.split(': ')
-        else:
-            desc = value
-            version = None
-        node.deprecated = desc
-        if version is not None:
-            node.deprecated_version = version
-
-    def _parse_attributes(self, node, block):
-        annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
-        if annos_tag is None:
-            return
-        options = AnnotationParser.parse_options(annos_tag.value)
-        for key, value in options.iteritems():
-            if value:
-                node.attributes.append((key, value.one()))
-
-    def _parse_skip(self, node, block):
-        if block is not None:
-            if OPT_SKIP in block.options:
-                node.skip = True
-
-    def _parse_foreign(self, node, block):
-        if block is not None:
-            if OPT_FOREIGN in block.options:
-                node.foreign = True
-
-    def _parse_type_instance_tags(self, node, block):
-        tag = self._get_tag(block, TAG_UNREF_FUNC)
-        node.unref_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_REF_FUNC)
-        node.ref_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_SET_VALUE_FUNC)
-        node.set_value_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_GET_VALUE_FUNC)
-        node.get_value_func = tag.value if tag else None
-
-    def _parse_rename_to_func(self, node, block):
-        rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
-        if rename_to_tag is None:
-            return
-        new_name = rename_to_tag.value
-
-        shadowed = []
-
-        def shadowed_filter(n):
-            if isinstance(n, Function) and n.symbol == new_name:
-                shadowed.append(n)
-                return False
-            return True
-
-        self._namespace.remove_matching(shadowed_filter)
-        if len(shadowed) == 1:
-            # method override; use the same (stripped) name as the overloaded
-            # method referenced.
-            # Note that 'g_timeout_add_full' may specify a new_name of
-            # 'g_timeout_add' but the *real* name desired is the stripped name
-            # of 'g_timeout_add' which is 'timeout_add' (for example).
-            node.name = shadowed[0].name
-        elif len(shadowed) == 0:
-            # literal rename, to force a particular prefix strip or whatever
-            # Example: the "nm-utils" package uses a "NM" prefix in most places
-            # but some functions have an "nm_utils_" prefix; the 'Rename To:'
-            # annotation in this case is used to strip the 'utils_' part off.
-            node.name = new_name
-        else:
-            assert False # more than two shadowed methods?  Shouldn't happen.
-
-    def _guess_direction(self, node):
-        if node.direction:
-            return node.direction
-        is_pointer = False
-        if node.type.ctype:
-            is_pointer = '*' in node.type.ctype
-
-        if is_pointer and node.type.name in BASIC_GIR_TYPES:
-            return PARAM_DIRECTION_OUT
-
-        return PARAM_DIRECTION_IN
-
-    def _guess_transfer(self, node, options):
-        if node.transfer is not None:
-            return node.transfer
-
-        # Anything with 'const' gets none
-        if node.type.is_const:
-            return PARAM_TRANSFER_NONE
-        elif node.type.name in [TYPE_NONE, TYPE_ANY]:
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node.type, Varargs):
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node, Parameter):
-            if node.direction in [PARAM_DIRECTION_INOUT,
-                                  PARAM_DIRECTION_OUT]:
-                if node.caller_allocates:
-                    return PARAM_TRANSFER_NONE
-                return PARAM_TRANSFER_FULL
-            # This one is a hack for compatibility; the transfer
-            # for string parameters really has no defined meaning.
-            elif node.type.canonical == 'utf8':
-                return PARAM_TRANSFER_FULL
-            else:
-                return PARAM_TRANSFER_NONE
-        elif isinstance(node, Return):
-            if (isinstance(node.type, Array) and
-                    node.type.element_type is not None and
-                    node.type.element_type.name == 'utf8'):
-                return PARAM_TRANSFER_FULL
-            elif (node.type.canonical in BASIC_GIR_TYPES or
-                (node.type.canonical in [TYPE_NONE, TYPE_ANY] and
-                 node.type.is_const)):
-                return PARAM_TRANSFER_NONE
-            else:
-                return PARAM_TRANSFER_FULL
-        elif isinstance(node, Field):
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node, Property):
-            return PARAM_TRANSFER_NONE
-        else:
-            raise AssertionError(node)
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 8c7ea09..501b439 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,63 +19,165 @@
 # Boston, MA 02111-1307, USA.
 #
 
-"""AST nodes
-This file descbribes abstract data type nodes independent on the
-implementation language.
-
-These can later on be extended (eg subclassed) with additional information
-which is language/library/domain specific.
+from .odict import odict
+from .utils import to_underscores
+
+class Type(object):
+    """A Type can be either:
+* A reference to a node (target_giname)
+* A reference to a "fundamental" type like 'utf8'
+* A "foreign" type - this can be any string."
+If none are specified, then it's in an "unresolved" state.
+In this case, the ctype must be specified.
 """
 
+    resolved = property(lambda self: (self.target_fundamental or
+                                      self.target_giname or
+                                      self.target_foreign))
+
+    def __init__(self,
+                 ctype=None,
+                 target_fundamental=None,
+                 target_giname=None,
+                 target_foreign=None,
+                 _target_unknown=False,
+                 is_const=False,
+                 origin_symbol=None):
+        self.ctype = ctype
+        self.origin_symbol = origin_symbol
+        if _target_unknown:
+            assert isinstance(self, TypeUnknown)
+        elif target_fundamental:
+            assert target_giname is None
+            assert target_foreign is None
+        elif target_giname:
+            assert '.' in target_giname
+            assert target_fundamental is None
+            assert target_foreign is None
+        elif target_foreign:
+            assert ctype is not None
+            assert target_giname is None
+            assert target_fundamental is None
+        else:
+            assert ctype is not None
+        self.target_fundamental = target_fundamental
+        self.target_giname = target_giname
+        self.target_foreign = target_foreign
+        self.is_const = is_const
+
+    def get_giname(self):
+        assert self.target_giname is not None
+        return self.target_giname.split('.')[1]
+
+    def __cmp__(self, other):
+        if self.target_fundamental:
+            return cmp(self.target_fundamental, other.target_fundamental)
+        if self.target_giname:
+            return cmp(self.target_giname, other.target_giname)
+        if self.target_foreign:
+            return cmp(self.target_foreign, other.target_foreign)
+        return cmp(self.ctype, other.ctype)
+
+    def is_equiv(self, typeval):
+        """Return True if the specified types are compatible at
+        an introspection level, disregarding their C types.
+        A sequence may be given for typeval, in which case
+        this function returns True if the type is compatible with
+        any."""
+        if isinstance(typeval, (list, tuple)):
+            for val in typeval:
+                if self.is_equiv(val):
+                    return True
+            return False
+        return self == typeval
+
+    def clone(self):
+        return Type(target_fundamental=self.target_fundamental,
+                    target_giname=self.target_giname,
+                    target_foreign=self.target_foreign,
+                    ctype=self.ctype,
+                    is_const=self.is_const)
+
+    def __str__(self):
+        if self.target_fundamental:
+            return self.target_fundamental
+        elif self.target_giname:
+            return self.target_giname
+        elif self.target_foreign:
+            return self.target_foreign
+
+    def __repr__(self):
+        if self.target_fundamental:
+            data = 'target_fundamental=%s, ' % (self.target_fundamental, )
+        elif self.target_giname:
+            data = 'target_giname=%s, ' % (self.target_giname, )
+        elif self.target_foreign:
+            data = 'target_foreign=%s, ' % (self.target_foreign, )
+        else:
+            data = ''
+        return '%s(%sctype=%s)' % (self.__class__.__name__, data, self.ctype)
+
+class TypeUnknown(Type):
+    def __init__(self):
+        Type.__init__(self, _target_unknown=True)
+
 ######
 ## Fundamental types
 ######
 # Two special ones
-TYPE_NONE = 'none'
-TYPE_ANY = 'gpointer'
+TYPE_NONE = Type(target_fundamental='none', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
 # "Basic" types
-TYPE_BOOLEAN = 'gboolean'
-TYPE_INT8 = 'gint8'
-TYPE_UINT8 = 'guint8'
-TYPE_INT16 = 'gint16'
-TYPE_UINT16 = 'guint16'
-TYPE_INT32 = 'gint32'
-TYPE_UINT32 = 'guint32'
-TYPE_INT64 = 'gint64'
-TYPE_UINT64 = 'guint64'
-TYPE_CHAR = 'gchar'
-TYPE_SHORT = 'gshort'
-TYPE_USHORT = 'gushort'
-TYPE_INT = 'gint'
-TYPE_UINT = 'guint'
-TYPE_LONG = 'glong'
-TYPE_ULONG = 'gulong'
+TYPE_BOOLEAN = Type(target_fundamental='gboolean', ctype='gboolean')
+TYPE_INT8 = Type(target_fundamental='gint8', ctype='gint8')
+TYPE_UINT8 = Type(target_fundamental='guint8', ctype='guint8')
+TYPE_INT16 = Type(target_fundamental='gint16', ctype='gint16')
+TYPE_UINT16 = Type(target_fundamental='guint16', ctype='guint16')
+TYPE_INT32 = Type(target_fundamental='gint32', ctype='gint32')
+TYPE_UINT32 = Type(target_fundamental='guint32', ctype='guint32')
+TYPE_INT64 = Type(target_fundamental='gint64', ctype='gint64')
+TYPE_UINT64 = Type(target_fundamental='guint64', ctype='guint64')
+TYPE_CHAR = Type(target_fundamental='gchar', ctype='gchar')
+TYPE_SHORT = Type(target_fundamental='gshort', ctype='gshort')
+TYPE_USHORT = Type(target_fundamental='gushort', ctype='gushort')
+TYPE_INT = Type(target_fundamental='gint', ctype='gint')
+TYPE_UINT = Type(target_fundamental='guint', ctype='guint')
+TYPE_LONG = Type(target_fundamental='glong', ctype='glong')
+TYPE_ULONG = Type(target_fundamental='gulong', ctype='gulong')
 # C99 types
-TYPE_LONG_LONG = 'long long'
-TYPE_LONG_ULONG = 'unsigned long long'
-TYPE_FLOAT = 'gfloat'
-TYPE_DOUBLE = 'gdouble'
+TYPE_LONG_LONG = Type(target_fundamental='long long', ctype='long long')
+TYPE_LONG_ULONG = Type(target_fundamental='unsigned long long',
+                       ctype='unsigned long long')
+TYPE_FLOAT = Type(target_fundamental='gfloat', ctype='gfloat')
+TYPE_DOUBLE = Type(target_fundamental='gdouble', ctype='gdouble')
 # ?
-TYPE_LONG_DOUBLE = 'long double'
+TYPE_LONG_DOUBLE = Type(target_fundamental='long double',
+                        ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
 
 # C types with semantics overlaid
-TYPE_GTYPE = 'GType'
-TYPE_STRING = 'utf8'
-TYPE_FILENAME = 'filename'
+TYPE_GTYPE = Type(target_fundamental='GType', ctype='GType')
+TYPE_STRING = Type(target_fundamental='utf8', ctype='gchar*')
+TYPE_FILENAME = Type(target_fundamental='filename', ctype='gchar*')
+
+TYPE_VALIST = Type(target_fundamental='va_list', ctype='va_list')
 
 BASIC_GIR_TYPES = [TYPE_BOOLEAN, TYPE_INT8, TYPE_UINT8, TYPE_INT16,
                    TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64,
                    TYPE_UINT64, TYPE_CHAR, TYPE_SHORT, TYPE_USHORT, TYPE_INT,
                    TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_LONG_LONG,
                    TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
-                   TYPE_LONG_DOUBLE, TYPE_GTYPE]
+                   TYPE_LONG_DOUBLE, TYPE_UNICHAR, TYPE_GTYPE]
 GIR_TYPES = [TYPE_NONE, TYPE_ANY]
 GIR_TYPES.extend(BASIC_GIR_TYPES)
-GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
+GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME, TYPE_VALIST])
 
 type_names = {}
 for typeval in GIR_TYPES:
-    type_names[typeval] = typeval
+    type_names[typeval.target_fundamental] = typeval
+basic_type_names = {}
+for typeval in BASIC_GIR_TYPES:
+    basic_type_names[typeval.target_fundamental] = typeval
 
 # C builtin
 type_names['char'] = TYPE_CHAR
@@ -106,13 +208,12 @@ type_names['signed long long'] = TYPE_LONG_LONG
 type_names['guchar'] = TYPE_UINT8
 type_names['gchararray'] = TYPE_STRING
 type_names['gchar*'] = TYPE_STRING
+type_names['goffset'] = TYPE_INT64
+type_names['gunichar2'] = TYPE_UINT16
 type_names['gsize'] = TYPE_ULONG
 type_names['gssize'] = TYPE_LONG
 type_names['gconstpointer'] = TYPE_ANY
 
-# Some special C types that aren't scriptable, and we just squash
-type_names['va_list'] = TYPE_ANY
-
 # C stdio, used in GLib public headers; squash this for now here
 # until we move scanning into GLib and can (skip)
 type_names['FILE*'] = TYPE_ANY
@@ -142,19 +243,6 @@ type_names['ssize_t'] = TYPE_LONG
 # Obj-C
 type_names['id'] = TYPE_ANY
 
-# These types, when seen by reference, are converted into an Array()
-# by default
-default_array_types = {}
-default_array_types['guint8*'] = TYPE_UINT8
-default_array_types['guchar*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
-default_array_types['char**'] = TYPE_STRING
-default_array_types['gchar**'] = TYPE_STRING
-
-# These types, when seen by reference, are interpreted as out parameters
-default_out_types = (TYPE_SHORT, TYPE_USHORT, TYPE_INT, TYPE_UINT,
-                     TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE)
-
 ##
 ## Parameters
 ##
@@ -171,24 +259,185 @@ PARAM_TRANSFER_NONE = 'none'
 PARAM_TRANSFER_CONTAINER = 'container'
 PARAM_TRANSFER_FULL = 'full'
 
-def type_name_from_ctype(ctype):
-    return type_names.get(ctype, ctype)
-
+class Namespace(object):
+    names = property(lambda self: self._names)
+    aliases = property(lambda self: self._aliases)
+    type_names = property(lambda self: self._type_names)
+    ctypes = property(lambda self: self._ctypes)
 
-class Node(object):
+    def __init__(self, name, version,
+                 identifier_prefixes=None,
+                 symbol_prefixes=None):
+        self.name = name
+        self.version = version
+        if identifier_prefixes:
+            self.identifier_prefixes = identifier_prefixes
+        else:
+            self.identifier_prefixes = [name]
+        if symbol_prefixes:
+            self.symbol_prefixes = symbol_prefixes
+        else:
+            ps = self.identifier_prefixes
+            self.symbol_prefixes = [to_underscores(p).lower() for p in ps]
+        self._names = odict() # Maps from GIName -> node
+        self._aliases = {} # Maps from GIName -> GIName
+        self._type_names = {} # Maps from GTName -> node
+        self._ctypes = {} # Maps from CType -> node
+        self._symbols = {} # Maps from function symbols -> Function
+
+    def type_from_name(self, name, ctype=None):
+        """Backwards compatibility method for older .gir files, which
+only use the 'name' attribute.  If name refers to a fundamental type,
+create a Type object referncing it.  If name is already a
+fully-qualified GIName like 'Foo.Bar', returns a Type targeting it .
+Otherwise a Type targeting name qualififed with the namespace name is
+returned."""
+        if name in type_names:
+            return Type(target_fundamental=name, ctype=ctype)
+        if '.' in name:
+            target = name
+        else:
+            target = '%s.%s' % (self.name, name)
+        return Type(target_giname=target, ctype=ctype)
+
+    def contains_ident(self, ident):
+        """Return True if this namespace should contain the given C
+identifier string."""
+        if not self.identifier_prefixes:
+            return False
+        for prefix in self.identifier_prefixes:
+            if ident.startswith(prefix):
+                return True
+        return False
+
+    def append(self, node, replace=False):
+        previous = self._names.get(node.name)
+        if previous is not None:
+            if not replace:
+                raise ValueError("Namespace conflict")
+            self.remove(previous)
+        # A layering violation...but oh well.
+        from .glibast import GLibBoxed
+        if isinstance(node, Alias):
+            self._aliases[node.name] = node
+        elif isinstance(node, (GLibBoxed, Interface, Class)):
+            self._type_names[node.type_name] = node
+        elif isinstance(node, Function):
+            self._symbols[node.symbol] = node
+        assert isinstance(node, Node)
+        assert node.namespace is None
+        node.namespace = self
+        self._names[node.name] = node
+        if hasattr(node, 'ctype'):
+            self._ctypes[node.ctype] = node
+        if hasattr(node, 'symbol'):
+            self._ctypes[node.symbol] = node
+
+    def remove(self, node):
+        from .glibast import GLibBoxed
+        if isinstance(node, Alias):
+            del self._aliases[node.name]
+        elif isinstance(node, (GLibBoxed, Interface, Class)):
+            del self._type_names[node.type_name]
+        del self._names[node.name]
+        node.namespace = None
+        if hasattr(node, 'ctype'):
+            del self._ctypes[node.ctype]
+        if isinstance(node, Function):
+            del self._symbols[node.symbol]
+
+    def float(self, node):
+        """Like remove(), but doesn't unset the node's namespace
+back-reference, and it's still possible to look up
+functions via get_by_symbol()."""
+        if isinstance(node, Function):
+            symbol = node.symbol
+        self.remove(node)
+        self._symbols[symbol] = node
+        node.namespace = self
+
+    def __iter__(self):
+        return iter(self._names)
+
+    def iteritems(self):
+        return self._names.iteritems()
+
+    def itervalues(self):
+        return self._names.itervalues()
+
+    def get(self, name):
+        return self._names.get(name)
+
+    def get_by_ctype(self, ctype):
+        return self._ctypes.get(ctype)
+
+    def get_by_symbol(self, symbol):
+        return self._symbols.get(symbol)
+
+    def walk(self, callback):
+        for node in self.itervalues():
+            node.walk(callback, [])
+
+class Include(object):
 
-    def __init__(self, name=None):
+    def __init__(self, name, version):
         self.name = name
+        self.version = version
+
+    @classmethod
+    def from_string(self, string):
+        return Include(*string.split('-', 1))
+
+    def __cmp__(self, other):
+        if not isinstance(other, Include):
+            return cmp(self, other)
+        namecmp = cmp(self.name, other.name)
+        if namecmp != 0:
+            return namecmp
+        return cmp(self.version, other.version)
+
+    def __hash__(self):
+        return hash((self.name, self.version))
+
+    def __str__(self):
+        return '%s-%s' % (self.name, self.version)
+
+class Annotated(object):
+    """An object which has a few generic metadata
+properties."""
+    def __init__(self):
+        self.version = None
         self.skip = False
         self.introspectable = True
         self.attributes = [] # (key, value)*
         self.deprecated = None
         self.deprecated_version = None
-        self.version = None
+        self.doc = None
+
+class Node(Annotated):
+    """A node is a type of object which is uniquely identified by its
+(namespace, name) pair.  When combined with a ., this is called a
+GIName.  It's possible for nodes to contain or point to other nodes."""
+
+    c_name = property(lambda self: self.namespace.name + self.name)
+    gi_name = property(lambda self: '%s.%s' % (self.namespace.name, self.name))
+
+    def __init__(self, name=None):
+        Annotated.__init__(self)
+        self.namespace = None # Should be set later by Namespace.append()
+        self.name = name
         self.foreign = False
         self.file_positions = set()
 
+    def create_type(self):
+        """Create a Type object referencing this node."""
+        assert self.namespace is not None
+        return Type(target_giname=('%s.%s' % (self.namespace.name, self.name)))
+
     def __cmp__(self, other):
+        nscmp = cmp(self.namespace, other.namespace)
+        if nscmp != 0:
+            return nscmp
         return cmp(self.name, other.name)
 
     def __repr__(self):
@@ -207,49 +456,17 @@ class Node(object):
         if symbol.source_filename:
             self.add_file_position(symbol.source_filename, symbol.line, -1)
 
-class Namespace(Node):
-
-    def __init__(self, name, version):
-        Node.__init__(self, name)
-        self.version = version
-        self.nodes = []
-
-    def __repr__(self):
-        return '%s(%r, %r, %r)' % (self.__class__.__name__, self.name,
-                                   self.version, self.nodes)
-
-    def remove_matching(self, pred):
+    def walk(self, callback, chain):
+        res = callback(self, chain)
+        assert res in (True, False), "Walk function must return boolean, not %r" % (res, )
+        if not res:
+            return False
+        chain.append(self)
+        self._walk(callback, chain)
+        chain.pop()
 
-        def recursive_pred(node):
-            node.remove_matching_children(pred)
-            return pred(node)
-
-        self.nodes = filter(recursive_pred, self.nodes)
-
-class Include(Node):
-
-    def __init__(self, name, version):
-        Node.__init__(self, 'include')
-        self.name = name
-        self.version = version
-
-    @classmethod
-    def from_string(self, string):
-        return Include(*string.split('-', 1))
-
-    def __cmp__(self, other):
-        if not isinstance(other, Include):
-            return cmp(self, other)
-        namecmp = cmp(self.name, other.name)
-        if namecmp != 0:
-            return namecmp
-        return cmp(self.version, other.version)
-
-    def __hash__(self):
-        return hash((self.name, self.version))
-
-    def __str__(self):
-        return '%s-%s' % (self.name, self.version)
+    def _walk(self, callback, chain):
+        pass
 
 class Callable(Node):
 
@@ -258,30 +475,29 @@ class Callable(Node):
         self.retval = retval
         self.parameters = parameters
         self.throws = not not throws
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r, %r)' % (self.__class__.__name__,
-                                   self.name, self.retval,
-                                   self.parameters)
-
-class Function(Callable):
-
-    def __init__(self, name, retval, parameters, symbol, throws=None):
-        Callable.__init__(self, name, retval, parameters, throws)
-        self.symbol = symbol
-        self.is_method = False
-        self.doc = None
 
     def get_parameter_index(self, name):
         for i, parameter in enumerate(self.parameters):
-            if parameter.name == name:
-                return i + int(self.is_method)
+            if parameter.argname == name:
+                return i
+        raise ValueError("Unknown argument %s" % (name, ))
 
     def get_parameter(self, name):
         for parameter in self.parameters:
-            if parameter.name == name:
+            if parameter.argname == name:
                 return parameter
+        raise ValueError("Unknown argument %s" % (name, ))
+
+
+class Function(Callable):
+
+    def __init__(self, name, retval, parameters, throws, symbol):
+        Callable.__init__(self, name, retval, parameters, throws)
+        self.symbol = symbol
+        self.is_method = False
+        self.is_constructor = False
+        self.shadowed_by = None # C symbol string
+        self.shadows = None # C symbol string
 
 
 class VFunction(Callable):
@@ -297,59 +513,72 @@ class VFunction(Callable):
         return obj
 
 
-class Type(Node):
-
-    def __init__(self, name, ctype=None):
-        Node.__init__(self, name)
-        self.ctype = ctype
-        self.resolved = False
-        self.is_const = False
-        self.canonical = None
-        self.derefed_canonical = None
-
 
 class Varargs(Type):
 
     def __init__(self):
-        Type.__init__(self, '<varargs>')
+        Type.__init__(self, '<varargs>', target_fundamental='<varargs>')
 
 
 class Array(Type):
-
-    def __init__(self, name, ctype, element_type):
-        if name is None:
-            name = '<carray>'
-        Type.__init__(self, name, ctype)
+    C = '<c>'
+    GLIB_ARRAY = 'GLib.Array'
+    GLIB_BYTEARRAY = 'GLib.ByteArray'
+    GLIB_PTRARRAY = 'GLib.PtrArray'
+
+    def __init__(self, array_type, element_type, **kwargs):
+        Type.__init__(self, target_fundamental='<array>',
+                      **kwargs)
+        if array_type in (None, self.C):
+            self.array_type = self.C
+        else:
+            assert array_type in (self.GLIB_ARRAY,
+                                  self.GLIB_BYTEARRAY,
+                                  self.GLIB_PTRARRAY)
+            self.array_type = array_type
+        assert isinstance(element_type, Type)
         self.element_type = element_type
         self.zeroterminated = True
-        self.length_param_index = -1
         self.length_param_name = None
         self.size = None
 
-    def __repr__(self):
-        return 'Array(%r, %r)' % (self.name, self.element_type, )
-
+    def clone(self):
+        arr = Array(self.array_type, self.element_type)
+        arr.element_type = self.element_type
+        arr.zeroterminated = self.zeroterminated
+        arr.length_param_name = self.length_param_name
+        arr.size = self.size
+        return arr
 
 class List(Type):
 
-    def __init__(self, name, ctype, element_type):
-        Type.__init__(self, name, ctype)
+    def __init__(self, name, element_type, **kwargs):
+        Type.__init__(self, target_fundamental='<list>',
+                      **kwargs)
+        self.name = name
+        assert isinstance(element_type, Type)
         self.element_type = element_type
 
-    def __repr__(self):
-        return 'List(%r of %r)' % (self.name, self.element_type, )
-
+    def clone(self):
+        l = List(self.name, self.element_type)
+        l.element_type = self.element_type
+        l.zeroterminated = self.zeroterminated
+        l.length_param_name = self.length_param_name
+        l.size = self.size
+        return l
 
 class Map(Type):
 
-    def __init__(self, name, ctype, key_type, value_type):
-        Type.__init__(self, name, ctype)
+    def __init__(self, key_type, value_type, **kwargs):
+        Type.__init__(self, target_fundamental='<map>', **kwargs)
+        assert isinstance(key_type, Type)
         self.key_type = key_type
+        assert isinstance(value_type, Type)
         self.value_type = value_type
 
-    def __repr__(self):
-        return 'Map(%r <%r,%r>)' % (self.name, self.key_type, self.value_type)
-
+    def clone(self):
+        m = Map(self.key_type, self.value_type)
+        return m
 
 class Alias(Node):
 
@@ -358,42 +587,43 @@ class Alias(Node):
         self.target = target
         self.ctype = ctype
 
-    def __repr__(self):
-        return 'Alias(%r, %r)' % (self.name, self.target)
-
 
-class TypeContainer(Node):
+class TypeContainer(Annotated):
+    """A fundamental base class for Return and Parameter."""
 
-    def __init__(self, name, typenode, transfer):
-        Node.__init__(self, name)
+    def __init__(self, typenode, transfer):
+        Annotated.__init__(self)
         self.type = typenode
-        if transfer in [PARAM_TRANSFER_NONE, PARAM_TRANSFER_CONTAINER,
-                        PARAM_TRANSFER_FULL]:
+        if transfer is not None:
             self.transfer = transfer
+        elif typenode.is_const:
+            self.transfer = PARAM_TRANSFER_NONE
         else:
             self.transfer = None
 
 
 class Parameter(TypeContainer):
-
-    def __init__(self, name, typenode, direction=None,
-                 transfer=None, allow_none=False, scope=None):
-        TypeContainer.__init__(self, name, typenode, transfer)
-        if direction in [PARAM_DIRECTION_IN, PARAM_DIRECTION_OUT,
-                         PARAM_DIRECTION_INOUT, None]:
-            self.direction = direction
-        else:
-            self.direction = PARAM_DIRECTION_IN
-
-        self.caller_allocates = False
+    """An argument to a function."""
+
+    def __init__(self, argname, typenode, direction=None,
+                 transfer=None, allow_none=False, scope=None,
+                 caller_allocates=False):
+        TypeContainer.__init__(self, typenode, transfer)
+        self.argname = argname
+        self.direction = direction
         self.allow_none = allow_none
         self.scope = scope
-        self.closure_index = -1
-        self.destroy_index = -1
-        self.doc = None
+        self.caller_allocates = caller_allocates
+        self.closure_name = None
+        self.destroy_name = None
 
-    def __repr__(self):
-        return 'Parameter(%r, %r)' % (self.name, self.type)
+
+class Return(TypeContainer):
+    """A return value from a function."""
+
+    def __init__(self, rtype, transfer=None):
+        TypeContainer.__init__(self, rtype, transfer)
+        self.direction = PARAM_DIRECTION_OUT
 
 
 class Enum(Node):
@@ -402,10 +632,6 @@ class Enum(Node):
         Node.__init__(self, name)
         self.symbol = symbol
         self.members = members
-        self.doc = None
-
-    def __repr__(self):
-        return 'Enum(%r, %r)' % (self.name, self.members)
 
 
 class Bitfield(Node):
@@ -414,21 +640,18 @@ class Bitfield(Node):
         Node.__init__(self, name)
         self.symbol = symbol
         self.members = members
-        self.doc = None
-
-    def __repr__(self):
-        return 'Bitfield(%r, %r)' % (self.name, self.members)
 
 
-class Member(Node):
+class Member(Annotated):
 
     def __init__(self, name, value, symbol):
-        Node.__init__(self, name)
+        Annotated.__init__(self)
+        self.name = name
         self.value = value
         self.symbol = symbol
 
-    def __repr__(self):
-        return 'Member(%r, %r)' % (self.name, self.value)
+    def __cmp__(self, other):
+        return cmp(self.name, other.name)
 
 
 class Record(Node):
@@ -439,44 +662,41 @@ class Record(Node):
         self.constructors = []
         self.symbol = symbol
         self.disguised = disguised
-        self.doc = None
         self.methods = []
+        self.static_methods = []
+
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for func in self.methods:
+            func.walk(callback, chain)
+        for func in self.static_methods:
+            func.walk(callback, chain)
+        for field in self.fields:
+            if field.anonymous_node is not None:
+                field.anonymous_node.walk(callback, chain)
 
     def remove_matching_children(self, pred):
         self.fields = filter(pred, self.fields)
         self.constructors = filter(pred, self.constructors)
         self.methods = filter(pred, self.methods)
 
-# BW compat, remove
-Struct = Record
-
 
-class Field(Node):
+class Field(Annotated):
 
-    def __init__(self, name, typenode, symbol, readable, writable, bits=None):
-        Node.__init__(self, name)
+    def __init__(self, name, typenode, readable, writable, bits=None,
+                 anonymous_node=None):
+        Annotated.__init__(self)
+        assert (typenode or anonymous_node)
+        self.name = name
         self.type = typenode
-        self.symbol = symbol
         self.readable = readable
         self.writable = writable
         self.bits = bits
+        self.anonymous_node = anonymous_node
 
-    def __repr__(self):
-        if self.bits:
-            return 'Field(%r, %r, %r)' % (self.name, self.type, self.bits)
-        else:
-            return 'Field(%r, %r)' % (self.name, self.type)
-
-
-class Return(TypeContainer):
-
-    def __init__(self, rtype, transfer=None):
-        TypeContainer.__init__(self, None, rtype, transfer)
-        self.direction = PARAM_DIRECTION_OUT
-        self.doc = None
-
-    def __repr__(self):
-        return 'Return(%r)' % (self.type, )
+    def __cmp__(self, other):
+        return cmp(self.name, other.name)
 
 
 class Class(Node):
@@ -484,7 +704,12 @@ class Class(Node):
     def __init__(self, name, parent, is_abstract):
         Node.__init__(self, name)
         self.ctype = name
+        self.c_symbol_prefix = None
         self.parent = parent
+        # When we're in the scanner, we keep around a list
+        # of parents so that we can transparently fall back
+        # if there are 'hidden' parents
+        self.parent_chain = []
         self.glib_type_struct = None
         self.is_abstract = is_abstract
         self.methods = []
@@ -494,7 +719,6 @@ class Class(Node):
         self.constructors = []
         self.properties = []
         self.fields = []
-        self.doc = None
 
     def remove_matching_children(self, pred):
         self.methods = filter(pred, self.methods)
@@ -502,87 +726,71 @@ class Class(Node):
         self.properties = filter(pred, self.properties)
         self.fields = filter(pred, self.fields)
 
-    def __repr__(self):
-        return '%s(%r, %r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.parent, self.methods)
-
+    def _walk(self, callback, chain):
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.virtual_methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for field in self.fields:
+            if field.anonymous_node:
+                field.anonymous_node.walk(callback, chain)
 
 class Interface(Node):
 
     def __init__(self, name, parent):
         Node.__init__(self, name)
+        self.c_symbol_prefix = None
         self.parent = parent
+        self.parent_chain = []
         self.methods = []
+        self.static_methods = []
         self.virtual_methods = []
         self.glib_type_struct = None
         self.properties = []
         self.fields = []
         self.prerequisites = []
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.methods)
 
+    def _walk(self, callback, chain):
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+        for meth in self.virtual_methods:
+            meth.walk(callback, chain)
+        for field in self.fields:
+            if field.anonymous_node:
+                field.anonymous_node.walk(callback, chain)
 
 class Constant(Node):
 
-    def __init__(self, name, type_name, value):
+    def __init__(self, name, value_type, value):
         Node.__init__(self, name)
-        self.type = Type(type_name)
+        self.value_type = value_type
         self.value = value
 
-    def __repr__(self):
-        return 'Constant(%r, %r, %r)' % (
-            self.name, self.type, self.value)
-
 
-class Property(TypeContainer):
+class Property(Node):
 
-    def __init__(self, name, type_name, readable, writable,
-                 construct, construct_only, ctype=None, transfer=None):
-        self.type = Type(type_name, ctype)
-        TypeContainer.__init__(self, name, self.type, transfer)
+    def __init__(self, name, typeobj, readable, writable,
+                 construct, construct_only, transfer=None):
+        Node.__init__(self, name)
+        self.type = typeobj
         self.readable = readable
         self.writable = writable
         self.construct = construct
         self.construct_only = construct_only
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.type)
-
-
-# FIXME: Inherit from Function
+        self.transfer = PARAM_TRANSFER_NONE
 
 
-class Callback(Node):
+class Callback(Callable):
 
-    def __init__(self, name, retval, parameters, ctype=None):
-        Node.__init__(self, name)
-        self.retval = retval
-        self.parameters = parameters
+    def __init__(self, name, retval, parameters, throws, ctype=None):
+        Callable.__init__(self, name, retval, parameters, throws)
         self.ctype = ctype
-        self.throws = False
-        self.doc = None
-
-    def get_parameter_index(self, name):
-        for i, parameter in enumerate(self.parameters):
-            if parameter.name == name:
-                return i
-
-    def get_parameter(self, name):
-        for parameter in self.parameters:
-            if parameter.name == name:
-                return parameter
-
-    def __repr__(self):
-        return 'Callback(%r, %r, %r)' % (
-            self.name, self.retval, self.parameters)
 
 
 class Union(Node):
@@ -592,8 +800,16 @@ class Union(Node):
         self.fields = []
         self.constructors = []
         self.methods = []
+        self.static_methods = []
         self.symbol = symbol
-        self.doc = None
 
-    def __repr__(self):
-        return 'Union(%r, %r)' % (self.name, self.fields, )
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+        for field in self.fields:
+            if field.anonymous_node:
+                field.anonymous_node.walk(callback, chain)
diff --git a/giscanner/codegen.py b/giscanner/codegen.py
new file mode 100644
index 0000000..0c0c559
--- /dev/null
+++ b/giscanner/codegen.py
@@ -0,0 +1,137 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2010  Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from contextlib import contextmanager
+from . import ast
+
+class CCodeGenerator(object):
+    def __init__(self, namespace, out_h_filename, out_c_filename):
+        self.out_h_filename = out_h_filename
+        self.out_c_filename = out_c_filename
+        self._function_bodies = {}
+        self.namespace = namespace
+
+    def gen_symbol(self, name):
+        name = name.replace(' ', '_')
+        return '%s_%s' % (self.namespace.symbol_prefixes[0], name)
+
+    def _typecontainer_to_ctype(self, param):
+        if (isinstance(param, ast.Parameter) and
+            param.direction in (ast.PARAM_DIRECTION_OUT,
+                                ast.PARAM_DIRECTION_INOUT)):
+            suffix = '*'
+        else:
+            suffix = ''
+        if (param.type.is_equiv((ast.TYPE_STRING, ast.TYPE_FILENAME)) and
+            param.transfer == ast.PARAM_TRANSFER_NONE):
+            return "const gchar*" + suffix
+        return param.type.ctype + suffix
+
+    def _write_prelude(self, out, func):
+        out.write("""
+%s
+%s (""" % (self._typecontainer_to_ctype(func.retval), func.symbol))
+        l = len(func.parameters)
+        if func.parameters:
+            for i, param in enumerate(func.parameters):
+                ctype = self._typecontainer_to_ctype(param)
+                out.write('%s %s' % (ctype, param.argname))
+                if i < l - 1:
+                    out.write(", ")
+        else:
+            out.write('void')
+        out.write(")")
+
+    def _write_prototype(self, func):
+        self._write_prelude(self.out_h, func)
+        self.out_h.write(";\n\n")
+
+    def _write_annotation_transfer(self, transfer):
+        self.out_c.write("(transfer %s)" % (transfer, ))
+
+    def _write_docs(self, func):
+        self.out_c.write("/**\n * %s:\n" % (func.symbol, ))
+        for param in func.parameters:
+            self.out_c.write(" * @%s: " % (param.argname, ))
+            if param.direction in (ast.PARAM_DIRECTION_OUT,
+                                   ast.PARAM_DIRECTION_INOUT):
+                if param.caller_allocates:
+                    allocate_string = ' caller-allocates'
+                else:
+                    allocate_string = ''
+                self.out_c.write("(%s%s) " % (param.direction,
+                                              allocate_string))
+                self._write_annotation_transfer(param.transfer)
+            self.out_c.write(":\n")
+        self.out_c.write(' *\n')
+        self.out_c.write(' * Undocumented.\n')
+        self.out_c.write(' *\n')
+        self.out_c.write(' * Returns: ')
+        self._write_annotation_transfer(func.retval.transfer)
+        self.out_c.write('\n */')
+
+    @contextmanager
+    def _function(self, func):
+        self._write_prototype(func)
+        self._write_docs(func)
+        self._write_prelude(self.out_c, func)
+        self.out_c.write("\n{\n")
+        yield
+        self.out_c.write("}\n\n")
+
+    def _codegen_start(self):
+        warning = '/* GENERATED BY testcodegen.py; DO NOT EDIT */\n\n'
+        self.out_h.write(warning)
+        nsupper = self.namespace.name.upper()
+        self.out_h.write("""
+#ifndef __%s_H__
+#define __%s_H__
+
+#include <glib-object.h>
+""" % (nsupper, nsupper))
+
+        self.out_c.write(warning)
+        self.out_c.write("""#include "%s"\n\n""" % (self.out_h_filename, ))
+
+    def _codegen_end(self):
+        self.out_h.write("""#endif\n""")
+
+        self.out_h.close()
+        self.out_c.close()
+
+    def set_function_body(self, node, body):
+        assert isinstance(node, ast.Function)
+        self._function_bodies[node] = body
+
+    def codegen(self):
+        self.out_h = open(self.out_h_filename, 'w')
+        self.out_c = open(self.out_c_filename, 'w')
+
+        self._codegen_start()
+
+        for node in self.namespace.itervalues():
+            if isinstance(node, ast.Function):
+                with self._function(node):
+                    body = self._function_bodies.get(node)
+                    if not body:
+                        body = ''
+                    self.out_c.write(body)
+
+        self._codegen_end()
diff --git a/giscanner/finaltransformer.py b/giscanner/finaltransformer.py
new file mode 100644
index 0000000..16a00b3
--- /dev/null
+++ b/giscanner/finaltransformer.py
@@ -0,0 +1,159 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from . import ast
+from . import glibast
+
+class FinalTransformer(object):
+
+    def __init__(self, transformer):
+        self._transformer = transformer
+        self._namespace = transformer.namespace
+
+    # Public API
+
+    def validate(self):
+        self._namespace.walk(self._analyze_node)
+        self._namespace.walk(self._introspectable_callable_analysis)
+        self._namespace.walk(self._introspectable_callable_analysis)
+        self._namespace.walk(self._introspectable_pass3)
+
+    def _interface_vfunc_check(self, node, stack):
+        if isinstance(node, glibast.GLibInterface):
+            for vfunc in node.virtual_methods:
+                if not vfunc.invoker:
+                    self._transformer.log_node_warning(vfunc,
+"""Virtual function %r has no known invoker""" % (vfunc.name, ),
+                    context=node)
+
+    def _parameter_warning(self, parent, param, text, *args):
+        if hasattr(parent, 'symbol'):
+            prefix = '%s: ' % (parent.symbol, )
+        else:
+            prefix = ''
+        if isinstance(param, ast.Parameter):
+            context = "argument %s: " % (param.argname, )
+        else:
+            context = "return value: "
+        self._transformer.log_node_warning(parent, prefix + context + text, *args)
+
+    def _introspectable_param_analysis(self, parent, node):
+        if not node.type.resolved:
+            self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+            parent.introspectable = False
+        elif isinstance(node.type, ast.Varargs):
+            parent.introspectable = False
+        elif not isinstance(node.type, ast.List) and \
+                (node.type.target_giname == 'GLib.List'):
+            self._parameter_warning(parent, node, "Missing (element-type) annotation")
+            parent.introspectable = False
+        elif node.transfer is None:
+            self._parameter_warning(parent, node, "Missing (transfer) annotation")
+            parent.introspectable = False
+
+        if isinstance(node, ast.Parameter) and node.type.target_giname:
+            target = self._transformer.lookup_typenode(node.type)
+            if (isinstance(target, ast.Callback)
+                and not target.create_type().target_giname in ('GLib.DestroyNotify',
+                                                               'Gio.AsyncReadyCallback')
+                and node.scope is None):
+                self._parameter_warning(parent, node,
+                    ("Missing (scope) annotation for callback" +
+                     " without GDestroyNotify (valid: %s, %s)")
+                     % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
+                parent.introspectable = False
+
+    def _type_is_introspectable(self, typeval, warn=False):
+        if not typeval.resolved:
+            return False
+        if isinstance(typeval, (ast.Array, ast.List)):
+            return self._type_is_introspectable(typeval.element_type)
+        elif isinstance(typeval, ast.Map):
+            return (self._type_is_introspectable(typeval.key_type)
+                    and self._type_is_introspectable(typeval.value_type))
+        if typeval.target_foreign:
+            return True
+        if typeval.target_fundamental:
+            if typeval.is_equiv(ast.TYPE_VALIST):
+                return False
+            # Mark UCHAR as not introspectable temporarily until
+            # we're ready to land the typelib changes
+            if typeval.is_equiv(ast.TYPE_UNICHAR):
+                return False
+            # These are not introspectable pending us adding
+            # larger type tags to the typelib (in theory these could
+            # be 128 bit or larger)
+            if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
+                                 ast.TYPE_LONG_DOUBLE)):
+                return False
+            return True
+        target = self._transformer.lookup_typenode(typeval)
+        if not target:
+            return False
+        return target.introspectable
+
+    def _analyze_node(self, obj, stack):
+        if obj.skip:
+            return False
+        # Combine one-pass checks here
+        self._interface_vfunc_check(obj, stack)
+        # Our first pass for scriptability
+        if isinstance(obj, ast.Callable):
+            for param in obj.parameters:
+                self._introspectable_param_analysis(obj, param)
+            self._introspectable_param_analysis(obj, obj.retval)
+        if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+            for field in obj.fields:
+                if field.type:
+                    if not self._type_is_introspectable(field.type):
+                        field.introspectable = False
+        return True
+
+    def _introspectable_callable_analysis(self, obj, stack):
+        if obj.skip:
+            return True
+        # Propagate introspectability of parameters to entire functions
+        if isinstance(obj, ast.Callable):
+            for param in obj.parameters:
+                if not self._type_is_introspectable(param.type):
+                    obj.introspectable = False
+                    return True
+            if not self._type_is_introspectable(obj.retval.type):
+                obj.introspectable = False
+                return True
+        return True
+
+    def _introspectable_pass3(self, obj, stack):
+        if obj.skip:
+            return True
+        # Propagate introspectability for fields
+        if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+            for field in obj.fields:
+                if field.anonymous_node:
+                    if not field.anonymous_node.introspectable:
+                        field.introspectable = False
+                else:
+                    if not self._type_is_introspectable(field.type):
+                        field.introspectable = False
+        # Propagate introspectability for properties
+        if isinstance(obj, (ast.Class, ast.Interface)):
+            for prop in obj.properties:
+                if not self._type_is_introspectable(prop.type):
+                    prop.introspectable = False
+        return True
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 9fee1fc..540779f 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -22,13 +22,8 @@ import os
 
 from xml.etree.cElementTree import parse
 
-from .ast import (Alias, Array, Callback, Constant, Enum, Function, Field,
-                  Namespace, Parameter, Property, Return, Union, Struct, Type,
-                  Varargs, Include)
-from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
-                      GLibInterface, GLibObject, GLibBoxedStruct,
-                      GLibBoxedUnion, GLibBoxedOther)
-
+from . import ast
+from . import glibast
 from .girwriter import COMPATIBLE_GIR_VERSION
 
 CORE_NS = "http://www.gtk.org/introspection/core/1.0";
@@ -51,7 +46,6 @@ def _cns(tag):
 class GIRParser(object):
 
     def __init__(self):
-        self._include_parsing = False
         self._shared_libraries = []
         self._includes = set()
         self._pkgconfig_packages = set()
@@ -72,6 +66,8 @@ class GIRParser(object):
         self._namespace = None
         self._shared_libraries = []
         self._pkgconfig_packages = set()
+        self._c_includes = set()
+        self._c_prefix = None
         self._parse_api(tree.getroot())
 
     def get_namespace(self):
@@ -83,6 +79,12 @@ class GIRParser(object):
     def get_includes(self):
         return self._includes
 
+    def get_c_includes(self):
+        return self._c_includes
+
+    def get_c_prefix(self):
+        return self._c_prefix
+
     def get_pkgconfig_packages(self):
         if not hasattr(self, '_pkgconfig_packages'):
             self._pkgconfig_packages = []
@@ -91,11 +93,21 @@ class GIRParser(object):
     def get_doc(self):
         return parse(self._filename)
 
-    def set_include_parsing(self, include_parsing):
-        self._include_parsing = include_parsing
-
     # Private
 
+    def _find_first_child(self, node, name):
+        for child in node.getchildren():
+            if child.tag == name:
+                return child
+        return None
+
+    def _find_children(self, node, name):
+        result = []
+        for child in node.getchildren():
+            if child.tag == name:
+                result.append(child)
+        return result
+
     def _get_current_file(self):
         if not self._filename_stack:
             return None
@@ -105,9 +117,6 @@ class GIRParser(object):
             return curfile[len(cwd):]
         return curfile
 
-    def _add_node(self, node):
-        self._namespace.nodes.append(node)
-
     def _parse_api(self, root):
         assert root.tag == _corens('repository')
         version = root.attrib['version']
@@ -121,11 +130,21 @@ class GIRParser(object):
                 self._parse_include(node)
             elif node.tag == _corens('package'):
                 self._parse_pkgconfig_package(node)
+            elif node.tag == _cns('include'):
+                self._parse_c_include(node)
 
         ns = root.find(_corens('namespace'))
         assert ns is not None
-        self._namespace = Namespace(ns.attrib['name'],
-                                    ns.attrib['version'])
+        identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
+        if identifier_prefixes:
+            identifier_prefixes = identifier_prefixes.split(',')
+        symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
+        if symbol_prefixes:
+            symbol_prefixes = symbol_prefixes.split(',')
+        self._namespace = ast.Namespace(ns.attrib['name'],
+                                    ns.attrib['version'],
+                                    identifier_prefixes=identifier_prefixes,
+                                    symbol_prefixes=symbol_prefixes)
         if 'shared-library' in ns.attrib:
             self._shared_libraries.extend(
                 ns.attrib['shared-library'].split(','))
@@ -141,7 +160,7 @@ class GIRParser(object):
             _corens('interface'): self._parse_object_interface,
             _corens('record'): self._parse_record,
             _corens('union'): self._parse_union,
-            _corens('boxed'): self._parse_boxed,
+            _glibns('boxed'): self._parse_boxed,
             }
 
         for node in ns.getchildren():
@@ -150,31 +169,54 @@ class GIRParser(object):
                 method(node)
 
     def _parse_include(self, node):
-        include = Include(node.attrib['name'],
+        include = ast.Include(node.attrib['name'],
                           node.attrib['version'])
         self._includes.add(include)
 
     def _parse_pkgconfig_package(self, node):
-        if not hasattr(self, '_pkgconfig_packages'):
-            self._pkgconfig_packages = []
         self._pkgconfig_packages.add(node.attrib['name'])
 
+    def _parse_c_include(self, node):
+        self._c_includes.add(node.attrib['name'])
+
     def _parse_alias(self, node):
         typeval = self._parse_type(node)
-        alias = Alias(node.attrib['name'],
+        alias = ast.Alias(node.attrib['name'],
                       typeval,
                       node.attrib.get(_cns('type')))
-        self._add_node(alias)
+        self._namespace.append(alias)
+
+    def _parse_generic_attribs(self, node, obj):
+        assert isinstance(obj, ast.Annotated)
+        doc = node.find(_corens('doc'))
+        if doc is not None:
+            obj.doc = doc.text
+        version = node.attrib.get('version')
+        if version:
+            obj.version = version
+        deprecated = node.attrib.get('deprecated')
+        if deprecated:
+            obj.deprecated = deprecated
+        introspectable = node.attrib.get('introspectable')
+        if introspectable:
+            obj.introspectable = int(introspectable) > 0
 
     def _parse_object_interface(self, node):
+        parent = node.attrib.get('parent')
+        if parent:
+            parent_type = self._namespace.type_from_name(parent)
+        else:
+            parent_type = None
+
         ctor_args = [node.attrib['name'],
-                     node.attrib.get('parent'),
+                     parent_type,
                      node.attrib[_glibns('type-name')],
-                     node.attrib[_glibns('get-type')]]
+                     node.attrib[_glibns('get-type')],
+                     node.attrib.get(_cns('symbol-prefix'))]
         if node.tag == _corens('interface'):
-            klass = GLibInterface
+            klass = glibast.GLibInterface
         elif node.tag == _corens('class'):
-            klass = GLibObject
+            klass = glibast.GLibObject
             is_abstract = node.attrib.get('abstract')
             is_abstract = is_abstract and is_abstract != '0'
             ctor_args.append(is_abstract)
@@ -182,38 +224,49 @@ class GIRParser(object):
             raise AssertionError(node)
 
         obj = klass(*ctor_args)
-        self._add_node(obj)
+        self._parse_generic_attribs(node, obj)
+        type_struct = node.attrib.get(_glibns('type-struct'))
+        if type_struct:
+            obj.glib_type_struct = self._namespace.type_from_name(type_struct)
+        self._namespace.append(obj)
 
-        if self._include_parsing:
-            return
         ctor_args.append(node.attrib.get(_cns('type')))
-        for iface in node.findall(_corens('implements')):
-            obj.interfaces.append(iface.attrib['name'])
-        for iface in node.findall(_corens('prerequisites')):
-            obj.prerequisities.append(iface.attrib['name'])
-        for method in node.findall(_corens('method')):
-            func = self._parse_function_common(method, Function)
+        for iface in self._find_children(node, _corens('implements')):
+            obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
+        for iface in self._find_children(node, _corens('prerequisite')):
+            obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
+        for func_node in self._find_children(node, _corens('function')):
+            func = self._parse_function_common(func_node, ast.Function)
+            obj.static_methods.append(func)
+        for method in self._find_children(node, _corens('method')):
+            func = self._parse_function_common(method, ast.Function)
             func.is_method = True
             obj.methods.append(func)
-        for ctor in node.findall(_corens('constructor')):
-            obj.constructors.append(
-                self._parse_function_common(ctor, Function))
-        for callback in node.findall(_corens('callback')):
-            obj.fields.append(self._parse_function_common(callback, Callback))
-        for field in node.findall(_corens('field')):
-            obj.fields.append(self._parse_field(field))
-        for prop in node.findall(_corens('property')):
+        for method in self._find_children(node, _corens('virtual-method')):
+            func = self._parse_function_common(method, ast.VFunction)
+            self._parse_generic_attribs(method, func)
+            func.is_method = True
+            func.invoker = method.get('invoker')
+            obj.virtual_methods.append(func)
+        for ctor in self._find_children(node, _corens('constructor')):
+            func = self._parse_function_common(ctor, ast.Function)
+            func.is_constructor = True
+            obj.constructors.append(func)
+        obj.fields.extend(self._parse_fields(node))
+        for prop in self._find_children(node, _corens('property')):
             obj.properties.append(self._parse_property(prop))
-        for signal in node.findall(_glibns('signal')):
-            obj.signals.append(self._parse_function_common(signal, Function))
+        for signal in self._find_children(node, _glibns('signal')):
+            obj.signals.append(self._parse_function_common(signal, ast.Function))
 
     def _parse_callback(self, node):
-        callback = self._parse_function_common(node, Callback)
-        self._add_node(callback)
+        callback = self._parse_function_common(node, ast.Callback)
+        self._namespace.append(callback)
 
     def _parse_function(self, node):
-        function = self._parse_function_common(node, Function)
-        self._add_node(function)
+        function = self._parse_function_common(node, ast.Function)
+        function.shadows = node.attrib.get('shadows', None)
+        function.shadowed_by = node.attrib.get('shadowed-by', None)
+        self._namespace.append(function)
 
     def _parse_function_common(self, node, klass):
         name = node.attrib['name']
@@ -221,187 +274,297 @@ class GIRParser(object):
         if not returnnode:
             raise ValueError('node %r has no return-value' % (name, ))
         transfer = returnnode.attrib.get('transfer-ownership')
-        retval = Return(self._parse_type(returnnode), transfer)
+        retval = ast.Return(self._parse_type(returnnode), transfer)
+        self._parse_generic_attribs(returnnode, retval)
         parameters = []
 
-        if klass is Callback:
-            func = klass(name, retval, parameters,
+        throws = (node.attrib.get('throws') == '1')
+
+        if klass is ast.Callback:
+            func = klass(name, retval, parameters, throws,
                          node.attrib.get(_cns('type')))
-        else:
+        elif klass is ast.Function:
             identifier = node.attrib.get(_cns('identifier'))
-            throws = (node.attrib.get('throws') == '1')
-            func = klass(name, retval, parameters, identifier, throws)
-
-        if self._include_parsing:
-            return func
+            func = klass(name, retval, parameters, throws, identifier)
+        elif klass is ast.VFunction:
+            func = klass(name, retval, parameters, throws)
+        else:
+            assert False
 
         parameters_node = node.find(_corens('parameters'))
         if (parameters_node is not None):
-            for paramnode in parameters_node.findall(_corens('parameter')):
-                param = Parameter(paramnode.attrib.get('name'),
-                                  self._parse_type(paramnode),
-                                  paramnode.attrib.get('direction'),
+            for paramnode in self._find_children(parameters_node, _corens('parameter')):
+                typeval = self._parse_type(paramnode)
+                param = ast.Parameter(paramnode.attrib.get('name'),
+                                  typeval,
+                                  paramnode.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
                                   paramnode.attrib.get('transfer-ownership'),
-                                  paramnode.attrib.get('allow-none') == '1')
+                                  paramnode.attrib.get('allow-none') == '1',
+                                  paramnode.attrib.get('scope'),
+                                  paramnode.attrib.get('caller-allocates') == '1')
+                self._parse_generic_attribs(paramnode, param)
                 parameters.append(param)
+            for i, paramnode in enumerate(self._find_children(parameters_node,
+                                                              _corens('parameter'))):
+                param = parameters[i]
+                self._parse_type_second_pass(func, paramnode, param.type)
+                closure = paramnode.attrib.get('closure')
+                if closure:
+                    idx = int(closure)
+                    assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+                    param.closure_name = parameters[idx].argname
+                destroy = paramnode.attrib.get('destroy')
+                if destroy:
+                    idx = int(destroy)
+                    assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+                    param.destroy_name = parameters[idx].argname
+
+        self._parse_type_second_pass(func, returnnode, retval.type)
+
+        self._parse_generic_attribs(node, func)
 
         return func
 
-    def _parse_record(self, node):
+    def _parse_fields(self, node):
+        res = []
+        names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
+        for child in node.getchildren():
+            if child.tag in names:
+                fieldobj = self._parse_field(child)
+                res.append(fieldobj)
+        return res
+
+    def _parse_record(self, node, anonymous=False):
         if _glibns('type-name') in node.attrib:
-            struct = GLibBoxedStruct(node.attrib['name'],
+            struct = glibast.GLibBoxedStruct(node.attrib['name'],
                                      node.attrib[_glibns('type-name')],
                                      node.attrib[_glibns('get-type')],
+                                     node.attrib.get(_cns('symbol-prefix')),
                                      node.attrib.get(_cns('type')))
+        elif _glibns('is-gtype-struct-for') in node.attrib:
+            struct = glibast.GLibRecord(node.attrib['name'],
+                                node.attrib.get(_cns('type')),
+                                disguised=node.attrib.get('disguised') == '1')
+            is_gtype_struct_for = node.attrib[_glibns('is-gtype-struct-for')]
+            struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
         else:
-            disguised = node.attrib.get('disguised') == '1'
-            struct = Struct(node.attrib['name'],
+            struct = ast.Record(node.attrib.get('name'),
                             node.attrib.get(_cns('type')),
-                            disguised=disguised)
-        self._add_node(struct)
-
-        if self._include_parsing:
-            return
-        for field in node.findall(_corens('field')):
-            struct.fields.append(self._parse_field(field))
-        for callback in node.findall(_corens('callback')):
-            struct.fields.append(
-                self._parse_function_common(callback, Callback))
-        for method in node.findall(_corens('method')):
-            struct.fields.append(
-                self._parse_function_common(method, Function))
-        for ctor in node.findall(_corens('constructor')):
+                            disguised=node.attrib.get('disguised') == '1')
+        if node.attrib.get('foreign') == '1':
+            struct.foreign = True
+        self._parse_generic_attribs(node, struct)
+        if not anonymous:
+            self._namespace.append(struct)
+
+        struct.fields.extend(self._parse_fields(node))
+        for method in self._find_children(node, _corens('method')):
+            struct.methods.append(
+                self._parse_function_common(method, ast.Function))
+        for func in self._find_children(node, _corens('function')):
+            struct.static_methods.append(
+                self._parse_function_common(func, ast.Function))
+        for ctor in self._find_children(node, _corens('constructor')):
             struct.constructors.append(
-                self._parse_function_common(ctor, Function))
+                self._parse_function_common(ctor, ast.Function))
+        return struct
 
-    def _parse_union(self, node):
+    def _parse_union(self, node, anonymous=False):
         if _glibns('type-name') in node.attrib:
-            union = GLibBoxedUnion(node.attrib['name'],
+            union = glibast.GLibBoxedUnion(node.attrib['name'],
                                     node.attrib[_glibns('type-name')],
                                     node.attrib[_glibns('get-type')],
+                                    node.attrib.get(_cns('symbol-prefix')),
                                     node.attrib.get(_cns('type')))
         else:
-            union = Union(node.attrib['name'],
+            union = ast.Union(node.attrib.get('name'),
                           node.attrib.get(_cns('type')))
-        self._add_node(union)
+        if not anonymous:
+            self._namespace.append(union)
 
-        if self._include_parsing:
-            return
-        for callback in node.findall(_corens('callback')):
+        for callback in self._find_children(node, _corens('callback')):
             union.fields.append(
-                self._parse_function_common(callback, Callback))
-        for field in node.findall(_corens('field')):
-            union.fields.append(self._parse_field(field))
-        for method in node.findall(_corens('method')):
-            union.fields.append(
-                self._parse_function_common(method, Function))
-        for ctor in node.findall(_corens('constructor')):
+                self._parse_function_common(callback, ast.Callback))
+        union.fields.extend(self._parse_fields(node))
+        for method in self._find_children(node, _corens('method')):
+            union.methods.append(
+                self._parse_function_common(method, ast.Function))
+        for func in self._find_children(node, _corens('function')):
+            union.static_methods.append(
+                self._parse_function_common(func, ast.Function))
+        for ctor in self._find_children(node, _corens('constructor')):
             union.constructors.append(
-                self._parse_function_common(ctor, Function))
-
-    def _parse_type(self, node):
-        typenode = node.find(_corens('type'))
-        if typenode is not None:
-            return Type(typenode.attrib['name'],
-                        typenode.attrib.get(_cns('type')))
-
-        typenode = node.find(_corens('array'))
-        if typenode is not None:
-
-            array_type = typenode.attrib.get(_cns('type'))
-            if array_type.startswith('GArray*') or \
-               array_type.startswith('GPtrArray*') or \
-               array_type.startswith('GByteArray*'):
-                element_type = None
-            else:
-                element_type = self._parse_type(typenode)
-
-            ret = Array(None, array_type, element_type)
+                self._parse_function_common(ctor, ast.Function))
+        return union
+
+    def _parse_type_simple(self, typenode):
+        # ast.Fields can contain inline callbacks
+        if typenode.tag == _corens('callback'):
+            typeval = self._namespace.type_from_name(typenode.attrib['name'])
+            typeval.ctype = typenode.attrib.get(_cns('type'))
+            return typeval
+        # ast.Arrays have their own toplevel XML
+        elif typenode.tag == _corens('array'):
+            array_type = typenode.attrib.get('name')
+            element_type = self._parse_type(typenode)
+            array_ctype = typenode.attrib.get(_cns('type'))
+            ret = ast.Array(array_type, element_type, ctype=array_ctype)
+            # zero-terminated defaults to true...
+            zero = typenode.attrib.get('zero-terminated')
+            if zero and zero == '0':
+                ret.zeroterminated = False
+            fixed_size = typenode.attrib.get('fixed-size')
+            if fixed_size:
+                ret.size = int(fixed_size)
 
-            lenidx = typenode.attrib.get('length')
-            if lenidx:
-                ret.length_param_index = int(lenidx)
             return ret
+        elif typenode.tag == _corens('varargs'):
+            return ast.Varargs()
+        elif typenode.tag == _corens('type'):
+            name = typenode.attrib.get('name')
+            ctype = typenode.attrib.get(_cns('type'))
+            if name is None:
+                if ctype is None:
+                    return ast.TypeUnknown()
+                return ast.Type(ctype=ctype)
+            elif name in ['GLib.List', 'GLib.SList']:
+                subchild = self._find_first_child(typenode, _corens('type'))
+                if subchild is not None:
+                    element_type = self._parse_type(typenode)
+                else:
+                    element_type = ast.TYPE_ANY
+                return ast.List(name, element_type, ctype=ctype)
+            elif name == 'GLib.HashTable':
+                subchildren = self._find_children(typenode, _corens('type'))
+                subchildren_types = map(self._parse_type_simple, subchildren)
+                while len(subchildren_types) < 2:
+                    subchildren_types.append(ast.TYPE_ANY)
+                return ast.Map(subchildren_types[0],
+                           subchildren_types[1],
+                           ctype=ctype)
+            else:
+                return self._namespace.type_from_name(name, ctype)
+        else:
+            assert False, "Failed to parse inner type"
 
-        typenode = node.find(_corens('varargs'))
-        if typenode is not None:
-            return Varargs()
-
-        raise ValueError("Couldn't parse type of node %r; children=%r",
-                         node, list(node))
+    def _parse_type(self, node):
+        for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
+            typenode = node.find(name)
+            if typenode is not None:
+                return self._parse_type_simple(typenode)
+        assert False, "Failed to parse toplevel type"
+
+    def _parse_type_second_pass(self, parent, node, typeval):
+        """A hack necessary to handle the integer parameter indexes on
+           array types."""
+        typenode = node.find(_corens('array'))
+        if typenode is None:
+            return
+        lenidx = typenode.attrib.get('length')
+        if lenidx is not None:
+            idx = int(lenidx)
+            assert idx < len(parent.parameters), "%r %d >= %d" \
+                      % (parent, idx, len(parent.parameters))
+            typeval.length_param_name = parent.parameters[idx].argname
 
     def _parse_boxed(self, node):
-        obj = GLibBoxedOther(node.attrib[_glibns('name')],
+        obj = glibast.GLibBoxedOther(node.attrib[_glibns('name')],
                              node.attrib[_glibns('type-name')],
-                             node.attrib[_glibns('get-type')])
-        self._add_node(obj)
-        if self._include_parsing:
-            return
-        for method in node.findall(_corens('method')):
-            func = self._parse_function_common(method, Function)
+                             node.attrib[_glibns('get-type')],
+                             node.attrib.get(_cns('symbol-prefix')))
+        self._parse_generic_attribs(node, obj)
+        self._namespace.append(obj)
+        for method in self._find_children(node, _corens('method')):
+            func = self._parse_function_common(method, ast.Function)
             func.is_method = True
             obj.methods.append(func)
-        for ctor in node.findall(_corens('constructor')):
+        for ctor in self._find_children(node, _corens('constructor')):
             obj.constructors.append(
-                self._parse_function_common(ctor, Function))
-        for callback in node.findall(_corens('callback')):
+                self._parse_function_common(ctor, ast.Function))
+        for callback in self._find_children(node, _corens('callback')):
             obj.fields.append(
-                self._parse_function_common(callback, Callback))
+                self._parse_function_common(callback, ast.Callback))
 
     def _parse_field(self, node):
-        type_node = self._parse_type(node)
-        return Field(node.attrib['name'],
-                     type_node,
-                     type_node.ctype,
-                     node.attrib.get('readable') != '0',
-                     node.attrib.get('writable') == '1',
-                     node.attrib.get('bits'))
+        type_node = None
+        anonymous_node = None
+        if node.tag in map(_corens, ('record', 'union')):
+            anonymous_elt = node
+        else:
+            anonymous_elt = self._find_first_child(node, _corens('callback'))
+        if anonymous_elt is not None:
+            if anonymous_elt.tag == _corens('callback'):
+                anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
+            elif anonymous_elt.tag == _corens('record'):
+                anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
+            elif anonymous_elt.tag == _corens('union'):
+                anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
+            else:
+                assert False, anonymous_elt.tag
+        else:
+            assert node.tag == _corens('field'), node.tag
+            type_node = self._parse_type(node)
+        field = ast.Field(node.attrib.get('name'),
+                      type_node,
+                      node.attrib.get('readable') != '0',
+                      node.attrib.get('writable') == '1',
+                      node.attrib.get('bits'),
+                      anonymous_node=anonymous_node)
+        self._parse_generic_attribs(node, field)
+        return field
 
     def _parse_property(self, node):
-        type_node = self._parse_type(node)
-        return Property(node.attrib['name'],
-                        type_node.name,
+        prop = ast.Property(node.attrib['name'],
+                        self._parse_type(node),
                         node.attrib.get('readable') != '0',
                         node.attrib.get('writable') == '1',
                         node.attrib.get('construct') == '1',
-                        node.attrib.get('construct-only') == '1',
-                        type_node.ctype)
+                        node.attrib.get('construct-only') == '1')
+        self._parse_generic_attribs(node, prop)
+        return prop
 
     def _parse_member(self, node):
-        return GLibEnumMember(node.attrib['name'],
-                              node.attrib['value'],
-                              node.attrib.get(_cns('identifier')),
-                              node.attrib.get(_glibns('nick')))
+        member = glibast.GLibEnumMember(node.attrib['name'],
+                                node.attrib['value'],
+                                node.attrib.get(_cns('identifier')),
+                                node.attrib.get(_glibns('nick')))
+        self._parse_generic_attribs(node, member)
+        return member
 
     def _parse_constant(self, node):
         type_node = self._parse_type(node)
-        constant = Constant(node.attrib['name'],
-                            type_node.name,
+        constant = ast.Constant(node.attrib['name'],
+                            type_node,
                             node.attrib['value'])
-        self._add_node(constant)
+        self._parse_generic_attribs(node, constant)
+        self._namespace.append(constant)
 
     def _parse_enumeration_bitfield(self, node):
         name = node.attrib.get('name')
         ctype = node.attrib.get(_cns('type'))
         get_type = node.attrib.get(_glibns('get-type'))
         type_name = node.attrib.get(_glibns('type-name'))
-        if get_type:
+        glib_error_quark = node.attrib.get(_glibns('error-quark'))
+        if get_type or glib_error_quark:
             if node.tag == _corens('bitfield'):
-                klass = GLibFlags
+                klass = glibast.GLibFlags
             else:
-                klass = GLibEnum
+                klass = glibast.GLibEnum
         else:
-            klass = Enum
+            if node.tag == _corens('bitfield'):
+                klass = ast.Bitfield
+            else:
+                klass = ast.Enum
             type_name = ctype
         members = []
-        if klass is Enum:
+        if klass in (ast.Enum, ast.Bitfield):
             obj = klass(name, type_name, members)
         else:
             obj = klass(name, type_name, members, get_type)
+            obj.error_quark = glib_error_quark
             obj.ctype = ctype
-        self._add_node(obj)
+        self._parse_generic_attribs(node, obj)
+        self._namespace.append(obj)
 
-        if self._include_parsing:
-            return
-        for member in node.findall(_corens('member')):
+        for member in self._find_children(node, _corens('member')):
             members.append(self._parse_member(member))
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index c810349..ef9169c 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,8 +22,8 @@
 from __future__ import with_statement
 
 from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
-                  Function, Interface, List, Map, Member, Struct, Union,
-                  Varargs, Type, TYPE_ANY)
+                  Function, Interface, List, Map, Member, Record, Union,
+                  Varargs, Type)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
                       GLibFlags, GLibObject, GLibInterface,
                       GLibRecord)
@@ -31,21 +31,21 @@ from .xmlwriter import XMLWriter
 
 # Bump this for *incompatible* changes to the .gir.
 # Compatible changes we just make inline
-COMPATIBLE_GIR_VERSION = '1.1'
+COMPATIBLE_GIR_VERSION = '1.2'
 
 class GIRWriter(XMLWriter):
 
-    def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
+    def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
         super(GIRWriter, self).__init__()
         self.write_comment(
 '''This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations. ''')
         self._write_repository(namespace, shlibs, includes, pkgs,
-                               c_includes, cprefix)
+                               c_includes)
 
     def _write_repository(self, namespace, shlibs, includes=None,
-                          packages=None, c_includes=None, cprefix=None):
+                          packages=None, c_includes=None):
         if includes is None:
             includes = frozenset()
         if packages is None:
@@ -65,7 +65,9 @@ and/or use gtk-doc annotations. ''')
                 self._write_pkgconfig_pkg(pkg)
             for c_include in sorted(set(c_includes)):
                 self._write_c_include(c_include)
-            self._write_namespace(namespace, shlibs, cprefix)
+            self._namespace = namespace
+            self._write_namespace(namespace, shlibs)
+            self._namespace = None
 
     def _write_include(self, include):
         attrs = [('name', include.name), ('version', include.version)]
@@ -79,11 +81,12 @@ and/or use gtk-doc annotations. ''')
         attrs = [('name', c_include)]
         self.write_tag('c:include', attrs)
 
-    def _write_namespace(self, namespace, shlibs, cprefix):
+    def _write_namespace(self, namespace, shlibs):
         attrs = [('name', namespace.name),
                  ('version', namespace.version),
                  ('shared-library', ','.join(shlibs)),
-                 ('c:prefix', cprefix)]
+                 ('c:identifier-prefixes', ','.join(namespace.identifier_prefixes)),
+                 ('c:symbol-prefixes', ','.join(namespace.symbol_prefixes))]
         with self.tagcontext('namespace', attrs):
             # We define a custom sorting function here because
             # we want aliases to be first.  They're a bit
@@ -98,7 +101,7 @@ and/or use gtk-doc annotations. ''')
                     return 1
                 else:
                     return cmp(a, b)
-            for node in sorted(namespace.nodes, cmp=nscmp):
+            for node in sorted(namespace.itervalues(), cmp=nscmp):
                 self._write_node(node)
 
     def _write_node(self, node):
@@ -112,7 +115,7 @@ and/or use gtk-doc annotations. ''')
             self._write_class(node)
         elif isinstance(node, Callback):
             self._write_callback(node)
-        elif isinstance(node, Struct):
+        elif isinstance(node, Record):
             self._write_record(node)
         elif isinstance(node, Union):
             self._write_union(node)
@@ -167,11 +170,17 @@ and/or use gtk-doc annotations. ''')
         self._append_throws(callable, attrs)
         with self.tagcontext(tag_name, attrs):
             self._write_generic(callable)
-            self._write_return_type(callable.retval)
-            self._write_parameters(callable.parameters)
+            self._write_return_type(callable.retval, parent=callable)
+            self._write_parameters(callable, callable.parameters)
 
     def _write_function(self, func, tag_name='function'):
-        attrs = [('c:identifier', func.symbol)]
+        attrs = []
+        if hasattr(func, 'symbol'):
+            attrs.append(('c:identifier', func.symbol))
+        if func.shadowed_by:
+            attrs.append(('shadowed-by', func.shadowed_by))
+        elif func.shadows:
+            attrs.append(('shadows', func.shadows))
         self._write_callable(func, tag_name, attrs)
 
     def _write_method(self, method):
@@ -183,105 +192,100 @@ and/or use gtk-doc annotations. ''')
     def _write_constructor(self, method):
         self._write_function(method, tag_name='constructor')
 
-    def _write_return_type(self, return_):
+    def _write_return_type(self, return_, parent=None):
         if not return_:
             return
 
-        assert return_.transfer is not None, return_
-
         attrs = []
-        attrs.append(('transfer-ownership', return_.transfer))
+        if return_.transfer:
+            attrs.append(('transfer-ownership', return_.transfer))
         with self.tagcontext('return-value', attrs):
             self._write_generic(return_)
-            self._write_type(return_.type)
+            self._write_type(return_.type, function=parent)
 
-    def _write_parameters(self, parameters):
+    def _write_parameters(self, parent, parameters):
         if not parameters:
             return
         with self.tagcontext('parameters'):
             for parameter in parameters:
-                self._write_parameter(parameter)
-
-    def _write_parameter(self, parameter):
-        assert parameter.transfer is not None, parameter
+                self._write_parameter(parent, parameter)
 
+    def _write_parameter(self, parent, parameter):
         attrs = []
-        if parameter.name is not None:
-            attrs.append(('name', parameter.name))
-        if parameter.direction != 'in':
+        if parameter.argname is not None:
+            attrs.append(('name', parameter.argname))
+        if (parameter.direction is not None) and (parameter.direction != 'in'):
             attrs.append(('direction', parameter.direction))
             attrs.append(('caller-allocates',
                           '1' if parameter.caller_allocates else '0'))
-        attrs.append(('transfer-ownership',
-                     parameter.transfer))
+        if parameter.transfer:
+            attrs.append(('transfer-ownership',
+                          parameter.transfer))
         if parameter.allow_none:
             attrs.append(('allow-none', '1'))
         if parameter.scope:
             attrs.append(('scope', parameter.scope))
-        if parameter.closure_index >= 0:
-            attrs.append(('closure', '%d' % parameter.closure_index))
-        if parameter.destroy_index >= 0:
-            attrs.append(('destroy', '%d' % parameter.destroy_index))
+        if parameter.closure_name is not None:
+            idx = parent.get_parameter_index(parameter.closure_name)
+            attrs.append(('closure', '%d' % (idx, )))
+        if parameter.destroy_name is not None:
+            idx = parent.get_parameter_index(parameter.destroy_name)
+            attrs.append(('destroy', '%d' % (idx, )))
         with self.tagcontext('parameter', attrs):
             self._write_generic(parameter)
-            self._write_type(parameter.type)
-
-    def _type_to_string(self, ntype):
-        if isinstance(ntype, basestring):
-            return ntype
-        return ntype.name
-
-    def _write_type(self, ntype, relation=None):
-        if isinstance(ntype, basestring):
-            typename = ntype
-            type_cname = None
-        else:
-            typename = ntype.name
-            type_cname = ntype.ctype
+            self._write_type(parameter.type, function=parent)
+
+    def _type_to_name(self, typeval):
+        if not typeval.resolved:
+            raise AssertionError("Caught unresolved type %r (ctype=%r)" % (typeval, typeval.ctype))
+        assert typeval.target_giname is not None
+        prefix = self._namespace.name + '.'
+        if typeval.target_giname.startswith(prefix):
+            return typeval.target_giname[len(prefix):]
+        return typeval.target_giname
+
+    def _write_type(self, ntype, relation=None, function=None):
+        assert isinstance(ntype, Type), ntype
+        attrs = []
+        if ntype.ctype:
+            attrs.append(('c:type', ntype.ctype))
         if isinstance(ntype, Varargs):
             with self.tagcontext('varargs', []):
                 pass
-            return
-        if isinstance(ntype, Array):
-            attrs = []
+        elif isinstance(ntype, Array):
+            if ntype.array_type != Array.C:
+                attrs.insert(0, ('name', ntype.array_type))
             if not ntype.zeroterminated:
-                attrs.append(('zero-terminated', '0'))
-            if ntype.length_param_index >= 0:
-                attrs.append(('length', '%d' % (ntype.length_param_index, )))
-            if ntype.name in ['GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray']:
-                attrs.append(('name', ntype.name))
-            attrs.append(('c:type', ntype.ctype))
+                attrs.insert(0, ('zero-terminated', '0'))
             if ntype.size is not None:
-                attrs.append(('fixed-size', ntype.size))
+                attrs.append(('fixed-size', '%d' % (ntype.size, )))
+            if ntype.length_param_name is not None:
+                assert function
+                attrs.insert(0, ('length', '%d' \
+                            % (function.get_parameter_index(ntype.length_param_name, ))))
 
             with self.tagcontext('array', attrs):
-                if ntype.element_type is not None:
-                    self._write_type(ntype.element_type)
-                else:
-                    self._write_type(Type(TYPE_ANY, ctype='gpointer'))
-            return
-        attrs = [('name', self._type_to_string(ntype))]
-        # FIXME: figure out if type references a basic type
-        #        or a boxed/class/interface etc. and skip
-        #        writing the ctype if the latter.
-        if type_cname is not None:
-            attrs.append(('c:type', type_cname))
-        if (isinstance(ntype, List)
-            or typename in ('GLib.List',
-                            'GLib.SList')):
+                self._write_type(ntype.element_type)
+        elif isinstance(ntype, List):
+            if ntype.name:
+                attrs.insert(0, ('name', ntype.name))
             with self.tagcontext('type', attrs):
-                if isinstance(ntype, List) and ntype.element_type:
-                    self._write_type(ntype.element_type)
-                else:
-                    self._write_type(Type(TYPE_ANY, ctype='gpointer'))
-            return
-        if isinstance(ntype, Map) and ntype.key_type:
+                self._write_type(ntype.element_type)
+        elif isinstance(ntype, Map):
+            attrs.insert(0, ('name', 'GLib.HashTable'))
             with self.tagcontext('type', attrs):
                 self._write_type(ntype.key_type)
                 self._write_type(ntype.value_type)
-            return
-        # Not a special type, just write it out
-        self.write_tag('type', attrs)
+        else:
+            # REWRITEFIXME - enable this for 1.2
+            if ntype.target_giname:
+                attrs.insert(0, ('name', self._type_to_name(ntype)))
+            elif ntype.target_fundamental:
+                # attrs = [('fundamental', ntype.target_fundamental)]
+                attrs.insert(0, ('name', ntype.target_fundamental))
+            elif ntype.target_foreign:
+                attrs.insert(0, ('foreign', '1'))
+            self.write_tag('type', attrs)
 
     def _write_enum(self, enum):
         attrs = [('name', enum.name)]
@@ -325,20 +329,21 @@ and/or use gtk-doc annotations. ''')
         self.write_tag('member', attrs)
 
     def _write_constant(self, constant):
-        attrs = [('name', constant.name),
-                 ('value', str(constant.value))]
+        attrs = [('name', constant.name), ('value', constant.value)]
         with self.tagcontext('constant', attrs):
-            self._write_type(constant.type)
+            self._write_type(constant.value_type)
 
     def _write_class(self, node):
         attrs = [('name', node.name),
+                 ('c:symbol-prefix', node.c_symbol_prefix),
                  ('c:type', node.ctype)]
         self._append_version(node, attrs)
         self._append_node_generic(node, attrs)
         if isinstance(node, Class):
             tag_name = 'class'
             if node.parent is not None:
-                attrs.append(('parent', node.parent))
+                attrs.append(('parent',
+                              self._type_to_name(node.parent)))
             if node.is_abstract:
                 attrs.append(('abstract', '1'))
         else:
@@ -348,7 +353,8 @@ and/or use gtk-doc annotations. ''')
             if node.get_type:
                 attrs.append(('glib:get-type', node.get_type))
             if node.glib_type_struct:
-                attrs.append(('glib:type-struct', node.glib_type_struct.name))
+                attrs.append(('glib:type-struct',
+                              self._type_to_name(node.glib_type_struct)))
         if isinstance(node, GLibObject):
             if node.fundamental:
                 attrs.append(('glib:fundamental', '1'))
@@ -364,10 +370,12 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(node)
             if isinstance(node, GLibObject):
                 for iface in sorted(node.interfaces):
-                    self.write_tag('implements', [('name', iface)])
+                    self.write_tag('implements',
+                                   [('name', self._type_to_name(iface))])
             if isinstance(node, Interface):
                 for iface in sorted(node.prerequisites):
-                    self.write_tag('prerequisite', [('name', iface)])
+                    self.write_tag('prerequisite',
+                                   [('name', self._type_to_name(iface))])
             if isinstance(node, Class):
                 for method in sorted(node.constructors):
                     self._write_constructor(method)
@@ -394,6 +402,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in sorted(boxed.methods):
                 self._write_method(method)
+            for method in sorted(boxed.static_methods):
+                self._write_static_method(method)
 
     def _write_property(self, prop):
         attrs = [('name', prop.name)]
@@ -408,7 +418,8 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('construct', '1'))
         if prop.construct_only:
             attrs.append(('construct-only', '1'))
-        attrs.append(('transfer-ownership', prop.transfer))
+        if prop.transfer:
+            attrs.append(('transfer-ownership', prop.transfer))
         with self.tagcontext('property', attrs):
             self._write_generic(prop)
             self._write_type(prop.type)
@@ -416,16 +427,19 @@ and/or use gtk-doc annotations. ''')
     def _write_vfunc(self, vf):
         attrs = []
         if vf.invoker:
-            attrs.append(('invoker', vf.invoker.name))
+            attrs.append(('invoker', vf.invoker))
         self._write_callable(vf, 'virtual-method', attrs)
 
     def _write_callback(self, callback):
-        attrs = [('c:type', callback.ctype)]
+        attrs = []
+        if callback.namespace:
+            attrs.append(('c:type', callback.c_name))
         self._write_callable(callback, 'callback', attrs)
 
     def _boxed_attrs(self, boxed):
         return [('glib:type-name', boxed.type_name),
-                ('glib:get-type', boxed.get_type)]
+                ('glib:get-type', boxed.get_type),
+                ('c:symbol-prefix', boxed.c_symbol_prefix)]
 
     def _write_record(self, record, extra_attrs=[]):
         is_gtype_struct = False
@@ -442,7 +456,7 @@ and/or use gtk-doc annotations. ''')
             if record.is_gtype_struct_for:
                 is_gtype_struct = True
                 attrs.append(('glib:is-gtype-struct-for',
-                              record.is_gtype_struct_for))
+                              self._type_to_name(record.is_gtype_struct_for)))
         self._append_version(record, attrs)
         self._append_node_generic(record, attrs)
         if isinstance(record, GLibBoxed):
@@ -456,6 +470,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in sorted(record.methods):
                 self._write_method(method)
+            for method in sorted(record.static_methods):
+                self._write_static_method(method)
 
     def _write_union(self, union):
         attrs = []
@@ -476,27 +492,26 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in sorted(union.methods):
                 self._write_method(method)
+            for method in sorted(union.static_methods):
+                self._write_static_method(method)
 
     def _write_field(self, field, is_gtype_struct=False):
-        if isinstance(field, Function):
-            self._write_method(field)
-            return
-
-        if isinstance(field, Callback):
-            attrs = [('name', field.name)]
-            with self.tagcontext('field', attrs):
-                self._write_generic(field)
-                if is_gtype_struct:
-                    self._write_callback(field)
-                else:
-                    attrs = [('name', TYPE_ANY), ('c:type', 'pointer')]
-                    self.write_tag('type', attrs)
-        elif isinstance(field, Struct):
-            self._write_record(field)
-        elif isinstance(field, Union):
-            self._write_union(field)
+        if field.anonymous_node:
+            if isinstance(field.anonymous_node, Callback):
+                attrs = [('name', field.name)]
+                self._append_node_generic(field, attrs)
+                with self.tagcontext('field', attrs):
+                    self._write_callback(field.anonymous_node)
+            elif isinstance(field.anonymous_node, Record):
+                self._write_record(field.anonymous_node)
+            elif isinstance(field.anonymous_node, Union):
+                self._write_union(field.anonymous_node)
+            else:
+                raise AssertionError("Unknown field anonymous: %r" \
+                                         % (field.anonymous_node, ))
         else:
             attrs = [('name', field.name)]
+            self._append_node_generic(field, attrs)
             # Fields are assumed to be read-only
             # (see also girparser.c and generate.c)
             if not field.readable:
@@ -516,4 +531,4 @@ and/or use gtk-doc annotations. ''')
         with self.tagcontext('glib:signal', attrs):
             self._write_generic(signal)
             self._write_return_type(signal.retval)
-            self._write_parameters(signal.parameters)
+            self._write_parameters(signal, signal.parameters)
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index ad87926..85092b8 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -18,12 +18,11 @@
 # Boston, MA 02111-1307, USA.
 #
 
-from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
-                  Property, Union, Record)
+from . import ast
 
-class GLibRecord(Record):
+class GLibRecord(ast.Record):
     def __init__(self, *args, **kwargs):
-        Record.__init__(self, *args, **kwargs)
+        ast.Record.__init__(self, *args, **kwargs)
 
     @classmethod
     def from_record(cls, record):
@@ -38,10 +37,10 @@ class GLibRecord(Record):
         obj.is_gtype_struct_for = False
         return obj
 
-class GLibEnum(Enum):
+class GLibEnum(ast.Enum):
 
     def __init__(self, name, type_name, members, get_type):
-        Enum.__init__(self, name, type_name, members)
+        ast.Enum.__init__(self, name, type_name, members)
         self.ctype = type_name
         self.type_name = type_name
         self.get_type = get_type
@@ -52,10 +51,10 @@ class GLibEnum(Enum):
                                          self.get_type)
 
 
-class GLibFlags(Bitfield):
+class GLibFlags(ast.Bitfield):
 
     def __init__(self, name, type_name, members, get_type):
-        Bitfield.__init__(self, name, type_name, members)
+        ast.Bitfield.__init__(self, name, type_name, members)
         self.ctype = type_name
         self.type_name = type_name
         self.get_type = get_type
@@ -65,20 +64,21 @@ class GLibFlags(Bitfield):
                                           self.get_type)
 
 
-class GLibEnumMember(Member):
+class GLibEnumMember(ast.Member):
 
     def __init__(self, name, value, symbol, nick):
-        Member.__init__(self, name, value, symbol)
+        ast.Member.__init__(self, name, value, symbol)
         self.nick = nick
 
 
-class GLibObject(Class):
+class GLibObject(ast.Class):
 
     def __init__(self, name, parent, type_name, get_type,
-                 is_abstract, ctype=None):
-        Class.__init__(self, name, parent, is_abstract)
+                 c_symbol_prefix, is_abstract, ctype=None):
+        ast.Class.__init__(self, name, parent, is_abstract)
         self.type_name = type_name
         self.get_type = get_type
+        self.c_symbol_prefix = c_symbol_prefix
         self.fundamental = False
         self.unref_func = None
         self.ref_func = None
@@ -87,60 +87,75 @@ class GLibObject(Class):
         self.signals = []
         self.ctype = ctype or type_name
 
+    def _walk(self, callback, chain):
+        super(GLibObject, self)._walk(callback, chain)
+        for sig in self.signals:
+            sig.walk(callback, chain)
+
 
 class GLibBoxed:
 
-    def __init__(self, type_name, get_type):
+    def __init__(self, type_name, get_type, c_symbol_prefix):
         self.type_name = type_name
         self.get_type = get_type
+        self.c_symbol_prefix = c_symbol_prefix
 
 
+class GLibBoxedStruct(ast.Record, GLibBoxed):
 
-
-class GLibBoxedStruct(Record, GLibBoxed):
-
-    def __init__(self, name, type_name, get_type, ctype=None):
-        Record.__init__(self, name, ctype or type_name)
-        GLibBoxed.__init__(self, type_name, get_type)
+    def __init__(self, name, type_name, get_type, c_symbol_prefix, ctype=None):
+        ast.Record.__init__(self, name, ctype or type_name)
+        GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
 
 
-class GLibBoxedUnion(Union, GLibBoxed):
+class GLibBoxedUnion(ast.Union, GLibBoxed):
 
-    def __init__(self, name, type_name, get_type, ctype=None):
-        Union.__init__(self, name, ctype or type_name)
-        GLibBoxed.__init__(self, type_name, get_type)
+    def __init__(self, name, type_name, get_type, c_symbol_prefix, ctype=None):
+        ast.Union.__init__(self, name, ctype or type_name)
+        GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
 
 
-class GLibBoxedOther(Node, GLibBoxed):
+class GLibBoxedOther(ast.Node, GLibBoxed):
 
-    def __init__(self, name, type_name, get_type):
-        Node.__init__(self, name)
-        GLibBoxed.__init__(self, type_name, get_type)
+    def __init__(self, name, type_name, get_type, c_symbol_prefix):
+        ast.Node.__init__(self, name)
+        GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
         self.constructors = []
         self.methods = []
+        self.static_methods = []
         self.ctype = type_name
         self.doc = None
 
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
 
-class GLibInterface(Interface):
+
+class GLibInterface(ast.Interface):
 
     def __init__(self, name, parent, type_name, get_type,
-                 ctype=None):
-        Interface.__init__(self, name, parent)
+                 c_symbol_prefix, ctype=None):
+        ast.Interface.__init__(self, name, parent)
         self.type_name = type_name
         self.get_type = get_type
+        self.c_symbol_prefix = c_symbol_prefix
         self.signals = []
         self.ctype = ctype or type_name
 
+    def _walk(self, callback, chain):
+        super(GLibInterface, self)._walk(callback, chain)
+        for sig in self.signals:
+            sig.walk(callback, chain)
 
-class GLibProperty(Property):
+class GLibProperty(ast.Property):
     pass
 
 
-class GLibSignal(Node):
+class GLibSignal(ast.Callable):
 
-    def __init__(self, name, retval):
-        Node.__init__(self, name)
-        self.retval = retval
-        self.parameters = []
-        self.doc = None
+    def __init__(self, name, retval, parameters):
+        ast.Callable.__init__(self, name, retval, parameters, False)
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index 6aae64b..b5bfe35 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -20,25 +20,12 @@
 
 import os
 import sys
-import re
 import tempfile
 import shutil
 import subprocess
 
-from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
-                  Function, Interface, Member, Namespace, Node, Parameter,
-                  Property, Record, Return, Type, TypeContainer, Union,
-                  Field, VFunction, type_name_from_ctype, default_array_types,
-                  TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
-                  TYPE_LONG_LONG, TYPE_LONG_DOUBLE,
-                  Map, Varargs, type_names)
-from .transformer import Names
-from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
-                      GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
-                      GLibBoxedUnion, GLibBoxedOther, GLibRecord)
-from .utils import to_underscores, to_underscores_noprefix
-
-default_array_types['guchar*'] = TYPE_UINT8
+from . import ast
+from . import glibast
 
 # GParamFlags
 G_PARAM_READABLE = 1 << 0
@@ -50,30 +37,6 @@ G_PARAM_STATIC_NAME = 1 << 5
 G_PARAM_STATIC_NICK = 1 << 6
 G_PARAM_STATIC_BLURB = 1 << 7
 
-SYMBOL_BLACKLIST = [
-    # These ones break GError conventions
-    'g_simple_async_result_new_from_error',
-    'g_simple_async_result_set_from_error',
-    'g_simple_async_result_propagate_error',
-    'g_simple_async_result_report_error_in_idle',
-    'gtk_print_operation_get_error',
-]
-
-SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
-                           [r'\w+_marshal_[A-Z]+__', ]]
-
-GET_TYPE_OVERRIDES = {
-    # this is a special case, from glibtransforer.py:create_gobject
-    'intern': 'g_object_get_type',
-    # this is presumably a typo, should be fixed upstream
-    'g_gstring_get_type': 'g_string_get_type',
-    # this is historical cruft: there's a deprecated
-    #   #define gdk_window_get_type gdk_window_get_window_type
-    # upstream; this method can be renamed properly upstream once
-    # that deprecated alias is removed (in some future release)
-    'gdk_window_object_get_type': 'gdk_window_get_type',
-}
-
 
 class IntrospectionBinary(object):
 
@@ -97,20 +60,14 @@ class UnknownTypeError(Exception):
 
 class GLibTransformer(object):
 
-    def __init__(self, transformer, noclosure=False):
+    def __init__(self, transformer):
         self._transformer = transformer
-        self._noclosure = noclosure
-        self._namespace_name = None
-        self._names = Names()
-        self._uscore_type_names = {}
+        self._namespace = transformer.namespace
         self._binary = None
         self._get_type_functions = []
-        self._error_quark_functions = []
         self._gtype_data = {}
-        self._failed_types = {}
         self._boxed_types = {}
         self._private_internal_types = {}
-        self._validating = False
 
     # Public API
 
@@ -121,18 +78,16 @@ class GLibTransformer(object):
 
         """
 
-        namespace = self._transformer.parse()
-        self._namespace_name = namespace.name
-        self._namespace_version = namespace.version
+        self._transformer.parse()
 
         # First pass: parsing
-        for node in namespace.nodes:
-            self._parse_node(node)
-
-        # We don't want an alias for this - it's handled specially in
-        # the typelib compiler.
-        if namespace.name == 'GObject':
-            del self._names.aliases['Type']
+        for node in self._namespace.itervalues():
+            if isinstance(node, ast.Function):
+                self._initparse_function(node)
+        if self._namespace.name == 'GObject':
+            for node in self._namespace.itervalues():
+                if isinstance(node, ast.Record):
+                    self._initparse_gobject_record(node)
 
     def get_get_type_functions(self):
         return self._get_type_functions
@@ -144,148 +99,46 @@ class GLibTransformer(object):
         """Do remaining parsing steps requiring introspection binary"""
 
         # Get all the GObject data by passing our list of get_type
-        # functions to the compiled binary
-
-        self._execute_binary()
-
-        # Introspection is done from within parsing
+        # functions to the compiled binary, returning an XML blob.
+        tree = self._execute_binary_get_tree()
+        root = tree.getroot()
+        for child in root:
+            self._gtype_data[child.attrib['name']] = child
+        for child in root:
+            self._introspect_type(child)
 
-        # Second pass: pair boxed structures
-        for boxed in self._boxed_types.itervalues():
+        # Pair up boxed types and class records
+        for name, boxed in self._boxed_types.iteritems():
             self._pair_boxed_type(boxed)
-        # Third pass: delete class structures, resolve
-        # all types we now know about
-        nodes = list(self._names.names.itervalues())
-        for (ns, node) in nodes:
-            try:
-                self._resolve_node(node)
-            except KeyError, e:
-                self._transformer.log_node_warning(node,
-"""Unresolvable entry %r""" % (e, ))
-                self._remove_attribute(node.name)
-        # Another pass, since we need to have the methods parsed
-        # in order to correctly modify them after class/record
-        # pairing
-        for (ns, node) in nodes:
-            # associate GtkButtonClass with GtkButton
-            if isinstance(node, Record):
-                self._pair_class_record(node)
-        for (ns, alias) in self._names.aliases.itervalues():
-            self._resolve_alias(alias)
-
-        self._resolve_quarks()
-
-        # Our final pass replacing types
-        self._resolve_types(nodes)
-
-        # Create a new namespace with what we found
-        namespace = Namespace(self._namespace_name, self._namespace_version)
-        namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
-        for (ns, x) in self._names.names.itervalues():
-            namespace.nodes.append(x)
-        return namespace
-
-    # Private
-
-    def _add_attribute(self, node, replace=False):
-        node_name = node.name
-        if (not replace) and node_name in self._names.names:
-            return
-        self._names.names[node_name] = (None, node)
-
-    def _remove_attribute(self, name):
-        del self._names.names[name]
-
-    def _get_attribute(self, name):
-        node = self._names.names.get(name)
-        if node:
-            return node[1]
-        return None
-
-    def _lookup_node(self, name):
-        if name in type_names:
-            return None
-        node = self._get_attribute(name)
-        if node is None:
-            node = self._transformer.get_names().names.get(name)
-            if node:
-                return node[1]
-        return node
-
-    def _get_no_uscore_prefixed_name(self, type_name):
-        # Besides the straight underscore conversion, we also try
-        # removing the underscores from the namespace as a possible C
-        # mapping; e.g. it's webkit_web_view, not web_kit_web_view
-        suffix = self._transformer.remove_prefix(type_name)
-        prefix = type_name[:-len(suffix)]
-        return (prefix + '_' + to_underscores(suffix)).lower()
-
-    def _register_internal_type(self, type_name, node):
-        self._names.type_names[type_name] = (None, node)
-        uscored = to_underscores(type_name).lower()
-        # prefer the prefix of the get_type method, if there is one
-        if hasattr(node, 'get_type'):
-            uscored = GET_TYPE_OVERRIDES.get(node.get_type, node.get_type)
-            uscored = uscored[:-len('_get_type')]
-        self._uscore_type_names[uscored] = node
-
-        no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
-        # since this is a guess, don't overwrite any 'real' prefix
-        if no_uscore_prefixed not in self._uscore_type_names:
-            self._uscore_type_names[no_uscore_prefixed] = node
-
-    def _resolve_quarks(self):
-        # self._uscore_type_names is an authoritative mapping of types
-        # to underscored versions, since it is based on get_type() methods;
-        # but only covers enums that are registered as GObject enums.
-        # Create a fallback mapping based on all known enums in this module.
-        uscore_enums = {}
-        for enum in self._transformer.iter_enums():
-            type_name = enum.symbol
-            uscored = to_underscores(type_name).lower()
-
-            uscore_enums[uscored] = enum
-
-            no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
-            if no_uscore_prefixed not in uscore_enums:
-                uscore_enums[no_uscore_prefixed] = enum
-
-        for node in self._error_quark_functions:
-            short = node.symbol[:-len('_quark')]
-            if short == "g_io_error":
-                # Special case; GIOError was already taken forcing GIOErrorEnum
-                enum = self._names.type_names["GIOErrorEnum"][1]
-            else:
-                enum = self._uscore_type_names.get(short)
-                if enum is None:
-                    enum = uscore_enums.get(short)
-            if enum is not None:
-                enum.error_quark = node.symbol
-            else:
-                self._transformer.log_node_warning(node,
-"""Couldn't find corresponding enumeration""")
+        for node in self._namespace.itervalues():
+            if isinstance(node, (ast.Class, ast.Interface)):
+                self._find_class_record(node)
+
+        # Clear the _get_type functions out of the namespace;
+        # Anyone who wants them can get them from the ast.Class/Interface/Boxed
+        to_remove = []
+        for name, node in self._namespace.iteritems():
+            if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed,
+                                 glibast.GLibEnum, glibast.GLibFlags)):
+                get_type_name = node.get_type
+                if get_type_name == 'intern':
+                    continue
+                assert get_type_name, node
+                (ns, name) = self._transformer.split_csymbol(get_type_name)
+                assert ns is self._namespace
+                get_type_func = self._namespace.get(name)
+                assert get_type_func, name
+                to_remove.append(get_type_func)
+        for node in to_remove:
+            self._namespace.remove(node)
 
     # Helper functions
 
-    def _resolve_gtypename(self, gtype_name):
-        try:
-            return self._transformer.gtypename_to_giname(gtype_name,
-                                                         self._names)
-        except KeyError, e:
-            return Unresolved(gtype_name)
-
-    def _resolve_gtypename_chain(self, gtype_names):
-        """Like _resolve_gtypename, but grab the first one that resolves.
-        If none of them do, return an Unresolved for the first."""
-        for gtype_name in gtype_names:
-            try:
-                return self._transformer.gtypename_to_giname(gtype_name,
-                                                             self._names)
-            except KeyError, e:
-                continue
-        return Unresolved(gtype_names[0])
+    def _execute_binary_get_tree(self):
+        """Load the library (or executable), returning an XML
+blob containing data gleaned from GObject's primitive introspection."""
+        from xml.etree.cElementTree import parse
 
-    def _execute_binary(self):
         in_path = os.path.join(self._binary.tmpdir, 'types.txt')
         f = open(in_path, 'w')
         # TODO: Introspect GQuark functions
@@ -301,22 +154,14 @@ class GLibTransformer(object):
 
         # Invoke the binary, having written our get_type functions to types.txt
         try:
-            subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
-        except subprocess.CalledProcessError, e:
-            raise SystemExit(e)
-        self._read_introspect_dump(out_path)
-
-        # Clean up temporaries
-        shutil.rmtree(self._binary.tmpdir)
-
-    def _read_introspect_dump(self, xmlpath):
-        from xml.etree.cElementTree import parse
-        tree = parse(xmlpath)
-        root = tree.getroot()
-        for child in root:
-            self._gtype_data[child.attrib['name']] = child
-        for child in root:
-            self._introspect_type(child)
+            try:
+                subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
+            except subprocess.CalledProcessError, e:
+                # Clean up temporaries
+                raise SystemExit(e)
+            return parse(out_path)
+        finally:
+            shutil.rmtree(self._binary.tmpdir)
 
     def _create_gobject(self, node):
         type_name = 'G' + node.name
@@ -324,12 +169,11 @@ class GLibTransformer(object):
             parent_gitype = None
             symbol = 'intern'
         elif type_name == 'GInitiallyUnowned':
-            parent_type_name = 'GObject'
-            parent_gitype = self._resolve_gtypename(parent_type_name)
+            parent_gitype = ast.Type(target_giname='GLib.Object')
             symbol = 'g_initially_unowned_get_type'
         else:
             assert False
-        gnode = GLibObject(node.name, parent_gitype, type_name, symbol, True)
+        gnode = glibast.GLibObject(node.name, parent_gitype, type_name, symbol, 'object', True)
         if type_name == 'GObject':
             gnode.fields.extend(node.fields)
         else:
@@ -340,345 +184,51 @@ class GLibTransformer(object):
             # what we do here is copy all of the GObject fields into
             # GInitiallyUnowned so that struct offset computation
             # works correctly.
-            gnode.fields = self._names.names['Object'][1].fields
-        self._add_attribute(gnode)
-        self._register_internal_type(type_name, gnode)
+            gnode.fields = self._namespace.get('Object').fields
+        self._namespace.append(gnode, replace=True)
 
     # Parser
 
-    def _parse_node(self, node):
-        if isinstance(node, Enum):
-            self._parse_enum(node)
-        elif isinstance(node, Bitfield):
-            self._parse_bitfield(node)
-        elif isinstance(node, Function):
-            self._parse_function(node)
-        elif isinstance(node, Record):
-            self._parse_record(node)
-        elif isinstance(node, Callback):
-            self._parse_callback(node)
-        elif isinstance(node, Alias):
-            self._parse_alias(node)
-        elif isinstance(node, Member):
-            # FIXME: atk_misc_instance singletons
-            pass
-        elif isinstance(node, Union):
-            self._parse_union(node)
-        elif isinstance(node, Constant):
-            self._parse_constant(node)
-        else:
-            print 'GLIB Transformer: Unhandled node:', node
-
-    def _parse_alias(self, alias):
-        self._names.aliases[alias.name] = (None, alias)
-
-    def _parse_enum(self, enum):
-        self._add_attribute(enum)
-
-    def _parse_bitfield(self, enum):
-        self._add_attribute(enum)
-
-    def _parse_constant(self, constant):
-        self._add_attribute(constant)
-
-    def _parse_function(self, func):
-        if func.symbol in SYMBOL_BLACKLIST:
-            return
-        if func.symbol.startswith('_'):
-            return
-        for regexp in SYMBOL_BLACKLIST_RE:
-            if regexp.match(func.symbol):
-                return
-        if self._parse_get_type_function(func):
-            return
-        if self._parse_error_quark_function(func):
+    def _initparse_function(self, func):
+        symbol = func.symbol
+        if symbol.startswith('_'):
             return
+        elif symbol.endswith('_get_type'):
+            self._initparse_get_type_function(func)
 
-        self._add_attribute(func)
-
-    def _parse_get_type_function(self, func):
-        symbol = func.symbol
-        if not symbol.endswith('_get_type'):
-            return False
-        if self._namespace_name == 'GLib':
+    def _initparse_get_type_function(self, func):
+        if self._namespace.name == 'GLib':
             # No GObjects in GLib
             return False
-        if (self._namespace_name == 'GObject' and
-            symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
+        if (self._namespace.name == 'GObject' and
+            func.symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
             # We handle these internally, see _create_gobject
             return True
         if func.parameters:
             return False
         # GType *_get_type(void)
-        if func.retval.type.name not in ['Type',
-                                         'GType',
-                                         'GObject.Type',
-                                         'Gtk.Type']:
-            self._transformer.log_("Warning: *_get_type function returns '%r'"
-                   ", not GObject.Type") % (func.retval.type.name, )
-            return False
-
-        self._get_type_functions.append(symbol)
-        return True
-
-    def _parse_error_quark_function(self, func):
-        if not func.symbol.endswith('_error_quark'):
-            return False
-        if func.parameters:
-            return False
-        if (func.retval.type.name != 'GLib.Quark' and
-            func.retval.type.ctype != 'GQuark'):
+        rettype = func.retval.type
+        if not (rettype.is_equiv(ast.TYPE_GTYPE)
+                or rettype.target_giname == 'Gtk.Type'):
+            self._transformer.log_warning("function returns '%r', not a GType"
+                                          % (func.retval.type, ))
             return False
 
-        self._error_quark_functions.append(func)
+        self._get_type_functions.append(func.symbol)
         return True
 
-    def _name_is_internal_gtype(self, giname):
-        try:
-            node = self._get_attribute(giname)
-            return isinstance(node, (GLibObject, GLibInterface,
-                                     GLibBoxed, GLibEnum, GLibFlags))
-        except KeyError, e:
-            return False
-
-    def _parse_static_method(self, func):
-        components = func.symbol.split('_')
-        if len(components) < 2:
-            return None
-        target_klass = None
-        prefix_components = None
-        methname = None
-        for i in xrange(1, len(components)):
-            prefix_components = '_'.join(components[0:-i])
-            methname = '_'.join(components[-i:])
-            target_klass = self._uscore_type_names.get(prefix_components)
-            if target_klass and isinstance(target_klass, GLibObject):
-                break
-            target_klass = None
-        if not target_klass:
-            return None
-        self._remove_attribute(func.name)
-        func.name = methname
-        target_klass.static_methods.append(func)
-        func.is_method = True
-        return func
-
-    def _parse_method(self, func):
-        if not func.parameters:
-            return False
-        return self._parse_method_common(func, True)
-
-    def _parse_constructor(self, func):
-        return self._parse_method_common(func, False)
-
-    def _parse_method_common(self, func, is_method):
-        # Skip _get_type functions, we processed them
-        # already
-        if func.symbol.endswith('_get_type'):
-            return None
-
-        if not is_method:
-            target_arg = func.retval
-        else:
-            target_arg = func.parameters[0]
-
-        if is_method:
-            # Methods require their first arg to be a known class
-            # Look at the original C type (before namespace stripping), without
-            # pointers: GtkButton -> gtk_button_, so we can figure out the
-            # method name
-            argtype = target_arg.type.ctype.replace('*', '')
-            name = self._transformer.remove_prefix(argtype)
-            name_uscore = to_underscores_noprefix(name).lower()
-            # prefer the prefix of the _get_type method, if there is one
-            if argtype in self._names.type_names:
-                node = self._names.type_names[argtype][1]
-                if hasattr(node, 'get_type'):
-                    name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
-                                                         node.get_type)
-                    name_uscore = name_uscore[:-len('_get_type')]
-            name_offset = func.symbol.find(name_uscore + '_')
-            if name_offset < 0:
-                return None
-            prefix = func.symbol[:name_offset+len(name_uscore)]
-        else:
-            # Constructors must have _new
-            # Take everything before that as class name
-            new_idx = func.symbol.find('_new')
-            if new_idx < 0:
-                return None
-            # Constructors don't return basic types
-            derefed = self._transformer.follow_aliases(target_arg.type.name,
-                                                       self._names)
-            if derefed in type_names:
-                #print "NOTE: Rejecting constructor returning basic: %r" \
-                #    % (func.symbol, )
-                return None
-            prefix = func.symbol[:new_idx]
-
-        klass = self._uscore_type_names.get(prefix)
-        if klass is None:
-            #print "NOTE: No valid matching class for likely "+\
-            #    "method or constructor: %r" % (func.symbol, )
-            return None
-        # Enums can't have ctors or methods
-        if isinstance(klass, (GLibEnum, GLibFlags)):
-            return None
-
-        # The _uscore_type_names member holds the plain GLibBoxed
-        # object; we want to actually use the struct/record associated
-        if isinstance(klass, (Record, Union)):
-            remove_prefix = klass.symbol
-        else:
-            remove_prefix = klass.type_name
-
-        name = self._transformer.remove_prefix(remove_prefix)
-        klass = self._get_attribute(name)
-        if klass is None:
-            return
-
-        if not is_method:
-            # Interfaces can't have constructors, punt to global scope
-            if isinstance(klass, GLibInterface):
-                #print "NOTE: Rejecting constructor for"+\
-                #    " interface type: %r" % (func.symbol, )
-                return None
-            # TODO - check that the return type is a subclass of the
-            # class from the prefix
-            # But for now, ensure that constructor returns are always
-            # the most concrete class
-            name = self._transformer.remove_prefix(remove_prefix)
-            func.retval.type = Type(name, func.retval.type.ctype)
-
-        self._remove_attribute(func.name)
-        # Strip namespace and object prefix: gtk_window_new -> new
-        func.name = func.symbol[len(prefix)+1:]
-        if is_method:
-            # We don't need the "this" parameter
-            del func.parameters[0]
-            klass.methods.append(func)
-            func.is_method = True
-        else:
-            klass.constructors.append(func)
-        return func
-
-    def _parse_record(self, record):
-        # This is a hack, but GObject is a rather fundamental piece so.
-        internal_names = ["Object", 'InitiallyUnowned']
-        g_internal_names = ["G" + x for x in internal_names]
-        if (self._namespace_name == 'GObject' and
-            record.name in internal_names):
+    def _initparse_gobject_record(self, record):
+        # Special handling for when we're parsing GObject
+        internal_names = ("Object", 'InitiallyUnowned')
+        if record.name in internal_names:
             self._create_gobject(record)
             return
-        elif record.name in g_internal_names:
-            # Avoid duplicates
-            return
         if record.name == 'InitiallyUnownedClass':
-            record.fields = self._names.names['ObjectClass'][1].fields
-        node = self._names.names.get(record.name)
-        if node is None:
-            self._add_attribute(record, replace=True)
-            self._register_internal_type(record.symbol, record)
-            return
-        (ns, node) = node
-        node.fields = record.fields[:]
-
-    def _parse_union(self, union):
-        node = self._names.names.get(union.name)
-        if node is None:
-            self._add_attribute(union, replace=True)
-            self._register_internal_type(union.symbol, union)
-            return
-        (ns, node) = node
-        node.fields = union.fields[:]
+            record.fields = self._namespace.get('ObjectClass').fields
+        self._namespace.append(record, replace=True)
 
-    def _parse_callback(self, callback):
-        self._add_attribute(callback)
-
-    def _strip_class_suffix(self, name):
-        if (name.endswith('Class') or
-            name.endswith('Iface')):
-            return name[:-5]
-        elif name.endswith('Interface'):
-            return name[:-9]
-        else:
-            return name
-
-    def _arg_is_failed(self, param):
-        ctype = self._transformer.ctype_of(param).replace('*', '')
-        uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
-        if uscored in self._failed_types:
-            print "Warning: failed type: %r" % (param, )
-            return True
-        return False
-
-    def _pair_class_record(self, maybe_class):
-        name = self._strip_class_suffix(maybe_class.name)
-        if name == maybe_class.name:
-            return
-
-        class_struct = maybe_class
-        if self._arg_is_failed(class_struct):
-            print "WARNING: deleting no-type %r" % (class_struct.name, )
-            del self._names.names[class_struct.name]
-            return
-
-        pair_class = self._get_attribute(name)
-        if (not pair_class or
-            not isinstance(pair_class, (GLibObject, GLibInterface))):
-            return
-
-        # Object class fields are assumed to be read-only
-        # (see also _introspect_object and transformer.py)
-        for field in maybe_class.fields:
-            if isinstance(field, Field):
-                field.writable = False
-
-        # Loop through fields to determine which are virtual
-        # functions and which are signal slots by
-        # assuming everything that doesn't share a name
-        # with a known signal is a virtual slot.
-        for field in maybe_class.fields:
-            if not isinstance(field, Callback):
-                continue
-            # Check the first parameter is the object
-            if len(field.parameters) == 0:
-                continue
-            firstparam_type = field.parameters[0].type
-            if firstparam_type != pair_class:
-                continue
-            # Also double check we don't have a signal with this
-            # name.
-            matched_signal = False
-            for signal in pair_class.signals:
-                if signal.name.replace('-', '_') == field.name:
-                    matched_signal = True
-                    break
-            if matched_signal:
-                continue
-            vfunc = VFunction.from_callback(field)
-            vfunc.inherit_file_positions(field)
-            pair_class.virtual_methods.append(vfunc)
-
-        # Take the set of virtual methods we found, and try
-        # to pair up with any matching methods using the
-        # name+signature.
-        for vfunc in pair_class.virtual_methods:
-            for method in pair_class.methods:
-                if (method.name != vfunc.name or
-                    method.retval != vfunc.retval or
-                    method.parameters != vfunc.parameters):
-                    continue
-                vfunc.invoker = method
-
-        gclass_struct = GLibRecord.from_record(class_struct)
-        self._remove_attribute(class_struct.name)
-        self._add_attribute(gclass_struct, True)
-        pair_class.glib_type_struct = gclass_struct
-        pair_class.inherit_file_positions(class_struct)
-        gclass_struct.is_gtype_struct_for = name
-
-    # Introspection
+    # Introspection over the data we get from the dynamic
+    # GObject/GType system out of the binary
 
     def _introspect_type(self, xmlnode):
         if xmlnode.tag in ('enum', 'flags'):
@@ -700,17 +250,24 @@ class GLibTransformer(object):
             # Keep the name closer to what we'd take from C by default;
             # see http://bugzilla.gnome.org/show_bug.cgi?id=575613
             name = member.attrib['nick'].replace('-', '_')
-            members.append(GLibEnumMember(name,
+            members.append(glibast.GLibEnumMember(name,
                                           member.attrib['value'],
                                           member.attrib['name'],
                                           member.attrib['nick']))
 
-        klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
+        klass = (glibast.GLibFlags if node.tag == 'flags' else glibast.GLibEnum)
         type_name = node.attrib['name']
-        enum_name = self._transformer.remove_prefix(type_name)
+        enum_name = self._transformer.strip_identifier_or_warn(type_name, fatal=True)
         node = klass(enum_name, type_name, members, node.attrib['get-type'])
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
+
+    def _split_type_and_symbol_prefix(self, xmlnode):
+        """Infer the C symbol prefix from the _get_type function."""
+        get_type = xmlnode.attrib['get-type']
+        (ns, name) = self._transformer.split_csymbol(get_type)
+        assert ns is self._namespace
+        assert name.endswith('_get_type')
+        return (get_type, name[:-len('_get_type')])
 
     def _introspect_object(self, xmlnode):
         type_name = xmlnode.attrib['name']
@@ -719,58 +276,53 @@ class GLibTransformer(object):
         # to skip it
         if type_name == 'GObject':
             return
-        # Get a list of parents here; some of them may be hidden, and what
-        # we really want to do is use the most-derived one that we know of.
-        #
-        parent_type_names = xmlnode.attrib['parents'].split(',')
-        parent_gitype = self._resolve_gtypename_chain(parent_type_names)
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
-        node = GLibObject(
-            self._transformer.remove_prefix(type_name),
-            parent_gitype,
+        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+        node = glibast.GLibObject(
+            self._transformer.strip_identifier_or_warn(type_name, fatal=True),
+            None,
             type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+            get_type, c_symbol_prefix, is_abstract)
+        self._parse_parents(xmlnode, node)
         self._introspect_properties(node, xmlnode)
         self._introspect_signals(node, xmlnode)
         self._introspect_implemented_interfaces(node, xmlnode)
 
         self._add_record_fields(node)
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
 
     def _introspect_interface(self, xmlnode):
         type_name = xmlnode.attrib['name']
-        node = GLibInterface(
-            self._transformer.remove_prefix(type_name),
+        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+        node = glibast.GLibInterface(
+            self._transformer.strip_identifier_or_warn(type_name, fatal=True),
             None,
-            type_name, xmlnode.attrib['get-type'])
+            type_name, get_type, c_symbol_prefix)
         self._introspect_properties(node, xmlnode)
         self._introspect_signals(node, xmlnode)
         for child in xmlnode.findall('prerequisite'):
             name = child.attrib['name']
-            prereq = self._resolve_gtypename(name)
+            prereq = self._transformer.create_type_from_user_string(name)
             node.prerequisites.append(prereq)
         # GtkFileChooserEmbed is an example of a private interface, we
         # just filter them out
         if xmlnode.attrib['get-type'].startswith('_'):
-            print "NOTICE: Marking %s as internal type" % (type_name, )
             self._private_internal_types[type_name] = node
         else:
-            self._add_attribute(node, replace=True)
-            self._register_internal_type(type_name, node)
+            self._namespace.append(node, replace=True)
 
     def _introspect_boxed(self, xmlnode):
         type_name = xmlnode.attrib['name']
         # This one doesn't go in the main namespace; we associate it with
         # the struct or union
-        node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
+        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+        node = glibast.GLibBoxed(type_name, get_type, c_symbol_prefix)
         self._boxed_types[node.type_name] = node
-        self._register_internal_type(type_name, node)
 
     def _introspect_implemented_interfaces(self, node, xmlnode):
         gt_interfaces = []
         for interface in xmlnode.findall('implements'):
-            gitype = self._resolve_gtypename(interface.attrib['name'])
+            gitype = self._transformer.create_type_from_user_string(interface.attrib['name'])
             gt_interfaces.append(gitype)
         node.interfaces = gt_interfaces
 
@@ -782,9 +334,9 @@ class GLibTransformer(object):
             writable = (flags & G_PARAM_WRITABLE) != 0
             construct = (flags & G_PARAM_CONSTRUCT) != 0
             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
-            node.properties.append(Property(
+            node.properties.append(ast.Property(
                 pspec.attrib['name'],
-                type_name_from_ctype(ctype),
+                self._transformer.create_type_from_user_string(ctype),
                 readable, writable, construct, construct_only,
                 ctype,
                 ))
@@ -793,23 +345,31 @@ class GLibTransformer(object):
     def _introspect_signals(self, node, xmlnode):
         for signal_info in xmlnode.findall('signal'):
             rctype = signal_info.attrib['return']
-            rtype = Type(self._transformer.parse_ctype(rctype), rctype)
-            return_ = Return(rtype, signal_info.attrib['return'])
-            return_.transfer = PARAM_TRANSFER_FULL
-            signal = GLibSignal(signal_info.attrib['name'], return_)
+            rtype = self._transformer.create_type_from_user_string(rctype)
+            return_ = ast.Return(rtype)
+            parameters = []
             for i, parameter in enumerate(signal_info.findall('param')):
                 if i == 0:
-                    name = 'object'
+                    argname = 'object'
                 else:
-                    name = 'p%s' % (i-1, )
+                    argname = 'p%s' % (i-1, )
                 pctype = parameter.attrib['type']
-                ptype = Type(self._transformer.parse_ctype(pctype), pctype)
-                param = Parameter(name, ptype)
-                param.transfer = 'none'
-                signal.parameters.append(param)
+                ptype = self._transformer.create_type_from_user_string(pctype)
+                param = ast.Parameter(argname, ptype)
+                param.transfer = ast.PARAM_TRANSFER_NONE
+                parameters.append(param)
+            signal = glibast.GLibSignal(signal_info.attrib['name'], return_, parameters)
             node.signals.append(signal)
         node.signals = node.signals
 
+    def _parse_parents(self, xmlnode, node):
+        if 'parents' in xmlnode.attrib:
+            parent_types = map(lambda s: self._transformer.create_type_from_user_string(s),
+                               xmlnode.attrib['parents'].split(','))
+        else:
+            parent_types = []
+        node.parent_chain = parent_types
+
     def _introspect_fundamental(self, xmlnode):
         # We only care about types that can be instantiatable, other
         # fundamental types such as the Clutter.Fixed/CoglFixed registers
@@ -819,387 +379,78 @@ class GLibTransformer(object):
             return
 
         type_name = xmlnode.attrib['name']
-
-        # Get a list of parents here; some of them may be hidden, and what
-        # we really want to do is use the most-derived one that we know of.
-        if 'parents' in xmlnode.attrib:
-            parent_type_names = xmlnode.attrib['parents'].split(',')
-            parent_gitype = self._resolve_gtypename_chain(parent_type_names)
-        else:
-            parent_gitype = None
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
-        node = GLibObject(
-            self._transformer.remove_prefix(type_name),
-            parent_gitype,
+        (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+        node = glibast.GLibObject(
+            self._transformer.strip_identifier_or_warn(type_name, fatal=True),
+            None,
             type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+            get_type, c_symbol_prefix, is_abstract)
+        self._parse_parents(xmlnode, node)
         node.fundamental = True
         self._introspect_implemented_interfaces(node, xmlnode)
 
         self._add_record_fields(node)
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
 
     def _add_record_fields(self, node):
         # add record fields
-        record = self._get_attribute(node.name)
-        if record is None:
+        record = self._namespace.get(node.name)
+        if not isinstance(record, ast.Record):
             return
         node.fields = record.fields
         for field in node.fields:
-            if isinstance(field, Field):
+            if isinstance(field, ast.Field):
                 # Object instance fields are assumed to be read-only
-                # (see also _pair_class_record and transformer.py)
+                # (see also _find_class_record and transformer.py)
                 field.writable = False
 
     def _pair_boxed_type(self, boxed):
-        name = self._transformer.remove_prefix(boxed.type_name)
-        pair_node = self._get_attribute(name)
+        name = self._transformer.strip_identifier_or_warn(boxed.type_name, fatal=True)
+        pair_node = self._namespace.get(name)
         if not pair_node:
-            boxed_item = GLibBoxedOther(name, boxed.type_name,
-                                        boxed.get_type)
-        elif isinstance(pair_node, Record):
-            boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
-                                         boxed.get_type)
+            boxed_item = glibast.GLibBoxedOther(name, boxed.type_name,
+                                        boxed.get_type,
+                                        boxed.c_symbol_prefix)
+        elif isinstance(pair_node, ast.Record):
+            boxed_item = glibast.GLibBoxedStruct(pair_node.name, boxed.type_name,
+                                         boxed.get_type,
+                                         boxed.c_symbol_prefix)
             boxed_item.inherit_file_positions(pair_node)
             boxed_item.fields = pair_node.fields
-        elif isinstance(pair_node, Union):
-            boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
-                                         boxed.get_type)
+        elif isinstance(pair_node, ast.Union):
+            boxed_item = glibast.GLibBoxedUnion(pair_node.name, boxed.type_name,
+                                        boxed.get_type,
+                                        boxed.c_symbol_prefix)
             boxed_item.inherit_file_positions(pair_node)
             boxed_item.fields = pair_node.fields
         else:
             return False
-        self._add_attribute(boxed_item, replace=True)
-
-    # Node walking
-
-    def _walk(self, node, callback, chain):
-        if not isinstance(node, Node):
-            return
-        if not callback(node, chain):
-            return
-        chain.append(node)
-        def _subwalk(subnode):
-            self._walk(subnode, callback, chain)
-        if isinstance(node, (Callback, Callable)):
-            _subwalk(node.retval)
-            for parameter in node.parameters:
-                _subwalk(parameter)
-        elif isinstance(node, (Array, List)):
-            _subwalk(node.element_type)
-        elif isinstance(node, Map):
-            _subwalk(node.key_type)
-            _subwalk(node.value_type)
-        elif isinstance(node, Bitfield):
-            pass
-        elif isinstance(node, Record):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for func in node.methods:
-                _subwalk(func)
-        elif isinstance(node, Field):
-            _subwalk(node.type)
-        elif isinstance(node, Class):
-            for meth in node.methods:
-                _subwalk(meth)
-            for meth in node.virtual_methods:
-                _subwalk(meth)
-            for meth in node.static_methods:
-                _subwalk(meth)
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for prop in node.properties:
-                _subwalk(prop)
-            for field in node.fields:
-                _subwalk(field)
-        elif isinstance(node, Interface):
-            for meth in node.methods:
-                _subwalk(meth)
-            for meth in node.virtual_methods:
-                _subwalk(meth)
-            for prop in node.properties:
-                _subwalk(prop)
-            for field in node.fields:
-                _subwalk(field)
-        elif isinstance(node, Constant):
-            _subwalk(node.type)
-        elif isinstance(node, Union):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for meth in node.methods:
-                _subwalk(meth)
-        elif isinstance(node, GLibBoxed):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for meth in node.methods:
-                _subwalk(meth)
-
-        if isinstance(node, (GLibObject, GLibInterface)):
-            for sig in node.signals:
-                _subwalk(sig)
+        self._namespace.append(boxed_item, replace=True)
 
-        chain.pop()
-
-    # Resolver
-
-    def _resolve_type_name(self, type_name, ctype=None):
-        # Workaround glib bug #548689, to be included in 2.18.0
-        if type_name == "GParam":
-            type_name = "GObject.ParamSpec"
-        res = self._transformer.resolve_type_name_full
-        try:
-            return res(type_name, ctype, self._names)
-        except KeyError, e:
-            return self._transformer.resolve_type_name(type_name, ctype)
-
-    def _resolve_param_type(self, ptype, **kwargs):
-        # Workaround glib bug #548689, to be included in 2.18.0
-        if ptype.name == "GParam":
-            ptype.name = "GObject.ParamSpec"
-        elif ptype.name == "GObject.Strv":
-            return Array(None, ptype.ctype, Type('utf8'))
-
-        return self._transformer.resolve_param_type_full(ptype,
-                                                         self._names,
-                                                         **kwargs)
-
-    def _resolve_node(self, node):
-        if isinstance(node, Function):
-            self._resolve_function_toplevel(node)
-
-        elif isinstance(node, Callback):
-            self._resolve_function(node)
-        elif isinstance(node, GLibObject):
-            self._resolve_glib_object(node)
-        elif isinstance(node, GLibInterface):
-            self._resolve_glib_interface(node)
-        elif isinstance(node, Record):
-            self._resolve_record(node)
-        elif isinstance(node, Union):
-            self._resolve_union(node)
-        elif isinstance(node, Alias):
-            self._resolve_alias(node)
-
-    def _resolve_function_toplevel(self, func):
-        for parser in [self._parse_constructor,
-                       self._parse_method,
-                       self._parse_static_method]:
-            newfunc = parser(func)
-            if newfunc:
-                self._resolve_function(newfunc)
-                return
-        self._resolve_function(func)
-
-    def _resolve_record(self, node):
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _resolve_union(self, node):
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _force_resolve(self, item, allow_unknown=False):
-        if isinstance(item, Unresolved):
-            if item.target in self._private_internal_types:
-                return None
-            try:
-                return self._transformer.gtypename_to_giname(item.target,
-                                                             self._names)
-            except KeyError, e:
-                if allow_unknown:
-                    self._transformer.log_warning(
-"""Skipping unknown interface %s""" % (item.target, ))
-                    return None
-                else:
-                    raise
-        if item in self._private_internal_types:
-            return None
-        return item
-
-    def _resolve_glib_interface(self, node):
-        node.parent = self._force_resolve(node.parent)
-        self._resolve_methods(node.methods)
-        self._resolve_properties(node.properties, node)
-        self._resolve_signals(node.signals)
-        node.prerequisites = filter(None,
-            [self._force_resolve(x, allow_unknown=True)
-             for x in node.prerequisites])
-
-    def _resolve_glib_object(self, node):
-        # If we can't find the parent class, just drop back to GObject.
-        # This supports hidden parent classes.
-        # http://bugzilla.gnome.org/show_bug.cgi?id=561360
-        try:
-            node.parent = self._force_resolve(node.parent)
-        except KeyError, e:
-            #print ("WARNING: Parent %r of class %r" +\
-            #       " not found; using GObject") % (node.parent.target,
-            #                                       node.name)
-            node.parent = self._transformer.gtypename_to_giname("GObject",
-                                                                self._names)
-        node.interfaces = filter(None,
-            [self._force_resolve(x, allow_unknown=True)
-                                    for x in node.interfaces])
-        self._resolve_constructors(node.constructors)
-        self._resolve_methods(node.methods)
-        self._resolve_methods(node.static_methods)
-        self._resolve_properties(node.properties, node)
-        self._resolve_signals(node.signals)
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _resolve_glib_boxed(self, node):
-        self._resolve_constructors(node.constructors)
-        self._resolve_methods(node.methods)
-
-    def _resolve_constructors(self, constructors):
-        for ctor in constructors:
-            self._resolve_function(ctor)
-
-    def _resolve_methods(self, methods):
-        for method in methods:
-            self._resolve_function(method)
-
-    def _resolve_signals(self, signals):
-        for signal in signals:
-            self._resolve_function(signal)
-
-    def _resolve_properties(self, properties, context):
-        failed = []
-        for prop in properties:
-            try:
-                self._resolve_property(prop)
-            except KeyError, e:
-                failed.append(prop)
-        for fail in failed:
-            #print ("WARNING: Deleting object property %r (of %r) "
-            #       "with unknown type") % (fail, context)
-            properties.remove(fail)
-
-    def _resolve_property(self, prop):
-        prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
-
-    def _adjust_throws(self, func):
-        if func.parameters == []:
-            return
-
-        last_param = func.parameters.pop()
-
-        # Checking type.name=='GLib.Error' generates false positives
-        # on methods that take a 'GError *'
-        if last_param.type.ctype == 'GError**':
-            func.throws = True
+    def _strip_class_suffix(self, name):
+        if (name.endswith('Class') or
+            name.endswith('Iface')):
+            return name[:-5]
+        elif name.endswith('Interface'):
+            return name[:-9]
         else:
-            func.parameters.append(last_param)
-
-    def _resolve_function(self, func):
-        self._resolve_parameters(func.parameters)
-        func.retval.type = self._resolve_param_type(func.retval.type)
-        self._adjust_throws(func)
-
-    def _resolve_parameters(self, parameters):
-        for parameter in parameters:
-            parameter.type = self._resolve_param_type(parameter.type)
+            return None
 
-    def _resolve_field(self, field):
-        if isinstance(field, Callback):
-            self._resolve_function(field)
-        elif isinstance(field, Record): # non-typedef'd struct
-            self._resolve_record(field)
-        elif isinstance(field, Union): # non-typedef'd union
-            self._resolve_union(field)
+    def _find_class_record(self, cls):
+        pair_record = None
+        if isinstance(cls, ast.Class):
+            pair_record = self._namespace.get(cls.name + 'Class')
         else:
-            field.type = self._resolve_param_type(field.type)
-
-    def _resolve_alias(self, alias):
-        alias.target = self._resolve_type_name(alias.target, alias.target)
-
-    def _resolve_types(self, nodes):
-        nodes = list(self._names.names.itervalues())
-        i = 0
-        self._validating = True
-        while True:
-            initlen = len(nodes)
-
-            nodes = list(self._names.names.itervalues())
-            for node in nodes:
-                try:
-                    self._resolve_node(node)
-                except UnknownTypeError, e:
-                    print "WARNING: %s: Deleting %r" % (e, node)
-                    self._remove_attribute(node.name)
-            if len(nodes) == initlen:
-                break
-            i += 1
-        self._validating = False
-
-    # Validation
-
-    def _interface_vfunc_check(self, node, stack):
-        if isinstance(node, GLibInterface):
-            for vfunc in node.virtual_methods:
-                if not vfunc.invoker:
-                    self._transformer.log_node_warning(vfunc,
-"""Virtual function %r has no known invoker""" % (vfunc.name, ),
-                    context=node)
-
-    def _is_unannotated_list(self, node):
-        # Already annotated
-        if isinstance(node.type, List):
-            return False
-        if (node.type.name == 'GLib.List' or
-            node.type.name == 'GLib.SList'):
-            return True
-        if (self._transformer._namespace.name == 'GLib' and
-            (node.type.name == 'List' or
-             node.type.name == 'SList')):
-            return True
-        return False
-
-    def _introspectable_analysis(self, node, stack):
-        if isinstance(node, TypeContainer):
-            parent = stack[-1]
-            if node.type.name in (TYPE_LONG_LONG, TYPE_LONG_DOUBLE):
-                parent.introspectable = False
-            elif isinstance(node.type, Varargs):
-                parent.introspectable = False
-            elif self._is_unannotated_list(node):
-                if isinstance(node, Parameter):
-                    self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on argument %r""" % (node.name, ),
-                                                    context=parent)
-                else:
-                    self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on return value""", context=parent)
-                parent.introspectable = False
-
-    def _analyze_node(self, node, stack):
-        if node.skip:
-            return False
-        # Combine one-pass checks here
-        self._interface_vfunc_check(node, stack)
-        # Our first pass for scriptability
-        self._introspectable_analysis(node, stack)
-        return True
-
-    def _introspectable_pass2(self, node, stack):
-        if node.skip:
-            return False
-        # In the second introspectable pass, we propagate introspectablity;
-        # for example, a varargs callback as an argument to a function
-        # makes the whole function unintrospectable
-        if isinstance(node, TypeContainer):
-            parent = stack[-1]
-            target = self._lookup_node(node.type.name)
-            if target and not target.introspectable:
-                parent.introspectable = False
-        return True
+            for suffix in ('Iface', 'Interface'):
+                pair_record = self._namespace.get(cls.name + suffix)
+                if pair_record:
+                    break
+        if not (pair_record and isinstance(pair_record, ast.Record)):
+            return
 
-    # This function is called at the very end, before we hand back the
-    # completed namespace to the writer.  Add static analysis checks here.
-    def final_analyze(self):
-        for (ns, node) in self._names.names.itervalues():
-            self._walk(node, self._analyze_node, [])
-        for (ns, node) in self._names.names.itervalues():
-            self._walk(node, self._introspectable_pass2, [])
+        gclass_struct = glibast.GLibRecord.from_record(pair_record)
+        self._namespace.append(gclass_struct, replace=True)
+        cls.glib_type_struct = gclass_struct.create_type()
+        cls.inherit_file_positions(pair_record)
+        gclass_struct.is_gtype_struct_for = cls.create_type()
diff --git a/giscanner/primarytransformer.py b/giscanner/primarytransformer.py
new file mode 100644
index 0000000..d26851d
--- /dev/null
+++ b/giscanner/primarytransformer.py
@@ -0,0 +1,928 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import re
+
+from . import ast
+from . import glibast
+from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
+                               TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
+                               TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
+                               TAG_GET_VALUE_FUNC)
+from .annotationparser import (OPT_ALLOW_NONE,
+                               OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
+                               OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
+                               OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
+                               OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
+                               OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED)
+from .annotationparser import AnnotationParser
+from .utils import to_underscores, to_underscores_noprefix
+
+class PrimaryTransformer(object):
+
+    def __init__(self, transformer, blocks):
+        self._transformer = transformer
+        self._blocks = blocks
+        self._namespace = transformer.namespace
+        self._uscore_type_names = {}
+
+    # Public API
+
+    def transform(self):
+        # We have a rough tree which should have most of of the types
+        # we know about.  Let's attempt closure; walk over all of the
+        # Type() types and see if they match up with something.
+        self._namespace.walk(self._pass_type_resolution)
+
+        # Determine some default values for transfer etc.
+        # based on the current tree.
+        self._namespace.walk(self._pass_callable_defaults)
+
+        # Read in most annotations now.
+        self._namespace.walk(self._pass_read_annotations)
+
+        # Now that we've possibly seen more types from annotations,
+        # do another type resolution pass.
+        self._namespace.walk(self._pass_type_resolution)
+
+        # Generate a reverse mapping "bar_baz" -> BarBaz
+        for node in self._namespace.itervalues():
+            if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed)):
+                self._uscore_type_names[node.c_symbol_prefix] = node
+            elif isinstance(node, (ast.Record, ast.Union)):
+                uscored = to_underscores_noprefix(node.name).lower()
+                self._uscore_type_names[uscored] = node
+
+        for node in list(self._namespace.itervalues()):
+            if isinstance(node, ast.Function):
+                # Discover which toplevel functions are actually methods
+                self._pair_function(node)
+            if isinstance(node, (ast.Class, ast.Interface)):
+                self._pair_class_virtuals(node)
+
+        # Some annotations need to be post function pairing
+        self._namespace.walk(self._pass_read_annotations2)
+
+        # Another type resolution pass after we've parsed virtuals, etc.
+        self._namespace.walk(self._pass_type_resolution)
+
+        self._namespace.walk(self._pass3)
+
+        # TODO - merge into pass3
+        self._resolve_quarks()
+
+    # Private
+
+    def _is_gi_subclass(self, typeval, supercls_type):
+        cls = self._transformer.lookup_typenode(typeval)
+        assert cls, str(typeval)
+        supercls = self._transformer.lookup_typenode(supercls_type)
+        assert supercls
+        if cls is supercls:
+            return True
+        if cls.parent:
+            return self._is_gi_subclass(cls.parent, supercls_type)
+        return False
+
+    def _get_validate_parameter_name(self, parent, param_name, origin):
+        try:
+            param = parent.get_parameter(param_name)
+        except ValueError, e:
+            param = None
+        if param is None:
+            if isinstance(origin, ast.Parameter):
+                origin_name = 'parameter %s' % (origin.argname, )
+            else:
+                origin_name = 'return value'
+            self._transformer.log_node_warning(parent,
+                "can't find parameter %s referenced by %s of %r"
+                % (param_name, origin_name, parent.name), fatal=True)
+
+        return param.argname
+
+    def _apply_annotations_function(self, node, chain):
+        block = self._blocks.get(node.symbol)
+        self._apply_annotations_callable(node, chain, block)
+        if block:
+            rename_to = block.get(TAG_RENAME_TO)
+            if rename_to:
+                rename_to = rename_to.value
+                target = self._namespace.get_by_symbol(rename_to)
+                if not target:
+                    self._transformer.log_node_warning(node,
+"Can't find symbol %r referenced by Rename annotation" % (rename_to, ))
+                elif target.shadowed_by:
+                    self._transformer.log_node_warning(node,
+"Function %r already shadowed by %r, can't overwrite with %r" % (target.symbol,
+                                                                 target.shadowed_by,
+                                                                 rename_to))
+                elif target.shadows:
+                    self._transformer.log_node_warning(node,
+"Function %r already shadows %r, can't multiply shadow with %r" % (target.symbol,
+                                                                   target.shadows,
+                                                                   rename_to))
+                else:
+                    target.shadows = node.symbol
+                    node.shadowed_by = target.symbol
+
+    def _pass_callable_defaults(self, node, chain):
+        if isinstance(node, (ast.Callable, glibast.GLibSignal)):
+            for param in node.parameters:
+                if param.transfer is None:
+                    param.transfer = self._get_transfer_default(node, param)
+            if node.retval.transfer is None:
+                node.retval.transfer = self._get_transfer_default(node, node.retval)
+        return True
+
+    def _pass_read_annotations(self, node, chain):
+        if not node.namespace:
+            return False
+        if isinstance(node, ast.Function):
+            self._apply_annotations_function(node, chain)
+        if isinstance(node, ast.Callback):
+            block = self._blocks.get(node.c_name)
+            self._apply_annotations_callable(node, chain, block)
+        if isinstance(node, (ast.Class, ast.Interface, ast.Record,
+                             ast.Union, ast.Enum, ast.Bitfield,
+                             ast.Callback)):
+            block = self._blocks.get(node.c_name)
+            self._apply_annotations_annotated(node, block)
+        if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+            for field in node.fields:
+                self._blocks.get('%s::%s' % (node.c_name, field.name))
+                self._apply_annotations_field(node, block, field)
+        if isinstance(node, (ast.Class, ast.Interface)):
+            for prop in node.properties:
+                self._apply_annotations_property(node, prop)
+            for sig in node.signals:
+                self._apply_annotations_signal(node, sig)
+        if isinstance(node, ast.Class):
+            block = self._blocks.get(node.c_name)
+            if block:
+                tag = block.get(TAG_UNREF_FUNC)
+                node.unref_func = tag.value if tag else None
+                tag = block.get(TAG_REF_FUNC)
+                node.ref_func = tag.value if tag else None
+                tag = block.get(TAG_SET_VALUE_FUNC)
+                node.set_value_func = tag.value if tag else None
+                tag = block.get(TAG_GET_VALUE_FUNC)
+                node.get_value_func = tag.value if tag else None
+        return True
+
+    def _adjust_container_type(self, parent, node, options):
+        has_element_type = OPT_ELEMENT_TYPE in options
+        has_array = OPT_ARRAY in options
+
+        if has_array:
+            self._apply_annotations_array(parent, node, options)
+        elif has_element_type:
+            self._apply_annotations_element_type(parent, node, options)
+
+    def _resolve(self, type_str, orig_node=None):
+        def grab_one(type_str, resolver, top_combiner, combiner):
+            """Return a complete type, and the trailing string part after it.
+            Use resolver() on each identifier, and combiner() on the parts of
+            each complete type. (top_combiner is used on the top-most type.)"""
+            bits = re.split(r'([,<>])', type_str, 1)
+            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+            args = [resolver(first)]
+            if sep == '<':
+                while sep != '>':
+                    next, rest = grab_one(rest, resolver, combiner, combiner)
+                    args.append(next)
+                    sep, rest = rest[0], rest[1:]
+            else:
+                rest = sep + rest
+            return top_combiner(*args), rest
+        def resolver(ident):
+            res = self._transformer.create_type_from_user_string(ident)
+            return res
+        def combiner(base, *rest):
+            if not rest:
+                return base
+            if isinstance(base, ast.List) and len(rest) == 1:
+                return ast.List(base.name, *rest)
+            if isinstance(base, ast.Map) and len(rest) == 2:
+                return ast.Map(*rest)
+            self._transformer.log_warning(
+"Too many parameters in type specification %r" % (type_str, ))
+            return base
+        def top_combiner(base, *rest):
+            if orig_node is not None:
+                base.is_const = orig_node.is_const
+            return combiner(base, *rest)
+
+        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+        if rest:
+            self._transformer.log_warning(
+"Trailing components in type specification %r" % (type_str, ))
+        return result
+
+    def _apply_annotations_array(self, parent, node, options):
+        array_opt = options.get(OPT_ARRAY)
+        if array_opt:
+            array_values = array_opt.all()
+        else:
+            array_values = {}
+
+        element_type = options.get(OPT_ELEMENT_TYPE)
+        if element_type is not None:
+            element_type_node = self._resolve(element_type.one())
+        elif isinstance(node.type, ast.Array):
+            element_type_node = node.type.element_type
+        else:
+            # We're assuming here that Foo* with an (array) annotation
+            # and no (element-type) means array of Foo
+            element_type_node = node.type.clone()
+            # Explicitly erase ctype since it's no longer valid
+            element_type_node.ctype = None
+
+        if isinstance(node.type, ast.Array):
+            array_type = node.type.array_type
+        else:
+            array_type = None
+        container_type = ast.Array(array_type, element_type_node,
+                               ctype=node.type.ctype,
+                               is_const=node.type.is_const)
+        if OPT_ARRAY_ZERO_TERMINATED in array_values:
+            container_type.zeroterminated = array_values.get(
+                OPT_ARRAY_ZERO_TERMINATED) == '1'
+        length = array_values.get(OPT_ARRAY_LENGTH)
+        if length is not None:
+            paramname = self._get_validate_parameter_name(parent, length, node)
+            if paramname:
+                param = parent.get_parameter(paramname)
+                param.direction = node.direction
+                if param.direction == ast.PARAM_DIRECTION_OUT:
+                    param.transfer = ast.PARAM_TRANSFER_FULL
+                container_type.length_param_name = param.argname
+        fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
+        if fixed:
+            container_type.size = int(fixed)
+        node.type = container_type
+
+    def _apply_annotations_element_type(self, parent, node, options):
+        element_type_opt = options.get(OPT_ELEMENT_TYPE)
+        element_type = element_type_opt.flat()
+        if isinstance(node.type, ast.List):
+            assert len(element_type) == 1
+            node.type.element_type = self._resolve(element_type[0])
+        elif isinstance(node.type, ast.Map):
+            assert len(element_type) == 2
+            node.type.key_type = self._resolve(element_type[0])
+            node.type.value_type = self._resolve(element_type[1])
+        elif isinstance(node.type, ast.Array):
+            node.type.element_type = self._resolve(element_type[0])
+        else:
+            self._transformer.log_node_warning(parent,
+                "Unknown container %r for element-type annotation" % (node.type, ))
+
+    def _get_transfer_default_param(self, parent, node):
+        if node.direction in [ast.PARAM_DIRECTION_INOUT,
+                              ast.PARAM_DIRECTION_OUT]:
+            if node.caller_allocates:
+                return ast.PARAM_TRANSFER_NONE
+            return ast.PARAM_TRANSFER_FULL
+        return ast.PARAM_TRANSFER_NONE
+
+    def _get_transfer_default_returntype_basic(self, typeval):
+        if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
+            or typeval.is_const
+            or typeval.is_equiv(ast.TYPE_NONE)):
+            return ast.PARAM_TRANSFER_NONE
+        elif typeval.is_equiv(ast.TYPE_STRING):
+            # Non-const strings default to FULL
+            return ast.PARAM_TRANSFER_FULL
+        elif typeval.target_fundamental:
+            # This looks like just GType right now
+            return None
+        return None
+
+    def _get_transfer_default_return(self, parent, node):
+        typeval = node.type
+        basic = self._get_transfer_default_returntype_basic(typeval)
+        if basic:
+            return basic
+        if typeval.target_giname:
+            target = self._transformer.lookup_typenode(typeval)
+            if isinstance(target, ast.Alias):
+                return self._get_transfer_default_returntype_basic(target.target)
+            elif isinstance(target, glibast.GLibBoxed):
+                return ast.PARAM_TRANSFER_FULL
+            elif isinstance(target, (ast.Enum, ast.Bitfield)):
+                return ast.PARAM_TRANSFER_NONE
+            # Handle constructors specially here
+            elif isinstance(parent, ast.Function) and parent.is_constructor:
+                if isinstance(target, ast.Class):
+                    initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
+                    initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
+                    if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
+                        return ast.PARAM_TRANSFER_NONE
+                    else:
+                        return ast.PARAM_TRANSFER_FULL
+                elif isinstance(target, (ast.Record, ast.Union)):
+                    return ast.PARAM_TRANSFER_FULL
+                else:
+                    assert False, "Invalid constructor"
+            elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
+                # Explicitly no default for these
+                return None
+            else:
+                return None
+        return None
+
+    def _get_transfer_default(self, parent, node):
+        if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
+            return ast.PARAM_TRANSFER_NONE
+        elif isinstance(node, ast.Parameter):
+            return self._get_transfer_default_param(parent, node)
+        elif isinstance(node, ast.Return):
+            return self._get_transfer_default_return(parent, node)
+        elif isinstance(node, ast.Field):
+            return ast.PARAM_TRANSFER_NONE
+        elif isinstance(node, ast.Property):
+            return ast.PARAM_TRANSFER_NONE
+        else:
+            raise AssertionError(node)
+
+    def _apply_annotations_param_ret_common(self, parent, node, tag):
+        options = getattr(tag, 'options', {})
+
+        param_type = options.get(OPT_TYPE)
+        if param_type:
+            node.type = self._resolve(param_type.one(), node.type)
+
+        caller_allocates = False
+        annotated_direction = None
+        if (OPT_INOUT in options or
+            OPT_INOUT_ALT in options):
+            annotated_direction = ast.PARAM_DIRECTION_INOUT
+        elif OPT_OUT in options:
+            subtype = options[OPT_OUT]
+            if subtype is not None:
+                subtype = subtype.one()
+            annotated_direction = ast.PARAM_DIRECTION_OUT
+            if subtype in (None, ''):
+                if node.type.target_giname and node.type.ctype:
+                    caller_allocates = '**' not in node.type.ctype
+                else:
+                    caller_allocates = False
+            elif subtype == 'caller-allocates':
+                caller_allocates = True
+            elif subtype == 'callee-allocates':
+                caller_allocates = False
+            else:
+                self._transformer.log_warning(
+"out allocation for %s is invalid (%r)" % (node, subtype), fatal=True)
+        elif OPT_IN in options:
+            annotated_direction = ast.PARAM_DIRECTION_IN
+
+        if (annotated_direction is not None) and (annotated_direction != node.direction):
+            node.direction = annotated_direction
+            node.caller_allocates = caller_allocates
+            # Also reset the transfer default if we're toggling direction
+            node.transfer = self._get_transfer_default(parent, node)
+
+        transfer_tag = options.get(TAG_TRANSFER)
+        if transfer_tag:
+            node.transfer = transfer_tag.one()
+
+        self._adjust_container_type(parent, node, options)
+
+        if (OPT_ALLOW_NONE in options or
+            node.type.target_giname == 'Gio.Cancellable'):
+            node.allow_none = True
+
+        if tag is not None and tag.comment is not None:
+            node.doc = tag.comment
+
+        for key in options:
+            if '.' in key:
+                value = options.get(key)
+                if value:
+                    node.attributes.append((key, value.one()))
+
+    def _apply_annotations_annotated(self, node, block):
+        if block is None:
+            return
+
+        node.doc = block.comment
+
+        since_tag = block.get(TAG_SINCE)
+        if since_tag is not None:
+            node.version = since_tag.value
+
+        deprecated_tag = block.get(TAG_DEPRECATED)
+        if deprecated_tag is not None:
+            value = deprecated_tag.value
+            if ': ' in value:
+                version, desc = value.split(': ')
+            else:
+                desc = value
+                version = None
+            node.deprecated = desc
+            if version is not None:
+                node.deprecated_version = version
+
+        annos_tag = block.get(TAG_ATTRIBUTES)
+        if annos_tag is not None:
+            options = AnnotationParser.parse_options(annos_tag.value)
+            for key, value in options.iteritems():
+                if value:
+                    node.attributes.append((key, value.one()))
+
+        if OPT_SKIP in block.options:
+            node.skip = True
+
+        if OPT_FOREIGN in block.options:
+            node.foreign = True
+
+    def _apply_annotations_param(self, parent, param, tag):
+        if tag:
+            options = tag.options
+        else:
+            options = {}
+        if isinstance(parent, ast.Function):
+            scope = options.get(OPT_SCOPE)
+            if scope:
+                scope = scope.one()
+                if scope not in [ast.PARAM_SCOPE_CALL,
+                                 ast.PARAM_SCOPE_ASYNC,
+                                 ast.PARAM_SCOPE_NOTIFIED]:
+                    self._transformer.log_warning(parent,
+"Invalid scope %r for parameter %r" % (scope, param.name))
+                else:
+                    param.scope = scope
+                    param.transfer = ast.PARAM_TRANSFER_NONE
+
+            destroy = options.get(OPT_DESTROY)
+            if destroy:
+                param.destroy_name = self._get_validate_parameter_name(parent,
+                                                                       destroy.one(),
+                                                                       param)
+                if param.destroy_name is not None:
+                    param.scope = ast.PARAM_SCOPE_NOTIFIED
+                    destroy_param = parent.get_parameter(param.destroy_name)
+                    # This is technically bogus; we're setting the scope on the destroy
+                    # itself.  But this helps avoid tripping a warning from finaltransformer,
+                    # since we don't have a way right now to flag this callback a destroy.
+                    destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
+            closure = options.get(OPT_CLOSURE)
+            if closure:
+                param.closure_name = self._get_validate_parameter_name(parent,
+                                                                       closure.one(),
+                                                                       param)
+        elif isinstance(parent, ast.Callback):
+            if OPT_CLOSURE in options:
+                # For callbacks, (closure) appears without an
+                # argument, and tags a parameter that is a closure. We
+                # represent it (weirdly) in the gir and typelib by
+                # setting param.closure_name to itself.
+                param.closure_name = param.argname
+
+        self._apply_annotations_param_ret_common(parent, param, tag)
+
+    def _apply_annotations_return(self, parent, return_, block):
+        if block:
+            tag = block.get(TAG_RETURNS)
+        else:
+            tag = None
+        self._apply_annotations_param_ret_common(parent, return_, tag)
+
+    def _apply_annotations_params(self, parent, params, block):
+        for param in params:
+            if block:
+                tag = block.get(param.argname)
+            else:
+                tag = None
+            self._apply_annotations_param(parent, param, tag)
+
+    def _apply_annotations_callable(self, node, chain, block):
+        self._apply_annotations_annotated(node, block)
+        self._apply_annotations_params(node, node.parameters, block)
+        self._apply_annotations_return(node, node.retval, block)
+
+    def _check_arg_annotations(self, parent, params, block):
+        if block is None:
+            return
+        for tag in block.tags.keys():
+            if tag == TAG_RETURNS:
+                continue
+            for param in params:
+                if param.argname == tag:
+                    break
+            else:
+                self._transformer.log_warning(
+"Annotation for '%s' refers to unknown argument '%s'"
+% (parent.name, tag))
+
+    def _apply_annotations_field(self, parent, block, field):
+        if not block:
+            return
+        tag = block.get(field.name)
+        if not tag:
+            return
+        t = tag.options.get('type')
+        if not t:
+            return
+        field.type = self._transformer.create_type_from_user_string(t.one())
+
+    def _apply_annotations_property(self, parent, prop):
+        block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
+        self._apply_annotations_annotated(prop, block)
+        if not block:
+            return
+        transfer_tag = block.get(TAG_TRANSFER)
+        if transfer_tag is not None:
+            prop.transfer = transfer_tag.value
+        else:
+            prop.transfer = self._get_transfer_default(parent, prop)
+        type_tag = block.get(TAG_TYPE)
+        if type_tag:
+            prop.type = self._resolve(type_tag.value, prop.type)
+
+    def _apply_annotations_signal(self, parent, signal):
+        block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
+        self._apply_annotations_annotated(signal, block)
+        # We're only attempting to name the signal parameters if
+        # the number of parameter tags (@foo) is the same or greater
+        # than the number of signal parameters
+        if block and len(block.tags) > len(signal.parameters):
+            names = block.tags.items()
+        else:
+            names = []
+        for i, param in enumerate(signal.parameters):
+            if names:
+                name, tag = names[i+1]
+                param.name = name
+                options = getattr(tag, 'options', {})
+                param_type = options.get(OPT_TYPE)
+                if param_type:
+                    param.type = self._resolve(param_type.one(), param.type)
+            else:
+                tag = None
+            self._apply_annotations_param(signal, param, tag)
+        self._apply_annotations_return(signal, signal.retval, block)
+
+    def _pass_read_annotations2(self, node, chain):
+        if isinstance(node, ast.Function):
+            self._apply_annotations2_function(node, chain)
+        return True
+
+    def _apply_annotations2_function(self, node, chain):
+        # Handle virtual invokers
+        parent = chain[-1] if chain else None
+        block = self._blocks.get(node.symbol)
+        if not (block and parent):
+            return
+        virtual = block.get(TAG_VFUNC)
+        if not virtual:
+            return
+        invoker_name = virtual.value
+        matched = False
+        for vfunc in parent.virtual_methods:
+            if vfunc.name == invoker_name:
+                matched = True
+                vfunc.invoker = node.name
+                # Also merge in annotations
+                self._apply_annotations_callable(vfunc, [parent], block)
+                break
+        if not matched:
+            self._transformer.log_symbol_warning(node.symbol,
+                "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
+
+    def _pass_type_resolution(self, node, chain):
+        if isinstance(node, ast.Alias):
+            self._transformer.resolve_type(node.target)
+        if isinstance(node, ast.Callable):
+            for parameter in node.parameters:
+                self._transformer.resolve_type(parameter.type)
+            self._transformer.resolve_type(node.retval.type)
+        if isinstance(node, ast.Constant):
+            self._transformer.resolve_type(node.value_type)
+        if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+            for field in node.fields:
+                if field.anonymous_node:
+                    pass
+                else:
+                    self._transformer.resolve_type(field.type)
+        if isinstance(node, (ast.Class, ast.Interface)):
+            resolved_parent = None
+            for parent in node.parent_chain:
+                try:
+                    self._transformer.resolve_type(parent)
+                except ValueError, e:
+                    continue
+                target = self._transformer.lookup_typenode(parent)
+                if target:
+                    node.parent = parent
+                    break
+            for prop in node.properties:
+                self._transformer.resolve_type(prop.type)
+            for sig in node.signals:
+                for param in sig.parameters:
+                    self._transformer.resolve_type(param.type)
+        if isinstance(node, ast.Class):
+            for iface in node.interfaces:
+                self._transformer.resolve_type(iface)
+        if isinstance(node, ast.Interface):
+            for iface in node.prerequisites:
+                self._transformer.resolve_type(iface)
+        return True
+
+    def _resolve_quarks(self):
+        # self._uscore_type_names is an authoritative mapping of types
+        # to underscored versions, since it is based on get_type() methods;
+        # but only covers enums that are registered as GObject enums.
+        # Create a fallback mapping based on all known enums in this module.
+        uscore_enums = {}
+        for enum in self._namespace.itervalues():
+            if not isinstance(enum, ast.Enum):
+                continue
+            type_name = enum.symbol
+            uscored = to_underscores(type_name).lower()
+
+            uscore_enums[uscored] = enum
+
+            no_uscore_prefixed = self._transformer.strip_identifier_or_warn(type_name)
+            if no_uscore_prefixed not in uscore_enums:
+                uscore_enums[no_uscore_prefixed] = enum
+
+        for node in self._namespace.itervalues():
+            if not isinstance(node, ast.Function):
+                continue
+            if node.retval.type.target_giname != 'GLib.Quark':
+                continue
+            short = node.symbol[:-len('_quark')]
+            if short == "g_io_error":
+                # Special case; GIOError was already taken forcing GIOErrorEnum
+                assert self._namespace.name == 'Gio'
+                enum = self._namespace.get('IOErrorEnum')
+            else:
+                enum = self._uscore_type_names.get(short)
+                if enum is None:
+                    enum = uscore_enums.get(short)
+            if enum is not None:
+                enum.error_quark = node.symbol
+            else:
+                self._transformer.log_node_warning(node,
+"""%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
+
+    def _split_uscored_by_type(self, uscored):
+        """'uscored' should be an un-prefixed uscore string.  This
+function searches through the namespace for the longest type which
+prefixes uscored, and returns (type, suffix).  Example, assuming
+namespace Gtk, type is TextBuffer:
+
+_split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
+        node = None
+        count = 0
+        prev_split_count = -1
+        while True:
+            components = uscored.rsplit('_', count)
+            if len(components) == prev_split_count:
+                return None
+            prev_split_count = len(components)
+            type_string = components[0]
+            node = self._uscore_type_names.get(type_string)
+            if node:
+                return (node, '_'.join(components[1:]))
+            count += 1
+
+    def _pair_function(self, func):
+        """Check to see whether a toplevel function should be a
+method or constructor of some type."""
+        if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
+            return
+        (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
+        assert ns == self._namespace
+        if self._pair_constructor(func, subsymbol):
+            return
+        elif self._pair_method(func, subsymbol):
+            return
+        elif self._pair_static_method(func, subsymbol):
+            return
+
+    def _uscored_identifier_for_type(self, typeval):
+        """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
+        name = typeval.get_giname()
+        return to_underscores_noprefix(name).lower()
+
+    def _pair_method(self, func, subsymbol):
+        if not func.parameters:
+            return False
+        first = func.parameters[0]
+        target = self._transformer.lookup_typenode(first.type)
+        if not isinstance(target, (ast.Class, ast.Interface,
+                                   ast.Record, ast.Union,
+                                   glibast.GLibBoxedOther)):
+            return False
+
+        # A quick hack here...in the future we should catch C signature/GI signature
+        # mismatches in a general way in finaltransformer
+        if first.type.ctype.count('*') != 1:
+            return False
+
+        uscored = self._uscored_identifier_for_type(first.type)
+        if not subsymbol.startswith(uscored):
+            return False
+        del func.parameters[0]
+        subsym_idx = func.symbol.find(subsymbol)
+        self._namespace.float(func)
+        func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
+        target.methods.append(func)
+        func.is_method = True
+        return True
+
+    def _pair_static_method(self, func, subsymbol):
+        split = self._split_uscored_by_type(subsymbol)
+        if split is None:
+            return False
+        (node, funcname) = split
+        if not isinstance(node, (ast.Class, ast.Interface,
+                                 ast.Record, ast.Union, glibast.GLibBoxedOther)):
+            return False
+        self._namespace.float(func)
+        func.name = funcname
+        node.static_methods.append(func)
+
+    def _pair_constructor(self, func, subsymbol):
+        if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
+            return False
+        target = self._transformer.lookup_typenode(func.retval.type)
+        if not isinstance(target, (ast.Class, ast.Record, ast.Union, glibast.GLibBoxedOther)):
+            return False
+        new_idx = func.symbol.rfind('_new')
+        assert (new_idx >= 0)
+        prefix = func.symbol[:new_idx]
+        split = self._split_uscored_by_type(subsymbol)
+        if split is None:
+            # TODO - need a e.g. (method) annotation
+            self._transformer.log_node_warning(func,
+                "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
+            return False
+        (origin_node, funcname) = split
+        if isinstance(target, ast.Class):
+            parent = origin_node
+            while parent:
+                if parent == target:
+                    break
+                if parent.parent:
+                    parent = self._transformer.lookup_typenode(parent.parent)
+                else:
+                    parent = None
+                if parent is None:
+                    self._transformer.log_node_warning(func,
+"Return value is not superclass for constructor; symbol=%r constructed=%r return=%r"
+% (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+                    return False
+        else:
+            if origin_node != target:
+                self._transformer.log_node_warning(func,
+"Constructor return type mismatch symbol=%r constructed=%r return=%r"
+% (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+                return False
+        self._namespace.float(func)
+        func.name = funcname
+        func.is_constructor = True
+        target.constructors.append(func)
+        # Constructors have default return semantics
+        if not func.retval.transfer:
+            func.retval.transfer = self._get_transfer_default_return(func, func.retval)
+        return True
+
+    def _pair_class_virtuals(self, node):
+        """Look for virtual methods from the class structure."""
+        if not node.glib_type_struct:
+            self._transformer.log_node_warning(node,
+                "Failed to find class structure for %r" % (node.name, ))
+            return
+
+        node_type = node.create_type()
+        class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
+
+        # Object class fields are assumed to be read-only
+        # (see also _introspect_object and transformer.py)
+        for field in class_struct.fields:
+            if isinstance(field, ast.Field):
+                field.writable = False
+
+        # Loop through fields to determine which are virtual
+        # functions and which are signal slots by
+        # assuming everything that doesn't share a name
+        # with a known signal is a virtual slot.
+        for field in class_struct.fields:
+            if not isinstance(field.anonymous_node, ast.Callback):
+                continue
+            callback = field.anonymous_node
+            # Check the first parameter is the object
+            if len(callback.parameters) == 0:
+                continue
+            firstparam_type = callback.parameters[0].type
+            if firstparam_type != node_type:
+                continue
+            # Also double check we don't have a signal with this
+            # name.
+            matched_signal = False
+            for signal in node.signals:
+                if signal.name.replace('-', '_') == callback.name:
+                    matched_signal = True
+                    break
+            if matched_signal:
+                continue
+            vfunc = ast.VFunction.from_callback(callback)
+            vfunc.inherit_file_positions(callback)
+            node.virtual_methods.append(vfunc)
+
+        # Take the set of virtual methods we found, and try
+        # to pair up with any matching methods using the
+        # name+signature.
+        for vfunc in node.virtual_methods:
+            for method in node.methods:
+                if method.name != vfunc.name:
+                    continue
+                if method.retval.type != vfunc.retval.type:
+                    continue
+                if len(method.parameters) != len(vfunc.parameters):
+                    continue
+                for i in xrange(len(method.parameters)):
+                    m_type = method.parameters[i].type
+                    v_type = vfunc.parameters[i].type
+                    if m_type != v_type:
+                        continue
+                vfunc.invoker = method.name
+                # Apply any annotations we have from the invoker to
+                # the vfunc
+                block = self._blocks.get(method.symbol)
+                self._apply_annotations_callable(vfunc, [], block)
+
+    def _pass3(self, node, chain):
+        """Pass 3 is after we've loaded GType data and performed type
+        closure."""
+        if isinstance(node, ast.Callable):
+            self._pass3_callable_callbacks(node)
+            self._pass3_callable_throws(node)
+        return True
+
+    def _pass3_callable_callbacks(self, node):
+        """Check to see if we have anything that looks like a
+        callback+user_data+GDestroyNotify set."""
+
+        params = node.parameters
+
+        # First, do defaults for well-known callback types
+        for i, param in enumerate(params):
+            argnode = self._transformer.lookup_typenode(param.type)
+            if isinstance(argnode, ast.Callback):
+                if param.type.target_giname in ('Gio.AsyncReadyCallback',
+                                                'GLib.DestroyNotify'):
+                    param.scope = ast.PARAM_SCOPE_ASYNC
+                    param.transfer = ast.PARAM_TRANSFER_NONE
+
+        callback_param = None
+        for i, param in enumerate(params):
+            argnode = self._transformer.lookup_typenode(param.type)
+            is_destroynotify = False
+            if isinstance(argnode, ast.Callback):
+                if param.type.target_giname == 'GLib.DestroyNotify':
+                    is_destroynotify = True
+                else:
+                    callback_param = param
+                    continue
+            if callback_param is None:
+                continue
+            if is_destroynotify:
+                callback_param.destroy_name = param.argname
+                callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
+                callback_param.transfer = ast.PARAM_TRANSFER_NONE
+            elif (param.type.is_equiv(ast.TYPE_ANY) and
+                  param.argname.endswith('data')):
+                callback_param.closure_name = param.argname
+
+    def _pass3_callable_throws(self, node):
+        """Check to see if we have anything that looks like a
+        callback+user_data+GDestroyNotify set."""
+        if not node.parameters:
+            return
+        last_param = node.parameters[-1]
+        # Checking type.name=='GLib.Error' generates false positives
+        # on methods that take a 'GError *'
+        if last_param.type.ctype == 'GError**':
+            node.parameters.pop()
+            node.throws = True
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 3694282..daa5a9a 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -21,19 +21,25 @@
 #
 
 import subprocess
+import tempfile
 import optparse
 import os
 import sys
 
-from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
+from giscanner.annotationparser import AnnotationParser
 from giscanner.ast import Include
 from giscanner.cachestore import CacheStore
 from giscanner.dumper import compile_introspection_binary
 from giscanner.glibtransformer import GLibTransformer, IntrospectionBinary
-from giscanner.minixpath import myxpath, xpath_assert
+from giscanner.minixpath import xpath_assert
 from giscanner.sourcescanner import SourceScanner
 from giscanner.shlibs import resolve_shlibs
 from giscanner.transformer import Transformer
+from giscanner.primarytransformer import PrimaryTransformer
+from giscanner.finaltransformer import FinalTransformer
+from giscanner.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
+from giscanner.utils import files_are_identical
 
 def _get_option_parser():
     parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +55,15 @@ def _get_option_parser():
     parser.add_option("-i", "--include",
                       action="append", dest="includes", default=[],
                       help="include types for other gidls")
+    parser.add_option('', "--test-codegen",
+                      action="store", dest="test_codegen", default=None,
+                      help="Generate test code for given namespace,output.h,output.c")
+    parser.add_option('', "--passthrough-gir",
+                      action="store", dest="passthrough_gir", default=None,
+                      help="Parse and re-output the specified GIR")
+    parser.add_option('', "--reparse-validate",
+                      action="store_true", dest="reparse_validate_gir", default=False,
+                      help="After generating the GIR, re-parse it to ensure validity")
     parser.add_option("", "--add-include-path",
                       action="append", dest="include_paths", default=[],
                       help="include paths for other GIR files")
@@ -73,13 +88,18 @@ def _get_option_parser():
     parser.add_option("-n", "--namespace",
                       action="store", dest="namespace_name",
                       help=("name of namespace for this unit, also "
-                            "used as --strip-prefix default"))
+                            "used to compute --identifier-prefix and --symbol-prefix"))
     parser.add_option("", "--nsversion",
                       action="store", dest="namespace_version",
                       help="version of namespace for this unit")
-    parser.add_option("", "--strip-prefix",
-                      action="store", dest="strip_prefix", default=None,
-                      help="remove this prefix from objects and functions")
+    parser.add_option("", "--identifier-prefix",
+                      action="append", dest="identifier_prefixes", default=[],
+                      help="""Remove this prefix from C identifiers (structure typedefs, etc.).
+May be specified multiple times.  This is also used as the default for --symbol-prefix if
+the latter is not specified.""")
+    parser.add_option("", "--symbol-prefix",
+                      action="append", dest="symbol_prefixes", default=[],
+                      help="Remove this prefix from C symbols (function names)")
     parser.add_option("", "--add-init-section",
                       action="append", dest="init_sections", default=[],
             help="add extra initialization code in the introspection program")
@@ -101,15 +121,9 @@ def _get_option_parser():
     parser.add_option("-v", "--verbose",
                       action="store_true", dest="verbose",
                       help="be verbose")
-    parser.add_option("", "--noclosure",
-                      action="store_true", dest="noclosure",
-                      help="do not delete unknown types")
     parser.add_option("", "--typelib-xml",
                       action="store_true", dest="typelib_xml",
                       help="Just convert GIR to typelib XML")
-    parser.add_option("", "--inject",
-                      action="store_true", dest="inject",
-                      help="Inject additional components into GIR XML")
     parser.add_option("", "--xpath-assertions",
                       action="store", dest="xpath_assertions",
             help="Use given file to create assertions on GIR content")
@@ -136,53 +150,25 @@ def _get_option_parser():
 def _error(msg):
     raise SystemExit('ERROR: %s' % (msg, ))
 
-def typelib_xml_strip(path):
-    from giscanner.girparser import GIRParser
-    from giscanner.girwriter import GIRWriter
-    from giscanner.girparser import C_NS
-    from xml.etree.cElementTree import parse
-
-    c_ns_key = '{%s}' % (C_NS, )
-
-    tree = parse(path)
-    root = tree.getroot()
-    for node in root.getiterator():
-        for attrib in list(node.attrib):
-            if attrib.startswith(c_ns_key):
-                del node.attrib[attrib]
+def passthrough_gir(path, f):
     parser = GIRParser()
-    parser.parse_tree(tree)
+    parser.parse(path)
 
     writer = GIRWriter(parser.get_namespace(),
                        parser.get_shared_libraries(),
-                       parser.get_includes())
-    sys.stdout.write(writer.get_xml())
-    return 0
-
-def inject(path, additions, outpath):
-    from giscanner.girparser import GIRParser
-    from giscanner.girwriter import GIRWriter
-    from xml.etree.cElementTree import parse
-
-    tree = parse(path)
-    root = tree.getroot()
-    injectDoc = parse(open(additions))
-    for node in injectDoc.getroot():
-        injectPath = node.attrib['path']
-        target = myxpath(root, injectPath)
-        if not target:
-            raise ValueError("Couldn't find path %r" % (injectPath, ))
-        for child in node:
-            target.append(child)
-
-    parser = GIRParser()
-    parser.parse_tree(tree)
-    writer = GIRWriter(parser.get_namespace(),
-                       parser.get_shared_libraries(),
-                       parser.get_includes())
-    outf = open(outpath, 'w')
-    outf.write(writer.get_xml())
-    outf.close()
+                       parser.get_includes(),
+                       parser.get_pkgconfig_packages(),
+                       parser.get_c_includes())
+    f.write(writer.get_xml())
+
+def test_codegen(optstring):
+    (namespace, out_h_filename, out_c_filename) = optstring.split(',')
+    if namespace == 'Everything':
+        from .testcodegen import EverythingCodeGenerator
+        gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
+        gen.write()
+    else:
+        raise ValueError("Invaild namespace %r" % (namespace, ))
     return 0
 
 def validate(assertions, path):
@@ -237,18 +223,14 @@ def scanner_main(args):
     parser = _get_option_parser()
     (options, args) = parser.parse_args(args)
 
+    if options.passthrough_gir:
+        passthrough_gir(options.passthrough_gir, sys.stdout)
+    if options.test_codegen:
+        return test_codegen(options.test_codegen)
+
     if len(args) <= 1:
         _error('Need at least one filename')
 
-    if options.typelib_xml:
-        return typelib_xml_strip(args[1])
-
-    if options.inject:
-        if len(args) != 4:
-            _error('Need three filenames; e.g. g-ir-scanner '
-                   '--inject Source.gir Additions.xml SourceOut.gir')
-        return inject(*args[1:4])
-
     if options.xpath_assertions:
         return validate(options.xpath_assertions, args[1])
 
@@ -281,11 +263,9 @@ def scanner_main(args):
     cachestore = CacheStore()
     transformer = Transformer(cachestore,
                               options.namespace_name,
-                              options.namespace_version)
-    if options.strip_prefix:
-        transformer.set_strip_prefix(options.strip_prefix)
-    else:
-        transformer.set_strip_prefix(options.namespace_name)
+                              options.namespace_version,
+                              options.identifier_prefixes,
+                              options.symbol_prefixes)
     if options.warn_all:
         transformer.enable_warnings(True)
     transformer.set_include_paths(options.include_paths)
@@ -318,8 +298,7 @@ def scanner_main(args):
 
     # Transform the C AST nodes into higher level
     # GLib/GObject nodes
-    glibtransformer = GLibTransformer(transformer,
-                                      noclosure=options.noclosure)
+    glibtransformer = GLibTransformer(transformer)
 
     # Do enough parsing that we have the get_type() functions to reference
     # when creating the introspection binary
@@ -336,33 +315,47 @@ def scanner_main(args):
     shlibs = resolve_shlibs(options, binary, libraries)
 
     glibtransformer.set_introspection_binary(binary)
+    glibtransformer.parse()
 
-    namespace = glibtransformer.parse()
+    ap = AnnotationParser(ss, transformer)
+    blocks = ap.parse()
 
-    ap = AnnotationParser(namespace, ss, transformer)
-    try:
-        ap.parse()
-    except InvalidAnnotationError, e:
-        raise SystemExit("ERROR in annotation: %s" % (str(e), ))
+    primary = PrimaryTransformer(transformer, blocks)
+    primary.transform()
 
-    glibtransformer.final_analyze()
+    final = FinalTransformer(transformer)
+    final.validate()
 
     if options.warn_fatal and transformer.did_warn():
-        return 1
+        transformer.log_warning("warnings configured as fatal", fatal=True)
+        # Redundant sys.exit here, just in case
+        sys.exit(1)
 
     # Write out AST
     if options.packages_export:
         exported_packages = options.packages_export
     else:
         exported_packages = options.packages
-    writer = Writer(namespace, shlibs, transformer.get_includes(),
-                    exported_packages, options.c_includes,
-                    transformer.get_strip_prefix())
+    writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
+                    exported_packages, options.c_includes)
     data = writer.get_xml()
     if options.output:
-        fd = open(options.output, "w")
-        fd.write(data)
+        tempdir = os.path.dirname(options.output) or os.getcwd()
+        (tempfd, main_temppath) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+        f = os.fdopen(tempfd, 'w')
+        f.write(data)
+        f.close()
+        if options.reparse_validate_gir:
+            (tempfd, tempgir_path) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+            temp_f = os.fdopen(tempfd, 'w')
+            passthrough_gir(main_temppath, temp_f)
+            temp_f.close()
+            if not files_are_identical(main_temppath, tempgir_path):
+                raise SystemExit(
+"Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_temppath, tempgir_path))
+            os.unlink(tempgir_path)
+        os.rename(main_temppath, options.output)
     else:
-        print data
+        sys.stdout.write(data)
 
     return 0
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index cd7a432..a2db2e7 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -154,10 +154,16 @@ class SourceSymbol(object):
         self._symbol = symbol
 
     def __repr__(self):
-        return '<%s type=%r ident=%r>' % (
+        src = self.source_filename
+        if src:
+            line = self.line
+            if line:
+                src += ':%r' % (line, )
+        return '<%s type=%r ident=%r src=%r>' % (
             self.__class__.__name__,
             symbol_type_name(self.type),
-            self.ident)
+            self.ident,
+            src)
 
     @property
     def const_int(self):
diff --git a/giscanner/testcodegen.py b/giscanner/testcodegen.py
new file mode 100644
index 0000000..9155c4c
--- /dev/null
+++ b/giscanner/testcodegen.py
@@ -0,0 +1,124 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2010  Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+from StringIO import StringIO
+from . import ast
+from .codegen import CCodeGenerator
+
+INTROSPECTABLE_BASIC = filter(lambda x: x not in (
+        ast.TYPE_NONE, ast.TYPE_ANY,
+        ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
+        ast.TYPE_LONG_DOUBLE, ast.TYPE_VALIST), ast.GIR_TYPES)
+
+DEFAULT_C_VALUES = {ast.TYPE_ANY: 'NULL',
+                    ast.TYPE_STRING: '""',
+                    ast.TYPE_FILENAME: '""',
+                    ast.TYPE_GTYPE: 'g_object_get_type ()'}
+
+def get_default_for_typeval(typeval):
+    default = DEFAULT_C_VALUES.get(typeval)
+    if default:
+        return default
+    return "0"
+
+def uscore_from_type(typeval):
+    if typeval.target_fundamental:
+        return typeval.target_fundamental.replace(' ', '_')
+    elif typeval.target_giname:
+        return typeval.target_giname.replace('.', '').lower()
+    else:
+        assert False, typeval
+
+class EverythingCodeGenerator(object):
+
+    def __init__(self, out_h_filename, out_c_filename):
+        self.namespace = ast.Namespace('Everything', '1.0')
+        self.gen = CCodeGenerator(self.namespace, out_h_filename, out_c_filename)
+
+    def write(self):
+        func = ast.Function('nullfunc',
+                            ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+                            [], False, self.gen.gen_symbol('nullfunc'))
+        self.namespace.append(func)
+        body = "  return;\n"
+        self.gen.set_function_body(func, body)
+
+        # First pass, generate constant returns
+        prefix = 'const return '
+        for typeval in INTROSPECTABLE_BASIC:
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = ast.Function(name,
+                                ast.Return(typeval, transfer=ast.PARAM_TRANSFER_NONE),
+                                [], False, sym)
+            self.namespace.append(func)
+            default = get_default_for_typeval(typeval)
+            body = "  return %s;\n" % (default, )
+            self.gen.set_function_body(func, body)
+
+        # Void return, one parameter
+        prefix = 'oneparam '
+        for typeval in INTROSPECTABLE_BASIC:
+            if typeval is ast.TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = ast.Function(name,
+                                ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+                                [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+                                               direction=ast.PARAM_DIRECTION_IN)], False, sym)
+            self.namespace.append(func)
+            self.gen.set_function_body(func, "  return;\n")
+
+        # Void return, one (out) parameter
+        prefix = 'one_outparam '
+        for typeval in INTROSPECTABLE_BASIC:
+            if typeval is ast.TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = ast.Function(name,
+                                ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+                                [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+                                               direction=ast.PARAM_DIRECTION_OUT)], False, sym)
+            self.namespace.append(func)
+            body = StringIO('w')
+            default = get_default_for_typeval(func.retval)
+            body.write("  *arg0 = %s;\n" % (default, ))
+            body.write("  return;\n")
+            self.gen.set_function_body(func, body.getvalue())
+
+        # Passthrough one parameter
+        prefix = 'passthrough_one '
+        for typeval in INTROSPECTABLE_BASIC:
+            if typeval is ast.TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = ast.Function(name, ast.Return(typeval, transfer=ast.PARAM_TRANSFER_NONE),
+                            [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+                                       direction=ast.PARAM_DIRECTION_IN)], False, sym)
+            self.namespace.append(func)
+            body = StringIO('w')
+            default = get_default_for_typeval(func.retval)
+            body.write("  return arg0;\n")
+            self.gen.set_function_body(func, body.getvalue())
+
+        self.gen.codegen()
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index e2a2204..f6639a6 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -20,17 +20,11 @@
 
 import os
 import sys
+import re
 
-from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
-                  Parameter, Return, Struct, Field,
-                  Type, Array, Alias, Interface, Class, Node, Union,
-                  Varargs, Constant, type_name_from_ctype,
-                  type_names, TYPE_ANY, TYPE_STRING,
-                  BASIC_GIR_TYPES)
+from . import ast
 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
 from .girparser import GIRParser
-from .odict import odict
 from .sourcescanner import (
     SourceSymbol, ctype_name, CTYPE_POINTER,
     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
@@ -39,57 +33,36 @@ from .sourcescanner import (
     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
     TYPE_QUALIFIER_CONST)
-from .utils import to_underscores
 
-_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
-                      + [DATADIR, '/usr/share'] if x]
-
-
-class SkipError(Exception):
+class TypeResolutionException(Exception):
     pass
 
-
-class Names(object):
-    names = property(lambda self: self._names)
-    aliases = property(lambda self: self._aliases)
-    type_names = property(lambda self: self._type_names)
-    ctypes = property(lambda self: self._ctypes)
-
-    def __init__(self):
-        super(Names, self).__init__()
-        self._names = odict() # Maps from GIName -> (namespace, node)
-        self._aliases = {} # Maps from GIName -> GIName
-        self._type_names = {} # Maps from GTName -> (namespace, node)
-        self._ctypes = {} # Maps from CType -> (namespace, node)
-
+_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
+                      + [DATADIR, '/usr/share'] if x]
 
 class Transformer(object):
+    namespace = property(lambda self: self._namespace)
 
-    def __init__(self, cachestore, namespace_name, namespace_version):
+    UCASE_CONSTANT_RE = re.compile(r'[_A-Z0-9]+')
+
+    def __init__(self, cachestore, namespace_name, namespace_version,
+                 identifier_prefixes=None, symbol_prefixes=None):
         self._cwd = os.getcwd() + os.sep
         self._cachestore = cachestore
         self.generator = None
-        self._namespace = Namespace(namespace_name, namespace_version)
-        self._names = Names()
+        self._namespace = ast.Namespace(namespace_name, namespace_version,
+                                    identifier_prefixes=identifier_prefixes,
+                                    symbol_prefixes=symbol_prefixes)
         self._pkg_config_packages = set()
         self._typedefs_ns = {}
-        self._strip_prefix = ''
         self._enable_warnings = False
         self._warned = False
-        self._includes = set()
+        self._includes = {}
+        self._include_names = set()
         self._includepaths = []
 
-    def get_names(self):
-        return self._names
-
     def get_includes(self):
-        return self._includes
-
-    def set_strip_prefix(self, strip_prefix):
-        self._strip_prefix = strip_prefix
-
-    def get_strip_prefix(self):
-        return self._strip_prefix
+        return self._include_names
 
     def enable_warnings(self, enable):
         self._enable_warnings = enable
@@ -103,39 +76,97 @@ class Transformer(object):
     def set_source_ast(self, src_ast):
         self.generator = src_ast
 
+    def _append_new_node(self, node):
+        original = self._namespace.get(node.name)
+        # Special case constants here; we allow duplication to sort-of
+        # handle #ifdef.  But this introduces an arch-dependency in the .gir
+        # file.  So far this has only come up scanning glib - in theory, other
+        # modules will just depend on that.
+        if isinstance(original, ast.Constant) and isinstance(node, ast.Constant):
+            pass
+        elif original:
+            positions = set()
+            positions.update(original.file_positions)
+            positions.update(node.file_positions)
+            self.log_warning("Namespace conflict for '%s'" % (node.name, ),
+                             positions, fatal=True)
+        else:
+            self._namespace.append(node)
+
     def parse(self):
-        nodes = []
         for symbol in self.generator.get_symbols():
-            try:
-                node = self._traverse_one(symbol)
-            except SkipError:
-                continue
-            self._add_node(node)
-        return self._namespace
+            node = self._traverse_one(symbol)
+            if node:
+                self._append_new_node(node)
+
+        # Now look through the namespace for things like
+        # typedef struct _Foo Foo;
+        # where we've never seen the struct _Foo.  Just create
+        # an empty structure for these as "disguised"
+        # If we do have a class/interface, merge fields
+        for typedef, compound in self._typedefs_ns.iteritems():
+            ns_compound = self._namespace.get(compound.name)
+            if not ns_compound:
+                ns_compound = self._namespace.get('_' + compound.name)
+            if (not ns_compound and isinstance(compound, (ast.Record, ast.Union))
+                and len(compound.fields) == 0):
+                disguised = ast.Record(compound.name, typedef, disguised=True)
+                self._namespace.append(disguised)
+            elif not ns_compound:
+                self._namespace.append(compound)
+            elif isinstance(ns_compound, (ast.Record, ast.Union)) and len(ns_compound.fields) == 0:
+                ns_compound.fields = compound.fields
+        self._typedefs_ns = None
 
     def set_include_paths(self, paths):
         self._includepaths = list(paths)
 
     def register_include(self, include):
-        if include in self._includes:
+        if include in self._include_names:
             return
         filename = self._find_include(include)
         self._parse_include(filename)
-        self._includes.add(include)
+        self._include_names.add(include)
+
+    def lookup_giname(self, name):
+        """Given a name of the form Foo or Bar.Foo,
+return the corresponding ast.Node, or None if none
+available.  Will throw KeyError however for unknown
+namespaces."""
+        if '.' not in name:
+            return self._namespace.get(name)
+        else:
+            (ns, name) = name.split('.', 1)
+            if ns == self._namespace.name:
+                return self._namespace.get(name)
+            include = self._includes[ns]
+            return include.get(name)
+
+    def lookup_typenode(self, typeobj):
+        """Given a Type object, if it points to a giname,
+calls lookup_giname() on the name.  Otherwise return
+None."""
+        if typeobj.target_giname:
+            return self.lookup_giname(typeobj.target_giname)
+        return None
 
     # Private
 
-    def log_warning(self, text, file_positions=None, prefix=None):
+    def log_warning(self, text, file_positions=None, prefix=None,
+                    fatal=False):
         """Log a warning, using optional file positioning information.
-If the warning is related to a Node type, see log_node_warning()."""
-        if not self._enable_warnings:
+If the warning is related to a ast.Node type, see log_node_warning()."""
+        if not fatal and not self._enable_warnings:
             return
 
+        self._warned = True
+
         if file_positions is None or len(file_positions) == 0:
             target_file_positions = [('<unknown>', -1, -1)]
         else:
             target_file_positions = file_positions
 
+        position_strings = []
         for (filename, line, column) in target_file_positions:
             if filename.startswith(self._cwd):
                 filename = filename[len(self._cwd):]
@@ -145,41 +176,58 @@ If the warning is related to a Node type, see log_node_warning()."""
                 position = '%s:%d' % (filename, line, )
             else:
                 position = '%s:' % (filename, )
+            position_strings.append(position)
 
+        for position in position_strings[:-1]:
+            print >>sys.stderr, "%s:" % (position, )
+        last_position = position_strings[-1]
+        error_type = 'error' if fatal else 'warning'
         if prefix:
             print >>sys.stderr, \
-'''%s: warning: %s: %s: %s''' % (position, self._namespace.name,
+'''%s: %s: %s: %s: %s''' % (last_position, error_type, self._namespace.name,
                                  prefix, text)
         else:
             print >>sys.stderr, \
-'''%s: warning: %s: %s''' % (position, self._namespace.name, text)
+'''%s: %s: %s: %s''' % (last_position, error_type, self._namespace.name, text)
+        if fatal:
+            sys.exit(1)
 
-    def log_symbol_warning(self, symbol, text):
+    def log_symbol_warning(self, symbol, text, **kwargs):
         """Log a warning in the context of the given symbol."""
-        file_positions = [(symbol.source_filename, symbol.line, -1)]
+        if symbol.source_filename:
+            file_positions = [(symbol.source_filename, symbol.line, -1)]
+        else:
+            file_positions = None
         prefix = "symbol=%r" % (symbol.ident, )
-        self.log_warning(text, file_positions, prefix=prefix)
+        self.log_warning(text, file_positions, prefix=prefix, **kwargs)
 
-    def log_node_warning(self, node, text, context=None):
+    def log_node_warning(self, node, text, context=None, fatal=False):
         """Log a warning, using information about file positions from
 the given node.  The optional context argument, if given, should be
-another Node type which will also be displayed.  If no file position
+another ast.Node type which will also be displayed.  If no file position
 information is available from the node, the position data from the
 context will be used."""
-        if len(node.file_positions) == 0 and \
-                (context is not None) and len(context.file_positions) > 0:
-            file_positions = context.file_positions
+        if hasattr(node, 'file_positions'):
+            if (len(node.file_positions) == 0 and
+                (context is not None) and len(context.file_positions) > 0):
+                file_positions = context.file_positions
+            else:
+                file_positions = node.file_positions
         else:
-            file_positions = node.file_positions
+            file_positions = None
+            if not context:
+                text = "context=%r %s" % (node, text)
 
         if context:
-            if isinstance(context, Function):
+            if isinstance(context, ast.Function):
                 name = context.symbol
             else:
                 name = context.name
             text = "%s: %s" % (name, text)
+        elif len(file_positions) == 0 and hasattr(node, 'name'):
+            text = "(%s)%s: %s" % (node.__class__.__name__, node.name, text)
 
-        self.log_warning(text, file_positions)
+        self.log_warning(text, file_positions, fatal=fatal)
 
     def _find_include(self, include):
         searchdirs = self._includepaths[:]
@@ -200,7 +248,6 @@ context will be used."""
         parser = self._cachestore.load(filename)
         if parser is None:
             parser = GIRParser()
-            parser.set_include_parsing(True)
             parser.parse(filename)
             self._cachestore.store(filename, parser)
 
@@ -210,49 +257,95 @@ context will be used."""
         for pkg in parser.get_pkgconfig_packages():
             self._pkg_config_packages.add(pkg)
         namespace = parser.get_namespace()
-        nsname = namespace.name
-        for node in namespace.nodes:
-            if isinstance(node, Alias):
-                self._names.aliases[node.name] = (nsname, node)
-            elif isinstance(node, (GLibBoxed, Interface, Class)):
-                self._names.type_names[node.type_name] = (nsname, node)
-            giname = '%s.%s' % (nsname, node.name)
-            self._names.names[giname] = (nsname, node)
-            if hasattr(node, 'ctype'):
-                self._names.ctypes[node.ctype] = (nsname, node)
-            elif hasattr(node, 'symbol'):
-                self._names.ctypes[node.symbol] = (nsname, node)
-
-    def _add_node(self, node):
-        if node is None:
-            return
-        if node.name.startswith('_'):
-            return
-        self._namespace.nodes.append(node)
-        self._names.names[node.name] = (None, node)
-
-    def _strip_namespace_func(self, name):
-        prefix = self._namespace.name.lower() + '_'
-        if name.lower().startswith(prefix):
-            name = name[len(prefix):]
-        else:
-            prefix = to_underscores(self._namespace.name).lower() + '_'
-            if name.lower().startswith(prefix):
-                name = name[len(prefix):]
-        return self.remove_prefix(name, isfunction=True)
-
-    def remove_prefix(self, name, isfunction=False):
-        # when --strip-prefix=g:
-        #   GHashTable -> HashTable
-        #   g_hash_table_new -> hash_table_new
-        prefix = self._strip_prefix.lower()
-        if isfunction:
-            prefix += '_'
-        if len(name) > len(prefix) and name.lower().startswith(prefix):
-            name = name[len(prefix):]
-
-        while name.startswith('_'):
-            name = name[1:]
+        self._includes[namespace.name] = namespace
+
+    def _iter_namespaces(self):
+        """Return an iterator over all included namespaces; the
+currently-scanned namespace is first."""
+        yield self._namespace
+        for ns in self._includes.itervalues():
+            yield ns
+
+    def _sort_matches(self, x, y):
+        if x[0] is self._namespace:
+            return 1
+        elif y[0] is self._namespace:
+            return -1
+        return cmp(x[2], y[2])
+
+    def split_ctype_namespaces(self, ident):
+        """Given a StudlyCaps string identifier like FooBar, return a
+list of (namespace, stripped_identifier) sorted by namespace length,
+or raise ValueError.  As a special case, if the current namespace matches,
+it is always biggest (i.e. last)."""
+        matches = []
+        for ns in self._iter_namespaces():
+            for prefix in ns.identifier_prefixes:
+                if ident.startswith(prefix):
+                    matches.append((ns, ident[len(prefix):], len(prefix)))
+                    break
+        if matches:
+            matches.sort(self._sort_matches)
+            return map(lambda x: (x[0], x[1]), matches)
+        raise ValueError("Unknown namespace for identifier %r" % (ident, ))
+
+    def split_csymbol(self, symbol):
+        """Given a C symbol like foo_bar_do_baz, return a pair of
+(namespace, stripped_symbol) or raise ValueError."""
+        matches = []
+        for ns in self._iter_namespaces():
+            for prefix in ns.symbol_prefixes:
+                if not prefix.endswith('_'):
+                    prefix = prefix + '_'
+                if symbol.startswith(prefix):
+                    matches.append((ns, symbol[len(prefix):], len(prefix)))
+                    break
+        if matches:
+            matches.sort(self._sort_matches)
+            return (matches[-1][0], matches[-1][1])
+        raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
+
+    def strip_identifier_or_warn(self, ident, fatal=False):
+        hidden = ident.startswith('_')
+        if hidden:
+            ident = ident[1:]
+        try:
+            matches = self.split_ctype_namespaces(ident)
+        except ValueError, e:
+            self.log_warning(str(e), fatal=fatal)
+            return None
+        for ns, name in matches:
+            if ns is self._namespace:
+                if hidden:
+                    return '_' + name
+                return name
+        (ns, name) = matches[-1]
+        self.log_warning("Skipping foreign identifier %r from namespace %s" % (ident, ns.name, ),
+                         fatal=fatal)
+        return None
+
+    def _strip_symbol_or_warn(self, symbol, is_constant=False, fatal=False):
+        ident = symbol.ident
+        if is_constant:
+            # Temporarily lowercase
+            ident = ident.lower()
+        hidden = ident.startswith('_')
+        if hidden:
+            ident = ident[1:]
+        try:
+            (ns, name) = self.split_csymbol(ident)
+        except ValueError, e:
+            self.log_symbol_warning(symbol, "Unknown namespace", fatal=fatal)
+            return None
+        if ns != self._namespace:
+            self.log_symbol_warning(symbol,
+"Skipping foreign symbol from namespace %s" % (ns.name, ),
+                                    fatal=fatal)
+            return None
+        if is_constant:
+            name = name.upper()
+        if hidden:
+            return '_' + name
         return name
 
     def _traverse_one(self, symbol, stype=None):
@@ -268,17 +361,17 @@ context will be used."""
             return self._create_struct(symbol)
         elif stype == CSYMBOL_TYPE_ENUM:
             return self._create_enum(symbol)
-        elif stype == CSYMBOL_TYPE_OBJECT:
-            return self._create_object(symbol)
         elif stype == CSYMBOL_TYPE_MEMBER:
             return self._create_member(symbol)
         elif stype == CSYMBOL_TYPE_UNION:
             return self._create_union(symbol)
         elif stype == CSYMBOL_TYPE_CONST:
             return self._create_const(symbol)
+        # Ignore variable declarations in the header
+        elif stype == CSYMBOL_TYPE_OBJECT:
+            pass
         else:
-            raise NotImplementedError(
-                'Transformer: unhandled symbol: %r' % (symbol, ))
+            print 'transformer: unhandled symbol: %r' % (symbol, )
 
     def _enum_common_prefix(self, symbol):
         def common_prefix(a, b):
@@ -316,86 +409,31 @@ context will be used."""
                 # Ok, the enum members don't have a consistent prefix
                 # among them, so let's just remove the global namespace
                 # prefix.
-                name = self.remove_prefix(child.ident)
-            members.append(Member(name.lower(),
+                name = self._strip_symbol_or_warn(child, is_constant=True)
+                if name is None:
+                    return None
+            members.append(ast.Member(name.lower(),
                                   child.const_int,
                                   child.ident))
 
-        enum_name = self.remove_prefix(symbol.ident)
+        enum_name = self.strip_identifier_or_warn(symbol.ident)
+        if not enum_name:
+            return None
         if symbol.base_type.is_bitfield:
-            klass = Bitfield
+            klass = ast.Bitfield
         else:
-            klass = Enum
+            klass = ast.Enum
         node = klass(enum_name, symbol.ident, members)
         node.add_symbol_reference(symbol)
-        self._names.type_names[symbol.ident] = (None, node)
-        return node
-
-    def _create_object(self, symbol):
-        node = Member(symbol.ident, symbol.base_type.name,
-                      symbol.ident)
-        node.add_symbol_reference(symbol)
         return node
 
-    def _type_is_callback(self, type):
-        if isinstance(type, Callback):
-            return True
-        node = self._names.names.get(type.name)
-        if node and isinstance(node[1], Callback):
-            return True
-        return False
-
-    def _handle_closure(self, param, closure_idx, closure_param):
-        if (closure_param.type.name == TYPE_ANY and
-            closure_param.name.endswith('data')):
-            param.closure_name = closure_param.name
-            param.closure_index = closure_idx
-            return True
-        return False
-
-    def _handle_destroy(self, param, destroy_idx, destroy_param):
-        if (destroy_param.type.name == 'GLib.DestroyNotify' or
-            destroy_param.type.ctype == 'GDestroyNotify'):
-            param.destroy_name = destroy_param.name
-            param.destroy_index = destroy_idx
-            return True
-        return False
-
-    def _augment_callback_params(self, params):
-        for i, param in enumerate(params):
-            if not self._type_is_callback(param.type):
-                continue
-
-            # set a default scope
-            if param.scope is None:
-                param.scope = 'call'
-
-            # j is the index where we look for closure/destroy to
-            # group with the callback param
-            j = i + 1
-            if j == len(params):
-                continue # no more args -> nothing to group
-            # look at the param directly following for either a
-            # closure or a destroy; only one of these will fire
-            had_closure = self._handle_closure(param, j, params[j])
-            had_destroy = self._handle_destroy(param, j, params[j])
-            j += 1
-            # are we out of params, or did we find neither?
-            if j == len(params) or (not had_closure and not had_destroy):
-                continue
-            # we found either a closure or a destroy; check the
-            # parameter following for the other
-            if not had_closure:
-                self._handle_closure(param, j, params[j])
-            if not had_destroy:
-                self._handle_destroy(param, j, params[j])
-
     def _create_function(self, symbol):
         parameters = list(self._create_parameters(symbol.base_type))
         return_ = self._create_return(symbol.base_type.base_type)
-        self._augment_callback_params(parameters)
-        name = self._strip_namespace_func(symbol.ident)
-        func = Function(name, return_, parameters, symbol.ident)
+        name = self._strip_symbol_or_warn(symbol)
+        if not name:
+            return None
+        func = ast.Function(name, return_, parameters, False, symbol.ident)
         func.add_symbol_reference(symbol)
         return func
 
@@ -413,11 +451,10 @@ context will be used."""
         elif source_type.type == CTYPE_POINTER:
             value = self._create_source_type(source_type.base_type) + '*'
         else:
-            value = TYPE_ANY
+            value = 'gpointer'
         return value
 
     def _create_parameters(self, base_type):
-
         # warn if we see annotations for unknown parameters
         param_names = set(child.ident for child in base_type.child_list)
         for child in base_type.child_list:
@@ -427,7 +464,7 @@ context will be used."""
         source_type = symbol.base_type
         if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
-            node = self._create_callback(symbol)
+            node = self._create_callback(symbol, member=True)
         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
             node = self._create_struct(symbol, anonymous=True)
         elif source_type.type == CTYPE_UNION and source_type.name is None:
@@ -442,21 +479,18 @@ context will be used."""
                     derefed_name = canonical_ctype[:-1]
                 else:
                     derefed_name = canonical_ctype
-                derefed_name = self.resolve_param_type(derefed_name)
-                ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+                ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
+                                  ctype=derefed_name)
                 child_list = list(symbol.base_type.child_list)
                 ftype.zeroterminated = False
                 if child_list:
-                    ftype.size = '%d' % (child_list[0].const_int, )
+                    ftype.size = child_list[0].const_int
             else:
-                ftype = self._create_type(symbol.base_type,
-                                          is_param=False, is_retval=False)
-            ftype = self.resolve_param_type(ftype)
-            # Fields are assumed to be read-write
+                ftype = self._create_type_from_base(symbol.base_type)
+            # ast.Fields are assumed to be read-write
             # (except for Objects, see also glibtransformer.py)
-            node = Field(symbol.ident, ftype, ftype.name,
+            node = ast.Field(symbol.ident, ftype,
                          readable=True, writable=True, bits=symbol.const_int)
-            node.add_symbol_reference(symbol)
         return node
 
     def _create_typedef(self, symbol):
@@ -477,14 +511,16 @@ context will be used."""
                        CTYPE_POINTER,
                        CTYPE_BASIC_TYPE,
                        CTYPE_VOID):
-            name = self.remove_prefix(symbol.ident)
+            name = self.strip_identifier_or_warn(symbol.ident)
+            if not name:
+                return None
             if symbol.base_type.name:
-                target = self.remove_prefix(symbol.base_type.name)
+                target = self.create_type_from_ctype_string(symbol.base_type.name)
             else:
-                target = 'none'
-            if name in type_names:
+                target = ast.TYPE_ANY
+            if name in ast.type_names:
                 return None
-            return Alias(name, target, ctype=symbol.ident)
+            return ast.Alias(name, target, ctype=symbol.ident)
         else:
             raise NotImplementedError(
                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
@@ -494,22 +530,20 @@ context will be used."""
         # First look up the ctype including any pointers;
         # a few type names like 'char*' have their own aliases
         # and we need pointer information for those.
-        firstpass = type_name_from_ctype(ctype)
+        firstpass = ast.type_names.get(ctype)
 
         # If we have a particular alias for this, skip deep
         # canonicalization to prevent changing
         # e.g. char* -> int8*
-        if firstpass != ctype:
-            return firstpass
+        if firstpass:
+            return firstpass.target_fundamental
 
-        # We're also done if the type is already a fundamental
-        # known type, or there are no pointers.
-        if ctype in type_names or not firstpass.endswith('*'):
-            return firstpass
+        if not ctype.endswith('*'):
+            return ctype
 
         # We have a pointer type.
         # Strip the end pointer, canonicalize our base type
-        base = firstpass[:-1]
+        base = ctype[:-1]
         canonical_base = self._canonicalize_ctype(base)
 
         # Append the pointer again
@@ -527,52 +561,71 @@ context will be used."""
 
         # Preserve "pointerness" of struct/union members
         if (is_member and canonical.endswith('*') and
-            derefed_typename in BASIC_GIR_TYPES):
-            return TYPE_ANY
+            derefed_typename in ast.basic_type_names):
+            return 'gpointer'
         else:
             return derefed_typename
 
-    def _create_type(self, source_type, is_param, is_retval):
+    def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
         ctype = self._create_source_type(source_type)
-        if ctype.startswith('va_list'):
-            raise SkipError()
-        # FIXME: FILE* should not be skipped, it should be handled
-        #        properly instead
-        elif ctype == 'FILE*':
-            raise SkipError
-
-        is_member = not (is_param or is_retval)
-        # Here we handle basic type parsing; most of the heavy lifting
-        # and inference comes in annotationparser.py when we merge
-        # in annotation data.
-        derefed_name = self.parse_ctype(ctype, is_member)
-        rettype = Type(derefed_name, ctype)
-        rettype.canonical = self._canonicalize_ctype(ctype)
-        derefed_ctype = ctype.replace('*', '')
-        rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
-
-        canontype = type_name_from_ctype(ctype)
-        # Is it a const char * or a const gpointer?
-        if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
-            (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
-            rettype.is_const = True
-        return rettype
+        const = ((source_type.type == CTYPE_POINTER) and
+                 (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
+        return self.create_type_from_ctype_string(ctype, is_const=const,
+                                                  is_parameter=is_parameter, is_return=is_return)
+
+    def _create_bare_container_type(self, base, ctype=None,
+                                    is_const=False):
+        if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
+            if base in ('GList', 'GSList'):
+                name = 'GLib.' + base[1:]
+            else:
+                name = base
+            return ast.List(name, ast.TYPE_ANY, ctype=ctype,
+                        is_const=is_const)
+        elif base in ('GArray', 'GPtrArray', 'GByteArray',
+                      'GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray'):
+            if base in ('GArray', 'GPtrArray', 'GByteArray'):
+                name = 'GLib.' + base[1:]
+            else:
+                name = base
+            return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
+                         is_const=is_const)
+        elif base in ('GHashTable', 'GLib.HashTable'):
+            return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const)
+        return None
+
+    def create_type_from_ctype_string(self, ctype, is_const=False,
+                                      is_parameter=False, is_return=False):
+        canonical = self._canonicalize_ctype(ctype)
+        base = canonical.replace('*', '')
+
+        # Special default: char ** -> ast.Array, same for GStrv
+        if (is_return and canonical == 'utf8*') or base == 'GStrv':
+            bare_utf8 = ast.TYPE_STRING.clone()
+            bare_utf8.ctype = None
+            return ast.Array(None, bare_utf8, ctype=ctype,
+                             is_const=is_const)
+
+        fundamental = ast.type_names.get(base)
+        if fundamental is not None:
+            return ast.Type(target_fundamental=fundamental.target_fundamental,
+                        ctype=ctype,
+                        is_const=is_const)
+        container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const)
+        if container:
+            return container
+        return ast.Type(ctype=ctype, is_const=is_const)
 
     def _create_parameter(self, symbol):
         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
-            ptype = Varargs()
+            ptype = ast.Varargs()
         else:
-            ptype = self._create_type(symbol.base_type,
-                                      is_param=True, is_retval=False)
-            ptype = self.resolve_param_type(ptype)
-        return Parameter(symbol.ident, ptype)
+            ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
+        return ast.Parameter(symbol.ident, ptype)
 
     def _create_return(self, source_type):
-        rtype = self._create_type(source_type,
-                                  is_param=False, is_retval=True)
-        rtype = self.resolve_param_type(rtype)
-        return_ = Return(rtype)
-        return return_
+        typeval = self._create_type_from_base(source_type, is_return=True)
+        return ast.Return(typeval)
 
     def _create_const(self, symbol):
         # Don't create constants for non-public things
@@ -580,44 +633,67 @@ context will be used."""
         if (symbol.source_filename is None or
             not symbol.source_filename.endswith('.h')):
             return None
-        name = self._strip_namespace_func(symbol.ident)
+        # ignore non-uppercase defines
+        if not self.UCASE_CONSTANT_RE.match(symbol.ident):
+            return None
+        name = self._strip_symbol_or_warn(symbol, is_constant=True)
+        if not name:
+            return None
         if symbol.const_string is not None:
-            type_name = 'utf8'
+            typeval = ast.TYPE_STRING
             value = symbol.const_string
         elif symbol.const_int is not None:
-            type_name = 'gint'
-            value = symbol.const_int
+            typeval = ast.TYPE_INT
+            value = '%d' % (symbol.const_int, )
         elif symbol.const_double is not None:
-            type_name = 'gdouble'
-            value = symbol.const_double
+            typeval = ast.TYPE_DOUBLE
+            value = '%f' % (symbol.const_double, )
         else:
             raise AssertionError()
 
-        const = Constant(name, type_name, value)
+        const = ast.Constant(name, typeval, value)
         const.add_symbol_reference(symbol)
         return const
 
     def _create_typedef_struct(self, symbol, disguised=False):
-        name = self.remove_prefix(symbol.ident)
-        struct = Struct(name, symbol.ident, disguised)
+        name = self.strip_identifier_or_warn(symbol.ident)
+        if not name:
+            return None
+        struct = ast.Record(name, symbol.ident, disguised)
+        self._parse_fields(symbol, struct)
         struct.add_symbol_reference(symbol)
         self._typedefs_ns[symbol.ident] = struct
-        self._create_struct(symbol)
-        return struct
+        return None
 
     def _create_typedef_union(self, symbol):
-        name = self.remove_prefix(symbol.ident)
-        union = Union(name, symbol.ident)
+        name = self.strip_identifier_or_warn(symbol.ident)
+        if not name:
+            return None
+        union = ast.Union(name, symbol.ident)
+        self._parse_fields(symbol, union)
         union.add_symbol_reference(symbol)
         self._typedefs_ns[symbol.ident] = union
-        self._create_union(symbol)
-        return union
+        return None
 
     def _create_typedef_callback(self, symbol):
         callback = self._create_callback(symbol)
+        if not callback:
+            return None
         self._typedefs_ns[callback.name] = callback
         return callback
 
+    def _parse_fields(self, symbol, compound):
+        for child in symbol.base_type.child_list:
+            child_node = self._traverse_one(child)
+            if not child_node:
+                continue
+            if isinstance(child_node, ast.Field):
+                field = child_node
+            else:
+                field = ast.Field(child.ident, None, True, False,
+                              anonymous_node=child_node)
+            compound.fields.append(field)
+
     def _create_compound(self, klass, symbol, anonymous):
         if symbol.ident is None:
             # the compound is an anonymous member of another union or a struct
@@ -631,100 +707,95 @@ context will be used."""
             # to resolve through the typedefs to find the real
             # name
             if symbol.ident.startswith('_'):
-                name = symbol.ident[1:]
-                compound = self._typedefs_ns.get(name, None)
-            else:
-                name = symbol.ident
+                compound = self._typedefs_ns.get(symbol.ident[1:], None)
             if compound is None:
-                name = self.remove_prefix(name)
+                if anonymous:
+                    name = symbol.ident
+                else:
+                    name = self.strip_identifier_or_warn(symbol.ident)
+                    if not name:
+                        return None
                 compound = klass(name, symbol.ident)
 
-        for child in symbol.base_type.child_list:
-            field = self._traverse_one(child)
-            if field:
-                compound.fields.append(field)
-
+        self._parse_fields(symbol, compound)
         compound.add_symbol_reference(symbol)
         return compound
 
     def _create_struct(self, symbol, anonymous=False):
-        return self._create_compound(Struct, symbol, anonymous)
+        return self._create_compound(ast.Record, symbol, anonymous)
 
     def _create_union(self, symbol, anonymous=False):
-        return self._create_compound(Union, symbol, anonymous)
+        return self._create_compound(ast.Union, symbol, anonymous)
 
-    def _create_callback(self, symbol):
+    def _create_callback(self, symbol, member=False):
         parameters = list(self._create_parameters(symbol.base_type.base_type))
         retval = self._create_return(symbol.base_type.base_type.base_type)
 
         # Mark the 'user_data' arguments
         for i, param in enumerate(parameters):
-            if (param.type.name == TYPE_ANY and
-                param.name == 'user_data'):
-                param.closure_index = i
-
-        if symbol.ident.find('_') > 0:
-            name = self.remove_prefix(symbol.ident, True)
+            if (param.type.target_fundamental == 'gpointer' and
+                param.argname == 'user_data'):
+                param.closure_name = param.argname
+
+        if member:
+            name = symbol.ident
+        elif symbol.ident.find('_') > 0:
+            name = self._strip_symbol_or_warn(symbol)
+            if not name:
+                return None
         else:
-            name = self.remove_prefix(symbol.ident)
-        callback = Callback(name, retval, parameters, symbol.ident)
+            name = self.strip_identifier_or_warn(symbol.ident)
+            if not name:
+                return None
+        callback = ast.Callback(name, retval, parameters, False)
         callback.add_symbol_reference(symbol)
 
         return callback
 
+    def create_type_from_user_string(self, typestr):
+        """Parse a C type string (as might be given from an
+        annotation) and resolve it.  For compatibility, we can consume
+both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style."""
+        if '.' in typestr:
+            container = self._create_bare_container_type(typestr)
+            if container:
+                return container
+            return self._namespace.type_from_name(typestr)
+        typeval = self.create_type_from_ctype_string(typestr)
+        self.resolve_type(typeval)
+        # Explicitly clear out the c_type; there isn't one in this case.
+        typeval.ctype = None
+        return typeval
+
+    def resolve_type(self, typeval):
+        if isinstance(typeval, (ast.Array, ast.List)):
+            self.resolve_type(typeval.element_type)
+            return
+        elif isinstance(typeval, ast.Map):
+            self.resolve_type(typeval.key_type)
+            self.resolve_type(typeval.value_type)
+            return
+        elif not typeval.resolved and typeval.ctype:
+            pointer_stripped = typeval.ctype.replace('*', '')
+            try:
+                matches = self.split_ctype_namespaces(pointer_stripped)
+            except ValueError, e:
+                raise TypeResolutionException(e)
+            target_giname=None
+            for namespace, name in matches:
+                target = namespace.get(name)
+                if not target:
+                    target = namespace.get_by_ctype(pointer_stripped)
+                if target:
+                    typeval.target_giname='%s.%s' % (namespace.name, target.name)
+                    return
+
     def _typepair_to_str(self, item):
         nsname, item = item
         if nsname is None:
             return item.name
         return '%s.%s' % (nsname, item.name)
 
-    def _resolve_type_name_1(self, type_name, ctype, names):
-        # First look using the built-in names
-        if ctype:
-            try:
-                return type_names[ctype]
-            except KeyError, e:
-                pass
-        try:
-            return type_names[type_name]
-        except KeyError, e:
-            pass
-
-        if ctype:
-            ctype = ctype.replace('*', '')
-            resolved = names.ctypes.get(ctype)
-            if resolved:
-                return self._typepair_to_str(resolved)
-        type_name = self.remove_prefix(type_name)
-        resolved = names.aliases.get(type_name)
-        if resolved:
-            return self._typepair_to_str(resolved)
-        resolved = names.names.get(type_name)
-        if resolved:
-            return self._typepair_to_str(resolved)
-        resolved = names.type_names.get(type_name)
-        if resolved:
-            return self._typepair_to_str(resolved)
-        raise KeyError("failed to find %r" % (type_name, ))
-
-    def resolve_type_name_full(self, type_name, ctype,
-                               names, allow_invalid=True):
-        try:
-            return self._resolve_type_name_1(type_name, ctype, names)
-        except KeyError, e:
-            try:
-                return self._resolve_type_name_1(type_name, ctype, self._names)
-            except KeyError, e:
-                if not allow_invalid:
-                    raise
-                return type_name
-
-    def resolve_type_name(self, type_name, ctype=None):
-        try:
-            return self.resolve_type_name_full(type_name, ctype, self._names)
-        except KeyError, e:
-            return type_name
-
     def gtypename_to_giname(self, gtname, names):
         resolved = names.type_names.get(gtname)
         if resolved:
@@ -742,23 +813,6 @@ context will be used."""
         else:
             return None
 
-    def resolve_param_type_full(self, ptype, names, **kwargs):
-        if isinstance(ptype, Node):
-            ptype.name = self.resolve_type_name_full(ptype.name,
-                                                     self.ctype_of(ptype),
-                                                     names, **kwargs)
-        elif isinstance(ptype, basestring):
-            return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
-        else:
-            raise AssertionError("Unhandled param: %r" % (ptype, ))
-        return ptype
-
-    def resolve_param_type(self, ptype):
-        try:
-            return self.resolve_param_type_full(ptype, self._names)
-        except KeyError, e:
-            return ptype
-
     def follow_aliases(self, type_name, names):
         while True:
             resolved = names.aliases.get(type_name)
@@ -768,8 +822,3 @@ context will be used."""
             else:
                 break
         return type_name
-
-    def iter_enums(self):
-        for node in self._namespace.nodes:
-            if isinstance(node, Enum):
-                yield node
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 1bd23fc..dba958e 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -102,3 +102,16 @@ def get_libtool_command(options):
         return None
 
     return ['libtool']
+
+
+def files_are_identical(path1, path2):
+    f1 = open(path1)
+    f2 = open(path2)
+    buf1 = f1.read(8192)
+    buf2 = f2.read(8192)
+    while buf1 == buf2 and buf1 != '':
+        buf1 = f1.read(8192)
+        buf2 = f2.read(8192)
+    f1.close()
+    f2.close()
+    return buf1 == buf2
diff --git a/misc/pep8.py b/misc/pep8.py
index fef8a54..1a50efc 100644
--- a/misc/pep8.py
+++ b/misc/pep8.py
@@ -183,7 +183,7 @@ def maximum_line_length(physical_line):
     length to 72 characters is recommended.
     """
     length = len(physical_line.rstrip())
-    if length > 79:
+    if length > 99:
         return 79, "E501 line too long (%d characters)" % length
 
 
diff --git a/tests/scanner/Annotation-1.0-expected.gir b/tests/scanner/Annotation-1.0-expected.gir
index 1d73c25..086f063 100644
--- a/tests/scanner/Annotation-1.0-expected.gir
+++ b/tests/scanner/Annotation-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -13,7 +13,8 @@ and/or use gtk-doc annotations.  -->
   <namespace name="Annotation"
              version="1.0"
              shared-library="libannotation.so"
-             c:prefix="Annotation">
+             c:identifier-prefixes="Annotation"
+             c:symbol-prefixes="annotation">
     <callback name="Callback" c:type="AnnotationCallback">
       <doc xml:whitespace="preserve">This is a callback.</doc>
       <return-value transfer-ownership="none">
@@ -74,6 +75,7 @@ and/or use gtk-doc annotations.  -->
       </parameters>
     </callback>
     <class name="Object"
+           c:symbol-prefix="object"
            c:type="AnnotationObject"
            parent="GObject.Object"
            glib:type-name="AnnotationObject"
@@ -154,7 +156,7 @@ and/or use gtk-doc annotations.  -->
         <parameters>
           <parameter name="nums" transfer-ownership="none">
             <doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
-            <array zero-terminated="0" length="2" c:type="int*">
+            <array length="1" zero-terminated="0" c:type="int*">
               <type name="gint"/>
             </array>
           </parameter>
@@ -173,7 +175,7 @@ and/or use gtk-doc annotations.  -->
         <parameters>
           <parameter name="nums" transfer-ownership="none">
             <doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
-            <array length="2" c:type="int*">
+            <array length="1" c:type="int*">
               <type name="gint"/>
             </array>
           </parameter>
@@ -215,7 +217,7 @@ and/or use gtk-doc annotations.  -->
           <parameter name="func"
                      transfer-ownership="none"
                      scope="call"
-                     closure="2">
+                     closure="1">
             <doc xml:whitespace="preserve">Callback to invoke</doc>
             <type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
           </parameter>
@@ -365,7 +367,7 @@ each string needs to be freed.</doc>
                      caller-allocates="0"
                      transfer-ownership="full">
             <doc xml:whitespace="preserve">Argument vector</doc>
-            <array length="1" c:type="char***">
+            <array length="0" c:type="char***">
               <type name="utf8"/>
             </array>
           </parameter>
@@ -379,7 +381,7 @@ each string needs to be freed.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="guchar*">
+            <array length="1" c:type="guchar*">
               <type name="guint8"/>
             </array>
           </parameter>
@@ -397,7 +399,7 @@ each string needs to be freed.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="gchar*">
+            <array length="1" c:type="gchar*">
               <type name="gint8"/>
             </array>
           </parameter>
@@ -416,7 +418,7 @@ type.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="gpointer">
+            <array length="1" c:type="gpointer">
               <type name="guint8"/>
             </array>
           </parameter>
@@ -448,13 +450,33 @@ type.</doc>
         </return-value>
         <parameters>
           <parameter name="bytes" transfer-ownership="none">
-            <array c:type="guchar*">
-              <type name="guint8"/>
-            </array>
+            <type name="guint8" c:type="guchar*"/>
           </parameter>
         </parameters>
       </method>
-      <method name="watch" c:identifier="annotation_object_watch_full">
+      <method name="watch"
+              c:identifier="annotation_object_watch"
+              shadows="annotation_object_watch_full"
+              introspectable="0">
+        <doc xml:whitespace="preserve">This is here just for the sake of being overriden by its
+annotation_object_watch_full().</doc>
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <parameter name="func" transfer-ownership="none" closure="1">
+            <doc xml:whitespace="preserve">The callback</doc>
+            <type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
+          </parameter>
+          <parameter name="user_data" transfer-ownership="none">
+            <doc xml:whitespace="preserve">The callback data</doc>
+            <type name="gpointer" c:type="gpointer"/>
+          </parameter>
+        </parameters>
+      </method>
+      <method name="watch_full"
+              c:identifier="annotation_object_watch_full"
+              shadowed-by="annotation_object_watch">
         <doc xml:whitespace="preserve">Test overriding via the "Rename To" annotation.</doc>
         <return-value transfer-ownership="none">
           <type name="none" c:type="void"/>
@@ -463,8 +485,8 @@ type.</doc>
           <parameter name="func"
                      transfer-ownership="none"
                      scope="notified"
-                     closure="2"
-                     destroy="3">
+                     closure="1"
+                     destroy="2">
             <doc xml:whitespace="preserve">The callback</doc>
             <type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
           </parameter>
@@ -472,7 +494,7 @@ type.</doc>
             <doc xml:whitespace="preserve">The callback data</doc>
             <type name="gpointer" c:type="gpointer"/>
           </parameter>
-          <parameter name="destroy" transfer-ownership="none" scope="call">
+          <parameter name="destroy" transfer-ownership="none" scope="async">
             <doc xml:whitespace="preserve">Destroy notification</doc>
             <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
           </parameter>
@@ -492,7 +514,7 @@ type.</doc>
                 writable="1"
                 construct="1"
                 transfer-ownership="none">
-        <type name="Callback" c:type="gpointer"/>
+        <type name="Callback"/>
       </property>
       <property name="string-property"
                 version="1.0"
@@ -502,7 +524,7 @@ type.</doc>
                 construct="1"
                 transfer-ownership="none">
         <doc xml:whitespace="preserve">This is a property which is a string</doc>
-        <type name="utf8" c:type="gchararray"/>
+        <type name="utf8"/>
       </property>
       <field name="parent_instance">
         <type name="GObject.Object" c:type="GObject"/>
@@ -512,42 +534,42 @@ type.</doc>
         <return-value transfer-ownership="full">
           <attribute name="some.annotation.foo3" value="val3"/>
           <doc xml:whitespace="preserve">the return value</doc>
-          <type name="utf8" c:type="gchararray"/>
+          <type name="utf8"/>
         </return-value>
         <parameters>
-          <parameter name="arg1" transfer-ownership="none">
+          <parameter name="object" transfer-ownership="none">
             <attribute name="some.annotation.foo1" value="val1"/>
             <doc xml:whitespace="preserve">a value</doc>
-            <type name="utf8" c:type="gchararray"/>
+            <type name="utf8"/>
           </parameter>
-          <parameter name="arg2" transfer-ownership="none">
+          <parameter name="p0" transfer-ownership="none">
             <attribute name="some.annotation.foo2" value="val2"/>
             <doc xml:whitespace="preserve">another value</doc>
-            <type name="utf8" c:type="gchararray"/>
+            <type name="utf8"/>
           </parameter>
         </parameters>
       </glib:signal>
       <glib:signal name="doc-empty-arg-parsing">
         <doc xml:whitespace="preserve">This signal tests an empty document argument (@arg1)</doc>
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
         <parameters>
           <parameter name="object" transfer-ownership="none">
-            <type name="gpointer" c:type="gpointer"/>
+            <type name="gpointer"/>
           </parameter>
         </parameters>
       </glib:signal>
       <glib:signal name="list-signal">
         <doc xml:whitespace="preserve">This is a signal which takes a list of strings, but it's not
 known by GObject as it's only marked as G_TYPE_POINTER</doc>
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
         <parameters>
-          <parameter name="list" transfer-ownership="container">
+          <parameter name="object" transfer-ownership="container">
             <doc xml:whitespace="preserve">a list of strings</doc>
-            <type name="GLib.List" c:type="gpointer">
+            <type name="GLib.List">
               <type name="utf8"/>
             </type>
           </parameter>
@@ -559,13 +581,13 @@ known by GObject as it's only marked as G_TYPE_POINTER</doc>
                    deprecated-version="1.2">
         <doc xml:whitespace="preserve">This is a signal which has a broken signal handler,
 it says it's pointer but it's actually a string.</doc>
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
         <parameters>
-          <parameter name="string" transfer-ownership="none">
+          <parameter name="object" transfer-ownership="none">
             <doc xml:whitespace="preserve">a string</doc>
-            <type name="utf8" c:type="gpointer"/>
+            <type name="utf8"/>
           </parameter>
         </parameters>
       </glib:signal>
@@ -580,8 +602,8 @@ it says it's pointer but it's actually a string.</doc>
     <record name="Struct" c:type="AnnotationStruct">
       <doc xml:whitespace="preserve">This is a test of an array of object in an field of a struct.</doc>
       <field name="objects" writable="1">
-        <array zero-terminated="0" c:type="AnnotationObject*" fixed-size="10">
-          <type name="Object"/>
+        <array zero-terminated="0" c:type="AnnotationObject" fixed-size="10">
+          <type name="Object" c:type="AnnotationObject*"/>
         </array>
       </field>
     </record>
@@ -614,13 +636,16 @@ detection, and fixing it via annotations.</doc>
       <parameters>
         <parameter name="callback"
                    transfer-ownership="none"
-                   scope="call"
+                   scope="notified"
                    closure="2"
                    destroy="1">
           <doc xml:whitespace="preserve">Destroy notification</doc>
           <type name="Callback" c:type="AnnotationCallback"/>
         </parameter>
-        <parameter name="destroy" transfer-ownership="none" scope="call">
+        <parameter name="destroy"
+                   transfer-ownership="none"
+                   scope="notified"
+                   closure="2">
           <type name="NotifyFunc" c:type="AnnotationNotifyFunc"/>
         </parameter>
         <parameter name="data" transfer-ownership="none">
@@ -631,7 +656,7 @@ detection, and fixing it via annotations.</doc>
     <function name="get_source_file" c:identifier="annotation_get_source_file">
       <return-value transfer-ownership="full">
         <doc xml:whitespace="preserve">Source file</doc>
-        <type name="filename" c:type="char*"/>
+        <type name="filename"/>
       </return-value>
     </function>
     <function name="init" c:identifier="annotation_init">
@@ -669,7 +694,9 @@ detection, and fixing it via annotations.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="ptr_array" c:identifier="annotation_ptr_array">
+    <function name="ptr_array"
+              c:identifier="annotation_ptr_array"
+              introspectable="0">
       <return-value transfer-ownership="none">
         <type name="none" c:type="void"/>
       </return-value>
@@ -706,7 +733,7 @@ detection, and fixing it via annotations.</doc>
       <parameters>
         <parameter name="fname" transfer-ownership="none">
           <doc xml:whitespace="preserve">Source file</doc>
-          <type name="filename" c:type="char*"/>
+          <type name="filename"/>
         </parameter>
       </parameters>
     </function>
diff --git a/tests/scanner/Bar-1.0-expected.gir b/tests/scanner/Bar-1.0-expected.gir
index dee28b3..4d919fd 100644
--- a/tests/scanner/Bar-1.0-expected.gir
+++ b/tests/scanner/Bar-1.0-expected.gir
@@ -2,15 +2,20 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
   <include name="GLib" version="2.0"/>
   <include name="GObject" version="2.0"/>
   <package name="gobject-2.0"/>
-  <namespace name="Bar" version="1.0" shared-library="" c:prefix="Bar">
+  <namespace name="Bar"
+             version="1.0"
+             shared-library=""
+             c:identifier-prefixes="Bar"
+             c:symbol-prefixes="bar">
     <class name="Baz"
+           c:symbol-prefix="baz"
            c:type="BarBaz"
            parent="GObject.Object"
            glib:type-name="BarBaz"
diff --git a/tests/scanner/Foo-1.0-expected.gir b/tests/scanner/Foo-1.0-expected.gir
index 003485b..c52a923 100644
--- a/tests/scanner/Foo-1.0-expected.gir
+++ b/tests/scanner/Foo-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -15,12 +15,13 @@ and/or use gtk-doc annotations.  -->
   <namespace name="Foo"
              version="1.0"
              shared-library="libfoo.so"
-             c:prefix="Foo">
+             c:identifier-prefixes="Foo"
+             c:symbol-prefixes="foo">
     <alias name="ObjectCookie" c:type="FooObjectCookie">
-      <type name="gpointer"/>
+      <type name="gpointer" c:type="gpointer"/>
     </alias>
     <alias name="XEvent" c:type="FooXEvent">
-      <type name="none"/>
+      <type name="gpointer" c:type="gpointer"/>
     </alias>
     <enumeration name="ASingle" c:type="FooASingle">
       <member name="some_single_enum"
@@ -35,7 +36,8 @@ and/or use gtk-doc annotations.  -->
     <record name="BRect"
             c:type="FooBRect"
             glib:type-name="FooBRect"
-            glib:get-type="foo_brect_get_type">
+            glib:get-type="foo_brect_get_type"
+            c:symbol-prefix="brect">
       <field name="x" writable="1">
         <type name="gdouble" c:type="double"/>
       </field>
@@ -69,7 +71,8 @@ and/or use gtk-doc annotations.  -->
     <union name="BUnion"
            c:type="FooBUnion"
            glib:type-name="FooBUnion"
-           glib:get-type="foo_bunion_get_type">
+           glib:get-type="foo_bunion_get_type"
+           c:symbol-prefix="bunion">
       <field name="type" writable="1">
         <type name="gint" c:type="int"/>
       </field>
@@ -94,7 +97,8 @@ and/or use gtk-doc annotations.  -->
     <record name="Boxed"
             c:type="FooBoxed"
             glib:type-name="FooBoxed"
-            glib:get-type="foo_boxed_get_type">
+            glib:get-type="foo_boxed_get_type"
+            c:symbol-prefix="boxed">
       <constructor name="new" c:identifier="foo_boxed_new">
         <return-value transfer-ownership="full">
           <type name="Boxed" c:type="FooBoxed*"/>
@@ -107,6 +111,7 @@ and/or use gtk-doc annotations.  -->
       </method>
     </record>
     <class name="Buffer"
+           c:symbol-prefix="buffer"
            c:type="FooBuffer"
            parent="Object"
            glib:type-name="FooBuffer"
@@ -121,6 +126,7 @@ and/or use gtk-doc annotations.  -->
     </class>
     <record name="BufferClass"
             c:type="FooBufferClass"
+            disguised="1"
             glib:is-gtype-struct-for="Buffer">
     </record>
     <callback name="Callback" c:type="FooCallback">
@@ -142,7 +148,8 @@ and/or use gtk-doc annotations.  -->
     <record name="DBusData"
             c:type="FooDBusData"
             glib:type-name="FooDBusData"
-            glib:get-type="foo_dbus_data_get_type">
+            glib:get-type="foo_dbus_data_get_type"
+            c:symbol-prefix="dbus_data">
       <method name="method" c:identifier="foo_dbus_data_method">
         <return-value transfer-ownership="none">
           <type name="none" c:type="void"/>
@@ -150,7 +157,7 @@ and/or use gtk-doc annotations.  -->
       </method>
     </record>
     <constant name="DEFINE_SHOULD_BE_EXPOSED" value="should be exposed">
-      <type name="utf8"/>
+      <type name="utf8" c:type="gchar*"/>
     </constant>
     <enumeration name="EnumFullname" c:type="FooEnumFullname">
       <member name="one" value="1" c:identifier="FOO_ENUM_FULLNAME_ONE"/>
@@ -250,6 +257,7 @@ and/or use gtk-doc annotations.  -->
       </field>
     </record>
     <interface name="Interface"
+               c:symbol-prefix="interface"
                c:type="FooInterface"
                glib:type-name="FooInterface"
                glib:get-type="foo_interface_get_type"
@@ -282,7 +290,7 @@ and/or use gtk-doc annotations.  -->
         <type name="GObject.TypeInterface" c:type="GTypeInterface"/>
       </field>
       <field name="do_foo">
-        <callback name="do_foo" c:type="do_foo">
+        <callback name="do_foo">
           <return-value transfer-ownership="none">
             <type name="none" c:type="void"/>
           </return-value>
@@ -298,6 +306,7 @@ and/or use gtk-doc annotations.  -->
       </field>
     </record>
     <class name="Object"
+           c:symbol-prefix="object"
            c:type="FooObject"
            parent="GObject.Object"
            glib:type-name="FooObject"
@@ -314,7 +323,7 @@ and/or use gtk-doc annotations.  -->
 uses a C sugar return type.</doc>
         <return-value transfer-ownership="none">
           <doc xml:whitespace="preserve">The global #FooSubobject</doc>
-          <type name="Subobject" c:type="FooObject*"/>
+          <type name="Subobject"/>
         </return-value>
       </function>
       <function name="static_meth" c:identifier="foo_object_static_meth">
@@ -388,7 +397,7 @@ uses a C sugar return type.</doc>
               c:identifier="foo_object_new_cookie"
               introspectable="0">
         <doc xml:whitespace="preserve">Not sure why this test is here...</doc>
-        <return-value transfer-ownership="full">
+        <return-value>
           <type name="ObjectCookie" c:type="FooObjectCookie"/>
         </return-value>
         <parameters>
@@ -460,11 +469,18 @@ uses a C sugar return type.</doc>
           </parameter>
         </parameters>
       </method>
+      <property name="hidden"
+                introspectable="0"
+                writable="1"
+                construct-only="1"
+                transfer-ownership="none">
+        <type/>
+      </property>
       <property name="string"
                 writable="1"
                 construct="1"
                 transfer-ownership="none">
-        <type name="utf8" c:type="gchararray"/>
+        <type name="utf8"/>
       </property>
       <field name="parent_instance">
         <type name="GObject.Object" c:type="GObject"/>
@@ -474,14 +490,14 @@ uses a C sugar return type.</doc>
       </field>
       <glib:signal name="signal">
         <return-value transfer-ownership="full">
-          <type name="utf8" c:type="gchararray"/>
+          <type name="utf8"/>
         </return-value>
         <parameters>
           <parameter name="object" transfer-ownership="none">
-            <type name="GObject.Object" c:type="GObject"/>
+            <type name="GObject.Object"/>
           </parameter>
           <parameter name="p0" transfer-ownership="none">
-            <type name="gpointer" c:type="gpointer"/>
+            <type name="gpointer"/>
           </parameter>
         </parameters>
       </glib:signal>
@@ -493,7 +509,7 @@ uses a C sugar return type.</doc>
         <type name="GObject.ObjectClass" c:type="GObjectClass"/>
       </field>
       <field name="virtual_method">
-        <callback name="virtual_method" c:type="virtual_method">
+        <callback name="virtual_method">
           <return-value transfer-ownership="none">
             <type name="gboolean" c:type="gboolean"/>
           </return-value>
@@ -508,7 +524,7 @@ uses a C sugar return type.</doc>
         </callback>
       </field>
       <field name="read_fn">
-        <callback name="read_fn" c:type="read_fn">
+        <callback name="read_fn">
           <return-value transfer-ownership="none">
             <type name="none" c:type="void"/>
           </return-value>
@@ -529,12 +545,12 @@ uses a C sugar return type.</doc>
       </field>
       <field name="_reserved">
         <array zero-terminated="0" c:type="GCallback" fixed-size="4">
-          <type name="GObject.Callback"/>
+          <type name="GObject.Callback" c:type="GCallback"/>
         </array>
       </field>
     </record>
-    <constant name="PIE_IS_TASTY" value="3.14159">
-      <type name="gdouble"/>
+    <constant name="PIE_IS_TASTY" value="3.141590">
+      <type name="gdouble" c:type="gdouble"/>
     </constant>
     <record name="Rectangle" c:type="FooRectangle">
       <field name="x" writable="1">
@@ -581,7 +597,7 @@ uses a C sugar return type.</doc>
       </method>
     </record>
     <constant name="SUCCESS_INT" value="4408">
-      <type name="gint"/>
+      <type name="gint" c:type="gint"/>
     </constant>
     <enumeration name="Skippable" introspectable="0" c:type="FooSkippable">
       <doc xml:whitespace="preserve">Some type that is only interesting from C and should not be
@@ -612,9 +628,10 @@ exposed to language bindings.</doc>
         <type name="gint" c:type="int"/>
       </field>
     </record>
-    <record name="StructPrivate" c:type="FooStructPrivate">
+    <record name="StructPrivate" c:type="FooStructPrivate" disguised="1">
     </record>
     <interface name="SubInterface"
+               c:symbol-prefix="sub_interface"
                c:type="FooSubInterface"
                glib:type-name="FooSubInterface"
                glib:get-type="foo_sub_interface_get_type"
@@ -631,8 +648,8 @@ exposed to language bindings.</doc>
         </return-value>
       </method>
       <glib:signal name="destroy-event">
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
       </glib:signal>
     </interface>
@@ -643,7 +660,7 @@ exposed to language bindings.</doc>
         <type name="GObject.TypeInterface" c:type="GTypeInterface"/>
       </field>
       <field name="destroy_event">
-        <callback name="destroy_event" c:type="destroy_event">
+        <callback name="destroy_event">
           <return-value transfer-ownership="none">
             <type name="none" c:type="void"/>
           </return-value>
@@ -655,7 +672,7 @@ exposed to language bindings.</doc>
         </callback>
       </field>
       <field name="do_bar">
-        <callback name="do_bar" c:type="do_bar">
+        <callback name="do_bar">
           <return-value transfer-ownership="none">
             <type name="none" c:type="void"/>
           </return-value>
@@ -668,6 +685,7 @@ exposed to language bindings.</doc>
       </field>
     </record>
     <class name="Subobject"
+           c:symbol-prefix="subobject"
            c:type="FooSubobject"
            parent="Object"
            abstract="1"
@@ -699,12 +717,12 @@ exposed to language bindings.</doc>
         <type name="gint" c:type="int"/>
       </field>
       <field name="lines" writable="1">
-        <array zero-terminated="0" c:type="char" fixed-size="80">
-          <type name="gchar"/>
+        <array zero-terminated="0" c:type="gchar" fixed-size="80">
+          <type name="gchar" c:type="char"/>
         </array>
       </field>
       <field name="data" writable="1">
-        <type name="gpointer" c:type="guchar*"/>
+        <type name="guint8" c:type="guchar*"/>
       </field>
     </record>
     <union name="Union" c:type="FooUnion">
@@ -769,7 +787,7 @@ exposed to language bindings.</doc>
         <parameter name="data" transfer-ownership="none">
           <type name="gpointer" c:type="gpointer"/>
         </parameter>
-        <parameter name="destroy" transfer-ownership="none" scope="call">
+        <parameter name="destroy" transfer-ownership="none" scope="async">
           <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
         </parameter>
       </parameters>
@@ -785,7 +803,7 @@ exposed to language bindings.</doc>
       </parameters>
     </function>
     <function name="enum_type_returnv" c:identifier="foo_enum_type_returnv">
-      <return-value transfer-ownership="full">
+      <return-value transfer-ownership="none">
         <type name="EnumType" c:type="FooEnumType"/>
       </return-value>
       <parameters>
@@ -794,6 +812,11 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </function>
+    <function name="error_quark" c:identifier="foo_error_quark">
+      <return-value transfer-ownership="none">
+        <type name="GLib.Quark" c:type="GQuark"/>
+      </return-value>
+    </function>
     <function name="init" c:identifier="foo_init">
       <return-value transfer-ownership="none">
         <type name="gint" c:type="gint"/>
@@ -832,6 +855,36 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </function>
+    <function name="some_variant"
+              c:identifier="foo_some_variant"
+              introspectable="0">
+      <return-value transfer-ownership="none">
+        <type name="none" c:type="void"/>
+      </return-value>
+      <parameters>
+        <parameter name="x" transfer-ownership="none">
+          <type name="guint" c:type="guint"/>
+        </parameter>
+        <parameter name="args" transfer-ownership="none">
+          <type name="va_list" c:type="va_list"/>
+        </parameter>
+      </parameters>
+    </function>
+    <function name="some_variant_ptr"
+              c:identifier="foo_some_variant_ptr"
+              introspectable="0">
+      <return-value transfer-ownership="none">
+        <type name="none" c:type="void"/>
+      </return-value>
+      <parameters>
+        <parameter name="x" transfer-ownership="none">
+          <type name="guint" c:type="guint"/>
+        </parameter>
+        <parameter name="args" transfer-ownership="none">
+          <type name="va_list" c:type="va_list*"/>
+        </parameter>
+      </parameters>
+    </function>
     <function name="test_array" c:identifier="foo_test_array">
       <return-value transfer-ownership="container">
         <array name="GLib.Array" c:type="GArray*">
@@ -929,7 +982,7 @@ exposed to language bindings.</doc>
         <parameter name="i" transfer-ownership="none">
           <type name="gint" c:type="gint"/>
         </parameter>
-        <parameter name="callback" transfer-ownership="none" scope="call">
+        <parameter name="callback" transfer-ownership="none">
           <type name="VarargsCallback" c:type="FooVarargsCallback"/>
         </parameter>
       </parameters>
@@ -941,7 +994,7 @@ exposed to language bindings.</doc>
         <type name="none" c:type="void"/>
       </return-value>
       <parameters>
-        <parameter name="callback" transfer-ownership="none" scope="call">
+        <parameter name="callback" transfer-ownership="none">
           <type name="VarargsCallback" c:type="FooVarargsCallback"/>
         </parameter>
       </parameters>
@@ -953,10 +1006,10 @@ exposed to language bindings.</doc>
         <type name="none" c:type="void"/>
       </return-value>
       <parameters>
-        <parameter name="callback" transfer-ownership="none" scope="call">
+        <parameter name="callback" transfer-ownership="none">
           <type name="VarargsCallback" c:type="FooVarargsCallback"/>
         </parameter>
-        <parameter name="callback2" transfer-ownership="none" scope="call">
+        <parameter name="callback2" transfer-ownership="none">
           <type name="VarargsCallback" c:type="FooVarargsCallback"/>
         </parameter>
       </parameters>
diff --git a/tests/scanner/GtkFrob-1.0-expected.gir b/tests/scanner/GtkFrob-1.0-expected.gir
index a890e95..c875847 100644
--- a/tests/scanner/GtkFrob-1.0-expected.gir
+++ b/tests/scanner/GtkFrob-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -12,7 +12,8 @@ and/or use gtk-doc annotations.  -->
   <namespace name="GtkFrob"
              version="1.0"
              shared-library="libgtkfrob.so"
-             c:prefix="Gtk">
+             c:identifier-prefixes="Gtk"
+             c:symbol-prefixes="gtk_frob">
     <function name="language_manager_get_default"
               c:identifier="gtk_frob_language_manager_get_default">
       <return-value transfer-ownership="none">
diff --git a/tests/scanner/Makefile.am b/tests/scanner/Makefile.am
index 1b5dc7f..a30336a 100644
--- a/tests/scanner/Makefile.am
+++ b/tests/scanner/Makefile.am
@@ -91,7 +91,7 @@ GtkFrob_1_0_gir_PACKAGES = gobject-2.0
 GtkFrob_1_0_gir_LIBS = libgtkfrob.la
 GtkFrob_1_0_gir_INCLUDES = GObject-2.0
 GtkFrob_1_0_gir_FILES = $(libgtkfrob_la_SOURCES)
-GtkFrob_1_0_gir_SCANNERFLAGS = --strip-prefix=Gtk
+GtkFrob_1_0_gir_SCANNERFLAGS = --identifier-prefix=Gtk --symbol-prefix=gtk_frob
 GIRS += GtkFrob-1.0.gir
 
 noinst_PROGRAMS = barapp
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index e909a2f..d540a95 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -13,11 +13,13 @@ and/or use gtk-doc annotations.  -->
   <namespace name="Regress"
              version="1.0"
              shared-library="libregress.so"
-             c:prefix="Regress">
+             c:identifier-prefixes="Regress"
+             c:symbol-prefixes="regress">
     <record name="TestBoxed"
             c:type="RegressTestBoxed"
             glib:type-name="RegressTestBoxed"
-            glib:get-type="regress_test_boxed_get_type">
+            glib:get-type="regress_test_boxed_get_type"
+            c:symbol-prefix="test_boxed">
       <field name="some_int8" writable="1">
         <type name="gint8" c:type="gint8"/>
       </field>
@@ -63,7 +65,7 @@ and/or use gtk-doc annotations.  -->
           <type name="TestBoxed" c:type="RegressTestBoxed*"/>
         </return-value>
         <parameters>
-          <parameter name="s" transfer-ownership="full">
+          <parameter name="s" transfer-ownership="none">
             <type name="utf8" c:type="char*"/>
           </parameter>
         </parameters>
@@ -84,7 +86,9 @@ and/or use gtk-doc annotations.  -->
         </parameters>
       </method>
     </record>
-    <record name="TestBoxedPrivate" c:type="RegressTestBoxedPrivate">
+    <record name="TestBoxedPrivate"
+            c:type="RegressTestBoxedPrivate"
+            disguised="1">
     </record>
     <callback name="TestCallback" c:type="RegressTestCallback">
       <return-value transfer-ownership="none">
@@ -136,13 +140,14 @@ and/or use gtk-doc annotations.  -->
               glib:nick="flag3"/>
     </bitfield>
     <class name="TestFloating"
+           c:symbol-prefix="test_floating"
            c:type="RegressTestFloating"
            parent="GObject.InitiallyUnowned"
            glib:type-name="RegressTestFloating"
            glib:get-type="regress_test_floating_get_type"
            glib:type-struct="TestFloatingClass">
       <constructor name="new" c:identifier="regress_test_floating_new">
-        <return-value transfer-ownership="full">
+        <return-value transfer-ownership="none">
           <doc xml:whitespace="preserve">A new floating #RegressTestFloating</doc>
           <type name="TestFloating" c:type="RegressTestFloating*"/>
         </return-value>
@@ -160,6 +165,7 @@ and/or use gtk-doc annotations.  -->
       </field>
     </record>
     <class name="TestFundamentalObject"
+           c:symbol-prefix="test_fundamental_object"
            c:type="RegressTestFundamentalObject"
            abstract="1"
            glib:type-name="RegressTestFundamentalObject"
@@ -234,6 +240,7 @@ and/or use gtk-doc annotations.  -->
       </parameters>
     </callback>
     <class name="TestFundamentalSubObject"
+           c:symbol-prefix="test_fundamental_sub_object"
            c:type="RegressTestFundamentalSubObject"
            parent="TestFundamentalObject"
            glib:type-name="RegressTestFundamentalSubObject"
@@ -269,6 +276,7 @@ and/or use gtk-doc annotations.  -->
       </field>
     </record>
     <interface name="TestInterface"
+               c:symbol-prefix="test_interface"
                c:type="RegressTestInterface"
                glib:type-name="RegressTestInterface"
                glib:get-type="regress_test_interface_get_type"
@@ -282,6 +290,7 @@ and/or use gtk-doc annotations.  -->
       </field>
     </record>
     <class name="TestObj"
+           c:symbol-prefix="test_obj"
            c:type="RegressTestObj"
            parent="GObject.Object"
            glib:type-name="RegressTestObj"
@@ -304,7 +313,7 @@ and/or use gtk-doc annotations.  -->
           <parameter name="user_data" transfer-ownership="none">
             <type name="gpointer" c:type="gpointer"/>
           </parameter>
-          <parameter name="notify" transfer-ownership="none" scope="call">
+          <parameter name="notify" transfer-ownership="none" scope="async">
             <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
           </parameter>
         </parameters>
@@ -321,6 +330,21 @@ and/or use gtk-doc annotations.  -->
           </parameter>
         </parameters>
       </constructor>
+      <function name="null_out" c:identifier="regress_test_obj_null_out">
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <parameter name="obj"
+                     direction="out"
+                     caller-allocates="0"
+                     transfer-ownership="full"
+                     allow-none="1">
+            <doc xml:whitespace="preserve">A #RegressTestObj</doc>
+            <type name="TestObj" c:type="RegressTestObj**"/>
+          </parameter>
+        </parameters>
+      </function>
       <function name="static_method"
                 c:identifier="regress_test_obj_static_method">
         <return-value transfer-ownership="none">
@@ -399,11 +423,6 @@ case.</doc>
           <type name="none" c:type="void"/>
         </return-value>
       </method>
-      <method name="null_out" c:identifier="regress_test_obj_null_out">
-        <return-value transfer-ownership="none">
-          <type name="none" c:type="void"/>
-        </return-value>
-      </method>
       <method name="set_bare" c:identifier="regress_test_obj_set_bare">
         <return-value transfer-ownership="none">
           <type name="none" c:type="void"/>
@@ -487,10 +506,10 @@ case.</doc>
         </parameters>
       </method>
       <property name="bare" writable="1" transfer-ownership="none">
-        <type name="GObject.Object" c:type="GObject"/>
+        <type name="GObject.Object"/>
       </property>
       <property name="boxed" writable="1" transfer-ownership="none">
-        <type name="TestBoxed" c:type="RegressTestBoxed"/>
+        <type name="TestBoxed"/>
       </property>
       <property name="double" writable="1" transfer-ownership="none">
         <type name="gdouble"/>
@@ -499,7 +518,7 @@ case.</doc>
         <type name="gfloat"/>
       </property>
       <property name="hash-table" writable="1" transfer-ownership="container">
-        <type name="GLib.HashTable" c:type="GHashTable">
+        <type name="GLib.HashTable">
           <type name="utf8"/>
           <type name="gint8"/>
         </type>
@@ -508,7 +527,7 @@ case.</doc>
         <type name="gint"/>
       </property>
       <property name="list" writable="1" transfer-ownership="none">
-        <type name="GLib.List" c:type="gpointer">
+        <type name="GLib.List">
           <type name="utf8"/>
         </type>
       </property>
@@ -525,7 +544,10 @@ case.</doc>
         <type name="TestBoxed" c:type="RegressTestBoxed*"/>
       </field>
       <field name="hash_table">
-        <type name="GLib.HashTable" c:type="GHashTable*"/>
+        <type name="GLib.HashTable" c:type="GHashTable*">
+          <type name="gpointer" c:type="gpointer"/>
+          <type name="gpointer" c:type="gpointer"/>
+        </type>
       </field>
       <field name="list">
         <type name="GLib.List" c:type="GList*">
@@ -545,17 +567,17 @@ case.</doc>
         <type name="utf8" c:type="char*"/>
       </field>
       <glib:signal name="test">
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
       </glib:signal>
       <glib:signal name="test-with-static-scope-arg">
-        <return-value transfer-ownership="full">
-          <type name="none" c:type="void"/>
+        <return-value transfer-ownership="none">
+          <type name="none"/>
         </return-value>
         <parameters>
           <parameter name="object" transfer-ownership="none">
-            <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA"/>
+            <type name="TestSimpleBoxedA"/>
           </parameter>
         </parameters>
       </glib:signal>
@@ -567,7 +589,7 @@ case.</doc>
         <type name="GObject.ObjectClass" c:type="GObjectClass"/>
       </field>
       <field name="matrix">
-        <callback name="matrix" c:type="matrix">
+        <callback name="matrix">
           <return-value transfer-ownership="none">
             <type name="gint" c:type="int"/>
           </return-value>
@@ -592,7 +614,8 @@ case.</doc>
     <record name="TestSimpleBoxedA"
             c:type="RegressTestSimpleBoxedA"
             glib:type-name="RegressTestSimpleBoxedA"
-            glib:get-type="regress_test_simple_boxed_a_get_type">
+            glib:get-type="regress_test_simple_boxed_a_get_type"
+            c:symbol-prefix="test_simple_boxed_a">
       <field name="some_int" writable="1">
         <type name="gint" c:type="gint"/>
       </field>
@@ -620,11 +643,18 @@ case.</doc>
           </parameter>
         </parameters>
       </method>
+      <function name="const_return"
+                c:identifier="regress_test_simple_boxed_a_const_return">
+        <return-value transfer-ownership="none">
+          <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA*"/>
+        </return-value>
+      </function>
     </record>
     <record name="TestSimpleBoxedB"
             c:type="RegressTestSimpleBoxedB"
             glib:type-name="RegressTestSimpleBoxedB"
-            glib:get-type="regress_test_simple_boxed_b_get_type">
+            glib:get-type="regress_test_simple_boxed_b_get_type"
+            c:symbol-prefix="test_simple_boxed_b">
       <field name="some_int8" writable="1">
         <type name="gint8" c:type="gint8"/>
       </field>
@@ -694,15 +724,8 @@ case.</doc>
         </parameters>
       </method>
     </record>
-    <record name="TestStructC" c:type="_RegressTestStructC">
-      <field name="another_int" writable="1">
-        <type name="gint" c:type="gint"/>
-      </field>
-      <field name="obj" writable="1">
-        <type name="GObject.Object" c:type="GObject*"/>
-      </field>
-    </record>
     <class name="TestSubObj"
+           c:symbol-prefix="test_sub_obj"
            c:type="RegressTestSubObj"
            parent="TestObj"
            glib:type-name="RegressTestSubObj"
@@ -736,6 +759,7 @@ case.</doc>
       </field>
     </record>
     <class name="TestWi8021x"
+           c:symbol-prefix="test_wi_802_1x"
            c:type="RegressTestWi8021x"
            parent="GObject.Object"
            glib:type-name="RegressTestWi8021x"
@@ -746,36 +770,44 @@ case.</doc>
           <type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
         </return-value>
       </constructor>
-      <function name="static_method"
-                c:identifier="regress_test_wi_802_1x_static_method">
+      <function name="get_testbool"
+                c:identifier="regress_test_wi_802_1x_get_testbool">
         <return-value transfer-ownership="none">
-          <type name="gint" c:type="int"/>
+          <type name="gboolean" c:type="gboolean"/>
         </return-value>
         <parameters>
-          <parameter name="x" transfer-ownership="none">
-            <type name="gint" c:type="int"/>
+          <parameter name="obj" transfer-ownership="none">
+            <type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
           </parameter>
         </parameters>
       </function>
-      <method name="get_testbool"
-              c:identifier="regress_test_wi_802_1x_get_testbool">
-        <return-value transfer-ownership="none">
-          <type name="gboolean" c:type="gboolean"/>
-        </return-value>
-      </method>
-      <method name="set_testbool"
-              c:identifier="regress_test_wi_802_1x_set_testbool">
+      <function name="set_testbool"
+                c:identifier="regress_test_wi_802_1x_set_testbool">
         <return-value transfer-ownership="none">
           <type name="none" c:type="void"/>
         </return-value>
         <parameters>
+          <parameter name="obj" transfer-ownership="none">
+            <type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
+          </parameter>
           <parameter name="v" transfer-ownership="none">
             <type name="gboolean" c:type="gboolean"/>
           </parameter>
         </parameters>
-      </method>
+      </function>
+      <function name="static_method"
+                c:identifier="regress_test_wi_802_1x_static_method">
+        <return-value transfer-ownership="none">
+          <type name="gint" c:type="int"/>
+        </return-value>
+        <parameters>
+          <parameter name="x" transfer-ownership="none">
+            <type name="gint" c:type="int"/>
+          </parameter>
+        </parameters>
+      </function>
       <property name="testbool" writable="1" transfer-ownership="none">
-        <type name="gboolean" c:type="gboolean"/>
+        <type name="gboolean"/>
       </property>
       <field name="parent_instance">
         <type name="GObject.Object" c:type="GObject"/>
@@ -791,6 +823,14 @@ case.</doc>
         <type name="GObject.ObjectClass" c:type="GObjectClass"/>
       </field>
     </record>
+    <record name="_TestStructC" c:type="_RegressTestStructC">
+      <field name="another_int" writable="1">
+        <type name="gint" c:type="gint"/>
+      </field>
+      <field name="obj" writable="1">
+        <type name="GObject.Object" c:type="GObject*"/>
+      </field>
+    </record>
     <function name="set_abort_on_error"
               c:identifier="regress_set_abort_on_error">
       <return-value transfer-ownership="none">
@@ -1235,7 +1275,7 @@ is invoked.</doc>
         <parameter name="user_data" transfer-ownership="none">
           <type name="gpointer" c:type="gpointer"/>
         </parameter>
-        <parameter name="notify" transfer-ownership="none" scope="call">
+        <parameter name="notify" transfer-ownership="none" scope="async">
           <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
         </parameter>
       </parameters>
@@ -1408,7 +1448,7 @@ call and can be released on return.</doc>
               c:identifier="regress_test_ghash_nested_everything_return">
       <doc xml:whitespace="preserve">Specify nested parameterized types directly with the (type ) annotation.</doc>
       <return-value transfer-ownership="full">
-        <type name="GLib.HashTable" c:type="GHashTable*">
+        <type name="GLib.HashTable">
           <type name="utf8"/>
           <type name="GLib.HashTable">
             <type name="utf8"/>
@@ -1914,12 +1954,6 @@ call and can be released on return.</doc>
         </parameter>
       </parameters>
     </function>
-    <function name="test_simple_boxed_a_const_return"
-              c:identifier="regress_test_simple_boxed_a_const_return">
-      <return-value transfer-ownership="none">
-        <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA*"/>
-      </return-value>
-    </function>
     <function name="test_simple_callback"
               c:identifier="regress_test_simple_callback">
       <return-value transfer-ownership="none">
@@ -2124,7 +2158,7 @@ call and can be released on return.</doc>
         <parameter name="user_data" transfer-ownership="none">
           <type name="gpointer" c:type="gpointer"/>
         </parameter>
-        <parameter name="notify" transfer-ownership="none" scope="call">
+        <parameter name="notify" transfer-ownership="none" scope="async">
           <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
         </parameter>
         <parameter name="y"
@@ -2279,7 +2313,7 @@ call and can be released on return.</doc>
         <type name="none" c:type="void"/>
       </return-value>
       <parameters>
-        <parameter name="in" transfer-ownership="full" allow-none="1">
+        <parameter name="in" transfer-ownership="none" allow-none="1">
           <type name="utf8" c:type="char*"/>
         </parameter>
       </parameters>
@@ -2353,7 +2387,7 @@ call and can be released on return.</doc>
     <function name="test_value_get_fundamental_object"
               c:identifier="regress_test_value_get_fundamental_object"
               introspectable="0">
-      <return-value transfer-ownership="full">
+      <return-value>
         <type name="TestFundamentalObject"
               c:type="RegressTestFundamentalObject*"/>
       </return-value>
diff --git a/tests/scanner/TestInherit-1.0-expected.gir b/tests/scanner/TestInherit-1.0-expected.gir
index 4ed155e..8465454 100644
--- a/tests/scanner/TestInherit-1.0-expected.gir
+++ b/tests/scanner/TestInherit-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -13,8 +13,10 @@ and/or use gtk-doc annotations.  -->
   <namespace name="TestInherit"
              version="1.0"
              shared-library="libtestinherit.so"
-             c:prefix="TestInherit">
+             c:identifier-prefixes="TestInherit"
+             c:symbol-prefixes="test_inherit">
     <class name="Drawable"
+           c:symbol-prefix="drawable"
            c:type="TestInheritDrawable"
            parent="GObject.Object"
            abstract="1"
diff --git a/tests/scanner/Utility-1.0-expected.gir b/tests/scanner/Utility-1.0-expected.gir
index f4c6909..c09c056 100644
--- a/tests/scanner/Utility-1.0-expected.gir
+++ b/tests/scanner/Utility-1.0-expected.gir
@@ -2,7 +2,7 @@
 <!-- This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations.  -->
-<repository version="1.1"
+<repository version="1.2"
             xmlns="http://www.gtk.org/introspection/core/1.0";
             xmlns:c="http://www.gtk.org/introspection/c/1.0";
             xmlns:glib="http://www.gtk.org/introspection/glib/1.0";>
@@ -12,13 +12,14 @@ and/or use gtk-doc annotations.  -->
   <namespace name="Utility"
              version="1.0"
              shared-library="libutility.so"
-             c:prefix="Utility">
+             c:identifier-prefixes="Utility"
+             c:symbol-prefixes="utility">
     <alias name="Glyph" c:type="UtilityGlyph">
-      <type name="guint32"/>
+      <type name="guint32" c:type="guint32"/>
     </alias>
     <record name="Buffer" c:type="UtilityBuffer">
       <field name="data" writable="1">
-        <type name="gpointer" c:type="char*"/>
+        <type name="gpointer"/>
       </field>
       <field name="length" writable="1">
         <type name="gulong" c:type="gsize"/>
@@ -61,6 +62,7 @@ and/or use gtk-doc annotations.  -->
       <member name="c" value="4" c:identifier="UTILITY_FLAG_C"/>
     </bitfield>
     <class name="Object"
+           c:symbol-prefix="object"
            c:type="UtilityObject"
            parent="GObject.Object"
            glib:type-name="UtilityObject"
@@ -77,14 +79,14 @@ and/or use gtk-doc annotations.  -->
           <parameter name="func"
                      transfer-ownership="none"
                      scope="notified"
-                     closure="3"
-                     destroy="4">
+                     closure="2"
+                     destroy="3">
             <type name="FileFunc" c:type="UtilityFileFunc"/>
           </parameter>
           <parameter name="user_data" transfer-ownership="none">
             <type name="gpointer" c:type="gpointer"/>
           </parameter>
-          <parameter name="destroy" transfer-ownership="none" scope="call">
+          <parameter name="destroy" transfer-ownership="none" scope="async">
             <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
           </parameter>
         </parameters>
@@ -112,7 +114,7 @@ and/or use gtk-doc annotations.  -->
       </field>
       <field name="data" writable="1">
         <array zero-terminated="0" c:type="guint8" fixed-size="16">
-          <type name="guint8"/>
+          <type name="guint8" c:type="guint8"/>
         </array>
       </field>
     </record>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]