Type checking rework
authorColin Walters <walters@src.gnome.org>
Tue, 19 Aug 2008 21:20:10 +0000 (21:20 +0000)
committerColin Walters <walters@src.gnome.org>
Tue, 19 Aug 2008 21:20:10 +0000 (21:20 +0000)
svn path=/trunk/; revision=400

gir/Makefile.am
giscanner/ast.py
giscanner/girparser.py
giscanner/girwriter.py
giscanner/glibast.py
giscanner/glibtransformer.py
giscanner/transformer.py
tests/scanner/Foo-expected.gir
tools/g-ir-scanner

index 5f757cd5d3d98566968654ed32deae7aa848724b..67757a8d92fc3461edf93aad377df484df735b6f 100644 (file)
@@ -9,6 +9,7 @@ GLIB_INCLUDEDIR=`pkg-config --variable=includedir glib-2.0`/glib-2.0
 GLIB_LIBDIR=`pkg-config --variable=libdir glib-2.0`
 glib-2.0.gir: $(G_IR_SCANNER) $(G_IR_SCANNER_FILES)
        $(G_IR_SCANNER) -v --namespace GLib \
+           --noclosure \
            --output $@ \
            --strip-prefix=g \
            -I$(GLIB_INCLUDEDIR) \
@@ -21,8 +22,9 @@ BUILT_SOURCES += glib-2.0.gir
 # gobject
 GOBJECT_INCLUDEDIR=`pkg-config --variable=includedir gobject-2.0`/glib-2.0
 GOBJECT_LIBDIR=`pkg-config --variable=libdir gobject-2.0`
-gobject-2.0.gir: $(G_IR_SCANNER) $(G_IR_SCANNER_FILES)
+gobject-2.0.gir: glib-2.0.gir $(G_IR_SCANNER) $(G_IR_SCANNER_FILES)
        $(G_IR_SCANNER) -v --namespace GObject \
+           --noclosure \
            --output $@ \
            --strip-prefix=g \
             --include=$(builddir)/glib-2.0.gir \
@@ -40,6 +42,7 @@ GIO_INCLUDEDIR=`pkg-config --variable=includedir gio-2.0`/glib-2.0
 GIO_LIBDIR=`pkg-config --variable=libdir gio-2.0`
 gio-2.0.gir: $(G_IR_SCANNER) $(G_IR_SCANNER_FILES)
        $(G_IR_SCANNER) -v --namespace Gio \
+           --noclosure \
            --output $@ \
            --strip-prefix=g \
             --include=$(builddir)/glib-2.0.gir \
