[STRIP_SUFFIX]
[gnome.gobject-introspection] / giscanner / transformer.py
index f886f67..af6a3bb 100644 (file)
@@ -2,32 +2,31 @@
 # GObject-Introspection - a framework for introspecting GObject libraries
 # Copyright (C) 2008  Johan Dahlin
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU 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 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# 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 re
-
-from .ast import (Callback, Enum, Function, Namespace, Member,
-                  Parameter, Return, Array, Struct, Field,
-                  Type, Alias, Interface, Class, Node, Union,
-                  List, Map, Varargs, Constant, type_name_from_ctype,
-                  type_names, default_array_types, default_out_types,
-                  TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
-from .config import DATADIR
+import sys
+
+from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
+                  Parameter, Return, Struct, Field,
+                  Type, Array, Alias, Interface, Class, Node, Union,
+                  Varargs, Constant, type_name_from_ctype,
+                  type_names, TYPE_STRING, BASIC_GIR_TYPES)
+from .config import DATADIR, GIR_DIR, GIR_SUFFIX
 from .glibast import GLibBoxed
 from .girparser import GIRParser
 from .odict import odict
@@ -39,7 +38,7 @@ from .sourcescanner import (
     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
     TYPE_QUALIFIER_CONST)
-from .utils import strip_common_prefix, to_underscores
+from .utils import to_underscores
 
 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
                       + [DATADIR, '/usr/share'] if x]
@@ -48,6 +47,8 @@ _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
 class SkipError(Exception):
     pass
 
+class VaListSkipError(SkipError):
+    pass
 
 class Names(object):
     names = property(lambda self: self._names)
@@ -65,16 +66,16 @@ class Names(object):
 
 class Transformer(object):
 
-    def __init__(self, generator, namespace_name, namespace_version):
-        self.generator = generator
+    def __init__(self, cachestore, namespace_name, namespace_version):
+        self._cachestore = cachestore
+        self.generator = None
         self._namespace = Namespace(namespace_name, namespace_version)
         self._names = Names()
+        self._pkg_config_packages = set()
         self._typedefs_ns = {}
         self._strip_prefix = ''
         self._includes = set()
         self._includepaths = []
-        self._list_ctypes = []
-        self._map_ctypes = []
 
     def get_names(self):
         return self._names
@@ -82,17 +83,28 @@ class Transformer(object):
     def get_includes(self):
         return self._includes
 
-    def set_container_types(self, list_ctypes, map_ctypes):
-        self._list_ctypes = list_ctypes
-        self._map_ctypes = map_ctypes
+    def set_strip_suffix(self, strip_suffix):
+        self._strip_suffix = strip_suffix
 
     def set_strip_prefix(self, strip_prefix):
         self._strip_prefix = strip_prefix
 
+    def get_strip_prefix(self):
+        return self._strip_prefix
+
+    def get_pkgconfig_packages(self):
+        return self._pkg_config_packages
+
+    def set_source_ast(self, src_ast):
+        self.generator = src_ast
+
     def parse(self):
         nodes = []
         for symbol in self.generator.get_symbols():
-            node = self._traverse_one(symbol)
+            try:
+                node = self._traverse_one(symbol)
+            except SkipError:
+                continue
             self._add_node(node)
         return self._namespace
 
@@ -100,6 +112,8 @@ class Transformer(object):
         self._includepaths = list(paths)
 
     def register_include(self, include):
+        if include in self._includes:
+            return
         filename = self._find_include(include)
         self._parse_include(filename)
         self._includes.add(include)
@@ -109,28 +123,40 @@ class Transformer(object):
     def _find_include(self, include):
         searchdirs = self._includepaths[:]
         for path in _xdg_data_dirs:
-            searchdirs.append(os.path.join(path, 'gir'))
+            searchdirs.append(os.path.join(path, GIR_SUFFIX))
+        searchdirs.append(GIR_DIR)
 
         girname = '%s-%s.gir' % (include.name, include.version)
-        for d in self._includepaths:
+        for d in searchdirs:
             path = os.path.join(d, girname)
             if os.path.exists(path):
                 return path
-        else:
-            raise ValueError("Couldn't find include %r (search path: %r)"\
-                             % (girname, searchdirs))
+        sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
+                         % (girname, searchdirs))
+        sys.exit(1)
 
     def _parse_include(self, filename):
-        parser = GIRParser(filename, include_parsing=True)
+        parser = self._cachestore.load(filename)
+        if parser is None:
+            parser = GIRParser()
+            parser.set_include_parsing(True)
+            parser.parse(filename)
+            self._cachestore.store(filename, parser)
+
         for include in parser.get_includes():
             self.register_include(include)
-        nsname = parser.get_namespace().name
-        for node in parser.get_namespace().nodes:
+
+        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)
-            self._names.names[node.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'):
@@ -158,14 +184,23 @@ class Transformer(object):
         # when --strip-prefix=g:
         #   GHashTable -> HashTable
         #   g_hash_table_new -> hash_table_new
+        stripped = False
         prefix = self._strip_prefix.lower()
-        if isfunction:
+        
+        if isfunction and '_' in name:
             prefix += '_'
         if len(name) > len(prefix) and name.lower().startswith(prefix):
             name = name[len(prefix):]
+            stripped = True
 
         while name.startswith('_'):
             name = name[1:]
+
+        if (stripped and self._strip_suffix and 
+            len(name) > len(self._strip_suffix) and
+            name.endswith(self._strip_suffix):
+            name = name[:-1*len(self._strip_suffix)]
+            
         return name
 
     def _traverse_one(self, symbol, stype=None):
@@ -174,10 +209,7 @@ class Transformer(object):
         if stype is None:
             stype = symbol.type
         if stype == CSYMBOL_TYPE_FUNCTION:
-            try:
-                return self._create_function(symbol)
-            except SkipError:
-                return
+            return self._create_function(symbol)
         elif stype == CSYMBOL_TYPE_TYPEDEF:
             return self._create_typedef(symbol)
         elif stype == CSYMBOL_TYPE_STRUCT:
@@ -196,78 +228,119 @@ class Transformer(object):
             raise NotImplementedError(
                 'Transformer: unhandled symbol: %r' % (symbol, ))
 
+    def _enum_common_prefix(self, symbol):
+        def common_prefix(a, b):
+            commonparts = []
+            for aword, bword in zip(a.split('_'), b.split('_')):
+                if aword != bword:
+                    return '_'.join(commonparts) + '_'
+                commonparts.append(aword)
+            return min(a, b)
+
+        # Nothing less than 2 has a common prefix
+        if len(list(symbol.base_type.child_list)) < 2:
+            return None
+        prefix = None
+        for child in symbol.base_type.child_list:
+            if prefix is None:
+                prefix = child.ident
+            else:
+                prefix = common_prefix(prefix, child.ident)
+                if prefix == '':
+                    return None
+        return prefix
+
     def _create_enum(self, symbol):
+        prefix = self._enum_common_prefix(symbol)
+        if prefix:
+            prefixlen = len(prefix)
+        else:
+            prefixlen = 0
         members = []
         for child in symbol.base_type.child_list:
-            name = strip_common_prefix(symbol.ident, child.ident).lower()
-            members.append(Member(name,
+            if prefixlen > 0:
+                name = child.ident[prefixlen:]
+            else:
+                # Ok, the enum members don't have a consistent prefix
+                # among them, so let's just remove the global namespace
+                # prefix.
+                name = self.remove_prefix(child.ident)
+            members.append(Member(name.lower(),
                                   child.const_int,
                                   child.ident))
 
         enum_name = self.remove_prefix(symbol.ident)
-        enum = Enum(enum_name, symbol.ident, members)
-        self._names.type_names[symbol.ident] = (None, enum)
-        return enum
+        if symbol.base_type.is_bitfield:
+            klass = Bitfield
+        else:
+            klass = Enum
+        node = klass(enum_name, symbol.ident, members)
+        self._names.type_names[symbol.ident] = (None, node)
+        return node
 
     def _create_object(self, symbol):
         return Member(symbol.ident, symbol.base_type.name,
                       symbol.ident)
 
-    def _parse_deprecated(self, node, directives):
-        deprecated = directives.get('deprecated', False)
-        if deprecated:
-            deprecated_value = deprecated[0]
-            if ':' in deprecated_value:
-                # Split out gtk-doc version
-                (node.deprecated_version, node.deprecated) = \
-                    [x.strip() for x in deprecated_value.split(':', 1)]
-            else:
-                # No version, just include str
-                node.deprecated = deprecated_value.strip()
-
-    def _pair_array(self, params, array):
-        if not array.type.length_param_name:
-            return
-        target_name = array.type.length_param_name
+    def _type_is_callback(self, type):
+        if isinstance(type, Callback):
+            return True
+        node = self._names.names.get(type.name)
+        if node and isinstance(node[1], Callback):
+            return True
+        return False
+
+    def _handle_closure(self, param, closure_idx, closure_param):
+        if (closure_param.type.name == 'any' and
+            closure_param.name.endswith('data')):
+            param.closure_name = closure_param.name
+            param.closure_index = closure_idx
+            return True
+        return False
+
+    def _handle_destroy(self, param, destroy_idx, destroy_param):
+        if (destroy_param.type.name == 'GLib.DestroyNotify' or
+            destroy_param.type.ctype == 'GDestroyNotify'):
+            param.destroy_name = destroy_param.name
+            param.destroy_index = destroy_idx
+            return True
+        return False
+
+    def _augment_callback_params(self, params):
         for i, param in enumerate(params):
-            if param.name == array.type.length_param_name:
-                array.type.length_param_index = i
-                return
-        raise ValueError("Unmatched length parameter name %r"\
-                             % (target_name, ))
-
-    def _pair_annotations(self, params):
-        names = {}
-        for param in params:
-            if param.name in names:
-                raise ValueError("Duplicate parameter name %r"\
-                                     % (param.name, ))
-            names[param.name] = 1
-            if isinstance(param.type, Array):
-                self._pair_array(params, param)
-
-    # We take the annotations from the parser as strings; here we
-    # want to split them into components, so:
-    # (transfer full) -> {'transfer' : [ 'full' ]}
-
-    def _parse_options(self, options):
-        ret = {}
-        ws_re = re.compile(r'\s+')
-        for opt in options:
-            items = ws_re.split(opt)
-            ret[items[0]] = items[1:]
-        return ret
+            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):
-        directives = symbol.directives()
-        parameters = list(self._create_parameters(
-            symbol.base_type, directives))
-        self._pair_annotations(parameters)
-        return_ = self._create_return(symbol.base_type.base_type,
-                                      directives.get('return', {}))
+        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)
-        self._parse_deprecated(func, directives)
         return func
 
     def _create_source_type(self, source_type):
@@ -277,60 +350,82 @@ class Transformer(object):
             value = 'void'
         elif source_type.type == CTYPE_BASIC_TYPE:
             value = source_type.name
+            if source_type.base_type:
+                value += ' ' + self._create_source_type(source_type.base_type)
         elif source_type.type == CTYPE_TYPEDEF:
             value = source_type.name
         elif source_type.type == CTYPE_ARRAY:
             return self._create_source_type(source_type.base_type)
         elif source_type.type == CTYPE_POINTER:
             value = self._create_source_type(source_type.base_type) + '*'
+        elif source_type.type == CTYPE_STRUCT:
+            value = source_type.name
         else:
             value = 'any'
         return value
 
-    def _create_parameters(self, base_type, directives=None):
-        if directives is None:
-            dirs = {}
-        else:
-            dirs = directives
+    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)
-        dirs_for = set(dirs)
-        dirs_for = dirs_for.difference(param_names)
-        dirs_for.discard('return')
-        if dirs_for:
-            print 'Unexpected annotations for %s, parameters are %s' % (
-                list(dirs_for), list(param_names), )
-
         for child in base_type.child_list:
-            yield self._create_parameter(
-                child, dirs.get(child.ident, {}))
+            yield self._create_parameter(child)
 
     def _create_member(self, symbol):
-        ctype = symbol.base_type.type
-        if (ctype == CTYPE_POINTER and
+        source_type = symbol.base_type
+        if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
-            node = self._create_callback(symbol)
+            try:
+                node = self._create_callback(symbol)
+            except VaListSkipError:
+                #this handles va_list members, and converts them
+                #to unwritable, unreadable void*
+                ftype = Type("any", "void*")
+                ftype = self.resolve_param_type(ftype)
+                node = Field(symbol.ident, ftype, ftype.name,
+                         readable=False, writable=False, bits=symbol.const_int)
+
+
+        elif source_type.type == CTYPE_STRUCT and source_type.name is None:
+            node = self._create_struct(symbol, anonymous=True)
+        elif source_type.type == CTYPE_UNION and source_type.name is None:
+            node = self._create_union(symbol, anonymous=True)
         else:
-            opts = {}
-            if ctype == CTYPE_ARRAY:
-                opts['array'] = []
+            # Special handling for fields; we don't have annotations on them
+            # to apply later, yet.
+            if source_type.type == CTYPE_ARRAY:
+                ctype = self._create_source_type(source_type)
+                canonical_ctype = self._canonicalize_ctype(ctype)
+                if canonical_ctype[-1] == '*':
+                    derefed_name = canonical_ctype[:-1]
+                else:
+                    derefed_name = canonical_ctype
+                derefed_name = self.resolve_param_type(derefed_name)
+                ftype = Array(ctype, self.parse_ctype(derefed_name))
                 child_list = list(symbol.base_type.child_list)
+                ftype.zeroterminated = False
                 if child_list:
-                    size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
-                    opts['array'].append(size_opt)
-            ftype = self._create_type(symbol.base_type, opts, True)
+                    ftype.size = '%d' % (child_list[0].const_int, )
+            else:
+                ftype = self._create_type(symbol.base_type,
+                                          is_param=False, is_retval=False)
+            ftype = self.resolve_param_type(ftype)
             # Fields are assumed to be read-write
             # (except for Objects, see also glibtransformer.py)
-            node = Field(symbol.ident, ftype, symbol.ident,
-                       readable=True, writable=True, bits=symbol.const_int)
+            node = Field(symbol.ident, ftype, ftype.name,
+                         readable=True, writable=True, bits=symbol.const_int)
         return node
 
     def _create_typedef(self, symbol):
         ctype = symbol.base_type.type
         if (ctype == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
-            node = self._create_callback(symbol)
+            node = self._create_typedef_callback(symbol)
+        if (ctype ==  CTYPE_FUNCTION):
+            node = self._create_typedef_callback(symbol)
+        elif (ctype == CTYPE_POINTER and
+            symbol.base_type.base_type.type == CTYPE_STRUCT):
+            node = self._create_typedef_struct(symbol, disguised=True)
         elif ctype == CTYPE_STRUCT:
             node = self._create_typedef_struct(symbol)
         elif ctype == CTYPE_UNION:
@@ -344,6 +439,13 @@ class Transformer(object):
             name = self.remove_prefix(symbol.ident)
             if symbol.base_type.name:
                 target = self.remove_prefix(symbol.base_type.name)
+            elif (ctype == CTYPE_POINTER and
+                symbol.base_type.base_type.name):
+                target = self.remove_prefix(
+                    symbol.base_type.base_type.name) + '*'
+            elif (ctype == CTYPE_POINTER and
+                symbol.base_type.base_type.type == CTYPE_VOID):
+                target = 'any'
             else:
                 target = 'none'
             if name in type_names:
@@ -354,210 +456,115 @@ class Transformer(object):
                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
         return node
 
-    def _parse_ctype(self, ctype):
+    def _canonicalize_ctype(self, ctype):
         # 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)
 
+        # If we have a particular alias for this, skip deep
+        # canonicalization to prevent changing
+        # e.g. char* -> int8*
+        if firstpass != ctype:
+            return firstpass
+
+        # 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
+
+        # We have a pointer type.
+        # Strip the end pointer, canonicalize our base type
+        base = firstpass[:-1]
+        canonical_base = self._canonicalize_ctype(base)
+
+        # Append the pointer again
+        canonical = canonical_base + '*'
+
+        return canonical
+
+    def parse_ctype(self, ctype, is_member=False):
+        canonical = self._canonicalize_ctype(ctype)
+
         # Remove all pointers - we require standard calling
         # conventions.  For example, an 'int' is always passed by
         # value (unless it's out or inout).
-        derefed = firstpass.replace('*', '')
+        derefed_typename = canonical.replace('*', '')
 
-        # Canonicalize our type again, this time without the pointer;
-        # this ensures we turn e.g. plain "guint" => "int"
-        return type_name_from_ctype(derefed)
+        # Preserve "pointerness" of struct/union members
+        if (is_member and canonical.endswith('*') and
+            derefed_typename in BASIC_GIR_TYPES):
+            return 'any'
+        else:
+            return derefed_typename
 
-    def _create_type(self, source_type, options, is_param):
+    def _create_type(self, source_type, is_param, is_retval):
         ctype = self._create_source_type(source_type)
-        if ctype == 'va_list':
-            raise SkipError()
+        if ctype.startswith('va_list'):
+            raise VaListSkipError()
         # FIXME: FILE* should not be skipped, it should be handled
         #        properly instead
         elif ctype == 'FILE*':
             raise SkipError
 
-        # Now check for a list/map/array type
-        if ctype in self._list_ctypes:
-            param = options.get('element-type')
-            if param:
-                contained_type = self._parse_ctype(param[0])
-            else:
-                contained_type = None
-            derefed_name = self._parse_ctype(ctype)
-            rettype = List(derefed_name,
-                           ctype,
-                           contained_type)
-        elif ctype in self._map_ctypes:
-            param = options.get('element-type')
-            if param:
-                key_type = self._parse_ctype(param[0])
-                value_type = self._parse_ctype(param[1])
-            else:
-                key_type = None
-                value_type = None
-            derefed_name = self._parse_ctype(ctype)
-            rettype = Map(derefed_name,
-                          ctype,
-                          key_type, value_type)
-        elif (ctype in default_array_types) or ('array' in options):
-            derefed_name = ctype[:-1] if ctype[-1] == '*' else ctype
-            rettype = Array(ctype,
-                            self._parse_ctype(derefed_name))
-            array_opts = dict([opt.split('=')
-                               for opt in options.get('array', [])])
-            if 'length' in array_opts:
-                rettype.length_param_name = array_opts['length']
-            if 'fixed-size' in array_opts:
-                rettype.size = array_opts['fixed-size']
-                rettype.zeroterminated = False
-        else:
-            derefed_name = self._parse_ctype(ctype)
-            rettype = Type(derefed_name, ctype)
-
-        # Deduce direction for some types passed by reference that
-        # aren't arrays; modifies the options array.
-        if ('array' not in options and
-            not ('out' in options or
-                 'in' in options or
-                 'inout' in options or
-                 'in-out' in options) and
-            source_type.type == CTYPE_POINTER and
-            derefed_name in default_out_types):
-            options['out'] = []
-
-        if 'transfer' in options:
-            # Transfer is specified, we don't question it.
-            return rettype
+        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)
-
-        # Since no transfer is specified, we drop into a bunch of
-        # heuristics to guess it.  This mutates the options array to
-        # set the 'transfer' option.
-        # Note that we inferred the transfer
-        options['transfer-inferred'] = []
-        stype = source_type
-        if canontype == TYPE_STRING:
-            # It's a string - we just look at 'const'
-            if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
-                options['transfer'] = ['none']
-            else:
-                options['transfer'] = ['full']
-        elif 'array' in options or stype.type == CTYPE_ARRAY:
-            # It's rare to mutate arrays in public GObject APIs
-            options['transfer'] = ['none']
-        elif (canontype in BASIC_GIR_TYPES or
-              canontype == TYPE_NONE or
-              stype.type == CTYPE_ENUM):
-            # Basic types default to 'none'
-            options['transfer'] = ['none']
-        elif (stype.type == CTYPE_POINTER and
-              stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
-            # Anything with 'const' gets none
-            options['transfer'] = ['none']
-        elif is_param and stype.type == CTYPE_POINTER:
-            # For generic pointer types, let's look at the argument
-            # direction.  An out/inout argument gets full, everything
-            # else none.
-            if ('out' in options or
-                'inout' in options or
-                'in-out' in options):
-                options['transfer'] = ['full']
-            else:
-                options['transfer'] = ['none']
-        else:
-            # For anything else we default to none for parameters;
-            # this covers enums and possibly some other corner cases.
-            # Return values of structures and the like will end up
-            # full.
-            if is_param:
-                options['transfer'] = ['none']
-            else:
-                options['transfer'] = ['full']
-
+        # 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
 
-    def _handle_generic_param_options(self, param, options):
-        for option, data in options.iteritems():
-            if option == 'transfer':
-                if data:
-                    depth = data[0]
-                    if depth not in ('none', 'container', 'full'):
-                        raise ValueError("Invalid transfer %r" % (depth, ))
-                else:
-                    depth = 'full'
-                param.transfer = depth
-            elif option == 'transfer-inferred':
-                # This is a purely internal flag; we don't expect
-                # people to write it
-                param.transfer_inferred = True
-
-    def _create_parameter(self, symbol, options):
-        options = self._parse_options(options)
+    def _create_parameter(self, symbol):
         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
             ptype = Varargs()
-            if 'transfer' not in options:
-                options['transfer'] = ['none']
         else:
-            ptype = self._create_type(symbol.base_type, options, True)
-        param = Parameter(symbol.ident, ptype)
-        for option, data in options.iteritems():
-            if option in ['in-out', 'inout']:
-                param.direction = 'inout'
-            elif option == 'in':
-                param.direction = 'in'
-            elif option == 'out':
-                param.direction = 'out'
-            elif option == 'allow-none':
-                param.allow_none = True
-            elif option.startswith(('element-type', 'array')):
-                pass
-            elif option in ('transfer', 'transfer-inferred'):
-                pass
-            else:
-                print 'Unhandled parameter annotation option: %r' % (
-                    option, )
-        self._handle_generic_param_options(param, options)
-
-        assert param.transfer is not None, param
-        return param
-
-    def _create_return(self, source_type, options=None):
-        if options is None:
-            options_map = {}
-        else:
-            options_map = self._parse_options(options)
-        rtype = self._create_type(source_type, options_map, False)
+            ptype = self._create_type(symbol.base_type,
+                                      is_param=True, is_retval=False)
+            ptype = self.resolve_param_type(ptype)
+        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)
-        self._handle_generic_param_options(return_, options_map)
-        for option, data in options_map.iteritems():
-            if option in ('transfer', 'transfer-inferred',
-                          'element-type', 'out'):
-                pass
-            else:
-                print 'Unhandled return type annotation option: %r' % (
-                    option, )
-
-        assert return_.transfer is not None, return_
         return return_
 
     def _create_const(self, symbol):
+        # Don't create constants for non-public things
+        # http://bugzilla.gnome.org/show_bug.cgi?id=572790
+        if (symbol.source_filename is None or
+            not symbol.source_filename.endswith('.h')):
+            return None
         name = self.remove_prefix(symbol.ident)
-        if symbol.const_string is None:
+        if symbol.const_string is not None:
+            type_name = 'utf8'
+            value = symbol.const_string
+        elif symbol.const_int is not None:
             type_name = 'int'
             value = symbol.const_int
+        elif symbol.const_double is not None:
+            type_name = 'double'
+            value = symbol.const_double
         else:
-            type_name = 'utf8'
-            value = symbol.const_string
+            raise AssertionError()
+
         const = Constant(name, type_name, value)
         return const
 
-    def _create_typedef_struct(self, symbol):
+    def _create_typedef_struct(self, symbol, disguised=False):
         name = self.remove_prefix(symbol.ident)
-        struct = Struct(name, symbol.ident)
+        struct = Struct(name, symbol.ident, disguised)
         self._typedefs_ns[symbol.ident] = struct
         self._create_struct(symbol)
         return struct
@@ -569,57 +576,66 @@ class Transformer(object):
         self._create_union(symbol)
         return union
 
-    def _create_struct(self, symbol):
-        struct = self._typedefs_ns.get(symbol.ident, None)
-        if struct is None:
-            # This is a bit of a hack; really we should try
-            # to resolve through the typedefs to find the real
-            # name
-            if symbol.ident.startswith('_'):
-                name = symbol.ident[1:]
-            else:
-                name = symbol.ident
-            name = self.remove_prefix(name)
-            struct = Struct(name, symbol.ident)
-
-        for child in symbol.base_type.child_list:
-            field = self._traverse_one(child)
-            if field:
-                struct.fields.append(field)
+    def _create_typedef_callback(self, symbol):
+        callback = self._create_callback(symbol)
+        self._typedefs_ns[callback.name] = callback
+        return callback
 
-        return struct
+    def _create_compound(self, klass, symbol, anonymous):
+        if symbol.ident is None:
+            # the compound is an anonymous member of another union or a struct
+            assert anonymous
+            compound = klass(None, None)
+        else:
+            compound = self._typedefs_ns.get(symbol.ident, None)
 
-    def _create_union(self, symbol):
-        union = self._typedefs_ns.get(symbol.ident, None)
-        if union is None:
+        if compound is None:
             # This is a bit of a hack; really we should try
             # to resolve through the typedefs to find the real
             # name
             if symbol.ident.startswith('_'):
                 name = symbol.ident[1:]
+                compound = self._typedefs_ns.get(name, None)
             else:
                 name = symbol.ident
-            name = self.remove_prefix(name)
-            union = Union(name, symbol.ident)
+            if compound is None:
+                name = self.remove_prefix(name)
+                compound = klass(name, symbol.ident)
 
         for child in symbol.base_type.child_list:
             field = self._traverse_one(child)
             if field:
-                union.fields.append(field)
+                compound.fields.append(field)
 
-        return union
+        return compound
+
+    def _create_struct(self, symbol, anonymous=False):
+        return self._create_compound(Struct, symbol, anonymous)
+
+    def _create_union(self, symbol, anonymous=False):
+        return self._create_compound(Union, symbol, anonymous)
 
     def _create_callback(self, symbol):
-        directives = symbol.directives()
-        parameters = self._create_parameters(symbol.base_type.base_type,
-            directives)
-        retval = self._create_return(symbol.base_type.base_type.base_type,
-            directives.get('return', {}))
+        parameters = list(self._create_parameters(symbol.base_type.base_type))
+        retval = self._create_return(symbol.base_type.base_type.base_type)
+        ret_type = symbol.base_type.base_type.base_type
+        if not ret_type:
+            ret_type = symbol.base_type.base_type
+        retval = self._create_return(ret_type)
+
+        # Mark the 'user_data' arguments
+        for i, param in enumerate(parameters):
+            if (param.type.name == 'any' and
+                param.name == '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)
-        return Callback(name, retval, list(parameters), symbol.ident)
+        callback = Callback(name, retval, parameters, symbol.ident)
+
+        return callback
 
     def _typepair_to_str(self, item):
         nsname, item = item
@@ -638,6 +654,12 @@ class Transformer(object):
             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:
@@ -645,11 +667,6 @@ class Transformer(object):
         resolved = names.names.get(type_name)
         if resolved:
             return self._typepair_to_str(resolved)
-        if ctype:
-            ctype = ctype.replace('*', '')
-            resolved = names.ctypes.get(ctype)
-            if resolved:
-                return self._typepair_to_str(resolved)
         resolved = names.type_names.get(type_name)
         if resolved:
             return self._typepair_to_str(resolved)
@@ -695,21 +712,8 @@ class Transformer(object):
             ptype.name = self.resolve_type_name_full(ptype.name,
                                                      self.ctype_of(ptype),
                                                      names, **kwargs)
-            if isinstance(ptype, (Array, List)):
-                if ptype.element_type is not None:
-                    ptype.element_type = \
-                        self.resolve_param_type_full(ptype.element_type,
-                                                     names, **kwargs)
-            if isinstance(ptype, Map):
-                if ptype.key_type is not None:
-                    ptype.key_type = \
-                        self.resolve_param_type_full(ptype.key_type,
-                                                     names, **kwargs)
-                    ptype.value_type = \
-                        self.resolve_param_type_full(ptype.value_type,
-                                                     names, **kwargs)
         elif isinstance(ptype, basestring):
-            return self.resolve_type_name_full(ptype, None, names, **kwargs)
+            return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
         else:
             raise AssertionError("Unhandled param: %r" % (ptype, ))
         return ptype
@@ -729,3 +733,8 @@ class Transformer(object):
             else:
                 break
         return type_name
+
+    def iter_enums(self):
+        for node in self._namespace.nodes:
+            if isinstance(node, Enum):
+                yield node