[gobject-introspection] scanner: Better handling of GType names



commit f9db355f2f1f55307e32e0f9f2d787f46941681b
Author: Colin Walters <walters verbum org>
Date:   Wed Sep 1 17:55:30 2010 -0400

    scanner: Better handling of GType names
    
    Before, Type instances could be indeterminate, holding a "ctype",
    which means "This is some unresolved string".  However, we also get
    data from GType, so add gtype_name as another indeterminate state.
    
    Clean up how we create and resolve Type instances from GType data.

 giscanner/ast.py                |   18 +++++++++++++++---
 giscanner/gdumpparser.py        |   12 ++++++------
 giscanner/introspectablepass.py |    5 ++++-
 giscanner/transformer.py        |   31 ++++++++++++++++++++++++++++---
 4 files changed, 53 insertions(+), 13 deletions(-)
---
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 915c3ed..c819d02 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -27,12 +27,14 @@ class Type(object):
 * A reference to a node (target_giname)
 * A reference to a "fundamental" type like 'utf8'
 * A "foreign" type - this can be any string."
-If none are specified, then it's in an "unresolved" state.
-In this case, the ctype must be specified.
+If none are specified, then it's in an "unresolved" state.  An
+unresolved type can have two data sources; a "ctype" which comes
+from a C type string, or a gtype_name (from g_type_name()).
 """
 
     def __init__(self,
                  ctype=None,
+                 gtype_name=None,
                  target_fundamental=None,
                  target_giname=None,
                  target_foreign=None,
@@ -40,6 +42,7 @@ In this case, the ctype must be specified.
                  is_const=False,
                  origin_symbol=None):
         self.ctype = ctype
+        self.gtype_name = gtype_name
         self.origin_symbol = origin_symbol
         if _target_unknown:
             assert isinstance(self, TypeUnknown)
@@ -55,7 +58,7 @@ In this case, the ctype must be specified.
             assert target_giname is None
             assert target_fundamental is None
         else:
-            assert ctype is not None
+            assert (ctype is not None) or (gtype_name is not None)
         self.target_fundamental = target_fundamental
         self.target_giname = target_giname
         self.target_foreign = target_foreign
@@ -67,6 +70,15 @@ In this case, the ctype must be specified.
                 self.target_giname or
                 self.target_foreign)
 
+    @property
+    def unresolved_string(self):
+        if self.ctype:
+            return self.ctype
+        elif self.gtype_name:
+            return self.gtype_name
+        else:
+            assert False
+
     def get_giname(self):
         assert self.target_giname is not None
         return self.target_giname.split('.')[1]
diff --git a/giscanner/gdumpparser.py b/giscanner/gdumpparser.py
index ce6f9a2..ca9b38a 100644
--- a/giscanner/gdumpparser.py
+++ b/giscanner/gdumpparser.py
@@ -301,7 +301,7 @@ blob containing data gleaned from GObject's primitive introspection."""
         self._introspect_signals(node, xmlnode)
         for child in xmlnode.findall('prerequisite'):
             name = child.attrib['name']
-            prereq = self._transformer.create_type_from_user_string(name)
+            prereq = self._transformer.create_type_from_gtype_name(name)
             node.prerequisites.append(prereq)
         # GtkFileChooserEmbed is an example of a private interface, we
         # just filter them out
@@ -321,7 +321,7 @@ blob containing data gleaned from GObject's primitive introspection."""
     def _introspect_implemented_interfaces(self, node, xmlnode):
         gt_interfaces = []
         for interface in xmlnode.findall('implements'):
-            gitype = self._transformer.create_type_from_user_string(interface.attrib['name'])
+            gitype = self._transformer.create_type_from_gtype_name(interface.attrib['name'])
             gt_interfaces.append(gitype)
         node.interfaces = gt_interfaces
 
@@ -335,7 +335,7 @@ blob containing data gleaned from GObject's primitive introspection."""
             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
             node.properties.append(ast.Property(
                 pspec.attrib['name'],
-                self._transformer.create_type_from_user_string(ctype),
+                self._transformer.create_type_from_gtype_name(ctype),
                 readable, writable, construct, construct_only,
                 ctype,
                 ))
@@ -344,7 +344,7 @@ blob containing data gleaned from GObject's primitive introspection."""
     def _introspect_signals(self, node, xmlnode):
         for signal_info in xmlnode.findall('signal'):
             rctype = signal_info.attrib['return']
-            rtype = self._transformer.create_type_from_user_string(rctype)
+            rtype = self._transformer.create_type_from_gtype_name(rctype)
             return_ = ast.Return(rtype)
             parameters = []
             for i, parameter in enumerate(signal_info.findall('param')):
@@ -353,7 +353,7 @@ blob containing data gleaned from GObject's primitive introspection."""
                 else:
                     argname = 'p%s' % (i-1, )
                 pctype = parameter.attrib['type']
-                ptype = self._transformer.create_type_from_user_string(pctype)
+                ptype = self._transformer.create_type_from_gtype_name(pctype)
                 param = ast.Parameter(argname, ptype)
                 param.transfer = ast.PARAM_TRANSFER_NONE
                 parameters.append(param)
@@ -364,7 +364,7 @@ blob containing data gleaned from GObject's primitive introspection."""
     def _parse_parents(self, xmlnode, node):
         parents_str = xmlnode.attrib.get('parents', '')
         if parents_str != '':
-            parent_types = map(lambda s: self._transformer.create_type_from_user_string(s),
+            parent_types = map(lambda s: self._transformer.create_type_from_gtype_name(s),
                                parents_str.split(','))
         else:
             parent_types = []
diff --git a/giscanner/introspectablepass.py b/giscanner/introspectablepass.py
index f3ab7e6..7843411 100644
--- a/giscanner/introspectablepass.py
+++ b/giscanner/introspectablepass.py
@@ -64,7 +64,8 @@ class IntrospectablePass(object):
             target = None
 
         if not node.type.resolved:
-            self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+            self._parameter_warning(parent, node,
+"Unresolved type: %r" % (node.type.unresolved_string, ))
             parent.introspectable = False
             return
 
@@ -113,6 +114,8 @@ class IntrospectablePass(object):
     def _type_is_introspectable(self, typeval, warn=False):
         if not typeval.resolved:
             return False
+        if isinstance(typeval, ast.TypeUnknown):
+            return False
         if isinstance(typeval, (ast.Array, ast.List)):
             return self._type_is_introspectable(typeval.element_type)
         elif isinstance(typeval, ast.Map):
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index a851f79..de9d7ca 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -23,6 +23,7 @@ import sys
 import re
 
 from . import ast
+from . import glibast
 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
 from .girparser import GIRParser
 from .sourcescanner import (
@@ -304,9 +305,8 @@ currently-scanned namespace is first."""
             for ns in unprefixed_namespaces:
                 if name in ns:
                     return [(ns, name)]
-        else:
-            raise ValueError("Unknown namespace for %s %r"
-                % ('identifier' if is_identifier else 'symbol', name, ))
+        raise ValueError("Unknown namespace for %s %r"
+                         % ('identifier' if is_identifier else 'symbol', name, ))
 
     def split_ctype_namespaces(self, ident):
         """Given a StudlyCaps string identifier like FooBar, return a
@@ -791,7 +791,18 @@ Note that type resolution may not succeed."""
         typeval.ctype = None
         return typeval
 
+    def create_type_from_gtype_name(self, gtype_name):
+        """Parse a GType name (as from g_type_name()), and return a
+Type instance.  Note that this function performs namespace lookup,
+in contrast to the other create_type() functions."""
+        # First, is it a fundamental?
+        fundamental = ast.type_names.get(gtype_name)
+        if fundamental is not None:
+            return ast.Type(target_fundamental=fundamental.target_fundamental)
+        return ast.Type(gtype_name=gtype_name)
+
     def _resolve_type_from_ctype(self, typeval):
+        assert typeval.ctype is not None
         pointer_stripped = typeval.ctype.replace('*', '')
         try:
             matches = self.split_ctype_namespaces(pointer_stripped)
@@ -807,6 +818,18 @@ Note that type resolution may not succeed."""
                 return True
         return False
 
+    def _resolve_type_from_gtype_name(self, typeval):
+        assert typeval.gtype_name is not None
+        for ns in self._iter_namespaces():
+            for node in ns.itervalues():
+                if not isinstance(node, (ast.Class, ast.Interface,
+                                         glibast.GLibBoxed)):
+                    continue
+                if node.type_name == typeval.gtype_name:
+                    typeval.target_giname = '%s.%s' % (ns.name, node.name)
+                    return True
+        return False
+
     def resolve_type(self, typeval):
         if isinstance(typeval, (ast.Array, ast.List)):
             return self.resolve_type(typeval.element_type)
@@ -818,6 +841,8 @@ Note that type resolution may not succeed."""
             return True
         elif typeval.ctype:
             return self._resolve_type_from_ctype(typeval)
+        elif typeval.gtype_name:
+            return self._resolve_type_from_gtype_name(typeval)
 
     def _typepair_to_str(self, item):
         nsname, item = item



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