index 97710070c829eeee9b3a007870c6ca23c1a74152..bde22ad0d8640e6b270a43d6cd8ab96c8b51a4b7 100644 (file)
@@ -27,10 +27,12 @@ which is language/library/domain specific.
 """
 
 ##
-## Types
+## Basic types, modeled on GITypeTag but not equivalent
 ##
 
-# Basic types
+TYPE_NONE = 'none' # We differ from repository on these first two
+TYPE_ANY = 'any'
+TYPE_BOOLEAN = 'boolean'
 TYPE_INT8 = 'int8'
 TYPE_UINT8 = 'uint8'
 TYPE_INT16 = 'int16'
@@ -39,33 +41,33 @@ TYPE_INT = 'int'
 TYPE_UINT = 'uint'
 TYPE_INT32 = 'int32'
 TYPE_UINT32 = 'uint32'
-TYPE_LONG = 'long'
-TYPE_ULONG = 'ulong'
 TYPE_INT64 = 'int64'
 TYPE_UINT64 = 'uint64'
-
-# Floating-point
+TYPE_LONG = 'long'
+TYPE_ULONG = 'ulong'
+TYPE_SSIZET = 'ssize_t'
+TYPE_SIZET = 'size_t'
 TYPE_FLOAT = 'float'
 TYPE_DOUBLE = 'double'
+TYPE_STRING = 'string' # requires zero-terminated
+TYPE_FILENAME = 'filename'
+
+BASIC_GIR_TYPES = [TYPE_BOOLEAN, TYPE_INT8, TYPE_UINT8, TYPE_INT16,
+                   TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64,
+                   TYPE_UINT64, TYPE_INT, TYPE_UINT, TYPE_LONG,
+                   TYPE_ULONG, TYPE_SSIZET, TYPE_SIZET, TYPE_FLOAT,
+                   TYPE_DOUBLE]
+GIR_TYPES = [TYPE_NONE, TYPE_ANY]
+GIR_TYPES.extend(BASIC_GIR_TYPES)
+GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
 
 # Higher-level data types
-TYPE_NONE = 'none'
-TYPE_ANY = 'any'           # CORBA Any/Variant/GValue, holds anything.
-TYPE_BOOLEAN = 'boolean'   # True/False
-TYPE_STRING = 'string'     # Sequence of characters
 TYPE_SEQUENCE = 'sequence' # Sequence of something
-TYPE_CHAR = 'char'         # Character
-TYPE_UCHAR = 'uchar'       # Unsigned Character
-TYPE_SIZE = 'size'         # Size type (memory, buffer etc)
-TYPE_SSIZE = 'ssize'
 
 # Wide/Unicode
 TYPE_UCHAR = 'uchar'
 TYPE_USTRING = 'ustring'
 
-# Domain specific, but practically useful
-TYPE_FILENAME = 'filename'
-
 ##
 ## Parameters
 ##
@@ -75,14 +77,16 @@ PARAM_DIRECTION_OUT = 'out'
 PARAM_DIRECTION_INOUT = 'inout'
 
 type_names = {}
+for name in GIR_TYPES:
+    type_names[name] = name
 
 # C
-type_names['char'] = TYPE_CHAR
-type_names['unsigned char'] = TYPE_UCHAR
+type_names['char'] = TYPE_INT8
+type_names['unsigned char'] = TYPE_UINT8
 type_names['short'] = TYPE_INT16
 type_names['unsigned short'] = TYPE_UINT16
-type_names['int'] = TYPE_INT32
-type_names['unsigned int'] = TYPE_UINT32
+type_names['int'] = TYPE_INT
+type_names['unsigned int'] = TYPE_UINT
 type_names['long'] = TYPE_LONG
 type_names['unsigned long'] = TYPE_ULONG
 type_names['float'] = TYPE_FLOAT
@@ -90,15 +94,8 @@ type_names['double'] = TYPE_DOUBLE
 type_names['char*'] = TYPE_STRING
 type_names['void*'] = TYPE_ANY
 type_names['void'] = TYPE_NONE
-type_names['size_t'] = TYPE_SIZE
-type_names['ssize_t'] = TYPE_SSIZE
-
-# GIR names
-type_names['none'] = TYPE_NONE
-type_names['string'] = TYPE_STRING
-type_names['int32'] = TYPE_INT32
-type_names['uint32'] = TYPE_UINT32
-type_names['any'] = TYPE_ANY
+type_names['size_t'] = TYPE_SIZET
+type_names['ssize_t'] = TYPE_SSIZET
 
 
 def type_name_from_ctype(ctype):
@@ -148,6 +145,7 @@ class Type(Node):
     def __init__(self, name, ctype=None):
         Node.__init__(self, name)
         self.ctype = ctype
+        self.resolved = False
 
 
 class Alias(Node):
@@ -305,3 +303,6 @@ class Sequence(Type):
         Type.__init__(self, name, ctype)
         self.element_type = element_type
         self.transfer = False
+
+    def __repr__(self):
+        return 'Sequence(%r of %r)' % (self.name, self.element_type, )
index 6550a6821a90ff1ad4d270576eceddd873a6dba6..5cf845f61713c2d5d3a65b261bfdae5b6121c792 100644 (file)
@@ -105,7 +105,8 @@ class GIRParser(object):
         obj = klass(node.attrib['name'],
                     node.attrib.get('parent'),
                     node.attrib[_glibns('type-name')],
-                    node.attrib[_glibns('get-type')])
+                    node.attrib[_glibns('get-type')],
+                    node.attrib.get(_cns('type')))
         for method in node.findall(_corens('method')):
             obj.methods.append(self._parse_function(method, Function))
         for ctor in node.findall(_corens('constructor')):
@@ -143,7 +144,8 @@ class GIRParser(object):
     def _parse_boxed(self, node):
         obj = GLibBoxed(node.attrib[_glibns('name')],
                         node.attrib[_glibns('type-name')],
-                        node.attrib[_glibns('get-type')])
+                        node.attrib[_glibns('get-type')],
+                        node.attrib.get(_cns('type')))
         for method in node.findall(_corens('method')):
             obj.methods.append(self._parse_function(method, Function))
         for ctor in node.findall(_corens('constructor')):
index 68872c12d4630458f5f65653c5e7cebdc05eb622..996893e38df3774fcad85806c6cc16e9efa34de4 100644 (file)
@@ -98,10 +98,7 @@ class GIRWriter(XMLWriter):
             attrs.append(('transfer-ownership',
                           str(int(return_.transfer))))
         with self.tagcontext('return-value', attrs):
-            if isinstance(return_.type, Sequence):
-                self._write_sequence(return_.type)
-            else:
-                self._write_type(return_.type)
+            self._write_type(return_.type)
 
     def _write_parameters(self, parameters):
         if not parameters:
@@ -124,23 +121,29 @@ class GIRWriter(XMLWriter):
         with self.tagcontext('parameter', attrs):
             self._write_type(parameter.type)
 
-    def _write_type(self, ntype):
-        attrs = [('name', ntype.name)]
-        # FIXME: figure out if type references a basic type
-        #        or a boxed/class/interface etc. and skip
-        #        writing the ctype if the latter.
-        if ntype.ctype is not None:
-            attrs.append(('c:type', ntype.ctype))
-        self.write_tag('type', attrs)
-
-    def _write_sequence(self, sequence):
-        attrs = []
-        if sequence.transfer:
-            attrs.append(('transfer-ownership',
-                          str(int(sequence.transfer))))
-        with self.tagcontext('sequence', attrs):
-            attrs = [('c:identifier', sequence.element_type)]
-            self.write_tag('element-type', attrs)
+    def _write_type(self, ntype, relation=None):
+        if isinstance(ntype, basestring):
+            typename = ntype
+            type_cname = None
+        else:
+            typename = ntype.name
+            type_cname = ntype.ctype
+        attrs = [('name', typename)]
+        if relation:
+            attrs.append(('relation', relation))
+        if isinstance(ntype, Sequence):
+            if ntype.transfer:
+                attrs.append(('transfer-ownership',
+                              str(int(ntype.transfer))))
+            with self.tagcontext('type', attrs):
+                self._write_type(ntype.element_type, relation="element")
+        else:
+            # 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))
+            self.write_tag('type', attrs)
 
     def _write_enum(self, enum):
         attrs = [('name', enum.name),
index 64ef07d073de8b5c6b2ed99a2e4ee8918da8ed90..93139887dedf0fd44ef74b129f1c71378bd211b8 100644 (file)
@@ -22,9 +22,11 @@ from .ast import Class, Enum, Interface, Member, Node, Property, Struct
 from .ast import (
     type_names,
     TYPE_STRING, TYPE_INT8, TYPE_UINT8, TYPE_INT16, TYPE_UINT16,
-    TYPE_UINT32, TYPE_INT32, TYPE_LONG, TYPE_ULONG, TYPE_INT64,
-    TYPE_UINT64, TYPE_FLOAT, TYPE_INT, TYPE_UINT,
-    TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_ANY, TYPE_SIZE, TYPE_SSIZE)
+    TYPE_INT, TYPE_UINT, TYPE_UINT32, TYPE_INT32, TYPE_LONG,
+    TYPE_ULONG, TYPE_INT64, TYPE_UINT64, TYPE_FLOAT,
+    TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_ANY, TYPE_SSIZET,
+    TYPE_SIZET)
+
 
 # Glib type names
 type_names['gchararray'] = TYPE_STRING
@@ -46,8 +48,8 @@ type_names['gchar*'] = TYPE_STRING
 type_names['gboolean'] = TYPE_BOOLEAN
 type_names['gpointer'] = TYPE_ANY
 type_names['gconstpointer'] = TYPE_ANY
-type_names['gsize'] = TYPE_SIZE
-type_names['gssize'] = TYPE_SSIZE
+type_names['gsize'] = TYPE_SIZET
+type_names['gssize'] = TYPE_SSIZET
 
 
 class GLibEnum(Enum):
@@ -79,34 +81,35 @@ class GLibEnumMember(Member):
 
 class GLibObject(Class):
 
-    def __init__(self, name, parent, type_name, get_type):
+    def __init__(self, name, parent, type_name, get_type, ctype=None):
         Class.__init__(self, name, parent)
-        self.ctype = type_name
         self.type_name = type_name
         self.get_type = get_type
         self.signals = []
+        self.ctype = ctype or type_name
 
 
 class GLibBoxed(Struct):
 
-    def __init__(self, name, type_name, get_type):
+    def __init__(self, name, type_name, get_type, ctype=None):
         Struct.__init__(self, name, get_type)
-        self.ctype = name
         self.constructors = []
         self.methods = []
         self.type_name = type_name
         self.symbol = type_name
         self.get_type = get_type
+        self.ctype = ctype or type_name
 
 
 class GLibInterface(Interface):
 
-    def __init__(self, name, parent, type_name, get_type):
+    def __init__(self, name, parent, type_name, get_type,
+                 ctype=None):
         Interface.__init__(self, name, parent)
-        self.ctype = type_name
         self.type_name = type_name
         self.get_type = get_type
         self.signals = []
+        self.ctype = ctype or type_name
 
 
 class GLibProperty(Property):
index 560eccd0b51b92fc835f7fff70a55552884c1b5d..52651564a2b5e93cade3f382970f61645d9b5fc0 100644 (file)
 #
 
 import ctypes
-import sys
 
 from . import cgobject
-from .odict import odict
 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
-                  Property, Return, Struct, Type, Alias, type_name_from_ctype)
+                  Sequence, Property, Return, Struct, Type, Alias,
+                  type_name_from_ctype)
+from .transformer import Names
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal, type_names)
 from .utils import extract_libtool, to_underscores
 
 
+class Unresolved(object):
+
+    def __init__(self, target):
+        self.target = target
+
+
+class UnknownTypeError(Exception):
+    pass
+
+
 class GLibTransformer(object):
 
-    def __init__(self, transformer):
+    def __init__(self, transformer, noclosure=False):
         self._transformer = transformer
         self._namespace_name = None
-        self._aliases = []
-        self._output_ns = odict()
+        self._names = Names()
         self._libraries = []
         self._failed_types = {}
-        self._internal_types = {}
+        self._noclosure = noclosure
         self._validating = False
 
     # Public API
@@ -61,60 +70,74 @@ class GLibTransformer(object):
 
         # Second pass, delete class structures, resolve
         # all types we now know about
-        nodes = list(self._output_ns.itervalues())
-        for node in nodes:
+        nodes = list(self._names.names.itervalues())
+        for (ns, node) in nodes:
             self._resolve_node(node)
             # associate GtkButtonClass with GtkButton
             if isinstance(node, Struct):
                 self._pair_class_struct(node)
-        for alias in self._aliases:
+        for (ns, alias) in self._names.aliases.itervalues():
             self._resolve_alias(alias)
 
         # Third pass; ensure all types are known
-        if 0:
+        if not self._noclosure:
             self._validate(nodes)
 
         # Create a new namespace with what we found
         namespace = Namespace(namespace.name)
-        namespace.nodes = self._aliases + list(self._output_ns.itervalues())
+        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._output_ns:
+        if (not replace) and node_name in self._names.names:
             return
-        self._output_ns[node_name] = node
+        self._names.names[node_name] = (None, node)
 
     def _remove_attribute(self, name):
-        del self._output_ns[name]
+        del self._names.names[name]
 
     def _get_attribute(self, name):
-        return self._output_ns.get(name)
+        node = self._names.names.get(name)
+        if node:
+            return node[1]
+        return None
 
     def _register_internal_type(self, type_name, node):
-        self._internal_types[type_name] = node
+        self._names.type_names[type_name] = (None, node)
 
     # Helper functions
 
     def _create_type(self, type_id):
         ctype = cgobject.type_name(type_id)
         type_name = type_name_from_ctype(ctype)
+        type_name = type_name.replace('*', '')
         type_name = self._resolve_type_name(type_name)
         return Type(type_name, ctype)
 
+    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 _create_gobject(self, node):
         type_name = 'G' + node.name
         if type_name == 'GObject':
-            parent_type_name = None
+            parent_gitype = None
             symbol = 'intern'
         else:
             type_id = cgobject.type_from_name(type_name)
             parent_type_name = cgobject.type_name(
                 cgobject.type_parent(type_id))
+            parent_gitype = self._resolve_gtypename(parent_type_name)
             symbol = to_underscores(type_name).lower() + '_get_type'
-        node = GLibObject(node.name, parent_type_name, type_name, symbol)
+        node = GLibObject(node.name, parent_gitype, type_name, symbol)
         type_id = cgobject.TYPE_OBJECT
         self._introspect_properties(node, type_id)
         self._introspect_signals(node, type_id)
@@ -141,7 +164,7 @@ class GLibTransformer(object):
             print 'GOBJECT BUILDER: Unhandled node:', node
 
     def _parse_alias(self, alias):
-        self._aliases.append(alias)
+        self._names.aliases[alias.name] = (None, alias)
 
     def _parse_enum(self, enum):
         self._add_attribute(enum)
@@ -247,10 +270,11 @@ class GLibTransformer(object):
         elif struct.name in g_internal_names:
             # Avoid duplicates
             return
-        node = self._output_ns.get(struct.name)
+        node = self._names.names.get(struct.name)
         if node is None:
             self._add_attribute(struct, replace=True)
             return
+        (ns, node) = node
         node.fields = struct.fields[:]
 
     def _parse_callback(self, callback):
@@ -269,7 +293,7 @@ class GLibTransformer(object):
         ctype = self._transformer.ctype_of(param).replace('*', '')
         uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
         if uscored in self._failed_types:
-            print >> sys.stderr, "failed type: %r" % (param, )
+            print "Warning: failed type: %r" % (param, )
             return True
         return False
 
@@ -280,25 +304,24 @@ class GLibTransformer(object):
 
         if self._arg_is_failed(maybe_class):
             print "WARNING: deleting no-type %r" % (maybe_class.name, )
-            del self._output_ns[maybe_class.name]
+            del self._names.names[maybe_class.name]
             return
 
         name = self._resolve_type_name(name)
         resolved = self._transformer.strip_namespace_object(name)
-        pair_class = self._output_ns.get(resolved)
+        pair_class = self._get_attribute(resolved)
         if pair_class and isinstance(pair_class,
                                      (GLibObject, GLibBoxed, GLibInterface)):
-
-            del self._output_ns[maybe_class.name]
+            del self._names.names[maybe_class.name]
             for field in maybe_class.fields[1:]:
                 pair_class.fields.append(field)
             return
         name = self._transformer.strip_namespace_object(maybe_class.name)
-        pair_class = self._output_ns.get(name)
+        pair_class = self._get_attribute(name)
         if pair_class and isinstance(pair_class,
                                      (GLibObject, GLibBoxed, GLibInterface)):
 
-            del self._output_ns[maybe_class.name]
+            del self._names.names[maybe_class.name]
 
     # Introspection
 
@@ -346,9 +369,10 @@ class GLibTransformer(object):
     def _introspect_object(self, type_id, symbol):
         type_name = cgobject.type_name(type_id)
         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
+        parent_gitype = self._resolve_gtypename(parent_type_name)
         node = GLibObject(
             self._transformer.strip_namespace_object(type_name),
-            self._resolve_type_name(parent_type_name),
+            parent_gitype,
             type_name, symbol)
         self._introspect_properties(node, type_id)
         self._introspect_signals(node, type_id)
@@ -358,9 +382,13 @@ class GLibTransformer(object):
     def _introspect_interface(self, type_id, symbol):
         type_name = cgobject.type_name(type_id)
         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
+        if parent_type_name == 'GInterface':
+            parent_gitype = None
+        else:
+            parent_gitype = self._resolve_gtypename(parent_type_name)
         node = GLibInterface(
             self._transformer.strip_namespace_object(type_name),
-            self._resolve_type_name(parent_type_name),
+            parent_gitype,
             type_name, symbol)
         self._introspect_properties(node, type_id)
         self._introspect_signals(node, type_id)
@@ -411,38 +439,43 @@ class GLibTransformer(object):
     # Resolver
 
     def _resolve_type_name(self, type_name):
-        type_name = type_name.replace('*', '')
-        possible_name = self._transformer.resolve_type_name(type_name)
-        if possible_name != type_name:
-            return possible_name
-        possible_node = self._internal_types.get(type_name)
-        if possible_node:
-            return possible_node.name
-        return type_name
-
-    def _validate_type(self, name):
+        res = self._transformer.resolve_type_name_full
+        try:
+            return res(type_name, None, self._names)
+        except KeyError, e:
+            return self._transformer.resolve_type_name(type_name, None)
+
+    def _validate_type_name(self, name):
         if name in type_names:
             return True
         if name.find('.') >= 0:
             return True
-        if name in self._internal_types:
+        if name in self._names.aliases:
             return True
-        if name in self._aliases:
-            return True
-        if name in self._output_ns:
+        if name in self._names.names:
             return True
         return False
 
+    def _validate_type(self, ptype):
+        if isinstance(ptype, Sequence):
+            etype = ptype.element_type
+            if isinstance(etype, Sequence):
+                return self._validate_type(etype)
+            return self._validate_type_name(etype)
+        return self._validate_type_name(ptype.name)
+
+    def _resolve_param_type_validate(self, ptype):
+        ptype = self._resolve_param_type(ptype)
+        if self._validating and not self._validate_type(ptype):
+            raise UnknownTypeError("Unknown type %r" % (ptype, ))
+        return ptype
+
     def _resolve_param_type(self, ptype):
-        ptype.name = ptype.name.replace('*', '')
-        type_name = ptype.name
-        possible_node = self._internal_types.get(type_name)
-        if possible_node:
-            ptype.name = possible_node.name
-        else:
-            ptype = self._transformer.resolve_param_type(ptype)
-        if self._validating and not self._validate_type(ptype.name):
-            raise ValueError("Unknown type %r" % (ptype.name, ))
+        try:
+            return self._transformer.resolve_param_type_full(ptype,
+                                                             self._names)
+        except KeyError, e:
+            return self._transformer.resolve_param_type(ptype)
         return ptype
 
     def _resolve_node(self, node):
@@ -463,12 +496,22 @@ class GLibTransformer(object):
         for field in node.fields:
             self._resolve_field(field)
 
+    def _resolve_parent(self, node):
+        if not isinstance(node, (GLibInterface, GLibObject)):
+            raise AssertionError
+        if isinstance(node.parent, Unresolved):
+            node.parent = \
+                self._transformer.gtypename_to_giname(node.parent.target,
+                                                      self._names)
+
     def _resolve_glib_interface(self, node):
+        self._resolve_parent(node)
         self._resolve_methods(node.methods)
         self._resolve_properties(node.properties)
         self._resolve_signals(node.signals)
 
     def _resolve_glib_object(self, node):
+        self._resolve_parent(node)
         self._resolve_constructors(node.constructors)
         self._resolve_methods(node.methods)
         self._resolve_properties(node.properties)
@@ -511,23 +554,23 @@ class GLibTransformer(object):
             return
         field.type = self._resolve_param_type(field.type)
 
-    def _resolve_alias(self, field):
-        field.target = self._resolve_type_name(field.target)
+    def _resolve_alias(self, alias):
+        alias.target = self._resolve_type_name(alias.target)
 
     # Validation
 
     def _validate(self, nodes):
-        nodes = list(self._output_ns.itervalues())
+        nodes = list(self._names.names.itervalues())
         i = 0
         self._validating = True
         while True:
             print "Type resolution; pass=%d" % (i, )
             initlen = len(nodes)
-            nodes = list(self._output_ns.itervalues())
+            nodes = list(self._names.names.itervalues())
             for node in nodes:
                 try:
                     self._resolve_node(node)
-                except ValueError, e:
+                except UnknownTypeError, e:
                     print "WARNING: %s: Deleting %r" % (e, node)
                     self._remove_attribute(node.name)
             if len(nodes) == initlen:
index ac6301b5cfe739e2f9e46cdac6a6eb5e87dd16b5..3b117fff99aa5e08c56f649ea7684e57e748e4bd 100644 (file)
@@ -20,8 +20,8 @@
 
 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
                            Parameter, Return, Sequence, Struct, Field,
-                           Type, Alias, Interface, Class,
-                           type_name_from_ctype)
+                           Type, Alias, Interface, Class, Node,
+                           type_name_from_ctype, type_names)
 from .glibast import GLibBoxed
 from giscanner.sourcescanner import (
     SourceSymbol, ctype_name, CTYPE_POINTER,
@@ -30,6 +30,7 @@ from giscanner.sourcescanner import (
     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
     CSYMBOL_TYPE_MEMBER)
+from .odict import odict
 from .utils import strip_common_prefix
 
 
@@ -37,23 +38,31 @@ class SkipError(Exception):
     pass
 
 
+class Names(object):
+    names = property(lambda self: self._names)
+    aliases = property(lambda self: self._aliases)
+    type_names = property(lambda self: self._type_names)
+    ctypes = property(lambda self: self._ctypes)
+
+    def __init__(self):
+        super(Names, self).__init__()
+        self._names = odict() # Maps from GIName -> (namespace, node)
+        self._aliases = {} # Maps from GIName -> GIName
+        self._type_names = {} # Maps from GTName -> (namespace, node)
+        self._ctypes = {} # Maps from CType -> (namespace, node)
+
+
 class Transformer(object):
 
     def __init__(self, generator, namespace_name):
         self.generator = generator
         self._namespace = Namespace(namespace_name)
-        self._output_ns = {}
-        self._alias_names = {} # Maps from GIName -> GIName
-        self._type_names = {} # Maps from GTName -> (namespace, node)
-        self._ctype_names = {} # Maps from CType -> (namespace, node)
+        self._names = Names()
         self._typedefs_ns = {}
         self._strip_prefix = ''
 
-    def get_type_names(self):
-        return self._type_names
-
-    def get_alias_names(self):
-        return self._alias_names
+    def get_names(self):
+        return self._names
 
     def set_strip_prefix(self, strip_prefix):
         self._strip_prefix = strip_prefix
@@ -77,15 +86,14 @@ class Transformer(object):
         nsname = parser.get_namespace_name()
         for node in parser.get_nodes():
             if isinstance(node, Alias):
-                self._alias_names[node.ctype] = (nsname, node)
+                self._names.aliases[node.name] = (nsname, node)
             elif isinstance(node, (GLibBoxed, Interface, Class)):
-                self._type_names[node.type_name] = (nsname, node)
-            elif hasattr(node, 'ctype'):
-                self._ctype_names[node.ctype] = (nsname, node)
+                self._names.type_names[node.type_name] = (nsname, node)
+            self._names.names[node.name] = (nsname, node)
+            if hasattr(node, 'ctype'):
+                self._names.ctypes[node.ctype] = (nsname, node)
             elif hasattr(node, 'symbol'):
-                self._ctype_names[node.symbol] = (nsname, node)
-            else:
-                self._type_names[node.name] = (nsname, node)
+                self._names.ctypes[node.symbol] = (nsname, node)
 
     def strip_namespace_object(self, name):
         prefix = self._namespace.name.lower()
@@ -101,7 +109,7 @@ class Transformer(object):
         if node.name.startswith('_'):
             return
         self._namespace.nodes.append(node)
-        self._output_ns[node.name] = node
+        self._names.names[node.name] = (None, node)
 
     def _strip_namespace_func(self, name):
         prefix = self._namespace.name.lower() + '_'
@@ -159,7 +167,7 @@ class Transformer(object):
         enum_name = symbol.ident[-len(enum_name):]
         enum_name = self._remove_prefix(enum_name)
         enum = Enum(enum_name, symbol.ident, members)
-        self._type_names[symbol.ident] = (None, enum)
+        self._names.type_names[symbol.ident] = (None, enum)
         return enum
 
     def _create_object(self, symbol):
@@ -245,6 +253,7 @@ class Transformer(object):
         elif ctype == 'FILE*':
             raise SkipError
         type_name = type_name_from_ctype(ctype)
+        type_name = type_name.replace('*', '')
         resolved_type_name = self.resolve_type_name(type_name)
         return Type(resolved_type_name, ctype)
 
@@ -278,7 +287,10 @@ class Transformer(object):
                 return_.transfer = True
             elif option.startswith('seq '):
                 value, element_options = option[3:].split(None, 2)
-                element_type = self._parse_type_annotation(value)
+                c_element_type = self._parse_type_annotation(value)
+                element_type = c_element_type.replace('*', '')
+                element_type = self.resolve_type_name(element_type,
+                                                      c_element_type)
                 seq = Sequence(rtype.name,
                                type_name_from_ctype(rtype.name),
                                element_type)
@@ -336,21 +348,55 @@ class Transformer(object):
             return item.name
         return '%s.%s' % (nsname, item.name)
 
-    def resolve_type_name(self, type_name, ctype=None):
-        type_name = type_name.replace('*', '')
+    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
         type_name = self.strip_namespace_object(type_name)
-        resolved = self._alias_names.get(type_name)
+        resolved = names.aliases.get(type_name)
         if resolved:
             return self._typepair_to_str(resolved)
-        resolved = self._type_names.get(type_name)
+        resolved = names.names.get(type_name)
         if resolved:
             return self._typepair_to_str(resolved)
         if ctype:
             ctype = ctype.replace('*', '')
-            resolved = self._ctype_names.get(ctype)
+            resolved = names.ctypes.get(ctype)
             if resolved:
                 return self._typepair_to_str(resolved)
-        return type_name
+        raise KeyError("failed to find %r" % (type_name, ))
+
+    def resolve_type_name_full(self, type_name, ctype,
+                               names):
+        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:
+                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:
+            return self._typepair_to_str(resolved)
+        resolved = self._names.type_names.get(gtname)
+        if resolved:
+            return self._typepair_to_str(resolved)
+        raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
 
     def ctype_of(self, obj):
         if hasattr(obj, 'ctype'):
@@ -360,7 +406,22 @@ class Transformer(object):
         else:
             return None
 
-    def resolve_param_type(self, ptype):
-        ptype.name = self.resolve_type_name(ptype.name,
-                                            self.ctype_of(ptype))
+    def resolve_param_type_full(self, ptype, names):
+        if isinstance(ptype, Sequence):
+            ptype.element_type = \
+                self.resolve_param_type_full(ptype.element_type, names)
+        elif isinstance(ptype, Node):
+            ptype.name = self.resolve_type_name_full(ptype.name,
+                                                     self.ctype_of(ptype),
+                                                     names)
+        elif isinstance(ptype, basestring):
+            return self.resolve_type_name_full(ptype, None, names)
+        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
index aefee4380677556d2bba315d5f8f6d485d170ca2..43289c85f7dd9ce49793605bd7b1e3feaa4d71be 100644 (file)
       </method>
       <method name="get_strings" c:identifier="foo_object_get_strings">
         <return-value>
-          <sequence transfer-ownership="1">
-            <element-type c:identifier="char*"/>
-          </sequence>
+          <type name="GLib.List" transfer-ownership="1">
+            <type name="string" relation="element"/>
+          </type>
         </return-value>
         <parameters>
           <parameter name="object">
       </method>
       <method name="get_objects" c:identifier="foo_object_get_objects">
         <return-value>
-          <sequence transfer-ownership="1">
-            <element-type c:identifier="FooObject*"/>
-          </sequence>
+          <type name="GLib.SList" transfer-ownership="1">
+            <type name="Object" relation="element"/>
+          </type>
         </return-value>
         <parameters>
           <parameter name="object">
             <type name="Object" c:type="FooObject*"/>
           </parameter>
           <parameter name="first_param">
-            <type name="int32" c:type="int"/>
+            <type name="int" c:type="int"/>
           </parameter>
         </parameters>
       </callback>
     </enumeration>
     <function name="enum_type_method" c:identifier="foo_enum_type_method">
       <return-value>
-        <type name="int32" c:type="int"/>
+        <type name="int" c:type="int"/>
       </return-value>
       <parameters>
         <parameter name="foo_enum">
       </return-value>
       <parameters>
         <parameter name="x">
-          <type name="int32" c:type="int"/>
+          <type name="int" c:type="int"/>
         </parameter>
       </parameters>
     </function>
       <member name="deux" value="2" c:identifier="FOO_ENUM_DEUX"/>
       <member name="trois" value="3" c:identifier="FOO_ENUM_TROIS"/>
     </enumeration>
-    <glib:boxed c:type="Boxed"
+    <glib:boxed c:type="FooBoxed"
                 glib:name="Boxed"
                 glib:type-name="FooBoxed"
                 glib:get-type="foo_boxed_get_type">
         <type name="StructPrivate" c:type="FooStructPrivate*"/>
       </field>
       <field name="member">
-        <type name="int32" c:type="int"/>
+        <type name="int" c:type="int"/>
       </field>
     </record>
     <record name="StructPrivate" c:type="FooStructPrivate"/>
index 179266127513530cbffb7d2d8e5dae0afa5dc40c..31c7129b6d3c432c0ca6a2a72108c22bf35886db 100755 (executable)
@@ -67,6 +67,10 @@ 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",# default=True,
+                      help="do not delete unknown types")
+
 
     group = optparse.OptionGroup(parser, "Preprocessor options")
     group.add_option("-I", help="Pre-processor include file",
@@ -137,7 +141,7 @@ def main(args):
 
     # Transform the C AST nodes into higher level
     # GLib/GObject nodes
-    glibtransformer = GLibTransformer(transformer)
+    glibtransformer = GLibTransformer(transformer, noclosure=options.noclosure)
     if options.libraries:
         for library in options.libraries:
             glibtransformer.add_library(library)