[gobject-introspection/wip/transformer] We can scan GLib



commit fc9188b03db88ded5f4c9cdadeda043f510d914b
Author: Colin Walters <walters verbum org>
Date:   Thu Jun 17 15:55:37 2010 -0400

    We can scan GLib

 docs/g-ir-scanner.1           |    3 -
 gir/Makefile.am               |    9 +-
 gir/glib-2.0.c                |    2 +-
 giscanner/annotationparser.py |  149 ++++------
 giscanner/ast.py              |  627 +++++++++++++++++++++++------------------
 giscanner/girparser.py        |  306 +++++++++++++-------
 giscanner/girwriter.py        |  143 +++++-----
 giscanner/glibast.py          |   55 +---
 giscanner/glibtransformer.py  |  634 ++++++++++++-----------------------------
 giscanner/scannermain.py      |   86 ++-----
 giscanner/sourcescanner.py    |   10 +-
 giscanner/transformer.py      |  397 +++++++++-----------------
 tests/scanner/annotation.c    |    2 +-
 13 files changed, 1043 insertions(+), 1380 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 8fa02b0..f9de713 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 = --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 = --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 = --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 --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/glib-2.0.c b/gir/glib-2.0.c
index 6155dee..d91e74d 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/giscanner/annotationparser.py b/giscanner/annotationparser.py
index c642d56..b135b2d 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -36,7 +36,7 @@ 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 .odict import odict
 from .glibast import GLibBoxed
 
@@ -327,7 +327,7 @@ class AnnotationApplier(object):
 
     def parse(self, namespace):
         self._namespace = namespace
-        for node in namespace.nodes[:]:
+        for node in namespace.itervalues():
             self._parse_node(node)
         del self._namespace
 
@@ -472,8 +472,7 @@ class AnnotationApplier(object):
     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'):
+            if param.type.target_giname != 'GLib.DestroyNotify':
                 continue
             if i < 2:
                 break
@@ -554,7 +553,7 @@ class AnnotationApplier(object):
             t = tag.options.get('type')
             if not t:
                 return
-            field.type.name = self._transformer.resolve_type_name(t.one())
+            field.type = self._transformer.create_and_resolve_type(t.one())
 
     def _check_arg_annotations(self, parent, params, block):
         if block is None:
@@ -563,29 +562,32 @@ class AnnotationApplier(object):
             if tag == TAG_RETURNS:
                 continue
             for param in params:
-                if param.name == tag:
+                if param.argname == tag:
                     break
             else:
-                return
-                #print 'WARNING: annotation for "%s" refers to unknown ' \
-                #        'argument "%s"' % (parent.name, tag)
+                self._transformer.log_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)
+            tag = self._get_tag(block, param.argname)
             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):
+    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'
             raise InvalidAnnotationError(
-                "can't find parameter %s referenced by parameter %s of %r"
-                % (param_name, location_name, parent.name))
+                "can't find parameter %s referenced by %s of %r"
+                % (param_name, origin_name, parent.name))
 
         return index
 
@@ -603,8 +605,7 @@ class AnnotationApplier(object):
                         "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'):
+            elif param.type.target_giname == 'Gio.AsyncReadyCallback':
                 param.scope = OPT_SCOPE_ASYNC
                 param.transfer = PARAM_TRANSFER_NONE
 
@@ -612,13 +613,13 @@ class AnnotationApplier(object):
             if destroy:
                 param.destroy_index = self._get_parameter_index(parent,
                                                                 destroy.one(),
-                                                                param.name)
+                                                                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.name)
+                                                                param)
                 self._fixup_param_closure(parent, param)
         if isinstance(parent, Callback):
             if OPT_CLOSURE in options:
@@ -626,7 +627,7 @@ class AnnotationApplier(object):
                 # 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)
+                param.closure_index = parent.get_parameter_index(param)
                 self._fixup_param_closure(parent, param)
 
         self._parse_param_ret_common(parent, param, tag)
@@ -645,10 +646,7 @@ class AnnotationApplier(object):
         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
+        self._adjust_container_type(parent, node, options)
         if node.direction is None:
             node.direction = self._guess_direction(node)
             node.caller_allocates = False
@@ -682,7 +680,7 @@ class AnnotationApplier(object):
                 subtype = subtype.one()
             direction = PARAM_DIRECTION_OUT
             if subtype in (None, ''):
-                if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
+                if (not node.type.is_equiv(BASIC_GIR_TYPES)) and node.type.ctype:
                     caller_allocates = '**' not in node.type.ctype
                 else:
                     caller_allocates = False
@@ -705,25 +703,14 @@ class AnnotationApplier(object):
             return False
         if not ctype.endswith('*'):
             return False
-        if node.type.canonical in default_array_types:
+        if node.type 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):
+    def _adjust_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
@@ -734,13 +721,9 @@ class AnnotationApplier(object):
                 has_array = True
 
         if has_array:
-            container_type = self._parse_array(parent, node, options)
+            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
+            self._parse_element_type(parent, node, options)
 
     def _parse_array(self, parent, node, options):
         array_opt = options.get(OPT_ARRAY)
@@ -749,43 +732,39 @@ class AnnotationApplier(object):
         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
+            # 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
+
+        container_type = Array(None, 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.name)
+            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.name == 'utf8' and
+                node.type.is_equiv(TYPE_STRING) 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('int8')
-        container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
-        return container_type
+                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 _resolve(self, type_str, orig_node=None):
         def grab_one(type_str, resolver, top_combiner, combiner):
@@ -804,7 +783,7 @@ class AnnotationApplier(object):
                 rest = sep + rest
             return top_combiner(*args), rest
         def resolver(ident):
-            return self._transformer.resolve_param_type(Type(ident))
+            return self._transformer.create_and_resolve_type(ident)
         def combiner(base, *rest):
             if not rest:
                 return base
@@ -831,28 +810,17 @@ class AnnotationApplier(object):
     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*']):
+        if isinstance(node.type, List):
             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*']):
+            node.element_type = self._resolve(element_type[0])
+        elif isinstance(node.type, Map):
             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]))
