[gobject-introspection/wip/transformer] major: Use names for parameters internally



commit 82619e9e74cc6620aa86fcedddab081cea3fef82
Author: Colin Walters <walters verbum org>
Date:   Fri Aug 20 13:13:50 2010 -0400

    major: Use names for parameters internally
    
    This avoids all the indexes we've cached becoming wrong when something
    turns from a function into a method (or vice versa).

 giscanner/ast.py                          |   19 ++-------
 giscanner/girparser.py                    |   43 ++++++++++++++-----
 giscanner/girwriter.py                    |   36 +++++++++-------
 giscanner/primarytransformer.py           |   64 ++++++++++++-----------------
 giscanner/transformer.py                  |    2 +-
 tests/scanner/Annotation-1.0-expected.gir |   12 +++---
 6 files changed, 90 insertions(+), 86 deletions(-)
---
diff --git a/giscanner/ast.py b/giscanner/ast.py
index ae1af8b..501b439 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -480,11 +480,13 @@ class Callable(Node):
         for i, parameter in enumerate(self.parameters):
             if parameter.argname == name:
                 return i
+        raise ValueError("Unknown argument %s" % (name, ))
 
     def get_parameter(self, name):
         for parameter in self.parameters:
             if parameter.argname == name:
                 return parameter
+        raise ValueError("Unknown argument %s" % (name, ))
 
 
 class Function(Callable):
@@ -497,16 +499,6 @@ class Function(Callable):
         self.shadowed_by = None # C symbol string
         self.shadows = None # C symbol string
 
-    def get_parameter_index(self, name):
-        for i, parameter in enumerate(self.parameters):
-            if parameter.argname == name:
-                return i + int(self.is_method)
-
-    def get_parameter(self, name):
-        for parameter in self.parameters:
-            if parameter.argname == name:
-                return parameter
-
 
 class VFunction(Callable):
 
@@ -547,7 +539,6 @@ class Array(Type):
         assert isinstance(element_type, Type)
         self.element_type = element_type
         self.zeroterminated = True
-        self.length_param_index = -1
         self.length_param_name = None
         self.size = None
 
@@ -555,7 +546,6 @@ class Array(Type):
         arr = Array(self.array_type, self.element_type)
         arr.element_type = self.element_type
         arr.zeroterminated = self.zeroterminated
-        arr.length_param_index = self.length_param_index
         arr.length_param_name = self.length_param_name
         arr.size = self.size
         return arr
@@ -573,7 +563,6 @@ class List(Type):
         l = List(self.name, self.element_type)
         l.element_type = self.element_type
         l.zeroterminated = self.zeroterminated
-        l.length_param_index = self.length_param_index
         l.length_param_name = self.length_param_name
         l.size = self.size
         return l
@@ -625,8 +614,8 @@ class Parameter(TypeContainer):
         self.allow_none = allow_none
         self.scope = scope
         self.caller_allocates = caller_allocates
-        self.closure_index = -1
-        self.destroy_index = -1
+        self.closure_name = None
+        self.destroy_name = None
 
 
 class Return(TypeContainer):
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 2c40c48..acb005c 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -294,21 +294,32 @@ class GIRParser(object):
         parameters_node = node.find(_corens('parameters'))
         if (parameters_node is not None):
             for paramnode in self._find_children(parameters_node, _corens('parameter')):
+                typeval = self._parse_type(paramnode)
                 param = ast.Parameter(paramnode.attrib.get('name'),
-                                  self._parse_type(paramnode),
+                                  typeval,
                                   paramnode.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
                                   paramnode.attrib.get('transfer-ownership'),
                                   paramnode.attrib.get('allow-none') == '1',
                                   paramnode.attrib.get('scope'),
                                   paramnode.attrib.get('caller-allocates') == '1')
+                self._parse_generic_attribs(paramnode, param)
+                parameters.append(param)
+            for i, paramnode in enumerate(self._find_children(parameters_node,
+                                                              _corens('parameter'))):
+                param = parameters[i]
+                self._parse_type_second_pass(func, paramnode, param.type)
                 closure = paramnode.attrib.get('closure')
                 if closure:
-                    param.closure_index = int(closure)
+                    idx = int(closure)
+                    assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+                    param.closure_name = parameters[idx].argname
                 destroy = paramnode.attrib.get('destroy')
                 if destroy:
-                    param.destroy_index = int(destroy)
-                self._parse_generic_attribs(paramnode, param)
-                parameters.append(param)
+                    idx = int(destroy)
+                    assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+                    param.destroy_name = parameters[idx].argname
+
+        self._parse_type_second_pass(func, returnnode, retval.type)
 
         self._parse_generic_attribs(node, func)
 
@@ -383,7 +394,7 @@ class GIRParser(object):
                 self._parse_function_common(ctor, ast.Function))
         return union
 
-    def _parse_type_1(self, typenode):
+    def _parse_type_simple(self, typenode):
         # ast.Fields can contain inline callbacks
         if typenode.tag == _corens('callback'):
             typeval = self._namespace.type_from_name(typenode.attrib['name'])
@@ -403,9 +414,6 @@ class GIRParser(object):
             if fixed_size:
                 ret.size = int(fixed_size)
 
-            lenidx = typenode.attrib.get('length')
-            if lenidx:
-                ret.length_param_index = int(lenidx)
             return ret
         elif typenode.tag == _corens('varargs'):
             return ast.Varargs()
@@ -425,7 +433,7 @@ class GIRParser(object):
                 return ast.List(name, element_type, ctype=ctype)
             elif name == 'GLib.HashTable':
                 subchildren = self._find_children(typenode, _corens('type'))
-                subchildren_types = map(self._parse_type_1, subchildren)
+                subchildren_types = map(self._parse_type_simple, subchildren)
                 while len(subchildren_types) < 2:
                     subchildren_types.append(ast.TYPE_ANY)
                 return ast.Map(subchildren_types[0],
@@ -440,9 +448,22 @@ class GIRParser(object):
         for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
             typenode = node.find(name)
             if typenode is not None:
-                return self._parse_type_1(typenode)
+                return self._parse_type_simple(typenode)
         assert False, "Failed to parse toplevel type"
 
+    def _parse_type_second_pass(self, parent, node, typeval):
+        """A hack necessary to handle the integer parameter indexes on
+           array types."""
+        typenode = node.find(_corens('array'))
+        if typenode is None:
+            return
+        lenidx = typenode.attrib.get('length')
+        if lenidx is not None:
+            idx = int(lenidx)
+            assert idx < len(parent.parameters), "%r %d >= %d" \
+                      % (parent, idx, len(parent.parameters))
+            typeval.length_param_name = parent.parameters[idx].argname
+
     def _parse_boxed(self, node):
         obj = glibast.GLibBoxedOther(node.attrib[_glibns('name')],
                              node.attrib[_glibns('type-name')],
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index 7386d93..d9062b5 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -170,8 +170,8 @@ and/or use gtk-doc annotations. ''')
         self._append_throws(callable, attrs)
         with self.tagcontext(tag_name, attrs):
             self._write_generic(callable)
-            self._write_return_type(callable.retval)
-            self._write_parameters(callable.parameters)
+            self._write_return_type(callable.retval, parent=callable)
+            self._write_parameters(callable, callable.parameters)
 
     def _write_function(self, func, tag_name='function'):
         attrs = []
@@ -192,7 +192,7 @@ and/or use gtk-doc annotations. ''')
     def _write_constructor(self, method):
         self._write_function(method, tag_name='constructor')
 
-    def _write_return_type(self, return_):
+    def _write_return_type(self, return_, parent=None):
         if not return_:
             return
 
@@ -201,16 +201,16 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('transfer-ownership', return_.transfer))
         with self.tagcontext('return-value', attrs):
             self._write_generic(return_)
-            self._write_type(return_.type)
+            self._write_type(return_.type, function=parent)
 
-    def _write_parameters(self, parameters):
+    def _write_parameters(self, parent, parameters):
         if not parameters:
             return
         with self.tagcontext('parameters'):
             for parameter in parameters:
-                self._write_parameter(parameter)
+                self._write_parameter(parent, parameter)
 
-    def _write_parameter(self, parameter):
+    def _write_parameter(self, parent, parameter):
         attrs = []
         if parameter.argname is not None:
             attrs.append(('name', parameter.argname))
@@ -225,13 +225,15 @@ and/or use gtk-doc annotations. ''')
             attrs.append(('allow-none', '1'))
         if parameter.scope:
             attrs.append(('scope', parameter.scope))
-        if parameter.closure_index >= 0:
-            attrs.append(('closure', '%d' % parameter.closure_index))
-        if parameter.destroy_index >= 0:
-            attrs.append(('destroy', '%d' % parameter.destroy_index))
+        if parameter.closure_name is not None:
+            idx = parent.get_parameter_index(parameter.closure_name)
+            attrs.append(('closure', '%d' % (idx, )))
+        if parameter.destroy_name is not None:
+            idx = parent.get_parameter_index(parameter.destroy_name)
+            attrs.append(('destroy', '%d' % (idx, )))
         with self.tagcontext('parameter', attrs):
             self._write_generic(parameter)
-            self._write_type(parameter.type)
+            self._write_type(parameter.type, function=parent)
 
     def _type_to_name(self, typeval):
         if not typeval.resolved:
@@ -242,7 +244,7 @@ and/or use gtk-doc annotations. ''')
             return typeval.target_giname[len(prefix):]
         return typeval.target_giname
 
-    def _write_type(self, ntype, relation=None):
+    def _write_type(self, ntype, relation=None, function=None):
         assert isinstance(ntype, Type), ntype
         attrs = []
         if ntype.ctype:
@@ -257,8 +259,10 @@ and/or use gtk-doc annotations. ''')
                 attrs.insert(0, ('zero-terminated', '0'))
             if ntype.size is not None:
                 attrs.append(('fixed-size', '%d' % (ntype.size, )))
-            if ntype.length_param_index >= 0:
-                attrs.insert(0, ('length', '%d' % (ntype.length_param_index, )))
+            if ntype.length_param_name is not None:
+                assert function
+                attrs.insert(0, ('length', '%d' \
+                            % (function.get_parameter_index(ntype.length_param_name, ))))
 
             with self.tagcontext('array', attrs):
                 self._write_type(ntype.element_type)
@@ -527,4 +531,4 @@ and/or use gtk-doc annotations. ''')
         with self.tagcontext('glib:signal', attrs):
             self._write_generic(signal)
             self._write_return_type(signal.retval)
-            self._write_parameters(signal.parameters)
+            self._write_parameters(signal, signal.parameters)
diff --git a/giscanner/primarytransformer.py b/giscanner/primarytransformer.py
index 1dda3c4..7bac244 100644
--- a/giscanner/primarytransformer.py
+++ b/giscanner/primarytransformer.py
@@ -100,9 +100,12 @@ class PrimaryTransformer(object):
             return self._is_gi_subclass(cls.parent, supercls_type)
         return False
 
-    def _get_parameter_index(self, parent, param_name, origin):
-        index = parent.get_parameter_index(param_name)
-        if index is None:
+    def _get_validate_parameter_name(self, parent, param_name, origin):
+        try:
+            param = parent.get_parameter(param_name)
+        except ValueError, e:
+            param = None
+        if param is None:
             if isinstance(origin, ast.Parameter):
                 origin_name = 'parameter %s' % (origin.argname, )
             else:
@@ -111,7 +114,7 @@ class PrimaryTransformer(object):
                 "can't find parameter %s referenced by %s of %r"
                 % (param_name, origin_name, parent.name), fatal=True)
 
-        return index
+        return param.argname
 
     def _apply_annotations_function(self, node, chain):
         block = self._blocks.get(node.symbol)
@@ -182,16 +185,6 @@ class PrimaryTransformer(object):
                 node.get_value_func = tag.value if tag else None
         return True
 
-    def _fixup_param_destroy(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.destroy_index == param.destroy_index:
-                p.destroy_index = -1
-
-    def _fixup_param_closure(self, parent, param):
-        for p in parent.parameters:
-            if p is not param and p.closure_index == param.closure_index:
-                p.closure_index = -1
-
     def _adjust_container_type(self, parent, node, options):
         has_element_type = OPT_ELEMENT_TYPE in options
         has_array = OPT_ARRAY in options
@@ -272,12 +265,13 @@ class PrimaryTransformer(object):
                 OPT_ARRAY_ZERO_TERMINATED) == '1'
         length = array_values.get(OPT_ARRAY_LENGTH)
         if length is not None:
-            param_index = self._get_parameter_index(parent, length, node)
-            param = parent.parameters[param_index]
-            param.direction = node.direction
-            if param.direction == ast.PARAM_DIRECTION_OUT:
-                param.transfer = ast.PARAM_TRANSFER_FULL
-            container_type.length_param_index = param_index
+            paramname = self._get_validate_parameter_name(parent, length, node)
+            if paramname:
+                param = parent.get_parameter(paramname)
+                param.direction = node.direction
+                if param.direction == ast.PARAM_DIRECTION_OUT:
+                    param.transfer = ast.PARAM_TRANSFER_FULL
+                container_type.length_param_name = param.argname
         fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
         if fixed:
             container_type.size = int(fixed)
@@ -479,32 +473,28 @@ class PrimaryTransformer(object):
 
             destroy = options.get(OPT_DESTROY)
             if destroy:
-                destroy_name = destroy.one()
-                param.destroy_index = self._get_parameter_index(parent,
-                                                                destroy_name,
-                                                                param)
-                param.scope = ast.PARAM_SCOPE_NOTIFIED
-                if param.destroy_index >= 0:
-                    destroy_param = parent.parameters[param.destroy_index]
+                param.destroy_name = self._get_validate_parameter_name(parent,
+                                                                       destroy.one(),
+                                                                       param)
+                if param.destroy_name is not None:
+                    param.scope = ast.PARAM_SCOPE_NOTIFIED
+                    destroy_param = parent.get_parameter(param.destroy_name)
                     # This is technically bogus; we're setting the scope on the destroy
                     # itself.  But this helps avoid tripping a warning from finaltransformer,
                     # since we don't have a way right now to flag this callback a destroy.
                     destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
-                self._fixup_param_destroy(parent, param)
             closure = options.get(OPT_CLOSURE)
             if closure:
-                param.closure_index = self._get_parameter_index(parent,
-                                                                closure.one(),
-                                                                param)
-                self._fixup_param_closure(parent, param)
+                param.closure_name = self._get_validate_parameter_name(parent,
+                                                                       closure.one(),
+                                                                       param)
         elif isinstance(parent, ast.Callback):
             if OPT_CLOSURE in options:
                 # For callbacks, (closure) appears without an
                 # argument, and tags a parameter that is a closure. We
                 # represent it (weirdly) in the gir and typelib by
-                # setting param.closure_index to its own index.
-                param.closure_index = parent.get_parameter_index(param.argname)
-                self._fixup_param_closure(parent, param)
+                # setting param.closure_name to itself.
+                param.closure_name = param.argname
 
         self._apply_annotations_param_ret_common(parent, param, tag)
 
@@ -912,12 +902,12 @@ method or constructor of some type."""
             if callback_param is None:
                 continue
             if is_destroynotify:
-                callback_param.destroy_index = i
+                callback_param.destroy_name = param.argname
                 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
                 callback_param.transfer = ast.PARAM_TRANSFER_NONE
             elif (param.type.is_equiv(ast.TYPE_ANY) and
                   param.argname.endswith('data')):
-                callback_param.closure_index = i
+                callback_param.closure_name = param.argname
 
     def _pass3_callable_throws(self, node):
         """Check to see if we have anything that looks like a
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 7f5fa12..08a7ef1 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -735,7 +735,7 @@ it is always biggest (i.e. last)."""
         for i, param in enumerate(parameters):
             if (param.type.target_fundamental == 'gpointer' and
                 param.argname == 'user_data'):
-                param.closure_index = i
+                param.closure_name = param.argname
 
         if member:
             name = symbol.ident
diff --git a/tests/scanner/Annotation-1.0-expected.gir b/tests/scanner/Annotation-1.0-expected.gir
index 068fea8..a0ddb16 100644
--- a/tests/scanner/Annotation-1.0-expected.gir
+++ b/tests/scanner/Annotation-1.0-expected.gir
@@ -156,7 +156,7 @@ and/or use gtk-doc annotations.  -->
         <parameters>
           <parameter name="nums" transfer-ownership="none">
             <doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
-            <array length="2" zero-terminated="0" c:type="int*">
+            <array length="1" zero-terminated="0" c:type="int*">
               <type name="gint"/>
             </array>
           </parameter>
@@ -175,7 +175,7 @@ and/or use gtk-doc annotations.  -->
         <parameters>
           <parameter name="nums" transfer-ownership="none">
             <doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
-            <array length="2" c:type="int*">
+            <array length="1" c:type="int*">
               <type name="gint"/>
             </array>
           </parameter>
@@ -367,7 +367,7 @@ each string needs to be freed.</doc>
                      caller-allocates="0"
                      transfer-ownership="full">
             <doc xml:whitespace="preserve">Argument vector</doc>
-            <array length="1" c:type="char***">
+            <array length="0" c:type="char***">
               <type name="utf8"/>
             </array>
           </parameter>
@@ -381,7 +381,7 @@ each string needs to be freed.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="guchar*">
+            <array length="1" c:type="guchar*">
               <type name="guint8"/>
             </array>
           </parameter>
@@ -399,7 +399,7 @@ each string needs to be freed.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="gchar*">
+            <array length="1" c:type="gchar*">
               <type name="gint8"/>
             </array>
           </parameter>
@@ -418,7 +418,7 @@ type.</doc>
         <parameters>
           <parameter name="data" transfer-ownership="none">
             <doc xml:whitespace="preserve">The data</doc>
-            <array length="2" c:type="gpointer">
+            <array length="1" c:type="gpointer">
               <type name="guint8"/>
             </array>
           </parameter>



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