Assume allow-none for GCancellable
[gnome.gobject-introspection] / giscanner / annotationparser.py
index bb74cdc..fe5e721 100644 (file)
@@ -63,6 +63,7 @@ OPT_TRANSFER = 'transfer'
 OPT_TYPE = 'type'
 OPT_CLOSURE = 'closure'
 OPT_DESTROY = 'destroy'
+OPT_SKIP = 'skip'
 
 # Specific option values
 OPT_VAL_BITFIELD = 'bitfield'
@@ -72,6 +73,9 @@ OPT_ARRAY_FIXED_SIZE = 'fixed-size'
 OPT_ARRAY_LENGTH = 'length'
 OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated'
 
+OPT_SCOPE_ASYNC = 'async'
+OPT_SCOPE_CALL = 'call'
+OPT_SCOPE_NOTIFIED = 'notified'
 
 class InvalidAnnotationError(Exception):
     pass
@@ -205,8 +209,9 @@ class AnnotationParser(object):
             tag_name, value = self._split_tag_namevalue(line)
             canon_name = tag_name.lower()
             if canon_name in block.tags:
-                print >>sys.stderr, "Multiple definition of tag %r" \
-                    % (canon_name, )
+                print >> sys.stderr, (
+                    "Symbol %s has multiple definition of tag %r" % (
+                    block_name, canon_name, ))
             block.tags[canon_name] = self._create_tag(canon_name, value)
         block.comment = '\n'.join(comment_lines)
         self._blocks[block.name] = block
@@ -217,6 +222,8 @@ class AnnotationParser(object):
         if len(parts) == 1:
             tag_name = parts[0]
             value = ''
+            if tag_name.endswith(':'):
+                tag_name = tag_name[:-1]
         else:
             tag_name, value = parts
         return (tag_name, value)
@@ -332,7 +339,7 @@ class AnnotationApplier(object):
         self._parse_node_common(record, block)
         self._parse_constructors(record.constructors)
         self._parse_methods(record, record.methods)
-        self._parse_fields(record, record.fields)
+        self._parse_fields(record, record.fields, block)
         if block:
             record.doc = block.comment
 
@@ -347,7 +354,7 @@ class AnnotationApplier(object):
     def _parse_union(self, union):
         block = self._blocks.get(union.name)
         self._parse_node_common(union, block)
-        self._parse_fields(union, union.fields)
+        self._parse_fields(union, union.fields, block)
         self._parse_constructors(union.constructors)
         self._parse_methods(union, union.methods)
         if block:
@@ -373,9 +380,9 @@ class AnnotationApplier(object):
         for ctor in constructors:
             self._parse_function(ctor)
 
-    def _parse_fields(self, parent, fields):
+    def _parse_fields(self, parent, fields, block=None):
         for field in fields:
-            self._parse_field(parent, field)
+            self._parse_field(parent, field, block)
 
     def _parse_properties(self, parent, properties):
         for prop in properties:
@@ -409,6 +416,17 @@ class AnnotationApplier(object):
 
     def _parse_callable(self, callable, block):
         self._parse_node_common(callable, block)
+        for i, param in enumerate(callable.parameters):
+            if (param.type.ctype != 'GDestroyNotify' and
+                param.type.name != 'GLib.DestroyNotify'):
+                continue
+            if i < 2:
+                break
+            callback_param = callable.parameters[i-2]
+            if callback_param.closure_index != -1:
+                callback_param.scope = OPT_SCOPE_NOTIFIED
+                callback_param.transfer = PARAM_TRANSFER_NONE
+
         self._parse_params(callable, callable.parameters, block)
         self._parse_return(callable, callable.retval, block)
         if block:
@@ -464,9 +482,19 @@ class AnnotationApplier(object):
         key = '%s::%s' % (parent.type_name, vfunc.name)
         self._parse_callable(vfunc, self._blocks.get(key))
 
-    def _parse_field(self, parent, field):
+    def _parse_field(self, parent, field, block=None):
         if isinstance(field, Callback):
             self._parse_callback(field)