+            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:
-            print 'FIXME: unhandled element-type container:', node
-        return container_type
+            self._transformer.log_node_warning(node, "Unknown container for element-type annotation")
 
     def _extract_transfer(self, parent, node, options):
         transfer_opt = options.get(OPT_TRANSFER)
@@ -927,6 +895,8 @@ class AnnotationApplier(object):
         node.get_value_func = tag.value if tag else None
 
     def _parse_rename_to_func(self, node, block):
+        # FIXME - this needs rethinking
+        return
         rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
         if rename_to_tag is None:
             return
@@ -964,7 +934,7 @@ class AnnotationApplier(object):
         if node.type.ctype:
             is_pointer = '*' in node.type.ctype
 
-        if is_pointer and node.type.name in BASIC_GIR_TYPES:
+        if is_pointer and node.type.is_equiv(BASIC_GIR_TYPES):
             return PARAM_DIRECTION_OUT
 
         return PARAM_DIRECTION_IN
@@ -976,7 +946,7 @@ class AnnotationApplier(object):
         # Anything with 'const' gets none
         if node.type.is_const:
             return PARAM_TRANSFER_NONE
-        elif node.type.name in [TYPE_NONE, TYPE_ANY]:
+        elif node.type in [TYPE_NONE, TYPE_ANY]:
             return PARAM_TRANSFER_NONE
         elif isinstance(node.type, Varargs):
             return PARAM_TRANSFER_NONE
@@ -988,18 +958,17 @@ class AnnotationApplier(object):
                 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':
+            elif node.type.is_equiv(TYPE_STRING):
                 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'):
+                node.type.element_type.is_equiv(TYPE_STRING)):
                 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)):
+            elif (node.type.is_equiv(BASIC_GIR_TYPES) or
+                  (node.type.is_equiv([TYPE_NONE, TYPE_ANY]) and
+                   node.type.is_const)):
                 return PARAM_TRANSFER_NONE
             else:
                 return PARAM_TRANSFER_FULL
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 3678e8c..d6fe47f 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,82 +19,127 @@
 # 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
+
+class Type(object):
+    resolved = property(lambda self: (self.target_fundamental or
+                                      self.target_giname or
+                                      self.target_foreign))
+    """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.
 """
-
-##
-## Basic types, modeled on GITypeTag but not equivalent
-##
-
-TYPE_NONE = 'none' # We differ from repository on these first two
-TYPE_ANY = 'any'
-TYPE_BOOLEAN = 'boolean'
-TYPE_INT8 = 'int8'
-TYPE_UINT8 = 'uint8'
-TYPE_SHORT = 'short'
-TYPE_USHORT = 'ushort'
-TYPE_INT16 = 'int16'
-TYPE_UINT16 = 'uint16'
-TYPE_INT = 'int'
-TYPE_UINT = 'uint'
-TYPE_INT32 = 'int32'
-TYPE_UINT32 = 'uint32'
-TYPE_INT64 = 'int64'
-TYPE_UINT64 = 'uint64'
-TYPE_LONG = 'long'
-TYPE_ULONG = 'ulong'
-TYPE_SIZET = 'gsize'
-TYPE_SSIZET = 'gssize'
-TYPE_GTYPE = 'GType'
-TYPE_FLOAT = 'float'
-TYPE_DOUBLE = 'double'
-TYPE_STRING = 'utf8' # requires zero-terminated
-TYPE_FILENAME = 'filename'
+    def __init__(self, 
+                 ctype=None, 
+                 target_fundamental=None,
+                 target_giname=None,
+                 target_foreign=None,
+                 is_const=False,
+                 origin_symbol=None):
+        self.ctype = ctype
+        self.origin_symbol = origin_symbol
+        if target_giname == 'signed int':
+            raise ValueError()
+        if 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 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):
+            for val in typeval:
+                if self.is_equiv(val):
+                    return True
+            return False
+        if not (self.resolved and typeval.resolved):
+            return False
+        if self.target_fundamental:
+            return typeval.target_fundamental == self.target_fundamental
+        elif self.target_giname:
+            return typeval.target_giname == self.target_giname
+        elif self.target_foreign:
+            return typeval.target_foreign == self.target_foreign
+
+    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)
+
+######
+## Fundamental types
+######
+# Two special ones
+TYPE_NONE = Type(target_fundamental='void', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
+# "Basic" types
+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 = 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 = Type(target_fundamental='long double', ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
+
+# C types with semantics overlaid
+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_SHORT, TYPE_USHORT, TYPE_INT,
-                   TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_SSIZET,
-                   TYPE_SIZET, TYPE_FLOAT, TYPE_DOUBLE,
-                   TYPE_GTYPE]
+                   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_UNICHAR, TYPE_GTYPE]
 GIR_TYPES = [TYPE_NONE, TYPE_ANY]
 GIR_TYPES.extend(BASIC_GIR_TYPES)
 GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
 
-# Higher-level data types
-TYPE_SEQUENCE = 'sequence' # Sequence of something
-
-# Wide/Unicode
-TYPE_UCHAR = 'uchar'
-TYPE_USTRING = 'ustring'
-
-##
-## Parameters
-##
-
-PARAM_DIRECTION_IN = 'in'
-PARAM_DIRECTION_OUT = 'out'
-PARAM_DIRECTION_INOUT = 'inout'
-
-PARAM_SCOPE_CALL = 'call'
-PARAM_SCOPE_ASYNC = 'async'
-PARAM_SCOPE_NOTIFIED = 'notified'
-
-PARAM_TRANSFER_NONE = 'none'
-PARAM_TRANSFER_CONTAINER = 'container'
-PARAM_TRANSFER_FULL = 'full'
-
 type_names = {}
-for name in GIR_TYPES:
-    type_names[name] = name
+for typeval in GIR_TYPES:
+    type_names[typeval.target_fundamental] = typeval
 
 # C builtin
-type_names['char'] = TYPE_INT8
+type_names['char'] = TYPE_CHAR
 type_names['signed char'] = TYPE_INT8
 type_names['unsigned char'] = TYPE_UINT8
 type_names['short'] = TYPE_SHORT
