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



commit 92ed0c322c8c836e9885919f9d3950b9253d13c5
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.

 docs/g-ir-scanner.1                                |    3 -
 gir/Makefile.am                                    |    9 +-
 gir/gio-2.0.c                                      |    2 +-
 gir/glib-2.0.c                                     |    2 +-
 girepository/girparser.c                           |  107 ++-
 giscanner/Makefile.am                              |    4 +
 giscanner/annotationparser.py                      |  717 +--------------
 giscanner/ast.py                                   |  671 +++++++++-----
 giscanner/codegen.py                               |  137 +++
 giscanner/finaltransformer.py                      |  153 +++
 giscanner/girparser.py                             |  349 +++++---
 giscanner/girwriter.py                             |  182 ++--
 giscanner/glibast.py                               |   17 +-
 giscanner/glibtransformer.py                       | 1013 +++-----------------
 giscanner/primarytransformer.py                    |  852 ++++++++++++++++
 giscanner/scannermain.py                           |  142 ++--
 giscanner/sourcescanner.py                         |   10 +-
 giscanner/testcodegen.py                           |  124 +++
 giscanner/transformer.py                           |  534 +++++------
 giscanner/utils.py                                 |   13 +
 tests/Makefile.am                                  |   29 +
 ....0-expected.gir => Annotation-1.0-expected.gir} |    0
 tests/scanner/annotation.c                         |   15 +-
 23 files changed, 2605 insertions(+), 2480 deletions(-)
---
diff --git a/docs/g-ir-scanner.1 b/docs/g-ir-scanner.1
index b43d3f1..750660a 100644
--- a/docs/g-ir-scanner.1
+++ b/docs/g-ir-scanner.1
@@ -106,9 +106,6 @@ several pkg-config packages.
 .B \--verbose
 Be verbose, include some debugging information.
 .TP
-.B \--noclosure
-Do not delete unknown types from the resulting format.
-.TP
 .B \--typelib-xml
 Convert the resulting xml to only output the types relevant
 to the typelib compiler. This is mainly useful for verifying the
diff --git a/gir/Makefile.am b/gir/Makefile.am
index f183463..c3d97da 100644
--- a/gir/Makefile.am
+++ b/gir/Makefile.am
@@ -31,7 +31,7 @@ GLIB_LIBRARY=glib-2.0
 endif
 
 GLib_2_0_gir_LIBS = $(GLIB_LIBRARY)
-GLib_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib.h"
+GLib_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="glib.h"
 GLib_2_0_gir_PACKAGES = glib-2.0
 GLib_2_0_gir_CFLAGS = \
             -I$(GLIB_INCLUDEDIR) \
@@ -61,7 +61,7 @@ endif
 GObject-2.0.gir: GLib-2.0.gir
 
 GObject_2_0_gir_LIBS = $(GOBJECT_LIBRARY)
-GObject_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib-object.h" --add-include-path=.
+GObject_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-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 = \
@@ -87,7 +87,7 @@ endif
 GModule-2.0.gir: GLib-2.0.gir
 
 GModule_2_0_gir_LIBS = $(GMODULE_LIBRARY)
-GModule_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="gmodule.h" --add-include-path=.
+GModule_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-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 = \
@@ -118,7 +118,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 = --warn-all --reparse-validate --strip-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 = \
@@ -137,7 +137,6 @@ 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 = \
-        --noclosure \
         --strip-prefix=g \
         --c-include="girepository.h" \
         --pkg-export gobject-introspection-1.0 \
diff --git a/gir/gio-2.0.c b/gir/gio-2.0.c
index 2ec02a3..71df3e8 100644
--- a/gir/gio-2.0.c
+++ b/gir/gio-2.0.c
@@ -83,7 +83,7 @@
 /**
  * g_file_enumerator_next_files_finish:
  *
- * Return value: (transfer full) (element-type FileInfo):
+ * Return value: (transfer full) (element-type GFileInfo):
  */
 
 /**
diff --git a/gir/glib-2.0.c b/gir/glib-2.0.c
index 6203121..6591c64 100644
--- a/gir/glib-2.0.c
+++ b/gir/glib-2.0.c
@@ -6,7 +6,7 @@
 
 /**
  * g_file_set_contents:
- * @contents: (array length=length) (element-type uint8):
+ * @contents: (array length=length) (element-type guint8):
  */
 
 /**
diff --git a/girepository/girparser.c b/girepository/girparser.c
index 570e648..28ca5d5 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -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,
@@ -787,15 +787,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 +808,12 @@ start_function (GMarkupParseContext *context,
   if (!found)
     return FALSE;
 
-  if (ctx->state == STATE_STRUCT_FIELD)
-    ctx->in_embedded_type = TRUE;
-
   if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
     return TRUE;
 
+  if (ctx->state == STATE_STRUCT_FIELD)
+    ctx->in_embedded_type = TRUE;
+
   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 +963,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 +991,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 +1082,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 +1147,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 +1174,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 +1195,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 +1224,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 +1232,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 +1240,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 +1248,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 +1269,6 @@ start_field (GMarkupParseContext *context,
 
 	    union_->discriminators = g_list_append (union_->discriminators, constant);
 	  }
-	state_switch (ctx, STATE_UNION_FIELD);
       }
       break;
     default:
@@ -2169,7 +2195,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 +3006,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 +3054,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 +3407,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 +3515,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/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..bbbba78 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -36,7 +36,8 @@ from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
                   PARAM_TRANSFER_NONE,
                   PARAM_TRANSFER_CONTAINER,
                   PARAM_TRANSFER_FULL,
-                  TYPE_ANY, TYPE_NONE)
+                  TYPE_ANY, TYPE_NONE, TYPE_STRING)
+from .transformer import TypeResolutionException
 from .odict import odict
 from .glibast import GLibBoxed
 
@@ -85,10 +86,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 +150,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:
@@ -311,701 +308,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
old mode 100644
new mode 100755
index 4008b66..6f273f9
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,63 +19,149 @@
 # 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 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
+
+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*')
 
 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_ULONG, TYPE_FLOAT, TYPE_DOUBLE, 
+                   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])
 
 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
@@ -143,11 +229,10 @@ type_names['id'] = TYPE_ANY
 
 # These types, when seen by reference, are converted into an Array()
 # by default
+# If you add/change these, be sure to update glibast.py too
 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
@@ -170,24 +255,167 @@ 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, c_prefix=None):
+        self.name = name
+        self.version = version
+        self.c_prefix = c_prefix or name
+        self._lower_c_prefix = self.c_prefix.lower()
+        self.uscore_prefix = to_underscores(self.c_prefix).lower()
+        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
+
+    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.c_prefix:
+            return False
+        return (ident.startswith(self.c_prefix)
+                or ident.startswith(self._lower_c_prefix))
+
+    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
+        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
+        elif 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 hasattr(node, 'symbol'):
+            del self._ctypes[node.symbol]
+
+    def float(self, node):
+        """Like remove(), but doesn't unset the node's namespace
+back-reference."""
+        self.remove(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 walk(self, callback):
+        for node in self.itervalues():
+            node.walk(callback, [])
+
+    def iter_recursive(self):
+        """Recursively iterate over all Node instances."""
+        def doyield(node, chain):
+            yield node
+        self.walk(test)
+
+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)
+
+    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):
@@ -206,49 +434,15 @@ 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 recursive_pred(node):
-            node.remove_matching_children(pred)
-            return pred(node)
+    def walk(self, callback, chain):
+        if not callback(self, chain):
+            return False
+        chain.append(self)
+        self._walk(callback, chain)
+        chain.pop()
 
-        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):
 
@@ -257,29 +451,33 @@ 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)
+    def get_parameter_index(self, name):
+        for i, parameter in enumerate(self.parameters):
+            if parameter.argname == name:
+                return i
+
+    def get_parameter(self, name):
+        for parameter in self.parameters:
+            if parameter.argname == name:
+                return parameter
+
 
 class Function(Callable):
 
-    def __init__(self, name, retval, parameters, symbol, throws=None):
+    def __init__(self, name, retval, parameters, throws, symbol):
         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:
+            if parameter.argname == name:
                 return i + int(self.is_method)
 
     def get_parameter(self, name):
         for parameter in self.parameters:
-            if parameter.name == name:
+            if parameter.argname == name:
                 return parameter
 
 
@@ -296,59 +494,75 @@ 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_index = self.length_param_index
+        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_index = self.length_param_index
+        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):
 
@@ -357,42 +571,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.caller_allocates = caller_allocates
         self.closure_index = -1
         self.destroy_index = -1
-        self.doc = 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):
@@ -401,10 +616,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):
@@ -413,22 +624,16 @@ 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)
-
 
 class Record(Node):
 
@@ -438,45 +643,37 @@ 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:
+                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)
+        self.name = name
         self.type = typenode
-        self.symbol = symbol
         self.readable = readable
         self.writable = writable
         self.bits = bits
-
-    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, )
-
+        self.anonymous_node = anonymous_node
 
 class Class(Node):
 
@@ -484,6 +681,10 @@ class Class(Node):
         Node.__init__(self, name)
         self.ctype = name
         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 = []
@@ -493,7 +694,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)
@@ -501,87 +701,70 @@ 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.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
+        self.transfer = PARAM_TRANSFER_NONE
 
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.type)
-
-
-# FIXME: Inherit from Function
 
+class Callback(Callable):
 
-class Callback(Node):
-
-    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):
@@ -591,8 +774,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..34d50eb
--- /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.
+#
+
+import os, sys
+from contextlib import contextmanager
+from .ast import *
+from .glibast import *
+
+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.uscore_prefix, name)
+
+    def _typecontainer_to_ctype(self, param):
+        if (isinstance(param, Parameter) and
+            param.direction in (PARAM_DIRECTION_OUT,
+                                PARAM_DIRECTION_INOUT)):
+            suffix = '*'
+        else:
+            suffix = ''
+        if (param.type.is_equiv((TYPE_STRING, TYPE_FILENAME)) and
+            param.transfer == 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 (PARAM_DIRECTION_OUT, 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, 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, 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..0962851
--- /dev/null
+++ b/giscanner/finaltransformer.py
@@ -0,0 +1,153 @@
+# -*- 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 os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import * 
+from .glibast import *
+from .utils import to_underscores, to_underscores_noprefix
+
+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, 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, 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, Varargs):
+            parent.introspectable = False
+        elif not isinstance(node.type, 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
+
+    def _type_is_introspectable(self, typeval, warn=False):
+        if not typeval.resolved:
+            return False
+        if isinstance(typeval, (Array, List)):
+            return self._type_is_introspectable(typeval.element_type)
+        elif isinstance(typeval, 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:
+            # Mark UCHAR as not introspectable temporarily until
+            # we're ready to land the typelib changes
+            if typeval.is_equiv(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((TYPE_LONG_LONG, TYPE_LONG_ULONG, 
+                                 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, Callable):
+            for param in obj.parameters:
+                self._introspectable_param_analysis(obj, param)
+            self._introspectable_param_analysis(obj, obj.retval)
+        if isinstance(obj, (Class, Interface, Record, 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, 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, (Class, Interface, Record, 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, (Class, 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..a1f4761 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 .ast import *
+from .glibast import *
 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,14 @@ 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'])
+                                    ns.attrib['version'],
+                                    ns.attrib.get(_cns('prefix')))
         if 'shared-library' in ns.attrib:
             self._shared_libraries.extend(
                 ns.attrib['shared-library'].split(','))
@@ -155,20 +167,42 @@ class GIRParser(object):
         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'],
                       typeval,
                       node.attrib.get(_cns('type')))
-        self._add_node(alias)
+        self._namespace.append(alias)
+
+    def _parse_generic_attribs(self, node, obj):
+        assert isinstance(obj, 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 = Type(target_giname=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')]]
         if node.tag == _corens('interface'):
@@ -182,38 +216,46 @@ 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 = Type(target_giname=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')):
+        for iface in self._find_children(node, _corens('implements')):
+            obj.interfaces.append(Type(target_giname=iface.attrib['name']))
+        for iface in self._find_children(node, _corens('prerequisite')):
+            obj.prerequisites.append(Type(target_giname=iface.attrib['name']))
+        for func_node in self._find_children(node, _corens('function')):
+            func = self._parse_function_common(func_node, Function)
+            obj.static_methods.append(func)
+        for method in self._find_children(node, _corens('method')):
             func = self._parse_function_common(method, Function)
             func.is_method = True
             obj.methods.append(func)
-        for ctor in node.findall(_corens('constructor')):
+        for method in self._find_children(node, _corens('virtual-method')):
+            func = self._parse_function_common(method, 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')):
             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')):
+        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')):
+        for signal in self._find_children(node, _glibns('signal')):
             obj.signals.append(self._parse_function_common(signal, Function))
 
     def _parse_callback(self, node):
         callback = self._parse_function_common(node, Callback)
-        self._add_node(callback)
+        self._namespace.append(callback)
 
     def _parse_function(self, node):
         function = self._parse_function_common(node, Function)
-        self._add_node(function)
+        self._namespace.append(function)
 
     def _parse_function_common(self, node, klass):
         name = node.attrib['name']
@@ -222,59 +264,89 @@ class GIRParser(object):
             raise ValueError('node %r has no return-value' % (name, ))
         transfer = returnnode.attrib.get('transfer-ownership')
         retval = Return(self._parse_type(returnnode), transfer)
+        self._parse_generic_attribs(returnnode, retval)
         parameters = []
 
+        throws = (node.attrib.get('throws') == '1')
+
         if klass is Callback:
-            func = klass(name, retval, parameters,
+            func = klass(name, retval, parameters, throws,
                          node.attrib.get(_cns('type')))
-        else:
+        elif klass is 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 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')):
+            for paramnode in self._find_children(parameters_node, _corens('parameter')):
                 param = Parameter(paramnode.attrib.get('name'),
                                   self._parse_type(paramnode),
-                                  paramnode.attrib.get('direction'),
+                                  paramnode.attrib.get('direction') or 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')
+                closure = paramnode.attrib.get('closure')
+                if closure:
+                    param.closure_index = int(closure)
+                destroy = paramnode.attrib.get('destroy')
+                if destroy:
+                    param.destroy_index = int(destroy)
+                self._parse_generic_attribs(paramnode, param)
                 parameters.append(param)
 
+        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'],
                                      node.attrib[_glibns('type-name')],
                                      node.attrib[_glibns('get-type')],
                                      node.attrib.get(_cns('type')))
+        elif _glibns('is-gtype-struct-for') in node.attrib:
+            struct = 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 = Type(target_giname=is_gtype_struct_for)
         else:
-            disguised = node.attrib.get('disguised') == '1'
-            struct = Struct(node.attrib['name'],
+            struct = Record(node.attrib['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(
+                            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, Function))
-        for ctor in node.findall(_corens('constructor')):
+        for func in self._find_children(node, _corens('function')):
+            struct.static_methods.append(
+                self._parse_function_common(func, Function))
+        for ctor in self._find_children(node, _corens('constructor')):
             struct.constructors.append(
                 self._parse_function_common(ctor, 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'],
                                     node.attrib[_glibns('type-name')],
@@ -283,50 +355,72 @@ class GIRParser(object):
         else:
             union = Union(node.attrib['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.extend(self._parse_fields(node))
+        for method in self._find_children(node, _corens('method')):
             union.fields.append(
                 self._parse_function_common(method, Function))
-        for ctor in node.findall(_corens('constructor')):
+        for ctor in self._find_children(node, _corens('constructor')):
             union.constructors.append(
                 self._parse_function_common(ctor, Function))
+        return union
 
     def _parse_type(self, node):
-        typenode = node.find(_corens('type'))
+        # Fields can contain inline callbacks
+        typenode = node.find(_corens('callback'))
         if typenode is not None:
-            return Type(typenode.attrib['name'],
-                        typenode.attrib.get(_cns('type')))
+            return Type(target_giname=typenode.attrib['name'],
+                        ctype=typenode.attrib.get(_cns('type')))
 
+        # Arrays have their own toplevel XML
         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)
+            array_type = typenode.attrib.get('name')
+            element_type = self._parse_type(typenode)
+            array_ctype = typenode.attrib.get(_cns('type'))
+            ret = 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
 
+        # Dispsense with varargs
         typenode = node.find(_corens('varargs'))
         if typenode is not None:
             return Varargs()
 
+        # Okay now...could be a list, let's see.
+        typenode = self._find_first_child(node, _corens('type'))
+        if typenode is not None:
+            name = typenode.attrib.get('name')
+            ctype = typenode.attrib.get(_cns('type'))
+            if name is None:
+                if ctype is None:
+                    return TypeUnknown()
+                return Type(ctype=ctype)
+            if 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 = TYPE_ANY
+                return List(name, element_type, ctype=ctype)
+            else:
+                return self._namespace.type_from_name(name, ctype)
+
         raise ValueError("Couldn't parse type of node %r; children=%r",
                          node, list(node))
 
@@ -334,74 +428,99 @@ class GIRParser(object):
         obj = 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')):
+        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, 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')):
+        for callback in self._find_children(node, _corens('callback')):
             obj.fields.append(
                 self._parse_function_common(callback, 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, ('callback', 'record', 'union')):
+            anonymous_elt = node 
+        else:
+            anonymous_elt = None
+        if anonymous_elt:
+            if anonymous_elt.tag == _corens('callback'):
+                anonymous_node = self._parse_function_common(anonymous_elt, 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')
+            type_node = self._parse_type(node)
+        field = Field(node.attrib['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 = 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 = 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,
+                            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
             else:
                 klass = GLibEnum
         else:
-            klass = Enum
+            if node.tag == _corens('bitfield'):
+                klass = Bitfield
+            else:
+                klass = Enum
             type_name = ctype
         members = []
-        if klass is Enum:
+        if klass in (Enum, 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 fd0b99f..84c2ba1 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)
@@ -35,17 +35,17 @@ COMPATIBLE_GIR_VERSION = '1.1'
 
 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,11 @@ 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:prefix', namespace.c_prefix)]
         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 +100,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 +114,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)
@@ -171,7 +173,9 @@ and/or use gtk-doc annotations. ''')
             self._write_parameters(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))
         self._write_callable(func, tag_name, attrs)
 
     def _write_method(self, method):
@@ -187,10 +191,9 @@ and/or use gtk-doc annotations. ''')
         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)
@@ -203,17 +206,16 @@ and/or use gtk-doc annotations. ''')
                 self._write_parameter(parameter)
 
     def _write_parameter(self, parameter):
-        assert parameter.transfer is not None, 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:
@@ -226,62 +228,56 @@ and/or use gtk-doc annotations. ''')
             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 _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):
-        if isinstance(ntype, basestring):
-            typename = ntype
-            type_cname = None
-        else:
-            typename = ntype.name
-            type_cname = ntype.ctype
+        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'))
+                attrs.insert(0, ('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))
             if ntype.size is not None:
-                attrs.append(('fixed-size', ntype.size))
+                attrs.append(('fixed-size', '%d' % (ntype.size, )))
 
             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):
+            if ntype.name:
+                attrs.insert(0, ('name', name))
             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,10 +321,9 @@ 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),
@@ -338,7 +333,8 @@ and/or use gtk-doc annotations. ''')
         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 +344,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 +361,12 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(node)
             if isinstance(node, GLibObject):
                 for iface in 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 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 node.constructors:
                     self._write_constructor(method)
@@ -394,6 +393,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in boxed.methods:
                 self._write_method(method)
+            for method in boxed.static_methods:
+                self._write_static_method(method)
 
     def _write_property(self, prop):
         attrs = [('name', prop.name)]
@@ -408,7 +409,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,11 +418,13 @@ 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):
@@ -442,7 +446,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 +460,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in record.methods:
                 self._write_method(method)
+            for method in record.static_methods:
+                self._write_static_method(method)
 
     def _write_union(self, union):
         attrs = []
@@ -476,27 +482,23 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in union.methods:
                 self._write_method(method)
+            for method in 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):
+                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:
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index ad87926..69f1c0b 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -19,7 +19,7 @@
 #
 
 from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
-                  Property, Union, Record)
+                  Property, Union, Record, Annotated)
 
 class GLibRecord(Record):
     def __init__(self, *args, **kwargs):
@@ -118,9 +118,18 @@ class GLibBoxedOther(Node, GLibBoxed):
         GLibBoxed.__init__(self, type_name, get_type)
         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):
 
@@ -137,10 +146,10 @@ class GLibProperty(Property):
     pass
 
 
-class GLibSignal(Node):
+class GLibSignal(Annotated):
 
     def __init__(self, name, retval):
-        Node.__init__(self, name)
+        Annotated.__init__(self)
+        self.name = name
         self.retval = retval
         self.parameters = []
-        self.doc = None
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index a8fdcc3..42fc6ef 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,11 +28,9 @@ 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,
+                  Class, Field, VFunction, default_array_types,
+                  TYPE_ANY, TYPE_GTYPE, TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
                   Map, Varargs, type_names)
-from .transformer import Names
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
                       GLibBoxedUnion, GLibBoxedOther, GLibRecord)
@@ -50,30 +48,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 +71,15 @@ 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._namespace = transformer.namespace
         self._uscore_type_names = {}
         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 +90,21 @@ 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)
+        for node in self._namespace.itervalues():
+            if isinstance(node, Function):
+                self._initparse_function(node)
+        if self._namespace.name == 'GObject':
+            for node in self._namespace.itervalues():
+                if isinstance(node, Record):
+                    self._initparse_gobject_record(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']
+        if self._namespace.name == 'GObject':
+            del self._namespace.aliases['Type']
 
     def get_get_type_functions(self):
         return self._get_type_functions
@@ -144,148 +116,28 @@ 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():
-            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):
+        # Pair up boxed structures and class structures
+        for node in self._namespace.itervalues():
+            if isinstance(node, GLibBoxed):
+                self._pair_boxed_type(node)
+            elif 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""")
 
     # 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 +153,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,8 +168,7 @@ 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 = Type(target_giname='GLib.Object')
             symbol = 'g_initially_unowned_get_type'
         else:
             assert False
@@ -340,345 +183,50 @@ 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(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'):
@@ -709,8 +257,7 @@ class GLibTransformer(object):
         type_name = node.attrib['name']
         enum_name = self._transformer.remove_prefix(type_name)
         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 _introspect_object(self, xmlnode):
         type_name = xmlnode.attrib['name']
@@ -719,58 +266,47 @@ 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,
-            type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+        node = GLibObject(self._transformer.remove_prefix(type_name),
+                          None,
+                          type_name,
+                          xmlnode.attrib['get-type'], 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),
-            None,
-            type_name, xmlnode.attrib['get-type'])
+        node = GLibInterface(self._transformer.remove_prefix(type_name),
+                             None,
+                             type_name, xmlnode.attrib['get-type'])
         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(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'])
-        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(interface.attrib['name'])
             gt_interfaces.append(gitype)
         node.interfaces = sorted(gt_interfaces)
 
@@ -784,7 +320,7 @@ class GLibTransformer(object):
             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
             node.properties.append(Property(
                 pspec.attrib['name'],
-                type_name_from_ctype(ctype),
+                self._transformer.create_type(ctype),
                 readable, writable, construct, construct_only,
                 ctype,
                 ))
@@ -793,23 +329,30 @@ 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
+            rtype = self._transformer.create_type(rctype)
+            return_ = Return(rtype, PARAM_TRANSFER_FULL)
             signal = GLibSignal(signal_info.attrib['name'], return_)
             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)
+                ptype = self._transformer.create_type(pctype)
+                param = Parameter(argname, ptype)
                 param.transfer = 'none'
                 signal.parameters.append(param)
             node.signals.append(signal)
         node.signals = sorted(node.signals)
 
+    def _parse_parents(self, xmlnode, node):
+        if 'parents' in xmlnode.attrib:
+            parent_types = map(lambda s: self._transformer.create_type(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,30 +362,21 @@ 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,
-            type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+        node = GLibObject(self._transformer.remove_prefix(type_name),
+                          None,
+                          type_name,
+                          xmlnode.attrib['get-type'], 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)
+        record = self._namespace.get(node.name)
         if record is None:
             return
         node.fields = record.fields
@@ -854,7 +388,7 @@ class GLibTransformer(object):
 
     def _pair_boxed_type(self, boxed):
         name = self._transformer.remove_prefix(boxed.type_name)
-        pair_node = self._get_attribute(name)
+        pair_node = self._namespace.get(name)
         if not pair_node:
             boxed_item = GLibBoxedOther(name, boxed.type_name,
                                         boxed.get_type)
@@ -870,336 +404,31 @@ class GLibTransformer(object):
             boxed_item.fields = pair_node.fields
         else:
             return False
-        self._add_attribute(boxed_item, replace=True)
+        self._namespace.append(boxed_item, replace=True)
 
-    # Node walking
+    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 _walk(self, node, callback, chain):
-        if not isinstance(node, Node):
-            return
-        if not callback(node, chain):
+    def _pair_class_record(self, maybe_class):
+        name = self._strip_class_suffix(maybe_class.name)
+        if name == maybe_class.name:
             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)
-
-        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 == []:
+        class_struct = maybe_class
+        pair_class = self._namespace.get(name)
+        if not pair_class:
+            return
+        if not isinstance(pair_class, (GLibObject, GLibInterface)):
             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
-        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)
-
-    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)
-        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
-
-    # 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 = GLibRecord.from_record(class_struct)
+        self._namespace.append(gclass_struct, replace=True)
+        pair_class.glib_type_struct = gclass_struct.create_type()
+        pair_class.inherit_file_positions(class_struct)
+        gclass_struct.is_gtype_struct_for = pair_class.create_type()
diff --git a/giscanner/primarytransformer.py b/giscanner/primarytransformer.py
new file mode 100644
index 0000000..12a03ab
--- /dev/null
+++ b/giscanner/primarytransformer.py
@@ -0,0 +1,852 @@
+# -*- 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 os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import *
+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_TRANSFER,
+                               OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
+                               OPT_FOREIGN, OPT_VAL_BITFIELD, OPT_ARRAY_FIXED_SIZE,
+                               OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
+                               OPT_SCOPE_ASYNC, OPT_SCOPE_CALL, OPT_SCOPE_NOTIFIED)
+from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
+                      GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
+                      GLibBoxedUnion, GLibBoxedOther, GLibRecord)
+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):
+        # Do a type resolution pass before doing resolution
+        # so that annotation processing can figure out transfer
+        # defaults better.
+        self._namespace.walk(self._pass_type_resolution)
+
+        self._namespace.walk(self._pass_read_annotations)
+        
+        # We have a rough tree which should have all
+        # 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)
+
+        # Generate a reverse mapping "bar_baz" -> BarBaz
+        for node in self._namespace.itervalues():
+            uscored = to_underscores_noprefix(node.name).lower()
+            if isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+                self._uscore_type_names[uscored] = node
+
+        for node in list(self._namespace.itervalues()):
+            if isinstance(node, Function):
+                # Discover which toplevel functions are actually methods
+                self._pair_function(node)
+            if isinstance(node, (Class, Interface)):
+                self._pair_class_virtuals(node)
+
+        # 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 _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 _get_parameter_index(self, parent, param_name, origin):
+        index = parent.get_parameter_index(param_name)
+        if index is None:
+            if isinstance(origin, 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 index
+
+    def _pass_read_annotations(self, node, chain):
+        if not node.namespace:
+            return False
+        if isinstance(node, Function):
+            block = self._blocks.get(node.symbol)
+            self._apply_annotations_callable(node, chain, block)
+        if isinstance(node, Callback):
+            block = self._blocks.get(node.c_name)
+            self._apply_annotations_callable(node, chain, block)
+        if isinstance(node, (Class, Interface, Record, Union, Enum, Bitfield,
+                             Callback)):
+            block = self._blocks.get(node.c_name)
+            self._apply_annotations_annotated(node, block)    
+        if isinstance(node, (Class, Interface, Record, 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, (Class, Interface)):
+            for prop in node.properties:
+                self._apply_annotations_property(node, prop)
+            for sig in node.signals:
+                self._apply_annotations_signal(node, sig)
+        return True
+
+    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 _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 _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.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:
+                raise InvalidAnnotationError(
+                    "out allocation 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 _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.create_and_resolve_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:
+                if base.target_fundamental and (not isinstance(base, Array)):
+                    orig_node.target_fundamental = base.target_fundamental
+                elif base.target_giname:
+                    orig_node.target_giname = base.target_giname
+                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 _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())
+        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, Array):
+            array_type = node.type.array_type
+        else:
+            array_type = None
+        container_type = 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:
+            param_index = self._get_parameter_index(parent, length, node)
+            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.is_equiv(TYPE_STRING) and
+                node.direction == PARAM_DIRECTION_IN and
+                element_type is None):
+                # FIXME: unsigned char/guchar should be uint8
+                container_type.element_type = TYPE_UINT8
+        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, List):
+            assert len(element_type) == 1
+            node.type.element_type = self._resolve(element_type[0])
+        elif isinstance(node.type, 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, Array):
+            node.type.element_type = self._resolve(element_type[0]) 
+        else:
+            self._transformer.log_node_warning(parent, "Unknown container for element-type annotation")
+
+    def _get_transfer_default(self, parent, node, options):
+        if node.transfer is not None:
+            return node.transfer
+
+        if node.type.is_equiv(TYPE_NONE):
+            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
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node, Return):
+            if (node.type.is_equiv(BASIC_GIR_TYPES) or
+                (node.type.is_equiv((TYPE_STRING, TYPE_FILENAME, TYPE_ANY))
+                 and node.type.is_const)):
+                return PARAM_TRANSFER_NONE
+            elif node.type.target_fundamental:
+                # This looks like just GType right now
+                return None
+            if node.type.target_giname:
+                target = self._transformer.lookup_typenode(node.type)
+                if isinstance(target, GLibBoxed):
+                    return PARAM_TRANSFER_FULL
+                if isinstance(target, (Enum, Bitfield)):
+                    return PARAM_TRANSFER_NONE
+                if isinstance(target, (Class, Record, Union)):
+                    # Explicitly no default for these
+                    return None
+                else:
+                    return None
+        elif isinstance(node, Field):
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node, Property):
+            return PARAM_TRANSFER_NONE
+        else:
+            raise AssertionError(node)
+
+    def _apply_annotations_param_ret_common(self, parent, node, tag):
+        options = getattr(tag, 'options', {})
+        (node.direction, node.caller_allocates) = \
+            self._extract_direction(node, options)
+        transfer_tag = options.get(TAG_TRANSFER)
+        if transfer_tag:
+            node.transfer = transfer_tag.one()
+        else:
+            node.transfer = self._get_transfer_default(parent, node, options)
+            
+        self._adjust_container_type(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.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 None:
+            return
+        node.version = since_tag.value
+
+        deprecated_tag = block.get(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
+
+        annos_tag = block.get(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()))
+
+        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, Function):
+            scope = options.get(OPT_SCOPE)
+            if scope:
+                scope = scope.one()
+                if scope not in [PARAM_SCOPE_CALL,
+                                 PARAM_SCOPE_ASYNC,
+                                 PARAM_SCOPE_NOTIFIED]:
+                    self._transformer.log_warning(parent, "Invalid scope %r for parameter %r" % (scope, param.name))
+                else:
+                    param.scope = scope
+                    param.transfer = PARAM_TRANSFER_NONE
+
+            destroy = options.get(OPT_DESTROY)
+            if destroy:
+                param.destroy_index = self._get_parameter_index(parent,
+                                                                destroy.one(),
+                                                                param)
+                self._fixup_param_destroy(parent, param)
+            closure = options.get(OPT_CLOSURE)
+            if closure:
+                param.closure_index = self._get_parameter_index(parent,
+                                                                closure.one(),
+                                                                param)
+                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)
+                self._fixup_param_closure(parent, param)
+
+        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 _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_params(self, parent, params, block):
+        self._check_arg_annotations(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_and_resolve_type(t.one())
+
+    def _apply_annotations_property(self, parent, prop):
+        block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
+        if not block:
+            return
+        self._apply_annotations_annotated(prop, block)
+        transfer_tag = block.get(TAG_TRANSFER)
+        if transfer_tag is not None:
+            options = {OPT_TRANSFER: Option(transfer_tag.value)}
+        else:
+            options = {}
+        prop.transfer = self._get_transfer_default(parent, prop, options)
+
+        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_type_resolution(self, node, chain):
+        if isinstance(node, Alias):
+            self._transformer.resolve_type(node.target)
+        if isinstance(node, Callable):
+            for parameter in node.parameters:
+                self._transformer.resolve_type(parameter.type)
+            self._transformer.resolve_type(node.retval.type)
+        if isinstance(node, Constant):
+            self._transformer.resolve_type(node.value_type)
+        if isinstance(node, (Class, Interface, Record, Union)):
+            for field in node.fields:
+                if field.anonymous_node:
+                    pass
+                else:
+                    self._transformer.resolve_type(field.type)
+        if isinstance(node, (Class, 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, Class):
+            for iface in node.interfaces:
+                self._transformer.resolve_type(iface)
+        if isinstance(node, 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, Enum):
+                continue
+            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._namespace.itervalues():
+            if not isinstance(node, 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) -> (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, (Class, Interface, Record, Union, GLibBoxedOther)):
+            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, (Class, Interface, Record, Union, 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, (Class, Record, Union, 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, 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
+        target.constructors.append(func)
+        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, 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, 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 = 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, 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."""
+
+        def _handle_closure(param, closure_idx, closure_param):
+            if (closure_param.type.is_equiv(TYPE_ANY) and
+                closure_param.argname.endswith('data')):
+                param.closure_name = closure_param.argname
+                param.closure_index = closure_idx
+                return True
+            return False
+
+        def _handle_destroy(param, destroy_idx, destroy_param):
+            if destroy_param.type.target_giname == 'GLib.DestroyNotify':
+                param.destroy_name = destroy_param.argname
+                param.destroy_index = destroy_idx
+                param.scope = PARAM_SCOPE_NOTIFIED
+                param.transfer = PARAM_TRANSFER_NONE
+                return True
+            return False
+
+        params = node.parameters
+        callback_index = -1
+        for i, param in enumerate(params):
+            node = self._transformer.lookup_typenode(param.type)
+            if not isinstance(node, Callback):
+                continue
+            # Special case GAsyncReadyCallback
+            if param.type.target_giname == 'Gio.AsyncReadyCallback':
+                param.scope = OPT_SCOPE_ASYNC
+                param.transfer = PARAM_TRANSFER_NONE
+                continue
+
+            # 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 = _handle_closure(param, j, params[j])
+            had_destroy = _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:
+                _handle_closure(param, j, params[j])
+            if not had_destroy:
+                _handle_destroy(param, j, params[j])
+
+    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 363f6f6..574dfff 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -21,11 +21,12 @@
 #
 
 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
@@ -34,6 +35,11 @@ from giscanner.minixpath import myxpath, 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")
@@ -101,15 +116,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 +145,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)
-
-    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.parse(path)
 
-    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 +218,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])
 
@@ -277,11 +254,8 @@ 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,
+                              strip_prefix=options.strip_prefix)
     if options.warn_all:
         transformer.enable_warnings(True)
     transformer.set_include_paths(options.include_paths)
@@ -314,8 +288,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
@@ -332,16 +305,16 @@ 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
@@ -351,14 +324,25 @@ def scanner_main(args):
         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 ae3d29d..aff9a2f 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..0bb8958
--- /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.
+#
+
+import os, sys
+from StringIO import StringIO
+from contextlib import contextmanager
+from .ast import *
+from .glibast import *
+from .codegen import CCodeGenerator
+
+INTROSPECTABLE_BASIC = filter(lambda x: x not in (TYPE_NONE, TYPE_ANY,
+                                                  TYPE_LONG_LONG, TYPE_LONG_ULONG,
+                                                  TYPE_LONG_DOUBLE), GIR_TYPES)
+
+DEFAULT_C_VALUES = {TYPE_ANY: 'NULL',
+                    TYPE_STRING: '""',
+                    TYPE_FILENAME: '""',
+                    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 = Namespace('Everything', '1.0')
+        self.gen = CCodeGenerator(self.namespace, out_h_filename, out_c_filename)
+
+    def write(self):
+        
+        func = Function('nullfunc', Return(TYPE_NONE, transfer=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 = Function(name, Return(typeval, transfer=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 TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = Function(name, Return(TYPE_NONE, transfer=PARAM_TRANSFER_NONE),
+                            [Parameter('arg0', typeval, transfer=PARAM_TRANSFER_NONE,
+                                       direction=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 TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = Function(name, Return(TYPE_NONE, transfer=PARAM_TRANSFER_NONE),
+                            [Parameter('arg0', typeval, transfer=PARAM_TRANSFER_NONE,
+                                       direction=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 TYPE_NONE:
+                continue
+            name = prefix + uscore_from_type(typeval)
+            sym = self.gen.gen_symbol(name)
+            func = Function(name, Return(typeval, transfer=PARAM_TRANSFER_NONE),
+                            [Parameter('arg0', typeval, transfer=PARAM_TRANSFER_NONE,
+                                       direction=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..399306e 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -22,13 +22,11 @@ import os
 import sys
 
 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)
+                  Parameter, Return, Record, Field,
+                  Type, Array, List, Map, Alias, Interface, Class, Node, Union,
+                  Varargs, Constant, type_names, basic_type_names,
+                  default_array_types, TYPE_STRING, TYPE_ANY)
 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
 from .girparser import GIRParser
 from .odict import odict
 from .sourcescanner import (
@@ -41,55 +39,33 @@ from .sourcescanner import (
     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):
+    def __init__(self, cachestore, namespace_name, namespace_version,
+                 strip_prefix=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 = Namespace(namespace_name, namespace_version,
+                                    c_prefix=strip_prefix)
         self._pkg_config_packages = set()
         self._typedefs_ns = {}
-        self._strip_prefix = ''
+        self._strip_prefix = self._namespace.c_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
@@ -106,26 +82,65 @@ class Transformer(object):
     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:
+                try:
+                    self._namespace.append(node)
+                except ValueError, e:
+                    original = self._namespace.get(node.name)
+                    positions = set()
+                    positions.update(original.file_positions)
+                    positions.update(node.file_positions)
+                    self.log_warning("Namespace conflict for '%s'" % (node.name, ),
+                                     positions)
+                    sys.exit(1)
+        # 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"
+        for typedef,compound in self._typedefs_ns.iteritems():
+            ns_compound = self._namespace.get(compound.name)
+            if not ns_compound:
+                disguised = Record(compound.name, typedef, disguised=True)
+                self._namespace.append(disguised)
 
     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 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:
@@ -136,6 +151,7 @@ If the warning is related to a Node type, see log_node_warning()."""
         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,14 +161,20 @@ 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]
         if prefix:
             print >>sys.stderr, \
-'''%s: warning: %s: %s: %s''' % (position, self._namespace.name,
+'''%s: warning: %s: %s: %s''' % (last_position, self._namespace.name,
                                  prefix, text)
         else:
             print >>sys.stderr, \
-'''%s: warning: %s: %s''' % (position, self._namespace.name, text)
+'''%s: warning: %s: %s''' % (last_position, self._namespace.name, text)
+        if fatal:
+            sys.exit(1)
 
     def log_symbol_warning(self, symbol, text):
         """Log a warning in the context of the given symbol."""
@@ -160,17 +182,22 @@ If the warning is related to a Node type, see log_node_warning()."""
         prefix = "symbol=%r" % (symbol.ident, )
         self.log_warning(text, file_positions, prefix=prefix)
 
-    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
 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):
@@ -179,7 +206,7 @@ context will be used."""
                 name = context.name
             text = "%s: %s" % (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 +227,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,36 +236,57 @@ 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)
+        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 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."""
+        matches = []
+        for ns in self._iter_namespaces():
+            if ns.contains_ident(ident):
+                matches.append((ns, ident[len(ns.c_prefix):]))
+        if matches:
+            matches.sort(lambda x,y : cmp(len(x[0].c_prefix), len(y[0].c_prefix)))
+            return 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():
+            prefix = ns.uscore_prefix + '_'
+            if symbol.startswith(prefix):
+                matches.append((ns, symbol[len(prefix):]))
+        if matches:
+            matches.sort(lambda x,y : cmp(len(x[0].uscore_prefix), len(y[0].uscore_prefix)))
+            return matches[0]
+        raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
+
+    def _strip_symbol_or_warn(self, symbol):
+        ident = symbol.ident
+        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")
+            return None
+        if ns != self._namespace:
+            self.log_symbol_warning(symbol, "Skipping foreign symbol from namespace %s" % (ns.name, ))
+            return None
+        if hidden:
+            return '_' + name
+        return name
 
     def remove_prefix(self, name, isfunction=False):
         # when --strip-prefix=g:
@@ -268,17 +315,20 @@ 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)
+        # FIXME - we need to require an annotation on
+        # #defines to have them be constants, otherwise
+        # namespace explosion can occur
         elif stype == CSYMBOL_TYPE_CONST:
-            return self._create_const(symbol)
+            pass
+        # 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):
@@ -328,74 +378,15 @@ context will be used."""
             klass = 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 = Function(name, return_, parameters, False, symbol.ident)
         func.add_symbol_reference(symbol)
         return func
 
@@ -413,11 +404,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:
@@ -442,21 +432,17 @@ 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 = Array(None, self.create_type(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)
+                ftype = self._create_type_from_base(symbol.base_type)
             # Fields are assumed to be read-write
             # (except for Objects, see also glibtransformer.py)
-            node = Field(symbol.ident, ftype, ftype.name,
+            node = Field(symbol.ident, ftype,
                          readable=True, writable=True, bits=symbol.const_int)
-            node.add_symbol_reference(symbol)
         return node
 
     def _create_typedef(self, symbol):
@@ -479,9 +465,9 @@ context will be used."""
                        CTYPE_VOID):
             name = self.remove_prefix(symbol.ident)
             if symbol.base_type.name:
-                target = self.remove_prefix(symbol.base_type.name)
+                target = self.create_type(symbol.base_type.name)
             else:
-                target = 'none'
+                target = TYPE_ANY
             if name in type_names:
                 return None
             return Alias(name, target, ctype=symbol.ident)
@@ -494,22 +480,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 = 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 +511,49 @@ 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 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):
         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(ctype, is_const=const)
+
+    def create_type(self, ctype, is_const=False):
+        canonical = self._canonicalize_ctype(ctype)
+        base = canonical.replace('*', '')
+
+        fundamental = type_names.get(base)
+        if fundamental is not None:
+            return Type(target_fundamental=fundamental.target_fundamental,
+                        ctype=ctype,
+                        is_const=is_const)
+        elif canonical in default_array_types:
+            element_type = self.create_type(canonical[:-1])
+            return Array(None, element_type, ctype=ctype,
+                         is_const=is_const)
+        elif base in ('GList', 'GSList'):
+            return List('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+                        is_const=is_const)
+        elif base in ('GArray', 'GPtrArray', 'GByteArray'):
+            return Array('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+                         is_const=is_const) 
+        elif base == 'GLib.HashTable':
+            return Map(TYPE_ANY, TYPE_ANY, ctype=ctype, is_const=is_const)
+        return Type(ctype=ctype, is_const=is_const)
 
     def _create_parameter(self, symbol):
         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
             ptype = Varargs()
         else:
-            ptype = self._create_type(symbol.base_type,
-                                      is_param=True, is_retval=False)
-            ptype = self.resolve_param_type(ptype)
+            ptype = self._create_type_from_base(symbol.base_type)
         return 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_
+        return Return(self._create_type_from_base(source_type))
 
     def _create_const(self, symbol):
         # Don't create constants for non-public things
@@ -580,15 +561,17 @@ 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)
+        name = self._strip_symbol_or_warn(symbol)
+        if not name:
+            return None
         if symbol.const_string is not None:
-            type_name = 'utf8'
+            typeval = TYPE_STRING
             value = symbol.const_string
         elif symbol.const_int is not None:
-            type_name = 'gint'
+            type_name = 'int'
             value = symbol.const_int
         elif symbol.const_double is not None:
-            type_name = 'gdouble'
+            type_name = 'double'
             value = symbol.const_double
         else:
             raise AssertionError()
@@ -599,19 +582,17 @@ context will be used."""
 
     def _create_typedef_struct(self, symbol, disguised=False):
         name = self.remove_prefix(symbol.ident)
-        struct = Struct(name, symbol.ident, disguised)
+        struct = Record(name, symbol.ident, disguised)
         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)
         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)
@@ -640,15 +621,19 @@ context will be used."""
                 compound = klass(name, symbol.ident)
 
         for child in symbol.base_type.child_list:
-            field = self._traverse_one(child)
-            if field:
-                compound.fields.append(field)
+            child_node = self._traverse_one(child)
+            if isinstance(child_node, Field):
+                field = child_node
+            else:
+                field = Field(child.ident, None, True, False,
+                              anonymous_node=child_node)
+            compound.fields.append(field)
 
         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(Record, symbol, anonymous)
 
     def _create_union(self, symbol, anonymous=False):
         return self._create_compound(Union, symbol, anonymous)
@@ -659,72 +644,53 @@ context will be used."""
 
         # Mark the 'user_data' arguments
         for i, param in enumerate(parameters):
-            if (param.type.name == TYPE_ANY and
-                param.name == 'user_data'):
+            if (param.type.target_fundamental == 'gpointer' and
+                param.argname == 'user_data'):
                 param.closure_index = i
 
         if symbol.ident.find('_') > 0:
             name = self.remove_prefix(symbol.ident, True)
         else:
             name = self.remove_prefix(symbol.ident)
-        callback = Callback(name, retval, parameters, symbol.ident)
+        callback = Callback(name, retval, parameters, False)
         callback.add_symbol_reference(symbol)
 
         return callback
 
+    def create_and_resolve_type(self, ctype):
+        """Parse a C type string, returning a Type object, and
+        resolve it."""
+        typeval = self.create_type(ctype)
+        self.resolve_type(typeval)
+        return typeval
+
+    def resolve_type(self, typeval):
+        if isinstance(typeval, (Array, List)):
+            self.resolve_type(typeval.element_type)
+            return
+        elif isinstance(typeval, 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 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 +708,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 +717,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/tests/Makefile.am b/tests/Makefile.am
index 0c51358..157ec99 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1 +1,30 @@
 SUBDIRS = . scanner repository offsets
+
+BUILT_SOURCES=
+CLEANFILES=
+
+INCLUDES = $(GOBJECT_CFLAGS)
+LIBADD = $(GOBJECT_LIBS)
+
+noinst_LTLIBRARIES = libeverything-1.0.la
+
+libeverything_1_0_la_SOURCES = everything.c
+
+BUILT_SOURCES += everything.c everything.h
+
+CLEANFILES += $(BUILT_SOURCES) everything-stamp.h
+everything-stamp.h: Makefile
+	$(AM_V_GEN) $(top_builddir)/tools/g-ir-scanner --test-codegen=Everything,everything.h,everything.c
+	touch $@
+
+everything.c: everything-stamp.h
+	@true
+
+everything.h: everything-stamp.h
+	@true
+
+Everything-1.0.gir: libeverything-1.0.la
+	$(AM_V_GEN) $(top_builddir)/tools/g-ir-scanner --warn-all --reparse-validate --namespace=Everything --version=1.0 --library=libeverything-1.0.la everything.h everything.c --output=$@
+
+Everything-1.0.typelib: Everything-1.0.gir
+	$(AM_V_GEN) $(top_builddir)/tools/g-ir-compiler $< -o $@
diff --git a/tests/scanner/annotation-1.0-expected.gir b/tests/scanner/Annotation-1.0-expected.gir
similarity index 100%
rename from tests/scanner/annotation-1.0-expected.gir
rename to tests/scanner/Annotation-1.0-expected.gir
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 015fd83..8d39d85 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -310,7 +310,7 @@ annotation_object_calleesowns (AnnotationObject *object,
  * This is a test for returning a list of strings, where
  * each string needs to be freed.
  *
- * Return value: (element-type utf8) (transfer): list of strings
+ * Return value: (element-type utf8) (transfer full): list of strings
  */
 GList*
 annotation_object_get_strings (AnnotationObject *object)
