summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Dahlin <johan@src.gnome.org>2009-01-11 22:41:31 +0000
committerJohan Dahlin <johan@src.gnome.org>2009-01-11 22:41:31 +0000
commit8ec1e3a52040a226110c04c3bf17ec190812e95b (patch)
treebe1a1dfa0c35c5f9f506e9cc11fa81564b280c01
parentba4ee2e606545ac703941698aa25e6d865e6976f (diff)
downloadgobject-introspection-8ec1e3a52040a226110c04c3bf17ec190812e95b.tar.gz
commit annotation parse patch
svn path=/branches/annotation/; revision=1008
-rw-r--r--giscanner/Makefile.am1
-rw-r--r--giscanner/annotationparser.py497
-rw-r--r--giscanner/ast.py30
-rw-r--r--giscanner/dumper.py2
-rw-r--r--giscanner/girparser.py11
-rw-r--r--giscanner/giscannermodule.c128
-rw-r--r--giscanner/glibtransformer.py36
-rw-r--r--giscanner/scannerlexer.l172
-rw-r--r--giscanner/sourcescanner.c33
-rw-r--r--giscanner/sourcescanner.h19
-rw-r--r--giscanner/sourcescanner.py11
-rw-r--r--giscanner/transformer.py390
-rw-r--r--giscanner/xmlwriter.py2
-rw-r--r--tests/scanner/annotation-1.0-expected.gir7
-rw-r--r--tests/scanner/annotation-1.0-expected.tgir7
-rw-r--r--tests/scanner/annotation.c4
-rw-r--r--tests/scanner/annotation.h5
-rwxr-xr-xtools/g-ir-scanner7
18 files changed, 678 insertions, 684 deletions
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..f3fcd2c3
--- /dev/null
+++ b/giscanner/annotationparser.py
@@ -0,0 +1,497 @@
+# -*- 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 = DocBlock(comment[:pos-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', {})
+ self._parse_param_ret_common(parent, param, options)
+
+ if isinstance(parent, Function):
+ scope = options.get('scope')
+ if scope:
+ param.scope = scope.one()
+
+ 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)
+ node.allow_none = 'allow-none' in options
+
+ 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
+ # Input basic types default to 'none'
+ elif node.type.canonical in BASIC_GIR_TYPES:
+ return PARAM_TRANSFER_NONE
+ else:
+ return PARAM_TRANSFER_FULL
+ elif isinstance(node, Return):
+ if node.type.canonical in BASIC_GIR_TYPES:
+ 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..98f33d63 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,17 +296,14 @@ 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
@@ -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/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()