[gobject-introspection/wip/transformer: 6/7] Major scanner rewrite



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

    Major scanner rewrite

 docs/g-ir-scanner.1             |    3 -
 gir/Makefile.am                 |   13 +-
 gir/gio-2.0.c                   |    2 +-
 gir/glib-2.0.c                  |    2 +-
 giscanner/Makefile.am           |    1 +
 giscanner/annotationparser.py   |  717 +---------------------------
 giscanner/ast.py                |  640 +++++++++++++++---------
 giscanner/finaltransformer.py   |  106 ++++
 giscanner/girparser.py          |  339 +++++++++-----
 giscanner/girwriter.py          |  172 ++++----
 giscanner/glibast.py            |   17 +-
 giscanner/glibtransformer.py    | 1013 +++++----------------------------------
 giscanner/primarytransformer.py |  820 +++++++++++++++++++++++++++++++
 giscanner/scannermain.py        |  129 ++---
 giscanner/sourcescanner.py      |   10 +-
 giscanner/transformer.py        |  506 ++++++++-----------
 giscanner/utils.py              |   13 +
 17 files changed, 2075 insertions(+), 2428 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..f938e98 100644
--- a/gir/Makefile.am
+++ b/gir/Makefile.am
@@ -31,7 +31,7 @@ GLIB_LIBRARY=glib-2.0
 endif
 
 GLib_2_0_gir_LIBS = $(GLIB_LIBRARY)
-GLib_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib.h"
+GLib_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="glib.h"
 GLib_2_0_gir_PACKAGES = glib-2.0
 GLib_2_0_gir_CFLAGS = \
             -I$(GLIB_INCLUDEDIR) \
@@ -61,7 +61,7 @@ endif
 GObject-2.0.gir: GLib-2.0.gir
 
 GObject_2_0_gir_LIBS = $(GOBJECT_LIBRARY)
-GObject_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib-object.h" --add-include-path=.
+GObject_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="glib-object.h" --add-include-path=.
 GObject_2_0_gir_PACKAGES = gobject-2.0
 GObject_2_0_gir_INCLUDES = GLib-2.0
 GObject_2_0_gir_CFLAGS = \
@@ -87,7 +87,7 @@ endif
 GModule-2.0.gir: GLib-2.0.gir
 
 GModule_2_0_gir_LIBS = $(GMODULE_LIBRARY)
-GModule_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="gmodule.h" --add-include-path=.
+GModule_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="gmodule.h" --add-include-path=.
 GModule_2_0_gir_PACKAGES = gmodule-2.0
 GModule_2_0_gir_INCLUDES = GLib-2.0
 GModule_2_0_gir_CFLAGS = \
@@ -118,7 +118,7 @@ endif
 Gio-2.0.gir: GObject-2.0.gir
 
 Gio_2_0_gir_LIBS = $(GIO_LIBRARY)
-Gio_2_0_gir_SCANNERFLAGS = --warn-all --strip-prefix=g --c-include="gio/gio.h" --add-include-path=.
+Gio_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="gio/gio.h" --add-include-path=.
 Gio_2_0_gir_PACKAGES = gio-2.0 $(GIO_UNIX_PACKAGES)
 Gio_2_0_gir_INCLUDES = GObject-2.0
 Gio_2_0_gir_CFLAGS = \
@@ -137,7 +137,6 @@ GIRepository-2.0.gir: GObject-2.0.gir $(top_builddir)/girepository/libgireposito
 
 GIRepository_2_0_gir_LIBS = girepository-1.0
 GIRepository_2_0_gir_SCANNERFLAGS = \
-        --noclosure \
         --strip-prefix=g \
         --c-include="girepository.h" \
         --pkg-export gobject-introspection-1.0 \
@@ -180,7 +179,7 @@ if BUILD_TESTS
 Everything-1.0.gir: Gio-2.0.gir libgirepository-everything-1.0.la
 
 Everything_1_0_gir_LIBS = libgirepository-everything-1.0.la
-Everything_1_0_gir_SCANNERFLAGS = --warn-all --warn-error
+Everything_1_0_gir_SCANNERFLAGS = --warn-all --warn-error --reparse-validate
 Everything_1_0_gir_PACKAGES = gobject-2.0 cairo gio-2.0
 Everything_1_0_gir_INCLUDES = GObject-2.0 cairo-1.0 Gio-2.0
 Everything_1_0_gir_FILES = everything.h everything.c
@@ -210,7 +209,7 @@ GIMarshallingTests_1_0_gir_LIBS = libgirepository-gimarshallingtests-1.0.la
 GIMarshallingTests_1_0_gir_PACKAGES = gobject-2.0
 GIMarshallingTests_1_0_gir_INCLUDES = GObject-2.0
 GIMarshallingTests_1_0_gir_FILES = gimarshallingtests.h gimarshallingtests.c
-GIMarshallingTests_1_0_gir_SCANNERFLAGS = --warn-all --warn-error
+GIMarshallingTests_1_0_gir_SCANNERFLAGS = --warn-all --warn-error  --reparse-validate
 
 INTROSPECTION_GIRS += GIMarshallingTests-$(TYPELIB_VERSION).gir
 EXPECTEDGIRS += GIMarshallingTests-$(TYPELIB_VERSION)-expected.gir
diff --git a/gir/gio-2.0.c b/gir/gio-2.0.c
index d58972a..c03b96f 100644
--- a/gir/gio-2.0.c
+++ b/gir/gio-2.0.c
@@ -83,7 +83,7 @@
 /**
  * g_file_enumerator_next_files_finish:
  *
- * Return value: (transfer full) (element-type FileInfo):
+ * Return value: (transfer full) (element-type GFileInfo):
  */
 
 /**
diff --git a/gir/glib-2.0.c b/gir/glib-2.0.c
index 6203121..6591c64 100644
--- a/gir/glib-2.0.c
+++ b/gir/glib-2.0.c
@@ -6,7 +6,7 @@
 
 /**
  * g_file_set_contents:
- * @contents: (array length=length) (element-type uint8):
+ * @contents: (array length=length) (element-type guint8):
  */
 
 /**
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
index 642bcbc..6a83826 100644
--- a/giscanner/Makefile.am
+++ b/giscanner/Makefile.am
@@ -38,6 +38,7 @@ pkgpyexec_PYTHON = 		\
 	cachestore.py		\
 	config.py		\
 	dumper.py		\
+	finaltransformer.py	\
 	girparser.py		\
 	girwriter.py		\
 	glibast.py		\
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 9e4340f..bbbba78 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -36,7 +36,8 @@ from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
                   PARAM_TRANSFER_NONE,
                   PARAM_TRANSFER_CONTAINER,
                   PARAM_TRANSFER_FULL,
-                  TYPE_ANY, TYPE_NONE)
+                  TYPE_ANY, TYPE_NONE, TYPE_STRING)
+from .transformer import TypeResolutionException
 from .odict import odict
 from .glibast import GLibBoxed
 
@@ -85,10 +86,6 @@ OPT_SCOPE_ASYNC = 'async'
 OPT_SCOPE_CALL = 'call'
 OPT_SCOPE_NOTIFIED = 'notified'
 
-class InvalidAnnotationError(Exception):
-    pass
-
-
 class DocBlock(object):
 
     def __init__(self, name, options):
@@ -153,16 +150,16 @@ class AnnotationParser(object):
     OPTION_RE = re.compile(r'\([A-Za-z]+[^(]*\)')
     RETURNS_RE = re.compile(r'^return(s?)( value)?:', re.IGNORECASE)
 
-    def __init__(self, namespace, source_scanner, transformer):
+    def __init__(self, source_scanner, transformer):
         self._blocks = {}
-        self._namespace = namespace
         self._transformer = transformer
-        for comment in source_scanner.get_comments():
-            self._parse_comment(comment)
+        self._source_scanner = source_scanner
+        self._namespace = transformer.namespace
 
     def parse(self):
-        aa = AnnotationApplier(self._blocks, self._transformer)
-        aa.parse(self._namespace)
+        for comment in self._source_scanner.get_comments():
+            self._parse_comment(comment)
+        return self._blocks
 
     def _parse_comment(self, comment):
         # We're looking for gtk-doc comments here, they look like this:
@@ -311,701 +308,3 @@ class AnnotationParser(object):
                 opened = -1
 
         return options
-
-
-class AnnotationApplier(object):
-
-    def __init__(self, blocks, transformer):
-        self._blocks = blocks
-        self._transformer = transformer
-
-    def _get_tag(self, block, tag_name):
-        if block is None:
-            return None
-
-        return block.get(tag_name)
-
-    def parse(self, namespace):
-        self._namespace = namespace
-        for node in namespace.nodes[:]:
-            self._parse_node(node)
-        del self._namespace
-
-    # Boring parsing boilerplate.
-
-    def _parse_node(self, node):
-        if isinstance(node, Function):
-            self._parse_function(node)
-        elif isinstance(node, Enum):
-            self._parse_enum(node)
-        elif isinstance(node, Bitfield):
-            self._parse_bitfield(node)
-        elif isinstance(node, Class):
-            self._parse_class(node)
-        elif isinstance(node, Interface):
-            self._parse_interface(node)
-        elif isinstance(node, Callback):
-            self._parse_callback(node)
-        elif isinstance(node, Record):
-            self._parse_record(node)
-        elif isinstance(node, Union):
-            self._parse_union(node)
-        elif isinstance(node, GLibBoxed):
-            self._parse_boxed(node)
-
-    def _parse_class(self, class_):
-        block = self._blocks.get(class_.type_name)
-        self._parse_node_common(class_, block)
-        self._parse_constructors(class_.constructors)
-        self._parse_methods(class_, class_.methods)
-        self._parse_vfuncs(class_, class_.virtual_methods)
-        self._parse_methods(class_, class_.static_methods)
-        self._parse_properties(class_, class_.properties)
-        self._parse_signals(class_, class_.signals)
-        self._parse_fields(class_, class_.fields)
-        if block:
-            class_.doc = block.comment
-        self._parse_type_instance_tags(class_, block)
-
-    def _parse_interface(self, interface):
-        block = self._blocks.get(interface.type_name)
-        self._parse_node_common(interface, block)
-        self._parse_methods(interface, interface.methods)
-        self._parse_vfuncs(interface, interface.virtual_methods)
-        self._parse_properties(interface, interface.properties)
-        self._parse_signals(interface, interface.signals)
-        self._parse_fields(interface, interface.fields)
-        if block:
-            interface.doc = block.comment
-
-    def _parse_record(self, record):
-        block = self._blocks.get(record.symbol)
-        self._parse_node_common(record, block)
-        self._parse_constructors(record.constructors)
-        self._parse_methods(record, record.methods)
-        self._parse_fields(record, record.fields, block)
-        if block:
-            record.doc = block.comment
-
-    def _parse_boxed(self, boxed):
-        block = self._blocks.get(boxed.name)
-        self._parse_node_common(boxed, block)
-        self._parse_constructors(boxed.constructors)
-        self._parse_methods(boxed, boxed.methods)
-        if block:
-            boxed.doc = block.comment
-
-    def _parse_union(self, union):
-        block = self._blocks.get(union.name)
-        self._parse_node_common(union, block)
-        self._parse_fields(union, union.fields, block)
-        self._parse_constructors(union.constructors)
-        self._parse_methods(union, union.methods)
-        if block:
-            union.doc = block.comment
-
-    def _parse_enum(self, enum):
-        block = self._blocks.get(enum.symbol)
-        self._parse_node_common(enum, block)
-        if block:
-            enum.doc = block.comment
-            type_opt = block.options.get(OPT_TYPE)
-            if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
-                # This is hack, but hey, it works :-)
-                enum.__class__ = Bitfield
-
-    def _parse_bitfield(self, bitfield):
-        block = self._blocks.get(bitfield.symbol)
-        self._parse_node_common(bitfield, block)
-        if block:
-            bitfield.doc = block.comment
-
-    def _parse_constructors(self, constructors):
-        for ctor in constructors:
-            self._parse_function(ctor)
-
-    def _parse_fields(self, parent, fields, block=None):
-        for field in fields:
-            self._parse_field(parent, field, block)
-
-    def _parse_properties(self, parent, properties):
-        for prop in properties:
-            self._parse_property(parent, prop)
-
-    def _parse_methods(self, parent, methods):
-        for method in methods:
-            self._parse_method(parent, method)
-
-    def _parse_vfuncs(self, parent, vfuncs):
-        for vfunc in vfuncs:
-            self._parse_vfunc(parent, vfunc)
-
-    def _parse_signals(self, parent, signals):
-        for signal in signals:
-            self._parse_signal(parent, signal)
-
-    def _parse_property(self, parent, prop):
-        block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
-        self._parse_node_common(prop, block)
-        if block:
-            prop.doc = block.comment
-
-        transfer_tag = self._get_tag(block, TAG_TRANSFER)
-        if transfer_tag is not None:
-            options = {OPT_TRANSFER: Option(transfer_tag.value)}
-        else:
-            options = {}
-        prop.transfer = self._extract_transfer(parent, prop, options)
-
-        type_tag = self._get_tag(block, TAG_TYPE)
-        if type_tag:
-            prop.type = self._resolve(type_tag.value, prop.type)
-
-    def _parse_callback(self, callback):
-        block = self._blocks.get(callback.ctype)
-        self._parse_node_common(callback, block)
-        self._parse_params(callback, callback.parameters, block)
-        self._parse_return(callback, callback.retval, block)
-        if block:
-            callback.doc = block.comment
-
-    def _parse_callable(self, callable, block):
-        self._parse_node_common(callable, block)
-        for i, param in enumerate(callable.parameters):
-            if (param.type.ctype != 'GDestroyNotify' and
-                param.type.name != 'GLib.DestroyNotify'):
-                continue
-            if i < 2:
-                break
-            callback_param = callable.parameters[i-2]
-            if callback_param.closure_index != -1:
-                callback_param.scope = OPT_SCOPE_NOTIFIED
-                callback_param.transfer = PARAM_TRANSFER_NONE
-
-        self._parse_params(callable, callable.parameters, block)
-        self._parse_return(callable, callable.retval, block)
-        if block:
-            callable.doc = block.comment
-
-    def _parse_function(self, func):
-        block = self._blocks.get(func.symbol)
-        self._parse_callable(func, block)
-        self._parse_rename_to_func(func, block)
-
-    def _parse_signal(self, parent, signal):
-        block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
-        self._parse_node_common(signal, block)
-        # We're only attempting to name the signal parameters if
-        # the number of parameter tags (@foo) is the same or greater
-        # than the number of signal parameters
-        if block and len(block.tags) > len(signal.parameters):
-            names = block.tags.items()
-        else:
-            names = []
-        for i, param in enumerate(signal.parameters):
-            if names:
-                name, tag = names[i+1]
-                param.name = name
-                options = getattr(tag, 'options', {})
-                param_type = options.get(OPT_TYPE)
-                if param_type:
-                    param.type = self._resolve(param_type.one(), param.type)
-            else:
-                tag = None
-            self._parse_param(signal, param, tag)
-        self._parse_return(signal, signal.retval, block)
-        if block:
-            signal.doc = block.comment
-
-    def _parse_method(self, parent, meth):
-        block = self._blocks.get(meth.symbol)
-        self._parse_function(meth)
-        virtual = self._get_tag(block, TAG_VFUNC)
-        if virtual:
-            invoker_name = virtual.value
-            matched = False
-            for vfunc in parent.virtual_methods:
-                if vfunc.name == invoker_name:
-                    matched = True
-                    vfunc.invoker = meth
-                    break
-            if not matched:
-                print "warning: unmatched virtual invoker %r for method %r" % \
-                    (invoker_name, meth.symbol)
-
-    def _parse_vfunc(self, parent, vfunc):
-        key = '%s::%s' % (parent.type_name, vfunc.name)
-        self._parse_callable(vfunc, self._blocks.get(key))
-        if vfunc.invoker:
-            # We normally expect annotations like (element-type) to be
-            # applied to the invoker.
-            block = self._blocks.get(vfunc.invoker.symbol)
-            self._parse_callable(vfunc, block)
-
-    def _parse_field(self, parent, field, block=None):
-        if isinstance(field, Callback):
-            self._parse_callback(field)
-        else:
-            if not block:
-                return
-            tag = block.get(field.name)
-            if not tag:
-                return
-            t = tag.options.get('type')
-            if not t:
-                return
-            field.type.name = self._transformer.resolve_type_name(t.one())
-
-    def _check_arg_annotations(self, parent, params, block):
-        if block is None:
-            return
-        for tag in block.tags.keys():
-            if tag == TAG_RETURNS:
-                continue
-            for param in params:
-                if param.name == tag:
-                    break
-            else:
-                return
-                #print 'WARNING: annotation for "%s" refers to unknown ' \
-                #        'argument "%s"' % (parent.name, tag)
-
-    def _parse_params(self, parent, params, block):
-        self._check_arg_annotations(parent, params, block)
-        for param in params:
-            tag = self._get_tag(block, param.name)
-            self._parse_param(parent, param, tag)
-
-    def _parse_return(self, parent, return_, block):
-        tag = self._get_tag(block, TAG_RETURNS)
-        self._parse_param_ret_common(parent, return_, tag)
-
-    def _get_parameter_index(self, parent, param_name, location_name):
-        index = parent.get_parameter_index(param_name)
-        if index is None:
-            raise InvalidAnnotationError(
-                "can't find parameter %s referenced by parameter %s of %r"
-                % (param_name, location_name, parent.name))
-
-        return index
-
-    def _parse_param(self, parent, param, tag):
-        options = getattr(tag, 'options', {})
-        if isinstance(parent, Function):
-            scope = options.get(OPT_SCOPE)
-            if scope:
-                param.scope = scope.one()
-                if param.scope not in [PARAM_SCOPE_CALL,
-                                       PARAM_SCOPE_ASYNC,
-                                       PARAM_SCOPE_NOTIFIED]:
-                    raise InvalidAnnotationError(
-                        "scope for %s of %r is invalid (%r), must be one of "
-                        "call, async, notified."
-                        % (param.name, parent.name, param.scope))
-                param.transfer = PARAM_TRANSFER_NONE
-            elif (param.type.ctype == 'GAsyncReadyCallback' or
-                  param.type.name == 'Gio.AsyncReadyCallback'):
-                param.scope = OPT_SCOPE_ASYNC
-                param.transfer = PARAM_TRANSFER_NONE
-
-            destroy = options.get(OPT_DESTROY)
-            if destroy:
-                param.destroy_index = self._get_parameter_index(parent,
-                                                                destroy.one(),
-                                                                param.name)
-                self._fixup_param_destroy(parent, param)
-            closure = options.get(OPT_CLOSURE)
-            if closure:
-                param.closure_index = self._get_parameter_index(parent,
-                                                                closure.one(),
-                                                                param.name)
-                self._fixup_param_closure(parent, param)
-        if isinstance(parent, Callback):
-            if OPT_CLOSURE in options:
-                # For callbacks, (closure) appears without an
-                # argument, and tags a parameter that is a closure. We
-                # represent it (weirdly) in the gir and typelib by
-                # setting param.closure_index to its own index.
-                param.closure_index = parent.get_parameter_index(param.name)
-                self._fixup_param_closure(parent, param)
-
-        self._parse_param_ret_common(parent, param, tag)
-
-    def _fixup_param_destroy(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.destroy_index == param.destroy_index:
-                p.destroy_index = -1
-
-    def _fixup_param_closure(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.closure_index == param.closure_index:
-                p.closure_index = -1
-
-    def _parse_param_ret_common(self, parent, node, tag):
-        options = getattr(tag, 'options', {})
-        (node.direction, node.caller_allocates) = \
-            self._extract_direction(node, options)
-        container_type = self._extract_container_type(
-            parent, node, options)
-        if container_type is not None:
-            node.type = container_type
-        if node.direction is None:
-            node.direction = self._guess_direction(node)
-            node.caller_allocates = False
-        node.transfer = self._extract_transfer(parent, node, options)
-        param_type = options.get(OPT_TYPE)
-        if param_type:
-            node.type = self._resolve(param_type.one(), node.type)
-
-        if (OPT_ALLOW_NONE in options or
-            node.type.ctype == 'GCancellable*'):
-            node.allow_none = True
-
-        assert node.transfer is not None
-        if tag is not None and tag.comment is not None:
-            node.doc = tag.comment
-
-        for key in options:
-            if '.' in key:
-                value = options.get(key)
-                if value:
-                    node.attributes.append((key, value.one()))
-
-    def _extract_direction(self, node, options):
-        caller_allocates = False
-        if (OPT_INOUT in options or
-            OPT_INOUT_ALT in options):
-            direction = PARAM_DIRECTION_INOUT
-        elif OPT_OUT in options:
-            subtype = options[OPT_OUT]
-            if subtype is not None:
-                subtype = subtype.one()
-            direction = PARAM_DIRECTION_OUT
-            if subtype in (None, ''):
-                if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
-                    caller_allocates = '**' not in node.type.ctype
-                else:
-                    caller_allocates = False
-            elif subtype == 'caller-allocates':
-                caller_allocates = True
-            elif subtype == 'callee-allocates':
-                caller_allocates = False
-            else:
-                raise InvalidAnnotationError(
-                    "out direction for %s is invalid (%r)" % (node, subtype))
-        elif OPT_IN in options:
-            direction = PARAM_DIRECTION_IN
-        else:
-            direction = node.direction
-        return (direction, caller_allocates)
-
-    def _guess_array(self, node):
-        ctype = node.type.ctype
-        if ctype is None:
-            return False
-        if not ctype.endswith('*'):
-            return False
-        if node.type.canonical in default_array_types:
-            return True
-        return False
-
-    def _is_array_type(self, node):
-        if node.type.name in ['GLib.Array', 'GLib.PtrArray',
-                              'GLib.ByteArray']:
-            return True
-        if node.type.ctype in ['GArray*', 'GPtrArray*', 'GByteArray*']:
-            return True
-        return False
-
-    def _extract_container_type(self, parent, node, options):
-        has_element_type = OPT_ELEMENT_TYPE in options
-        has_array = OPT_ARRAY in options
-
-        if not has_array:
-            has_array = self._is_array_type(node)
-
-        # FIXME: This is a hack :-(
-        if (not isinstance(node, Field) and
-            (not has_element_type and
-             (node.direction is None
-              or isinstance(node, Return)
-              or node.direction == PARAM_DIRECTION_IN))):
-            if self._guess_array(node):
-                has_array = True
-
-        if has_array:
-            container_type = self._parse_array(parent, node, options)
-        elif has_element_type:
-            container_type = self._parse_element_type(parent, node, options)
-        else:
-            container_type = None
-
-        return container_type
-
-    def _parse_array(self, parent, node, options):
-        array_opt = options.get(OPT_ARRAY)
-        if array_opt:
-            array_values = array_opt.all()
-        else:
-            array_values = {}
-
-        is_g_array = self._is_array_type(node)
-
-        element_type = options.get(OPT_ELEMENT_TYPE)
-        if element_type is not None:
-            element_type_node = self._resolve(element_type.one())
-        else:
-            if is_g_array:
-                element_type_node = None
-            else:
-                element_type_node = Type(node.type.name) # erase ctype
-
-        if is_g_array:
-            type_name = node.type.name
-        else:
-            type_name = None
-
-        container_type = Array(type_name, node.type.ctype,
-                               element_type_node)
-        container_type.is_const = node.type.is_const
-        if OPT_ARRAY_ZERO_TERMINATED in array_values:
-            container_type.zeroterminated = array_values.get(
-                OPT_ARRAY_ZERO_TERMINATED) == '1'
-        length = array_values.get(OPT_ARRAY_LENGTH)
-        if length is not None:
-            param_index = self._get_parameter_index(parent, length, node.name)
-            container_type.length_param_index = param_index
-            # For in parameters we're incorrectly deferring
-            # char/unsigned char to utf8 when a length annotation
-            # is specified.
-            if (isinstance(node, Parameter) and
-                node.type.name == 'utf8' and
-                self._guess_direction(node) == PARAM_DIRECTION_IN and
-                element_type is None):
-                # FIXME: unsigned char/guchar should be uint8
-                container_type.element_type = Type('gint8')
-        container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
-        return container_type
-
-    def _resolve(self, type_str, orig_node=None):
-        def grab_one(type_str, resolver, top_combiner, combiner):
-            """Return a complete type, and the trailing string part after it.
-            Use resolver() on each identifier, and combiner() on the parts of
-            each complete type. (top_combiner is used on the top-most type.)"""
-            bits = re.split(r'([,<>])', type_str, 1)
-            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
-            args = [resolver(first)]
-            if sep == '<':
-                while sep != '>':
-                    next, rest = grab_one(rest, resolver, combiner, combiner)
-                    args.append(next)
-                    sep, rest = rest[0], rest[1:]
-            else:
-                rest = sep + rest
-            return top_combiner(*args), rest
-        def resolver(ident):
-            return self._transformer.resolve_param_type(Type(ident))
-        def combiner(base, *rest):
-            if not rest:
-                return base
-            if (base.name in ['GLib.List', 'GLib.SList'] or
-                base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
-                return List(base.name, base.ctype, *rest)
-            if (base.name in ['GLib.HashTable'] or
-                base.ctype in ['GHashTable*']) and len(rest)==2:
-                return Map(base.name, base.ctype, *rest)
-            print "WARNING: throwing away type parameters:", type_str
-            return base
-        def top_combiner(base, *rest):
-            """For the top most type, recycle orig_node if possible."""
-            if orig_node is not None:
-                orig_node.name = base.name
-                base = orig_node # preserve other properties of orig_node
-            return combiner(base, *rest)
-
-        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
-        if rest:
-            print "WARNING: throwing away trailing part of type:", type_str
-        return result
-
-    def _parse_element_type(self, parent, node, options):
-        element_type_opt = options.get(OPT_ELEMENT_TYPE)
-        element_type = element_type_opt.flat()
-        if (node.type.name in ['GLib.List', 'GLib.SList'] or
-            node.type.ctype in ['GList*', 'GSList*']):
-            assert len(element_type) == 1
-            container_type = List(
-                node.type.name,
-                node.type.ctype,
-                self._resolve(element_type[0]))
-        elif (node.type.name in ['GLib.HashTable'] or
-              node.type.ctype in ['GHashTable*']):
-            assert len(element_type) == 2
-            container_type = Map(
-                node.type.name,
-                node.type.ctype,
-                self._resolve(element_type[0]),
-                self._resolve(element_type[1]))
-        elif self._is_array_type(node):
-            container_type = Array(node.type.name,
-                                   node.type.ctype,
-                                   self._resolve(element_type[0]))
-        else:
-            print 'FIXME: unhandled element-type container:', node
-        return container_type
-
-    def _extract_transfer(self, parent, node, options):
-        transfer_opt = options.get(OPT_TRANSFER)
-        if transfer_opt is None:
-            transfer = self._guess_transfer(node, options)
-        else:
-            transfer = transfer_opt.one()
-            if transfer is None:
-                transfer = PARAM_TRANSFER_FULL
-            if transfer not in [PARAM_TRANSFER_NONE,
-                                PARAM_TRANSFER_CONTAINER,
-                                PARAM_TRANSFER_FULL]:
-                raise InvalidAnnotationError(
-                    "transfer for %s of %r is invalid (%r), must be one of "
-                    "none, container, full." % (node, parent.name, transfer))
-        return transfer
-
-    def _parse_node_common(self, node, block):
-        self._parse_version(node, block)
-        self._parse_deprecated(node, block)
-        self._parse_attributes(node, block)
-        self._parse_skip(node, block)
-        self._parse_foreign(node, block)
-
-    def _parse_version(self, node, block):
-        since_tag = self._get_tag(block, TAG_SINCE)
-        if since_tag is None:
-            return
-        node.version = since_tag.value
-
-    def _parse_deprecated(self, node, block):
-        deprecated_tag = self._get_tag(block, TAG_DEPRECATED)
-        if deprecated_tag is None:
-            return
-        value = deprecated_tag.value
-        if ': ' in value:
-            version, desc = value.split(': ')
-        else:
-            desc = value
-            version = None
-        node.deprecated = desc
-        if version is not None:
-            node.deprecated_version = version
-
-    def _parse_attributes(self, node, block):
-        annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
-        if annos_tag is None:
-            return
-        options = AnnotationParser.parse_options(annos_tag.value)
-        for key, value in options.iteritems():
-            if value:
-                node.attributes.append((key, value.one()))
-
-    def _parse_skip(self, node, block):
-        if block is not None:
-            if OPT_SKIP in block.options:
-                node.skip = True
-
-    def _parse_foreign(self, node, block):
-        if block is not None:
-            if OPT_FOREIGN in block.options:
-                node.foreign = True
-
-    def _parse_type_instance_tags(self, node, block):
-        tag = self._get_tag(block, TAG_UNREF_FUNC)
-        node.unref_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_REF_FUNC)
-        node.ref_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_SET_VALUE_FUNC)
-        node.set_value_func = tag.value if tag else None
-        tag = self._get_tag(block, TAG_GET_VALUE_FUNC)
-        node.get_value_func = tag.value if tag else None
-
-    def _parse_rename_to_func(self, node, block):
-        rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
-        if rename_to_tag is None:
-            return
-        new_name = rename_to_tag.value
-
-        shadowed = []
-
-        def shadowed_filter(n):
-            if isinstance(n, Function) and n.symbol == new_name:
-                shadowed.append(n)
-                return False
-            return True
-
-        self._namespace.remove_matching(shadowed_filter)
-        if len(shadowed) == 1:
-            # method override; use the same (stripped) name as the overloaded
-            # method referenced.
-            # Note that 'g_timeout_add_full' may specify a new_name of
-            # 'g_timeout_add' but the *real* name desired is the stripped name
-            # of 'g_timeout_add' which is 'timeout_add' (for example).
-            node.name = shadowed[0].name
-        elif len(shadowed) == 0:
-            # literal rename, to force a particular prefix strip or whatever
-            # Example: the "nm-utils" package uses a "NM" prefix in most places
-            # but some functions have an "nm_utils_" prefix; the 'Rename To:'
-            # annotation in this case is used to strip the 'utils_' part off.
-            node.name = new_name
-        else:
-            assert False # more than two shadowed methods?  Shouldn't happen.
-
-    def _guess_direction(self, node):
-        if node.direction:
-            return node.direction
-        is_pointer = False
-        if node.type.ctype:
-            is_pointer = '*' in node.type.ctype
-
-        if is_pointer and node.type.name in BASIC_GIR_TYPES:
-            return PARAM_DIRECTION_OUT
-
-        return PARAM_DIRECTION_IN
-
-    def _guess_transfer(self, node, options):
-        if node.transfer is not None:
-            return node.transfer
-
-        # Anything with 'const' gets none
-        if node.type.is_const:
-            return PARAM_TRANSFER_NONE
-        elif node.type.name in [TYPE_NONE, TYPE_ANY]:
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node.type, Varargs):
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node, Parameter):
-            if node.direction in [PARAM_DIRECTION_INOUT,
-                                  PARAM_DIRECTION_OUT]:
-                if node.caller_allocates:
-                    return PARAM_TRANSFER_NONE
-                return PARAM_TRANSFER_FULL
-            # This one is a hack for compatibility; the transfer
-            # for string parameters really has no defined meaning.
-            elif node.type.canonical == 'utf8':
-                return PARAM_TRANSFER_FULL
-            else:
-                return PARAM_TRANSFER_NONE
-        elif isinstance(node, Return):
-            if (isinstance(node.type, Array) and
-                    node.type.element_type is not None and
-                    node.type.element_type.name == 'utf8'):
-                return PARAM_TRANSFER_FULL
-            elif (node.type.canonical in BASIC_GIR_TYPES or
-                (node.type.canonical in [TYPE_NONE, TYPE_ANY] and
-                 node.type.is_const)):
-                return PARAM_TRANSFER_NONE
-            else:
-                return PARAM_TRANSFER_FULL
-        elif isinstance(node, Field):
-            return PARAM_TRANSFER_NONE
-        elif isinstance(node, Property):
-            return PARAM_TRANSFER_NONE
-        else:
-            raise AssertionError(node)
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 4008b66..901667c 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,63 +19,149 @@
 # Boston, MA 02111-1307, USA.
 #
 
-"""AST nodes
-This file descbribes abstract data type nodes independent on the
-implementation language.
-
-These can later on be extended (eg subclassed) with additional information
-which is language/library/domain specific.
+from .odict import odict
+from .utils import to_underscores
+
+class Type(object):
+    """A Type can be either:
+* A reference to a node (target_giname)
+* A reference to a "fundamental" type like 'utf8'
+* A "foreign" type - this can be any string."
+If none are specified, then it's in an "unresolved" state.
+In this case, the ctype must be specified.
 """
 
+    resolved = property(lambda self: (self.target_fundamental or
+                                      self.target_giname or
+                                      self.target_foreign))
+
+    def __init__(self, 
+                 ctype=None, 
+                 target_fundamental=None,
+                 target_giname=None,
+                 target_foreign=None,
+                 _target_unknown=False,
+                 is_const=False,
+                 origin_symbol=None):
+        self.ctype = ctype
+        self.origin_symbol = origin_symbol
+        if _target_unknown:
+            assert isinstance(self, TypeUnknown)
+        elif target_fundamental:
+            assert target_giname is None
+            assert target_foreign is None
+        elif target_giname:
+            assert target_fundamental is None
+            assert target_foreign is None
+        elif target_foreign:
+            assert ctype is not None
+            assert target_giname is None
+            assert target_fundamental is None
+        else:
+            assert ctype is not None
+        self.target_fundamental = target_fundamental
+        self.target_giname = target_giname
+        self.target_foreign = target_foreign
+        self.is_const = is_const
+
+    def get_giname(self):
+        assert self.target_giname is not None
+        return self.target_giname.split('.')[1]
+
+    def __cmp__(self, other):
+        if self.target_fundamental:
+            return cmp(self.target_fundamental, other.target_fundamental)
+        if self.target_giname:
+            return cmp(self.target_giname, other.target_giname)
+        if self.target_foreign:
+            return cmp(self.target_foreign, other.target_foreign)
+        return cmp(self.ctype, other.ctype)
+
+    def is_equiv(self, typeval):
+        """Return True if the specified types are compatible at
+        an introspection level, disregarding their C types.
+        A sequence may be given for typeval, in which case
+        this function returns True if the type is compatible with
+        any."""
+        if isinstance(typeval, (list, tuple)):
+            for val in typeval:
+                if self.is_equiv(val):
+                    return True
+            return False
+        return self == typeval
+
+    def clone(self):
+        return Type(target_fundamental=self.target_fundamental,
+                    target_giname=self.target_giname,
+                    target_foreign=self.target_foreign,
+                    ctype=self.ctype,
+                    is_const=self.is_const)
+
+    def __str__(self):
+        if self.target_fundamental:
+            return 'Type(target_fundamental=%s)' % (self.target_fundamental, )
+        elif self.target_giname:
+            return 'Type(target_giname=%s)' % (self.target_giname, )
+        elif self.target_foreign:
+            return 'Type(target_foreign=%s)' % (self.target_foreign, )
+
+class TypeUnknown(Type):
+    def __init__(self):
+        Type.__init__(self, _target_unknown=True)
+
 ######
 ## Fundamental types
 ######
 # Two special ones
-TYPE_NONE = 'none'
-TYPE_ANY = 'gpointer'
+TYPE_NONE = Type(target_fundamental='void', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
 # "Basic" types
-TYPE_BOOLEAN = 'gboolean'
-TYPE_INT8 = 'gint8'
-TYPE_UINT8 = 'guint8'
-TYPE_INT16 = 'gint16'
-TYPE_UINT16 = 'guint16'
-TYPE_INT32 = 'gint32'
-TYPE_UINT32 = 'guint32'
-TYPE_INT64 = 'gint64'
-TYPE_UINT64 = 'guint64'
-TYPE_CHAR = 'gchar'
-TYPE_SHORT = 'gshort'
-TYPE_USHORT = 'gushort'
-TYPE_INT = 'gint'
-TYPE_UINT = 'guint'
-TYPE_LONG = 'glong'
-TYPE_ULONG = 'gulong'
+TYPE_BOOLEAN = Type(target_fundamental='gboolean', ctype='gboolean')
+TYPE_INT8 = Type(target_fundamental='gint8', ctype='gint8')
+TYPE_UINT8 = Type(target_fundamental='guint8', ctype='guint8')
+TYPE_INT16 = Type(target_fundamental='gint16', ctype='gint16')
+TYPE_UINT16 = Type(target_fundamental='guint16', ctype='guint16')
+TYPE_INT32 = Type(target_fundamental='gint32', ctype='gint32')
+TYPE_UINT32 = Type(target_fundamental='guint32', ctype='guint32')
+TYPE_INT64 = Type(target_fundamental='gint64', ctype='gint64')
+TYPE_UINT64 = Type(target_fundamental='guint64', ctype='guint64')
+TYPE_CHAR = Type(target_fundamental='gchar', ctype='gchar')
+TYPE_SHORT = Type(target_fundamental='gshort', ctype='gshort')
+TYPE_USHORT = Type(target_fundamental='gushort', ctype='gushort')
+TYPE_INT = Type(target_fundamental='gint', ctype='gint')
+TYPE_UINT = Type(target_fundamental='guint', ctype='guint')
+TYPE_LONG = Type(target_fundamental='glong', ctype='glong')
+TYPE_ULONG = Type(target_fundamental='gulong', ctype='gulong')
 # C99 types
-TYPE_LONG_LONG = 'long long'
-TYPE_LONG_ULONG = 'unsigned long long'
-TYPE_FLOAT = 'gfloat'
-TYPE_DOUBLE = 'gdouble'
+TYPE_LONG_LONG = Type(target_fundamental='long long', ctype='long long')
+TYPE_LONG_ULONG = Type(target_fundamental='unsigned long long', ctype='unsigned long long')
+TYPE_FLOAT = Type(target_fundamental='gfloat', ctype='gfloat')
+TYPE_DOUBLE = Type(target_fundamental='gdouble', ctype='gdouble')
 # ?
-TYPE_LONG_DOUBLE = 'long double'
+TYPE_LONG_DOUBLE = Type(target_fundamental='long double', ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
 
 # C types with semantics overlaid
-TYPE_GTYPE = 'GType'
-TYPE_STRING = 'utf8'
-TYPE_FILENAME = 'filename'
+TYPE_GTYPE = Type(target_fundamental='GType', ctype='GType')
+TYPE_STRING = Type(target_fundamental='utf8', ctype='gchar*')
+TYPE_FILENAME = Type(target_fundamental='filename', ctype='gchar*')
 
 BASIC_GIR_TYPES = [TYPE_BOOLEAN, TYPE_INT8, TYPE_UINT8, TYPE_INT16,
                    TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64,
                    TYPE_UINT64, TYPE_CHAR, TYPE_SHORT, TYPE_USHORT, TYPE_INT,
                    TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_LONG_LONG,
-                   TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
-                   TYPE_LONG_DOUBLE, TYPE_GTYPE]
+                   TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE, 
+                   TYPE_LONG_DOUBLE, TYPE_UNICHAR, TYPE_GTYPE]
 GIR_TYPES = [TYPE_NONE, TYPE_ANY]
 GIR_TYPES.extend(BASIC_GIR_TYPES)
 GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
 
 type_names = {}
 for typeval in GIR_TYPES:
-    type_names[typeval] = typeval
+    type_names[typeval.target_fundamental] = typeval
+basic_type_names = {}
+for typeval in BASIC_GIR_TYPES:
+    basic_type_names[typeval.target_fundamental] = typeval
 
 # C builtin
 type_names['char'] = TYPE_CHAR
@@ -143,11 +229,10 @@ type_names['id'] = TYPE_ANY
 
 # These types, when seen by reference, are converted into an Array()
 # by default
+# If you add/change these, be sure to update glibast.py too
 default_array_types = {}
 default_array_types['guint8*'] = TYPE_UINT8
 default_array_types['guchar*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
-default_array_types['char**'] = TYPE_STRING
 default_array_types['gchar**'] = TYPE_STRING
 
 # These types, when seen by reference, are interpreted as out parameters
@@ -170,24 +255,167 @@ PARAM_TRANSFER_NONE = 'none'
 PARAM_TRANSFER_CONTAINER = 'container'
 PARAM_TRANSFER_FULL = 'full'
 
-def type_name_from_ctype(ctype):
-    return type_names.get(ctype, ctype)
-
+class Namespace(object):
+    names = property(lambda self: self._names)
+    aliases = property(lambda self: self._aliases)
+    type_names = property(lambda self: self._type_names)
+    ctypes = property(lambda self: self._ctypes)
 
-class Node(object):
+    def __init__(self, name, version, c_prefix=None):
+        self.name = name
+        self.version = version
+        self.c_prefix = c_prefix or name
+        self._lower_c_prefix = self.c_prefix.lower()
+        self.uscore_prefix = to_underscores(self.c_prefix).lower()
+        self._names = odict() # Maps from GIName -> node
+        self._aliases = {} # Maps from GIName -> GIName
+        self._type_names = {} # Maps from GTName -> node
+        self._ctypes = {} # Maps from CType -> node
+
+    def type_from_name(self, name, ctype=None):
+        """Backwards compatibility method for older .gir files, which
+only use the 'name' attribute.  If name refers to a fundamental type,
+create a Type object referncing it.  If name is already a
+fully-qualified GIName like 'Foo.Bar', returns a Type targeting it .
+Otherwise a Type targeting name qualififed with the namespace name is
+returned."""
+        if name in type_names:
+            return Type(target_fundamental=name, ctype=ctype)
+        if '.' in name:
+            target = name
+        else:
+            target = '%s.%s' % (self.name, name)
+        return Type(target_giname=target, ctype=ctype)
+
+    def contains_ident(self, ident):
+        """Return True if this namespace should contain the given C
+identifier string."""
+        if not self.c_prefix:
+            return False
+        return (ident.startswith(self.c_prefix)
+                or ident.startswith(self._lower_c_prefix))
+
+    def append(self, node, replace=False):
+        previous = self._names.get(node.name)
+        if previous is not None:
+            if not replace:
+                raise ValueError("Namespace conflict")
+            self.remove(previous)
+        # A layering violation...but oh well.
+        from .glibast import GLibBoxed
+        if isinstance(node, Alias):
+            self._aliases[node.name] = node
+        elif isinstance(node, (GLibBoxed, Interface, Class)):
+            self._type_names[node.type_name] = node
+        assert isinstance(node, Node)
+        assert node.namespace is None
+        node.namespace = self
+        self._names[node.name] = node
+        if hasattr(node, 'ctype'):
+            self._ctypes[node.ctype] = node
+        elif hasattr(node, 'symbol'):
+            self._ctypes[node.symbol] = node
+
+    def remove(self, node):
+        from .glibast import GLibBoxed
+        if isinstance(node, Alias):
+            del self._aliases[node.name]
+        elif isinstance(node, (GLibBoxed, Interface, Class)):
+            del self._type_names[node.type_name]
+        del self._names[node.name]
+        node.namespace = None
+        if hasattr(node, 'ctype'):
+            del self._ctypes[node.ctype]
+        if hasattr(node, 'symbol'):
+            del self._ctypes[node.symbol]
+
+    def float(self, node):
+        """Like remove(), but doesn't unset the node's namespace
+back-reference."""
+        self.remove(node)
+        node.namespace = self
+
+    def __iter__(self):
+        return iter(self._names)
+
+    def iteritems(self):
+        return self._names.iteritems()
+
+    def itervalues(self):
+        return self._names.itervalues()
+
+    def get(self, name):
+        return self._names.get(name)
+
+    def walk(self, callback):
+        for node in self.itervalues():
+            node.walk(callback, [])
+
+    def iter_recursive(self):
+        """Recursively iterate over all Node instances."""
+        def doyield(node, chain):
+            yield node
+        self.walk(test)
+
+class Include(object):
 
-    def __init__(self, name=None):
+    def __init__(self, name, version):
         self.name = name
+        self.version = version
+
+    @classmethod
+    def from_string(self, string):
+        return Include(*string.split('-', 1))
+
+    def __cmp__(self, other):
+        if not isinstance(other, Include):
+            return cmp(self, other)
+        namecmp = cmp(self.name, other.name)
+        if namecmp != 0:
+            return namecmp
+        return cmp(self.version, other.version)
+
+    def __hash__(self):
+        return hash((self.name, self.version))
+
+    def __str__(self):
+        return '%s-%s' % (self.name, self.version)
+
+class Annotated(object):
+    """An object which has a few generic metadata
+properties."""
+    def __init__(self):
+        self.version = None
         self.skip = False
         self.introspectable = True
         self.attributes = [] # (key, value)*
         self.deprecated = None
         self.deprecated_version = None
-        self.version = None
+        self.doc = None
+
+class Node(Annotated):
+    """A node is a type of object which is uniquely identified by its
+(namespace, name) pair.  When combined with a ., this is called a
+GIName.  It's possible for nodes to contain or point to other nodes."""
+
+    c_name = property(lambda self: self.namespace.name + self.name)
+
+    def __init__(self, name=None):
+        Annotated.__init__(self)
+        self.namespace = None # Should be set later by Namespace.append()
+        self.name = name
         self.foreign = False
         self.file_positions = set()
 
+    def create_type(self):
+        """Create a Type object referencing this node."""
+        assert self.namespace is not None
+        return Type(target_giname=('%s.%s' % (self.namespace.name, self.name)))
+
     def __cmp__(self, other):
+        nscmp = cmp(self.namespace, other.namespace)
+        if nscmp != 0:
+            return nscmp
         return cmp(self.name, other.name)
 
     def __repr__(self):
@@ -206,49 +434,15 @@ class Node(object):
         if symbol.source_filename:
             self.add_file_position(symbol.source_filename, symbol.line, -1)
 
-class Namespace(Node):
+    def walk(self, callback, chain):
+        if not callback(self, chain):
+            return False
+        chain.append(self)
+        self._walk(callback, chain)
+        chain.pop()
 
-    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)
+    def _walk(self, callback, chain):
+        pass
 
 class Callable(Node):
 
@@ -257,29 +451,33 @@ class Callable(Node):
         self.retval = retval
         self.parameters = parameters
         self.throws = not not throws
-        self.doc = None
 
-    def __repr__(self):
-        return '%s(%r, %r, %r)' % (self.__class__.__name__,
-                                   self.name, self.retval,
-                                   self.parameters)
+    def get_parameter_index(self, name):
+        for i, parameter in enumerate(self.parameters):
+            if parameter.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
 
 
@@ -296,59 +494,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):
 
@@ -357,42 +552,43 @@ class Alias(Node):
         self.target = target
         self.ctype = ctype
 
-    def __repr__(self):
-        return 'Alias(%r, %r)' % (self.name, self.target)
-
 
-class TypeContainer(Node):
+class TypeContainer(Annotated):
+    """A fundamental base class for Return and Parameter."""
 
-    def __init__(self, name, typenode, transfer):
-        Node.__init__(self, name)
+    def __init__(self, typenode, transfer):
+        Annotated.__init__(self)
         self.type = typenode
-        if transfer in [PARAM_TRANSFER_NONE, PARAM_TRANSFER_CONTAINER,
-                        PARAM_TRANSFER_FULL]:
+        if transfer is not None:
             self.transfer = transfer
+        elif typenode.is_const:
+            self.transfer = PARAM_TRANSFER_NONE
         else:
             self.transfer = None
 
 
 class Parameter(TypeContainer):
-
-    def __init__(self, name, typenode, direction=None,
-                 transfer=None, allow_none=False, scope=None):
-        TypeContainer.__init__(self, name, typenode, transfer)
-        if direction in [PARAM_DIRECTION_IN, PARAM_DIRECTION_OUT,
-                         PARAM_DIRECTION_INOUT, None]:
-            self.direction = direction
-        else:
-            self.direction = PARAM_DIRECTION_IN
-
-        self.caller_allocates = False
+    """An argument to a function."""
+
+    def __init__(self, argname, typenode, direction=None,
+                 transfer=None, allow_none=False, scope=None,
+                 caller_allocates=False):
+        TypeContainer.__init__(self, typenode, transfer)
+        self.argname = argname
+        self.direction = direction
         self.allow_none = allow_none
         self.scope = scope
+        self.caller_allocates = caller_allocates
         self.closure_index = -1
         self.destroy_index = -1
-        self.doc = None
 
-    def __repr__(self):
-        return 'Parameter(%r, %r)' % (self.name, self.type)
+
+class Return(TypeContainer):
+    """A return value from a function."""
+
+    def __init__(self, rtype, transfer=None):
+        TypeContainer.__init__(self, rtype, transfer)
+        self.direction = PARAM_DIRECTION_OUT
 
 
 class Enum(Node):
@@ -401,10 +597,6 @@ class Enum(Node):
         Node.__init__(self, name)
         self.symbol = symbol
         self.members = members
-        self.doc = None
-
-    def __repr__(self):
-        return 'Enum(%r, %r)' % (self.name, self.members)
 
 
 class Bitfield(Node):
@@ -413,22 +605,16 @@ class Bitfield(Node):
         Node.__init__(self, name)
         self.symbol = symbol
         self.members = members
-        self.doc = None
-
-    def __repr__(self):
-        return 'Bitfield(%r, %r)' % (self.name, self.members)
 
 
-class Member(Node):
+class Member(Annotated):
 
     def __init__(self, name, value, symbol):
-        Node.__init__(self, name)
+        Annotated.__init__(self)
+        self.name = name
         self.value = value
         self.symbol = symbol
 
-    def __repr__(self):
-        return 'Member(%r, %r)' % (self.name, self.value)
-
 
 class Record(Node):
 
@@ -438,45 +624,34 @@ class Record(Node):
         self.constructors = []
         self.symbol = symbol
         self.disguised = disguised
-        self.doc = None
         self.methods = []
+        self.static_methods = []
+
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for func in self.methods:
+            func.walk(callback, chain)
+        for func in self.static_methods:
+            func.walk(callback, chain)
 
     def remove_matching_children(self, pred):
         self.fields = filter(pred, self.fields)
         self.constructors = filter(pred, self.constructors)
         self.methods = filter(pred, self.methods)
 
-# BW compat, remove
-Struct = Record
-
 
-class Field(Node):
+class Field(Annotated):
 
-    def __init__(self, name, typenode, symbol, readable, writable, bits=None):
-        Node.__init__(self, name)
+    def __init__(self, name, typenode, readable, writable, bits=None,
+                 anonymous_node=None):
+        Annotated.__init__(self)
+        self.name = name
         self.type = typenode
-        self.symbol = symbol
         self.readable = readable
         self.writable = writable
         self.bits = bits
-
-    def __repr__(self):
-        if self.bits:
-            return 'Field(%r, %r, %r)' % (self.name, self.type, self.bits)
-        else:
-            return 'Field(%r, %r)' % (self.name, self.type)
-
-
-class Return(TypeContainer):
-
-    def __init__(self, rtype, transfer=None):
-        TypeContainer.__init__(self, None, rtype, transfer)
-        self.direction = PARAM_DIRECTION_OUT
-        self.doc = None
-
-    def __repr__(self):
-        return 'Return(%r)' % (self.type, )
-
+        self.anonymous_node = anonymous_node
 
 class Class(Node):
 
@@ -484,6 +659,10 @@ class Class(Node):
         Node.__init__(self, name)
         self.ctype = name
         self.parent = parent
+        # When we're in the scanner, we keep around a list
+        # of parents so that we can transparently fall back
+        # if there are 'hidden' parents
+        self.parent_chain = []
         self.glib_type_struct = None
         self.is_abstract = is_abstract
         self.methods = []
@@ -493,7 +672,6 @@ class Class(Node):
         self.constructors = []
         self.properties = []
         self.fields = []
-        self.doc = None
 
     def remove_matching_children(self, pred):
         self.methods = filter(pred, self.methods)
@@ -501,87 +679,64 @@ class Class(Node):
         self.properties = filter(pred, self.properties)
         self.fields = filter(pred, self.fields)
 
-    def __repr__(self):
-        return '%s(%r, %r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.parent, self.methods)
-
+    def _walk(self, callback, chain):
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.virtual_methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
 
 class Interface(Node):
 
     def __init__(self, name, parent):
         Node.__init__(self, name)
         self.parent = parent
+        self.parent_chain = []
         self.methods = []
+        self.static_methods = []
         self.virtual_methods = []
         self.glib_type_struct = None
         self.properties = []
         self.fields = []
         self.prerequisites = []
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.methods)
 
+    def _walk(self, callback, chain):
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+        for meth in self.virtual_methods:
+            meth.walk(callback, chain)
 
 class Constant(Node):
 
-    def __init__(self, name, type_name, value):
+    def __init__(self, name, value_type, value):
         Node.__init__(self, name)
-        self.type = Type(type_name)
+        self.value_type = value_type
         self.value = value
 
-    def __repr__(self):
-        return 'Constant(%r, %r, %r)' % (
-            self.name, self.type, self.value)
-
 
-class Property(TypeContainer):
+class Property(Node):
 
-    def __init__(self, name, type_name, readable, writable,
-                 construct, construct_only, ctype=None, transfer=None):
-        self.type = Type(type_name, ctype)
-        TypeContainer.__init__(self, name, self.type, transfer)
+    def __init__(self, name, typeobj, readable, writable,
+                 construct, construct_only, transfer=None):
+        Node.__init__(self, name)
+        self.type = typeobj
         self.readable = readable
         self.writable = writable
         self.construct = construct
         self.construct_only = construct_only
-        self.doc = None
-
-    def __repr__(self):
-        return '%s(%r, %r)' % (
-            self.__class__.__name__,
-            self.name, self.type)
-
-
-# FIXME: Inherit from Function
+        self.transfer = PARAM_TRANSFER_NONE
 
 
-class Callback(Node):
+class Callback(Callable):
 
-    def __init__(self, name, retval, parameters, ctype=None):
-        Node.__init__(self, name)
-        self.retval = retval
-        self.parameters = parameters
+    def __init__(self, name, retval, parameters, throws, ctype=None):
+        Callable.__init__(self, name, retval, parameters, throws)
         self.ctype = ctype
-        self.throws = False
-        self.doc = None
-
-    def get_parameter_index(self, name):
-        for i, parameter in enumerate(self.parameters):
-            if parameter.name == name:
-                return i
-
-    def get_parameter(self, name):
-        for parameter in self.parameters:
-            if parameter.name == name:
-                return parameter
-
-    def __repr__(self):
-        return 'Callback(%r, %r, %r)' % (
-            self.name, self.retval, self.parameters)
 
 
 class Union(Node):
@@ -591,8 +746,13 @@ class Union(Node):
         self.fields = []
         self.constructors = []
         self.methods = []
+        self.static_methods = []
         self.symbol = symbol
-        self.doc = None
 
-    def __repr__(self):
-        return 'Union(%r, %r)' % (self.name, self.fields, )
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
diff --git a/giscanner/finaltransformer.py b/giscanner/finaltransformer.py
new file mode 100644
index 0000000..58f4233
--- /dev/null
+++ b/giscanner/finaltransformer.py
@@ -0,0 +1,106 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
+                  Function, Interface, Member, Namespace, Node, Parameter,
+                  Property, Record, Return, Type, TypeContainer, Union,
+                  Class, Field, VFunction, default_array_types,
+                  TYPE_ANY, TYPE_GTYPE, 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)
+from .utils import to_underscores, to_underscores_noprefix
+
+class FinalTransformer(object):
+
+    def __init__(self, transformer):
+        self._transformer = transformer
+        self._namespace = transformer.namespace
+
+    # Public API
+
+    def validate(self):
+        self._namespace.walk(self._analyze_node)
+        self._namespace.walk(self._introspectable_pass2)
+
+    def _interface_vfunc_check(self, node, stack):
+        if isinstance(node, GLibInterface):
+            for vfunc in node.virtual_methods:
+                if not vfunc.invoker:
+                    self._transformer.log_node_warning(vfunc,
+"""Virtual function %r has no known invoker""" % (vfunc.name, ),
+                    context=node)
+
+    def _parameter_warning(self, parent, param, text, *args):
+        if hasattr(parent, 'symbol'):
+            prefix = '%s: ' % (parent.symbol, )
+        else:
+            prefix = ''
+        if isinstance(param, Parameter):
+            context = "argument %s: " % (param.argname, )
+        else:
+            context = "return value: "
+        self._transformer.log_node_warning(parent, prefix + context + text, *args)
+
+    def _introspectable_param_analysis(self, parent, node):
+        if not node.type.resolved:
+            self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+            parent.introspectable = False
+        elif isinstance(node.type, Varargs):
+            parent.introspectable = False
+        elif not isinstance(node.type, List) and \
+                (node.type.target_giname == 'GLib.List'):
+            self._parameter_warning(parent, node, "Missing (element-type) annotation")
+            parent.introspectable = False
+        elif node.transfer is None:
+            self._parameter_warning(parent, node, "Missing (transfer) annotation")
+            parent.introspectable = False
+
+    def _analyze_node(self, obj, stack):
+        if obj.skip:
+            return False
+        # Combine one-pass checks here
+        self._interface_vfunc_check(obj, stack)
+        # Our first pass for scriptability
+        if isinstance(obj, Callable):
+            for param in obj.parameters:
+                self._introspectable_param_analysis(obj, param)
+            self._introspectable_param_analysis(obj, obj.retval)
+        return True
+
+    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(obj, TypeContainer):
+            parent = stack[-1]
+            target = self._transformer.lookup_typenode(obj.type)
+            if target and not target.introspectable:
+                parent.introspectable = False
+        return True
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 9fee1fc..9c4035e 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -22,13 +22,8 @@ import os
 
 from xml.etree.cElementTree import parse
 
-from .ast import (Alias, Array, Callback, Constant, Enum, Function, Field,
-                  Namespace, Parameter, Property, Return, Union, Struct, Type,
-                  Varargs, Include)
-from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
-                      GLibInterface, GLibObject, GLibBoxedStruct,
-                      GLibBoxedUnion, GLibBoxedOther)
-
+from .ast import *
+from .glibast import *
 from .girwriter import COMPATIBLE_GIR_VERSION
 
 CORE_NS = "http://www.gtk.org/introspection/core/1.0";
@@ -51,7 +46,6 @@ def _cns(tag):
 class GIRParser(object):
 
     def __init__(self):
-        self._include_parsing = False
         self._shared_libraries = []
         self._includes = set()
         self._pkgconfig_packages = set()
@@ -72,6 +66,8 @@ class GIRParser(object):
         self._namespace = None
         self._shared_libraries = []
         self._pkgconfig_packages = set()
+        self._c_includes = set()
+        self._c_prefix = None
         self._parse_api(tree.getroot())
 
     def get_namespace(self):
@@ -83,6 +79,12 @@ class GIRParser(object):
     def get_includes(self):
         return self._includes
 
+    def get_c_includes(self):
+        return self._c_includes
+
+    def get_c_prefix(self):
+        return self._c_prefix
+
     def get_pkgconfig_packages(self):
         if not hasattr(self, '_pkgconfig_packages'):
             self._pkgconfig_packages = []
@@ -91,11 +93,21 @@ class GIRParser(object):
     def get_doc(self):
         return parse(self._filename)
 
-    def set_include_parsing(self, include_parsing):
-        self._include_parsing = include_parsing
-
     # Private
 
+    def _find_first_child(self, node, name):
+        for child in node.getchildren():
+            if child.tag == name:
+                return child
+        return None
+
+    def _find_children(self, node, name):
+        result = []
+        for child in node.getchildren():
+            if child.tag == name:
+                result.append(child)
+        return result
+
     def _get_current_file(self):
         if not self._filename_stack:
             return None
@@ -105,9 +117,6 @@ class GIRParser(object):
             return curfile[len(cwd):]
         return curfile
 
-    def _add_node(self, node):
-        self._namespace.nodes.append(node)
-
     def _parse_api(self, root):
         assert root.tag == _corens('repository')
         version = root.attrib['version']
@@ -121,11 +130,14 @@ class GIRParser(object):
                 self._parse_include(node)
             elif node.tag == _corens('package'):
                 self._parse_pkgconfig_package(node)
+            elif node.tag == _cns('include'):
+                self._parse_c_include(node)
 
         ns = root.find(_corens('namespace'))
         assert ns is not None
         self._namespace = Namespace(ns.attrib['name'],
-                                    ns.attrib['version'])
+                                    ns.attrib['version'],
+                                    ns.attrib.get(_cns('prefix')))
         if 'shared-library' in ns.attrib:
             self._shared_libraries.extend(
                 ns.attrib['shared-library'].split(','))
@@ -155,20 +167,42 @@ class GIRParser(object):
         self._includes.add(include)
 
     def _parse_pkgconfig_package(self, node):
-        if not hasattr(self, '_pkgconfig_packages'):
-            self._pkgconfig_packages = []
         self._pkgconfig_packages.add(node.attrib['name'])
 
+    def _parse_c_include(self, node):
+        self._c_includes.add(node.attrib['name'])
+
     def _parse_alias(self, node):
         typeval = self._parse_type(node)
         alias = Alias(node.attrib['name'],
                       typeval,
                       node.attrib.get(_cns('type')))
-        self._add_node(alias)
+        self._namespace.append(alias)
+
+    def _parse_generic_attribs(self, node, obj):
+        assert isinstance(obj, Annotated)
+        doc = node.find(_corens('doc'))
+        if doc is not None:
+            obj.doc = doc.text
+        version = node.attrib.get('version')
+        if version:
+            obj.version = version
+        deprecated = node.attrib.get('deprecated')
+        if deprecated:
+            obj.deprecated = deprecated
+        introspectable = node.attrib.get('introspectable')
+        if introspectable:
+            obj.introspectable = int(introspectable) > 0
 
     def _parse_object_interface(self, node):
+        parent = node.attrib.get('parent')
+        if parent:
+            parent_type = Type(target_giname=parent)
+        else:
+            parent_type = None
+        
         ctor_args = [node.attrib['name'],
-                     node.attrib.get('parent'),
+                     parent_type,
                      node.attrib[_glibns('type-name')],
                      node.attrib[_glibns('get-type')]]
         if node.tag == _corens('interface'):
@@ -182,38 +216,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']
@@ -222,59 +267,82 @@ class GIRParser(object):
             raise ValueError('node %r has no return-value' % (name, ))
         transfer = returnnode.attrib.get('transfer-ownership')
         retval = Return(self._parse_type(returnnode), transfer)
+        self._parse_generic_attribs(returnnode, retval)
         parameters = []
 
+        throws = (node.attrib.get('throws') == '1')
+
         if klass is Callback:
-            func = klass(name, retval, parameters,
+            func = klass(name, retval, parameters, throws,
                          node.attrib.get(_cns('type')))
-        else:
+        elif klass is Function:
             identifier = node.attrib.get(_cns('identifier'))
-            throws = (node.attrib.get('throws') == '1')
-            func = klass(name, retval, parameters, identifier, throws)
-
-        if self._include_parsing:
-            return func
+            func = klass (name, retval, parameters, throws, identifier)
+        elif klass is VFunction:
+            func = klass(name, retval, parameters, throws)
+        else:
+            assert False
 
         parameters_node = node.find(_corens('parameters'))
         if (parameters_node is not None):
-            for paramnode in parameters_node.findall(_corens('parameter')):
+            for paramnode in self._find_children(parameters_node, _corens('parameter')):
                 param = Parameter(paramnode.attrib.get('name'),
                                   self._parse_type(paramnode),
-                                  paramnode.attrib.get('direction'),
+                                  paramnode.attrib.get('direction') or PARAM_DIRECTION_IN,
                                   paramnode.attrib.get('transfer-ownership'),
-                                  paramnode.attrib.get('allow-none') == '1')
+                                  paramnode.attrib.get('allow-none') == '1',
+                                  paramnode.attrib.get('scope'),
+                                  paramnode.attrib.get('caller-allocates') == '1')
+                closure = paramnode.attrib.get('closure')
+                if closure:
+                    param.closure_index = int(closure)
+                destroy = paramnode.attrib.get('destroy')
+                if destroy:
+                    param.destroy_index = int(destroy)
+                self._parse_generic_attribs(paramnode, param)
                 parameters.append(param)
 
+        self._parse_generic_attribs(node, func)
+
         return func
 
-    def _parse_record(self, node):
+    def _parse_record(self, node, anonymous=False):
         if _glibns('type-name') in node.attrib:
             struct = GLibBoxedStruct(node.attrib['name'],
                                      node.attrib[_glibns('type-name')],
                                      node.attrib[_glibns('get-type')],
                                      node.attrib.get(_cns('type')))
+        elif _glibns('is-gtype-struct-for') in node.attrib:
+            struct = GLibRecord(node.attrib['name'],
+                                node.attrib.get(_cns('type')),
+                                disguised=node.attrib.get('disguised') == '1')
+            is_gtype_struct_for = node.attrib[_glibns('is-gtype-struct-for')]
+            struct.is_gtype_struct_for = Type(target_giname=is_gtype_struct_for)
         else:
-            disguised = node.attrib.get('disguised') == '1'
-            struct = Struct(node.attrib['name'],
+            struct = Record(node.attrib['name'],
                             node.attrib.get(_cns('type')),
-                            disguised=disguised)
-        self._add_node(struct)
-
-        if self._include_parsing:
-            return
-        for field in node.findall(_corens('field')):
-            struct.fields.append(self._parse_field(field))
-        for callback in node.findall(_corens('callback')):
-            struct.fields.append(
-                self._parse_function_common(callback, Callback))
-        for method in node.findall(_corens('method')):
-            struct.fields.append(
+                            disguised=node.attrib.get('disguised') == '1')
+        if node.attrib.get('foreign') == '1':
+            struct.foreign = True
+        self._parse_generic_attribs(node, struct)
+        if not anonymous:
+            self._namespace.append(struct)
+
+        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 func in self._find_children(node, _corens('function')):
+            struct.static_methods.append(
+                self._parse_function_common(func, Function))
+        for ctor in self._find_children(node, _corens('constructor')):
             struct.constructors.append(
                 self._parse_function_common(ctor, Function))
+        return struct
 
-    def _parse_union(self, node):
+    def _parse_union(self, node, anonymous=False):
         if _glibns('type-name') in node.attrib:
             union = GLibBoxedUnion(node.attrib['name'],
                                     node.attrib[_glibns('type-name')],
@@ -283,50 +351,73 @@ class GIRParser(object):
         else:
             union = Union(node.attrib['name'],
                           node.attrib.get(_cns('type')))
-        self._add_node(union)
+        if not anonymous:
+            self._namespace.append(union)
 
-        if self._include_parsing:
-            return
-        for callback in node.findall(_corens('callback')):
+        for callback in self._find_children(node, _corens('callback')):
             union.fields.append(
                 self._parse_function_common(callback, Callback))
-        for field in node.findall(_corens('field')):
+        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))
+        return union
 
     def _parse_type(self, node):
-        typenode = node.find(_corens('type'))
+        # Fields can contain inline callbacks
+        typenode = node.find(_corens('callback'))
         if typenode is not None:
-            return Type(typenode.attrib['name'],
-                        typenode.attrib.get(_cns('type')))
+            return Type(target_giname=typenode.attrib['name'],
+                        ctype=typenode.attrib.get(_cns('type')))
 
+        # Arrays have their own toplevel XML
         typenode = node.find(_corens('array'))
         if typenode is not None:
-
-            array_type = typenode.attrib.get(_cns('type'))
-            if array_type.startswith('GArray*') or \
-               array_type.startswith('GPtrArray*') or \
-               array_type.startswith('GByteArray*'):
-                element_type = None
-            else:
-                element_type = self._parse_type(typenode)
-
-            ret = Array(None, array_type, element_type)
+            array_type = typenode.attrib.get('name')
+            element_type = self._parse_type(typenode)
+            array_ctype = typenode.attrib.get(_cns('type'))
+            ret = Array(array_type, element_type, ctype=array_ctype)
+            # zero-terminated defaults to true...
+            zero = typenode.attrib.get('zero-terminated')
+            if zero and zero == '0':
+                ret.zeroterminated = False
+            fixed_size = typenode.attrib.get('fixed-size') 
+            if fixed_size:
+                ret.size = int(fixed_size)
 
             lenidx = typenode.attrib.get('length')
             if lenidx:
                 ret.length_param_index = int(lenidx)
             return ret
 
+        # Dispsense with varargs
         typenode = node.find(_corens('varargs'))
         if typenode is not None:
             return Varargs()
 
+        # Okay now...could be a list, let's see.
+        typenode = self._find_first_child(node, _corens('type'))
+        if typenode is not None:
+            name = typenode.attrib.get('name')
+            ctype = typenode.attrib.get(_cns('type'))
+            if name is None:
+                if ctype is None:
+                    return TypeUnknown()
+                return Type(ctype=ctype)
+            if name in ['GLib.List', 'GLib.SList']:
+                subchild = self._find_first_child(typenode, _corens('type'))
+                if subchild is not None:
+                    element_type = self._parse_type(typenode)
+                else:
+                    element_type = TYPE_ANY
+                return List(name, element_type, ctype=ctype)
+            else:
+                return self._namespace.type_from_name(name, ctype)
+
         raise ValueError("Couldn't parse type of node %r; children=%r",
                          node, list(node))
 
@@ -334,74 +425,98 @@ 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'))
+        anonymous_node = None
+        type_node = None
+        for name in ('callback', 'record', 'union'):
+            anonymous_elt = self._find_first_child(node, _corens(name))
+            if anonymous_elt:
+                break
+        if anonymous_elt:
+            if anonymous_elt.tag == _corens('callback'):
+                anonymous_node = self._parse_function_common(anonymous_elt, Callback)
+            elif anonymous_elt.tag == _corens('record'):
+                anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
+            elif anonymous_elt.tag == _corens('union'):
+                anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
+            else:
+                assert False, anonymous_elt.tag
+        else:
+            type_node = self._parse_type(node)
+        field = Field(node.attrib['name'],
+                      type_node,
+                      node.attrib.get('readable') != '0',
+                      node.attrib.get('writable') == '1',
+                      node.attrib.get('bits'),
+                      anonymous_node=anonymous_node)
+        self._parse_generic_attribs(node, field)
+        return field
 
     def _parse_property(self, node):
-        type_node = self._parse_type(node)
-        return Property(node.attrib['name'],
-                        type_node.name,
+        prop = Property(node.attrib['name'],
+                        self._parse_type(node),
                         node.attrib.get('readable') != '0',
                         node.attrib.get('writable') == '1',
                         node.attrib.get('construct') == '1',
-                        node.attrib.get('construct-only') == '1',
-                        type_node.ctype)
+                        node.attrib.get('construct-only') == '1')
+        self._parse_generic_attribs(node, prop)
+        return prop
 
     def _parse_member(self, node):
-        return GLibEnumMember(node.attrib['name'],
-                              node.attrib['value'],
-                              node.attrib.get(_cns('identifier')),
-                              node.attrib.get(_glibns('nick')))
+        member = GLibEnumMember(node.attrib['name'],
+                                node.attrib['value'],
+                                node.attrib.get(_cns('identifier')),
+                                node.attrib.get(_glibns('nick')))
+        self._parse_generic_attribs(node, member)
+        return member
 
     def _parse_constant(self, node):
         type_node = self._parse_type(node)
         constant = Constant(node.attrib['name'],
-                            type_node.name,
+                            type_node,
                             node.attrib['value'])
-        self._add_node(constant)
+        self._parse_generic_attribs(node, constant)
+        self._namespace.append(constant)
 
     def _parse_enumeration_bitfield(self, node):
         name = node.attrib.get('name')
         ctype = node.attrib.get(_cns('type'))
         get_type = node.attrib.get(_glibns('get-type'))
         type_name = node.attrib.get(_glibns('type-name'))
-        if get_type:
+        glib_error_quark = node.attrib.get(_glibns('error-quark'))
+        if get_type or glib_error_quark:
             if node.tag == _corens('bitfield'):
                 klass = GLibFlags
             else:
                 klass = GLibEnum
         else:
-            klass = Enum
+            if node.tag == _corens('bitfield'):
+                klass = Bitfield
+            else:
+                klass = Enum
             type_name = ctype
         members = []
-        if klass is Enum:
+        if klass in (Enum, Bitfield):
             obj = klass(name, type_name, members)
         else:
             obj = klass(name, type_name, members, get_type)
+            obj.error_quark = glib_error_quark
             obj.ctype = ctype
-        self._add_node(obj)
+        self._parse_generic_attribs(node, obj)
+        self._namespace.append(obj)
 
-        if self._include_parsing:
-            return
-        for member in node.findall(_corens('member')):
+        for member in self._find_children(node, _corens('member')):
             members.append(self._parse_member(member))
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index fd0b99f..500b489 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,8 +22,8 @@
 from __future__ import with_statement
 
 from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
-                  Function, Interface, List, Map, Member, Struct, Union,
-                  Varargs, Type, TYPE_ANY)
+                  Function, Interface, List, Map, Member, Record, Union,
+                  Varargs, Type)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
                       GLibFlags, GLibObject, GLibInterface,
                       GLibRecord)
@@ -35,17 +35,17 @@ COMPATIBLE_GIR_VERSION = '1.1'
 
 class GIRWriter(XMLWriter):
 
-    def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
+    def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
         super(GIRWriter, self).__init__()
         self.write_comment(
 '''This file was automatically generated from C sources - DO NOT EDIT!
 To affect the contents of this file, edit the original C definitions,
 and/or use gtk-doc annotations. ''')
         self._write_repository(namespace, shlibs, includes, pkgs,
-                               c_includes, cprefix)
+                               c_includes)
 
     def _write_repository(self, namespace, shlibs, includes=None,
-                          packages=None, c_includes=None, cprefix=None):
+                          packages=None, c_includes=None):
         if includes is None:
             includes = frozenset()
         if packages is None:
@@ -65,7 +65,9 @@ and/or use gtk-doc annotations. ''')
                 self._write_pkgconfig_pkg(pkg)
             for c_include in sorted(set(c_includes)):
                 self._write_c_include(c_include)
-            self._write_namespace(namespace, shlibs, cprefix)
+            self._namespace = namespace
+            self._write_namespace(namespace, shlibs)
+            self._namespace = None
 
     def _write_include(self, include):
         attrs = [('name', include.name), ('version', include.version)]
@@ -79,11 +81,11 @@ and/or use gtk-doc annotations. ''')
         attrs = [('name', c_include)]
         self.write_tag('c:include', attrs)
 
-    def _write_namespace(self, namespace, shlibs, cprefix):
+    def _write_namespace(self, namespace, shlibs):
         attrs = [('name', namespace.name),
                  ('version', namespace.version),
                  ('shared-library', ','.join(shlibs)),
-                 ('c:prefix', cprefix)]
+                 ('c:prefix', namespace.c_prefix)]
         with self.tagcontext('namespace', attrs):
             # We define a custom sorting function here because
             # we want aliases to be first.  They're a bit
@@ -98,7 +100,7 @@ and/or use gtk-doc annotations. ''')
                     return 1
                 else:
                     return cmp(a, b)
-            for node in sorted(namespace.nodes, cmp=nscmp):
+            for node in sorted(namespace.itervalues(), cmp=nscmp):
                 self._write_node(node)
 
     def _write_node(self, node):
@@ -112,7 +114,7 @@ and/or use gtk-doc annotations. ''')
             self._write_class(node)
         elif isinstance(node, Callback):
             self._write_callback(node)
-        elif isinstance(node, Struct):
+        elif isinstance(node, Record):
             self._write_record(node)
         elif isinstance(node, Union):
             self._write_union(node)
@@ -171,7 +173,9 @@ and/or use gtk-doc annotations. ''')
             self._write_parameters(callable.parameters)
 
     def _write_function(self, func, tag_name='function'):
-        attrs = [('c:identifier', func.symbol)]
+        attrs = []
+        if hasattr(func, 'symbol'):
+            attrs.append(('c:identifier', func.symbol))
         self._write_callable(func, tag_name, attrs)
 
     def _write_method(self, method):
@@ -187,10 +191,9 @@ and/or use gtk-doc annotations. ''')
         if not return_:
             return
 
-        assert return_.transfer is not None, return_
-
         attrs = []
-        attrs.append(('transfer-ownership', return_.transfer))
+        if return_.transfer:
+            attrs.append(('transfer-ownership', return_.transfer))
         with self.tagcontext('return-value', attrs):
             self._write_generic(return_)
             self._write_type(return_.type)
@@ -203,17 +206,16 @@ and/or use gtk-doc annotations. ''')
                 self._write_parameter(parameter)
 
     def _write_parameter(self, parameter):
-        assert parameter.transfer is not None, parameter
-
         attrs = []
-        if parameter.name is not None:
-            attrs.append(('name', parameter.name))
-        if parameter.direction != 'in':
+        if parameter.argname is not None:
+            attrs.append(('name', parameter.argname))
+        if (parameter.direction is not None) and (parameter.direction != 'in'):
             attrs.append(('direction', parameter.direction))
             attrs.append(('caller-allocates',
                           '1' if parameter.caller_allocates else '0'))
-        attrs.append(('transfer-ownership',
-                     parameter.transfer))
+        if parameter.transfer:
+            attrs.append(('transfer-ownership',
+                          parameter.transfer))
         if parameter.allow_none:
             attrs.append(('allow-none', '1'))
         if parameter.scope:
@@ -226,62 +228,56 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(parameter)
             self._write_type(parameter.type)
 
-    def _type_to_string(self, ntype):
-        if isinstance(ntype, basestring):
-            return ntype
-        return ntype.name
+    def _type_to_name(self, typeval):
+        if not typeval.resolved:
+            raise AssertionError("Caught unresolved type %r (ctype=%r)" % (typeval, typeval.ctype))
+        assert typeval.target_giname is not None
+        prefix = self._namespace.name + '.'
+        if typeval.target_giname.startswith(prefix):
+            return typeval.target_giname[len(prefix):]
+        return typeval.target_giname
 
     def _write_type(self, ntype, relation=None):
-        if isinstance(ntype, basestring):
-            typename = ntype
-            type_cname = None
-        else:
-            typename = ntype.name
-            type_cname = ntype.ctype
+        assert isinstance(ntype, Type), ntype
+        attrs = []
+        if ntype.ctype:
+            attrs.append(('c:type', ntype.ctype))
         if isinstance(ntype, Varargs):
             with self.tagcontext('varargs', []):
                 pass
-            return
-        if isinstance(ntype, Array):
-            attrs = []
+        elif isinstance(ntype, Array):
+            if ntype.array_type != Array.C:
+                attrs.insert(0, ('name', ntype.array_type))
             if not ntype.zeroterminated:
-                attrs.append(('zero-terminated', '0'))
+                attrs.insert(0, ('zero-terminated', '0'))
             if ntype.length_param_index >= 0:
                 attrs.append(('length', '%d' % (ntype.length_param_index, )))
-            if ntype.name in ['GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray']:
-                attrs.append(('name', ntype.name))
-            attrs.append(('c:type', ntype.ctype))
             if ntype.size is not None:
-                attrs.append(('fixed-size', ntype.size))
+                attrs.append(('fixed-size', '%d' % (ntype.size, )))
 
             with self.tagcontext('array', attrs):
-                if ntype.element_type is not None:
-                    self._write_type(ntype.element_type)
-                else:
-                    self._write_type(Type(TYPE_ANY, ctype='gpointer'))
-            return
-        attrs = [('name', self._type_to_string(ntype))]
-        # FIXME: figure out if type references a basic type
-        #        or a boxed/class/interface etc. and skip
-        #        writing the ctype if the latter.
-        if type_cname is not None:
-            attrs.append(('c:type', type_cname))
-        if (isinstance(ntype, List)
-            or typename in ('GLib.List',
-                            'GLib.SList')):
+                self._write_type(ntype.element_type)
+        elif isinstance(ntype, List):
+            if ntype.name:
+                attrs.insert(0, ('name', ntype.name))
             with self.tagcontext('type', attrs):
-                if isinstance(ntype, List) and ntype.element_type:
-                    self._write_type(ntype.element_type)
-                else:
-                    self._write_type(Type(TYPE_ANY, ctype='gpointer'))
-            return
-        if isinstance(ntype, Map) and ntype.key_type:
+                self._write_type(ntype.element_type)
+        elif isinstance(ntype, Map):
+            if ntype.name:
+                attrs.insert(0, ('name', name))
             with self.tagcontext('type', attrs):
                 self._write_type(ntype.key_type)
                 self._write_type(ntype.value_type)
-            return
-        # Not a special type, just write it out
-        self.write_tag('type', attrs)
+        else:
+            # REWRITEFIXME - enable this for 1.2
+            if ntype.target_giname:
+                attrs.insert(0, ('name', self._type_to_name(ntype)))
+            elif ntype.target_fundamental:
+                # attrs = [('fundamental', ntype.target_fundamental)]
+                attrs.insert(0, ('name', ntype.target_fundamental))
+            elif ntype.target_foreign:
+                attrs.insert(0, ('foreign', '1'))
+            self.write_tag('type', attrs)
 
     def _write_enum(self, enum):
         attrs = [('name', enum.name)]
@@ -325,10 +321,9 @@ and/or use gtk-doc annotations. ''')
         self.write_tag('member', attrs)
 
     def _write_constant(self, constant):
-        attrs = [('name', constant.name),
-                 ('value', str(constant.value))]
+        attrs = [('name', constant.name), ('value', constant.value)]
         with self.tagcontext('constant', attrs):
-            self._write_type(constant.type)
+            self._write_type(constant.value_type)
 
     def _write_class(self, node):
         attrs = [('name', node.name),
@@ -338,7 +333,8 @@ and/or use gtk-doc annotations. ''')
         if isinstance(node, Class):
             tag_name = 'class'
             if node.parent is not None:
-                attrs.append(('parent', node.parent))
+                attrs.append(('parent', 
+                              self._type_to_name(node.parent)))
             if node.is_abstract:
                 attrs.append(('abstract', '1'))
         else:
@@ -348,7 +344,8 @@ and/or use gtk-doc annotations. ''')
             if node.get_type:
                 attrs.append(('glib:get-type', node.get_type))
             if node.glib_type_struct:
-                attrs.append(('glib:type-struct', node.glib_type_struct.name))
+                attrs.append(('glib:type-struct', 
+                              self._type_to_name(node.glib_type_struct)))
         if isinstance(node, GLibObject):
             if node.fundamental:
                 attrs.append(('glib:fundamental', '1'))
@@ -364,10 +361,12 @@ and/or use gtk-doc annotations. ''')
             self._write_generic(node)
             if isinstance(node, GLibObject):
                 for iface in node.interfaces:
-                    self.write_tag('implements', [('name', iface)])
+                    self.write_tag('implements',
+                                   [('name', self._type_to_name(iface))])
             if isinstance(node, Interface):
                 for iface in node.prerequisites:
-                    self.write_tag('prerequisite', [('name', iface)])
+                    self.write_tag('prerequisite', 
+                                   [('name', self._type_to_name(iface))])
             if isinstance(node, Class):
                 for method in node.constructors:
                     self._write_constructor(method)
@@ -394,6 +393,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in boxed.methods:
                 self._write_method(method)
+            for method in boxed.static_methods:
+                self._write_static_method(method)
 
     def _write_property(self, prop):
         attrs = [('name', prop.name)]
@@ -408,7 +409,8 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('construct', '1'))
         if prop.construct_only:
             attrs.append(('construct-only', '1'))
-        attrs.append(('transfer-ownership', prop.transfer))
+        if prop.transfer:
+            attrs.append(('transfer-ownership', prop.transfer))
         with self.tagcontext('property', attrs):
             self._write_generic(prop)
             self._write_type(prop.type)
@@ -416,7 +418,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):
@@ -442,7 +444,7 @@ and/or use gtk-doc annotations. ''')
             if record.is_gtype_struct_for:
                 is_gtype_struct = True
                 attrs.append(('glib:is-gtype-struct-for',
-                              record.is_gtype_struct_for))
+                              self._type_to_name(record.is_gtype_struct_for)))
         self._append_version(record, attrs)
         self._append_node_generic(record, attrs)
         if isinstance(record, GLibBoxed):
@@ -456,6 +458,8 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in record.methods:
                 self._write_method(method)
+            for method in record.static_methods:
+                self._write_static_method(method)
 
     def _write_union(self, union):
         attrs = []
@@ -476,25 +480,23 @@ and/or use gtk-doc annotations. ''')
                 self._write_constructor(method)
             for method in union.methods:
                 self._write_method(method)
+            for method in union.static_methods:
+                self._write_static_method(method)
 
     def _write_field(self, field, is_gtype_struct=False):
-        if isinstance(field, Function):
-            self._write_method(field)
-            return
-
-        if isinstance(field, Callback):
+        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)
+                elif isinstance(field.anonymous_node, Record):
+                    self._write_record(field.anonymous_node)
+                elif isinstance(field.anonymous_node, Union):
+                    self._write_union(field.anonymous_node)
                 else:
-                    attrs = [('name', TYPE_ANY), ('c:type', 'pointer')]
-                    self.write_tag('type', attrs)
-        elif isinstance(field, Struct):
-            self._write_record(field)
-        elif isinstance(field, Union):
-            self._write_union(field)
+                    raise AssertionError("Unknown field anonymous: %r" \
+                                             % (field.anonymous_node, ))
         else:
             attrs = [('name', field.name)]
             # Fields are assumed to be read-only
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index ad87926..69f1c0b 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -19,7 +19,7 @@
 #
 
 from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
-                  Property, Union, Record)
+                  Property, Union, Record, Annotated)
 
 class GLibRecord(Record):
     def __init__(self, *args, **kwargs):
@@ -118,9 +118,18 @@ class GLibBoxedOther(Node, GLibBoxed):
         GLibBoxed.__init__(self, type_name, get_type)
         self.constructors = []
         self.methods = []
+        self.static_methods = []
         self.ctype = type_name
         self.doc = None
 
+    def _walk(self, callback, chain):
+        for ctor in self.constructors:
+            ctor.walk(callback, chain)
+        for meth in self.methods:
+            meth.walk(callback, chain)
+        for meth in self.static_methods:
+            meth.walk(callback, chain)
+
 
 class GLibInterface(Interface):
 
@@ -137,10 +146,10 @@ class GLibProperty(Property):
     pass
 
 
-class GLibSignal(Node):
+class GLibSignal(Annotated):
 
     def __init__(self, name, retval):
-        Node.__init__(self, name)
+        Annotated.__init__(self)
+        self.name = name
         self.retval = retval
         self.parameters = []
-        self.doc = None
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index a8fdcc3..42fc6ef 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,11 +28,9 @@ import subprocess
 from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
                   Function, Interface, Member, Namespace, Node, Parameter,
                   Property, Record, Return, Type, TypeContainer, Union,
-                  Field, VFunction, type_name_from_ctype, default_array_types,
-                  TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
-                  TYPE_LONG_LONG, TYPE_LONG_DOUBLE,
+                  Class, Field, VFunction, default_array_types,
+                  TYPE_ANY, TYPE_GTYPE, TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
                   Map, Varargs, type_names)
-from .transformer import Names
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
                       GLibBoxedUnion, GLibBoxedOther, GLibRecord)
@@ -50,30 +48,6 @@ G_PARAM_STATIC_NAME = 1 << 5
 G_PARAM_STATIC_NICK = 1 << 6
 G_PARAM_STATIC_BLURB = 1 << 7
 
-SYMBOL_BLACKLIST = [
-    # These ones break GError conventions
-    'g_simple_async_result_new_from_error',
-    'g_simple_async_result_set_from_error',
-    'g_simple_async_result_propagate_error',
-    'g_simple_async_result_report_error_in_idle',
-    'gtk_print_operation_get_error',
-]
-
-SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
-                           [r'\w+_marshal_[A-Z]+__', ]]
-
-GET_TYPE_OVERRIDES = {
-    # this is a special case, from glibtransforer.py:create_gobject
-    'intern': 'g_object_get_type',
-    # this is presumably a typo, should be fixed upstream
-    'g_gstring_get_type': 'g_string_get_type',
-    # this is historical cruft: there's a deprecated
-    #   #define gdk_window_get_type gdk_window_get_window_type
-    # upstream; this method can be renamed properly upstream once
-    # that deprecated alias is removed (in some future release)
-    'gdk_window_object_get_type': 'gdk_window_get_type',
-}
-
 
 class IntrospectionBinary(object):
 
@@ -97,20 +71,15 @@ class UnknownTypeError(Exception):
 
 class GLibTransformer(object):
 
-    def __init__(self, transformer, noclosure=False):
+    def __init__(self, transformer):
         self._transformer = transformer
-        self._noclosure = noclosure
-        self._namespace_name = None
-        self._names = Names()
+        self._namespace = transformer.namespace
         self._uscore_type_names = {}
         self._binary = None
         self._get_type_functions = []
-        self._error_quark_functions = []
         self._gtype_data = {}
-        self._failed_types = {}
         self._boxed_types = {}
         self._private_internal_types = {}
-        self._validating = False
 
     # Public API
 
@@ -121,18 +90,21 @@ class GLibTransformer(object):
 
         """
 
-        namespace = self._transformer.parse()
-        self._namespace_name = namespace.name
-        self._namespace_version = namespace.version
+        self._transformer.parse()
 
         # First pass: parsing
-        for node in namespace.nodes:
-            self._parse_node(node)
+        for node in self._namespace.itervalues():
+            if isinstance(node, Function):
+                self._initparse_function(node)
+        if self._namespace.name == 'GObject':
+            for node in self._namespace.itervalues():
+                if isinstance(node, Record):
+                    self._initparse_gobject_record(node)
 
         # We don't want an alias for this - it's handled specially in
         # the typelib compiler.
-        if namespace.name == 'GObject':
-            del self._names.aliases['Type']
+        if self._namespace.name == 'GObject':
+            del self._namespace.aliases['Type']
 
     def get_get_type_functions(self):
         return self._get_type_functions
@@ -144,148 +116,28 @@ class GLibTransformer(object):
         """Do remaining parsing steps requiring introspection binary"""
 
         # Get all the GObject data by passing our list of get_type
-        # functions to the compiled binary
-
-        self._execute_binary()
-
-        # Introspection is done from within parsing
+        # functions to the compiled binary, returning an XML blob.
+        tree = self._execute_binary_get_tree()
+        root = tree.getroot()
+        for child in root:
+            self._gtype_data[child.attrib['name']] = child
+        for child in root:
+            self._introspect_type(child)
 
-        # Second pass: pair boxed structures
-        for boxed in self._boxed_types.itervalues():
-            self._pair_boxed_type(boxed)
-        # Third pass: delete class structures, resolve
-        # all types we now know about
-        nodes = list(self._names.names.itervalues())
-        for (ns, node) in nodes:
-            try:
-                self._resolve_node(node)
-            except KeyError, e:
-                self._transformer.log_node_warning(node,
-"""Unresolvable entry %r""" % (e, ))
-                self._remove_attribute(node.name)
-        # Another pass, since we need to have the methods parsed
-        # in order to correctly modify them after class/record
-        # pairing
-        for (ns, node) in nodes:
-            # associate GtkButtonClass with GtkButton
-            if isinstance(node, Record):
+        # Pair up boxed structures and class structures
+        for node in self._namespace.itervalues():
+            if isinstance(node, GLibBoxed):
+                self._pair_boxed_type(node)
+            elif isinstance(node, Record):
                 self._pair_class_record(node)
-        for (ns, alias) in self._names.aliases.itervalues():
-            self._resolve_alias(alias)
-
-        self._resolve_quarks()
-
-        # Our final pass replacing types
-        self._resolve_types(nodes)
-
-        # Create a new namespace with what we found
-        namespace = Namespace(self._namespace_name, self._namespace_version)
-        namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
-        for (ns, x) in self._names.names.itervalues():
-            namespace.nodes.append(x)
-        return namespace
-
-    # Private
-
-    def _add_attribute(self, node, replace=False):
-        node_name = node.name
-        if (not replace) and node_name in self._names.names:
-            return
-        self._names.names[node_name] = (None, node)
-
-    def _remove_attribute(self, name):
-        del self._names.names[name]
-
-    def _get_attribute(self, name):
-        node = self._names.names.get(name)
-        if node:
-            return node[1]
-        return None
-
-    def _lookup_node(self, name):
-        if name in type_names:
-            return None
-        node = self._get_attribute(name)
-        if node is None:
-            node = self._transformer.get_names().names.get(name)
-            if node:
-                return node[1]
-        return node
-
-    def _get_no_uscore_prefixed_name(self, type_name):
-        # Besides the straight underscore conversion, we also try
-        # removing the underscores from the namespace as a possible C
-        # mapping; e.g. it's webkit_web_view, not web_kit_web_view
-        suffix = self._transformer.remove_prefix(type_name)
-        prefix = type_name[:-len(suffix)]
-        return (prefix + '_' + to_underscores(suffix)).lower()
-
-    def _register_internal_type(self, type_name, node):
-        self._names.type_names[type_name] = (None, node)
-        uscored = to_underscores(type_name).lower()
-        # prefer the prefix of the get_type method, if there is one
-        if hasattr(node, 'get_type'):
-            uscored = GET_TYPE_OVERRIDES.get(node.get_type, node.get_type)
-            uscored = uscored[:-len('_get_type')]
-        self._uscore_type_names[uscored] = node
-
-        no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
-        # since this is a guess, don't overwrite any 'real' prefix
-        if no_uscore_prefixed not in self._uscore_type_names:
-            self._uscore_type_names[no_uscore_prefixed] = node
-
-    def _resolve_quarks(self):
-        # self._uscore_type_names is an authoritative mapping of types
-        # to underscored versions, since it is based on get_type() methods;
-        # but only covers enums that are registered as GObject enums.
-        # Create a fallback mapping based on all known enums in this module.
-        uscore_enums = {}
-        for enum in self._transformer.iter_enums():
-            type_name = enum.symbol
-            uscored = to_underscores(type_name).lower()
-
-            uscore_enums[uscored] = enum
-
-            no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
-            if no_uscore_prefixed not in uscore_enums:
-                uscore_enums[no_uscore_prefixed] = enum
-
-        for node in self._error_quark_functions:
-            short = node.symbol[:-len('_quark')]
-            if short == "g_io_error":
-                # Special case; GIOError was already taken forcing GIOErrorEnum
-                enum = self._names.type_names["GIOErrorEnum"][1]
-            else:
-                enum = self._uscore_type_names.get(short)
-                if enum is None:
-                    enum = uscore_enums.get(short)
-            if enum is not None:
-                enum.error_quark = node.symbol
-            else:
-                self._transformer.log_node_warning(node,
-"""Couldn't find corresponding enumeration""")
 
     # Helper functions
 
-    def _resolve_gtypename(self, gtype_name):
-        try:
-            return self._transformer.gtypename_to_giname(gtype_name,
-                                                         self._names)
-        except KeyError, e:
-            return Unresolved(gtype_name)
-
-    def _resolve_gtypename_chain(self, gtype_names):
-        """Like _resolve_gtypename, but grab the first one that resolves.
-        If none of them do, return an Unresolved for the first."""
-        for gtype_name in gtype_names:
-            try:
-                return self._transformer.gtypename_to_giname(gtype_name,
-                                                             self._names)
-            except KeyError, e:
-                continue
-        return Unresolved(gtype_names[0])
+    def _execute_binary_get_tree(self):
+        """Load the library (or executable), returning an XML
+blob containing data gleaned from GObject's primitive introspection."""
+        from xml.etree.cElementTree import parse
 
-    def _execute_binary(self):
         in_path = os.path.join(self._binary.tmpdir, 'types.txt')
         f = open(in_path, 'w')
         # TODO: Introspect GQuark functions
@@ -301,22 +153,14 @@ class GLibTransformer(object):
 
         # Invoke the binary, having written our get_type functions to types.txt
         try:
-            subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
-        except subprocess.CalledProcessError, e:
-            raise SystemExit(e)
-        self._read_introspect_dump(out_path)
-
-        # Clean up temporaries
-        shutil.rmtree(self._binary.tmpdir)
-
-    def _read_introspect_dump(self, xmlpath):
-        from xml.etree.cElementTree import parse
-        tree = parse(xmlpath)
-        root = tree.getroot()
-        for child in root:
-            self._gtype_data[child.attrib['name']] = child
-        for child in root:
-            self._introspect_type(child)
+            try:
+                subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
+            except subprocess.CalledProcessError, e:
+                # Clean up temporaries
+                raise SystemExit(e)
+            return parse(out_path)
+        finally:
+            shutil.rmtree(self._binary.tmpdir)
 
     def _create_gobject(self, node):
         type_name = 'G' + node.name
@@ -324,8 +168,7 @@ class GLibTransformer(object):
             parent_gitype = None
             symbol = 'intern'
         elif type_name == 'GInitiallyUnowned':
-            parent_type_name = 'GObject'
-            parent_gitype = self._resolve_gtypename(parent_type_name)
+            parent_gitype = Type(target_giname='GLib.Object')
             symbol = 'g_initially_unowned_get_type'
         else:
             assert False
@@ -340,345 +183,50 @@ class GLibTransformer(object):
             # what we do here is copy all of the GObject fields into
             # GInitiallyUnowned so that struct offset computation
             # works correctly.
-            gnode.fields = self._names.names['Object'][1].fields
-        self._add_attribute(gnode)
-        self._register_internal_type(type_name, gnode)
+            gnode.fields = self._namespace.get('Object').fields
+        self._namespace.append(gnode, replace=True)
 
     # Parser
 
-    def _parse_node(self, node):
-        if isinstance(node, Enum):
-            self._parse_enum(node)
-        elif isinstance(node, Bitfield):
-            self._parse_bitfield(node)
-        elif isinstance(node, Function):
-            self._parse_function(node)
-        elif isinstance(node, Record):
-            self._parse_record(node)
-        elif isinstance(node, Callback):
-            self._parse_callback(node)
-        elif isinstance(node, Alias):
-            self._parse_alias(node)
-        elif isinstance(node, Member):
-            # FIXME: atk_misc_instance singletons
-            pass
-        elif isinstance(node, Union):
-            self._parse_union(node)
-        elif isinstance(node, Constant):
-            self._parse_constant(node)
-        else:
-            print 'GLIB Transformer: Unhandled node:', node
-
-    def _parse_alias(self, alias):
-        self._names.aliases[alias.name] = (None, alias)
-
-    def _parse_enum(self, enum):
-        self._add_attribute(enum)
-
-    def _parse_bitfield(self, enum):
-        self._add_attribute(enum)
-
-    def _parse_constant(self, constant):
-        self._add_attribute(constant)
-
-    def _parse_function(self, func):
-        if func.symbol in SYMBOL_BLACKLIST:
-            return
-        if func.symbol.startswith('_'):
-            return
-        for regexp in SYMBOL_BLACKLIST_RE:
-            if regexp.match(func.symbol):
-                return
-        if self._parse_get_type_function(func):
-            return
-        if self._parse_error_quark_function(func):
+    def _initparse_function(self, func):
+        symbol = func.symbol
+        if symbol.startswith('_'):
             return
+        elif symbol.endswith('_get_type'):
+            self._initparse_get_type_function(func)
 
-        self._add_attribute(func)
-
-    def _parse_get_type_function(self, func):
-        symbol = func.symbol
-        if not symbol.endswith('_get_type'):
-            return False
-        if self._namespace_name == 'GLib':
+    def _initparse_get_type_function(self, func):
+        if self._namespace.name == 'GLib':
             # No GObjects in GLib
             return False
-        if (self._namespace_name == 'GObject' and
-            symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
+        if (self._namespace.name == 'GObject' and
+            func.symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
             # We handle these internally, see _create_gobject
             return True
         if func.parameters:
             return False
         # GType *_get_type(void)
-        if func.retval.type.name not in ['Type',
-                                         'GType',
-                                         'GObject.Type',
-                                         'Gtk.Type']:
-            self._transformer.log_("Warning: *_get_type function returns '%r'"
-                   ", not GObject.Type") % (func.retval.type.name, )
-            return False
-
-        self._get_type_functions.append(symbol)
-        return True
-
-    def _parse_error_quark_function(self, func):
-        if not func.symbol.endswith('_error_quark'):
-            return False
-        if func.parameters:
-            return False
-        if (func.retval.type.name != 'GLib.Quark' and
-            func.retval.type.ctype != 'GQuark'):
+        rettype = func.retval.type
+        if not (rettype.is_equiv(TYPE_GTYPE)
+                or rettype.target_giname == 'Gtk.Type'):
+            self._transformer.log_warning("function returns '%r', not a GType") % (func.retval.type, )
             return False
 
-        self._error_quark_functions.append(func)
+        self._get_type_functions.append(func.symbol)
         return True
 
-    def _name_is_internal_gtype(self, giname):
-        try:
-            node = self._get_attribute(giname)
-            return isinstance(node, (GLibObject, GLibInterface,
-                                     GLibBoxed, GLibEnum, GLibFlags))
-        except KeyError, e:
-            return False
-
-    def _parse_static_method(self, func):
-        components = func.symbol.split('_')
-        if len(components) < 2:
-            return None
-        target_klass = None
-        prefix_components = None
-        methname = None
-        for i in xrange(1, len(components)):
-            prefix_components = '_'.join(components[0:-i])
-            methname = '_'.join(components[-i:])
-            target_klass = self._uscore_type_names.get(prefix_components)
-            if target_klass and isinstance(target_klass, GLibObject):
-                break
-            target_klass = None
-        if not target_klass:
-            return None
-        self._remove_attribute(func.name)
-        func.name = methname
-        target_klass.static_methods.append(func)
-        func.is_method = True
-        return func
-
-    def _parse_method(self, func):
-        if not func.parameters:
-            return False
-        return self._parse_method_common(func, True)
-
-    def _parse_constructor(self, func):
-        return self._parse_method_common(func, False)
-
-    def _parse_method_common(self, func, is_method):
-        # Skip _get_type functions, we processed them
-        # already
-        if func.symbol.endswith('_get_type'):
-            return None
-
-        if not is_method:
-            target_arg = func.retval
-        else:
-            target_arg = func.parameters[0]
-
-        if is_method:
-            # Methods require their first arg to be a known class
-            # Look at the original C type (before namespace stripping), without
-            # pointers: GtkButton -> gtk_button_, so we can figure out the
-            # method name
-            argtype = target_arg.type.ctype.replace('*', '')
-            name = self._transformer.remove_prefix(argtype)
-            name_uscore = to_underscores_noprefix(name).lower()
-            # prefer the prefix of the _get_type method, if there is one
-            if argtype in self._names.type_names:
-                node = self._names.type_names[argtype][1]
-                if hasattr(node, 'get_type'):
-                    name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
-                                                         node.get_type)
-                    name_uscore = name_uscore[:-len('_get_type')]
-            name_offset = func.symbol.find(name_uscore + '_')
-            if name_offset < 0:
-                return None
-            prefix = func.symbol[:name_offset+len(name_uscore)]
-        else:
-            # Constructors must have _new
-            # Take everything before that as class name
-            new_idx = func.symbol.find('_new')
-            if new_idx < 0:
-                return None
-            # Constructors don't return basic types
-            derefed = self._transformer.follow_aliases(target_arg.type.name,
-                                                       self._names)
-            if derefed in type_names:
-                #print "NOTE: Rejecting constructor returning basic: %r" \
-                #    % (func.symbol, )
-                return None
-            prefix = func.symbol[:new_idx]
-
-        klass = self._uscore_type_names.get(prefix)
-        if klass is None:
-            #print "NOTE: No valid matching class for likely "+\
-            #    "method or constructor: %r" % (func.symbol, )
-            return None
-        # Enums can't have ctors or methods
-        if isinstance(klass, (GLibEnum, GLibFlags)):
-            return None
-
-        # The _uscore_type_names member holds the plain GLibBoxed
-        # object; we want to actually use the struct/record associated
-        if isinstance(klass, (Record, Union)):
-            remove_prefix = klass.symbol
-        else:
-            remove_prefix = klass.type_name
-
-        name = self._transformer.remove_prefix(remove_prefix)
-        klass = self._get_attribute(name)
-        if klass is None:
-            return
-
-        if not is_method:
-            # Interfaces can't have constructors, punt to global scope
-            if isinstance(klass, GLibInterface):
-                #print "NOTE: Rejecting constructor for"+\
-                #    " interface type: %r" % (func.symbol, )
-                return None
-            # TODO - check that the return type is a subclass of the
-            # class from the prefix
-            # But for now, ensure that constructor returns are always
-            # the most concrete class
-            name = self._transformer.remove_prefix(remove_prefix)
-            func.retval.type = Type(name, func.retval.type.ctype)
-
-        self._remove_attribute(func.name)
-        # Strip namespace and object prefix: gtk_window_new -> new
-        func.name = func.symbol[len(prefix)+1:]
-        if is_method:
-            # We don't need the "this" parameter
-            del func.parameters[0]
-            klass.methods.append(func)
-            func.is_method = True
-        else:
-            klass.constructors.append(func)
-        return func
-
-    def _parse_record(self, record):
-        # This is a hack, but GObject is a rather fundamental piece so.
-        internal_names = ["Object", 'InitiallyUnowned']
-        g_internal_names = ["G" + x for x in internal_names]
-        if (self._namespace_name == 'GObject' and
-            record.name in internal_names):
+    def _initparse_gobject_record(self, record):
+        # Special handling for when we're parsing GObject
+        internal_names = ("Object", 'InitiallyUnowned')
+        if record.name in internal_names:
             self._create_gobject(record)
             return
-        elif record.name in g_internal_names:
-            # Avoid duplicates
-            return
         if record.name == 'InitiallyUnownedClass':
-            record.fields = self._names.names['ObjectClass'][1].fields
-        node = self._names.names.get(record.name)
-        if node is None:
-            self._add_attribute(record, replace=True)
-            self._register_internal_type(record.symbol, record)
-            return
-        (ns, node) = node
-        node.fields = record.fields[:]
-
-    def _parse_union(self, union):
-        node = self._names.names.get(union.name)
-        if node is None:
-            self._add_attribute(union, replace=True)
-            self._register_internal_type(union.symbol, union)
-            return
-        (ns, node) = node
-        node.fields = union.fields[:]
+            record.fields = self._namespace.get('ObjectClass').fields
+        self._namespace.append(record, replace=True)
 
-    def _parse_callback(self, callback):
-        self._add_attribute(callback)
-
-    def _strip_class_suffix(self, name):
-        if (name.endswith('Class') or
-            name.endswith('Iface')):
-            return name[:-5]
-        elif name.endswith('Interface'):
-            return name[:-9]
-        else:
-            return name
-
-    def _arg_is_failed(self, param):
-        ctype = self._transformer.ctype_of(param).replace('*', '')
-        uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
-        if uscored in self._failed_types:
-            print "Warning: failed type: %r" % (param, )
-            return True
-        return False
-
-    def _pair_class_record(self, maybe_class):
-        name = self._strip_class_suffix(maybe_class.name)
-        if name == maybe_class.name:
-            return
-
-        class_struct = maybe_class
-        if self._arg_is_failed(class_struct):
-            print "WARNING: deleting no-type %r" % (class_struct.name, )
-            del self._names.names[class_struct.name]
-            return
-
-        pair_class = self._get_attribute(name)
-        if (not pair_class or
-            not isinstance(pair_class, (GLibObject, GLibInterface))):
-            return
-
-        # Object class fields are assumed to be read-only
-        # (see also _introspect_object and transformer.py)
-        for field in maybe_class.fields:
-            if isinstance(field, Field):
-                field.writable = False
-
-        # Loop through fields to determine which are virtual
-        # functions and which are signal slots by
-        # assuming everything that doesn't share a name
-        # with a known signal is a virtual slot.
-        for field in maybe_class.fields:
-            if not isinstance(field, Callback):
-                continue
-            # Check the first parameter is the object
-            if len(field.parameters) == 0:
-                continue
-            firstparam_type = field.parameters[0].type
-            if firstparam_type != pair_class:
-                continue
-            # Also double check we don't have a signal with this
-            # name.
-            matched_signal = False
-            for signal in pair_class.signals:
-                if signal.name.replace('-', '_') == field.name:
-                    matched_signal = True
-                    break
-            if matched_signal:
-                continue
-            vfunc = VFunction.from_callback(field)
-            vfunc.inherit_file_positions(field)
-            pair_class.virtual_methods.append(vfunc)
-
-        # Take the set of virtual methods we found, and try
-        # to pair up with any matching methods using the
-        # name+signature.
-        for vfunc in pair_class.virtual_methods:
-            for method in pair_class.methods:
-                if (method.name != vfunc.name or
-                    method.retval != vfunc.retval or
-                    method.parameters != vfunc.parameters):
-                    continue
-                vfunc.invoker = method
-
-        gclass_struct = GLibRecord.from_record(class_struct)
-        self._remove_attribute(class_struct.name)
-        self._add_attribute(gclass_struct, True)
-        pair_class.glib_type_struct = gclass_struct
-        pair_class.inherit_file_positions(class_struct)
-        gclass_struct.is_gtype_struct_for = name
-
-    # Introspection
+    # Introspection over the data we get from the dynamic
+    # GObject/GType system out of the binary
 
     def _introspect_type(self, xmlnode):
         if xmlnode.tag in ('enum', 'flags'):
@@ -709,8 +257,7 @@ class GLibTransformer(object):
         type_name = node.attrib['name']
         enum_name = self._transformer.remove_prefix(type_name)
         node = klass(enum_name, type_name, members, node.attrib['get-type'])
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
 
     def _introspect_object(self, xmlnode):
         type_name = xmlnode.attrib['name']
@@ -719,58 +266,47 @@ class GLibTransformer(object):
         # to skip it
         if type_name == 'GObject':
             return
-        # Get a list of parents here; some of them may be hidden, and what
-        # we really want to do is use the most-derived one that we know of.
-        #
-        parent_type_names = xmlnode.attrib['parents'].split(',')
-        parent_gitype = self._resolve_gtypename_chain(parent_type_names)
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
-        node = GLibObject(
-            self._transformer.remove_prefix(type_name),
-            parent_gitype,
-            type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+        node = GLibObject(self._transformer.remove_prefix(type_name),
+                          None,
+                          type_name,
+                          xmlnode.attrib['get-type'], is_abstract)
+        self._parse_parents(xmlnode, node)
         self._introspect_properties(node, xmlnode)
         self._introspect_signals(node, xmlnode)
         self._introspect_implemented_interfaces(node, xmlnode)
 
         self._add_record_fields(node)
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
 
     def _introspect_interface(self, xmlnode):
         type_name = xmlnode.attrib['name']
-        node = GLibInterface(
-            self._transformer.remove_prefix(type_name),
-            None,
-            type_name, xmlnode.attrib['get-type'])
+        node = GLibInterface(self._transformer.remove_prefix(type_name),
+                             None,
+                             type_name, xmlnode.attrib['get-type'])
         self._introspect_properties(node, xmlnode)
         self._introspect_signals(node, xmlnode)
         for child in xmlnode.findall('prerequisite'):
             name = child.attrib['name']
-            prereq = self._resolve_gtypename(name)
+            prereq = self._transformer.create_type(name)
             node.prerequisites.append(prereq)
         # GtkFileChooserEmbed is an example of a private interface, we
         # just filter them out
         if xmlnode.attrib['get-type'].startswith('_'):
-            print "NOTICE: Marking %s as internal type" % (type_name, )
             self._private_internal_types[type_name] = node
         else:
-            self._add_attribute(node, replace=True)
-            self._register_internal_type(type_name, node)
+            self._namespace.append(node, replace=True)
 
     def _introspect_boxed(self, xmlnode):
         type_name = xmlnode.attrib['name']
         # This one doesn't go in the main namespace; we associate it with
         # the struct or union
         node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
-        self._boxed_types[node.type_name] = node
-        self._register_internal_type(type_name, node)
 
     def _introspect_implemented_interfaces(self, node, xmlnode):
         gt_interfaces = []
         for interface in xmlnode.findall('implements'):
-            gitype = self._resolve_gtypename(interface.attrib['name'])
+            gitype = self._transformer.create_type(interface.attrib['name'])
             gt_interfaces.append(gitype)
         node.interfaces = sorted(gt_interfaces)
 
@@ -784,7 +320,7 @@ class GLibTransformer(object):
             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
             node.properties.append(Property(
                 pspec.attrib['name'],
-                type_name_from_ctype(ctype),
+                self._transformer.create_type(ctype),
                 readable, writable, construct, construct_only,
                 ctype,
                 ))
@@ -793,23 +329,30 @@ class GLibTransformer(object):
     def _introspect_signals(self, node, xmlnode):
         for signal_info in xmlnode.findall('signal'):
             rctype = signal_info.attrib['return']
-            rtype = Type(self._transformer.parse_ctype(rctype), rctype)
-            return_ = Return(rtype, signal_info.attrib['return'])
-            return_.transfer = PARAM_TRANSFER_FULL
+            rtype = self._transformer.create_type(rctype)
+            return_ = Return(rtype, PARAM_TRANSFER_FULL)
             signal = GLibSignal(signal_info.attrib['name'], return_)
             for i, parameter in enumerate(signal_info.findall('param')):
                 if i == 0:
-                    name = 'object'
+                    argname = 'object'
                 else:
-                    name = 'p%s' % (i-1, )
+                    argname = 'p%s' % (i-1, )
                 pctype = parameter.attrib['type']
-                ptype = Type(self._transformer.parse_ctype(pctype), pctype)
-                param = Parameter(name, ptype)
+                ptype = self._transformer.create_type(pctype)
+                param = Parameter(argname, ptype)
                 param.transfer = 'none'
                 signal.parameters.append(param)
             node.signals.append(signal)
         node.signals = sorted(node.signals)
 
+    def _parse_parents(self, xmlnode, node):
+        if 'parents' in xmlnode.attrib:
+            parent_types = map(lambda s: self._transformer.create_type(s),
+                               xmlnode.attrib['parents'].split(','))
+        else:
+            parent_types = []
+        node.parent_chain = parent_types
+
     def _introspect_fundamental(self, xmlnode):
         # We only care about types that can be instantiatable, other
         # fundamental types such as the Clutter.Fixed/CoglFixed registers
@@ -819,30 +362,21 @@ class GLibTransformer(object):
             return
 
         type_name = xmlnode.attrib['name']
-
-        # Get a list of parents here; some of them may be hidden, and what
-        # we really want to do is use the most-derived one that we know of.
-        if 'parents' in xmlnode.attrib:
-            parent_type_names = xmlnode.attrib['parents'].split(',')
-            parent_gitype = self._resolve_gtypename_chain(parent_type_names)
-        else:
-            parent_gitype = None
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
-        node = GLibObject(
-            self._transformer.remove_prefix(type_name),
-            parent_gitype,
-            type_name,
-            xmlnode.attrib['get-type'], is_abstract)
+        node = GLibObject(self._transformer.remove_prefix(type_name),
+                          None,
+                          type_name,
+                          xmlnode.attrib['get-type'], is_abstract)
+        self._parse_parents(xmlnode, node)
         node.fundamental = True
         self._introspect_implemented_interfaces(node, xmlnode)
 
         self._add_record_fields(node)
-        self._add_attribute(node, replace=True)
-        self._register_internal_type(type_name, node)
+        self._namespace.append(node, replace=True)
 
     def _add_record_fields(self, node):
         # add record fields
-        record = self._get_attribute(node.name)
+        record = self._namespace.get(node.name)
         if record is None:
             return
         node.fields = record.fields
@@ -854,7 +388,7 @@ class GLibTransformer(object):
 
     def _pair_boxed_type(self, boxed):
         name = self._transformer.remove_prefix(boxed.type_name)
-        pair_node = self._get_attribute(name)
+        pair_node = self._namespace.get(name)
         if not pair_node:
             boxed_item = GLibBoxedOther(name, boxed.type_name,
                                         boxed.get_type)
@@ -870,336 +404,31 @@ class GLibTransformer(object):
             boxed_item.fields = pair_node.fields
         else:
             return False
-        self._add_attribute(boxed_item, replace=True)
+        self._namespace.append(boxed_item, replace=True)
 
-    # Node walking
+    def _strip_class_suffix(self, name):
+        if (name.endswith('Class') or
+            name.endswith('Iface')):
+            return name[:-5]
+        elif name.endswith('Interface'):
+            return name[:-9]
+        else:
+            return name
 
-    def _walk(self, node, callback, chain):
-        if not isinstance(node, Node):
-            return
-        if not callback(node, chain):
+    def _pair_class_record(self, maybe_class):
+        name = self._strip_class_suffix(maybe_class.name)
+        if name == maybe_class.name:
             return
-        chain.append(node)
-        def _subwalk(subnode):
-            self._walk(subnode, callback, chain)
-        if isinstance(node, (Callback, Callable)):
-            _subwalk(node.retval)
-            for parameter in node.parameters:
-                _subwalk(parameter)
-        elif isinstance(node, (Array, List)):
-            _subwalk(node.element_type)
-        elif isinstance(node, Map):
-            _subwalk(node.key_type)
-            _subwalk(node.value_type)
-        elif isinstance(node, Bitfield):
-            pass
-        elif isinstance(node, Record):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for func in node.methods:
-                _subwalk(func)
-        elif isinstance(node, Field):
-            _subwalk(node.type)
-        elif isinstance(node, Class):
-            for meth in node.methods:
-                _subwalk(meth)
-            for meth in node.virtual_methods:
-                _subwalk(meth)
-            for meth in node.static_methods:
-                _subwalk(meth)
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for prop in node.properties:
-                _subwalk(prop)
-            for field in node.fields:
-                _subwalk(field)
-        elif isinstance(node, Interface):
-            for meth in node.methods:
-                _subwalk(meth)
-            for meth in node.virtual_methods:
-                _subwalk(meth)
-            for prop in node.properties:
-                _subwalk(prop)
-            for field in node.fields:
-                _subwalk(field)
-        elif isinstance(node, Constant):
-            _subwalk(node.type)
-        elif isinstance(node, Union):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for meth in node.methods:
-                _subwalk(meth)
-        elif isinstance(node, GLibBoxed):
-            for ctor in node.constructors:
-                _subwalk(ctor)
-            for meth in node.methods:
-                _subwalk(meth)
-
-        if isinstance(node, (GLibObject, GLibInterface)):
-            for sig in node.signals:
-                _subwalk(sig)
-
-        chain.pop()
-
-    # Resolver
-
-    def _resolve_type_name(self, type_name, ctype=None):
-        # Workaround glib bug #548689, to be included in 2.18.0
-        if type_name == "GParam":
-            type_name = "GObject.ParamSpec"
-        res = self._transformer.resolve_type_name_full
-        try:
-            return res(type_name, ctype, self._names)
-        except KeyError, e:
-            return self._transformer.resolve_type_name(type_name, ctype)
-
-    def _resolve_param_type(self, ptype, **kwargs):
-        # Workaround glib bug #548689, to be included in 2.18.0
-        if ptype.name == "GParam":
-            ptype.name = "GObject.ParamSpec"
-        elif ptype.name == "GObject.Strv":
-            return Array(None, ptype.ctype, Type('utf8'))
-
-        return self._transformer.resolve_param_type_full(ptype,
-                                                         self._names,
-                                                         **kwargs)
-
-    def _resolve_node(self, node):
-        if isinstance(node, Function):
-            self._resolve_function_toplevel(node)
-
-        elif isinstance(node, Callback):
-            self._resolve_function(node)
-        elif isinstance(node, GLibObject):
-            self._resolve_glib_object(node)
-        elif isinstance(node, GLibInterface):
-            self._resolve_glib_interface(node)
-        elif isinstance(node, Record):
-            self._resolve_record(node)
-        elif isinstance(node, Union):
-            self._resolve_union(node)
-        elif isinstance(node, Alias):
-            self._resolve_alias(node)
 
-    def _resolve_function_toplevel(self, func):
-        for parser in [self._parse_constructor,
-                       self._parse_method,
-                       self._parse_static_method]:
-            newfunc = parser(func)
-            if newfunc:
-                self._resolve_function(newfunc)
-                return
-        self._resolve_function(func)
-
-    def _resolve_record(self, node):
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _resolve_union(self, node):
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _force_resolve(self, item, allow_unknown=False):
-        if isinstance(item, Unresolved):
-            if item.target in self._private_internal_types:
-                return None
-            try:
-                return self._transformer.gtypename_to_giname(item.target,
-                                                             self._names)
-            except KeyError, e:
-                if allow_unknown:
-                    self._transformer.log_warning(
-"""Skipping unknown interface %s""" % (item.target, ))
-                    return None
-                else:
-                    raise
-        if item in self._private_internal_types:
-            return None
-        return item
-
-    def _resolve_glib_interface(self, node):
-        node.parent = self._force_resolve(node.parent)
-        self._resolve_methods(node.methods)
-        self._resolve_properties(node.properties, node)
-        self._resolve_signals(node.signals)
-        node.prerequisites = filter(None,
-            [self._force_resolve(x, allow_unknown=True)
-             for x in node.prerequisites])
-
-    def _resolve_glib_object(self, node):
-        # If we can't find the parent class, just drop back to GObject.
-        # This supports hidden parent classes.
-        # http://bugzilla.gnome.org/show_bug.cgi?id=561360
-        try:
-            node.parent = self._force_resolve(node.parent)
-        except KeyError, e:
-            #print ("WARNING: Parent %r of class %r" +\
-            #       " not found; using GObject") % (node.parent.target,
-            #                                       node.name)
-            node.parent = self._transformer.gtypename_to_giname("GObject",
-                                                                self._names)
-        node.interfaces = filter(None,
-            [self._force_resolve(x, allow_unknown=True)
-                                    for x in node.interfaces])
-        self._resolve_constructors(node.constructors)
-        self._resolve_methods(node.methods)
-        self._resolve_methods(node.static_methods)
-        self._resolve_properties(node.properties, node)
-        self._resolve_signals(node.signals)
-        for field in node.fields:
-            self._resolve_field(field)
-
-    def _resolve_glib_boxed(self, node):
-        self._resolve_constructors(node.constructors)
-        self._resolve_methods(node.methods)
-
-    def _resolve_constructors(self, constructors):
-        for ctor in constructors:
-            self._resolve_function(ctor)
-
-    def _resolve_methods(self, methods):
-        for method in methods:
-            self._resolve_function(method)
-
-    def _resolve_signals(self, signals):
-        for signal in signals:
-            self._resolve_function(signal)
-
-    def _resolve_properties(self, properties, context):
-        failed = []
-        for prop in properties:
-            try:
-                self._resolve_property(prop)
-            except KeyError, e:
-                failed.append(prop)
-        for fail in failed:
-            #print ("WARNING: Deleting object property %r (of %r) "
-            #       "with unknown type") % (fail, context)
-            properties.remove(fail)
-
-    def _resolve_property(self, prop):
-        prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
-
-    def _adjust_throws(self, func):
-        if func.parameters == []:
+        class_struct = maybe_class
+        pair_class = self._namespace.get(name)
+        if not pair_class:
+            return
+        if not isinstance(pair_class, (GLibObject, GLibInterface)):
             return
 
-        last_param = func.parameters.pop()
-
-        # Checking type.name=='GLib.Error' generates false positives
-        # on methods that take a 'GError *'
-        if last_param.type.ctype == 'GError**':
-            func.throws = True
-        else:
-            func.parameters.append(last_param)
-
-    def _resolve_function(self, func):
-        self._resolve_parameters(func.parameters)
-        func.retval.type = self._resolve_param_type(func.retval.type)
-        self._adjust_throws(func)
-
-    def _resolve_parameters(self, parameters):
-        for parameter in parameters:
-            parameter.type = self._resolve_param_type(parameter.type)
-
-    def _resolve_field(self, field):
-        if isinstance(field, Callback):
-            self._resolve_function(field)
-        elif isinstance(field, Record): # non-typedef'd struct
-            self._resolve_record(field)
-        elif isinstance(field, Union): # non-typedef'd union
-            self._resolve_union(field)
-        else:
-            field.type = self._resolve_param_type(field.type)
-
-    def _resolve_alias(self, alias):
-        alias.target = self._resolve_type_name(alias.target, alias.target)
-
-    def _resolve_types(self, nodes):
-        nodes = list(self._names.names.itervalues())
-        i = 0
-        self._validating = True
-        while True:
-            initlen = len(nodes)
-
-            nodes = list(self._names.names.itervalues())
-            for node in nodes:
-                try:
-                    self._resolve_node(node)
-                except UnknownTypeError, e:
-                    print "WARNING: %s: Deleting %r" % (e, node)
-                    self._remove_attribute(node.name)
-            if len(nodes) == initlen:
-                break
-            i += 1
-        self._validating = False
-
-    # Validation
-
-    def _interface_vfunc_check(self, node, stack):
-        if isinstance(node, GLibInterface):
-            for vfunc in node.virtual_methods:
-                if not vfunc.invoker:
-                    self._transformer.log_node_warning(vfunc,
-"""Virtual function %r has no known invoker""" % (vfunc.name, ),
-                    context=node)
-
-    def _is_unannotated_list(self, node):
-        # Already annotated
-        if isinstance(node.type, List):
-            return False
-        if (node.type.name == 'GLib.List' or
-            node.type.name == 'GLib.SList'):
-            return True
-        if (self._transformer._namespace.name == 'GLib' and
-            (node.type.name == 'List' or
-             node.type.name == 'SList')):
-            return True
-        return False
-
-    def _introspectable_analysis(self, node, stack):
-        if isinstance(node, TypeContainer):
-            parent = stack[-1]
-            if node.type.name in (TYPE_LONG_LONG, TYPE_LONG_DOUBLE):
-                parent.introspectable = False
-            elif isinstance(node.type, Varargs):
-                parent.introspectable = False
-            elif self._is_unannotated_list(node):
-                if isinstance(node, Parameter):
-                    self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on argument %r""" % (node.name, ),
-                                                    context=parent)
-                else:
-                    self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on return value""", context=parent)
-                parent.introspectable = False
-
-    def _analyze_node(self, node, stack):
-        if node.skip:
-            return False
-        # Combine one-pass checks here
-        self._interface_vfunc_check(node, stack)
-        # Our first pass for scriptability
-        self._introspectable_analysis(node, stack)
-        return True
-
-    def _introspectable_pass2(self, node, stack):
-        if node.skip:
-            return False
-        # In the second introspectable pass, we propagate introspectablity;
-        # for example, a varargs callback as an argument to a function
-        # makes the whole function unintrospectable
-        if isinstance(node, TypeContainer):
-            parent = stack[-1]
-            target = self._lookup_node(node.type.name)
-            if target and not target.introspectable:
-                parent.introspectable = False
-        return True
-
-    # This function is called at the very end, before we hand back the
-    # completed namespace to the writer.  Add static analysis checks here.
-    def final_analyze(self):
-        for (ns, node) in self._names.names.itervalues():
-            self._walk(node, self._analyze_node, [])
-        for (ns, node) in self._names.names.itervalues():
-            self._walk(node, self._introspectable_pass2, [])
+        gclass_struct = GLibRecord.from_record(class_struct)
+        self._namespace.append(gclass_struct, replace=True)
+        pair_class.glib_type_struct = gclass_struct.create_type()
+        pair_class.inherit_file_positions(class_struct)
+        gclass_struct.is_gtype_struct_for = pair_class.create_type()
diff --git a/giscanner/primarytransformer.py b/giscanner/primarytransformer.py
new file mode 100644
index 0000000..8b8b2c5
--- /dev/null
+++ b/giscanner/primarytransformer.py
@@ -0,0 +1,820 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import *
+from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
+                               TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
+                               TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
+                               TAG_GET_VALUE_FUNC)
+from .annotationparser import (OPT_ALLOW_NONE,
+                               OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
+                               OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE, OPT_TRANSFER,
+                               OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
+                               OPT_FOREIGN, OPT_VAL_BITFIELD, OPT_ARRAY_FIXED_SIZE,
+                               OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
+                               OPT_SCOPE_ASYNC, OPT_SCOPE_CALL, OPT_SCOPE_NOTIFIED)
+from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
+                      GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
+                      GLibBoxedUnion, GLibBoxedOther, GLibRecord)
+from .utils import to_underscores, to_underscores_noprefix
+
+class PrimaryTransformer(object):
+
+    def __init__(self, transformer, blocks):
+        self._transformer = transformer
+        self._blocks = blocks
+        self._namespace = transformer.namespace
+        self._uscore_type_names = {}
+
+    # Public API
+
+    def transform(self):
+        self._namespace.walk(self._pass_read_annotations)
+        
+        # We have a rough tree which should have all
+        # of the types we know about.  Let's attempt closure; walk
+        # over all of the Type() types and see if they match up
+        # with something.
+        self._namespace.walk(self._pass_type_resolution)
+
+        # Generate a reverse mapping "bar_baz" -> BarBaz
+        for node in self._namespace.itervalues():
+            uscored = to_underscores_noprefix(node.name).lower()
+            if isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+                self._uscore_type_names[uscored] = node
+
+        for node in list(self._namespace.itervalues()):
+            if isinstance(node, Function):
+                # Discover which toplevel functions are actually methods
+                self._pair_function(node)
+            if isinstance(node, (Class, Interface)):
+                self._pair_class_virtuals(node)
+
+        # Another type resolution pass after we've parsed virtuals, etc.
+        self._namespace.walk(self._pass_type_resolution)
+
+        self._namespace.walk(self._pass3)
+
+        # TODO - merge into pass3
+        self._resolve_quarks()
+
+    # Private
+
+    def _get_no_uscore_prefixed_name(self, type_name):
+        # Besides the straight underscore conversion, we also try
+        # removing the underscores from the namespace as a possible C
+        # mapping; e.g. it's webkit_web_view, not web_kit_web_view
+        suffix = self._transformer.remove_prefix(type_name)
+        prefix = type_name[:-len(suffix)]
+        return (prefix + '_' + to_underscores(suffix)).lower()
+
+    def _get_parameter_index(self, parent, param_name, origin):
+        index = parent.get_parameter_index(param_name)
+        if index is None:
+            if isinstance(origin, Parameter):
+                origin_name = 'parameter %s' % (origin.argname, )
+            else:
+                origin_name = 'return value'
+            self._transformer.log_node_warning(parent, 
+                "can't find parameter %s referenced by %s of %r"
+                % (param_name, origin_name, parent.name), fatal=True)
+
+        return index
+
+    def _pass_read_annotations(self, node, chain):
+        if isinstance(node, Function):
+            block = self._blocks.get(node.symbol)
+            self._apply_annotations_callable(node, chain, block)
+        if isinstance(node, (Class, Interface, Record, Union, Enum, Bitfield,
+                             Callback)):
+            block = self._blocks.get(node.c_name)
+            self._apply_annotations_annotated(node, block)    
+        if isinstance(node, (Class, Interface, Record, Union)):
+            for field in node.fields:
+                self._blocks.get('%s::%s' % (node.c_name, field.name))
+                self._apply_annotations_field(node, block, field)
+        if isinstance(node, (Class, Interface)):
+            for prop in node.properties:
+                self._apply_annotations_property(node, prop)
+            for sig in node.signals:
+                self._apply_annotations_signal(node, sig)
+        return True
+
+    def _fixup_param_destroy(self, parent, param):
+        for p in parent.parameters:
+            if p is not param and p.destroy_index == param.destroy_index:
+                p.destroy_index = -1
+
+    def _fixup_param_closure(self, parent, param):
+        for p in parent.parameters:
+            if p is not param and p.closure_index == param.closure_index:
+                p.closure_index = -1
+
+    def _adjust_container_type(self, parent, node, options):
+        has_element_type = OPT_ELEMENT_TYPE in options
+        has_array = OPT_ARRAY in options
+
+        if has_array:
+            self._apply_annotations_array(parent, node, options)
+        elif has_element_type:
+            self._apply_annotations_element_type(parent, node, options)
+
+    def _extract_direction(self, node, options):
+        caller_allocates = False
+        if (OPT_INOUT in options or
+            OPT_INOUT_ALT in options):
+            direction = PARAM_DIRECTION_INOUT
+        elif OPT_OUT in options:
+            subtype = options[OPT_OUT]
+            if subtype is not None:
+                subtype = subtype.one()
+            direction = PARAM_DIRECTION_OUT
+            if subtype in (None, ''):
+                if node.type.target_giname and node.type.ctype:
+                    caller_allocates = '**' not in node.type.ctype
+                else:
+                    caller_allocates = False
+            elif subtype == 'caller-allocates':
+                caller_allocates = True
+            elif subtype == 'callee-allocates':
+                caller_allocates = False
+            else:
+                raise InvalidAnnotationError(
+                    "out allocation for %s is invalid (%r)" % (node, subtype))
+        elif OPT_IN in options:
+            direction = PARAM_DIRECTION_IN
+        else:
+            direction = node.direction
+        return (direction, caller_allocates)
+
+    def _resolve(self, type_str, orig_node=None):
+        def grab_one(type_str, resolver, top_combiner, combiner):
+            """Return a complete type, and the trailing string part after it.
+            Use resolver() on each identifier, and combiner() on the parts of
+            each complete type. (top_combiner is used on the top-most type.)"""
+            bits = re.split(r'([,<>])', type_str, 1)
+            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+            args = [resolver(first)]
+            if sep == '<':
+                while sep != '>':
+                    next, rest = grab_one(rest, resolver, combiner, combiner)
+                    args.append(next)
+                    sep, rest = rest[0], rest[1:]
+            else:
+                rest = sep + rest
+            return top_combiner(*args), rest
+        def resolver(ident):
+            return self._transformer.create_and_resolve_type(ident)
+        def combiner(base, *rest):
+            if not rest:
+                return base
+            if (base.name in ['GLib.List', 'GLib.SList'] or
+                base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
+                return List(base.name, base.ctype, *rest)
+            if (base.name in ['GLib.HashTable'] or
+                base.ctype in ['GHashTable*']) and len(rest)==2:
+                return Map(base.name, base.ctype, *rest)
+            print "WARNING: throwing away type parameters:", type_str
+            return base
+        def top_combiner(base, *rest):
+            """For the top most type, recycle orig_node if possible."""
+            if orig_node is not None:
+                orig_node.name = base.name
+                base = orig_node # preserve other properties of orig_node
+            return combiner(base, *rest)
+
+        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+        if rest:
+            print "WARNING: throwing away trailing part of type:", type_str
+        return result
+
+    def _apply_annotations_array(self, parent, node, options):
+        array_opt = options.get(OPT_ARRAY)
+        if array_opt:
+            array_values = array_opt.all()
+        else:
+            array_values = {}
+
+        element_type = options.get(OPT_ELEMENT_TYPE)
+        if element_type is not None:
+            element_type_node = self._resolve(element_type.one())
+        else:
+            # We're assuming here that Foo* with an (array) annotation
+            # and no (element-type) means array of Foo
+            element_type_node = node.type.clone()
+            # Explicitly erase ctype since it's no longer valid
+            element_type_node.ctype = None
+
+        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)
+            container_type.length_param_index = param_index
+            # For in parameters we're incorrectly deferring
+            # char/unsigned char to utf8 when a length annotation
+            # is specified.
+            if (isinstance(node, Parameter) and
+                node.type.is_equiv(TYPE_STRING) and
+                node.direction == PARAM_DIRECTION_IN and
+                element_type is None):
+                # FIXME: unsigned char/guchar should be uint8
+                container_type.element_type = TYPE_UINT8
+        fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
+        if fixed:
+            container_type.size = int(fixed)
+        node.type = container_type
+
+    def _apply_annotations_element_type(self, parent, node, options):
+        element_type_opt = options.get(OPT_ELEMENT_TYPE)
+        element_type = element_type_opt.flat()
+        if isinstance(node.type, List):
+            assert len(element_type) == 1
+            node.element_type = self._resolve(element_type[0])
+        elif isinstance(node.type, Map):
+            assert len(element_type) == 2
+            node.type.key_type = self._resolve(element_type[0])
+            node.type.value_type = self._resolve(element_type[1])
+        elif isinstance(node.type, Array):
+            node.type.element_type = self._resolve(element_type[0]) 
+        else:
+            self._transformer.log_node_warning(parent, "Unknown container for element-type annotation")
+
+    def _get_transfer_default(self, parent, node, options):
+        if node.transfer is not None:
+            return node.transfer
+
+        if node.type in [TYPE_NONE, TYPE_ANY]:
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node.type, Varargs):
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node, Parameter):
+            if node.direction in [PARAM_DIRECTION_INOUT,
+                                  PARAM_DIRECTION_OUT]:
+                if node.caller_allocates:
+                    return PARAM_TRANSFER_NONE
+                return PARAM_TRANSFER_FULL
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node, Return):
+            if (node.type.is_equiv(BASIC_GIR_TYPES) or
+                node.type.is_equiv((TYPE_STRING, TYPE_FILENAME)) or
+                  (node.type.is_equiv([TYPE_NONE, TYPE_ANY]) and
+                   node.type.is_const)):
+                return PARAM_TRANSFER_NONE
+            elif node.type.target_fundamental:
+                # This looks like just GType right now
+                return None
+            if node.type.target_giname:
+                target = self._transformer.lookup_typenode(node.type)
+                if isinstance(target, GLibBoxed):
+                    return PARAM_TRANSFER_FULL
+                if isinstance(target, (Enum, Bitfield)):
+                    return PARAM_TRANSFER_NONE
+                if isinstance(target, (Class, Record, Union)):
+                    # Explicitly no default for these
+                    return None
+                else:
+                    return None
+        elif isinstance(node, Field):
+            return PARAM_TRANSFER_NONE
+        elif isinstance(node, Property):
+            return PARAM_TRANSFER_NONE
+        else:
+            raise AssertionError(node)
+
+    def _apply_annotations_param_ret_common(self, parent, node, tag):
+        options = getattr(tag, 'options', {})
+        (node.direction, node.caller_allocates) = \
+            self._extract_direction(node, options)
+        node.transfer = self._get_transfer_default(parent, node, options)
+        self._adjust_container_type(parent, node, options)
+        param_type = options.get(OPT_TYPE)
+        if param_type:
+            node.type = self._resolve(param_type.one(), node.type)
+
+        if (OPT_ALLOW_NONE in options or
+            node.type.target_giname == 'Gio.Cancellable'):
+            node.allow_none = True
+
+        if tag is not None and tag.comment is not None:
+            node.doc = tag.comment
+
+        for key in options:
+            if '.' in key:
+                value = options.get(key)
+                if value:
+                    node.attributes.append((key, value.one()))
+
+    def _apply_annotations_annotated(self, node, block):
+        if block is None:
+            return
+
+        node.doc = block.comment
+
+        since_tag = block.get(TAG_SINCE)
+        if since_tag is None:
+            return
+        node.version = since_tag.value
+
+        deprecated_tag = block.get(TAG_DEPRECATED)
+        if deprecated_tag is None:
+            return
+        value = deprecated_tag.value
+        if ': ' in value:
+            version, desc = value.split(': ')
+        else:
+            desc = value
+            version = None
+        node.deprecated = desc
+        if version is not None:
+            node.deprecated_version = version
+
+        annos_tag = block.get(TAG_ATTRIBUTES)
+        if annos_tag is None:
+            return
+        options = AnnotationParser.parse_options(annos_tag.value)
+        for key, value in options.iteritems():
+            if value:
+                node.attributes.append((key, value.one()))
+
+        if OPT_SKIP in block.options:
+            node.skip = True
+
+        if OPT_FOREIGN in block.options:
+            node.foreign = True
+
+    def _apply_annotations_param(self, parent, param, tag):
+        options = getattr(tag, 'options', {})
+        if isinstance(parent, Function):
+            scope = options.get(OPT_SCOPE)
+            if scope:
+                scope = scope.one()
+                if scope not in [PARAM_SCOPE_CALL,
+                                 PARAM_SCOPE_ASYNC,
+                                 PARAM_SCOPE_NOTIFIED]:
+                    self._transformer.log_warning(parent, "Invalid scope %r for parameter %r" % (scope, param.name))
+                else:
+                    param.scope = scope
+                    param.transfer = PARAM_TRANSFER_NONE
+
+            destroy = options.get(OPT_DESTROY)
+            if destroy:
+                param.destroy_index = self._get_parameter_index(parent,
+                                                                destroy.one(),
+                                                                param)
+                self._fixup_param_destroy(parent, param)
+            closure = options.get(OPT_CLOSURE)
+            if closure:
+                param.closure_index = self._get_parameter_index(parent,
+                                                                closure.one(),
+                                                                param)
+                self._fixup_param_closure(parent, param)
+        if isinstance(parent, Callback):
+            if OPT_CLOSURE in options:
+                # For callbacks, (closure) appears without an
+                # argument, and tags a parameter that is a closure. We
+                # represent it (weirdly) in the gir and typelib by
+                # setting param.closure_index to its own index.
+                param.closure_index = parent.get_parameter_index(param)
+                self._fixup_param_closure(parent, param)
+
+        self._apply_annotations_param_ret_common(parent, param, tag)
+
+    def _apply_annotations_return(self, parent, return_, block):
+        if not block:
+            return
+        tag = block.get(TAG_RETURNS)
+        self._apply_annotations_param_ret_common(parent, return_, tag)
+
+    def _check_arg_annotations(self, parent, params, block):
+        if block is None:
+            return
+        for tag in block.tags.keys():
+            if tag == TAG_RETURNS:
+                continue
+            for param in params:
+                if param.argname == tag:
+                    break
+            else:
+                self._transformer.log_warning("""Annotation for '%s' refers to unknown argument '%s'""" 
+                                              % (parent.name, tag))
+
+    def _apply_annotations_params(self, parent, params, block):
+        self._check_arg_annotations(parent, params, block)
+        if not block:
+            return
+        for param in params:
+            tag = block.get(param.argname)
+            self._apply_annotations_param(parent, param, tag)
+
+    def _apply_annotations_callable(self, node, chain, block):
+        self._apply_annotations_annotated(node, block)
+        self._apply_annotations_params(node, node.parameters, block)
+        self._apply_annotations_return(node, node.retval, block)
+
+    def _check_arg_annotations(self, parent, params, block):
+        if block is None:
+            return
+        for tag in block.tags.keys():
+            if tag == TAG_RETURNS:
+                continue
+            for param in params:
+                if param.argname == tag:
+                    break
+            else:
+                self._transformer.log_warning("""Annotation for '%s' refers to unknown argument '%s'""" 
+                                              % (parent.name, tag))
+
+    def _apply_annotations_field(self, parent, block, field):
+        if not block:
+            return
+        tag = block.get(field.name)
+        if not tag:
+            return
+        t = tag.options.get('type')
+        if not t:
+            return
+        field.type = self._transformer.create_and_resolve_type(t.one())
+
+    def _apply_annotations_property(self, parent, prop):
+        block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
+        if not block:
+            return
+        self._apply_annotations_annotated(prop, block)
+        transfer_tag = block.get(TAG_TRANSFER)
+        if transfer_tag is not None:
+            options = {OPT_TRANSFER: Option(transfer_tag.value)}
+        else:
+            options = {}
+        prop.transfer = self._get_transfer_default(parent, prop, options)
+
+        type_tag = block.get(TAG_TYPE)
+        if type_tag:
+            prop.type = self._resolve(type_tag.value, prop.type)
+
+    def _apply_annotations_signal(self, parent, signal):
+        block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
+        self._apply_annotations_annotated(signal, block)
+        # We're only attempting to name the signal parameters if
+        # the number of parameter tags (@foo) is the same or greater
+        # than the number of signal parameters
+        if block and len(block.tags) > len(signal.parameters):
+            names = block.tags.items()
+        else:
+            names = []
+        for i, param in enumerate(signal.parameters):
+            if names:
+                name, tag = names[i+1]
+                param.name = name
+                options = getattr(tag, 'options', {})
+                param_type = options.get(OPT_TYPE)
+                if param_type:
+                    param.type = self._resolve(param_type.one(), param.type)
+            else:
+                tag = None
+            self._apply_annotations_param(signal, param, tag)
+        self._apply_annotations_return(signal, signal.retval, block)
+
+    def _pass_type_resolution(self, node, chain):
+        if isinstance(node, Alias):
+            self._transformer.resolve_type(node.target)
+        if isinstance(node, Callable):
+            for parameter in node.parameters:
+                self._transformer.resolve_type(parameter.type)
+            self._transformer.resolve_type(node.retval.type)
+        if isinstance(node, Constant):
+            self._transformer.resolve_type(node.value_type)
+        if isinstance(node, (Class, Interface, Record, Union)):
+            for field in node.fields:
+                if field.anonymous_node:
+                    field.anonymous_node.walk(self._pass_type_resolution, chain)
+                else:
+                    self._transformer.resolve_type(field.type)
+        if isinstance(node, (Class, Interface)):
+            resolved_parent = None
+            for parent in node.parent_chain:
+                try:
+                    self._transformer.resolve_type(parent)
+                except ValueError, e:
+                    continue
+                node.parent = parent
+                break
+            for prop in node.properties:
+                self._transformer.resolve_type(prop.type)
+            for sig in node.signals:
+                for param in sig.parameters:
+                    self._transformer.resolve_type(param.type)
+        if isinstance(node, Class):
+            for iface in node.interfaces:
+                self._transformer.resolve_type(iface)
+        if isinstance(node, Interface):
+            for iface in node.prerequisites:
+                self._transformer.resolve_type(iface)
+        return True
+
+    def _resolve_quarks(self):
+        # self._uscore_type_names is an authoritative mapping of types
+        # to underscored versions, since it is based on get_type() methods;
+        # but only covers enums that are registered as GObject enums.
+        # Create a fallback mapping based on all known enums in this module.
+        uscore_enums = {}
+        for enum in self._namespace.itervalues():
+            if not isinstance(enum, Enum):
+                continue
+            type_name = enum.symbol
+            uscored = to_underscores(type_name).lower()
+
+            uscore_enums[uscored] = enum
+
+            no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
+            if no_uscore_prefixed not in uscore_enums:
+                uscore_enums[no_uscore_prefixed] = enum
+
+        for node in self._namespace.itervalues():
+            if not isinstance(node, Function):
+                continue
+            if node.retval.type.target_giname != 'GLib.Quark':
+                continue
+            short = node.symbol[:-len('_quark')]
+            if short == "g_io_error":
+                # Special case; GIOError was already taken forcing GIOErrorEnum
+                enum = self._names.type_names["GIOErrorEnum"][1]
+            else:
+                enum = self._uscore_type_names.get(short)
+                if enum is None:
+                    enum = uscore_enums.get(short)
+            if enum is not None:
+                enum.error_quark = node.symbol
+            else:
+                self._transformer.log_node_warning(node,
+"""%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
+
+    def _split_uscored_by_type(self, uscored):
+        """'uscored' should be an un-prefixed uscore string.  This
+function searches through the namespace for the longest type which
+prefixes uscored, and returns (type, suffix).  Example, assuming
+namespace Gtk, type is TextBuffer:
+
+_split_uscored_by_type(text_buffer_try_new) -> (Class(TextBuffer), 'try_new')"""
+        node = None
+        count = 0
+        prev_split_count = -1
+        while True:
+            components = uscored.rsplit('_', count)
+            if len(components) == prev_split_count:
+                return None
+            prev_split_count = len(components)
+            type_string = components[0]
+            node = self._uscore_type_names.get(type_string)
+            if node:
+                return (node, '_'.join(components[1:]))
+            count += 1
+
+    def _pair_function(self, func):
+        """Check to see whether a toplevel function should be a
+method or constructor of some type."""
+        if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
+            return
+        (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
+        assert ns == self._namespace
+        if self._pair_constructor(func, subsymbol):
+            return
+        elif self._pair_method(func, subsymbol):
+            return
+        elif self._pair_static_method(func, subsymbol):
+            return
+
+    def _uscored_identifier_for_type(self, typeval):
+        """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
+        name = typeval.get_giname()
+        return to_underscores_noprefix(name).lower()
+
+    def _pair_method(self, func, subsymbol):
+        if not func.parameters:
+            return False
+        first = func.parameters[0]
+        target = self._transformer.lookup_typenode(first.type)
+        if not isinstance(target, (Class, Interface, Record, Union, GLibBoxedOther)):
+            return False
+        uscored = self._uscored_identifier_for_type(first.type)
+        if not subsymbol.startswith(uscored):
+            return False
+        del func.parameters[0]
+        subsym_idx = func.symbol.find(subsymbol)
+        self._namespace.float(func)
+        func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
+        target.methods.append(func)
+        func.is_method = True
+        return True
+
+    def _pair_static_method(self, func, subsymbol):
+        split = self._split_uscored_by_type(subsymbol)
+        if split is None:
+            return False
+        (node, funcname) = split
+        if not isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+            return False
+        self._namespace.float(func)
+        func.name = funcname
+        node.static_methods.append(func)
+
+    def _pair_constructor(self, func, subsymbol):
+        if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
+            return False
+        target = self._transformer.lookup_typenode(func.retval.type)
+        if not isinstance(target, (Class, Record, Union, GLibBoxedOther)):
+            return False
+        new_idx = func.symbol.rfind('_new')
+        assert (new_idx >= 0)
+        prefix = func.symbol[:new_idx]
+        split = self._split_uscored_by_type(subsymbol)
+        if split is None:
+            # TODO - need a e.g. (method) annotation
+            self._transformer.log_node_warning(func,
+                "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
+            return False
+        (origin_node, funcname) = split
+        if isinstance(target, Class):
+            parent = origin_node
+            while parent:
+                if parent == target:
+                    break
+                parent = self._transformer.lookup_typenode(parent.parent)
+                if parent is None:
+                    self._transformer.log_node_warning(func,
+                                                       "Return value is not superclass for constructor; symbol=%r constructed=%r return=%r" % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+                    return False
+        else:
+            if origin_node != target:
+                self._transformer.log_node_warning(func,
+                                                   "Constructor return type mismatch symbol=%r constructed=%r return=%r" % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+                return False
+        self._namespace.float(func)
+        func.name = funcname
+        target.constructors.append(func)
+        return True
+
+    def _pair_class_virtuals(self, node):
+        """Look for virtual methods from the class structure."""
+        if not node.glib_type_struct:
+            self._transformer.log_node_warning(node,
+                "Failed to find class structure for %r" % (node.name, ))
+            return
+
+        node_type = node.create_type()
+        class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
+        
+        # Object class fields are assumed to be read-only
+        # (see also _introspect_object and transformer.py)
+        for field in class_struct.fields:
+            if isinstance(field, Field):
+                field.writable = False
+
+        # Loop through fields to determine which are virtual
+        # functions and which are signal slots by
+        # assuming everything that doesn't share a name
+        # with a known signal is a virtual slot.
+        for field in class_struct.fields:
+            if not isinstance(field.anonymous_node, Callback):
+                continue
+            callback = field.anonymous_node
+            # Check the first parameter is the object
+            if len(callback.parameters) == 0:
+                continue
+            firstparam_type = callback.parameters[0].type
+            if firstparam_type != node_type:
+                continue
+            # Also double check we don't have a signal with this
+            # name.
+            matched_signal = False
+            for signal in node.signals:
+                if signal.name.replace('-', '_') == callback.name:
+                    matched_signal = True
+                    break
+            if matched_signal:
+                continue
+            vfunc = VFunction.from_callback(callback)
+            vfunc.inherit_file_positions(callback)
+            node.virtual_methods.append(vfunc)
+
+        # Take the set of virtual methods we found, and try
+        # to pair up with any matching methods using the
+        # name+signature.
+        for vfunc in node.virtual_methods:
+            for method in node.methods:
+                if method.name != vfunc.name:
+                    continue
+                if method.retval.type != vfunc.retval.type:
+                    continue
+                if len(method.parameters) != len(vfunc.parameters):
+                    continue
+                for i in xrange(len(method.parameters)):
+                    m_type = method.parameters[i].type
+                    v_type = vfunc.parameters[i].type
+                    if m_type != v_type:
+                        continue
+                vfunc.invoker = method.name
+                # Apply any annotations we have from the invoker to
+                # the vfunc
+                block = self._blocks.get(method.symbol)
+                self._apply_annotations_callable(vfunc, [], block)
+
+    def _pass3(self, node, chain):
+        """Pass 3 is after we've loaded GType data and performed type
+        closure."""
+        if isinstance(node, Callable):
+            self._pass3_callable_callbacks(node)
+            self._pass3_callable_throws(node)
+        return True
+
+    def _pass3_callable_callbacks(self, node):
+        """Check to see if we have anything that looks like a
+        callback+user_data+GDestroyNotify set."""
+
+        def _handle_closure(self, param, closure_idx, closure_param):
+            if (closure_param.type.is_equiv(TYPE_ANY) and
+                closure_param.name.endswith('data')):
+                param.closure_name = closure_param.argname
+                param.closure_index = closure_idx
+                return True
+            return False
+
+        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
+                param.scope = PARAM_SCOPE_NOTIFIED
+                param.transfer = PARAM_TRANSFER_NONE
+                return True
+            return False
+
+        params = node.parameters
+        callback_index = -1
+        for i, param in enumerate(params):
+            node = self._transformer.lookup_typenode(param.type)
+            if not isinstance(node, Callback):
+                continue
+            # Special case GAsyncReadyCallback
+            if node.type.target_giname == 'Gio.AsyncReadyCallback':
+                param.scope = OPT_SCOPE_ASYNC
+                param.transfer = PARAM_TRANSFER_NONE
+                continue
+
+            # j is the index where we look for closure/destroy to
+            # group with the callback param
+            j = i + 1
+            if j == len(params):
+                continue # no more args -> nothing to group
+            # look at the param directly following for either a
+            # closure or a destroy; only one of these will fire
+            had_closure = 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 = node.parameters[-1]
+        # Checking type.name=='GLib.Error' generates false positives
+        # on methods that take a 'GError *'
+        if last_param.type.ctype == 'GError**':
+            node.parameters.pop()
+            node.throws = True
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 363f6f6..fc59c76 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -21,11 +21,12 @@
 #
 
 import subprocess
+import tempfile
 import optparse
 import os
 import sys
 
-from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
+from giscanner.annotationparser import AnnotationParser
 from giscanner.ast import Include
 from giscanner.cachestore import CacheStore
 from giscanner.dumper import compile_introspection_binary
@@ -34,6 +35,11 @@ from giscanner.minixpath import myxpath, xpath_assert
 from giscanner.sourcescanner import SourceScanner
 from giscanner.shlibs import resolve_shlibs
 from giscanner.transformer import Transformer
+from giscanner.primarytransformer import PrimaryTransformer
+from giscanner.finaltransformer import FinalTransformer
+from giscanner.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
+from giscanner.utils import files_are_identical
 
 def _get_option_parser():
     parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +55,12 @@ 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('', "--reparse-validate",
+                      action="store_true", dest="reparse_validate_gir", default=False,
+                      help="After generating the GIR, re-parse it to ensure validity")
     parser.add_option("", "--add-include-path",
                       action="append", dest="include_paths", default=[],
                       help="include paths for other GIR files")
@@ -101,15 +113,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 +142,16 @@ def _get_option_parser():
 def _error(msg):
     raise SystemExit('ERROR: %s' % (msg, ))
 
-def typelib_xml_strip(path):
-    from giscanner.girparser import GIRParser
-    from giscanner.girwriter import GIRWriter
-    from giscanner.girparser import C_NS
-    from xml.etree.cElementTree import parse
-
-    c_ns_key = '{%s}' % (C_NS, )
-
-    tree = parse(path)
-    root = tree.getroot()
-    for node in root.getiterator():
-        for attrib in list(node.attrib):
-            if attrib.startswith(c_ns_key):
-                del node.attrib[attrib]
+def passthrough_gir(path, f):
     parser = GIRParser()
-    parser.parse_tree(tree)
-
-    writer = GIRWriter(parser.get_namespace(),
-                       parser.get_shared_libraries(),
-                       parser.get_includes())
-    sys.stdout.write(writer.get_xml())
-    return 0
-
-def inject(path, additions, outpath):
-    from giscanner.girparser import GIRParser
-    from giscanner.girwriter import GIRWriter
-    from xml.etree.cElementTree import parse
-
-    tree = parse(path)
-    root = tree.getroot()
-    injectDoc = parse(open(additions))
-    for node in injectDoc.getroot():
-        injectPath = node.attrib['path']
-        target = myxpath(root, injectPath)
-        if not target:
-            raise ValueError("Couldn't find path %r" % (injectPath, ))
-        for child in node:
-            target.append(child)
+    parser.parse(path)
 
-    parser = GIRParser()
-    parser.parse_tree(tree)
     writer = GIRWriter(parser.get_namespace(),
                        parser.get_shared_libraries(),
-                       parser.get_includes())
-    outf = open(outpath, 'w')
-    outf.write(writer.get_xml())
-    outf.close()
-    return 0
+                       parser.get_includes(),
+                       parser.get_pkgconfig_packages(),
+                       parser.get_c_includes())
+    f.write(writer.get_xml())
 
 def validate(assertions, path):
     from xml.etree.cElementTree import parse
@@ -237,18 +205,12 @@ def scanner_main(args):
     parser = _get_option_parser()
     (options, args) = parser.parse_args(args)
 
+    if options.passthrough_gir:
+        passthrough_gir(options.passthrough_gir, sys.stdout)
+
     if 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 +239,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 +273,7 @@ def scanner_main(args):
 
     # Transform the C AST nodes into higher level
     # GLib/GObject nodes
-    glibtransformer = GLibTransformer(transformer,
-                                      noclosure=options.noclosure)
+    glibtransformer = GLibTransformer(transformer)
 
     # Do enough parsing that we have the get_type() functions to reference
     # when creating the introspection binary
@@ -332,16 +290,16 @@ def scanner_main(args):
     shlibs = resolve_shlibs(options, binary, libraries)
 
     glibtransformer.set_introspection_binary(binary)
+    glibtransformer.parse()
 
-    namespace = glibtransformer.parse()
+    ap = AnnotationParser(ss, transformer)
+    blocks = ap.parse()
 
-    ap = AnnotationParser(namespace, ss, transformer)
-    try:
-        ap.parse()
-    except InvalidAnnotationError, e:
-        raise SystemExit("ERROR in annotation: %s" % (str(e), ))
+    primary = PrimaryTransformer(transformer, blocks)
+    primary.transform()
 
-    glibtransformer.final_analyze()
+    final = FinalTransformer(transformer)
+    final.validate()
 
     if options.warn_fatal and transformer.did_warn():
         return 1
@@ -351,14 +309,25 @@ def scanner_main(args):
         exported_packages = options.packages_export
     else:
         exported_packages = options.packages
-    writer = Writer(namespace, shlibs, transformer.get_includes(),
-                    exported_packages, options.c_includes,
-                    transformer.get_strip_prefix())
+    writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
+                    exported_packages, options.c_includes)
     data = writer.get_xml()
     if options.output:
-        fd = open(options.output, "w")
-        fd.write(data)
+        tempdir = os.path.dirname(options.output) or os.getcwd()
+        (tempfd, main_temppath) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+        f = os.fdopen(tempfd, 'w')
+        f.write(data)
+        f.close()
+        if options.reparse_validate_gir:
+            (tempfd, tempgir_path) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+            temp_f = os.fdopen(tempfd, 'w')
+            passthrough_gir(main_temppath, temp_f)
+            temp_f.close()
+            if not files_are_identical(main_temppath, tempgir_path):
+                raise SystemExit("Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_temppath, tempgir_path))
+            os.unlink(tempgir_path)
+        os.rename(main_temppath, options.output)
     else:
-        print data
+        sys.stdout.write(data)
 
     return 0
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index ae3d29d..aff9a2f 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -154,10 +154,16 @@ class SourceSymbol(object):
         self._symbol = symbol
 
     def __repr__(self):
-        return '<%s type=%r ident=%r>' % (
+        src = self.source_filename
+        if src:
+            line = self.line
+            if line:
+                src += ':%r' % (line,)
+        return '<%s type=%r ident=%r src=%r>' % (
             self.__class__.__name__,
             symbol_type_name(self.type),
-            self.ident)
+            self.ident,
+            src)
 
     @property
     def const_int(self):
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index b43e188..939e8f4 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -22,13 +22,11 @@ import os
 import sys
 
 from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
-                  Parameter, Return, Struct, Field,
-                  Type, Array, Alias, Interface, Class, Node, Union,
-                  Varargs, Constant, type_name_from_ctype,
-                  type_names, TYPE_ANY, TYPE_STRING,
-                  BASIC_GIR_TYPES)
+                  Parameter, Return, Record, Field,
+                  Type, Array, List, Map, Alias, Interface, Class, Node, Union,
+                  Varargs, Constant, type_names, basic_type_names,
+                  default_array_types, TYPE_STRING, TYPE_ANY)
 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
 from .girparser import GIRParser
 from .odict import odict
 from .sourcescanner import (
@@ -41,55 +39,33 @@ from .sourcescanner import (
     TYPE_QUALIFIER_CONST)
 from .utils import to_underscores
 
-_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
-                      + [DATADIR, '/usr/share'] if x]
-
-
-class SkipError(Exception):
+class TypeResolutionException(Exception):
     pass
 
-
-class Names(object):
-    names = property(lambda self: self._names)
-    aliases = property(lambda self: self._aliases)
-    type_names = property(lambda self: self._type_names)
-    ctypes = property(lambda self: self._ctypes)
-
-    def __init__(self):
-        super(Names, self).__init__()
-        self._names = odict() # Maps from GIName -> (namespace, node)
-        self._aliases = {} # Maps from GIName -> GIName
-        self._type_names = {} # Maps from GTName -> (namespace, node)
-        self._ctypes = {} # Maps from CType -> (namespace, node)
-
+_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
+                      + [DATADIR, '/usr/share'] if x]
 
 class Transformer(object):
+    namespace = property(lambda self: self._namespace)
 
-    def __init__(self, cachestore, namespace_name, namespace_version):
+    def __init__(self, cachestore, namespace_name, namespace_version,
+                 strip_prefix=None):
         self._cwd = os.getcwd() + os.sep
         self._cachestore = cachestore
         self.generator = None
-        self._namespace = Namespace(namespace_name, namespace_version)
-        self._names = Names()
+        self._namespace = Namespace(namespace_name, namespace_version,
+                                    c_prefix=strip_prefix)
         self._pkg_config_packages = set()
         self._typedefs_ns = {}
-        self._strip_prefix = ''
+        self._strip_prefix = self._namespace.c_prefix
         self._enable_warnings = False
         self._warned = False
-        self._includes = set()
+        self._includes = {}
+        self._include_names = set()
         self._includepaths = []
 
-    def get_names(self):
-        return self._names
-
     def get_includes(self):
-        return self._includes
-
-    def set_strip_prefix(self, strip_prefix):
-        self._strip_prefix = strip_prefix
-
-    def get_strip_prefix(self):
-        return self._strip_prefix
+        return self._include_names
 
     def enable_warnings(self, enable):
         self._enable_warnings = enable
@@ -106,26 +82,56 @@ class Transformer(object):
     def parse(self):
         nodes = []
         for symbol in self.generator.get_symbols():
-            try:
-                node = self._traverse_one(symbol)
-            except SkipError:
-                continue
-            self._add_node(node)
-        return self._namespace
+            node = self._traverse_one(symbol)
+            if node:
+                try:
+                    self._namespace.append(node)
+                except ValueError, e:
+                    original = self._namespace.get(node.name)
+                    positions = set()
+                    positions.update(original.file_positions)
+                    positions.update(node.file_positions)
+                    self.log_warning("Namespace conflict for '%s'" % (node.name, ),
+                                     positions)
+                    sys.exit(1)
 
     def set_include_paths(self, paths):
         self._includepaths = list(paths)
 
     def register_include(self, include):
-        if include in self._includes:
+        if include in self._include_names:
             return
         filename = self._find_include(include)
         self._parse_include(filename)
-        self._includes.add(include)
+        self._include_names.add(include)
+
+    def lookup_giname(self, name):
+        """Given a name of the form Foo or Bar.Foo,
+return the corresponding Node, or None if none
+available.  Will throw KeyError however for unknown
+namespaces."""
+        if '.' not in name:
+            return self._namespace.get(name)
+        else:
+            (ns, name) = name.split('.', 1)
+            if ns == self._namespace.name:
+                return self._namespace.get(name)
+            include = self._includes[ns]
+            return include.get(name)
+
+    def lookup_typenode(self, typeobj):
+        """Given a Type object, if it points to a giname,
+calls lookup_giname() on the name.  Otherwise return
+None."""
+        if typeobj.target_giname:
+            return self.lookup_giname(typeobj.target_giname)
+        return None
+
 
     # Private
 
-    def log_warning(self, text, file_positions=None, prefix=None):
+    def log_warning(self, text, file_positions=None, prefix=None,
+                    fatal=False):
         """Log a warning, using optional file positioning information.
 If the warning is related to a Node type, see log_node_warning()."""
         if not self._enable_warnings:
@@ -136,6 +142,7 @@ If the warning is related to a Node type, see log_node_warning()."""
         else:
             target_file_positions = file_positions
 
+        position_strings = []
         for (filename, line, column) in target_file_positions:
             if filename.startswith(self._cwd):
                 filename = filename[len(self._cwd):]
@@ -145,14 +152,20 @@ If the warning is related to a Node type, see log_node_warning()."""
                 position = '%s:%d' % (filename, line, )
             else:
                 position = '%s:' % (filename, )
+            position_strings.append(position)
 
+        for position in position_strings[:-1]:
+            print >>sys.stderr, "%s:" % (position, )
+        last_position = position_strings[-1]
         if prefix:
             print >>sys.stderr, \
-'''%s: warning: ns=%r %s: %s''' % (position, self._namespace.name,
+'''%s: warning: ns=%r %s: %s''' % (last_position, self._namespace.name,
                                    prefix, text)
         else:
             print >>sys.stderr, \
-'''%s: warning: ns=%r: %s''' % (position, self._namespace.name, text)
+'''%s: warning: ns=%r: %s''' % (last_position, self._namespace.name, text)
+        if fatal:
+            sys.exit(1)
 
     def log_symbol_warning(self, symbol, text):
         """Log a warning in the context of the given symbol."""
@@ -160,7 +173,7 @@ If the warning is related to a Node type, see log_node_warning()."""
         prefix = "symbol=%r" % (symbol.ident, )
         self.log_warning(text, file_positions, prefix=prefix)
 
-    def log_node_warning(self, node, text, context=None):
+    def log_node_warning(self, node, text, context=None, fatal=False):
         """Log a warning, using information about file positions from
 the given node.  The optional context argument, if given, should be
 another Node type which will also be displayed.  If no file position
@@ -175,7 +188,7 @@ context will be used."""
         if context:
             text = "context=%r %s" % (context.name, text)
 
-        self.log_warning(text, file_positions)
+        self.log_warning(text, file_positions, fatal=fatal)
 
     def _find_include(self, include):
         searchdirs = self._includepaths[:]
@@ -196,7 +209,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)
 
@@ -206,36 +218,56 @@ context will be used."""
         for pkg in parser.get_pkgconfig_packages():
             self._pkg_config_packages.add(pkg)
         namespace = parser.get_namespace()
-        nsname = namespace.name
-        for node in namespace.nodes:
-            if isinstance(node, Alias):
-                self._names.aliases[node.name] = (nsname, node)
-            elif isinstance(node, (GLibBoxed, Interface, Class)):
-                self._names.type_names[node.type_name] = (nsname, node)
-            giname = '%s.%s' % (nsname, node.name)
-            self._names.names[giname] = (nsname, node)
-            if hasattr(node, 'ctype'):
-                self._names.ctypes[node.ctype] = (nsname, node)
-            elif hasattr(node, 'symbol'):
-                self._names.ctypes[node.symbol] = (nsname, node)
-
-    def _add_node(self, node):
-        if node is None:
-            return
-        if node.name.startswith('_'):
-            return
-        self._namespace.nodes.append(node)
-        self._names.names[node.name] = (None, node)
-
-    def _strip_namespace_func(self, name):
-        prefix = self._namespace.name.lower() + '_'
-        if name.lower().startswith(prefix):
-            name = name[len(prefix):]
-        else:
-            prefix = to_underscores(self._namespace.name).lower() + '_'
-            if name.lower().startswith(prefix):
-                name = name[len(prefix):]
-        return self.remove_prefix(name, isfunction=True)
+        self._includes[namespace.name] = namespace
+
+    def _iter_namespaces(self):
+        """Return an iterator over all included namespaces; the
+currently-scanned namespace is first."""
+        yield self._namespace
+        for ns in self._includes.itervalues():
+            yield ns
+
+    def split_ctype(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 split_csymbol(self, symbol):
+        """Given a C symbol like foo_bar_do_baz, return a pair of
+(namespace, stripped_symbol) or raise ValueError."""
+        matches = []
+        for ns in self._iter_namespaces():
+            prefix = ns.uscore_prefix + '_'
+            if symbol.startswith(prefix):
+                matches.append((ns, symbol[len(prefix):]))
+        if matches:
+            matches.sort(lambda x,y : cmp(len(x[0].uscore_prefix), len(y[0].uscore_prefix)))
+            return matches[0]
+        raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
+
+    def _strip_symbol_or_warn(self, symbol):
+        ident = symbol.ident
+        hidden = ident.startswith('_')
+        if hidden:
+            ident = ident[1:]
+        try:
+            (ns, name) = self.split_csymbol(ident)
+        except ValueError, e:
+            self.log_symbol_warning(symbol, "Unknown namespace")
+            return None
+        if ns != self._namespace:
+            self.log_symbol_warning(symbol, "Skipping foreign symbol from namespace %s" % (ns.name, ))
+            return None
+        if hidden:
+            return '_' + name
+        return name
 
     def remove_prefix(self, name, isfunction=False):
         # when --strip-prefix=g:
@@ -264,17 +296,20 @@ context will be used."""
             return self._create_struct(symbol)
         elif stype == CSYMBOL_TYPE_ENUM:
             return self._create_enum(symbol)
-        elif stype == CSYMBOL_TYPE_OBJECT:
-            return self._create_object(symbol)
         elif stype == CSYMBOL_TYPE_MEMBER:
             return self._create_member(symbol)
         elif stype == CSYMBOL_TYPE_UNION:
             return self._create_union(symbol)
+        # FIXME - we need to require an annotation on
+        # #defines to have them be constants, otherwise
+        # namespace explosion can occur
         elif stype == CSYMBOL_TYPE_CONST:
-            return self._create_const(symbol)
+            pass
+        # Ignore variable declarations in the header
+        elif stype == CSYMBOL_TYPE_OBJECT:
+            pass
         else:
-            raise NotImplementedError(
-                'Transformer: unhandled symbol: %r' % (symbol, ))
+            print 'transformer: unhandled symbol: %r' % (symbol, )
 
     def _enum_common_prefix(self, symbol):
         def common_prefix(a, b):
@@ -324,74 +359,15 @@ context will be used."""
             klass = Enum
         node = klass(enum_name, symbol.ident, members)
         node.add_symbol_reference(symbol)
-        self._names.type_names[symbol.ident] = (None, node)
         return node
 
-    def _create_object(self, symbol):
-        node = Member(symbol.ident, symbol.base_type.name,
-                      symbol.ident)
-        node.add_symbol_reference(symbol)
-        return node
-
-    def _type_is_callback(self, type):
-        if isinstance(type, Callback):
-            return True
-        node = self._names.names.get(type.name)
-        if node and isinstance(node[1], Callback):
-            return True
-        return False
-
-    def _handle_closure(self, param, closure_idx, closure_param):
-        if (closure_param.type.name == TYPE_ANY and
-            closure_param.name.endswith('data')):
-            param.closure_name = closure_param.name
-            param.closure_index = closure_idx
-            return True
-        return False
-
-    def _handle_destroy(self, param, destroy_idx, destroy_param):
-        if (destroy_param.type.name == 'GLib.DestroyNotify' or
-            destroy_param.type.ctype == 'GDestroyNotify'):
-            param.destroy_name = destroy_param.name
-            param.destroy_index = destroy_idx
-            return True
-        return False
-
-    def _augment_callback_params(self, params):
-        for i, param in enumerate(params):
-            if not self._type_is_callback(param.type):
-                continue
-
-            # set a default scope
-            if param.scope is None:
-                param.scope = 'call'
-
-            # j is the index where we look for closure/destroy to
-            # group with the callback param
-            j = i + 1
-            if j == len(params):
-                continue # no more args -> nothing to group
-            # look at the param directly following for either a
-            # closure or a destroy; only one of these will fire
-            had_closure = self._handle_closure(param, j, params[j])
-            had_destroy = self._handle_destroy(param, j, params[j])
-            j += 1
-            # are we out of params, or did we find neither?
-            if j == len(params) or (not had_closure and not had_destroy):
-                continue
-            # we found either a closure or a destroy; check the
-            # parameter following for the other
-            if not had_closure:
-                self._handle_closure(param, j, params[j])
-            if not had_destroy:
-                self._handle_destroy(param, j, params[j])
-
     def _create_function(self, symbol):
         parameters = list(self._create_parameters(symbol.base_type))
         return_ = self._create_return(symbol.base_type.base_type)
-        self._augment_callback_params(parameters)
-        name = self._strip_namespace_func(symbol.ident)
-        func = Function(name, return_, parameters, symbol.ident)
+        name = self._strip_symbol_or_warn(symbol)
+        if not name:
+            return None
+        func = Function(name, return_, parameters, False, symbol.ident)
         func.add_symbol_reference(symbol)
         return func
 
@@ -409,11 +385,10 @@ context will be used."""
         elif source_type.type == CTYPE_POINTER:
             value = self._create_source_type(source_type.base_type) + '*'
         else:
-            value = TYPE_ANY
+            value = 'gpointer'
         return value
 
     def _create_parameters(self, base_type):
-
         # warn if we see annotations for unknown parameters
         param_names = set(child.ident for child in base_type.child_list)
         for child in base_type.child_list:
@@ -438,21 +413,17 @@ context will be used."""
                     derefed_name = canonical_ctype[:-1]
                 else:
                     derefed_name = canonical_ctype
-                derefed_name = self.resolve_param_type(derefed_name)
-                ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+                ftype = Array(None, self.create_type(ctype), ctype=derefed_name)
                 child_list = list(symbol.base_type.child_list)
                 ftype.zeroterminated = False
                 if child_list:
-                    ftype.size = '%d' % (child_list[0].const_int, )
+                    ftype.size = child_list[0].const_int
             else:
-                ftype = self._create_type(symbol.base_type,
-                                          is_param=False, is_retval=False)
-            ftype = self.resolve_param_type(ftype)
+                ftype = self._create_type_from_base(symbol.base_type)
             # Fields are assumed to be read-write
             # (except for Objects, see also glibtransformer.py)
-            node = Field(symbol.ident, ftype, ftype.name,
+            node = Field(symbol.ident, ftype,
                          readable=True, writable=True, bits=symbol.const_int)
-            node.add_symbol_reference(symbol)
         return node
 
     def _create_typedef(self, symbol):
@@ -475,9 +446,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)
@@ -490,22 +461,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,52 +492,47 @@ context will be used."""
 
         # Preserve "pointerness" of struct/union members
         if (is_member and canonical.endswith('*') and
-            derefed_typename in BASIC_GIR_TYPES):
-            return TYPE_ANY
+            derefed_typename in basic_type_names):
+            return 'gpointer'
         else:
             return derefed_typename
 
-    def _create_type(self, source_type, is_param, is_retval):
+    def _create_type_from_base(self, source_type):
         ctype = self._create_source_type(source_type)
-        if ctype.startswith('va_list'):
-            raise SkipError()
-        # FIXME: FILE* should not be skipped, it should be handled
-        #        properly instead
-        elif ctype == 'FILE*':
-            raise SkipError
-
-        is_member = not (is_param or is_retval)
-        # Here we handle basic type parsing; most of the heavy lifting
-        # and inference comes in annotationparser.py when we merge
-        # in annotation data.
-        derefed_name = self.parse_ctype(ctype, is_member)
-        rettype = Type(derefed_name, ctype)
-        rettype.canonical = self._canonicalize_ctype(ctype)
-        derefed_ctype = ctype.replace('*', '')
-        rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
-
-        canontype = type_name_from_ctype(ctype)
-        # Is it a const char * or a const gpointer?
-        if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
-            (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
-            rettype.is_const = True
-        return rettype
+        const = ((source_type.type == CTYPE_POINTER) and
+                 (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
+        return self.create_type(ctype, is_const=const)
+
+    def create_type(self, ctype, is_const=False):
+        canonical = self._canonicalize_ctype(ctype)
+        base = canonical.replace('*', '')
+
+        fundamental = type_names.get(base)
+        if fundamental is not None:
+            return Type(target_fundamental=fundamental.target_fundamental,
+                        ctype=ctype,
+                        is_const=is_const)
+        elif canonical in default_array_types:
+            element_type = self.create_type(canonical[:-1])
+            return Array(None, element_type, ctype=ctype,
+                         is_const=is_const)
+        elif base in ('GList', 'GSList'):
+            return List('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+                        is_const=is_const)
+        elif base in ('GArray', 'GPtrArray', 'GByteArray'):
+            return Array('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+                         is_const=is_const) 
+        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
@@ -576,15 +540,17 @@ context will be used."""
         if (symbol.source_filename is None or
             not symbol.source_filename.endswith('.h')):
             return None
-        name = self._strip_namespace_func(symbol.ident)
+        name = self._strip_symbol_or_warn(symbol)
+        if not name:
+            return None
         if symbol.const_string is not None:
-            type_name = 'utf8'
+            typeval = TYPE_STRING
             value = symbol.const_string
         elif symbol.const_int is not None:
-            type_name = 'gint'
+            type_name = 'int'
             value = symbol.const_int
         elif symbol.const_double is not None:
-            type_name = 'gdouble'
+            type_name = 'double'
             value = symbol.const_double
         else:
             raise AssertionError()
@@ -595,24 +561,22 @@ context will be used."""
 
     def _create_typedef_struct(self, symbol, disguised=False):
         name = self.remove_prefix(symbol.ident)
-        struct = Struct(name, symbol.ident, disguised)
+        struct = Record(name, symbol.ident, disguised)
         struct.add_symbol_reference(symbol)
         self._typedefs_ns[symbol.ident] = struct
-        self._create_struct(symbol)
-        return struct
+        return None
 
     def _create_typedef_union(self, symbol):
         name = self.remove_prefix(symbol.ident)
         union = Union(name, symbol.ident)
         union.add_symbol_reference(symbol)
         self._typedefs_ns[symbol.ident] = union
-        self._create_union(symbol)
-        return union
+        return None
 
     def _create_typedef_callback(self, symbol):
         callback = self._create_callback(symbol)
         self._typedefs_ns[callback.name] = callback
-        return callback
+        return None
 
     def _create_compound(self, klass, symbol, anonymous):
         if symbol.ident is None:
@@ -636,15 +600,19 @@ context will be used."""
                 compound = klass(name, symbol.ident)
 
         for child in symbol.base_type.child_list:
-            field = self._traverse_one(child)
-            if field:
-                compound.fields.append(field)
+            child_node = self._traverse_one(child)
+            if isinstance(child_node, Field):
+                field = child_node
+            else:
+                field = Field(child.ident, None, True, False,
+                              anonymous_node=child_node)
+            compound.fields.append(field)
 
         compound.add_symbol_reference(symbol)
         return compound
 
     def _create_struct(self, symbol, anonymous=False):
-        return self._create_compound(Struct, symbol, anonymous)
+        return self._create_compound(Record, symbol, anonymous)
 
     def _create_union(self, symbol, anonymous=False):
         return self._create_compound(Union, symbol, anonymous)
@@ -655,72 +623,48 @@ context will be used."""
 
         # Mark the 'user_data' arguments
         for i, param in enumerate(parameters):
-            if (param.type.name == TYPE_ANY and
-                param.name == 'user_data'):
+            if (param.type.target_fundamental == 'gpointer' and
+                param.argname == 'user_data'):
                 param.closure_index = i
 
         if symbol.ident.find('_') > 0:
             name = self.remove_prefix(symbol.ident, True)
         else:
             name = self.remove_prefix(symbol.ident)
-        callback = Callback(name, retval, parameters, symbol.ident)
+        callback = Callback(name, retval, parameters, False)
         callback.add_symbol_reference(symbol)
 
         return callback
 
+    def create_and_resolve_type(self, ctype):
+        """Parse a C type string, returning a Type object, and
+        resolve it."""
+        typeval = self.create_type(ctype)
+        self.resolve_type(typeval)
+        return typeval
+
+    def resolve_type(self, typeval):
+        if isinstance(typeval, (Array, List)):
+            self.resolve_type(typeval.element_type)
+            return
+        elif isinstance(typeval, Map):
+            self.resolve_type(typeval.key_type)
+            self.resolve_type(typeval.value_type)
+            return
+        elif not typeval.resolved and typeval.ctype:
+            pointer_stripped = typeval.ctype.replace('*', '')
+            try:
+                (ns, name) = self.split_ctype(pointer_stripped)
+            except ValueError, e:
+                raise TypeResolutionException(e)
+            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:
@@ -738,23 +682,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)
@@ -764,8 +691,3 @@ context will be used."""
             else:
                 break
         return type_name
-
-    def iter_enums(self):
-        for node in self._namespace.nodes:
-            if isinstance(node, Enum):
-                yield node
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 1bd23fc..dba958e 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -102,3 +102,16 @@ def get_libtool_command(options):
         return None
 
     return ['libtool']
+
+
+def files_are_identical(path1, path2):
+    f1 = open(path1)
+    f2 = open(path2)
+    buf1 = f1.read(8192)
+    buf2 = f2.read(8192)
+    while buf1 == buf2 and buf1 != '':
+        buf1 = f1.read(8192)
+        buf2 = f2.read(8192)
+    f1.close()
+    f2.close()
+    return buf1 == buf2



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