@@ -328,7 +328,7 @@ annotation_object_get_strings (AnnotationObject *object)
  * This is a test for returning a hash table mapping strings to
  * objects.
  *
- * Return value: (element-type utf8 GObject): hash table
+ * Return value: (element-type utf8 GObject) (transfer full): hash table
  */
 GHashTable*
 annotation_object_get_hash (AnnotationObject *object)
@@ -374,7 +374,7 @@ annotation_object_get_objects (AnnotationObject *object)
  *
  * Test returning a caller-owned object
  *
- * Return value: (transfer): The object
+ * Return value: (transfer full): The object
  **/
 GObject*
 annotation_object_create_object (AnnotationObject *object)
@@ -538,6 +538,8 @@ annotation_object_set_data3 (AnnotationObject *object,
  * annotation_object_allow_none:
  * @object: a #GObject
  * @somearg: (allow-none):
+ *
+ * Returns: (transfer none): %NULL always
  **/
 GObject*
 annotation_object_allow_none (AnnotationObject *object, const gchar *somearg)
@@ -562,6 +564,7 @@ annotation_object_notrans (AnnotationObject *object)
  * annotation_object_do_not_use:
  * @object: a #GObject
  *
+ * Returns: (transfer none): %NULL always
  * Deprecated: 0.12: Use annotation_object_create_object() instead.
  **/
 GObject*
@@ -620,7 +623,7 @@ annotation_init (int *argc, char ***argv)
  * annotation_return_array:
  * @length: (out): Number of return values
  *
- * Return value: (array length=length): The return value
+ * Return value: (transfer full) (array length=length): The return value
  **/
 char **
 annotation_return_array (int *length)
@@ -631,7 +634,7 @@ annotation_return_array (int *length)
 /**
  * annotation_string_zero_terminated:
  *
- * Return value: (array zero-terminated=1): The return value
+ * Return value: (transfer full) (array zero-terminated=1): The return value
  **/
 char **
 annotation_string_zero_terminated (void)
@@ -696,7 +699,7 @@ annotation_custom_destroy (AnnotationCallback callback,
 /**
  * annotation_get_source_file:
  *
- * Return value: (type filename): Source file
+ * Return value: (type filename) (transfer full): Source file
  */
 char *
 annotation_get_source_file (void)



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