@@ -115,8 +160,24 @@ type_names['double'] = TYPE_DOUBLE
 type_names['char*'] = TYPE_STRING
 type_names['void*'] = TYPE_ANY
 type_names['void'] = TYPE_NONE
+# Also alias the signed one here
+type_names['signed long long'] = TYPE_LONG_LONG
+
+# A few additional GLib type aliases
+type_names['gchararray'] = TYPE_STRING
+type_names['gchar*'] = TYPE_STRING
+type_names['gsize'] = TYPE_ULONG
+type_names['gssize'] = TYPE_LONG
+type_names['gconstpointer'] = TYPE_ANY
 
-# Random C unix type definitions; note some of these may be GNU Libc
+# Some special C types that aren't scriptable, and we just squash
+type_names['va_list'] = TYPE_ANY
+
+# C stdio, used in GLib public headers; squash this for now here
+# until we move scanning into GLib and can (skip)
+type_names['FILE*'] = TYPE_ANY
+
+# One off C unix type definitions; note some of these may be GNU Libc
 # specific.  If someone is actually bitten by this, feel free to do
 # the required configure goop to determine their size and replace
 # here.
@@ -127,58 +188,187 @@ type_names['void'] = TYPE_NONE
 # methods are added under #ifdefs inside GLib itself.  We could just (skip)
 # the relevant methods, but on the other hand, since these types are just
 # integers it's easy enough to expand them.
-type_names['size_t'] = TYPE_SIZET
+type_names['size_t'] = type_names['gsize']
 type_names['time_t'] = TYPE_LONG
-type_names['off_t'] = TYPE_SIZET
+type_names['off_t'] = type_names['gsize']
 type_names['pid_t'] = TYPE_INT
 type_names['uid_t'] = TYPE_UINT
 type_names['gid_t'] = TYPE_UINT
 type_names['dev_t'] = TYPE_INT
 type_names['socklen_t'] = TYPE_INT32
+type_names['size_t'] = TYPE_ULONG
+type_names['ssize_t'] = TYPE_LONG
 
 # Obj-C
 type_names['id'] = TYPE_ANY
 
-# Suppress some GLib names
-type_names['uchar'] = TYPE_UINT8
-type_names['ushort'] = TYPE_USHORT
-type_names['size'] = TYPE_SIZET
-type_names['ssize'] = TYPE_SSIZET
-type_names['pointer'] = TYPE_ANY
-type_names['constpointer'] = 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['uint8*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
+default_array_types['guint8*'] = TYPE_UINT8
+default_array_types['guchar*'] = TYPE_UINT8
+default_array_types['gchar**'] = TYPE_STRING
 
 # These types, when seen by reference, are interpreted as out parameters
 default_out_types = (TYPE_SHORT, TYPE_USHORT, TYPE_INT, TYPE_UINT,
-                     TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
-                     TYPE_SIZET, TYPE_SSIZET)
+                     TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE)
 
+##
+## Parameters
+##
+
+PARAM_DIRECTION_IN = 'in'
+PARAM_DIRECTION_OUT = 'out'
+PARAM_DIRECTION_INOUT = 'inout'
+
+PARAM_SCOPE_CALL = 'call'
+PARAM_SCOPE_ASYNC = 'async'
+PARAM_SCOPE_NOTIFIED = 'notified'
 
-def type_name_from_ctype(ctype):
-    return type_names.get(ctype, ctype)
+PARAM_TRANSFER_NONE = 'none'
+PARAM_TRANSFER_CONTAINER = 'container'
+PARAM_TRANSFER_FULL = 'full'
 
+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 = 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 GIR_TYPES:
+            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 SystemExit(
+                    "transformer: conflict: ns=%r orig=%r new=%r" % 
+                    (self.name, previous, node))
+            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]
+        node.namespace = None
+        if hasattr(node, 'ctype'):
+            del self._ctypes[node.ctype]
+        if hasattr(node, 'symbol'):
+            del self._ctypes[node.symbol]
+
+    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)
+
+
+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."""
+
+    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 __cmp__(self, other):
+        nscmp = cmp(self.namespace, other.namespace)
+        if nscmp != 0:
+            return nscmp
         return cmp(self.name, other.name)
 
     def __repr__(self):
@@ -197,49 +387,6 @@ 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)
-
-        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)
 
 class Callable(Node):
 
@@ -248,29 +395,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.name == name:
+                return i
+
+    def get_parameter(self, name):
+        for parameter in self.parameters:
+            if parameter.name == 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
 
 
@@ -287,59 +438,56 @@ 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, )
-
 
 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, )
-
 
 class Map(Type):
 
-    def __init__(self, name, ctype, key_type, value_type):
-        Type.__init__(self, name, ctype)
+    def __init__(self, ctype, key_type, value_type, **kwargs):
+        Type.__init__(self, ctype=ctype, target_fundamental='<map>')
+        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)
-
 
 class Alias(Node):
 
@@ -348,14 +496,12 @@ 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]:
@@ -365,25 +511,32 @@ class TypeContainer(Node):
 
 
 class Parameter(TypeContainer):
+    """An argument to a function."""
 
-    def __init__(self, name, typenode, direction=None,
-                 transfer=None, allow_none=False, scope=None):
-        TypeContainer.__init__(self, name, typenode, transfer)
+    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
         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
+        self.caller_allocates = caller_allocates
         self.allow_none = allow_none
         self.scope = scope
         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):
@@ -392,10 +545,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):
@@ -404,22 +553,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):
 
@@ -429,7 +572,6 @@ class Record(Node):
         self.constructors = []
         self.symbol = symbol
         self.disguised = disguised
-        self.doc = None
         self.methods = []
 
     def remove_matching_children(self, pred):
@@ -437,36 +579,18 @@ class Record(Node):
         self.constructors = filter(pred, self.constructors)
         self.methods = filter(pred, self.methods)
 
-# BW compat, remove
-Struct = Record
 
+class Field(Annotated):
 
-class Field(Node):
-
-    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,7 +608,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)
@@ -492,10 +615,6 @@ 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)
 
 
 class Interface(Node):
@@ -509,70 +628,34 @@ class Interface(Node):
         self.properties = []
         self.fields = []
         self.prerequisites = []
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.methods)
 
 
 class Constant(Node):
 
-    def __init__(self, name, type_name, value):
+    def __init__(self, name, value_type, value):
         Node.__init__(self, name)
-        self.type = Type(type_name)
+        self.value_type = value_type
         self.value = value
 
-    def __repr__(self):
-        return 'Constant(%r, %r, %r)' % (
-            self.name, self.type, self.value)
-
 
-class Property(TypeContainer):
+class Property(Node):
 
-    def __init__(self, name, type_name, readable, writable,
-                 construct, construct_only, ctype=None, transfer=None):
-        self.type = Type(type_name, ctype)
-        TypeContainer.__init__(self, name, self.type, transfer)
+    def __init__(self, name, typeobj, readable, writable,
+                 construct, construct_only, transfer=None):
+        Node.__init__(self, name)
+        self.type = typeobj
         self.readable = readable
         self.writable = writable
         self.construct = construct
         self.construct_only = construct_only
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.type)
+        self.transfer = PARAM_TRANSFER_NONE
 
 
-# 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):
@@ -583,7 +666,3 @@ class Union(Node):
         self.constructors = []
         self.methods = []
         self.symbol = symbol
-        self.doc = None
-
-    def __repr__(self):
-        return 'Union(%r, %r)' % (self.name, self.fields, )
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 17cf630..113ca49 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -23,11 +23,12 @@ 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)
+                  Namespace, Parameter, Property, Return, Union, Type, List,
+                  VFunction, Record, Varargs, Include, PARAM_DIRECTION_IN,
+                  Annotated)
 from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibBoxedStruct,
-                      GLibBoxedUnion, GLibBoxedOther)
+                      GLibBoxedUnion, GLibBoxedOther, GLibRecord)
 
 from .girwriter import COMPATIBLE_GIR_VERSION
 
@@ -51,7 +52,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 +72,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 +85,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 +99,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 +123,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 +136,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,19 +173,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'],
-                      node.attrib['target'],
+                      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'):
@@ -181,38 +222,49 @@ class GIRParser(object):
             raise AssertionError(node)
 
         obj = klass(*ctor_args)
-        self._add_node(obj)
+        self._parse_generic_attribs(node, obj)
+        type_struct = node.attrib.get(_glibns('type-struct'))
+        if type_struct:
+            obj.glib_type_struct = 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')):
+        for callback in self._find_children(node, _corens('callback')):
             obj.fields.append(self._parse_function_common(callback, Callback))
-        for field in node.findall(_corens('field')):
+        for field in self._find_children(node, _corens('field')):
             obj.fields.append(self._parse_field(field))
-        for prop in node.findall(_corens('property')):
+        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']
@@ -221,29 +273,43 @@ 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):
@@ -252,24 +318,26 @@ class GIRParser(object):
                                      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')
+        self._parse_generic_attribs(node, struct)
+        self._namespace.append(struct)
+
+        for field in self._find_children(node, _corens('field')):
+            fieldobj = self._parse_field(field) 
+            struct.fields.append(fieldobj)
+        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 ctor in self._find_children(node, _corens('constructor')):
             struct.constructors.append(
                 self._parse_function_common(ctor, Function))
 
@@ -282,50 +350,67 @@ class GIRParser(object):
         else:
             union = Union(node.attrib['name'],
                           node.attrib.get(_cns('type')))
-        self._add_node(union)
+        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')):
+        for field in self._find_children(node, _corens('field')):
             union.fields.append(self._parse_field(field))
-        for method in node.findall(_corens('method')):
+        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))
 
     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['name']
+            ctype = typenode.attrib.get(_cns('type'))
+            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))
 
@@ -333,58 +418,69 @@ 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'))
+        callback_node = self._find_first_child(node, _corens('callback'))
+        if callback_node:
+            anonymous_node = self._parse_function_common(callback_node, Callback)
+            type_node = None
+        else:
+            anonymous_node = None
+            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:
@@ -397,10 +493,10 @@ class GIRParser(object):
             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 2e0a6cd..d3c95be 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,7 +22,7 @@
 from __future__ import with_statement
 
 from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
-                  Function, Interface, List, Map, Member, Struct, Union,
+                  Function, Interface, List, Map, Member, Record, Union,
                   Varargs, Type)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
                       GLibFlags, GLibObject, GLibInterface,
@@ -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)
@@ -153,10 +155,11 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('throws', '1'))
 
     def _write_alias(self, alias):
-        attrs = [('name', alias.name), ('target', alias.target)]
+        attrs = [('name', alias.name)]
         if alias.ctype is not None:
             attrs.append(('c:type', alias.ctype))
-        self.write_tag('alias', attrs)
+        with self.tagcontext('alias', attrs):
+            self._write_type(alias.target)
 
     def _write_callable(self, callable, tag_name, extra_attrs):
         attrs = [('name', callable.name)]
@@ -170,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):
@@ -205,8 +210,8 @@ and/or use gtk-doc annotations. ''')
         assert parameter.transfer is not None, parameter
 
         attrs = []
-        if parameter.name is not None:
-            attrs.append(('name', parameter.name))
+        if parameter.argname is not None:
+            attrs.append(('name', parameter.argname))
         if parameter.direction != 'in':
             attrs.append(('direction', parameter.direction))
             attrs.append(('caller-allocates',
@@ -225,62 +230,55 @@ 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 _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)
+        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('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('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, ('ref', ntype.target_giname))
+                prefix = self._namespace.name + '.'
+                if ntype.target_giname.startswith(prefix):
+                    attrs.insert(0, ('name', ntype.target_giname[len(prefix):]))
+                else:
+                    attrs.insert(0, ('name', ntype.target_giname))
+            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'))
+            else:
+                raise AssertionError("Caught unresolved type %r (ctype=%r)" % (ntype, ntype.ctype))
+            # Not a special type, just write it out
+            self.write_tag('type', attrs)
 
     def _write_enum(self, enum):
         attrs = [('name', enum.name)]
@@ -324,10 +322,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),
@@ -337,7 +334,7 @@ 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', node.parent.target_giname))
             if node.is_abstract:
                 attrs.append(('abstract', '1'))
         else:
@@ -347,7 +344,7 @@ 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', node.glib_type_struct.target_giname))
         if isinstance(node, GLibObject):
             if node.fundamental:
                 attrs.append(('glib:fundamental', '1'))
@@ -363,10 +360,10 @@ 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', iface.target_giname)])
             if isinstance(node, Interface):
                 for iface in node.prerequisites:
-                    self.write_tag('prerequisite', [('name', iface)])
+                    self.write_tag('prerequisite', [('name', iface.target_giname)])
             if isinstance(node, Class):
                 for method in node.constructors:
                     self._write_constructor(method)
@@ -415,7 +412,7 @@ 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):
@@ -441,7 +438,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))
+                              record.is_gtype_struct_for.target_giname))
         self._append_version(record, attrs)
         self._append_node_generic(record, attrs)
         if isinstance(record, GLibBoxed):
@@ -477,20 +474,16 @@ and/or use gtk-doc annotations. ''')
                 self._write_method(method)
 
     def _write_field(self, field, is_gtype_struct=False):
-        if isinstance(field, Function):
-            self._write_method(field)
-            return
-
-        if isinstance(field, Callback):
+        if field.anonymous_node:
             attrs = [('name', field.name)]
             with self.tagcontext('field', attrs):
                 self._write_generic(field)
-                if is_gtype_struct:
-                    self._write_callback(field)
+                if isinstance(field.anonymous_node, Callback):
+                    self._write_callback(field.anonymous_node)
                 else:
-                    attrs = [('name', 'any'), ('c:type', 'pointer')]
-                    self.write_tag('type', attrs)
-        elif isinstance(field, Struct):
+                    raise AssertionError("Unknown field anonymous: %r" \
+                                             % (field.anonymous_node, ))
+        elif isinstance(field, Record):
             self._write_record(field)
         elif isinstance(field, Union):
             self._write_union(field)
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index c712988..dd6079d 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -19,48 +19,7 @@
 #
 
 from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
-                  Property, Struct, Union, Record)
-from .ast import (
-    type_names, default_array_types,
-    TYPE_STRING, TYPE_INT8, TYPE_UINT8, TYPE_SHORT, TYPE_USHORT,
-    TYPE_INT16, TYPE_UINT16, TYPE_INT, TYPE_UINT, TYPE_UINT32,
-    TYPE_INT32, TYPE_LONG, TYPE_ULONG, TYPE_INT64, TYPE_UINT64,
-    TYPE_FLOAT, TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_ANY, TYPE_SSIZET,
-    TYPE_SIZET)
-
-
-# Glib type names
-type_names['gchararray'] = TYPE_STRING
-type_names['gint8'] = TYPE_INT8
-type_names['guint8'] = TYPE_UINT8
-type_names['gint16'] = TYPE_INT16
-type_names['guint16'] = TYPE_UINT16
-type_names['gint'] = TYPE_INT
-type_names['guint'] = TYPE_UINT
-type_names['gint32'] = TYPE_INT32
-type_names['guint32'] = TYPE_UINT32
-type_names['glong'] = TYPE_LONG
-type_names['gulong'] = TYPE_ULONG
-type_names['gint64'] = TYPE_INT64
-type_names['guint64'] = TYPE_UINT64
-type_names['gfloat'] = TYPE_FLOAT
-type_names['gdouble'] = TYPE_DOUBLE
-type_names['gchar*'] = TYPE_STRING
-type_names['gboolean'] = TYPE_BOOLEAN
-type_names['gpointer'] = TYPE_ANY
-type_names['gconstpointer'] = TYPE_ANY
-type_names['gsize'] = TYPE_SIZET
-type_names['gssize'] = TYPE_SSIZET
-type_names['gchar'] = TYPE_INT8
-type_names['guchar'] = TYPE_UINT8
-type_names['gshort'] = TYPE_SHORT
-type_names['gushort'] = TYPE_USHORT
-
-# It's not very nice to duplicate the array types from ast.py,
-# but a clean fix is hard without essentially hardcoding
-# char * again inside transformer.py
-default_array_types['guint8*'] = TYPE_UINT8
-default_array_types['gchar**'] = TYPE_STRING
+                  Property, Union, Record)
 
 class GLibRecord(Record):
     def __init__(self, *args, **kwargs):
@@ -68,7 +27,7 @@ class GLibRecord(Record):
 
     @classmethod
     def from_record(cls, record):
-        obj = cls(record.name, record.symbol)
+        obj = cls(record.namespace, record.name, record.symbol)
         obj.fields = record.fields
         obj.constructors = record.constructors
         obj.disguised = record.disguised
@@ -127,6 +86,8 @@ class GLibObject(Class):
         self.get_value_func = None
         self.signals = []
         self.ctype = ctype or type_name
+        # Unresolved state
+        self.parent_gtype_names = []
 
 
 class GLibBoxed:
@@ -136,10 +97,10 @@ class GLibBoxed:
         self.get_type = get_type
 
 
-class GLibBoxedStruct(Struct, GLibBoxed):
+class GLibBoxedStruct(Record, GLibBoxed):
 
     def __init__(self, name, type_name, get_type, ctype=None):
-        Struct.__init__(self, name, ctype or type_name)
+        Record.__init__(self, name, ctype or type_name)
         GLibBoxed.__init__(self, type_name, get_type)
 
 
@@ -175,10 +136,10 @@ class GLibProperty(Property):
     pass
 
 
-class GLibSignal(Node):
+class GLibSignal(object):
 
     def __init__(self, name, retval):
-        Node.__init__(self, name)
+        self.name = name
         self.retval = retval
         self.parameters = []
         self.doc = None
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index 1178c5f..0fcec82 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,14 +28,12 @@ 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,
-                  Map, Varargs)
-from .transformer import Names
+                  Field, VFunction, default_array_types,
+                  TYPE_ANY, TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
+                  Map, Varargs, type_names)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
-                      GLibBoxedUnion, GLibBoxedOther, GLibRecord,
-                      type_names)
+                      GLibBoxedUnion, GLibBoxedOther, GLibRecord)
 from .utils import to_underscores, to_underscores_noprefix
 
 default_array_types['guchar*'] = TYPE_UINT8
@@ -50,18 +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',
@@ -97,11 +83,9 @@ 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 = []
@@ -110,7 +94,6 @@ class GLibTransformer(object):
         self._failed_types = {}
         self._boxed_types = {}
         self._private_internal_types = {}
-        self._validating = False
 
     # Public API
 
@@ -121,18 +104,22 @@ 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):
+                print "doing initparse of %r" % (node, )
+                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,73 +131,38 @@ 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
+        # Pair boxed structures; doesn't depend on anything else.
         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):
-                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
+        # Second pass; Initial type resolution
+        for node in self._namespace.itervalues():
+            self.walk_node(node, self._pass_type_resolution, [])
 
-    # Private
+        # Now move around the functions that are actually methods
+        #for node in self._namespace:
+        #    if isinstance(node, Function):
+        #        self._pair_function(node)
 
-    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]
+        # Third pass
+        for node in self._namespace.itervalues():
+            if isinstance(node, Record):
+                self._pass3_pair_class_record(node)
+            elif isinstance(node, Callable):
+                self._pass3_callable(node)
+        self._resolve_quarks()
 
-    def _get_attribute(self, name):
-        node = self._names.names.get(name)
-        if node:
-            return node[1]
-        return None
+        return self._namespace
 
-    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
+    # Private
 
     def _get_no_uscore_prefixed_name(self, type_name):
         # Besides the straight underscore conversion, we also try
@@ -234,13 +186,28 @@ class GLibTransformer(object):
         if no_uscore_prefixed not in self._uscore_type_names:
             self._uscore_type_names[no_uscore_prefixed] = node
 
+    def _pass_type_resolution(self, node, chain):
+        if isinstance(node, Alias):
+            self._transformer.resolve_type(node.target)
+        elif isinstance(node, Callable):
+            for parameter in node.parameters:
+                self._transformer.resolve_type(parameter.type)
+            self._transformer.resolve_type(node.retval.type)
+        elif isinstance(node, Constant):
+            self._transformer.resolve_type(node.value_type)
+        elif isinstance(node, (Class, Interface, Record, Union)):
+            for field in node.fields:
+                self._transformer.resolve_type(field.type)
+
     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():
+        for enum in self._namespace.itervalues():
+            if not isinstance(enum, Enum):
+                continue
             type_name = enum.symbol
             uscored = to_underscores(type_name).lower()
 
@@ -285,7 +252,11 @@ class GLibTransformer(object):
                 continue
         return Unresolved(gtype_names[0])
 
-    def _execute_binary(self):
+    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
+
         in_path = os.path.join(self._binary.tmpdir, 'types.txt')
         f = open(in_path, 'w')
         # TODO: Introspect GQuark functions
@@ -301,22 +272,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 +287,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 = 'GLib.Object'
             symbol = 'g_initially_unowned_get_type'
         else:
             assert False
@@ -341,69 +303,25 @@ class GLibTransformer(object):
             # GInitiallyUnowned so that struct offset computation
             # works correctly.
             gnode.fields = self._names.names['Object'][1].fields
-        self._add_attribute(gnode)
+        self._namespace.append(gnode, replace=True)
         self._register_internal_type(type_name, gnode)
 
     # 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)
+        elif symbol.endswith('_error_quark'):
+            self._initparse_error_quark_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
+        if (self._namespace.name == 'GObject' and
             symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
             # We handle these internally, see _create_gobject
             return True
@@ -421,13 +339,10 @@ class GLibTransformer(object):
         self._get_type_functions.append(symbol)
         return True
 
-    def _parse_error_quark_function(self, func):
-        if not func.symbol.endswith('_error_quark'):
-            return False
+    def _initparse_error_quark_function(self, func):
         if func.parameters:
             return False
-        if (func.retval.type.name != 'GLib.Quark' and
-            func.retval.type.ctype != 'GQuark'):
+        if func.retval.type.target_giname != 'GLib.Quark':
             return False
 
         self._error_quark_functions.append(func)
@@ -457,7 +372,7 @@ class GLibTransformer(object):
             target_klass = None
         if not target_klass:
             return None
-        self._remove_attribute(func.name)
+        self._namespace.remove(func)
         func.name = methname
         target_klass.static_methods.append(func)
         func.is_method = True
@@ -491,7 +406,7 @@ class GLibTransformer(object):
             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:
+            if argtype in self._namespace.type_names:
                 node = self._names.type_names[argtype][1]
                 if hasattr(node, 'get_type'):
                     name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
@@ -550,7 +465,7 @@ class GLibTransformer(object):
             name = self._transformer.remove_prefix(remove_prefix)
             func.retval.type = Type(name, func.retval.type.ctype)
 
-        self._remove_attribute(func.name)
+        self._namespace.remove(func)
         # Strip namespace and object prefix: gtk_window_new -> new
         func.name = func.symbol[len(prefix)+1:]
         if is_method:
@@ -562,38 +477,15 @@ class GLibTransformer(object):
             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[:]
-
-    def _parse_callback(self, callback):
-        self._add_attribute(callback)
+        self._namespace.append(record, replace=True)
 
     def _strip_class_suffix(self, name):
         if (name.endswith('Class') or
@@ -612,7 +504,7 @@ class GLibTransformer(object):
             return True
         return False
 
-    def _pair_class_record(self, maybe_class):
+    def _pass3_pair_class_record(self, maybe_class):
         name = self._strip_class_suffix(maybe_class.name)
         if name == maybe_class.name:
             return
@@ -672,13 +564,13 @@ class GLibTransformer(object):
                 vfunc.invoker = method
 
         gclass_struct = GLibRecord.from_record(class_struct)
-        self._remove_attribute(class_struct.name)
-        self._add_attribute(gclass_struct, True)
+        self._namespace.append(gclass_struct, replace=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 +601,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']
@@ -722,28 +613,24 @@ class GLibTransformer(object):
         # 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)
+        node.parent_gtype_names = xmlnode.attrib['parents'].split(',')
         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'):
@@ -753,11 +640,9 @@ class GLibTransformer(object):
         # 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']
@@ -784,7 +669,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,9 +678,8 @@ 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:
@@ -803,8 +687,8 @@ class GLibTransformer(object):
                 else:
                     name = '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(name, ptype, PARAM_TRANSFER_NONE)
                 param.transfer = 'none'
                 signal.parameters.append(param)
             node.signals.append(signal)
@@ -870,30 +754,18 @@ 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 _walk(self, node, callback, chain):
-        if not isinstance(node, Node):
-            return
+    def walk_node(self, node, callback, chain):
+        assert isinstance(node, Node)
         if not callback(node, chain):
             return
         chain.append(node)
         def _subwalk(subnode):
             self._walk(subnode, callback, chain)
-        if isinstance(node, (Callback, Callable)):
-            _subwalk(node.retval)
-            for parameter in node.parameters:
-                _subwalk(parameter)
-        elif isinstance(node, (Array, List)):
-            _subwalk(node.element_type)
-        elif isinstance(node, Map):
-            _subwalk(node.key_type)
-            _subwalk(node.value_type)
-        elif isinstance(node, Bitfield):
-            pass
-        elif isinstance(node, Record):
+        if isinstance(node, Record):
             for ctor in node.constructors:
                 _subwalk(ctor)
             for func in node.methods:
@@ -909,21 +781,11 @@ class GLibTransformer(object):
                 _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)
@@ -935,53 +797,9 @@ class GLibTransformer(object):
             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):
+    def _pair_function(self, func):
         for parser in [self._parse_constructor,
                        self._parse_method,
                        self._parse_static_method]:
@@ -991,148 +809,70 @@ class GLibTransformer(object):
                 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 _handle_closure(self, param, closure_idx, closure_param):
+        if (closure_param.type is TYPE_ANY and
+            closure_param.name.endswith('data')):
+            param.closure_name = closure_param.argname
+            param.closure_index = closure_idx
+            return True
+        return False
 
-    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 _handle_destroy(self, param, destroy_idx, destroy_param):
+        if destroy_param.type.target_giname == 'GLib.DestroyNotify':
+            param.destroy_name = destroy_param.argname
+            param.destroy_index = destroy_idx
+            return True
+        return False
 
-    def _resolve_property(self, prop):
-        prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
+    def _pass3_callable(self, node):
+        self._pass3_callable_callbacks(node)
+        self._pass3_callable_throws(node)
+
+    def _pass3_callable_callbacks(self, node):
+        """Check to see if we have anything that looks like a
+        callback+user_data+GDestroyNotify set."""
+        params = node.parameters
+        for i, param in enumerate(params):
+            node = self._transformer.lookup_typenode(param.type)
+            if not isinstance(node, Callback):
+                continue
 
-    def _adjust_throws(self, func):
-        if func.parameters == []:
+            # 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 _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 = func.parameters.pop()
-
+        last_param = node.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
+            node.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
+            node.parameters.append(last_param)
 
     # Validation
 
@@ -1150,9 +890,7 @@ class GLibTransformer(object):
             if isinstance(node.type, Varargs):
                 parent.introspectable = False
             elif not isinstance(node.type, List) and \
-                 (node.type.name == 'GLib.List' or
-                  (self._transformer._namespace.name == 'GLib'
-                   and node.type.name == 'List')):
+                 (node.type.target_giname == 'GLib.List'):
                 if isinstance(node, Parameter):
                     self._transformer.log_node_warning(parent,
 """Missing (element-type) annotation on argument %r""" % (node.name, ),
@@ -1162,24 +900,24 @@ class GLibTransformer(object):
 """Missing (element-type) annotation on return value""", context=parent)
                 parent.introspectable = False
 
-    def _analyze_node(self, node, stack):
-        if node.skip:
+    def _analyze_node(self, obj, stack):
+        if isinstance(obj, Node) and obj.skip:
             return False
         # Combine one-pass checks here
-        self._interface_vfunc_check(node, stack)
+        self._interface_vfunc_check(obj, stack)
         # Our first pass for scriptability
-        self._introspectable_analysis(node, stack)
+        self._introspectable_analysis(obj, stack)
         return True
 
-    def _introspectable_pass2(self, node, stack):
-        if node.skip:
+    def _introspectable_pass2(self, obj, stack):
+        if isinstance(obj, Node) and obj.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):
+        if isinstance(obj, TypeContainer):
             parent = stack[-1]
-            target = self._lookup_node(node.type.name)
+            target = self._transformer.lookup_typenode(obj.type)
             if target and not target.introspectable:
                 parent.introspectable = False
         return True
@@ -1187,7 +925,7 @@ class GLibTransformer(object):
     # 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, [])
+        for node in self._namespace.itervalues():
+            self.walk_node(node, self._analyze_node, [])
+        for node in self._namespace.itervalues():
+            self.walk_node(node, self._introspectable_pass2, [])
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 363f6f6..4eaed05 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -34,6 +34,8 @@ 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.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
 
 def _get_option_parser():
     parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +51,9 @@ def _get_option_parser():
     parser.add_option("-i", "--include",
                       action="append", dest="includes", default=[],
                       help="include types for other gidls")
+    parser.add_option('', "--passthrough-gir",
+                      action="store", dest="passthrough_gir", default=None,
+                      help="Parse and re-output the specified GIR")
     parser.add_option("", "--add-include-path",
                       action="append", dest="include_paths", default=[],
                       help="include paths for other GIR files")
@@ -101,15 +106,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,54 +135,18 @@ 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):
     parser = GIRParser()
-    parser.parse_tree(tree)
+    parser.parse(path)
 
     writer = GIRWriter(parser.get_namespace(),
                        parser.get_shared_libraries(),
-                       parser.get_includes())
+                       parser.get_includes(),
+                       parser.get_pkgconfig_packages(),
+                       parser.get_c_includes(),
+                       parser.get_c_prefix())
     sys.stdout.write(writer.get_xml())
-    return 0
-
-def inject(path, additions, outpath):
-    from giscanner.girparser import GIRParser
-    from giscanner.girwriter import GIRWriter
-    from xml.etree.cElementTree import parse
-
-    tree = parse(path)
-    root = tree.getroot()
-    injectDoc = parse(open(additions))
-    for node in injectDoc.getroot():
-        injectPath = node.attrib['path']
-        target = myxpath(root, injectPath)
-        if not target:
-            raise ValueError("Couldn't find path %r" % (injectPath, ))
-        for child in node:
-            target.append(child)
-
-    parser = GIRParser()
-    parser.parse_tree(tree)
-    writer = GIRWriter(parser.get_namespace(),
-                       parser.get_shared_libraries(),
-                       parser.get_includes())
-    outf = open(outpath, 'w')
-    outf.write(writer.get_xml())
-    outf.close()
-    return 0
+    sys.exit(0)
 
 def validate(assertions, path):
     from xml.etree.cElementTree import parse
@@ -237,18 +200,12 @@ def scanner_main(args):
     parser = _get_option_parser()
     (options, args) = parser.parse_args(args)
 
+    if options.passthrough_gir:
+        return passthrough_gir(options.passthrough_gir)
+
     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 +234,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 +268,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
@@ -352,8 +305,7 @@ def scanner_main(args):
     else:
         exported_packages = options.packages
     writer = Writer(namespace, shlibs, transformer.get_includes(),
-                    exported_packages, options.c_includes,
-                    transformer.get_strip_prefix())
+                    exported_packages, options.c_includes)
     data = writer.get_xml()
     if options.output:
         fd = open(options.output, "w")
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index acfc048..b024baf 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/transformer.py b/giscanner/transformer.py
index 319e43c..f23bf3c 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -22,12 +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_STRING, BASIC_GIR_TYPES)
+                  Parameter, Return, Record, Field,
+                  Type, Array, List, Map, Alias, Interface, Class, Node, Union,
+                  Varargs, Constant, type_names, default_array_types, TYPE_STRING, TYPE_ANY,
+                  BASIC_GIR_TYPES)
 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
 from .girparser import GIRParser
 from .odict import odict
 from .sourcescanner import (
@@ -43,52 +42,27 @@ 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):
-    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)
-
-
 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
@@ -105,22 +79,44 @@ class Transformer(object):
     def parse(self):
         nodes = []
         for symbol in self.generator.get_symbols():
-            try:
-                node = self._traverse_one(symbol)
-            except SkipError:
+            if symbol.ident.startswith('_'):
                 continue
-            self._add_node(node)
-        return self._namespace
+            node = self._traverse_one(symbol)
+            if node:
+                self._namespace.append(node)
 
     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
 
@@ -195,7 +191,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)
 
@@ -205,26 +200,26 @@ 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)
+        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_studly_identifier_namespace(self, ident):
+        """Given a StudlyCaps string identifier like FooBar, return a
+pair of (namespace, stripped_identifier) 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[0]
+        raise ValueError("Unknown namespace for identifier %r" % (ident, ))
 
     def _strip_namespace_func(self, name):
         prefix = self._namespace.name.lower() + '_'
@@ -263,17 +258,16 @@ context will be used."""
             return self._create_struct(symbol)
         elif stype == CSYMBOL_TYPE_ENUM:
             return self._create_enum(symbol)
-        elif stype == CSYMBOL_TYPE_OBJECT:
-            return self._create_object(symbol)
         elif stype == CSYMBOL_TYPE_MEMBER:
             return self._create_member(symbol)
         elif stype == CSYMBOL_TYPE_UNION:
             return self._create_union(symbol)
-        elif stype == CSYMBOL_TYPE_CONST:
-            return self._create_const(symbol)
+        # FIXME - we should require an annotation on
+        # #defines to have them be constants, really
+        #elif stype == CSYMBOL_TYPE_CONST:
+        #    return self._create_const(symbol)
         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):
@@ -323,74 +317,13 @@ 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 == '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)
+        func = Function(name, return_, parameters, False, symbol.ident)
         func.add_symbol_reference(symbol)
         return func
 
@@ -408,11 +341,10 @@ context will be used."""
         elif source_type.type == CTYPE_POINTER:
             value = self._create_source_type(source_type.base_type) + '*'
         else:
-            value = '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:
@@ -438,20 +370,17 @@ context will be used."""
                 else:
                     derefed_name = canonical_ctype
                 derefed_name = self.resolve_param_type(derefed_name)
-                ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+                ftype = Array(ctype, self.parse_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):
@@ -474,9 +403,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)
@@ -489,22 +418,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
@@ -523,51 +450,46 @@ context will be used."""
         # Preserve "pointerness" of struct/union members
         if (is_member and canonical.endswith('*') and
             derefed_typename in BASIC_GIR_TYPES):
-            return 'any'
+            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) 
+        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
@@ -577,7 +499,7 @@ context will be used."""
             return None
         name = self._strip_namespace_func(symbol.ident)
         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 = 'int'
@@ -594,7 +516,7 @@ 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)
@@ -643,7 +565,7 @@ context will be used."""
         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)
@@ -654,72 +576,45 @@ context will be used."""
 
         # Mark the 'user_data' arguments
         for i, param in enumerate(parameters):
-            if (param.type.name == '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:
+            pointer_stripped = typeval.ctype.replace('*', '')
+            (ns, name) = self._split_studly_identifier_namespace(pointer_stripped)
+            typeval.target_giname = '%s.%s' % (ns.name, name)
+        
     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:
@@ -737,23 +632,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)
@@ -763,8 +641,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/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 497d1ee..015fd83 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -520,7 +520,7 @@ annotation_object_set_data2 (AnnotationObject *object,
 /**
  * annotation_object_set_data3:
  * @object: a #AnnotationObject
- * @data: (array length=length) (element-type uint8): The data
+ * @data: (array length=length) (element-type guint8): The data
  * @length: Length of the data
  *
  * Test taking a gchar * with a length, overriding the array element



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