[gobject-introspection/wip/transformer] pair up methods/constructors



commit ca9e1df4b881c923284f0776929dbc44431eed1d
Author: Colin Walters <walters verbum org>
Date:   Wed Jul 21 16:09:05 2010 -0400

    pair up methods/constructors

 giscanner/annotationparser.py |    4 +
 giscanner/ast.py              |   84 +++++++++-
 giscanner/girwriter.py        |    7 +-
 giscanner/glibast.py          |   14 ++-
 giscanner/glibtransformer.py  |  364 ++++++++++++++++------------------------
 giscanner/transformer.py      |   56 +++++--
 6 files changed, 286 insertions(+), 243 deletions(-)
---
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 7ee6c0e..0e98556 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -375,6 +375,7 @@ class AnnotationApplier(object):
         block = self._blocks.get(interface.type_name)
         self._parse_node_common(interface, block)
         self._parse_methods(interface, interface.methods)
+        self._parse_methods(interface, interface.static_methods)
         self._parse_vfuncs(interface, interface.virtual_methods)
         self._parse_properties(interface, interface.properties)
         self._parse_signals(interface, interface.signals)
@@ -387,6 +388,7 @@ class AnnotationApplier(object):
         self._parse_node_common(record, block)
         self._parse_constructors(record.constructors)
         self._parse_methods(record, record.methods)
+        self._parse_methods(record, record.static_methods)
         self._parse_fields(record, record.fields, block)
         if block:
             record.doc = block.comment
@@ -396,6 +398,7 @@ class AnnotationApplier(object):
         self._parse_node_common(boxed, block)
         self._parse_constructors(boxed.constructors)
         self._parse_methods(boxed, boxed.methods)
+        self._parse_methods(boxed, boxed.static_methods)
         if block:
             boxed.doc = block.comment
 
@@ -405,6 +408,7 @@ class AnnotationApplier(object):
         self._parse_fields(union, union.fields, block)
         self._parse_constructors(union.constructors)
         self._parse_methods(union, union.methods)
+        self._parse_methods(union, union.static_methods)
         if block:
             union.doc = block.comment
 
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 92a047f..d37af5a 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -20,11 +20,9 @@
 #
 
 from .odict import odict
+from .utils import to_underscores
 
 class Type(object):
-    resolved = property(lambda self: (self.target_fundamental or
-                                      self.target_giname or
-                                      self.target_foreign))
     """A Type can be either:
 * A reference to a node (target_giname)
 * A reference to a "fundamental" type like 'utf8'
@@ -32,6 +30,11 @@ class Type(object):
 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,
@@ -60,6 +63,10 @@ In this case, the ctype must be specified.
         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 is_equiv(self, typeval):
         """Return True if the specified types are compatible at
         an introspection level, disregarding their C types.
@@ -87,6 +94,14 @@ In this case, the ctype must be specified.
                     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, )
+
 ######
 ## Fundamental types
 ######
@@ -241,6 +256,7 @@ class Namespace(object):
         self.version = version
         self.c_prefix = c_prefix or name
         self._lower_c_prefix = c_prefix.lower()
+        self.uscore_prefix = to_underscores(c_prefix).lower()
         self._names = odict() # Maps from GIName -> node
         self._aliases = {} # Maps from GIName -> GIName
         self._type_names = {} # Maps from GTName -> node
@@ -296,12 +312,19 @@ identifier string."""
             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)
 
@@ -314,6 +337,15 @@ identifier string."""
     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):
 
@@ -390,6 +422,15 @@ GIName.  It's possible for nodes to contain or point to other nodes."""
         if symbol.source_filename:
             self.add_file_position(symbol.source_filename, symbol.line, -1)
 
+    def walk(self, callback, chain):
+        if not callback(self, chain):
+            return False
+        chain.append(self)
+        self._walk(callback, chain)
+        chain.pop()
+
+    def _walk(self, callback, chain):
+        pass
 
 class Callable(Node):
 
@@ -576,6 +617,15 @@ class Record(Node):
         self.symbol = symbol
         self.disguised = disguised
         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)
@@ -595,7 +645,6 @@ class Field(Annotated):
         self.bits = bits
         self.anonymous_node = anonymous_node
 
-
 class Class(Node):
 
     def __init__(self, name, parent, is_abstract):
@@ -622,7 +671,15 @@ class Class(Node):
         self.properties = filter(pred, self.properties)
         self.fields = filter(pred, self.fields)
 
-
+    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):
 
@@ -631,12 +688,20 @@ class Interface(Node):
         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 = []
 
+    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):
 
@@ -673,4 +738,13 @@ class Union(Node):
         self.fields = []
         self.constructors = []
         self.methods = []
+        self.static_methods = []
         self.symbol = symbol
+
+    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/girwriter.py b/giscanner/girwriter.py
index 7d73364..09d27bd 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -176,7 +176,6 @@ and/or use gtk-doc annotations. ''')
         attrs = []
         if hasattr(func, 'symbol'):
             attrs.append(('c:identifier', func.symbol))
-        print "writing %r" % (func, )
         self._write_callable(func, tag_name, attrs)
 
     def _write_method(self, method):
@@ -399,6 +398,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)]
@@ -461,6 +462,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 = []
@@ -481,6 +484,8 @@ 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 field.anonymous_node:
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index 72be10f..69f1c0b 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -86,8 +86,6 @@ class GLibObject(Class):
         self.get_value_func = None
         self.signals = []
         self.ctype = ctype or type_name
-        # Unresolved state
-        self.parent_gtype_names = []
 
 
 class GLibBoxed:
@@ -97,6 +95,8 @@ class GLibBoxed:
         self.get_type = get_type
 
 
+
+
 class GLibBoxedStruct(Record, GLibBoxed):
 
     def __init__(self, name, type_name, get_type, ctype=None):
@@ -118,9 +118,19 @@ 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):
 
     def __init__(self, name, parent, type_name, get_type,
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index 10a980d..92c35d8 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,7 +28,7 @@ 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, default_array_types,
+                  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,
@@ -79,7 +79,6 @@ class GLibTransformer(object):
         self._get_type_functions = []
         self._error_quark_functions = []
         self._gtype_data = {}
-        self._failed_types = {}
         self._boxed_types = {}
         self._private_internal_types = {}
 
@@ -126,26 +125,35 @@ class GLibTransformer(object):
         for child in root:
             self._introspect_type(child)
 
-        # Pair boxed structures; doesn't depend on anything else.
-        for boxed in self._boxed_types.itervalues():
-            self._pair_boxed_type(boxed)
-
-        # Second pass; Initial type resolution
+        # 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)
+
+        # 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():
-            self.walk_node(node, self._pass_type_resolution, [])
+            uscored = to_underscores_noprefix(node.name).lower()
+            if isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+                self._uscore_type_names[uscored] = node
 
-        # Now move around the functions that are actually methods
-        #for node in self._namespace:
-        #    if isinstance(node, Function):
-        #        self._pair_function(node)
+        for node in list(self._namespace.itervalues()):
+            # Discover which functions are actually methods
+            if isinstance(node, Function):
+                self._pair_function(node)
+
+        self._namespace.walk(self._pass3_callable)
 
         # Third pass
         for node in self._namespace.itervalues():
-            if isinstance(node, Record):
-                self._pass3_pair_class_record(node)
-            elif isinstance(node, Callable):
-                self._pass3_callable(node)
-        self._resolve_quarks()
+            self._resolve_quarks()
 
         return self._namespace
 
@@ -176,10 +184,10 @@ class GLibTransformer(object):
             for parent in node.parent_chain:
                 try:
                     self._transformer.resolve_type(parent)
-                    resolved_parent = parent
                 except ValueError, e:
                     continue
-            node.parent = resolved_parent
+                node.parent = parent
+                break
             for prop in node.properties:
                 self._transformer.resolve_type(prop.type)
             for sig in node.signals:
@@ -280,7 +288,7 @@ blob containing data gleaned from GObject's primitive introspection."""
             parent_gitype = None
             symbol = 'intern'
         elif type_name == 'GInitiallyUnowned':
-            parent_gitype = 'GLib.Object'
+            parent_gitype = Type(target_giname='GLib.Object')
             symbol = 'g_initially_unowned_get_type'
         else:
             assert False
@@ -346,123 +354,110 @@ blob containing data gleaned from GObject's primitive introspection."""
         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._namespace.remove(func)
-        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 _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 _parse_method_common(self, func, is_method):
-        # Skip _get_type functions, we processed them
-        # already
+    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'):
-            return None
+            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
 
-        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._namespace.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
-            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
+    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()
 
-        name = self._transformer.remove_prefix(remove_prefix)
-        klass = self._namespace.get(name)
-        if klass is None:
-            return
+    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
 
-        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._namespace.remove(func)
-        # 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
+    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:
-            klass.constructors.append(func)
-        return func
+            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 _initparse_gobject_record(self, record):
         # Special handling for when we're parsing GObject
@@ -483,25 +478,12 @@ blob containing data gleaned from GObject's primitive introspection."""
         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 _pass3_pair_class_record(self, maybe_class):
+    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._namespace.get(name)
         if (not pair_class or
             not isinstance(pair_class, (GLibObject, GLibInterface))):
@@ -597,15 +579,12 @@ blob containing data gleaned from GObject's primitive introspection."""
         # 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.
-        #
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
         node = GLibObject(self._transformer.remove_prefix(type_name),
                           None,
                           type_name,
                           xmlnode.attrib['get-type'], is_abstract)
-        node.parent_gtype_names = xmlnode.attrib['parents'].split(',')
+        self._parse_parents(xmlnode, node)
         self._introspect_properties(node, xmlnode)
         self._introspect_signals(node, xmlnode)
         self._introspect_implemented_interfaces(node, xmlnode)
@@ -636,7 +615,6 @@ blob containing data gleaned from GObject's primitive introspection."""
         # 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
 
     def _introspect_implemented_interfaces(self, node, xmlnode):
         gt_interfaces = []
@@ -680,6 +658,14 @@ blob containing data gleaned from GObject's primitive introspection."""
             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
@@ -689,20 +675,12 @@ blob containing data gleaned from GObject's primitive introspection."""
             return
 
         type_name = xmlnode.attrib['name']
-
-        if 'parents' in xmlnode.attrib:
-            parent_types = map(lambda s: self._transformer.create_type(s),
-                               xmlnode.attrib['parents'].split(','))
-        else:
-            parent_types = []
-        parent_type = parent_types[0] if parent_types else None
         is_abstract = bool(xmlnode.attrib.get('abstract', False))
-        node = GLibObject(
-            self._transformer.remove_prefix(type_name),
-            parent_type,
-            type_name,
-            xmlnode.attrib['get-type'], is_abstract)
-        node.parent_chain = parent_types
+        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)
 
@@ -743,56 +721,6 @@ blob containing data gleaned from GObject's primitive introspection."""
 
     # Node walking
 
-    def walk_node(self, node, callback, chain):
-        assert isinstance(node, Node)
-        if not callback(node, chain):
-            return
-        chain.append(node)
-        def _subwalk(subnode):
-            self._walk(subnode, callback, chain)
-        if isinstance(node, 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)
-        elif isinstance(node, Interface):
-            for meth in node.methods:
-                _subwalk(meth)
-            for meth in node.virtual_methods:
-                _subwalk(meth)
-        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)
-
-        chain.pop()
-
-    def _pair_function(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 _handle_closure(self, param, closure_idx, closure_param):
         if (closure_param.type is TYPE_ANY and
@@ -809,7 +737,9 @@ blob containing data gleaned from GObject's primitive introspection."""
             return True
         return False
 
-    def _pass3_callable(self, node):
+    def _pass3_callable(self, node, chain):
+        if not isinstance(node, Callable):
+            return
         self._pass3_callable_callbacks(node)
         self._pass3_callable_throws(node)
 
@@ -910,7 +840,5 @@ blob containing data gleaned from GObject's primitive introspection."""
     # 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 node in self._namespace.itervalues():
-            self.walk_node(node, self._analyze_node, [])
-        for node in self._namespace.itervalues():
-            self.walk_node(node, self._introspectable_pass2, [])
+        self._namespace.walk(self._analyze_node)
+        self._namespace.walk(self._introspectable_pass2)
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 0ab6839..6129b1a 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -223,7 +223,7 @@ currently-scanned namespace is first."""
         for ns in self._includes.itervalues():
             yield ns
 
-    def _split_ctype(self, ident):
+    def split_ctype(self, ident):
         """Given a StudlyCaps string identifier like FooBar, return a
 pair of (namespace, stripped_identifier) or raise ValueError."""
         matches = []
@@ -235,15 +235,29 @@ pair of (namespace, stripped_identifier) or raise ValueError."""
             return matches[0]
         raise ValueError("Unknown namespace for identifier %r" % (ident, ))
 
-    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)
+    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_csymbol_or_warn(self, symbol):
+        try:
+            (ns, name) = self.split_csymbol(symbol.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
+        return name
 
     def remove_prefix(self, name, isfunction=False):
         # when --strip-prefix=g:
@@ -276,10 +290,14 @@ pair of (namespace, stripped_identifier) or raise ValueError."""
             return self._create_member(symbol)
         elif stype == CSYMBOL_TYPE_UNION:
             return self._create_union(symbol)
-        # FIXME - we should require an annotation on
-        # #defines to have them be constants, really
-        #elif stype == CSYMBOL_TYPE_CONST:
-        #    return self._create_const(symbol)
+        # FIXME - we need to require an annotation on
+        # #defines to have them be constants, otherwise
+        # namespace explosion can occur
+        elif stype == CSYMBOL_TYPE_CONST:
+            pass
+        # Ignore variable declarations in the header
+        elif stype == CSYMBOL_TYPE_OBJECT:
+            pass
         else:
             print 'transformer: unhandled symbol: %r' % (symbol, )
 
@@ -336,7 +354,9 @@ pair of (namespace, stripped_identifier) or raise ValueError."""
     def _create_function(self, symbol):
         parameters = list(self._create_parameters(symbol.base_type))
         return_ = self._create_return(symbol.base_type.base_type)
-        name = self._strip_namespace_func(symbol.ident)
+        name = self._strip_csymbol_or_warn(symbol)
+        if not name:
+            return None
         func = Function(name, return_, parameters, False, symbol.ident)
         func.add_symbol_reference(symbol)
         return func
@@ -511,7 +531,9 @@ pair of (namespace, stripped_identifier) or raise ValueError."""
         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_csymbol_or_warn(symbol)
+        if not name:
+            return None
         if symbol.const_string is not None:
             typeval = TYPE_STRING
             value = symbol.const_string
@@ -621,7 +643,7 @@ pair of (namespace, stripped_identifier) or raise ValueError."""
         elif not typeval.resolved:
             pointer_stripped = typeval.ctype.replace('*', '')
             try:
-                (ns, name) = self._split_ctype(pointer_stripped)
+                (ns, name) = self.split_ctype(pointer_stripped)
             except ValueError, e:
                 raise TypeResolutionException(e)
             typeval.target_giname = '%s.%s' % (ns.name, name)



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