diff options
-rw-r--r-- | ChangeLog | 35 | ||||
-rw-r--r-- | giscanner/Makefile.am | 1 | ||||
-rw-r--r-- | giscanner/annotationparser.py | 505 | ||||
-rw-r--r-- | giscanner/ast.py | 32 | ||||
-rw-r--r-- | giscanner/dumper.py | 2 | ||||
-rw-r--r-- | giscanner/girparser.py | 11 | ||||
-rw-r--r-- | giscanner/giscannermodule.c | 128 | ||||
-rw-r--r-- | giscanner/glibtransformer.py | 36 | ||||
-rw-r--r-- | giscanner/scannerlexer.l | 172 | ||||
-rw-r--r-- | giscanner/sourcescanner.c | 33 | ||||
-rw-r--r-- | giscanner/sourcescanner.h | 19 | ||||
-rw-r--r-- | giscanner/sourcescanner.py | 11 | ||||
-rw-r--r-- | giscanner/transformer.py | 390 | ||||
-rw-r--r-- | giscanner/xmlwriter.py | 2 | ||||
-rw-r--r-- | tests/scanner/annotation-1.0-expected.gir | 7 | ||||
-rw-r--r-- | tests/scanner/annotation-1.0-expected.tgir | 7 | ||||
-rw-r--r-- | tests/scanner/annotation.c | 4 | ||||
-rw-r--r-- | tests/scanner/annotation.h | 5 | ||||
-rw-r--r-- | tests/scanner/foo-1.0-expected.gir | 34 | ||||
-rw-r--r-- | tests/scanner/foo-1.0-expected.tgir | 30 | ||||
-rw-r--r-- | tests/scanner/foo.h | 5 | ||||
-rwxr-xr-x | tools/g-ir-scanner | 7 |
22 files changed, 791 insertions, 685 deletions
@@ -1,3 +1,38 @@ +2009-01-12 Johan Dahlin <jdahlin@async.com.br> + + Bug 563794 - Redo annotation parsing & applying + + Thanks to Colin for helping out considerably in landing this. + + * giscanner/Makefile.am: + * giscanner/ast.py: + * giscanner/dumper.py: + * giscanner/girparser.py: + * giscanner/giscannermodule.c (pygi_source_scanner_get_comments), + (calc_attrs_length), (pygi_collect_attributes), (init_giscanner): + * giscanner/glibtransformer.py: + * giscanner/scannerlexer.l: + * giscanner/sourcescanner.c (gi_source_symbol_unref), + (gi_source_scanner_new), (gi_source_scanner_free), + (gi_source_scanner_get_comments): + * giscanner/sourcescanner.h: + * giscanner/sourcescanner.py: + * giscanner/transformer.py: + * giscanner/xmlwriter.py: + * tests/scanner/annotation-1.0-expected.gir: + * tests/scanner/annotation-1.0-expected.tgir: + * tests/scanner/annotation.c: + * tests/scanner/annotation.h: + * tests/scanner/foo-1.0-expected.gir: + * tests/scanner/foo-1.0-expected.tgir: + * tests/scanner/foo.h: + * tools/g-ir-scanner: + + This commit merges the annotation parser rewrite branch. + It'll change the annotation parsing to be done completely in python + code which will make it easier to do further annotation parsing + easier. + 2009-01-03 Andreas Rottmann <a.rottmann@gmx.at> Bug 563469 – Arrays not treated correctly in struct offset calculation diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am index 1d6942b4..bc2977f6 100644 --- a/giscanner/Makefile.am +++ b/giscanner/Makefile.am @@ -35,6 +35,7 @@ pkgpyexecdir = $(pyexecdir)/giscanner pkgpyexec_LTLIBRARIES = _giscanner.la pkgpyexec_PYTHON = \ __init__.py \ + annotationparser.py \ ast.py \ cachestore.py \ config.py \ diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py new file mode 100644 index 00000000..630508a9 --- /dev/null +++ b/giscanner/annotationparser.py @@ -0,0 +1,505 @@ +# -*- Mode: Python -*- +# GObject-Introspection - a framework for introspecting GObject libraries +# Copyright (C) 2008 Johan Dahlin +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +# AnnotationParser - parses gtk-doc annotations + +# All gtk-doc comments needs to start with this: +_COMMENT_HEADER = '*\n * ' + +from .ast import (Array, Callback, Class, Enum, Field, Function, Interface, + List, Map, Parameter, Record, Return, Type, Union, Varargs, + default_array_types, + BASIC_GIR_TYPES, + PARAM_DIRECTION_INOUT, + PARAM_DIRECTION_IN, + PARAM_DIRECTION_OUT, + PARAM_TRANSFER_NONE, + PARAM_TRANSFER_CONTAINER, + PARAM_TRANSFER_FULL, + TYPE_ANY, TYPE_NONE) +from .glibast import GLibBoxed + + +class InvalidAnnotationError(Exception): + pass + + +class DocBlock(object): + + def __init__(self, name): + self.name = name + self.value = None + self.tags = {} + + def __repr__(self): + return '<Directive %r>' % (self.name, ) + + def get(self, name): + if name == 'Returns': + value = self.tags.get(name) + if value is None: + return self.tags.get('Return value') + else: + return value + else: + return self.tags.get(name) + + +class DocTag(object): + + def __init__(self, name): + self.name = name + self.options = [] + + +class Option(object): + + def __init__(self, option): + self._array = [] + self._dict = {} + for p in option.split(' '): + if '=' in p: + name, value = p.split('=', 1) + else: + name = p + value = None + self._dict[name] = value + if value is None: + self._array.append(name) + else: + self._array.append((name, value)) + + def __repr__(self): + return '<Option %r>' % (self._array, ) + + def one(self): + assert len(self._array) == 1 + return self._array[0] + + def flat(self): + return self._array + + def all(self): + return self._dict + + +class AnnotationParser(object): + + def __init__(self, namespace, source_scanner, transformer): + self._blocks = {} + self._namespace = namespace + self._transformer = transformer + for comment in source_scanner.get_comments(): + self._parse_comment(comment) + + def parse(self): + aa = AnnotationApplier(self._blocks, self._transformer) + aa.parse(self._namespace) + + def _parse_comment(self, comment): + if not comment.startswith(_COMMENT_HEADER): + return + comment = comment[len(_COMMENT_HEADER):] + pos = comment.index('\n ') + + block_name = comment[:pos] + block_name = block_name.strip() + if not block_name.endswith(':'): + return + block = DocBlock(block_name[:-1]) + content = comment[pos+1:] + for line in content.split('\n'): + line = line[2:].strip() # Skip ' *' + if not line: + continue + + if line.startswith('@'): + line = line[1:] + elif not ': ' in line: + continue + tag = self._parse_tag(line) + block.tags[tag.name] = tag + + self._blocks[block.name] = block + + def _parse_tag(self, value): + # Tag: bar + # Tag: bar opt1 opt2 + parts = value.split(': ', 1) + if len(parts) == 1: + tag_name = parts[0] + options = '' + else: + tag_name, options = parts + tag = DocTag(tag_name) + tag.value = options + tag.options = self._parse_options(options) + return tag + + def _parse_options(self, value): + # (foo) + # (bar opt1 opt2...) + opened = -1 + options = {} + for i, c in enumerate(value): + if c == '(' and opened == -1: + opened = i+1 + if c == ')' and opened != -1: + segment = value[opened:i] + parts = segment.split(' ', 1) + if len(parts) == 2: + name, option = parts + elif len(parts) == 1: + name = parts[0] + option = None + else: + raise AssertionError + if option is not None: + option = Option(option) + options[name] = option + opened = -1 + return options + + +class AnnotationApplier(object): + + def __init__(self, blocks, transformer): + self._blocks = blocks + self._transformer = transformer + + def _get_tag(self, block, tag_name): + if block is None: + return None + + return block.get(tag_name) + + def parse(self, namespace): + for node in namespace.nodes: + self._parse_node(node) + + # Boring parsing boilerplate. + + def _parse_node(self, node): + if isinstance(node, Function): + self._parse_function(node) + elif isinstance(node, Enum): + self._parse_enum(node) + elif isinstance(node, Class): + self._parse_class(node) + elif isinstance(node, Interface): + self._parse_interface(node) + elif isinstance(node, Callback): + self._parse_callback(node) + elif isinstance(node, Record): + self._parse_record(node) + elif isinstance(node, Union): + self._parse_union(node) + elif isinstance(node, GLibBoxed): + self._parse_boxed(node) + + def _parse_class(self, class_): + block = self._blocks.get(class_.name) + self._parse_version(class_, block) + self._parse_constructors(class_.constructors) + self._parse_methods(class_.methods) + self._parse_methods(class_.static_methods) + self._parse_properties(class_.properties) + self._parse_signals(class_.signals) + self._parse_fields(class_, class_.fields) + + def _parse_interface(self, interface): + block = self._blocks.get(interface.name) + self._parse_version(interface, block) + self._parse_methods(interface.methods) + self._parse_properties(interface.properties) + self._parse_signals(interface.signals) + self._parse_fields(interface, interface.fields) + + def _parse_record(self, record): + block = self._blocks.get(record.symbol) + self._parse_version(record, block) + self._parse_constructors(record.constructors) + self._parse_fields(record, record.fields) + if isinstance(record, GLibBoxed): + self._parse_methods(record.methods) + + def _parse_boxed(self, boxed): + block = self._blocks.get(boxed.name) + self._parse_version(boxed, block) + self._parse_constructors(boxed.constructors) + self._parse_methods(boxed.methods) + + def _parse_union(self, union): + block = self._blocks.get(union.name) + self._parse_fields(union, union.fields) + self._parse_constructors(union.constructors) + if isinstance(union, GLibBoxed): + self._parse_methods(union.methods) + + def _parse_enum(self, enum): + block = self._blocks.get(enum.symbol) + self._parse_version(enum, block) + + def _parse_constructors(self, constructors): + for ctor in constructors: + self._parse_function(ctor) + + def _parse_fields(self, parent, fields): + for field in fields: + self._parse_field(parent, field) + + def _parse_properties(self, properties): + for prop in properties: + self._parse_property(property) + + def _parse_methods(self, methods): + for method in methods: + self._parse_function(method) + + def _parse_signals(self, signals): + for signal in signals: + self._parse_signal(signal) + + def _parse_property(self, prop): + pass + + def _parse_callback(self, callback): + block = self._blocks.get(callback.ctype) + self._parse_version(callback, block) + self._parse_params(callback, callback.parameters, block) + self._parse_return(callback, callback.retval, block) + + def _parse_function(self, func): + block = self._blocks.get(func.symbol) + self._parse_version(func, block) + self._parse_deprecated(func, block) + self._parse_params(func, func.parameters, block) + self._parse_return(func, func.retval, block) + + def _parse_signal(self, signal): + block = self._blocks.get(signal.name) + self._parse_version(signal, block) + self._parse_params(signal, signal.parameters, block) + self._parse_return(signal, signal.retval, block) + + def _parse_field(self, parent, field): + if isinstance(field, Callback): + self._parse_callback(field) + + def _parse_params(self, parent, params, block): + for param in params: + self._parse_param(parent, param, block) + + def _parse_return(self, parent, return_, block): + tag = self._get_tag(block, 'Returns') + options = getattr(tag, 'options', {}) + self._parse_param_ret_common(parent, return_, options) + + def _parse_param(self, parent, param, block): + tag = self._get_tag(block, param.name) + options = getattr(tag, 'options', {}) + + if isinstance(parent, Function): + scope = options.get('scope') + if scope: + param.scope = scope.one() + param.transfer = PARAM_TRANSFER_NONE + self._parse_param_ret_common(parent, param, options) + + def _parse_param_ret_common(self, parent, node, options): + node.direction = self._extract_direction(node, options) + container_type = self._extract_container_type( + parent, node, options) + if container_type is not None: + node.type = container_type + if not node.direction: + node.direction = self._guess_direction(node) + node.transfer = self._extract_transfer(parent, node, options) + if 'allow-none' in options: + node.allow_none = True + + assert node.transfer is not None + + def _extract_direction(self, node, options): + if ('inout' in options or + 'in-out' in options): + direction = PARAM_DIRECTION_INOUT + elif 'out' in options: + direction = PARAM_DIRECTION_OUT + elif 'in' in options: + direction = PARAM_DIRECTION_IN + else: + direction = node.direction + return direction + + def _guess_array(self, node): + ctype = node.type.ctype + if ctype is None: + return False + if not ctype.endswith('*'): + return False + if node.type.canonical in default_array_types: + return True + return False + + def _extract_container_type(self, parent, node, options): + has_element_type = 'element-type' in options + has_array = 'array' in options + + # FIXME: This is a hack :-( + if (not isinstance(node, Field) and + (not has_element_type and + (node.direction is None + or node.direction == PARAM_DIRECTION_IN))): + if self._guess_array(node): + has_array = True + + if has_array: + container_type = self._parse_array(parent, node, options) + elif has_element_type: + container_type = self._parse_element_type(parent, node, options) + else: + container_type = None + + return container_type + + def _parse_array(self, parent, node, options): + array_opt = options.get('array') + if array_opt: + values = array_opt.all() + else: + values = {} + container_type = Array(node.type.ctype, node.type.name) + if 'zero-terminated' in values: + container_type.zeroterminated = values.get( + 'zero-terminated') == '1' + length = values.get('length') + if length is not None: + param_index = parent.get_parameter_index(length) + container_type.length_param_index = param_index + container_type.size = values.get('fized-size') + return container_type + + def _parse_element_type(self, parent, node, options): + element_type_opt = options.get('element-type') + element_type = element_type_opt.flat() + if node.type.name in ['GLib.List', 'GLib.SList']: + assert len(element_type) == 1 + etype = Type(element_type[0]) + container_type = List( + node.type.name, + node.type.ctype, + self._transformer.resolve_param_type(etype)) + elif node.type.name in ['GLib.HashTable']: + assert len(element_type) == 2 + key_type = Type(element_type[0]) + value_type = Type(element_type[1]) + container_type = Map( + node.type.name, + node.type.ctype, + self._transformer.resolve_param_type(key_type), + self._transformer.resolve_param_type(value_type)) + else: + print 'FIXME: unhandled element-type container:', node + return container_type + + def _extract_transfer(self, parent, node, options): + transfer_opt = options.get('transfer') + if transfer_opt is None: + transfer = self._guess_transfer(node, options) + else: + transfer = transfer_opt.one() + if transfer is None: + transfer = PARAM_TRANSFER_FULL + if transfer not in [PARAM_TRANSFER_NONE, + PARAM_TRANSFER_CONTAINER, + PARAM_TRANSFER_FULL]: + raise InvalidAnnotationError( + "transfer for %s of %r is invalid (%r), must be one of " + "none, container, full." % (node, parent.name, transfer)) + return transfer + + def _parse_version(self, node, block): + since_tag = self._get_tag(block, 'Since') + if since_tag is None: + return + node.version = since_tag.value + + def _parse_deprecated(self, node, block): + deprecated_tag = self._get_tag(block, 'Deprecated') + if deprecated_tag is None: + return + value = deprecated_tag.value + if ': ' in value: + version, desc = value.split(': ') + else: + desc = value + version = None + node.deprecated = desc + if version is not None: + node.deprecated_version = version + + def _guess_direction(self, node): + if node.direction: + return node.direction + is_pointer = False + if node.type.ctype: + is_pointer = '*' in node.type.ctype + + if is_pointer and node.type.name in BASIC_GIR_TYPES: + return PARAM_DIRECTION_OUT + + return PARAM_DIRECTION_IN + + def _guess_transfer(self, node, options): + if node.transfer is not None: + return node.transfer + + if isinstance(node.type, Array): + return PARAM_TRANSFER_NONE + # Anything with 'const' gets none + if node.type.is_const: + return PARAM_TRANSFER_NONE + + elif node.type.name in [TYPE_NONE, TYPE_ANY]: + return PARAM_TRANSFER_NONE + elif isinstance(node.type, Varargs): + return PARAM_TRANSFER_NONE + elif isinstance(node, Parameter): + if node.direction in [PARAM_DIRECTION_INOUT, + PARAM_DIRECTION_OUT]: + return PARAM_TRANSFER_FULL + # This one is a hack for compatibility; the transfer + # for string parameters really has no defined meaning. + elif node.type.canonical == 'utf8': + return PARAM_TRANSFER_FULL + else: + return PARAM_TRANSFER_NONE + elif isinstance(node, Return): + if (node.type.canonical in BASIC_GIR_TYPES or + (node.type.canonical in [TYPE_NONE, TYPE_ANY] and + node.type.is_const)): + return PARAM_TRANSFER_NONE + else: + return PARAM_TRANSFER_FULL + elif isinstance(node, Field): + return PARAM_TRANSFER_NONE + else: + raise AssertionError(node) diff --git a/giscanner/ast.py b/giscanner/ast.py index 6bd858b2..c5dc436c 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -200,6 +200,17 @@ class Function(Node): self.parameters = parameters self.symbol = symbol self.throws = not not throws + self.is_method = False + + def get_parameter_index(self, name): + for i, parameter in enumerate(self.parameters): + if parameter.name == name: + return i + int(self.is_method) + + def get_parameter(self, name): + for parameter in self.parameters: + if parameter.name == name: + return parameter def __repr__(self): return '%s(%r, %r, %r)' % (self.__class__.__name__, @@ -217,6 +228,9 @@ class Type(Node): Node.__init__(self, name) self.ctype = ctype self.resolved = False + self.is_const = False + self.canonical = None + self.derefed_canonical = None class Varargs(Type): @@ -236,7 +250,7 @@ class Array(Type): self.size = None def __repr__(self): - return 'Array(%r of %r)' % (self.name, self.element_type, ) + return 'Array(%r, %r)' % (self.name, self.element_type, ) class List(Type): @@ -282,22 +296,19 @@ class TypeContainer(Node): else: self.transfer = None - # transformer.py overrides this as needed - self.transfer_inferred = False - class Parameter(TypeContainer): - def __init__(self, name, typenode, direction=PARAM_DIRECTION_IN, + def __init__(self, name, typenode, direction=None, transfer=None, allow_none=False, scope=None): TypeContainer.__init__(self, name, typenode, transfer) if direction in [PARAM_DIRECTION_IN, PARAM_DIRECTION_OUT, - PARAM_DIRECTION_INOUT]: + PARAM_DIRECTION_INOUT, None]: self.direction = direction else: self.direction = PARAM_DIRECTION_IN - self.allow_none = not not allow_none + self.allow_none = allow_none self.scope = scope self.closure_index = -1 self.destroy_index = -1 @@ -328,7 +339,7 @@ class Member(Node): return 'Member(%r, %r)' % (self.name, self.value) -class Struct(Node): +class Record(Node): def __init__(self, name, symbol, disguised=False): Node.__init__(self, name) @@ -337,6 +348,9 @@ class Struct(Node): self.symbol = symbol self.disguised = disguised +# BW compat, remove +Struct = Record + class Field(Node): @@ -359,6 +373,7 @@ class Return(TypeContainer): def __init__(self, rtype, transfer=None): TypeContainer.__init__(self, None, rtype, transfer) + self.direction = PARAM_DIRECTION_OUT def __repr__(self): return 'Return(%r)' % (self.type, ) @@ -431,6 +446,7 @@ class Property(Node): # FIXME: Inherit from Function + class Callback(Node): def __init__(self, name, retval, parameters, ctype=None): diff --git a/giscanner/dumper.py b/giscanner/dumper.py index c85ae807..45dcc25f 100644 --- a/giscanner/dumper.py +++ b/giscanner/dumper.py @@ -88,6 +88,7 @@ class DumpCompiler(object): self._packages.append('gobject-introspection-1.0') # Public API + def run(self): c_path = self._generate_tempfile('.c') f = open(c_path, 'w') @@ -112,6 +113,7 @@ class DumpCompiler(object): return IntrospectionBinary([bin_path], self._tmpdir) # Private API + def _generate_tempfile(self, suffix=''): tmpl = '%s-%s%s' % (self._options.namespace_name, self._options.namespace_version, suffix) diff --git a/giscanner/girparser.py b/giscanner/girparser.py index 17f53521..3e2432ed 100644 --- a/giscanner/girparser.py +++ b/giscanner/girparser.py @@ -53,6 +53,7 @@ class GIRParser(object): self._namespace = None # Public API + def parse(self, filename): tree = parse(filename) self.parse_tree(tree) @@ -79,6 +80,7 @@ class GIRParser(object): self._include_parsing = include_parsing # Private + def _add_node(self, node): self._namespace.nodes.append(node) @@ -152,7 +154,9 @@ class GIRParser(object): for iface in node.findall(_corens('prerequisites')): obj.prerequisities.append(iface.attrib['name']) for method in node.findall(_corens('method')): - obj.methods.append(self._parse_function_common(method, Function)) + func = self._parse_function_common(method, Function) + func.is_method = True + obj.methods.append(func) for ctor in node.findall(_corens('constructor')): obj.constructors.append( self._parse_function_common(ctor, Function)) @@ -284,8 +288,9 @@ class GIRParser(object): if self._include_parsing: return for method in node.findall(_corens('method')): - obj.methods.append( - self._parse_function_common(method, Function)) + func = self._parse_function_common(method, Function) + func.is_method = True + obj.methods.append(func) for ctor in node.findall(_corens('constructor')): obj.constructors.append( self._parse_function_common(ctor, Function)) diff --git a/giscanner/giscannermodule.c b/giscanner/giscannermodule.c index df1ba095..afa4c29c 100644 --- a/giscanner/giscannermodule.c +++ b/giscanner/giscannermodule.c @@ -64,11 +64,6 @@ PyTypeObject Py##cname##_Type = { \ typedef struct { PyObject_HEAD - GISourceDirective *directive; -} PyGISourceDirective; - -typedef struct { - PyObject_HEAD GISourceType *type; } PyGISourceType; @@ -77,7 +72,6 @@ static PyObject * pygi_source_type_new (GISourceType *type); typedef struct { PyObject_HEAD GISourceSymbol *symbol; - PyObject *directives; } PyGISourceSymbol; typedef struct { @@ -85,76 +79,11 @@ typedef struct { GISourceScanner *scanner; } PyGISourceScanner; -NEW_CLASS (PyGISourceDirective, "SourceDirective", GISourceDirective); NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol); NEW_CLASS (PyGISourceType, "SourceType", GISourceType); NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner); -/* Directive */ - -static PyObject * -pygi_source_directive_new (GISourceDirective *directive) -{ - PyGISourceDirective *self; - - if (directive == NULL) - { - Py_INCREF (Py_None); - return Py_None; - } - - self = (PyGISourceDirective *)PyObject_New (PyGISourceDirective, - &PyGISourceDirective_Type); - self->directive = directive; - return (PyObject*)self; -} - -static PyObject * -directive_get_name (PyGISourceDirective *self, - void *context) -{ - return PyString_FromString (self->directive->name); -} - -static PyObject * -directive_get_value (PyGISourceDirective *self, - void *context) -{ - return PyString_FromString (self->directive->value); -} - -static PyObject * -directive_get_options (PyGISourceDirective *self, - void *context) -{ - GSList *l; - PyObject *list; - int i = 0; - - if (!self->directive) - return Py_BuildValue("[]"); - - list = PyList_New (g_slist_length (self->directive->options)); - - for (l = self->directive->options; l; l = l->next) - { - PyObject *item = PyString_FromString (l->data); - PyList_SetItem (list, i++, item); - Py_INCREF (item); - } - - Py_INCREF (list); - return list; -} - -static const PyGetSetDef _PyGISourceDirective_getsets[] = { - { "name", (getter)directive_get_name, NULL, NULL}, - { "value", (getter)directive_get_value, NULL, NULL}, - { "options", (getter)directive_get_options, NULL, NULL}, - { 0 } -}; - /* Symbol */ static PyObject * @@ -222,26 +151,6 @@ symbol_get_const_string (PyGISourceSymbol *self, return PyString_FromString (self->symbol->const_string); } -static PyObject * -symbol_get_directives (PyGISourceSymbol *self, - void *context) -{ - if (!self->directives) - self->directives = Py_BuildValue("[]"); - Py_INCREF (self->directives); - return self->directives; -} - -static int -symbol_set_directives (PyGISourceSymbol *self, - PyObject *value, - void *context) -{ - self->directives = value; - Py_INCREF(value); - return 0; -} - static const PyGetSetDef _PyGISourceSymbol_getsets[] = { /* int ref_count; */ { "type", (getter)symbol_get_type, NULL, NULL}, @@ -251,8 +160,6 @@ static const PyGetSetDef _PyGISourceSymbol_getsets[] = { /* gboolean const_int_set; */ { "const_int", (getter)symbol_get_const_int, NULL, NULL}, { "const_string", (getter)symbol_get_const_string, NULL, NULL}, - { "directives", (getter)symbol_get_directives, - (setter)symbol_set_directives, NULL}, { 0 } }; @@ -555,23 +462,18 @@ pygi_source_scanner_get_symbols (PyGISourceScanner *self) } static PyObject * -pygi_source_scanner_get_directives (PyGISourceScanner *self, - PyObject *args) +pygi_source_scanner_get_comments (PyGISourceScanner *self) { - GSList *l, *directives; + GSList *l, *comments; PyObject *list; int i = 0; - char *name; - - if (!PyArg_ParseTuple (args, "s:SourceScanner.get_directives", &name)) - return NULL; - directives = gi_source_scanner_get_directives (self->scanner, name); - list = PyList_New (g_slist_length (directives)); + comments = gi_source_scanner_get_comments (self->scanner); + list = PyList_New (g_slist_length (comments)); - for (l = directives; l; l = l->next) + for (l = comments; l; l = l->next) { - PyObject *item = pygi_source_directive_new (l->data); + PyObject *item = PyString_FromString (l->data); PyList_SetItem (list, i++, item); Py_INCREF (item); } @@ -581,7 +483,7 @@ pygi_source_scanner_get_directives (PyGISourceScanner *self, } static const PyMethodDef _PyGISourceScanner_methods[] = { - { "get_directives", (PyCFunction) pygi_source_scanner_get_directives, METH_VARARGS }, + { "get_comments", (PyCFunction) pygi_source_scanner_get_comments, METH_NOARGS }, { "get_symbols", (PyCFunction) pygi_source_scanner_get_symbols, METH_NOARGS }, { "append_filename", (PyCFunction) pygi_source_scanner_append_filename, METH_VARARGS }, { "parse_file", (PyCFunction) pygi_source_scanner_parse_file, METH_VARARGS }, @@ -611,7 +513,8 @@ static int calc_attrs_length(PyObject *attributes, int indent, if (PyTuple_GetItem(tuple, 1) == Py_None) continue; - g_assert(PyArg_ParseTuple(tuple, "ss", &attr, &value)); + if (!PyArg_ParseTuple(tuple, "ss", &attr, &value)) + return -1; escaped = g_markup_escape_text (value, -1); attr_length += 2 + strlen(attr) + strlen(escaped) + 2; @@ -631,6 +534,7 @@ pygi_collect_attributes (PyObject *self, char *indent_char; gboolean first; GString *attr_value; + int len; if (!PyArg_ParseTuple(args, "sOisi", &tag_name, &attributes, @@ -641,7 +545,10 @@ pygi_collect_attributes (PyObject *self, if (attributes == Py_None || !PyList_Size(attributes)) return PyString_FromString(""); - if (calc_attrs_length(attributes, indent, self_indent) > 79) + len = calc_attrs_length(attributes, indent, self_indent); + if (len < 0) + return NULL; + if (len > 79) indent_len = self_indent + strlen(tag_name) + 1; else indent_len = 0; @@ -660,7 +567,9 @@ pygi_collect_attributes (PyObject *self, if (PyTuple_GetItem(tuple, 1) == Py_None) continue; - g_assert(PyArg_ParseTuple(tuple, "ss", &attr, &value)); + /* this leaks, but we exit after, so */ + if (!PyArg_ParseTuple(tuple, "ss", &attr, &value)) + return NULL; if (indent_len && !first) { @@ -699,9 +608,6 @@ init_giscanner(void) (PyMethodDef*)pyscanner_functions); d = PyModule_GetDict (m); - PyGISourceDirective_Type.tp_getset = (PyGetSetDef*)_PyGISourceDirective_getsets; - REGISTER_TYPE (d, "SourceDirective", PyGISourceDirective_Type); - PyGISourceScanner_Type.tp_init = (initproc)pygi_source_scanner_init; PyGISourceScanner_Type.tp_methods = (PyMethodDef*)_PyGISourceScanner_methods; REGISTER_TYPE (d, "SourceScanner", PyGISourceScanner_Type); diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py index aacaa508..ee39862e 100644 --- a/giscanner/glibtransformer.py +++ b/giscanner/glibtransformer.py @@ -28,7 +28,7 @@ import subprocess from .ast import (Callback, Constant, Enum, Function, Member, Namespace, Parameter, Property, Return, Struct, Type, Alias, Union, Field, type_name_from_ctype, - default_array_types, TYPE_UINT8, PARAM_DIRECTION_IN) + default_array_types, TYPE_UINT8, PARAM_TRANSFER_FULL) from .transformer import Names from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags, GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct, @@ -85,8 +85,6 @@ class GLibTransformer(object): def __init__(self, transformer, noclosure=False): self._transformer = transformer self._noclosure = noclosure - self._transformer.set_container_types(['GList*', 'GSList*'], - ['GHashTable*']) self._namespace_name = None self._names = Names() self._uscore_type_names = {} @@ -99,6 +97,7 @@ class GLibTransformer(object): self._validating = False # Public API + def set_introspection_binary(self, binary): self._binary = binary @@ -165,6 +164,7 @@ class GLibTransformer(object): return namespace # Private + def _add_attribute(self, node, replace=False): node_name = node.name if (not replace) and node_name in self._names.names: @@ -203,6 +203,7 @@ class GLibTransformer(object): self._uscore_type_names[no_uscore_prefixed] = node # Helper functions + def _resolve_gtypename(self, gtype_name): try: return self._transformer.gtypename_to_giname(gtype_name, @@ -254,6 +255,7 @@ class GLibTransformer(object): self._register_internal_type(type_name, gnode) # Parser + def _parse_node(self, node): if isinstance(node, Enum): self._parse_enum(node) @@ -345,6 +347,7 @@ class GLibTransformer(object): self._remove_attribute(func.name) func.name = methname target_klass.static_methods.append(func) + func.is_method = True return func def _parse_method(self, func): @@ -431,6 +434,7 @@ class GLibTransformer(object): # We don't need the "this" parameter del func.parameters[0] klass.methods.append(func) + func.is_method = True else: klass.constructors.append(func) return func @@ -512,6 +516,7 @@ class GLibTransformer(object): del self._names.names[maybe_class.name] # Introspection + def _introspect_type(self, xmlnode): if xmlnode.tag in ('enum', 'flags'): self._introspect_enum(xmlnode) @@ -627,7 +632,7 @@ class GLibTransformer(object): rctype = signal_info.attrib['return'] rtype = Type(self._transformer.parse_ctype(rctype), rctype) return_ = Return(rtype, signal_info.attrib['return']) - return_.transfer = 'full' + return_.transfer = PARAM_TRANSFER_FULL signal = GLibSignal(signal_info.attrib['name'], return_) for i, parameter in enumerate(signal_info.findall('param')): if i == 0: @@ -642,6 +647,7 @@ class GLibTransformer(object): node.signals.append(signal) # Resolver + def _resolve_type_name(self, type_name, ctype=None): # Workaround glib bug #548689, to be included in 2.18.0 if type_name == "GParam": @@ -784,25 +790,6 @@ class GLibTransformer(object): def _resolve_property(self, prop): prop.type = self._resolve_param_type(prop.type, allow_invalid=False) - def _adjust_transfer(self, param): - if not (param.transfer is None or param.transfer_inferred): - return - - # Do GLib/GObject-specific type transformations here - node = self._lookup_node(param.type.name) - if node is None: - return - - if isinstance(param, Parameter): - if param.direction != PARAM_DIRECTION_IN: - transfer = 'full' - else: - transfer = 'none' - else: - transfer = 'full' - - param.transfer = transfer - def _adjust_throws(self, func): if func.parameters == []: return @@ -820,12 +807,10 @@ class GLibTransformer(object): self._resolve_parameters(func.parameters) func.retval.type = self._resolve_param_type(func.retval.type) self._adjust_throws(func) - self._adjust_transfer(func.retval) def _resolve_parameters(self, parameters): for parameter in parameters: parameter.type = self._resolve_param_type(parameter.type) - self._adjust_transfer(parameter) def _resolve_field(self, field): if isinstance(field, Callback): @@ -837,6 +822,7 @@ class GLibTransformer(object): alias.target = self._resolve_type_name(alias.target, alias.target) # Validation + def _validate(self, nodes): nodes = list(self._names.names.itervalues()) i = 0 diff --git a/giscanner/scannerlexer.l b/giscanner/scannerlexer.l index 27072cd4..53603e25 100644 --- a/giscanner/scannerlexer.l +++ b/giscanner/scannerlexer.l @@ -200,187 +200,27 @@ yywrap (void) static void -parse_gtkdoc (GISourceScanner *scanner, - gchar *symbol, - int *c1, - int *c2) -{ - gboolean isline = FALSE; - GString *line_buf; - char *line; - gchar **parts; - GISourceDirective *directive; - char *name,*value; - GSList *directives; - GSList *options = NULL; - char *rname; - int n_parts; - - line_buf = g_string_new (""); - - do - { - *c1 = *c2; - if (*c1 == '\n') - { - isline = TRUE; - break; - } - g_string_append_c (line_buf, *c1); - *c2 = input(); - } while (*c2 != EOF && !(*c1 == '*' && *c2 == '/')); - - if (!isline) - { - g_string_free (line_buf, TRUE); - return; - } - - line = g_string_free (line_buf, FALSE); - - /* Ignore lines that don't have a : - this is a hack but avoids - * trying to parse too many things as annotations - */ - if (!strchr (line, ':')) - { - g_free (line); - return; - } - - parts = g_strsplit (line, ":", 3); - n_parts = g_strv_length (parts); - - if (g_ascii_strcasecmp (parts[0], "eprecated") == 0) - { - if (n_parts == 3) - options = g_slist_prepend (options, g_strdup_printf ("%s: %s", parts[1], parts[2])); - else if (n_parts == 2) - options = g_slist_prepend (options, g_strdup (parts[1])); - else - options = g_slist_prepend (options, g_strdup ("")); - name = parts[0]; - value = NULL; - } - else if (g_ascii_strcasecmp (parts[0], "ince") == 0) - { - if (n_parts == 2) - options = g_slist_prepend (options, g_strdup (parts[1])); - else - options = g_slist_prepend (options, g_strdup ("")); - name = parts[0]; - value = NULL; - } - else if (n_parts >= 2) - { - name = parts[0]; - - if (n_parts == 3) - { - char *ptr = g_strdup (parts[1]); - char *start; - char *end; - - options = NULL; - start = strchr (ptr, '('); - while (start != NULL) - { - end = strchr (start, ')'); - if (end) - { - options = g_slist_prepend (options, g_strndup (start+1, end-(start+1))); - start = strchr (end+1, '('); - } - else - { - break; - } - } - g_free (ptr); - value = parts[2]; - } - else - value = parts[1]; - } - else /* parts == 1 */ - { - name = parts[0]; - value = NULL; - } - - /* - * Special cases for global annotations. - * Context-sensitive parsing would probably be the right way to go. - */ - if (g_ascii_strncasecmp ("eturn", name, 5) == 0) - rname = "return"; - else if (g_ascii_strncasecmp ("eprecated", name, 9) == 0) - rname = "deprecated"; - else if (g_ascii_strncasecmp ("ince", name, 4) == 0) - rname = "since"; - else - rname = name; - - directive = gi_source_directive_new (rname, value, options); - directives = g_hash_table_lookup (scanner->directives_map, symbol); - directives = g_slist_prepend (directives, directive); - g_hash_table_replace (scanner->directives_map, - g_strdup (symbol), directives); - - g_strfreev (parts); - g_free (line); -} - - -static void parse_comment (GISourceScanner *scanner) { - GString *symbol = NULL; - gboolean startofline = FALSE, have_symbol = FALSE, start1 = FALSE, start_symbol = FALSE; + GString *comment; int c1, c2; c1 = input(); c2 = input(); + comment = g_string_new (""); + while (c2 != EOF && !(c1 == '*' && c2 == '/')) { - if (c1 == ':') - have_symbol = TRUE; - else if (c1 == '\n') - start1 = TRUE; - else if (c1 == '*' && start1) - start_symbol = TRUE; - else if (!have_symbol && start_symbol) - { - if (!symbol) - symbol = g_string_new (""); - if (c1 != ' ') - g_string_append_c (symbol, c1); - } - - if (c1 == '\n') - { - ++lineno; - startofline = TRUE; - } + g_string_append_c (comment, c1); c1 = c2; c2 = input(); - if ((c1 != '*' && c1 != ' ')) - startofline = FALSE; - - if (startofline && (c1 == ' ') && ((c2 == '@') || (c2 == 'r') || (c2 == 'R') || (c2 == 'D') || (c2 == 'S'))) - { - c1 = c2; - c2 = input(); - if (symbol) - parse_gtkdoc (scanner, symbol->str, &c1, &c2); - } } - if (symbol) - g_string_free (symbol, TRUE); - + scanner->comments = g_slist_prepend (scanner->comments, + g_string_free (comment, FALSE)); } static int diff --git a/giscanner/sourcescanner.c b/giscanner/sourcescanner.c index 08f4b586..cf412362 100644 --- a/giscanner/sourcescanner.c +++ b/giscanner/sourcescanner.c @@ -60,8 +60,6 @@ gi_source_symbol_unref (GISourceSymbol * symbol) if (symbol->base_type) ctype_free (symbol->base_type); g_free (symbol->const_string); - g_slist_foreach (symbol->directives, (GFunc)gi_source_directive_free, NULL); - g_slist_free (symbol->directives); g_slice_free (GISourceSymbol, symbol); } } @@ -178,28 +176,6 @@ gi_source_function_new (void) return func; } -GISourceDirective * -gi_source_directive_new (const gchar *name, - const gchar *value, - GSList *options) -{ - GISourceDirective *directive; - - directive = g_slice_new (GISourceDirective); - directive->name = g_strdup (name); - directive->value = g_strdup (value); - directive->options = options; - return directive; -} - -void -gi_source_directive_free (GISourceDirective *directive) -{ - g_free (directive->name); - g_free (directive->value); - g_slice_free (GISourceDirective, directive); -} - GISourceScanner * gi_source_scanner_new (void) { @@ -208,7 +184,6 @@ gi_source_scanner_new (void) scanner = g_slice_new0 (GISourceScanner); scanner->typedef_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - scanner->directives_map = g_hash_table_new (g_str_hash, g_str_equal); scanner->struct_or_union_or_enum_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)gi_source_symbol_unref); @@ -221,10 +196,11 @@ gi_source_scanner_free (GISourceScanner *scanner) { g_free (scanner->current_filename); - g_hash_table_destroy (scanner->directives_map); g_hash_table_destroy (scanner->typedef_table); g_hash_table_destroy (scanner->struct_or_union_or_enum_table); + g_slist_foreach (scanner->comments, (GFunc)g_free, NULL); + g_slist_free (scanner->comments); g_slist_foreach (scanner->symbols, (GFunc)gi_source_symbol_unref, NULL); g_slist_free (scanner->symbols); @@ -295,8 +271,7 @@ gi_source_scanner_get_symbols (GISourceScanner *scanner) } GSList * -gi_source_scanner_get_directives(GISourceScanner *scanner, - const gchar *name) +gi_source_scanner_get_comments(GISourceScanner *scanner) { - return g_hash_table_lookup (scanner->directives_map, name); + return g_slist_reverse (scanner->comments); } diff --git a/giscanner/sourcescanner.h b/giscanner/sourcescanner.h index 3a391d85..69417d11 100644 --- a/giscanner/sourcescanner.h +++ b/giscanner/sourcescanner.h @@ -31,7 +31,6 @@ G_BEGIN_DECLS typedef struct _GISourceScanner GISourceScanner; typedef struct _GISourceSymbol GISourceSymbol; typedef struct _GISourceType GISourceType; -typedef struct _GISourceDirective GISourceDirective; typedef enum { @@ -102,7 +101,7 @@ struct _GISourceScanner gboolean macro_scan; GSList *symbols; GList *filenames; - GHashTable *directives_map; + GSList *comments; GHashTable *typedef_table; GHashTable *struct_or_union_or_enum_table; }; @@ -117,7 +116,6 @@ struct _GISourceSymbol gboolean const_int_set; int const_int; char *const_string; - GSList *directives; /* list of GISourceDirective */ }; struct _GISourceType @@ -131,13 +129,6 @@ struct _GISourceType GList *child_list; /* list of GISourceSymbol */ }; -struct _GISourceDirective -{ - char *name; - char *value; - GSList *options; /* list of options (key=value) */ -}; - GISourceScanner * gi_source_scanner_new (void); gboolean gi_source_scanner_lex_filename (GISourceScanner *igenerator, const gchar *filename); @@ -148,8 +139,7 @@ void gi_source_scanner_parse_macros (GISourceScanner *scanne void gi_source_scanner_set_macro_scan (GISourceScanner *scanner, gboolean macro_scan); GSList * gi_source_scanner_get_symbols (GISourceScanner *scanner); -GSList * gi_source_scanner_get_directives (GISourceScanner *scanner, - const gchar *name); +GSList * gi_source_scanner_get_comments (GISourceScanner *scanner); void gi_source_scanner_free (GISourceScanner *scanner); GISourceSymbol * gi_source_symbol_new (GISourceSymbolType type); @@ -157,11 +147,6 @@ gboolean gi_source_symbol_get_const_boolean (GISourceSymbol *symb GISourceSymbol * gi_source_symbol_ref (GISourceSymbol *symbol); void gi_source_symbol_unref (GISourceSymbol *symbol); -GISourceDirective * gi_source_directive_new (const gchar *name, - const gchar *value, - GSList *options); -void gi_source_directive_free (GISourceDirective *directive); - /* Private */ void gi_source_scanner_add_symbol (GISourceScanner *scanner, GISourceSymbol *symbol); diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py index 3188b262..5018bf43 100644 --- a/giscanner/sourcescanner.py +++ b/giscanner/sourcescanner.py @@ -154,12 +154,6 @@ class SourceSymbol(object): symbol_type_name(self.type), self.ident) - def directives(self): - mapping = {} - for directive in self._scanner.get_directives(self._symbol.ident): - mapping[directive.name] = directive.options - return mapping - @property def const_int(self): return self._symbol.const_int @@ -192,6 +186,7 @@ class SourceScanner(object): self._cpp_options = [] # Public API + def set_cpp_options(self, includes, defines, undefines): for prefix, args in [('-I', includes), ('-D', defines), @@ -226,12 +221,16 @@ class SourceScanner(object): for symbol in self._scanner.get_symbols(): yield SourceSymbol(self._scanner, symbol) + def get_comments(self): + return self._scanner.get_comments() + def dump(self): print '-'*30 for symbol in self._scanner.get_symbols(): print symbol.ident, symbol.base_type.name, symbol.type # Private + def _parse(self, filenames): if not filenames: return diff --git a/giscanner/transformer.py b/giscanner/transformer.py index 0ed7ca00..7493a353 100644 --- a/giscanner/transformer.py +++ b/giscanner/transformer.py @@ -19,14 +19,12 @@ # import os -import re from .ast import (Callback, Enum, Function, Namespace, Member, - Parameter, Return, Array, Struct, Field, - Type, Alias, Interface, Class, Node, Union, - List, Map, Varargs, Constant, type_name_from_ctype, - type_names, default_array_types, default_out_types, - TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE) + Parameter, Return, Struct, Field, + Type, Array, Alias, Interface, Class, Node, Union, + Varargs, Constant, type_name_from_ctype, + type_names, TYPE_STRING, BASIC_GIR_TYPES) from .config import DATADIR from .glibast import GLibBoxed from .girparser import GIRParser @@ -75,8 +73,6 @@ class Transformer(object): self._strip_prefix = '' self._includes = set() self._includepaths = [] - self._list_ctypes = [] - self._map_ctypes = [] def get_names(self): return self._names @@ -84,10 +80,6 @@ class Transformer(object): def get_includes(self): return self._includes - def set_container_types(self, list_ctypes, map_ctypes): - self._list_ctypes = list_ctypes - self._map_ctypes = map_ctypes - def set_strip_prefix(self, strip_prefix): self._strip_prefix = strip_prefix @@ -109,6 +101,7 @@ class Transformer(object): self._includes.add(include) # Private + def _find_include(self, include): searchdirs = self._includepaths[:] for path in _xdg_data_dirs: @@ -210,7 +203,6 @@ class Transformer(object): def _create_enum(self, symbol): members = [] - directives = symbol.directives() for child in symbol.base_type.child_list: name = strip_common_prefix(symbol.ident, child.ident).lower() members.append(Member(name, @@ -219,7 +211,6 @@ class Transformer(object): enum_name = self.remove_prefix(symbol.ident) enum = Enum(enum_name, symbol.ident, members) - self._parse_version(enum, directives) self._names.type_names[symbol.ident] = (None, enum) return enum @@ -227,48 +218,6 @@ class Transformer(object): return Member(symbol.ident, symbol.base_type.name, symbol.ident) - def _parse_deprecated(self, node, directives): - deprecated = directives.get('deprecated', False) - if deprecated: - deprecated_value = deprecated[0] - if ':' in deprecated_value: - # Split out gtk-doc version - (node.deprecated_version, node.deprecated) = \ - [x.strip() for x in deprecated_value.split(':', 1)] - else: - # No version, just include str - node.deprecated = deprecated_value.strip() - - def _parse_version(self, node, directives): - version = directives.get('since', False) - if version: - version_value = version[0] - node.version = version_value.strip() - - def _pair_array(self, params, array): - if not array.type.length_param_name: - return - target_name = array.type.length_param_name - for i, param in enumerate(params): - if param.name == array.type.length_param_name: - array.type.length_param_index = i - return - raise ValueError("Unmatched length parameter name %r"\ - % (target_name, )) - - def _pair_annotations(self, params, return_): - names = {} - for param in params: - if param.name in names: - raise ValueError("Duplicate parameter name %r"\ - % (param.name, )) - names[param.name] = 1 - if isinstance(param.type, Array): - self._pair_array(params, param) - - if isinstance(return_.type, Array): - self._pair_array(params, return_) - def _type_is_callback(self, type): if (isinstance(type, Callback) or isinstance(self._typedefs_ns.get(type.name), Callback)): @@ -294,50 +243,35 @@ class Transformer(object): def _augment_callback_params(self, params): for i, param in enumerate(params): - if self._type_is_callback(param.type): - # j is the index where we look for closure/destroy to - # group with the callback param - j = i + 1 - if j == len(params): - continue # no more args -> nothing to group look - # at the param directly following for either a closure - # or a destroy; only one of these will fire - had_closure = self._handle_closure(param, j, params[j]) - had_destroy = self._handle_destroy(param, j, params[j]) - j += 1 - # are we out of params, or did we find neither? - if j == len(params) or (not had_closure and not had_destroy): - continue - # we found either a closure or a destroy; check the - # parameter following for the other - if not had_closure: - self._handle_closure(param, j, params[j]) - if not had_destroy: - self._handle_destroy(param, j, params[j]) - - # We take the annotations from the parser as strings; here we - # want to split them into components, so: - # (transfer full) -> {'transfer' : [ 'full' ]} - def _parse_options(self, options): - ret = {} - ws_re = re.compile(r'\s+') - for opt in options: - items = ws_re.split(opt) - ret[items[0]] = items[1:] - return ret + if not self._type_is_callback(param.type): + continue + + # j is the index where we look for closure/destroy to + # group with the callback param + j = i + 1 + if j == len(params): + continue # no more args -> nothing to group look + # at the param directly following for either a closure + # or a destroy; only one of these will fire + had_closure = self._handle_closure(param, j, params[j]) + had_destroy = self._handle_destroy(param, j, params[j]) + j += 1 + # are we out of params, or did we find neither? + if j == len(params) or (not had_closure and not had_destroy): + continue + # we found either a closure or a destroy; check the + # parameter following for the other + if not had_closure: + self._handle_closure(param, j, params[j]) + if not had_destroy: + self._handle_destroy(param, j, params[j]) def _create_function(self, symbol): - directives = symbol.directives() - parameters = list(self._create_parameters( - symbol.base_type, directives)) - return_ = self._create_return(symbol.base_type.base_type, - directives.get('return', {})) + parameters = list(self._create_parameters(symbol.base_type)) + return_ = self._create_return(symbol.base_type.base_type) self._augment_callback_params(parameters) - self._pair_annotations(parameters, return_) name = self._strip_namespace_func(symbol.ident) func = Function(name, return_, parameters, symbol.ident) - self._parse_version(func, directives) - self._parse_deprecated(func, directives) return func def _create_source_type(self, source_type): @@ -357,47 +291,42 @@ class Transformer(object): value = 'any' return value - def _create_parameters(self, base_type, directives=None): - if directives is None: - dirs = {} - else: - dirs = directives + def _create_parameters(self, base_type): # warn if we see annotations for unknown parameters param_names = set(child.ident for child in base_type.child_list) - dirs_for = set(dirs) - dirs_for = dirs_for.difference(param_names) - dirs_for.discard('return') - dirs_for.discard('deprecated') - dirs_for.discard('since') - if dirs_for: - print 'Unexpected annotations for %s, parameters are %s' % ( - list(dirs_for), list(param_names), ) - for child in base_type.child_list: - yield self._create_parameter( - child, dirs.get(child.ident, {})) + yield self._create_parameter(child) def _create_member(self, symbol): - ctype = symbol.base_type.type - if (ctype == CTYPE_POINTER and + source_type = symbol.base_type + if (source_type.type == CTYPE_POINTER and symbol.base_type.base_type.type == CTYPE_FUNCTION): node = self._create_callback(symbol) else: - opts = {} - if ctype == CTYPE_ARRAY: - opts['array'] = [] + # Special handling for fields; we don't have annotations on them + # to apply later, yet. + if source_type.type == CTYPE_ARRAY: + ctype = self._create_source_type(source_type) + canonical_ctype = self._canonicalize_ctype(ctype) + if canonical_ctype[-1] == '*': + derefed_name = canonical_ctype[:-1] + else: + derefed_name = canonical_ctype + derefed_name = self.resolve_param_type(derefed_name) + ftype = Array(ctype, self.parse_ctype(derefed_name)) child_list = list(symbol.base_type.child_list) + ftype.zeroterminated = False if child_list: - size_opt = 'fixed-size=%d' % (child_list[0].const_int, ) - opts['array'].append(size_opt) - ftype = self._create_type(symbol.base_type, opts, - is_param=False, is_retval=False) + ftype.size = '%d' % (child_list[0].const_int, ) + else: + ftype = self._create_type(symbol.base_type, + is_param=False, is_retval=False) ftype = self.resolve_param_type(ftype) # Fields are assumed to be read-write # (except for Objects, see also glibtransformer.py) - node = Field(symbol.ident, ftype, symbol.ident, - readable=True, writable=True, bits=symbol.const_int) + node = Field(symbol.ident, ftype, ftype.name, + readable=True, writable=True, bits=symbol.const_int) return node def _create_typedef(self, symbol): @@ -473,7 +402,7 @@ class Transformer(object): else: return derefed_typename - def _create_type(self, source_type, options, is_param, is_retval): + def _create_type(self, source_type, is_param, is_retval): ctype = self._create_source_type(source_type) if ctype == 'va_list': raise SkipError() @@ -482,188 +411,37 @@ class Transformer(object): elif ctype == 'FILE*': raise SkipError - canonical_ctype = self._canonicalize_ctype(ctype) - - # Now check for a list/map/array type - if canonical_ctype in self._list_ctypes: - param = options.get('element-type') - if param: - contained_type = self.parse_ctype(param[0]) - else: - contained_type = None - derefed_name = self.parse_ctype(ctype) - rettype = List(derefed_name, - ctype, - contained_type) - elif canonical_ctype in self._map_ctypes: - param = options.get('element-type') - if param: - key_type = self.parse_ctype(param[0]) - value_type = self.parse_ctype(param[1]) - else: - key_type = None - value_type = None - derefed_name = self.parse_ctype(ctype) - rettype = Map(derefed_name, - ctype, - key_type, value_type) - elif ((is_param and canonical_ctype in default_array_types - and not 'out' in options) - or ('array' in options)): - if canonical_ctype[-1] == '*': - derefed_name = canonical_ctype[:-1] - else: - derefed_name = canonical_ctype - rettype = Array(ctype, - self.parse_ctype(derefed_name)) - array_opts = dict([opt.split('=') - for opt in options.get('array', [])]) - if 'length' in array_opts: - rettype.length_param_name = array_opts['length'] - rettype.zeroterminated = False - if 'fixed-size' in array_opts: - rettype.size = array_opts['fixed-size'] - rettype.zeroterminated = False - if 'zero-terminated' in array_opts: - rettype.zeroterminated = array_opts['zero-terminated'] != '0' - else: - derefed_name = self.parse_ctype(ctype, - not (is_param or is_retval)) - rettype = Type(derefed_name, ctype) - - # Deduce direction for some types passed by reference that - # aren't arrays; modifies the options array. - if ('array' not in options and - not ('out' in options or - 'in' in options or - 'inout' in options or - 'in-out' in options) and - source_type.type == CTYPE_POINTER and - derefed_name in default_out_types): - options['out'] = [] - - if 'transfer' in options: - # Transfer is specified, we don't question it. - return rettype + is_member = not (is_param or is_retval) + # Here we handle basic type parsing; most of the heavy lifting + # and inference comes in annotationparser.py when we merge + # in annotation data. + derefed_name = self.parse_ctype(ctype, is_member) + rettype = Type(derefed_name, ctype) + rettype.canonical = self._canonicalize_ctype(ctype) + derefed_ctype = ctype.replace('*', '') + rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype) canontype = type_name_from_ctype(ctype) - - # Since no transfer is specified, we drop into a bunch of - # heuristics to guess it. This mutates the options array to - # set the 'transfer' option. - # Note that we inferred the transfer - options['transfer-inferred'] = [] - stype = source_type - if canontype == TYPE_STRING: - # It's a string - we just look at 'const' - if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST: - options['transfer'] = ['none'] - else: - options['transfer'] = ['full'] - elif 'array' in options or stype.type == CTYPE_ARRAY: - # It's rare to mutate arrays in public GObject APIs - options['transfer'] = ['none'] - elif (canontype in BASIC_GIR_TYPES or - canontype == TYPE_NONE or - stype.type == CTYPE_ENUM): - # Basic types default to 'none' - options['transfer'] = ['none'] - elif (stype.type == CTYPE_POINTER and - stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST): - # Anything with 'const' gets none - options['transfer'] = ['none'] - elif is_param and stype.type == CTYPE_POINTER: - # For generic pointer types, let's look at the argument - # direction. An out/inout argument gets full, everything - # else none. - if ('out' in options or - 'inout' in options or - 'in-out' in options): - options['transfer'] = ['full'] - else: - options['transfer'] = ['none'] - else: - # For anything else we default to none for parameters; - # this covers enums and possibly some other corner cases. - # Return values of structures and the like will end up - # full. - if is_param: - options['transfer'] = ['none'] - else: - options['transfer'] = ['full'] - + if ((canontype == TYPE_STRING or + source_type.type == CTYPE_POINTER) and + source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST): + rettype.is_const = True return rettype - def _handle_generic_param_options(self, param, options): - for option, data in options.iteritems(): - if option == 'transfer': - if data: - depth = data[0] - if depth not in ('none', 'container', 'full'): - raise ValueError("Invalid transfer %r" % (depth, )) - else: - depth = 'full' - param.transfer = depth - elif option == 'transfer-inferred': - # This is a purely internal flag; we don't expect - # people to write it - param.transfer_inferred = True - - def _create_parameter(self, symbol, options): - options = self._parse_options(options) + def _create_parameter(self, symbol): if symbol.type == CSYMBOL_TYPE_ELLIPSIS: ptype = Varargs() - if 'transfer' not in options: - options['transfer'] = ['none'] else: - ptype = self._create_type(symbol.base_type, options, + ptype = self._create_type(symbol.base_type, is_param=True, is_retval=False) ptype = self.resolve_param_type(ptype) - param = Parameter(symbol.ident, ptype) - for option, data in options.iteritems(): - if option in ['in-out', 'inout']: - param.direction = 'inout' - elif option == 'in': - param.direction = 'in' - elif option == 'out': - param.direction = 'out' - elif option == 'allow-none': - param.allow_none = True - elif option.startswith(('element-type', 'array')): - pass - elif option in ('transfer', 'transfer-inferred'): - pass - elif option == 'scope': - param.scope = data[0] - else: - print 'Unhandled parameter annotation option: %r' % ( - option, ) - self._handle_generic_param_options(param, options) + return Parameter(symbol.ident, ptype) - assert param.transfer is not None, param - return param - - def _create_return(self, source_type, options=None): - if options is None: - options_map = {} - else: - options_map = self._parse_options(options) - rtype = self._create_type(source_type, options_map, + def _create_return(self, source_type): + rtype = self._create_type(source_type, is_param=False, is_retval=True) rtype = self.resolve_param_type(rtype) return_ = Return(rtype) - self._handle_generic_param_options(return_, options_map) - for option, data in options_map.iteritems(): - if option in ('transfer', 'transfer-inferred', - 'element-type', 'out'): - pass - elif option.startswith(('element-type', 'array')): - pass - else: - print 'Unhandled return type annotation option: %r' % ( - option, ) - - assert return_.transfer is not None, return_ return return_ def _create_const(self, symbol): @@ -697,7 +475,6 @@ class Transformer(object): return callback def _create_struct(self, symbol): - directives = symbol.directives() struct = self._typedefs_ns.get(symbol.ident, None) if struct is None: # This is a bit of a hack; really we should try @@ -715,12 +492,9 @@ class Transformer(object): if field: struct.fields.append(field) - self._parse_version(struct, directives) - return struct def _create_union(self, symbol): - directives = symbol.directives() union = self._typedefs_ns.get(symbol.ident, None) if union is None: # This is a bit of a hack; really we should try @@ -738,24 +512,17 @@ class Transformer(object): if field: union.fields.append(field) - self._parse_version(union, directives) - return union def _create_callback(self, symbol): - directives = symbol.directives() - parameters = self._create_parameters(symbol.base_type.base_type, - directives) - retval = self._create_return(symbol.base_type.base_type.base_type, - directives.get('return', {})) + parameters = self._create_parameters(symbol.base_type.base_type) + retval = self._create_return(symbol.base_type.base_type.base_type) if symbol.ident.find('_') > 0: name = self.remove_prefix(symbol.ident, True) else: name = self.remove_prefix(symbol.ident) callback = Callback(name, retval, list(parameters), symbol.ident) - self._parse_version(callback, directives) - return callback def _typepair_to_str(self, item): @@ -833,19 +600,6 @@ class Transformer(object): ptype.name = self.resolve_type_name_full(ptype.name, self.ctype_of(ptype), names, **kwargs) - if isinstance(ptype, (Array, List)): - if ptype.element_type is not None: - ptype.element_type = \ - self.resolve_param_type_full(ptype.element_type, - names, **kwargs) - if isinstance(ptype, Map): - if ptype.key_type is not None: - ptype.key_type = \ - self.resolve_param_type_full(ptype.key_type, - names, **kwargs) - ptype.value_type = \ - self.resolve_param_type_full(ptype.value_type, - names, **kwargs) elif isinstance(ptype, basestring): return self.resolve_type_name_full(ptype, None, names, **kwargs) else: diff --git a/giscanner/xmlwriter.py b/giscanner/xmlwriter.py index 6fd03f44..bed3f991 100644 --- a/giscanner/xmlwriter.py +++ b/giscanner/xmlwriter.py @@ -82,6 +82,7 @@ class XMLWriter(object): self._indent_char = ' ' # Private + def _open_tag(self, tag_name, attributes=None): attrs = collect_attributes( tag_name, attributes, self._indent, @@ -93,6 +94,7 @@ class XMLWriter(object): self.write_line('</%s>' % (tag_name, )) # Public API + def get_xml(self): return self._data.getvalue() diff --git a/tests/scanner/annotation-1.0-expected.gir b/tests/scanner/annotation-1.0-expected.gir index 155b9c4a..a0c2a68b 100644 --- a/tests/scanner/annotation-1.0-expected.gir +++ b/tests/scanner/annotation-1.0-expected.gir @@ -328,5 +328,12 @@ <type name="none" c:type="void"/> </return-value> </function> + <record name="Struct" c:type="_AnnotationStruct"> + <field name="objects" writable="1"> + <array zero-terminated="0" c:type="AnnotationObject*" fixed-size="10"> + <type name="Object"/> + </array> + </field> + </record> </namespace> </repository> diff --git a/tests/scanner/annotation-1.0-expected.tgir b/tests/scanner/annotation-1.0-expected.tgir index 576d6d74..0a654d56 100644 --- a/tests/scanner/annotation-1.0-expected.tgir +++ b/tests/scanner/annotation-1.0-expected.tgir @@ -306,5 +306,12 @@ <type name="none"/> </return-value> </function> + <record name="Struct"> + <field name="objects" writable="1"> + <array fixed-size="10"> + <type name="Object"/> + </array> + </field> + </record> </namespace> </repository> diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c index 6278b6ce..8fa6d8ec 100644 --- a/tests/scanner/annotation.c +++ b/tests/scanner/annotation.c @@ -50,7 +50,7 @@ annotation_object_out (AnnotationObject *object, int *outarg) * * This is a test for in arguments * - * @inarg: (in): This is an argument test + * @inarg: (in): (transfer none): This is an argument test * Return value: an int */ gint @@ -246,7 +246,7 @@ annotation_object_compute_sum (AnnotationObject *object, /** * annotation_object_compute_sum_n: * @object: a #GObject - * @nums: (array length=n_nums): Sequence of numbers + * @nums: (array length=n_nums zero-terminated=0): Sequence of numbers * @n_nums: Length of number array * * Test taking an array with length parameter diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h index 87946609..ddafeb6b 100644 --- a/tests/scanner/annotation.h +++ b/tests/scanner/annotation.h @@ -90,4 +90,9 @@ void annotation_init (int *argc, char ** annotation_return_array (int *length); void annotation_versioned (void); +struct _AnnotationStruct +{ + AnnotationObject *objects[10]; +}; + #endif /* __ANNOTATION_OBJECT_H__ */ diff --git a/tests/scanner/foo-1.0-expected.gir b/tests/scanner/foo-1.0-expected.gir index db76f277..84a5cc3a 100644 --- a/tests/scanner/foo-1.0-expected.gir +++ b/tests/scanner/foo-1.0-expected.gir @@ -578,6 +578,40 @@ </parameter> </parameters> </function> + <function name="test_const_char_retval" + c:identifier="foo_test_const_char_retval"> + <return-value transfer-ownership="none"> + <type name="utf8" c:type="char*"/> + </return-value> + </function> + <function name="test_const_struct_retval" + c:identifier="foo_test_const_struct_retval"> + <return-value transfer-ownership="none"> + <type name="Struct" c:type="FooStruct*"/> + </return-value> + </function> + <function name="test_const_char_param" + c:identifier="foo_test_const_char_param"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="param" transfer-ownership="none"> + <type name="utf8" c:type="char*"/> + </parameter> + </parameters> + </function> + <function name="test_const_struct_param" + c:identifier="foo_test_const_struct_param"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="param" transfer-ownership="none"> + <type name="Struct" c:type="FooStruct*"/> + </parameter> + </parameters> + </function> <constant name="SUCCESS_INT" value="4408"> <type name="int"/> </constant> diff --git a/tests/scanner/foo-1.0-expected.tgir b/tests/scanner/foo-1.0-expected.tgir index 168373e3..8c205a9c 100644 --- a/tests/scanner/foo-1.0-expected.tgir +++ b/tests/scanner/foo-1.0-expected.tgir @@ -453,6 +453,36 @@ </parameter> </parameters> </function> + <function name="test_const_char_retval" c:identifier="foo_test_const_char_retval"> + <return-value transfer-ownership="none"> + <type name="utf8"/> + </return-value> + </function> + <function name="test_const_struct_retval" c:identifier="foo_test_const_struct_retval"> + <return-value transfer-ownership="none"> + <type name="Struct"/> + </return-value> + </function> + <function name="test_const_char_param" c:identifier="foo_test_const_char_param"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="param" transfer-ownership="none"> + <type name="utf8"/> + </parameter> + </parameters> + </function> + <function name="test_const_struct_param" c:identifier="foo_test_const_struct_param"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="param" transfer-ownership="none"> + <type name="Struct"/> + </parameter> + </parameters> + </function> <constant name="SUCCESS_INT" value="4408"> <type name="int"/> </constant> diff --git a/tests/scanner/foo.h b/tests/scanner/foo.h index b51d0c88..dbef015b 100644 --- a/tests/scanner/foo.h +++ b/tests/scanner/foo.h @@ -281,4 +281,9 @@ void foo_test_string_array (char **array); void foo_test_string_array_with_g (gchar **array); +const char * foo_test_const_char_retval (void); +const FooStruct * foo_test_const_struct_retval (void); +void foo_test_const_char_param (const char * param); +void foo_test_const_struct_param (const FooStruct * param); + #endif /* __FOO_OBJECT_H__ */ diff --git a/tools/g-ir-scanner b/tools/g-ir-scanner index f69bdeb6..2152611d 100755 --- a/tools/g-ir-scanner +++ b/tools/g-ir-scanner @@ -41,6 +41,7 @@ else: 'site-packages') sys.path.insert(0, path) +from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError from giscanner.ast import Include from giscanner.cachestore import CacheStore from giscanner.dumper import compile_introspection_binary @@ -313,6 +314,12 @@ def main(args): namespace = glibtransformer.parse() + ap = AnnotationParser(namespace, ss, transformer) + try: + ap.parse() + except InvalidAnnotationError, e: + raise SystemExit("ERROR in annotation: %s" % (str(e), )) + # Write out AST writer = Writer(namespace, libraries, transformer.get_includes()) data = writer.get_xml() |