+        else:
+            if not block:
+                return
+            tag = block.get(field.name)
+            if not tag:
+                return
+            t = tag.options.get('type')
+            if not t:
+                return
+            field.type.name = self._transformer.resolve_type_name(t.one())
 
     def _parse_params(self, parent, params, block):
         for param in params:
@@ -484,6 +512,11 @@ class AnnotationApplier(object):
             if scope:
                 param.scope = scope.one()
                 param.transfer = PARAM_TRANSFER_NONE
+            elif (param.type.ctype == 'GAsyncReadyCallback' or
+                  param.type.name == 'Gio.AsyncReadyCallback'):
+                param.scope = OPT_SCOPE_ASYNC
+                param.transfer = PARAM_TRANSFER_NONE
+
             destroy = options.get(OPT_DESTROY)
             if destroy:
                 param.destroy_index = parent.get_parameter_index(destroy.one())
@@ -519,12 +552,14 @@ class AnnotationApplier(object):
         if node.direction is None:
             node.direction = self._guess_direction(node)
         node.transfer = self._extract_transfer(parent, node, options)
-        if OPT_ALLOW_NONE in options:
-            node.allow_none = True
         param_type = options.get(OPT_TYPE)
         if param_type:
             node.type = self._resolve(param_type.one(), node.type)
 
+        if (OPT_ALLOW_NONE in options or
+            node.type.ctype == 'GCancellable*'):
+            node.allow_none = True
+
         assert node.transfer is not None
         if tag is not None and tag.comment is not None:
             node.doc = tag.comment
@@ -601,7 +636,8 @@ class AnnotationApplier(object):
             # is specified.
             if (isinstance(node, Parameter) and
                 node.type.name == 'utf8' and
-                self._guess_direction(node) == PARAM_DIRECTION_IN):
+                self._guess_direction(node) == PARAM_DIRECTION_IN and
+                element_type is None):
                 # FIXME: unsigned char/guchar should be uint8
                 container_type.element_type = Type('int8')
         container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
@@ -628,9 +664,11 @@ class AnnotationApplier(object):
         def combiner(base, *rest):
             if not rest:
                 return base
-            if base.name in ['GLib.List', 'GLib.SList'] and len(rest)==1:
+            if (base.name in ['GLib.List', 'GLib.SList'] or
+                base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
                 return List(base.name, base.ctype, *rest)
-            if base.name in ['GLib.HashTable'] and len(rest)==2:
+            if (base.name in ['GLib.HashTable'] or
+                base.ctype in ['GHashTable*']) and len(rest)==2:
                 return Map(base.name, base.ctype, *rest)
             print "WARNING: throwing away type parameters:", type_str
             return base
@@ -649,13 +687,15 @@ class AnnotationApplier(object):
     def _parse_element_type(self, parent, node, options):
         element_type_opt = options.get(OPT_ELEMENT_TYPE)
         element_type = element_type_opt.flat()
-        if node.type.name in ['GLib.List', 'GLib.SList']:
+        if (node.type.name in ['GLib.List', 'GLib.SList'] or
+            node.type.ctype in ['GList*', 'GSList*']):
             assert len(element_type) == 1
             container_type = List(
                 node.type.name,
                 node.type.ctype,
                 self._resolve(element_type[0]))
-        elif node.type.name in ['GLib.HashTable']:
+        elif (node.type.name in ['GLib.HashTable'] or
+              node.type.ctype in ['GHashTable*']):
             assert len(element_type) == 2
             container_type = Map(
                 node.type.name,
@@ -686,6 +726,7 @@ class AnnotationApplier(object):
         self._parse_version(node, block)
         self._parse_deprecated(node, block)
         self._parse_attributes(node, block)
+        self._parse_skip(node, block)
 
     def _parse_version(self, node, block):
         since_tag = self._get_tag(block, TAG_SINCE)
@@ -714,6 +755,11 @@ class AnnotationApplier(object):
         for key, value in annos_tag.options.iteritems():
             node.attributes.append((key, value.one()))
 
+    def _parse_skip(self, node, block):
+        if block is not None:
+            if OPT_SKIP in block.options:
+                node.skip = True
+
     def _parse_rename_to_func(self, node, block):
         rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
         if rename_to_tag is None: