[gobject-introspection/wip/transformer: 6/7] Major scanner rewrite
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gobject-introspection/wip/transformer: 6/7] Major scanner rewrite
- Date: Tue, 27 Jul 2010 13:49:21 +0000 (UTC)
commit a8869b1400c8af5cd9cf43333eefda86c6e868f5
Author: Colin Walters <walters verbum org>
Date: Tue Jul 27 06:16:37 2010 -0400
Major scanner rewrite
docs/g-ir-scanner.1 | 3 -
gir/Makefile.am | 13 +-
gir/gio-2.0.c | 2 +-
gir/glib-2.0.c | 2 +-
giscanner/Makefile.am | 1 +
giscanner/annotationparser.py | 717 +---------------------------
giscanner/ast.py | 640 +++++++++++++++---------
giscanner/finaltransformer.py | 106 ++++
giscanner/girparser.py | 339 +++++++++-----
giscanner/girwriter.py | 172 ++++----
giscanner/glibast.py | 17 +-
giscanner/glibtransformer.py | 1013 +++++----------------------------------
giscanner/primarytransformer.py | 820 +++++++++++++++++++++++++++++++
giscanner/scannermain.py | 129 ++---
giscanner/sourcescanner.py | 10 +-
giscanner/transformer.py | 506 ++++++++-----------
giscanner/utils.py | 13 +
17 files changed, 2075 insertions(+), 2428 deletions(-)
---
diff --git a/docs/g-ir-scanner.1 b/docs/g-ir-scanner.1
index b43d3f1..750660a 100644
--- a/docs/g-ir-scanner.1
+++ b/docs/g-ir-scanner.1
@@ -106,9 +106,6 @@ several pkg-config packages.
.B \--verbose
Be verbose, include some debugging information.
.TP
-.B \--noclosure
-Do not delete unknown types from the resulting format.
-.TP
.B \--typelib-xml
Convert the resulting xml to only output the types relevant
to the typelib compiler. This is mainly useful for verifying the
diff --git a/gir/Makefile.am b/gir/Makefile.am
index 8fa02b0..f938e98 100644
--- a/gir/Makefile.am
+++ b/gir/Makefile.am
@@ -31,7 +31,7 @@ GLIB_LIBRARY=glib-2.0
endif
GLib_2_0_gir_LIBS = $(GLIB_LIBRARY)
-GLib_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib.h"
+GLib_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="glib.h"
GLib_2_0_gir_PACKAGES = glib-2.0
GLib_2_0_gir_CFLAGS = \
-I$(GLIB_INCLUDEDIR) \
@@ -61,7 +61,7 @@ endif
GObject-2.0.gir: GLib-2.0.gir
GObject_2_0_gir_LIBS = $(GOBJECT_LIBRARY)
-GObject_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="glib-object.h" --add-include-path=.
+GObject_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="glib-object.h" --add-include-path=.
GObject_2_0_gir_PACKAGES = gobject-2.0
GObject_2_0_gir_INCLUDES = GLib-2.0
GObject_2_0_gir_CFLAGS = \
@@ -87,7 +87,7 @@ endif
GModule-2.0.gir: GLib-2.0.gir
GModule_2_0_gir_LIBS = $(GMODULE_LIBRARY)
-GModule_2_0_gir_SCANNERFLAGS = --noclosure --strip-prefix=g --c-include="gmodule.h" --add-include-path=.
+GModule_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="gmodule.h" --add-include-path=.
GModule_2_0_gir_PACKAGES = gmodule-2.0
GModule_2_0_gir_INCLUDES = GLib-2.0
GModule_2_0_gir_CFLAGS = \
@@ -118,7 +118,7 @@ endif
Gio-2.0.gir: GObject-2.0.gir
Gio_2_0_gir_LIBS = $(GIO_LIBRARY)
-Gio_2_0_gir_SCANNERFLAGS = --warn-all --strip-prefix=g --c-include="gio/gio.h" --add-include-path=.
+Gio_2_0_gir_SCANNERFLAGS = --warn-all --reparse-validate --strip-prefix=G --c-include="gio/gio.h" --add-include-path=.
Gio_2_0_gir_PACKAGES = gio-2.0 $(GIO_UNIX_PACKAGES)
Gio_2_0_gir_INCLUDES = GObject-2.0
Gio_2_0_gir_CFLAGS = \
@@ -137,7 +137,6 @@ GIRepository-2.0.gir: GObject-2.0.gir $(top_builddir)/girepository/libgireposito
GIRepository_2_0_gir_LIBS = girepository-1.0
GIRepository_2_0_gir_SCANNERFLAGS = \
- --noclosure \
--strip-prefix=g \
--c-include="girepository.h" \
--pkg-export gobject-introspection-1.0 \
@@ -180,7 +179,7 @@ if BUILD_TESTS
Everything-1.0.gir: Gio-2.0.gir libgirepository-everything-1.0.la
Everything_1_0_gir_LIBS = libgirepository-everything-1.0.la
-Everything_1_0_gir_SCANNERFLAGS = --warn-all --warn-error
+Everything_1_0_gir_SCANNERFLAGS = --warn-all --warn-error --reparse-validate
Everything_1_0_gir_PACKAGES = gobject-2.0 cairo gio-2.0
Everything_1_0_gir_INCLUDES = GObject-2.0 cairo-1.0 Gio-2.0
Everything_1_0_gir_FILES = everything.h everything.c
@@ -210,7 +209,7 @@ GIMarshallingTests_1_0_gir_LIBS = libgirepository-gimarshallingtests-1.0.la
GIMarshallingTests_1_0_gir_PACKAGES = gobject-2.0
GIMarshallingTests_1_0_gir_INCLUDES = GObject-2.0
GIMarshallingTests_1_0_gir_FILES = gimarshallingtests.h gimarshallingtests.c
-GIMarshallingTests_1_0_gir_SCANNERFLAGS = --warn-all --warn-error
+GIMarshallingTests_1_0_gir_SCANNERFLAGS = --warn-all --warn-error --reparse-validate
INTROSPECTION_GIRS += GIMarshallingTests-$(TYPELIB_VERSION).gir
EXPECTEDGIRS += GIMarshallingTests-$(TYPELIB_VERSION)-expected.gir
diff --git a/gir/gio-2.0.c b/gir/gio-2.0.c
index d58972a..c03b96f 100644
--- a/gir/gio-2.0.c
+++ b/gir/gio-2.0.c
@@ -83,7 +83,7 @@
/**
* g_file_enumerator_next_files_finish:
*
- * Return value: (transfer full) (element-type FileInfo):
+ * Return value: (transfer full) (element-type GFileInfo):
*/
/**
diff --git a/gir/glib-2.0.c b/gir/glib-2.0.c
index 6203121..6591c64 100644
--- a/gir/glib-2.0.c
+++ b/gir/glib-2.0.c
@@ -6,7 +6,7 @@
/**
* g_file_set_contents:
- * @contents: (array length=length) (element-type uint8):
+ * @contents: (array length=length) (element-type guint8):
*/
/**
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
index 642bcbc..6a83826 100644
--- a/giscanner/Makefile.am
+++ b/giscanner/Makefile.am
@@ -38,6 +38,7 @@ pkgpyexec_PYTHON = \
cachestore.py \
config.py \
dumper.py \
+ finaltransformer.py \
girparser.py \
girwriter.py \
glibast.py \
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 9e4340f..bbbba78 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -36,7 +36,8 @@ from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
PARAM_TRANSFER_NONE,
PARAM_TRANSFER_CONTAINER,
PARAM_TRANSFER_FULL,
- TYPE_ANY, TYPE_NONE)
+ TYPE_ANY, TYPE_NONE, TYPE_STRING)
+from .transformer import TypeResolutionException
from .odict import odict
from .glibast import GLibBoxed
@@ -85,10 +86,6 @@ OPT_SCOPE_ASYNC = 'async'
OPT_SCOPE_CALL = 'call'
OPT_SCOPE_NOTIFIED = 'notified'
-class InvalidAnnotationError(Exception):
- pass
-
-
class DocBlock(object):
def __init__(self, name, options):
@@ -153,16 +150,16 @@ class AnnotationParser(object):
OPTION_RE = re.compile(r'\([A-Za-z]+[^(]*\)')
RETURNS_RE = re.compile(r'^return(s?)( value)?:', re.IGNORECASE)
- def __init__(self, namespace, source_scanner, transformer):
+ def __init__(self, source_scanner, transformer):
self._blocks = {}
- self._namespace = namespace
self._transformer = transformer
- for comment in source_scanner.get_comments():
- self._parse_comment(comment)
+ self._source_scanner = source_scanner
+ self._namespace = transformer.namespace
def parse(self):
- aa = AnnotationApplier(self._blocks, self._transformer)
- aa.parse(self._namespace)
+ for comment in self._source_scanner.get_comments():
+ self._parse_comment(comment)
+ return self._blocks
def _parse_comment(self, comment):
# We're looking for gtk-doc comments here, they look like this:
@@ -311,701 +308,3 @@ class AnnotationParser(object):
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):
- self._namespace = namespace
- for node in namespace.nodes[:]:
- self._parse_node(node)
- del self._namespace
-
- # 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, Bitfield):
- self._parse_bitfield(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_.type_name)
- self._parse_node_common(class_, block)
- self._parse_constructors(class_.constructors)
- self._parse_methods(class_, class_.methods)
- self._parse_vfuncs(class_, class_.virtual_methods)
- self._parse_methods(class_, class_.static_methods)
- self._parse_properties(class_, class_.properties)
- self._parse_signals(class_, class_.signals)
- self._parse_fields(class_, class_.fields)
- if block:
- class_.doc = block.comment
- self._parse_type_instance_tags(class_, block)
-
- def _parse_interface(self, interface):
- block = self._blocks.get(interface.type_name)
- self._parse_node_common(interface, block)
- self._parse_methods(interface, interface.methods)
- self._parse_vfuncs(interface, interface.virtual_methods)
- self._parse_properties(interface, interface.properties)
- self._parse_signals(interface, interface.signals)
- self._parse_fields(interface, interface.fields)
- if block:
- interface.doc = block.comment
-
- def _parse_record(self, record):
- block = self._blocks.get(record.symbol)
- self._parse_node_common(record, block)
- self._parse_constructors(record.constructors)
- self._parse_methods(record, record.methods)
- self._parse_fields(record, record.fields, block)
- if block:
- record.doc = block.comment
-
- def _parse_boxed(self, boxed):
- block = self._blocks.get(boxed.name)
- self._parse_node_common(boxed, block)
- self._parse_constructors(boxed.constructors)
- self._parse_methods(boxed, boxed.methods)
- if block:
- boxed.doc = block.comment
-
- def _parse_union(self, union):
- block = self._blocks.get(union.name)
- self._parse_node_common(union, block)
- self._parse_fields(union, union.fields, block)
- self._parse_constructors(union.constructors)
- self._parse_methods(union, union.methods)
- if block:
- union.doc = block.comment
-
- def _parse_enum(self, enum):
- block = self._blocks.get(enum.symbol)
- self._parse_node_common(enum, block)
- if block:
- enum.doc = block.comment
- type_opt = block.options.get(OPT_TYPE)
- if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
- # This is hack, but hey, it works :-)
- enum.__class__ = Bitfield
-
- def _parse_bitfield(self, bitfield):
- block = self._blocks.get(bitfield.symbol)
- self._parse_node_common(bitfield, block)
- if block:
- bitfield.doc = block.comment
-
- def _parse_constructors(self, constructors):
- for ctor in constructors:
- self._parse_function(ctor)
-
- def _parse_fields(self, parent, fields, block=None):
- for field in fields:
- self._parse_field(parent, field, block)
-
- def _parse_properties(self, parent, properties):
- for prop in properties:
- self._parse_property(parent, prop)
-
- def _parse_methods(self, parent, methods):
- for method in methods:
- self._parse_method(parent, method)
-
- def _parse_vfuncs(self, parent, vfuncs):
- for vfunc in vfuncs:
- self._parse_vfunc(parent, vfunc)
-
- def _parse_signals(self, parent, signals):
- for signal in signals:
- self._parse_signal(parent, signal)
-
- def _parse_property(self, parent, prop):
- block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
- self._parse_node_common(prop, block)
- if block:
- prop.doc = block.comment
-
- transfer_tag = self._get_tag(block, TAG_TRANSFER)
- if transfer_tag is not None:
- options = {OPT_TRANSFER: Option(transfer_tag.value)}
- else:
- options = {}
- prop.transfer = self._extract_transfer(parent, prop, options)
-
- type_tag = self._get_tag(block, TAG_TYPE)
- if type_tag:
- prop.type = self._resolve(type_tag.value, prop.type)
-
- def _parse_callback(self, callback):
- block = self._blocks.get(callback.ctype)
- self._parse_node_common(callback, block)
- self._parse_params(callback, callback.parameters, block)
- self._parse_return(callback, callback.retval, block)
- if block:
- callback.doc = block.comment
-
- def _parse_callable(self, callable, block):
- self._parse_node_common(callable, block)
- for i, param in enumerate(callable.parameters):
- if (param.type.ctype != 'GDestroyNotify' and
- param.type.name != 'GLib.DestroyNotify'):
- continue
- if i < 2:
- break
- callback_param = callable.parameters[i-2]
- if callback_param.closure_index != -1:
- callback_param.scope = OPT_SCOPE_NOTIFIED
- callback_param.transfer = PARAM_TRANSFER_NONE
-
- self._parse_params(callable, callable.parameters, block)
- self._parse_return(callable, callable.retval, block)
- if block:
- callable.doc = block.comment
-
- def _parse_function(self, func):
- block = self._blocks.get(func.symbol)
- self._parse_callable(func, block)
- self._parse_rename_to_func(func, block)
-
- def _parse_signal(self, parent, signal):
- block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
- self._parse_node_common(signal, block)
- # We're only attempting to name the signal parameters if
- # the number of parameter tags (@foo) is the same or greater
- # than the number of signal parameters
- if block and len(block.tags) > len(signal.parameters):
- names = block.tags.items()
- else:
- names = []
- for i, param in enumerate(signal.parameters):
- if names:
- name, tag = names[i+1]
- param.name = name
- options = getattr(tag, 'options', {})
- param_type = options.get(OPT_TYPE)
- if param_type:
- param.type = self._resolve(param_type.one(), param.type)
- else:
- tag = None
- self._parse_param(signal, param, tag)
- self._parse_return(signal, signal.retval, block)
- if block:
- signal.doc = block.comment
-
- def _parse_method(self, parent, meth):
- block = self._blocks.get(meth.symbol)
- self._parse_function(meth)
- virtual = self._get_tag(block, TAG_VFUNC)
- if virtual:
- invoker_name = virtual.value
- matched = False
- for vfunc in parent.virtual_methods:
- if vfunc.name == invoker_name:
- matched = True
- vfunc.invoker = meth
- break
- if not matched:
- print "warning: unmatched virtual invoker %r for method %r" % \
- (invoker_name, meth.symbol)
-
- def _parse_vfunc(self, parent, vfunc):
- key = '%s::%s' % (parent.type_name, vfunc.name)
- self._parse_callable(vfunc, self._blocks.get(key))
- if vfunc.invoker:
- # We normally expect annotations like (element-type) to be
- # applied to the invoker.
- block = self._blocks.get(vfunc.invoker.symbol)
- self._parse_callable(vfunc, block)
-
- def _parse_field(self, parent, field, block=None):
- if isinstance(field, Callback):
- self._parse_callback(field)
- else:
- if not block:
- return
- tag = block.get(field.name)
- if not tag:
- return
- t = tag.options.get('type')
- if not t:
- return
- field.type.name = self._transformer.resolve_type_name(t.one())
-
- def _check_arg_annotations(self, parent, params, block):
- if block is None:
- return
- for tag in block.tags.keys():
- if tag == TAG_RETURNS:
- continue
- for param in params:
- if param.name == tag:
- break
- else:
- return
- #print 'WARNING: annotation for "%s" refers to unknown ' \
- # 'argument "%s"' % (parent.name, tag)
-
- def _parse_params(self, parent, params, block):
- self._check_arg_annotations(parent, params, block)
- for param in params:
- tag = self._get_tag(block, param.name)
- self._parse_param(parent, param, tag)
-
- def _parse_return(self, parent, return_, block):
- tag = self._get_tag(block, TAG_RETURNS)
- self._parse_param_ret_common(parent, return_, tag)
-
- def _get_parameter_index(self, parent, param_name, location_name):
- index = parent.get_parameter_index(param_name)
- if index is None:
- raise InvalidAnnotationError(
- "can't find parameter %s referenced by parameter %s of %r"
- % (param_name, location_name, parent.name))
-
- return index
-
- def _parse_param(self, parent, param, tag):
- options = getattr(tag, 'options', {})
- if isinstance(parent, Function):
- scope = options.get(OPT_SCOPE)
- if scope:
- param.scope = scope.one()
- if param.scope not in [PARAM_SCOPE_CALL,
- PARAM_SCOPE_ASYNC,
- PARAM_SCOPE_NOTIFIED]:
- raise InvalidAnnotationError(
- "scope for %s of %r is invalid (%r), must be one of "
- "call, async, notified."
- % (param.name, parent.name, param.scope))
- param.transfer = PARAM_TRANSFER_NONE
- elif (param.type.ctype == 'GAsyncReadyCallback' or
- param.type.name == 'Gio.AsyncReadyCallback'):
- param.scope = OPT_SCOPE_ASYNC
- param.transfer = PARAM_TRANSFER_NONE
-
- destroy = options.get(OPT_DESTROY)
- if destroy:
- param.destroy_index = self._get_parameter_index(parent,
- destroy.one(),
- param.name)
- self._fixup_param_destroy(parent, param)
- closure = options.get(OPT_CLOSURE)
- if closure:
- param.closure_index = self._get_parameter_index(parent,
- closure.one(),
- param.name)
- self._fixup_param_closure(parent, param)
- if isinstance(parent, Callback):
- if OPT_CLOSURE in options:
- # For callbacks, (closure) appears without an
- # argument, and tags a parameter that is a closure. We
- # represent it (weirdly) in the gir and typelib by
- # setting param.closure_index to its own index.
- param.closure_index = parent.get_parameter_index(param.name)
- self._fixup_param_closure(parent, param)
-
- self._parse_param_ret_common(parent, param, tag)
-
- def _fixup_param_destroy(self, parent, param):
- for p in parent.parameters:
- if p is not param and p.destroy_index == param.destroy_index:
- p.destroy_index = -1
-
- def _fixup_param_closure(self, parent, param):
- for p in parent.parameters:
- if p is not param and p.closure_index == param.closure_index:
- p.closure_index = -1
-
- def _parse_param_ret_common(self, parent, node, tag):
- options = getattr(tag, 'options', {})
- (node.direction, node.caller_allocates) = \
- 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 node.direction is None:
- node.direction = self._guess_direction(node)
- node.caller_allocates = False
- node.transfer = self._extract_transfer(parent, node, options)
- param_type = options.get(OPT_TYPE)
- if param_type:
- node.type = self._resolve(param_type.one(), node.type)
-
- if (OPT_ALLOW_NONE in options or
- node.type.ctype == 'GCancellable*'):
- node.allow_none = True
-
- assert node.transfer is not None
- if tag is not None and tag.comment is not None:
- node.doc = tag.comment
-
- for key in options:
- if '.' in key:
- value = options.get(key)
- if value:
- node.attributes.append((key, value.one()))
-
- def _extract_direction(self, node, options):
- caller_allocates = False
- if (OPT_INOUT in options or
- OPT_INOUT_ALT in options):
- direction = PARAM_DIRECTION_INOUT
- elif OPT_OUT in options:
- subtype = options[OPT_OUT]
- if subtype is not None:
- subtype = subtype.one()
- direction = PARAM_DIRECTION_OUT
- if subtype in (None, ''):
- if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
- caller_allocates = '**' not in node.type.ctype
- else:
- caller_allocates = False
- elif subtype == 'caller-allocates':
- caller_allocates = True
- elif subtype == 'callee-allocates':
- caller_allocates = False
- else:
- raise InvalidAnnotationError(
- "out direction for %s is invalid (%r)" % (node, subtype))
- elif OPT_IN in options:
- direction = PARAM_DIRECTION_IN
- else:
- direction = node.direction
- return (direction, caller_allocates)
-
- 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 _is_array_type(self, node):
- if node.type.name in ['GLib.Array', 'GLib.PtrArray',
- 'GLib.ByteArray']:
- return True
- if node.type.ctype in ['GArray*', 'GPtrArray*', 'GByteArray*']:
- return True
- return False
-
- def _extract_container_type(self, parent, node, options):
- has_element_type = OPT_ELEMENT_TYPE in options
- has_array = OPT_ARRAY in options
-
- if not has_array:
- has_array = self._is_array_type(node)
-
- # FIXME: This is a hack :-(
- if (not isinstance(node, Field) and
- (not has_element_type and
- (node.direction is None
- or isinstance(node, Return)
- 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(OPT_ARRAY)
- if array_opt:
- array_values = array_opt.all()
- else:
- array_values = {}
-
- is_g_array = self._is_array_type(node)
-
- element_type = options.get(OPT_ELEMENT_TYPE)
- if element_type is not None:
- element_type_node = self._resolve(element_type.one())
- else:
- if is_g_array:
- element_type_node = None
- else:
- element_type_node = Type(node.type.name) # erase ctype
-
- if is_g_array:
- type_name = node.type.name
- else:
- type_name = None
-
- container_type = Array(type_name, node.type.ctype,
- element_type_node)
- container_type.is_const = node.type.is_const
- if OPT_ARRAY_ZERO_TERMINATED in array_values:
- container_type.zeroterminated = array_values.get(
- OPT_ARRAY_ZERO_TERMINATED) == '1'
- length = array_values.get(OPT_ARRAY_LENGTH)
- if length is not None:
- param_index = self._get_parameter_index(parent, length, node.name)
- container_type.length_param_index = param_index
- # For in parameters we're incorrectly deferring
- # char/unsigned char to utf8 when a length annotation
- # is specified.
- if (isinstance(node, Parameter) and
- node.type.name == 'utf8' and
- self._guess_direction(node) == PARAM_DIRECTION_IN and
- element_type is None):
- # FIXME: unsigned char/guchar should be uint8
- container_type.element_type = Type('gint8')
- container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
- return container_type
-
- def _resolve(self, type_str, orig_node=None):
- def grab_one(type_str, resolver, top_combiner, combiner):
- """Return a complete type, and the trailing string part after it.
- Use resolver() on each identifier, and combiner() on the parts of
- each complete type. (top_combiner is used on the top-most type.)"""
- bits = re.split(r'([,<>])', type_str, 1)
- first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
- args = [resolver(first)]
- if sep == '<':
- while sep != '>':
- next, rest = grab_one(rest, resolver, combiner, combiner)
- args.append(next)
- sep, rest = rest[0], rest[1:]
- else:
- rest = sep + rest
- return top_combiner(*args), rest
- def resolver(ident):
- return self._transformer.resolve_param_type(Type(ident))
- def combiner(base, *rest):
- if not rest:
- return base
- if (base.name in ['GLib.List', 'GLib.SList'] or
- base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
- return List(base.name, base.ctype, *rest)
- if (base.name in ['GLib.HashTable'] or
- base.ctype in ['GHashTable*']) and len(rest)==2:
- return Map(base.name, base.ctype, *rest)
- print "WARNING: throwing away type parameters:", type_str
- return base
- def top_combiner(base, *rest):
- """For the top most type, recycle orig_node if possible."""
- if orig_node is not None:
- orig_node.name = base.name
- base = orig_node # preserve other properties of orig_node
- return combiner(base, *rest)
-
- result, rest = grab_one(type_str, resolver, top_combiner, combiner)
- if rest:
- print "WARNING: throwing away trailing part of type:", type_str
- return result
-
- def _parse_element_type(self, parent, node, options):
- element_type_opt = options.get(OPT_ELEMENT_TYPE)
- element_type = element_type_opt.flat()
- if (node.type.name in ['GLib.List', 'GLib.SList'] or
- node.type.ctype in ['GList*', 'GSList*']):
- assert len(element_type) == 1
- container_type = List(
- node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]))
- elif (node.type.name in ['GLib.HashTable'] or
- node.type.ctype in ['GHashTable*']):
- assert len(element_type) == 2
- container_type = Map(
- node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]),
- self._resolve(element_type[1]))
- elif self._is_array_type(node):
- container_type = Array(node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]))
- else:
- print 'FIXME: unhandled element-type container:', node
- return container_type
-
- def _extract_transfer(self, parent, node, options):
- transfer_opt = options.get(OPT_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_node_common(self, node, block):
- self._parse_version(node, block)
- self._parse_deprecated(node, block)
- self._parse_attributes(node, block)
- self._parse_skip(node, block)
- self._parse_foreign(node, block)
-
- def _parse_version(self, node, block):
- since_tag = self._get_tag(block, TAG_SINCE)
- if since_tag is None:
- return
- node.version = since_tag.value
-
- def _parse_deprecated(self, node, block):
- deprecated_tag = self._get_tag(block, TAG_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 _parse_attributes(self, node, block):
- annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
- if annos_tag is None:
- return
- options = AnnotationParser.parse_options(annos_tag.value)
- for key, value in options.iteritems():
- if value:
- node.attributes.append((key, value.one()))
-
- def _parse_skip(self, node, block):
- if block is not None:
- if OPT_SKIP in block.options:
- node.skip = True
-
- def _parse_foreign(self, node, block):
- if block is not None:
- if OPT_FOREIGN in block.options:
- node.foreign = True
-
- def _parse_type_instance_tags(self, node, block):
- tag = self._get_tag(block, TAG_UNREF_FUNC)
- node.unref_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_REF_FUNC)
- node.ref_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_SET_VALUE_FUNC)
- node.set_value_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_GET_VALUE_FUNC)
- node.get_value_func = tag.value if tag else None
-
- def _parse_rename_to_func(self, node, block):
- rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
- if rename_to_tag is None:
- return
- new_name = rename_to_tag.value
-
- shadowed = []
-
- def shadowed_filter(n):
- if isinstance(n, Function) and n.symbol == new_name:
- shadowed.append(n)
- return False
- return True
-
- self._namespace.remove_matching(shadowed_filter)
- if len(shadowed) == 1:
- # method override; use the same (stripped) name as the overloaded
- # method referenced.
- # Note that 'g_timeout_add_full' may specify a new_name of
- # 'g_timeout_add' but the *real* name desired is the stripped name
- # of 'g_timeout_add' which is 'timeout_add' (for example).
- node.name = shadowed[0].name
- elif len(shadowed) == 0:
- # literal rename, to force a particular prefix strip or whatever
- # Example: the "nm-utils" package uses a "NM" prefix in most places
- # but some functions have an "nm_utils_" prefix; the 'Rename To:'
- # annotation in this case is used to strip the 'utils_' part off.
- node.name = new_name
- else:
- assert False # more than two shadowed methods? Shouldn't happen.
-
- 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
-
- # 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]:
- if node.caller_allocates:
- return PARAM_TRANSFER_NONE
- 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 (isinstance(node.type, Array) and
- node.type.element_type is not None and
- node.type.element_type.name == 'utf8'):
- return PARAM_TRANSFER_FULL
- elif (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
- elif isinstance(node, Property):
- return PARAM_TRANSFER_NONE
- else:
- raise AssertionError(node)
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 4008b66..901667c 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,63 +19,149 @@
# Boston, MA 02111-1307, USA.
#
-"""AST nodes
-This file descbribes abstract data type nodes independent on the
-implementation language.
-
-These can later on be extended (eg subclassed) with additional information
-which is language/library/domain specific.
+from .odict import odict
+from .utils import to_underscores
+
+class Type(object):
+ """A Type can be either:
+* A reference to a node (target_giname)
+* A reference to a "fundamental" type like 'utf8'
+* A "foreign" type - this can be any string."
+If none are specified, then it's in an "unresolved" state.
+In this case, the ctype must be specified.
"""
+ resolved = property(lambda self: (self.target_fundamental or
+ self.target_giname or
+ self.target_foreign))
+
+ def __init__(self,
+ ctype=None,
+ target_fundamental=None,
+ target_giname=None,
+ target_foreign=None,
+ _target_unknown=False,
+ is_const=False,
+ origin_symbol=None):
+ self.ctype = ctype
+ self.origin_symbol = origin_symbol
+ if _target_unknown:
+ assert isinstance(self, TypeUnknown)
+ elif target_fundamental:
+ assert target_giname is None
+ assert target_foreign is None
+ elif target_giname:
+ assert target_fundamental is None
+ assert target_foreign is None
+ elif target_foreign:
+ assert ctype is not None
+ assert target_giname is None
+ assert target_fundamental is None
+ else:
+ assert ctype is not None
+ self.target_fundamental = target_fundamental
+ self.target_giname = target_giname
+ self.target_foreign = target_foreign
+ self.is_const = is_const
+
+ def get_giname(self):
+ assert self.target_giname is not None
+ return self.target_giname.split('.')[1]
+
+ def __cmp__(self, other):
+ if self.target_fundamental:
+ return cmp(self.target_fundamental, other.target_fundamental)
+ if self.target_giname:
+ return cmp(self.target_giname, other.target_giname)
+ if self.target_foreign:
+ return cmp(self.target_foreign, other.target_foreign)
+ return cmp(self.ctype, other.ctype)
+
+ def is_equiv(self, typeval):
+ """Return True if the specified types are compatible at
+ an introspection level, disregarding their C types.
+ A sequence may be given for typeval, in which case
+ this function returns True if the type is compatible with
+ any."""
+ if isinstance(typeval, (list, tuple)):
+ for val in typeval:
+ if self.is_equiv(val):
+ return True
+ return False
+ return self == typeval
+
+ def clone(self):
+ return Type(target_fundamental=self.target_fundamental,
+ target_giname=self.target_giname,
+ target_foreign=self.target_foreign,
+ ctype=self.ctype,
+ is_const=self.is_const)
+
+ def __str__(self):
+ if self.target_fundamental:
+ return 'Type(target_fundamental=%s)' % (self.target_fundamental, )
+ elif self.target_giname:
+ return 'Type(target_giname=%s)' % (self.target_giname, )
+ elif self.target_foreign:
+ return 'Type(target_foreign=%s)' % (self.target_foreign, )
+
+class TypeUnknown(Type):
+ def __init__(self):
+ Type.__init__(self, _target_unknown=True)
+
######
## Fundamental types
######
# Two special ones
-TYPE_NONE = 'none'
-TYPE_ANY = 'gpointer'
+TYPE_NONE = Type(target_fundamental='void', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
# "Basic" types
-TYPE_BOOLEAN = 'gboolean'
-TYPE_INT8 = 'gint8'
-TYPE_UINT8 = 'guint8'
-TYPE_INT16 = 'gint16'
-TYPE_UINT16 = 'guint16'
-TYPE_INT32 = 'gint32'
-TYPE_UINT32 = 'guint32'
-TYPE_INT64 = 'gint64'
-TYPE_UINT64 = 'guint64'
-TYPE_CHAR = 'gchar'
-TYPE_SHORT = 'gshort'
-TYPE_USHORT = 'gushort'
-TYPE_INT = 'gint'
-TYPE_UINT = 'guint'
-TYPE_LONG = 'glong'
-TYPE_ULONG = 'gulong'
+TYPE_BOOLEAN = Type(target_fundamental='gboolean', ctype='gboolean')
+TYPE_INT8 = Type(target_fundamental='gint8', ctype='gint8')
+TYPE_UINT8 = Type(target_fundamental='guint8', ctype='guint8')
+TYPE_INT16 = Type(target_fundamental='gint16', ctype='gint16')
+TYPE_UINT16 = Type(target_fundamental='guint16', ctype='guint16')
+TYPE_INT32 = Type(target_fundamental='gint32', ctype='gint32')
+TYPE_UINT32 = Type(target_fundamental='guint32', ctype='guint32')
+TYPE_INT64 = Type(target_fundamental='gint64', ctype='gint64')
+TYPE_UINT64 = Type(target_fundamental='guint64', ctype='guint64')
+TYPE_CHAR = Type(target_fundamental='gchar', ctype='gchar')
+TYPE_SHORT = Type(target_fundamental='gshort', ctype='gshort')
+TYPE_USHORT = Type(target_fundamental='gushort', ctype='gushort')
+TYPE_INT = Type(target_fundamental='gint', ctype='gint')
+TYPE_UINT = Type(target_fundamental='guint', ctype='guint')
+TYPE_LONG = Type(target_fundamental='glong', ctype='glong')
+TYPE_ULONG = Type(target_fundamental='gulong', ctype='gulong')
# C99 types
-TYPE_LONG_LONG = 'long long'
-TYPE_LONG_ULONG = 'unsigned long long'
-TYPE_FLOAT = 'gfloat'
-TYPE_DOUBLE = 'gdouble'
+TYPE_LONG_LONG = Type(target_fundamental='long long', ctype='long long')
+TYPE_LONG_ULONG = Type(target_fundamental='unsigned long long', ctype='unsigned long long')
+TYPE_FLOAT = Type(target_fundamental='gfloat', ctype='gfloat')
+TYPE_DOUBLE = Type(target_fundamental='gdouble', ctype='gdouble')
# ?
-TYPE_LONG_DOUBLE = 'long double'
+TYPE_LONG_DOUBLE = Type(target_fundamental='long double', ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
# C types with semantics overlaid
-TYPE_GTYPE = 'GType'
-TYPE_STRING = 'utf8'
-TYPE_FILENAME = 'filename'
+TYPE_GTYPE = Type(target_fundamental='GType', ctype='GType')
+TYPE_STRING = Type(target_fundamental='utf8', ctype='gchar*')
+TYPE_FILENAME = Type(target_fundamental='filename', ctype='gchar*')
BASIC_GIR_TYPES = [TYPE_BOOLEAN, TYPE_INT8, TYPE_UINT8, TYPE_INT16,
TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64,
TYPE_UINT64, TYPE_CHAR, TYPE_SHORT, TYPE_USHORT, TYPE_INT,
TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_LONG_LONG,
- TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
- TYPE_LONG_DOUBLE, TYPE_GTYPE]
+ TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
+ TYPE_LONG_DOUBLE, TYPE_UNICHAR, TYPE_GTYPE]
GIR_TYPES = [TYPE_NONE, TYPE_ANY]
GIR_TYPES.extend(BASIC_GIR_TYPES)
GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
type_names = {}
for typeval in GIR_TYPES:
- type_names[typeval] = typeval
+ type_names[typeval.target_fundamental] = typeval
+basic_type_names = {}
+for typeval in BASIC_GIR_TYPES:
+ basic_type_names[typeval.target_fundamental] = typeval
# C builtin
type_names['char'] = TYPE_CHAR
@@ -143,11 +229,10 @@ type_names['id'] = TYPE_ANY
# These types, when seen by reference, are converted into an Array()
# by default
+# If you add/change these, be sure to update glibast.py too
default_array_types = {}
default_array_types['guint8*'] = TYPE_UINT8
default_array_types['guchar*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
-default_array_types['char**'] = TYPE_STRING
default_array_types['gchar**'] = TYPE_STRING
# These types, when seen by reference, are interpreted as out parameters
@@ -170,24 +255,167 @@ PARAM_TRANSFER_NONE = 'none'
PARAM_TRANSFER_CONTAINER = 'container'
PARAM_TRANSFER_FULL = 'full'
-def type_name_from_ctype(ctype):
- return type_names.get(ctype, ctype)
-
+class Namespace(object):
+ names = property(lambda self: self._names)
+ aliases = property(lambda self: self._aliases)
+ type_names = property(lambda self: self._type_names)
+ ctypes = property(lambda self: self._ctypes)
-class Node(object):
+ def __init__(self, name, version, c_prefix=None):
+ self.name = name
+ self.version = version
+ self.c_prefix = c_prefix or name
+ self._lower_c_prefix = self.c_prefix.lower()
+ self.uscore_prefix = to_underscores(self.c_prefix).lower()
+ self._names = odict() # Maps from GIName -> node
+ self._aliases = {} # Maps from GIName -> GIName
+ self._type_names = {} # Maps from GTName -> node
+ self._ctypes = {} # Maps from CType -> node
+
+ def type_from_name(self, name, ctype=None):
+ """Backwards compatibility method for older .gir files, which
+only use the 'name' attribute. If name refers to a fundamental type,
+create a Type object referncing it. If name is already a
+fully-qualified GIName like 'Foo.Bar', returns a Type targeting it .
+Otherwise a Type targeting name qualififed with the namespace name is
+returned."""
+ if name in type_names:
+ return Type(target_fundamental=name, ctype=ctype)
+ if '.' in name:
+ target = name
+ else:
+ target = '%s.%s' % (self.name, name)
+ return Type(target_giname=target, ctype=ctype)
+
+ def contains_ident(self, ident):
+ """Return True if this namespace should contain the given C
+identifier string."""
+ if not self.c_prefix:
+ return False
+ return (ident.startswith(self.c_prefix)
+ or ident.startswith(self._lower_c_prefix))
+
+ def append(self, node, replace=False):
+ previous = self._names.get(node.name)
+ if previous is not None:
+ if not replace:
+ raise ValueError("Namespace conflict")
+ self.remove(previous)
+ # A layering violation...but oh well.
+ from .glibast import GLibBoxed
+ if isinstance(node, Alias):
+ self._aliases[node.name] = node
+ elif isinstance(node, (GLibBoxed, Interface, Class)):
+ self._type_names[node.type_name] = node
+ assert isinstance(node, Node)
+ assert node.namespace is None
+ node.namespace = self
+ self._names[node.name] = node
+ if hasattr(node, 'ctype'):
+ self._ctypes[node.ctype] = node
+ elif hasattr(node, 'symbol'):
+ self._ctypes[node.symbol] = node
+
+ def remove(self, node):
+ from .glibast import GLibBoxed
+ if isinstance(node, Alias):
+ del self._aliases[node.name]
+ elif isinstance(node, (GLibBoxed, Interface, Class)):
+ del self._type_names[node.type_name]
+ del self._names[node.name]
+ node.namespace = None
+ if hasattr(node, 'ctype'):
+ del self._ctypes[node.ctype]
+ if hasattr(node, 'symbol'):
+ del self._ctypes[node.symbol]
+
+ def float(self, node):
+ """Like remove(), but doesn't unset the node's namespace
+back-reference."""
+ self.remove(node)
+ node.namespace = self
+
+ def __iter__(self):
+ return iter(self._names)
+
+ def iteritems(self):
+ return self._names.iteritems()
+
+ def itervalues(self):
+ return self._names.itervalues()
+
+ def get(self, name):
+ return self._names.get(name)
+
+ def walk(self, callback):
+ for node in self.itervalues():
+ node.walk(callback, [])
+
+ def iter_recursive(self):
+ """Recursively iterate over all Node instances."""
+ def doyield(node, chain):
+ yield node
+ self.walk(test)
+
+class Include(object):
- def __init__(self, name=None):
+ def __init__(self, name, version):
self.name = name
+ self.version = version
+
+ @classmethod
+ def from_string(self, string):
+ return Include(*string.split('-', 1))
+
+ def __cmp__(self, other):
+ if not isinstance(other, Include):
+ return cmp(self, other)
+ namecmp = cmp(self.name, other.name)
+ if namecmp != 0:
+ return namecmp
+ return cmp(self.version, other.version)
+
+ def __hash__(self):
+ return hash((self.name, self.version))
+
+ def __str__(self):
+ return '%s-%s' % (self.name, self.version)
+
+class Annotated(object):
+ """An object which has a few generic metadata
+properties."""
+ def __init__(self):
+ self.version = None
self.skip = False
self.introspectable = True
self.attributes = [] # (key, value)*
self.deprecated = None
self.deprecated_version = None
- self.version = None
+ self.doc = None
+
+class Node(Annotated):
+ """A node is a type of object which is uniquely identified by its
+(namespace, name) pair. When combined with a ., this is called a
+GIName. It's possible for nodes to contain or point to other nodes."""
+
+ c_name = property(lambda self: self.namespace.name + self.name)
+
+ def __init__(self, name=None):
+ Annotated.__init__(self)
+ self.namespace = None # Should be set later by Namespace.append()
+ self.name = name
self.foreign = False
self.file_positions = set()
+ def create_type(self):
+ """Create a Type object referencing this node."""
+ assert self.namespace is not None
+ return Type(target_giname=('%s.%s' % (self.namespace.name, self.name)))
+
def __cmp__(self, other):
+ nscmp = cmp(self.namespace, other.namespace)
+ if nscmp != 0:
+ return nscmp
return cmp(self.name, other.name)
def __repr__(self):
@@ -206,49 +434,15 @@ class Node(object):
if symbol.source_filename:
self.add_file_position(symbol.source_filename, symbol.line, -1)
-class Namespace(Node):
+ def walk(self, callback, chain):
+ if not callback(self, chain):
+ return False
+ chain.append(self)
+ self._walk(callback, chain)
+ chain.pop()
- def __init__(self, name, version):
- Node.__init__(self, name)
- self.version = version
- self.nodes = []
-
- def __repr__(self):
- return '%s(%r, %r, %r)' % (self.__class__.__name__, self.name,
- self.version, self.nodes)
-
- def remove_matching(self, pred):
-
- def recursive_pred(node):
- node.remove_matching_children(pred)
- return pred(node)
-
- self.nodes = filter(recursive_pred, self.nodes)
-
-class Include(Node):
-
- def __init__(self, name, version):
- Node.__init__(self, 'include')
- self.name = name
- self.version = version
-
- @classmethod
- def from_string(self, string):
- return Include(*string.split('-', 1))
-
- def __cmp__(self, other):
- if not isinstance(other, Include):
- return cmp(self, other)
- namecmp = cmp(self.name, other.name)
- if namecmp != 0:
- return namecmp
- return cmp(self.version, other.version)
-
- def __hash__(self):
- return hash((self.name, self.version))
-
- def __str__(self):
- return '%s-%s' % (self.name, self.version)
+ def _walk(self, callback, chain):
+ pass
class Callable(Node):
@@ -257,29 +451,33 @@ class Callable(Node):
self.retval = retval
self.parameters = parameters
self.throws = not not throws
- self.doc = None
- def __repr__(self):
- return '%s(%r, %r, %r)' % (self.__class__.__name__,
- self.name, self.retval,
- self.parameters)
+ def get_parameter_index(self, name):
+ for i, parameter in enumerate(self.parameters):
+ if parameter.name == name:
+ return i
+
+ def get_parameter(self, name):
+ for parameter in self.parameters:
+ if parameter.name == name:
+ return parameter
+
class Function(Callable):
- def __init__(self, name, retval, parameters, symbol, throws=None):
+ def __init__(self, name, retval, parameters, throws, symbol):
Callable.__init__(self, name, retval, parameters, throws)
self.symbol = symbol
self.is_method = False
- self.doc = None
def get_parameter_index(self, name):
for i, parameter in enumerate(self.parameters):
- if parameter.name == name:
+ if parameter.argname == name:
return i + int(self.is_method)
def get_parameter(self, name):
for parameter in self.parameters:
- if parameter.name == name:
+ if parameter.argname == name:
return parameter
@@ -296,59 +494,56 @@ class VFunction(Callable):
return obj
-class Type(Node):
-
- def __init__(self, name, ctype=None):
- Node.__init__(self, name)
- self.ctype = ctype
- self.resolved = False
- self.is_const = False
- self.canonical = None
- self.derefed_canonical = None
-
class Varargs(Type):
def __init__(self):
- Type.__init__(self, '<varargs>')
+ Type.__init__(self, '<varargs>', target_fundamental='<varargs>')
class Array(Type):
-
- def __init__(self, name, ctype, element_type):
- if name is None:
- name = '<carray>'
- Type.__init__(self, name, ctype)
+ C = '<c>'
+ GLIB_ARRAY = 'GLib.Array'
+ GLIB_BYTEARRAY = 'GLib.ByteArray'
+ GLIB_PTRARRAY = 'GLib.PtrArray'
+
+ def __init__(self, array_type, element_type, **kwargs):
+ Type.__init__(self, target_fundamental='<array>',
+ **kwargs)
+ if array_type in (None, self.C):
+ self.array_type = self.C
+ else:
+ assert array_type in (self.GLIB_ARRAY,
+ self.GLIB_BYTEARRAY,
+ self.GLIB_PTRARRAY)
+ self.array_type = array_type
+ assert isinstance(element_type, Type)
self.element_type = element_type
self.zeroterminated = True
self.length_param_index = -1
self.length_param_name = None
self.size = None
- def __repr__(self):
- return 'Array(%r, %r)' % (self.name, self.element_type, )
-
class List(Type):
- def __init__(self, name, ctype, element_type):
- Type.__init__(self, name, ctype)
+ def __init__(self, name, element_type, **kwargs):
+ Type.__init__(self, target_fundamental='<list>',
+ **kwargs)
+ self.name = name
+ assert isinstance(element_type, Type)
self.element_type = element_type
- def __repr__(self):
- return 'List(%r of %r)' % (self.name, self.element_type, )
-
class Map(Type):
- def __init__(self, name, ctype, key_type, value_type):
- Type.__init__(self, name, ctype)
+ def __init__(self, ctype, key_type, value_type, **kwargs):
+ Type.__init__(self, ctype=ctype, target_fundamental='<map>')
+ assert isinstance(key_type, Type)
self.key_type = key_type
+ assert isinstance(value_type, Type)
self.value_type = value_type
- def __repr__(self):
- return 'Map(%r <%r,%r>)' % (self.name, self.key_type, self.value_type)
-
class Alias(Node):
@@ -357,42 +552,43 @@ class Alias(Node):
self.target = target
self.ctype = ctype
- def __repr__(self):
- return 'Alias(%r, %r)' % (self.name, self.target)
-
-class TypeContainer(Node):
+class TypeContainer(Annotated):
+ """A fundamental base class for Return and Parameter."""
- def __init__(self, name, typenode, transfer):
- Node.__init__(self, name)
+ def __init__(self, typenode, transfer):
+ Annotated.__init__(self)
self.type = typenode
- if transfer in [PARAM_TRANSFER_NONE, PARAM_TRANSFER_CONTAINER,
- PARAM_TRANSFER_FULL]:
+ if transfer is not None:
self.transfer = transfer
+ elif typenode.is_const:
+ self.transfer = PARAM_TRANSFER_NONE
else:
self.transfer = None
class Parameter(TypeContainer):
-
- 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, None]:
- self.direction = direction
- else:
- self.direction = PARAM_DIRECTION_IN
-
- self.caller_allocates = False
+ """An argument to a function."""
+
+ def __init__(self, argname, typenode, direction=None,
+ transfer=None, allow_none=False, scope=None,
+ caller_allocates=False):
+ TypeContainer.__init__(self, typenode, transfer)
+ self.argname = argname
+ self.direction = direction
self.allow_none = allow_none
self.scope = scope
+ self.caller_allocates = caller_allocates
self.closure_index = -1
self.destroy_index = -1
- self.doc = None
- def __repr__(self):
- return 'Parameter(%r, %r)' % (self.name, self.type)
+
+class Return(TypeContainer):
+ """A return value from a function."""
+
+ def __init__(self, rtype, transfer=None):
+ TypeContainer.__init__(self, rtype, transfer)
+ self.direction = PARAM_DIRECTION_OUT
class Enum(Node):
@@ -401,10 +597,6 @@ class Enum(Node):
Node.__init__(self, name)
self.symbol = symbol
self.members = members
- self.doc = None
-
- def __repr__(self):
- return 'Enum(%r, %r)' % (self.name, self.members)
class Bitfield(Node):
@@ -413,22 +605,16 @@ class Bitfield(Node):
Node.__init__(self, name)
self.symbol = symbol
self.members = members
- self.doc = None
-
- def __repr__(self):
- return 'Bitfield(%r, %r)' % (self.name, self.members)
-class Member(Node):
+class Member(Annotated):
def __init__(self, name, value, symbol):
- Node.__init__(self, name)
+ Annotated.__init__(self)
+ self.name = name
self.value = value
self.symbol = symbol
- def __repr__(self):
- return 'Member(%r, %r)' % (self.name, self.value)
-
class Record(Node):
@@ -438,45 +624,34 @@ class Record(Node):
self.constructors = []
self.symbol = symbol
self.disguised = disguised
- self.doc = None
self.methods = []
+ self.static_methods = []
+
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for func in self.methods:
+ func.walk(callback, chain)
+ for func in self.static_methods:
+ func.walk(callback, chain)
def remove_matching_children(self, pred):
self.fields = filter(pred, self.fields)
self.constructors = filter(pred, self.constructors)
self.methods = filter(pred, self.methods)
-# BW compat, remove
-Struct = Record
-
-class Field(Node):
+class Field(Annotated):
- def __init__(self, name, typenode, symbol, readable, writable, bits=None):
- Node.__init__(self, name)
+ def __init__(self, name, typenode, readable, writable, bits=None,
+ anonymous_node=None):
+ Annotated.__init__(self)
+ self.name = name
self.type = typenode
- self.symbol = symbol
self.readable = readable
self.writable = writable
self.bits = bits
-
- def __repr__(self):
- if self.bits:
- return 'Field(%r, %r, %r)' % (self.name, self.type, self.bits)
- else:
- return 'Field(%r, %r)' % (self.name, self.type)
-
-
-class Return(TypeContainer):
-
- def __init__(self, rtype, transfer=None):
- TypeContainer.__init__(self, None, rtype, transfer)
- self.direction = PARAM_DIRECTION_OUT
- self.doc = None
-
- def __repr__(self):
- return 'Return(%r)' % (self.type, )
-
+ self.anonymous_node = anonymous_node
class Class(Node):
@@ -484,6 +659,10 @@ class Class(Node):
Node.__init__(self, name)
self.ctype = name
self.parent = parent
+ # When we're in the scanner, we keep around a list
+ # of parents so that we can transparently fall back
+ # if there are 'hidden' parents
+ self.parent_chain = []
self.glib_type_struct = None
self.is_abstract = is_abstract
self.methods = []
@@ -493,7 +672,6 @@ class Class(Node):
self.constructors = []
self.properties = []
self.fields = []
- self.doc = None
def remove_matching_children(self, pred):
self.methods = filter(pred, self.methods)
@@ -501,87 +679,64 @@ class Class(Node):
self.properties = filter(pred, self.properties)
self.fields = filter(pred, self.fields)
- def __repr__(self):
- return '%s(%r, %r, %r)' % (
- self.__class__.__name__,
- self.name, self.parent, self.methods)
-
+ def _walk(self, callback, chain):
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.virtual_methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
class Interface(Node):
def __init__(self, name, parent):
Node.__init__(self, name)
self.parent = parent
+ self.parent_chain = []
self.methods = []
+ self.static_methods = []
self.virtual_methods = []
self.glib_type_struct = None
self.properties = []
self.fields = []
self.prerequisites = []
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r)' % (
- self.__class__.__name__,
- self.name, self.methods)
+ def _walk(self, callback, chain):
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+ for meth in self.virtual_methods:
+ meth.walk(callback, chain)
class Constant(Node):
- def __init__(self, name, type_name, value):
+ def __init__(self, name, value_type, value):
Node.__init__(self, name)
- self.type = Type(type_name)
+ self.value_type = value_type
self.value = value
- def __repr__(self):
- return 'Constant(%r, %r, %r)' % (
- self.name, self.type, self.value)
-
-class Property(TypeContainer):
+class Property(Node):
- def __init__(self, name, type_name, readable, writable,
- construct, construct_only, ctype=None, transfer=None):
- self.type = Type(type_name, ctype)
- TypeContainer.__init__(self, name, self.type, transfer)
+ def __init__(self, name, typeobj, readable, writable,
+ construct, construct_only, transfer=None):
+ Node.__init__(self, name)
+ self.type = typeobj
self.readable = readable
self.writable = writable
self.construct = construct
self.construct_only = construct_only
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r)' % (
- self.__class__.__name__,
- self.name, self.type)
-
-
-# FIXME: Inherit from Function
+ self.transfer = PARAM_TRANSFER_NONE
-class Callback(Node):
+class Callback(Callable):
- def __init__(self, name, retval, parameters, ctype=None):
- Node.__init__(self, name)
- self.retval = retval
- self.parameters = parameters
+ def __init__(self, name, retval, parameters, throws, ctype=None):
+ Callable.__init__(self, name, retval, parameters, throws)
self.ctype = ctype
- self.throws = False
- self.doc = None
-
- def get_parameter_index(self, name):
- for i, parameter in enumerate(self.parameters):
- if parameter.name == name:
- return i
-
- def get_parameter(self, name):
- for parameter in self.parameters:
- if parameter.name == name:
- return parameter
-
- def __repr__(self):
- return 'Callback(%r, %r, %r)' % (
- self.name, self.retval, self.parameters)
class Union(Node):
@@ -591,8 +746,13 @@ class Union(Node):
self.fields = []
self.constructors = []
self.methods = []
+ self.static_methods = []
self.symbol = symbol
- self.doc = None
- def __repr__(self):
- return 'Union(%r, %r)' % (self.name, self.fields, )
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
diff --git a/giscanner/finaltransformer.py b/giscanner/finaltransformer.py
new file mode 100644
index 0000000..58f4233
--- /dev/null
+++ b/giscanner/finaltransformer.py
@@ -0,0 +1,106 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
+ Function, Interface, Member, Namespace, Node, Parameter,
+ Property, Record, Return, Type, TypeContainer, Union,
+ Class, Field, VFunction, default_array_types,
+ TYPE_ANY, TYPE_GTYPE, TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
+ Map, Varargs, type_names)
+from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
+ GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord)
+from .utils import to_underscores, to_underscores_noprefix
+
+class FinalTransformer(object):
+
+ def __init__(self, transformer):
+ self._transformer = transformer
+ self._namespace = transformer.namespace
+
+ # Public API
+
+ def validate(self):
+ self._namespace.walk(self._analyze_node)
+ self._namespace.walk(self._introspectable_pass2)
+
+ def _interface_vfunc_check(self, node, stack):
+ if isinstance(node, GLibInterface):
+ for vfunc in node.virtual_methods:
+ if not vfunc.invoker:
+ self._transformer.log_node_warning(vfunc,
+"""Virtual function %r has no known invoker""" % (vfunc.name, ),
+ context=node)
+
+ def _parameter_warning(self, parent, param, text, *args):
+ if hasattr(parent, 'symbol'):
+ prefix = '%s: ' % (parent.symbol, )
+ else:
+ prefix = ''
+ if isinstance(param, Parameter):
+ context = "argument %s: " % (param.argname, )
+ else:
+ context = "return value: "
+ self._transformer.log_node_warning(parent, prefix + context + text, *args)
+
+ def _introspectable_param_analysis(self, parent, node):
+ if not node.type.resolved:
+ self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+ parent.introspectable = False
+ elif isinstance(node.type, Varargs):
+ parent.introspectable = False
+ elif not isinstance(node.type, List) and \
+ (node.type.target_giname == 'GLib.List'):
+ self._parameter_warning(parent, node, "Missing (element-type) annotation")
+ parent.introspectable = False
+ elif node.transfer is None:
+ self._parameter_warning(parent, node, "Missing (transfer) annotation")
+ parent.introspectable = False
+
+ def _analyze_node(self, obj, stack):
+ if obj.skip:
+ return False
+ # Combine one-pass checks here
+ self._interface_vfunc_check(obj, stack)
+ # Our first pass for scriptability
+ if isinstance(obj, Callable):
+ for param in obj.parameters:
+ self._introspectable_param_analysis(obj, param)
+ self._introspectable_param_analysis(obj, obj.retval)
+ return True
+
+ def _introspectable_pass2(self, obj, stack):
+ if isinstance(obj, Node) and obj.skip:
+ return False
+ # In the second introspectable pass, we propagate introspectablity;
+ # for example, a varargs callback as an argument to a function
+ # makes the whole function unintrospectable
+ if isinstance(obj, TypeContainer):
+ parent = stack[-1]
+ target = self._transformer.lookup_typenode(obj.type)
+ if target and not target.introspectable:
+ parent.introspectable = False
+ return True
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 9fee1fc..9c4035e 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -22,13 +22,8 @@ import os
from xml.etree.cElementTree import parse
-from .ast import (Alias, Array, Callback, Constant, Enum, Function, Field,
- Namespace, Parameter, Property, Return, Union, Struct, Type,
- Varargs, Include)
-from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
- GLibInterface, GLibObject, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther)
-
+from .ast import *
+from .glibast import *
from .girwriter import COMPATIBLE_GIR_VERSION
CORE_NS = "http://www.gtk.org/introspection/core/1.0"
@@ -51,7 +46,6 @@ def _cns(tag):
class GIRParser(object):
def __init__(self):
- self._include_parsing = False
self._shared_libraries = []
self._includes = set()
self._pkgconfig_packages = set()
@@ -72,6 +66,8 @@ class GIRParser(object):
self._namespace = None
self._shared_libraries = []
self._pkgconfig_packages = set()
+ self._c_includes = set()
+ self._c_prefix = None
self._parse_api(tree.getroot())
def get_namespace(self):
@@ -83,6 +79,12 @@ class GIRParser(object):
def get_includes(self):
return self._includes
+ def get_c_includes(self):
+ return self._c_includes
+
+ def get_c_prefix(self):
+ return self._c_prefix
+
def get_pkgconfig_packages(self):
if not hasattr(self, '_pkgconfig_packages'):
self._pkgconfig_packages = []
@@ -91,11 +93,21 @@ class GIRParser(object):
def get_doc(self):
return parse(self._filename)
- def set_include_parsing(self, include_parsing):
- self._include_parsing = include_parsing
-
# Private
+ def _find_first_child(self, node, name):
+ for child in node.getchildren():
+ if child.tag == name:
+ return child
+ return None
+
+ def _find_children(self, node, name):
+ result = []
+ for child in node.getchildren():
+ if child.tag == name:
+ result.append(child)
+ return result
+
def _get_current_file(self):
if not self._filename_stack:
return None
@@ -105,9 +117,6 @@ class GIRParser(object):
return curfile[len(cwd):]
return curfile
- def _add_node(self, node):
- self._namespace.nodes.append(node)
-
def _parse_api(self, root):
assert root.tag == _corens('repository')
version = root.attrib['version']
@@ -121,11 +130,14 @@ class GIRParser(object):
self._parse_include(node)
elif node.tag == _corens('package'):
self._parse_pkgconfig_package(node)
+ elif node.tag == _cns('include'):
+ self._parse_c_include(node)
ns = root.find(_corens('namespace'))
assert ns is not None
self._namespace = Namespace(ns.attrib['name'],
- ns.attrib['version'])
+ ns.attrib['version'],
+ ns.attrib.get(_cns('prefix')))
if 'shared-library' in ns.attrib:
self._shared_libraries.extend(
ns.attrib['shared-library'].split(','))
@@ -155,20 +167,42 @@ class GIRParser(object):
self._includes.add(include)
def _parse_pkgconfig_package(self, node):
- if not hasattr(self, '_pkgconfig_packages'):
- self._pkgconfig_packages = []
self._pkgconfig_packages.add(node.attrib['name'])
+ def _parse_c_include(self, node):
+ self._c_includes.add(node.attrib['name'])
+
def _parse_alias(self, node):
typeval = self._parse_type(node)
alias = Alias(node.attrib['name'],
typeval,
node.attrib.get(_cns('type')))
- self._add_node(alias)
+ self._namespace.append(alias)
+
+ def _parse_generic_attribs(self, node, obj):
+ assert isinstance(obj, Annotated)
+ doc = node.find(_corens('doc'))
+ if doc is not None:
+ obj.doc = doc.text
+ version = node.attrib.get('version')
+ if version:
+ obj.version = version
+ deprecated = node.attrib.get('deprecated')
+ if deprecated:
+ obj.deprecated = deprecated
+ introspectable = node.attrib.get('introspectable')
+ if introspectable:
+ obj.introspectable = int(introspectable) > 0
def _parse_object_interface(self, node):
+ parent = node.attrib.get('parent')
+ if parent:
+ parent_type = Type(target_giname=parent)
+ else:
+ parent_type = None
+
ctor_args = [node.attrib['name'],
- node.attrib.get('parent'),
+ parent_type,
node.attrib[_glibns('type-name')],
node.attrib[_glibns('get-type')]]
if node.tag == _corens('interface'):
@@ -182,38 +216,49 @@ class GIRParser(object):
raise AssertionError(node)
obj = klass(*ctor_args)
- self._add_node(obj)
+ self._parse_generic_attribs(node, obj)
+ type_struct = node.attrib.get(_glibns('type-struct'))
+ if type_struct:
+ obj.glib_type_struct = Type(target_giname=type_struct)
+ self._namespace.append(obj)
- if self._include_parsing:
- return
ctor_args.append(node.attrib.get(_cns('type')))
- for iface in node.findall(_corens('implements')):
- obj.interfaces.append(iface.attrib['name'])
- for iface in node.findall(_corens('prerequisites')):
- obj.prerequisities.append(iface.attrib['name'])
- for method in node.findall(_corens('method')):
+ for iface in self._find_children(node, _corens('implements')):
+ obj.interfaces.append(Type(target_giname=iface.attrib['name']))
+ for iface in self._find_children(node, _corens('prerequisite')):
+ obj.prerequisites.append(Type(target_giname=iface.attrib['name']))
+ for func_node in self._find_children(node, _corens('function')):
+ func = self._parse_function_common(func_node, Function)
+ obj.static_methods.append(func)
+ for method in self._find_children(node, _corens('method')):
func = self._parse_function_common(method, Function)
func.is_method = True
obj.methods.append(func)
- for ctor in node.findall(_corens('constructor')):
+ for method in self._find_children(node, _corens('virtual-method')):
+ func = self._parse_function_common(method, VFunction)
+ self._parse_generic_attribs(method, func)
+ func.is_method = True
+ func.invoker = method.get('invoker')
+ obj.virtual_methods.append(func)
+ for ctor in self._find_children(node, _corens('constructor')):
obj.constructors.append(
self._parse_function_common(ctor, Function))
- for callback in node.findall(_corens('callback')):
+ for callback in self._find_children(node, _corens('callback')):
obj.fields.append(self._parse_function_common(callback, Callback))
- for field in node.findall(_corens('field')):
+ for field in self._find_children(node, _corens('field')):
obj.fields.append(self._parse_field(field))
- for prop in node.findall(_corens('property')):
+ for prop in self._find_children(node, _corens('property')):
obj.properties.append(self._parse_property(prop))
- for signal in node.findall(_glibns('signal')):
+ for signal in self._find_children(node, _glibns('signal')):
obj.signals.append(self._parse_function_common(signal, Function))
def _parse_callback(self, node):
callback = self._parse_function_common(node, Callback)
- self._add_node(callback)
+ self._namespace.append(callback)
def _parse_function(self, node):
function = self._parse_function_common(node, Function)
- self._add_node(function)
+ self._namespace.append(function)
def _parse_function_common(self, node, klass):
name = node.attrib['name']
@@ -222,59 +267,82 @@ class GIRParser(object):
raise ValueError('node %r has no return-value' % (name, ))
transfer = returnnode.attrib.get('transfer-ownership')
retval = Return(self._parse_type(returnnode), transfer)
+ self._parse_generic_attribs(returnnode, retval)
parameters = []
+ throws = (node.attrib.get('throws') == '1')
+
if klass is Callback:
- func = klass(name, retval, parameters,
+ func = klass(name, retval, parameters, throws,
node.attrib.get(_cns('type')))
- else:
+ elif klass is Function:
identifier = node.attrib.get(_cns('identifier'))
- throws = (node.attrib.get('throws') == '1')
- func = klass(name, retval, parameters, identifier, throws)
-
- if self._include_parsing:
- return func
+ func = klass (name, retval, parameters, throws, identifier)
+ elif klass is VFunction:
+ func = klass(name, retval, parameters, throws)
+ else:
+ assert False
parameters_node = node.find(_corens('parameters'))
if (parameters_node is not None):
- for paramnode in parameters_node.findall(_corens('parameter')):
+ for paramnode in self._find_children(parameters_node, _corens('parameter')):
param = Parameter(paramnode.attrib.get('name'),
self._parse_type(paramnode),
- paramnode.attrib.get('direction'),
+ paramnode.attrib.get('direction') or PARAM_DIRECTION_IN,
paramnode.attrib.get('transfer-ownership'),
- paramnode.attrib.get('allow-none') == '1')
+ paramnode.attrib.get('allow-none') == '1',
+ paramnode.attrib.get('scope'),
+ paramnode.attrib.get('caller-allocates') == '1')
+ closure = paramnode.attrib.get('closure')
+ if closure:
+ param.closure_index = int(closure)
+ destroy = paramnode.attrib.get('destroy')
+ if destroy:
+ param.destroy_index = int(destroy)
+ self._parse_generic_attribs(paramnode, param)
parameters.append(param)
+ self._parse_generic_attribs(node, func)
+
return func
- def _parse_record(self, node):
+ def _parse_record(self, node, anonymous=False):
if _glibns('type-name') in node.attrib:
struct = GLibBoxedStruct(node.attrib['name'],
node.attrib[_glibns('type-name')],
node.attrib[_glibns('get-type')],
node.attrib.get(_cns('type')))
+ elif _glibns('is-gtype-struct-for') in node.attrib:
+ struct = GLibRecord(node.attrib['name'],
+ node.attrib.get(_cns('type')),
+ disguised=node.attrib.get('disguised') == '1')
+ is_gtype_struct_for = node.attrib[_glibns('is-gtype-struct-for')]
+ struct.is_gtype_struct_for = Type(target_giname=is_gtype_struct_for)
else:
- disguised = node.attrib.get('disguised') == '1'
- struct = Struct(node.attrib['name'],
+ struct = Record(node.attrib['name'],
node.attrib.get(_cns('type')),
- disguised=disguised)
- self._add_node(struct)
-
- if self._include_parsing:
- return
- for field in node.findall(_corens('field')):
- struct.fields.append(self._parse_field(field))
- for callback in node.findall(_corens('callback')):
- struct.fields.append(
- self._parse_function_common(callback, Callback))
- for method in node.findall(_corens('method')):
- struct.fields.append(
+ disguised=node.attrib.get('disguised') == '1')
+ if node.attrib.get('foreign') == '1':
+ struct.foreign = True
+ self._parse_generic_attribs(node, struct)
+ if not anonymous:
+ self._namespace.append(struct)
+
+ for field in self._find_children(node, _corens('field')):
+ fieldobj = self._parse_field(field)
+ struct.fields.append(fieldobj)
+ for method in self._find_children(node, _corens('method')):
+ struct.methods.append(
self._parse_function_common(method, Function))
- for ctor in node.findall(_corens('constructor')):
+ for func in self._find_children(node, _corens('function')):
+ struct.static_methods.append(
+ self._parse_function_common(func, Function))
+ for ctor in self._find_children(node, _corens('constructor')):
struct.constructors.append(
self._parse_function_common(ctor, Function))
+ return struct
- def _parse_union(self, node):
+ def _parse_union(self, node, anonymous=False):
if _glibns('type-name') in node.attrib:
union = GLibBoxedUnion(node.attrib['name'],
node.attrib[_glibns('type-name')],
@@ -283,50 +351,73 @@ class GIRParser(object):
else:
union = Union(node.attrib['name'],
node.attrib.get(_cns('type')))
- self._add_node(union)
+ if not anonymous:
+ self._namespace.append(union)
- if self._include_parsing:
- return
- for callback in node.findall(_corens('callback')):
+ for callback in self._find_children(node, _corens('callback')):
union.fields.append(
self._parse_function_common(callback, Callback))
- for field in node.findall(_corens('field')):
+ for field in self._find_children(node, _corens('field')):
union.fields.append(self._parse_field(field))
- for method in node.findall(_corens('method')):
+ for method in self._find_children(node, _corens('method')):
union.fields.append(
self._parse_function_common(method, Function))
- for ctor in node.findall(_corens('constructor')):
+ for ctor in self._find_children(node, _corens('constructor')):
union.constructors.append(
self._parse_function_common(ctor, Function))
+ return union
def _parse_type(self, node):
- typenode = node.find(_corens('type'))
+ # Fields can contain inline callbacks
+ typenode = node.find(_corens('callback'))
if typenode is not None:
- return Type(typenode.attrib['name'],
- typenode.attrib.get(_cns('type')))
+ return Type(target_giname=typenode.attrib['name'],
+ ctype=typenode.attrib.get(_cns('type')))
+ # Arrays have their own toplevel XML
typenode = node.find(_corens('array'))
if typenode is not None:
-
- array_type = typenode.attrib.get(_cns('type'))
- if array_type.startswith('GArray*') or \
- array_type.startswith('GPtrArray*') or \
- array_type.startswith('GByteArray*'):
- element_type = None
- else:
- element_type = self._parse_type(typenode)
-
- ret = Array(None, array_type, element_type)
+ array_type = typenode.attrib.get('name')
+ element_type = self._parse_type(typenode)
+ array_ctype = typenode.attrib.get(_cns('type'))
+ ret = Array(array_type, element_type, ctype=array_ctype)
+ # zero-terminated defaults to true...
+ zero = typenode.attrib.get('zero-terminated')
+ if zero and zero == '0':
+ ret.zeroterminated = False
+ fixed_size = typenode.attrib.get('fixed-size')
+ if fixed_size:
+ ret.size = int(fixed_size)
lenidx = typenode.attrib.get('length')
if lenidx:
ret.length_param_index = int(lenidx)
return ret
+ # Dispsense with varargs
typenode = node.find(_corens('varargs'))
if typenode is not None:
return Varargs()
+ # Okay now...could be a list, let's see.
+ typenode = self._find_first_child(node, _corens('type'))
+ if typenode is not None:
+ name = typenode.attrib.get('name')
+ ctype = typenode.attrib.get(_cns('type'))
+ if name is None:
+ if ctype is None:
+ return TypeUnknown()
+ return Type(ctype=ctype)
+ if name in ['GLib.List', 'GLib.SList']:
+ subchild = self._find_first_child(typenode, _corens('type'))
+ if subchild is not None:
+ element_type = self._parse_type(typenode)
+ else:
+ element_type = TYPE_ANY
+ return List(name, element_type, ctype=ctype)
+ else:
+ return self._namespace.type_from_name(name, ctype)
+
raise ValueError("Couldn't parse type of node %r; children=%r",
node, list(node))
@@ -334,74 +425,98 @@ class GIRParser(object):
obj = GLibBoxedOther(node.attrib[_glibns('name')],
node.attrib[_glibns('type-name')],
node.attrib[_glibns('get-type')])
- self._add_node(obj)
- if self._include_parsing:
- return
- for method in node.findall(_corens('method')):
+ self._parse_generic_attribs(node, obj)
+ self._namespace.append(obj)
+ for method in self._find_children(node, _corens('method')):
func = self._parse_function_common(method, Function)
func.is_method = True
obj.methods.append(func)
- for ctor in node.findall(_corens('constructor')):
+ for ctor in self._find_children(node, _corens('constructor')):
obj.constructors.append(
self._parse_function_common(ctor, Function))
- for callback in node.findall(_corens('callback')):
+ for callback in self._find_children(node, _corens('callback')):
obj.fields.append(
self._parse_function_common(callback, Callback))
def _parse_field(self, node):
- type_node = self._parse_type(node)
- return Field(node.attrib['name'],
- type_node,
- type_node.ctype,
- node.attrib.get('readable') != '0',
- node.attrib.get('writable') == '1',
- node.attrib.get('bits'))
+ anonymous_node = None
+ type_node = None
+ for name in ('callback', 'record', 'union'):
+ anonymous_elt = self._find_first_child(node, _corens(name))
+ if anonymous_elt:
+ break
+ if anonymous_elt:
+ if anonymous_elt.tag == _corens('callback'):
+ anonymous_node = self._parse_function_common(anonymous_elt, Callback)
+ elif anonymous_elt.tag == _corens('record'):
+ anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
+ elif anonymous_elt.tag == _corens('union'):
+ anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
+ else:
+ assert False, anonymous_elt.tag
+ else:
+ type_node = self._parse_type(node)
+ field = Field(node.attrib['name'],
+ type_node,
+ node.attrib.get('readable') != '0',
+ node.attrib.get('writable') == '1',
+ node.attrib.get('bits'),
+ anonymous_node=anonymous_node)
+ self._parse_generic_attribs(node, field)
+ return field
def _parse_property(self, node):
- type_node = self._parse_type(node)
- return Property(node.attrib['name'],
- type_node.name,
+ prop = Property(node.attrib['name'],
+ self._parse_type(node),
node.attrib.get('readable') != '0',
node.attrib.get('writable') == '1',
node.attrib.get('construct') == '1',
- node.attrib.get('construct-only') == '1',
- type_node.ctype)
+ node.attrib.get('construct-only') == '1')
+ self._parse_generic_attribs(node, prop)
+ return prop
def _parse_member(self, node):
- return GLibEnumMember(node.attrib['name'],
- node.attrib['value'],
- node.attrib.get(_cns('identifier')),
- node.attrib.get(_glibns('nick')))
+ member = GLibEnumMember(node.attrib['name'],
+ node.attrib['value'],
+ node.attrib.get(_cns('identifier')),
+ node.attrib.get(_glibns('nick')))
+ self._parse_generic_attribs(node, member)
+ return member
def _parse_constant(self, node):
type_node = self._parse_type(node)
constant = Constant(node.attrib['name'],
- type_node.name,
+ type_node,
node.attrib['value'])
- self._add_node(constant)
+ self._parse_generic_attribs(node, constant)
+ self._namespace.append(constant)
def _parse_enumeration_bitfield(self, node):
name = node.attrib.get('name')
ctype = node.attrib.get(_cns('type'))
get_type = node.attrib.get(_glibns('get-type'))
type_name = node.attrib.get(_glibns('type-name'))
- if get_type:
+ glib_error_quark = node.attrib.get(_glibns('error-quark'))
+ if get_type or glib_error_quark:
if node.tag == _corens('bitfield'):
klass = GLibFlags
else:
klass = GLibEnum
else:
- klass = Enum
+ if node.tag == _corens('bitfield'):
+ klass = Bitfield
+ else:
+ klass = Enum
type_name = ctype
members = []
- if klass is Enum:
+ if klass in (Enum, Bitfield):
obj = klass(name, type_name, members)
else:
obj = klass(name, type_name, members, get_type)
+ obj.error_quark = glib_error_quark
obj.ctype = ctype
- self._add_node(obj)
+ self._parse_generic_attribs(node, obj)
+ self._namespace.append(obj)
- if self._include_parsing:
- return
- for member in node.findall(_corens('member')):
+ for member in self._find_children(node, _corens('member')):
members.append(self._parse_member(member))
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index fd0b99f..500b489 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,8 +22,8 @@
from __future__ import with_statement
from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
- Function, Interface, List, Map, Member, Struct, Union,
- Varargs, Type, TYPE_ANY)
+ Function, Interface, List, Map, Member, Record, Union,
+ Varargs, Type)
from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
GLibFlags, GLibObject, GLibInterface,
GLibRecord)
@@ -35,17 +35,17 @@ COMPATIBLE_GIR_VERSION = '1.1'
class GIRWriter(XMLWriter):
- def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
+ def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
super(GIRWriter, self).__init__()
self.write_comment(
'''This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. ''')
self._write_repository(namespace, shlibs, includes, pkgs,
- c_includes, cprefix)
+ c_includes)
def _write_repository(self, namespace, shlibs, includes=None,
- packages=None, c_includes=None, cprefix=None):
+ packages=None, c_includes=None):
if includes is None:
includes = frozenset()
if packages is None:
@@ -65,7 +65,9 @@ and/or use gtk-doc annotations. ''')
self._write_pkgconfig_pkg(pkg)
for c_include in sorted(set(c_includes)):
self._write_c_include(c_include)
- self._write_namespace(namespace, shlibs, cprefix)
+ self._namespace = namespace
+ self._write_namespace(namespace, shlibs)
+ self._namespace = None
def _write_include(self, include):
attrs = [('name', include.name), ('version', include.version)]
@@ -79,11 +81,11 @@ and/or use gtk-doc annotations. ''')
attrs = [('name', c_include)]
self.write_tag('c:include', attrs)
- def _write_namespace(self, namespace, shlibs, cprefix):
+ def _write_namespace(self, namespace, shlibs):
attrs = [('name', namespace.name),
('version', namespace.version),
('shared-library', ','.join(shlibs)),
- ('c:prefix', cprefix)]
+ ('c:prefix', namespace.c_prefix)]
with self.tagcontext('namespace', attrs):
# We define a custom sorting function here because
# we want aliases to be first. They're a bit
@@ -98,7 +100,7 @@ and/or use gtk-doc annotations. ''')
return 1
else:
return cmp(a, b)
- for node in sorted(namespace.nodes, cmp=nscmp):
+ for node in sorted(namespace.itervalues(), cmp=nscmp):
self._write_node(node)
def _write_node(self, node):
@@ -112,7 +114,7 @@ and/or use gtk-doc annotations. ''')
self._write_class(node)
elif isinstance(node, Callback):
self._write_callback(node)
- elif isinstance(node, Struct):
+ elif isinstance(node, Record):
self._write_record(node)
elif isinstance(node, Union):
self._write_union(node)
@@ -171,7 +173,9 @@ and/or use gtk-doc annotations. ''')
self._write_parameters(callable.parameters)
def _write_function(self, func, tag_name='function'):
- attrs = [('c:identifier', func.symbol)]
+ attrs = []
+ if hasattr(func, 'symbol'):
+ attrs.append(('c:identifier', func.symbol))
self._write_callable(func, tag_name, attrs)
def _write_method(self, method):
@@ -187,10 +191,9 @@ and/or use gtk-doc annotations. ''')
if not return_:
return
- assert return_.transfer is not None, return_
-
attrs = []
- attrs.append(('transfer-ownership', return_.transfer))
+ if return_.transfer:
+ attrs.append(('transfer-ownership', return_.transfer))
with self.tagcontext('return-value', attrs):
self._write_generic(return_)
self._write_type(return_.type)
@@ -203,17 +206,16 @@ and/or use gtk-doc annotations. ''')
self._write_parameter(parameter)
def _write_parameter(self, parameter):
- assert parameter.transfer is not None, parameter
-
attrs = []
- if parameter.name is not None:
- attrs.append(('name', parameter.name))
- if parameter.direction != 'in':
+ if parameter.argname is not None:
+ attrs.append(('name', parameter.argname))
+ if (parameter.direction is not None) and (parameter.direction != 'in'):
attrs.append(('direction', parameter.direction))
attrs.append(('caller-allocates',
'1' if parameter.caller_allocates else '0'))
- attrs.append(('transfer-ownership',
- parameter.transfer))
+ if parameter.transfer:
+ attrs.append(('transfer-ownership',
+ parameter.transfer))
if parameter.allow_none:
attrs.append(('allow-none', '1'))
if parameter.scope:
@@ -226,62 +228,56 @@ and/or use gtk-doc annotations. ''')
self._write_generic(parameter)
self._write_type(parameter.type)
- def _type_to_string(self, ntype):
- if isinstance(ntype, basestring):
- return ntype
- return ntype.name
+ def _type_to_name(self, typeval):
+ if not typeval.resolved:
+ raise AssertionError("Caught unresolved type %r (ctype=%r)" % (typeval, typeval.ctype))
+ assert typeval.target_giname is not None
+ prefix = self._namespace.name + '.'
+ if typeval.target_giname.startswith(prefix):
+ return typeval.target_giname[len(prefix):]
+ return typeval.target_giname
def _write_type(self, ntype, relation=None):
- if isinstance(ntype, basestring):
- typename = ntype
- type_cname = None
- else:
- typename = ntype.name
- type_cname = ntype.ctype
+ assert isinstance(ntype, Type), ntype
+ attrs = []
+ if ntype.ctype:
+ attrs.append(('c:type', ntype.ctype))
if isinstance(ntype, Varargs):
with self.tagcontext('varargs', []):
pass
- return
- if isinstance(ntype, Array):
- attrs = []
+ elif isinstance(ntype, Array):
+ if ntype.array_type != Array.C:
+ attrs.insert(0, ('name', ntype.array_type))
if not ntype.zeroterminated:
- attrs.append(('zero-terminated', '0'))
+ attrs.insert(0, ('zero-terminated', '0'))
if ntype.length_param_index >= 0:
attrs.append(('length', '%d' % (ntype.length_param_index, )))
- if ntype.name in ['GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray']:
- attrs.append(('name', ntype.name))
- attrs.append(('c:type', ntype.ctype))
if ntype.size is not None:
- attrs.append(('fixed-size', ntype.size))
+ attrs.append(('fixed-size', '%d' % (ntype.size, )))
with self.tagcontext('array', attrs):
- if ntype.element_type is not None:
- self._write_type(ntype.element_type)
- else:
- self._write_type(Type(TYPE_ANY, ctype='gpointer'))
- return
- attrs = [('name', self._type_to_string(ntype))]
- # FIXME: figure out if type references a basic type
- # or a boxed/class/interface etc. and skip
- # writing the ctype if the latter.
- if type_cname is not None:
- attrs.append(('c:type', type_cname))
- if (isinstance(ntype, List)
- or typename in ('GLib.List',
- 'GLib.SList')):
+ self._write_type(ntype.element_type)
+ elif isinstance(ntype, List):
+ if ntype.name:
+ attrs.insert(0, ('name', ntype.name))
with self.tagcontext('type', attrs):
- if isinstance(ntype, List) and ntype.element_type:
- self._write_type(ntype.element_type)
- else:
- self._write_type(Type(TYPE_ANY, ctype='gpointer'))
- return
- if isinstance(ntype, Map) and ntype.key_type:
+ self._write_type(ntype.element_type)
+ elif isinstance(ntype, Map):
+ if ntype.name:
+ attrs.insert(0, ('name', name))
with self.tagcontext('type', attrs):
self._write_type(ntype.key_type)
self._write_type(ntype.value_type)
- return
- # Not a special type, just write it out
- self.write_tag('type', attrs)
+ else:
+ # REWRITEFIXME - enable this for 1.2
+ if ntype.target_giname:
+ attrs.insert(0, ('name', self._type_to_name(ntype)))
+ elif ntype.target_fundamental:
+ # attrs = [('fundamental', ntype.target_fundamental)]
+ attrs.insert(0, ('name', ntype.target_fundamental))
+ elif ntype.target_foreign:
+ attrs.insert(0, ('foreign', '1'))
+ self.write_tag('type', attrs)
def _write_enum(self, enum):
attrs = [('name', enum.name)]
@@ -325,10 +321,9 @@ and/or use gtk-doc annotations. ''')
self.write_tag('member', attrs)
def _write_constant(self, constant):
- attrs = [('name', constant.name),
- ('value', str(constant.value))]
+ attrs = [('name', constant.name), ('value', constant.value)]
with self.tagcontext('constant', attrs):
- self._write_type(constant.type)
+ self._write_type(constant.value_type)
def _write_class(self, node):
attrs = [('name', node.name),
@@ -338,7 +333,8 @@ and/or use gtk-doc annotations. ''')
if isinstance(node, Class):
tag_name = 'class'
if node.parent is not None:
- attrs.append(('parent', node.parent))
+ attrs.append(('parent',
+ self._type_to_name(node.parent)))
if node.is_abstract:
attrs.append(('abstract', '1'))
else:
@@ -348,7 +344,8 @@ and/or use gtk-doc annotations. ''')
if node.get_type:
attrs.append(('glib:get-type', node.get_type))
if node.glib_type_struct:
- attrs.append(('glib:type-struct', node.glib_type_struct.name))
+ attrs.append(('glib:type-struct',
+ self._type_to_name(node.glib_type_struct)))
if isinstance(node, GLibObject):
if node.fundamental:
attrs.append(('glib:fundamental', '1'))
@@ -364,10 +361,12 @@ and/or use gtk-doc annotations. ''')
self._write_generic(node)
if isinstance(node, GLibObject):
for iface in node.interfaces:
- self.write_tag('implements', [('name', iface)])
+ self.write_tag('implements',
+ [('name', self._type_to_name(iface))])
if isinstance(node, Interface):
for iface in node.prerequisites:
- self.write_tag('prerequisite', [('name', iface)])
+ self.write_tag('prerequisite',
+ [('name', self._type_to_name(iface))])
if isinstance(node, Class):
for method in node.constructors:
self._write_constructor(method)
@@ -394,6 +393,8 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in boxed.methods:
self._write_method(method)
+ for method in boxed.static_methods:
+ self._write_static_method(method)
def _write_property(self, prop):
attrs = [('name', prop.name)]
@@ -408,7 +409,8 @@ and/or use gtk-doc annotations. ''')
attrs.append(('construct', '1'))
if prop.construct_only:
attrs.append(('construct-only', '1'))
- attrs.append(('transfer-ownership', prop.transfer))
+ if prop.transfer:
+ attrs.append(('transfer-ownership', prop.transfer))
with self.tagcontext('property', attrs):
self._write_generic(prop)
self._write_type(prop.type)
@@ -416,7 +418,7 @@ and/or use gtk-doc annotations. ''')
def _write_vfunc(self, vf):
attrs = []
if vf.invoker:
- attrs.append(('invoker', vf.invoker.name))
+ attrs.append(('invoker', vf.invoker))
self._write_callable(vf, 'virtual-method', attrs)
def _write_callback(self, callback):
@@ -442,7 +444,7 @@ and/or use gtk-doc annotations. ''')
if record.is_gtype_struct_for:
is_gtype_struct = True
attrs.append(('glib:is-gtype-struct-for',
- record.is_gtype_struct_for))
+ self._type_to_name(record.is_gtype_struct_for)))
self._append_version(record, attrs)
self._append_node_generic(record, attrs)
if isinstance(record, GLibBoxed):
@@ -456,6 +458,8 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in record.methods:
self._write_method(method)
+ for method in record.static_methods:
+ self._write_static_method(method)
def _write_union(self, union):
attrs = []
@@ -476,25 +480,23 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in union.methods:
self._write_method(method)
+ for method in union.static_methods:
+ self._write_static_method(method)
def _write_field(self, field, is_gtype_struct=False):
- if isinstance(field, Function):
- self._write_method(field)
- return
-
- if isinstance(field, Callback):
+ if field.anonymous_node:
attrs = [('name', field.name)]
with self.tagcontext('field', attrs):
self._write_generic(field)
- if is_gtype_struct:
- self._write_callback(field)
+ if isinstance(field.anonymous_node, Callback):
+ self._write_callback(field.anonymous_node)
+ elif isinstance(field.anonymous_node, Record):
+ self._write_record(field.anonymous_node)
+ elif isinstance(field.anonymous_node, Union):
+ self._write_union(field.anonymous_node)
else:
- attrs = [('name', TYPE_ANY), ('c:type', 'pointer')]
- self.write_tag('type', attrs)
- elif isinstance(field, Struct):
- self._write_record(field)
- elif isinstance(field, Union):
- self._write_union(field)
+ raise AssertionError("Unknown field anonymous: %r" \
+ % (field.anonymous_node, ))
else:
attrs = [('name', field.name)]
# Fields are assumed to be read-only
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index ad87926..69f1c0b 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -19,7 +19,7 @@
#
from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
- Property, Union, Record)
+ Property, Union, Record, Annotated)
class GLibRecord(Record):
def __init__(self, *args, **kwargs):
@@ -118,9 +118,18 @@ class GLibBoxedOther(Node, GLibBoxed):
GLibBoxed.__init__(self, type_name, get_type)
self.constructors = []
self.methods = []
+ self.static_methods = []
self.ctype = type_name
self.doc = None
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+
class GLibInterface(Interface):
@@ -137,10 +146,10 @@ class GLibProperty(Property):
pass
-class GLibSignal(Node):
+class GLibSignal(Annotated):
def __init__(self, name, retval):
- Node.__init__(self, name)
+ Annotated.__init__(self)
+ self.name = name
self.retval = retval
self.parameters = []
- self.doc = None
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index a8fdcc3..42fc6ef 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,11 +28,9 @@ import subprocess
from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
Function, Interface, Member, Namespace, Node, Parameter,
Property, Record, Return, Type, TypeContainer, Union,
- Field, VFunction, type_name_from_ctype, default_array_types,
- TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
- TYPE_LONG_LONG, TYPE_LONG_DOUBLE,
+ Class, Field, VFunction, default_array_types,
+ TYPE_ANY, TYPE_GTYPE, TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
Map, Varargs, type_names)
-from .transformer import Names
from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
GLibBoxedUnion, GLibBoxedOther, GLibRecord)
@@ -50,30 +48,6 @@ G_PARAM_STATIC_NAME = 1 << 5
G_PARAM_STATIC_NICK = 1 << 6
G_PARAM_STATIC_BLURB = 1 << 7
-SYMBOL_BLACKLIST = [
- # These ones break GError conventions
- 'g_simple_async_result_new_from_error',
- 'g_simple_async_result_set_from_error',
- 'g_simple_async_result_propagate_error',
- 'g_simple_async_result_report_error_in_idle',
- 'gtk_print_operation_get_error',
-]
-
-SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
- [r'\w+_marshal_[A-Z]+__', ]]
-
-GET_TYPE_OVERRIDES = {
- # this is a special case, from glibtransforer.py:create_gobject
- 'intern': 'g_object_get_type',
- # this is presumably a typo, should be fixed upstream
- 'g_gstring_get_type': 'g_string_get_type',
- # this is historical cruft: there's a deprecated
- # #define gdk_window_get_type gdk_window_get_window_type
- # upstream; this method can be renamed properly upstream once
- # that deprecated alias is removed (in some future release)
- 'gdk_window_object_get_type': 'gdk_window_get_type',
-}
-
class IntrospectionBinary(object):
@@ -97,20 +71,15 @@ class UnknownTypeError(Exception):
class GLibTransformer(object):
- def __init__(self, transformer, noclosure=False):
+ def __init__(self, transformer):
self._transformer = transformer
- self._noclosure = noclosure
- self._namespace_name = None
- self._names = Names()
+ self._namespace = transformer.namespace
self._uscore_type_names = {}
self._binary = None
self._get_type_functions = []
- self._error_quark_functions = []
self._gtype_data = {}
- self._failed_types = {}
self._boxed_types = {}
self._private_internal_types = {}
- self._validating = False
# Public API
@@ -121,18 +90,21 @@ class GLibTransformer(object):
"""
- namespace = self._transformer.parse()
- self._namespace_name = namespace.name
- self._namespace_version = namespace.version
+ self._transformer.parse()
# First pass: parsing
- for node in namespace.nodes:
- self._parse_node(node)
+ for node in self._namespace.itervalues():
+ if isinstance(node, Function):
+ self._initparse_function(node)
+ if self._namespace.name == 'GObject':
+ for node in self._namespace.itervalues():
+ if isinstance(node, Record):
+ self._initparse_gobject_record(node)
# We don't want an alias for this - it's handled specially in
# the typelib compiler.
- if namespace.name == 'GObject':
- del self._names.aliases['Type']
+ if self._namespace.name == 'GObject':
+ del self._namespace.aliases['Type']
def get_get_type_functions(self):
return self._get_type_functions
@@ -144,148 +116,28 @@ class GLibTransformer(object):
"""Do remaining parsing steps requiring introspection binary"""
# Get all the GObject data by passing our list of get_type
- # functions to the compiled binary
-
- self._execute_binary()
-
- # Introspection is done from within parsing
+ # functions to the compiled binary, returning an XML blob.
+ tree = self._execute_binary_get_tree()
+ root = tree.getroot()
+ for child in root:
+ self._gtype_data[child.attrib['name']] = child
+ for child in root:
+ self._introspect_type(child)
- # Second pass: pair boxed structures
- for boxed in self._boxed_types.itervalues():
- self._pair_boxed_type(boxed)
- # Third pass: delete class structures, resolve
- # all types we now know about
- nodes = list(self._names.names.itervalues())
- for (ns, node) in nodes:
- try:
- self._resolve_node(node)
- except KeyError, e:
- self._transformer.log_node_warning(node,
-"""Unresolvable entry %r""" % (e, ))
- self._remove_attribute(node.name)
- # Another pass, since we need to have the methods parsed
- # in order to correctly modify them after class/record
- # pairing
- for (ns, node) in nodes:
- # associate GtkButtonClass with GtkButton
- if isinstance(node, Record):
+ # Pair up boxed structures and class structures
+ for node in self._namespace.itervalues():
+ if isinstance(node, GLibBoxed):
+ self._pair_boxed_type(node)
+ elif isinstance(node, Record):
self._pair_class_record(node)
- for (ns, alias) in self._names.aliases.itervalues():
- self._resolve_alias(alias)
-
- self._resolve_quarks()
-
- # Our final pass replacing types
- self._resolve_types(nodes)
-
- # Create a new namespace with what we found
- namespace = Namespace(self._namespace_name, self._namespace_version)
- namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
- for (ns, x) in self._names.names.itervalues():
- namespace.nodes.append(x)
- return namespace
-
- # Private
-
- def _add_attribute(self, node, replace=False):
- node_name = node.name
- if (not replace) and node_name in self._names.names:
- return
- self._names.names[node_name] = (None, node)
-
- def _remove_attribute(self, name):
- del self._names.names[name]
-
- def _get_attribute(self, name):
- node = self._names.names.get(name)
- if node:
- return node[1]
- return None
-
- def _lookup_node(self, name):
- if name in type_names:
- return None
- node = self._get_attribute(name)
- if node is None:
- node = self._transformer.get_names().names.get(name)
- if node:
- return node[1]
- return node
-
- def _get_no_uscore_prefixed_name(self, type_name):
- # Besides the straight underscore conversion, we also try
- # removing the underscores from the namespace as a possible C
- # mapping; e.g. it's webkit_web_view, not web_kit_web_view
- suffix = self._transformer.remove_prefix(type_name)
- prefix = type_name[:-len(suffix)]
- return (prefix + '_' + to_underscores(suffix)).lower()
-
- def _register_internal_type(self, type_name, node):
- self._names.type_names[type_name] = (None, node)
- uscored = to_underscores(type_name).lower()
- # prefer the prefix of the get_type method, if there is one
- if hasattr(node, 'get_type'):
- uscored = GET_TYPE_OVERRIDES.get(node.get_type, node.get_type)
- uscored = uscored[:-len('_get_type')]
- self._uscore_type_names[uscored] = node
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- # since this is a guess, don't overwrite any 'real' prefix
- if no_uscore_prefixed not in self._uscore_type_names:
- self._uscore_type_names[no_uscore_prefixed] = node
-
- def _resolve_quarks(self):
- # self._uscore_type_names is an authoritative mapping of types
- # to underscored versions, since it is based on get_type() methods;
- # but only covers enums that are registered as GObject enums.
- # Create a fallback mapping based on all known enums in this module.
- uscore_enums = {}
- for enum in self._transformer.iter_enums():
- type_name = enum.symbol
- uscored = to_underscores(type_name).lower()
-
- uscore_enums[uscored] = enum
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- if no_uscore_prefixed not in uscore_enums:
- uscore_enums[no_uscore_prefixed] = enum
-
- for node in self._error_quark_functions:
- short = node.symbol[:-len('_quark')]
- if short == "g_io_error":
- # Special case; GIOError was already taken forcing GIOErrorEnum
- enum = self._names.type_names["GIOErrorEnum"][1]
- else:
- enum = self._uscore_type_names.get(short)
- if enum is None:
- enum = uscore_enums.get(short)
- if enum is not None:
- enum.error_quark = node.symbol
- else:
- self._transformer.log_node_warning(node,
-"""Couldn't find corresponding enumeration""")
# Helper functions
- def _resolve_gtypename(self, gtype_name):
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- return Unresolved(gtype_name)
-
- def _resolve_gtypename_chain(self, gtype_names):
- """Like _resolve_gtypename, but grab the first one that resolves.
- If none of them do, return an Unresolved for the first."""
- for gtype_name in gtype_names:
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- continue
- return Unresolved(gtype_names[0])
+ def _execute_binary_get_tree(self):
+ """Load the library (or executable), returning an XML
+blob containing data gleaned from GObject's primitive introspection."""
+ from xml.etree.cElementTree import parse
- def _execute_binary(self):
in_path = os.path.join(self._binary.tmpdir, 'types.txt')
f = open(in_path, 'w')
# TODO: Introspect GQuark functions
@@ -301,22 +153,14 @@ class GLibTransformer(object):
# Invoke the binary, having written our get_type functions to types.txt
try:
- subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
- except subprocess.CalledProcessError, e:
- raise SystemExit(e)
- self._read_introspect_dump(out_path)
-
- # Clean up temporaries
- shutil.rmtree(self._binary.tmpdir)
-
- def _read_introspect_dump(self, xmlpath):
- from xml.etree.cElementTree import parse
- tree = parse(xmlpath)
- root = tree.getroot()
- for child in root:
- self._gtype_data[child.attrib['name']] = child
- for child in root:
- self._introspect_type(child)
+ try:
+ subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
+ except subprocess.CalledProcessError, e:
+ # Clean up temporaries
+ raise SystemExit(e)
+ return parse(out_path)
+ finally:
+ shutil.rmtree(self._binary.tmpdir)
def _create_gobject(self, node):
type_name = 'G' + node.name
@@ -324,8 +168,7 @@ class GLibTransformer(object):
parent_gitype = None
symbol = 'intern'
elif type_name == 'GInitiallyUnowned':
- parent_type_name = 'GObject'
- parent_gitype = self._resolve_gtypename(parent_type_name)
+ parent_gitype = Type(target_giname='GLib.Object')
symbol = 'g_initially_unowned_get_type'
else:
assert False
@@ -340,345 +183,50 @@ class GLibTransformer(object):
# what we do here is copy all of the GObject fields into
# GInitiallyUnowned so that struct offset computation
# works correctly.
- gnode.fields = self._names.names['Object'][1].fields
- self._add_attribute(gnode)
- self._register_internal_type(type_name, gnode)
+ gnode.fields = self._namespace.get('Object').fields
+ self._namespace.append(gnode, replace=True)
# Parser
- def _parse_node(self, node):
- if isinstance(node, Enum):
- self._parse_enum(node)
- elif isinstance(node, Bitfield):
- self._parse_bitfield(node)
- elif isinstance(node, Function):
- self._parse_function(node)
- elif isinstance(node, Record):
- self._parse_record(node)
- elif isinstance(node, Callback):
- self._parse_callback(node)
- elif isinstance(node, Alias):
- self._parse_alias(node)
- elif isinstance(node, Member):
- # FIXME: atk_misc_instance singletons
- pass
- elif isinstance(node, Union):
- self._parse_union(node)
- elif isinstance(node, Constant):
- self._parse_constant(node)
- else:
- print 'GLIB Transformer: Unhandled node:', node
-
- def _parse_alias(self, alias):
- self._names.aliases[alias.name] = (None, alias)
-
- def _parse_enum(self, enum):
- self._add_attribute(enum)
-
- def _parse_bitfield(self, enum):
- self._add_attribute(enum)
-
- def _parse_constant(self, constant):
- self._add_attribute(constant)
-
- def _parse_function(self, func):
- if func.symbol in SYMBOL_BLACKLIST:
- return
- if func.symbol.startswith('_'):
- return
- for regexp in SYMBOL_BLACKLIST_RE:
- if regexp.match(func.symbol):
- return
- if self._parse_get_type_function(func):
- return
- if self._parse_error_quark_function(func):
+ def _initparse_function(self, func):
+ symbol = func.symbol
+ if symbol.startswith('_'):
return
+ elif symbol.endswith('_get_type'):
+ self._initparse_get_type_function(func)
- self._add_attribute(func)
-
- def _parse_get_type_function(self, func):
- symbol = func.symbol
- if not symbol.endswith('_get_type'):
- return False
- if self._namespace_name == 'GLib':
+ def _initparse_get_type_function(self, func):
+ if self._namespace.name == 'GLib':
# No GObjects in GLib
return False
- if (self._namespace_name == 'GObject' and
- symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
+ if (self._namespace.name == 'GObject' and
+ func.symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
# We handle these internally, see _create_gobject
return True
if func.parameters:
return False
# GType *_get_type(void)
- if func.retval.type.name not in ['Type',
- 'GType',
- 'GObject.Type',
- 'Gtk.Type']:
- self._transformer.log_("Warning: *_get_type function returns '%r'"
- ", not GObject.Type") % (func.retval.type.name, )
- return False
-
- self._get_type_functions.append(symbol)
- return True
-
- def _parse_error_quark_function(self, func):
- if not func.symbol.endswith('_error_quark'):
- return False
- if func.parameters:
- return False
- if (func.retval.type.name != 'GLib.Quark' and
- func.retval.type.ctype != 'GQuark'):
+ rettype = func.retval.type
+ if not (rettype.is_equiv(TYPE_GTYPE)
+ or rettype.target_giname == 'Gtk.Type'):
+ self._transformer.log_warning("function returns '%r', not a GType") % (func.retval.type, )
return False
- self._error_quark_functions.append(func)
+ self._get_type_functions.append(func.symbol)
return True
- def _name_is_internal_gtype(self, giname):
- try:
- node = self._get_attribute(giname)
- return isinstance(node, (GLibObject, GLibInterface,
- GLibBoxed, GLibEnum, GLibFlags))
- except KeyError, e:
- return False
-
- def _parse_static_method(self, func):
- components = func.symbol.split('_')
- if len(components) < 2:
- return None
- target_klass = None
- prefix_components = None
- methname = None
- for i in xrange(1, len(components)):
- prefix_components = '_'.join(components[0:-i])
- methname = '_'.join(components[-i:])
- target_klass = self._uscore_type_names.get(prefix_components)
- if target_klass and isinstance(target_klass, GLibObject):
- break
- target_klass = None
- if not target_klass:
- return None
- 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):
- if not func.parameters:
- return False
- return self._parse_method_common(func, True)
-
- def _parse_constructor(self, func):
- return self._parse_method_common(func, False)
-
- def _parse_method_common(self, func, is_method):
- # Skip _get_type functions, we processed them
- # already
- if func.symbol.endswith('_get_type'):
- return None
-
- if not is_method:
- target_arg = func.retval
- else:
- target_arg = func.parameters[0]
-
- if is_method:
- # Methods require their first arg to be a known class
- # Look at the original C type (before namespace stripping), without
- # pointers: GtkButton -> gtk_button_, so we can figure out the
- # method name
- argtype = target_arg.type.ctype.replace('*', '')
- name = self._transformer.remove_prefix(argtype)
- name_uscore = to_underscores_noprefix(name).lower()
- # prefer the prefix of the _get_type method, if there is one
- if argtype in self._names.type_names:
- node = self._names.type_names[argtype][1]
- if hasattr(node, 'get_type'):
- name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
- node.get_type)
- name_uscore = name_uscore[:-len('_get_type')]
- name_offset = func.symbol.find(name_uscore + '_')
- if name_offset < 0:
- return None
- prefix = func.symbol[:name_offset+len(name_uscore)]
- else:
- # Constructors must have _new
- # Take everything before that as class name
- new_idx = func.symbol.find('_new')
- if new_idx < 0:
- return None
- # Constructors don't return basic types
- derefed = self._transformer.follow_aliases(target_arg.type.name,
- self._names)
- if derefed in type_names:
- #print "NOTE: Rejecting constructor returning basic: %r" \
- # % (func.symbol, )
- return None
- prefix = func.symbol[:new_idx]
-
- klass = self._uscore_type_names.get(prefix)
- if klass is None:
- #print "NOTE: No valid matching class for likely "+\
- # "method or constructor: %r" % (func.symbol, )
- return None
- # Enums can't have ctors or methods
- if isinstance(klass, (GLibEnum, GLibFlags)):
- return None
-
- # The _uscore_type_names member holds the plain GLibBoxed
- # object; we want to actually use the struct/record associated
- if isinstance(klass, (Record, Union)):
- remove_prefix = klass.symbol
- else:
- remove_prefix = klass.type_name
-
- name = self._transformer.remove_prefix(remove_prefix)
- klass = self._get_attribute(name)
- if klass is None:
- return
-
- if not is_method:
- # Interfaces can't have constructors, punt to global scope
- if isinstance(klass, GLibInterface):
- #print "NOTE: Rejecting constructor for"+\
- # " interface type: %r" % (func.symbol, )
- return None
- # TODO - check that the return type is a subclass of the
- # class from the prefix
- # But for now, ensure that constructor returns are always
- # the most concrete class
- name = self._transformer.remove_prefix(remove_prefix)
- func.retval.type = Type(name, func.retval.type.ctype)
-
- self._remove_attribute(func.name)
- # Strip namespace and object prefix: gtk_window_new -> new
- func.name = func.symbol[len(prefix)+1:]
- if is_method:
- # 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
-
- def _parse_record(self, record):
- # This is a hack, but GObject is a rather fundamental piece so.
- internal_names = ["Object", 'InitiallyUnowned']
- g_internal_names = ["G" + x for x in internal_names]
- if (self._namespace_name == 'GObject' and
- record.name in internal_names):
+ def _initparse_gobject_record(self, record):
+ # Special handling for when we're parsing GObject
+ internal_names = ("Object", 'InitiallyUnowned')
+ if record.name in internal_names:
self._create_gobject(record)
return
- elif record.name in g_internal_names:
- # Avoid duplicates
- return
if record.name == 'InitiallyUnownedClass':
- record.fields = self._names.names['ObjectClass'][1].fields
- node = self._names.names.get(record.name)
- if node is None:
- self._add_attribute(record, replace=True)
- self._register_internal_type(record.symbol, record)
- return
- (ns, node) = node
- node.fields = record.fields[:]
-
- def _parse_union(self, union):
- node = self._names.names.get(union.name)
- if node is None:
- self._add_attribute(union, replace=True)
- self._register_internal_type(union.symbol, union)
- return
- (ns, node) = node
- node.fields = union.fields[:]
+ record.fields = self._namespace.get('ObjectClass').fields
+ self._namespace.append(record, replace=True)
- def _parse_callback(self, callback):
- self._add_attribute(callback)
-
- def _strip_class_suffix(self, name):
- if (name.endswith('Class') or
- name.endswith('Iface')):
- return name[:-5]
- elif name.endswith('Interface'):
- return name[:-9]
- else:
- return name
-
- def _arg_is_failed(self, param):
- ctype = self._transformer.ctype_of(param).replace('*', '')
- uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
- if uscored in self._failed_types:
- print "Warning: failed type: %r" % (param, )
- return True
- return False
-
- def _pair_class_record(self, maybe_class):
- name = self._strip_class_suffix(maybe_class.name)
- if name == maybe_class.name:
- return
-
- class_struct = maybe_class
- if self._arg_is_failed(class_struct):
- print "WARNING: deleting no-type %r" % (class_struct.name, )
- del self._names.names[class_struct.name]
- return
-
- pair_class = self._get_attribute(name)
- if (not pair_class or
- not isinstance(pair_class, (GLibObject, GLibInterface))):
- return
-
- # Object class fields are assumed to be read-only
- # (see also _introspect_object and transformer.py)
- for field in maybe_class.fields:
- if isinstance(field, Field):
- field.writable = False
-
- # Loop through fields to determine which are virtual
- # functions and which are signal slots by
- # assuming everything that doesn't share a name
- # with a known signal is a virtual slot.
- for field in maybe_class.fields:
- if not isinstance(field, Callback):
- continue
- # Check the first parameter is the object
- if len(field.parameters) == 0:
- continue
- firstparam_type = field.parameters[0].type
- if firstparam_type != pair_class:
- continue
- # Also double check we don't have a signal with this
- # name.
- matched_signal = False
- for signal in pair_class.signals:
- if signal.name.replace('-', '_') == field.name:
- matched_signal = True
- break
- if matched_signal:
- continue
- vfunc = VFunction.from_callback(field)
- vfunc.inherit_file_positions(field)
- pair_class.virtual_methods.append(vfunc)
-
- # Take the set of virtual methods we found, and try
- # to pair up with any matching methods using the
- # name+signature.
- for vfunc in pair_class.virtual_methods:
- for method in pair_class.methods:
- if (method.name != vfunc.name or
- method.retval != vfunc.retval or
- method.parameters != vfunc.parameters):
- continue
- vfunc.invoker = method
-
- gclass_struct = GLibRecord.from_record(class_struct)
- self._remove_attribute(class_struct.name)
- self._add_attribute(gclass_struct, True)
- pair_class.glib_type_struct = gclass_struct
- pair_class.inherit_file_positions(class_struct)
- gclass_struct.is_gtype_struct_for = name
-
- # Introspection
+ # Introspection over the data we get from the dynamic
+ # GObject/GType system out of the binary
def _introspect_type(self, xmlnode):
if xmlnode.tag in ('enum', 'flags'):
@@ -709,8 +257,7 @@ class GLibTransformer(object):
type_name = node.attrib['name']
enum_name = self._transformer.remove_prefix(type_name)
node = klass(enum_name, type_name, members, node.attrib['get-type'])
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
+ self._namespace.append(node, replace=True)
def _introspect_object(self, xmlnode):
type_name = xmlnode.attrib['name']
@@ -719,58 +266,47 @@ class GLibTransformer(object):
# to skip it
if type_name == 'GObject':
return
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- #
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
+ node = GLibObject(self._transformer.remove_prefix(type_name),
+ None,
+ type_name,
+ xmlnode.attrib['get-type'], is_abstract)
+ self._parse_parents(xmlnode, node)
self._introspect_properties(node, xmlnode)
self._introspect_signals(node, xmlnode)
self._introspect_implemented_interfaces(node, xmlnode)
self._add_record_fields(node)
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
+ self._namespace.append(node, replace=True)
def _introspect_interface(self, xmlnode):
type_name = xmlnode.attrib['name']
- node = GLibInterface(
- self._transformer.remove_prefix(type_name),
- None,
- type_name, xmlnode.attrib['get-type'])
+ node = GLibInterface(self._transformer.remove_prefix(type_name),
+ None,
+ type_name, xmlnode.attrib['get-type'])
self._introspect_properties(node, xmlnode)
self._introspect_signals(node, xmlnode)
for child in xmlnode.findall('prerequisite'):
name = child.attrib['name']
- prereq = self._resolve_gtypename(name)
+ prereq = self._transformer.create_type(name)
node.prerequisites.append(prereq)
# GtkFileChooserEmbed is an example of a private interface, we
# just filter them out
if xmlnode.attrib['get-type'].startswith('_'):
- print "NOTICE: Marking %s as internal type" % (type_name, )
self._private_internal_types[type_name] = node
else:
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
+ self._namespace.append(node, replace=True)
def _introspect_boxed(self, xmlnode):
type_name = xmlnode.attrib['name']
# This one doesn't go in the main namespace; we associate it with
# the struct or union
node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
- self._boxed_types[node.type_name] = node
- self._register_internal_type(type_name, node)
def _introspect_implemented_interfaces(self, node, xmlnode):
gt_interfaces = []
for interface in xmlnode.findall('implements'):
- gitype = self._resolve_gtypename(interface.attrib['name'])
+ gitype = self._transformer.create_type(interface.attrib['name'])
gt_interfaces.append(gitype)
node.interfaces = sorted(gt_interfaces)
@@ -784,7 +320,7 @@ class GLibTransformer(object):
construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
node.properties.append(Property(
pspec.attrib['name'],
- type_name_from_ctype(ctype),
+ self._transformer.create_type(ctype),
readable, writable, construct, construct_only,
ctype,
))
@@ -793,23 +329,30 @@ class GLibTransformer(object):
def _introspect_signals(self, node, xmlnode):
for signal_info in xmlnode.findall('signal'):
rctype = signal_info.attrib['return']
- rtype = Type(self._transformer.parse_ctype(rctype), rctype)
- return_ = Return(rtype, signal_info.attrib['return'])
- return_.transfer = PARAM_TRANSFER_FULL
+ rtype = self._transformer.create_type(rctype)
+ return_ = Return(rtype, PARAM_TRANSFER_FULL)
signal = GLibSignal(signal_info.attrib['name'], return_)
for i, parameter in enumerate(signal_info.findall('param')):
if i == 0:
- name = 'object'
+ argname = 'object'
else:
- name = 'p%s' % (i-1, )
+ argname = 'p%s' % (i-1, )
pctype = parameter.attrib['type']
- ptype = Type(self._transformer.parse_ctype(pctype), pctype)
- param = Parameter(name, ptype)
+ ptype = self._transformer.create_type(pctype)
+ param = Parameter(argname, ptype)
param.transfer = 'none'
signal.parameters.append(param)
node.signals.append(signal)
node.signals = sorted(node.signals)
+ def _parse_parents(self, xmlnode, node):
+ if 'parents' in xmlnode.attrib:
+ parent_types = map(lambda s: self._transformer.create_type(s),
+ xmlnode.attrib['parents'].split(','))
+ else:
+ parent_types = []
+ node.parent_chain = parent_types
+
def _introspect_fundamental(self, xmlnode):
# We only care about types that can be instantiatable, other
# fundamental types such as the Clutter.Fixed/CoglFixed registers
@@ -819,30 +362,21 @@ class GLibTransformer(object):
return
type_name = xmlnode.attrib['name']
-
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- if 'parents' in xmlnode.attrib:
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
- else:
- parent_gitype = None
is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
+ node = GLibObject(self._transformer.remove_prefix(type_name),
+ None,
+ type_name,
+ xmlnode.attrib['get-type'], is_abstract)
+ self._parse_parents(xmlnode, node)
node.fundamental = True
self._introspect_implemented_interfaces(node, xmlnode)
self._add_record_fields(node)
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
+ self._namespace.append(node, replace=True)
def _add_record_fields(self, node):
# add record fields
- record = self._get_attribute(node.name)
+ record = self._namespace.get(node.name)
if record is None:
return
node.fields = record.fields
@@ -854,7 +388,7 @@ class GLibTransformer(object):
def _pair_boxed_type(self, boxed):
name = self._transformer.remove_prefix(boxed.type_name)
- pair_node = self._get_attribute(name)
+ pair_node = self._namespace.get(name)
if not pair_node:
boxed_item = GLibBoxedOther(name, boxed.type_name,
boxed.get_type)
@@ -870,336 +404,31 @@ class GLibTransformer(object):
boxed_item.fields = pair_node.fields
else:
return False
- self._add_attribute(boxed_item, replace=True)
+ self._namespace.append(boxed_item, replace=True)
- # Node walking
+ def _strip_class_suffix(self, name):
+ if (name.endswith('Class') or
+ name.endswith('Iface')):
+ return name[:-5]
+ elif name.endswith('Interface'):
+ return name[:-9]
+ else:
+ return name
- def _walk(self, node, callback, chain):
- if not isinstance(node, Node):
- return
- if not callback(node, chain):
+ def _pair_class_record(self, maybe_class):
+ name = self._strip_class_suffix(maybe_class.name)
+ if name == maybe_class.name:
return
- chain.append(node)
- def _subwalk(subnode):
- self._walk(subnode, callback, chain)
- if isinstance(node, (Callback, Callable)):
- _subwalk(node.retval)
- for parameter in node.parameters:
- _subwalk(parameter)
- elif isinstance(node, (Array, List)):
- _subwalk(node.element_type)
- elif isinstance(node, Map):
- _subwalk(node.key_type)
- _subwalk(node.value_type)
- elif isinstance(node, Bitfield):
- pass
- elif isinstance(node, Record):
- for ctor in node.constructors:
- _subwalk(ctor)
- for func in node.methods:
- _subwalk(func)
- elif isinstance(node, Field):
- _subwalk(node.type)
- elif isinstance(node, Class):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for meth in node.static_methods:
- _subwalk(meth)
- for ctor in node.constructors:
- _subwalk(ctor)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Interface):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Constant):
- _subwalk(node.type)
- elif isinstance(node, Union):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
- elif isinstance(node, GLibBoxed):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
-
- if isinstance(node, (GLibObject, GLibInterface)):
- for sig in node.signals:
- _subwalk(sig)
-
- chain.pop()
-
- # 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":
- type_name = "GObject.ParamSpec"
- res = self._transformer.resolve_type_name_full
- try:
- return res(type_name, ctype, self._names)
- except KeyError, e:
- return self._transformer.resolve_type_name(type_name, ctype)
-
- def _resolve_param_type(self, ptype, **kwargs):
- # Workaround glib bug #548689, to be included in 2.18.0
- if ptype.name == "GParam":
- ptype.name = "GObject.ParamSpec"
- elif ptype.name == "GObject.Strv":
- return Array(None, ptype.ctype, Type('utf8'))
-
- return self._transformer.resolve_param_type_full(ptype,
- self._names,
- **kwargs)
-
- def _resolve_node(self, node):
- if isinstance(node, Function):
- self._resolve_function_toplevel(node)
-
- elif isinstance(node, Callback):
- self._resolve_function(node)
- elif isinstance(node, GLibObject):
- self._resolve_glib_object(node)
- elif isinstance(node, GLibInterface):
- self._resolve_glib_interface(node)
- elif isinstance(node, Record):
- self._resolve_record(node)
- elif isinstance(node, Union):
- self._resolve_union(node)
- elif isinstance(node, Alias):
- self._resolve_alias(node)
- def _resolve_function_toplevel(self, func):
- for parser in [self._parse_constructor,
- self._parse_method,
- self._parse_static_method]:
- newfunc = parser(func)
- if newfunc:
- self._resolve_function(newfunc)
- return
- self._resolve_function(func)
-
- def _resolve_record(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_union(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _force_resolve(self, item, allow_unknown=False):
- if isinstance(item, Unresolved):
- if item.target in self._private_internal_types:
- return None
- try:
- return self._transformer.gtypename_to_giname(item.target,
- self._names)
- except KeyError, e:
- if allow_unknown:
- self._transformer.log_warning(
-"""Skipping unknown interface %s""" % (item.target, ))
- return None
- else:
- raise
- if item in self._private_internal_types:
- return None
- return item
-
- def _resolve_glib_interface(self, node):
- node.parent = self._force_resolve(node.parent)
- self._resolve_methods(node.methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- node.prerequisites = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.prerequisites])
-
- def _resolve_glib_object(self, node):
- # If we can't find the parent class, just drop back to GObject.
- # This supports hidden parent classes.
- # http://bugzilla.gnome.org/show_bug.cgi?id=561360
- try:
- node.parent = self._force_resolve(node.parent)
- except KeyError, e:
- #print ("WARNING: Parent %r of class %r" +\
- # " not found; using GObject") % (node.parent.target,
- # node.name)
- node.parent = self._transformer.gtypename_to_giname("GObject",
- self._names)
- node.interfaces = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.interfaces])
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
- self._resolve_methods(node.static_methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_glib_boxed(self, node):
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
-
- def _resolve_constructors(self, constructors):
- for ctor in constructors:
- self._resolve_function(ctor)
-
- def _resolve_methods(self, methods):
- for method in methods:
- self._resolve_function(method)
-
- def _resolve_signals(self, signals):
- for signal in signals:
- self._resolve_function(signal)
-
- def _resolve_properties(self, properties, context):
- failed = []
- for prop in properties:
- try:
- self._resolve_property(prop)
- except KeyError, e:
- failed.append(prop)
- for fail in failed:
- #print ("WARNING: Deleting object property %r (of %r) "
- # "with unknown type") % (fail, context)
- properties.remove(fail)
-
- def _resolve_property(self, prop):
- prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
-
- def _adjust_throws(self, func):
- if func.parameters == []:
+ class_struct = maybe_class
+ pair_class = self._namespace.get(name)
+ if not pair_class:
+ return
+ if not isinstance(pair_class, (GLibObject, GLibInterface)):
return
- last_param = func.parameters.pop()
-
- # Checking type.name=='GLib.Error' generates false positives
- # on methods that take a 'GError *'
- if last_param.type.ctype == 'GError**':
- func.throws = True
- else:
- func.parameters.append(last_param)
-
- def _resolve_function(self, func):
- self._resolve_parameters(func.parameters)
- func.retval.type = self._resolve_param_type(func.retval.type)
- self._adjust_throws(func)
-
- def _resolve_parameters(self, parameters):
- for parameter in parameters:
- parameter.type = self._resolve_param_type(parameter.type)
-
- def _resolve_field(self, field):
- if isinstance(field, Callback):
- self._resolve_function(field)
- elif isinstance(field, Record): # non-typedef'd struct
- self._resolve_record(field)
- elif isinstance(field, Union): # non-typedef'd union
- self._resolve_union(field)
- else:
- field.type = self._resolve_param_type(field.type)
-
- def _resolve_alias(self, alias):
- alias.target = self._resolve_type_name(alias.target, alias.target)
-
- def _resolve_types(self, nodes):
- nodes = list(self._names.names.itervalues())
- i = 0
- self._validating = True
- while True:
- initlen = len(nodes)
-
- nodes = list(self._names.names.itervalues())
- for node in nodes:
- try:
- self._resolve_node(node)
- except UnknownTypeError, e:
- print "WARNING: %s: Deleting %r" % (e, node)
- self._remove_attribute(node.name)
- if len(nodes) == initlen:
- break
- i += 1
- self._validating = False
-
- # Validation
-
- def _interface_vfunc_check(self, node, stack):
- if isinstance(node, GLibInterface):
- for vfunc in node.virtual_methods:
- if not vfunc.invoker:
- self._transformer.log_node_warning(vfunc,
-"""Virtual function %r has no known invoker""" % (vfunc.name, ),
- context=node)
-
- def _is_unannotated_list(self, node):
- # Already annotated
- if isinstance(node.type, List):
- return False
- if (node.type.name == 'GLib.List' or
- node.type.name == 'GLib.SList'):
- return True
- if (self._transformer._namespace.name == 'GLib' and
- (node.type.name == 'List' or
- node.type.name == 'SList')):
- return True
- return False
-
- def _introspectable_analysis(self, node, stack):
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- if node.type.name in (TYPE_LONG_LONG, TYPE_LONG_DOUBLE):
- parent.introspectable = False
- elif isinstance(node.type, Varargs):
- parent.introspectable = False
- elif self._is_unannotated_list(node):
- if isinstance(node, Parameter):
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on argument %r""" % (node.name, ),
- context=parent)
- else:
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on return value""", context=parent)
- parent.introspectable = False
-
- def _analyze_node(self, node, stack):
- if node.skip:
- return False
- # Combine one-pass checks here
- self._interface_vfunc_check(node, stack)
- # Our first pass for scriptability
- self._introspectable_analysis(node, stack)
- return True
-
- def _introspectable_pass2(self, node, stack):
- if node.skip:
- return False
- # In the second introspectable pass, we propagate introspectablity;
- # for example, a varargs callback as an argument to a function
- # makes the whole function unintrospectable
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- target = self._lookup_node(node.type.name)
- if target and not target.introspectable:
- parent.introspectable = False
- return True
-
- # This function is called at the very end, before we hand back the
- # completed namespace to the writer. Add static analysis checks here.
- def final_analyze(self):
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._analyze_node, [])
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._introspectable_pass2, [])
+ gclass_struct = GLibRecord.from_record(class_struct)
+ self._namespace.append(gclass_struct, replace=True)
+ pair_class.glib_type_struct = gclass_struct.create_type()
+ pair_class.inherit_file_positions(class_struct)
+ gclass_struct.is_gtype_struct_for = pair_class.create_type()
diff --git a/giscanner/primarytransformer.py b/giscanner/primarytransformer.py
new file mode 100644
index 0000000..8b8b2c5
--- /dev/null
+++ b/giscanner/primarytransformer.py
@@ -0,0 +1,820 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import os
+import sys
+import re
+import tempfile
+import shutil
+import subprocess
+
+from .ast import *
+from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
+ TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
+ TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
+ TAG_GET_VALUE_FUNC)
+from .annotationparser import (OPT_ALLOW_NONE,
+ OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
+ OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE, OPT_TRANSFER,
+ OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
+ OPT_FOREIGN, OPT_VAL_BITFIELD, OPT_ARRAY_FIXED_SIZE,
+ OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
+ OPT_SCOPE_ASYNC, OPT_SCOPE_CALL, OPT_SCOPE_NOTIFIED)
+from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
+ GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord)
+from .utils import to_underscores, to_underscores_noprefix
+
+class PrimaryTransformer(object):
+
+ def __init__(self, transformer, blocks):
+ self._transformer = transformer
+ self._blocks = blocks
+ self._namespace = transformer.namespace
+ self._uscore_type_names = {}
+
+ # Public API
+
+ def transform(self):
+ self._namespace.walk(self._pass_read_annotations)
+
+ # We have a rough tree which should have all
+ # of the types we know about. Let's attempt closure; walk
+ # over all of the Type() types and see if they match up
+ # with something.
+ self._namespace.walk(self._pass_type_resolution)
+
+ # Generate a reverse mapping "bar_baz" -> BarBaz
+ for node in self._namespace.itervalues():
+ uscored = to_underscores_noprefix(node.name).lower()
+ if isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+ self._uscore_type_names[uscored] = node
+
+ for node in list(self._namespace.itervalues()):
+ if isinstance(node, Function):
+ # Discover which toplevel functions are actually methods
+ self._pair_function(node)
+ if isinstance(node, (Class, Interface)):
+ self._pair_class_virtuals(node)
+
+ # Another type resolution pass after we've parsed virtuals, etc.
+ self._namespace.walk(self._pass_type_resolution)
+
+ self._namespace.walk(self._pass3)
+
+ # TODO - merge into pass3
+ self._resolve_quarks()
+
+ # Private
+
+ def _get_no_uscore_prefixed_name(self, type_name):
+ # Besides the straight underscore conversion, we also try
+ # removing the underscores from the namespace as a possible C
+ # mapping; e.g. it's webkit_web_view, not web_kit_web_view
+ suffix = self._transformer.remove_prefix(type_name)
+ prefix = type_name[:-len(suffix)]
+ return (prefix + '_' + to_underscores(suffix)).lower()
+
+ def _get_parameter_index(self, parent, param_name, origin):
+ index = parent.get_parameter_index(param_name)
+ if index is None:
+ if isinstance(origin, Parameter):
+ origin_name = 'parameter %s' % (origin.argname, )
+ else:
+ origin_name = 'return value'
+ self._transformer.log_node_warning(parent,
+ "can't find parameter %s referenced by %s of %r"
+ % (param_name, origin_name, parent.name), fatal=True)
+
+ return index
+
+ def _pass_read_annotations(self, node, chain):
+ if isinstance(node, Function):
+ block = self._blocks.get(node.symbol)
+ self._apply_annotations_callable(node, chain, block)
+ if isinstance(node, (Class, Interface, Record, Union, Enum, Bitfield,
+ Callback)):
+ block = self._blocks.get(node.c_name)
+ self._apply_annotations_annotated(node, block)
+ if isinstance(node, (Class, Interface, Record, Union)):
+ for field in node.fields:
+ self._blocks.get('%s::%s' % (node.c_name, field.name))
+ self._apply_annotations_field(node, block, field)
+ if isinstance(node, (Class, Interface)):
+ for prop in node.properties:
+ self._apply_annotations_property(node, prop)
+ for sig in node.signals:
+ self._apply_annotations_signal(node, sig)
+ return True
+
+ def _fixup_param_destroy(self, parent, param):
+ for p in parent.parameters:
+ if p is not param and p.destroy_index == param.destroy_index:
+ p.destroy_index = -1
+
+ def _fixup_param_closure(self, parent, param):
+ for p in parent.parameters:
+ if p is not param and p.closure_index == param.closure_index:
+ p.closure_index = -1
+
+ def _adjust_container_type(self, parent, node, options):
+ has_element_type = OPT_ELEMENT_TYPE in options
+ has_array = OPT_ARRAY in options
+
+ if has_array:
+ self._apply_annotations_array(parent, node, options)
+ elif has_element_type:
+ self._apply_annotations_element_type(parent, node, options)
+
+ def _extract_direction(self, node, options):
+ caller_allocates = False
+ if (OPT_INOUT in options or
+ OPT_INOUT_ALT in options):
+ direction = PARAM_DIRECTION_INOUT
+ elif OPT_OUT in options:
+ subtype = options[OPT_OUT]
+ if subtype is not None:
+ subtype = subtype.one()
+ direction = PARAM_DIRECTION_OUT
+ if subtype in (None, ''):
+ if node.type.target_giname and node.type.ctype:
+ caller_allocates = '**' not in node.type.ctype
+ else:
+ caller_allocates = False
+ elif subtype == 'caller-allocates':
+ caller_allocates = True
+ elif subtype == 'callee-allocates':
+ caller_allocates = False
+ else:
+ raise InvalidAnnotationError(
+ "out allocation for %s is invalid (%r)" % (node, subtype))
+ elif OPT_IN in options:
+ direction = PARAM_DIRECTION_IN
+ else:
+ direction = node.direction
+ return (direction, caller_allocates)
+
+ def _resolve(self, type_str, orig_node=None):
+ def grab_one(type_str, resolver, top_combiner, combiner):
+ """Return a complete type, and the trailing string part after it.
+ Use resolver() on each identifier, and combiner() on the parts of
+ each complete type. (top_combiner is used on the top-most type.)"""
+ bits = re.split(r'([,<>])', type_str, 1)
+ first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+ args = [resolver(first)]
+ if sep == '<':
+ while sep != '>':
+ next, rest = grab_one(rest, resolver, combiner, combiner)
+ args.append(next)
+ sep, rest = rest[0], rest[1:]
+ else:
+ rest = sep + rest
+ return top_combiner(*args), rest
+ def resolver(ident):
+ return self._transformer.create_and_resolve_type(ident)
+ def combiner(base, *rest):
+ if not rest:
+ return base
+ if (base.name in ['GLib.List', 'GLib.SList'] or
+ base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
+ return List(base.name, base.ctype, *rest)
+ if (base.name in ['GLib.HashTable'] or
+ base.ctype in ['GHashTable*']) and len(rest)==2:
+ return Map(base.name, base.ctype, *rest)
+ print "WARNING: throwing away type parameters:", type_str
+ return base
+ def top_combiner(base, *rest):
+ """For the top most type, recycle orig_node if possible."""
+ if orig_node is not None:
+ orig_node.name = base.name
+ base = orig_node # preserve other properties of orig_node
+ return combiner(base, *rest)
+
+ result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+ if rest:
+ print "WARNING: throwing away trailing part of type:", type_str
+ return result
+
+ def _apply_annotations_array(self, parent, node, options):
+ array_opt = options.get(OPT_ARRAY)
+ if array_opt:
+ array_values = array_opt.all()
+ else:
+ array_values = {}
+
+ element_type = options.get(OPT_ELEMENT_TYPE)
+ if element_type is not None:
+ element_type_node = self._resolve(element_type.one())
+ else:
+ # We're assuming here that Foo* with an (array) annotation
+ # and no (element-type) means array of Foo
+ element_type_node = node.type.clone()
+ # Explicitly erase ctype since it's no longer valid
+ element_type_node.ctype = None
+
+ container_type = Array(None, element_type_node,
+ ctype=node.type.ctype,
+ is_const=node.type.is_const)
+ if OPT_ARRAY_ZERO_TERMINATED in array_values:
+ container_type.zeroterminated = array_values.get(
+ OPT_ARRAY_ZERO_TERMINATED) == '1'
+ length = array_values.get(OPT_ARRAY_LENGTH)
+ if length is not None:
+ param_index = self._get_parameter_index(parent, length, node)
+ container_type.length_param_index = param_index
+ # For in parameters we're incorrectly deferring
+ # char/unsigned char to utf8 when a length annotation
+ # is specified.
+ if (isinstance(node, Parameter) and
+ node.type.is_equiv(TYPE_STRING) and
+ node.direction == PARAM_DIRECTION_IN and
+ element_type is None):
+ # FIXME: unsigned char/guchar should be uint8
+ container_type.element_type = TYPE_UINT8
+ fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
+ if fixed:
+ container_type.size = int(fixed)
+ node.type = container_type
+
+ def _apply_annotations_element_type(self, parent, node, options):
+ element_type_opt = options.get(OPT_ELEMENT_TYPE)
+ element_type = element_type_opt.flat()
+ if isinstance(node.type, List):
+ assert len(element_type) == 1
+ node.element_type = self._resolve(element_type[0])
+ elif isinstance(node.type, Map):
+ assert len(element_type) == 2
+ node.type.key_type = self._resolve(element_type[0])
+ node.type.value_type = self._resolve(element_type[1])
+ elif isinstance(node.type, Array):
+ node.type.element_type = self._resolve(element_type[0])
+ else:
+ self._transformer.log_node_warning(parent, "Unknown container for element-type annotation")
+
+ def _get_transfer_default(self, parent, node, options):
+ if node.transfer is not None:
+ return node.transfer
+
+ if node.type 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]:
+ if node.caller_allocates:
+ return PARAM_TRANSFER_NONE
+ return PARAM_TRANSFER_FULL
+ return PARAM_TRANSFER_NONE
+ elif isinstance(node, Return):
+ if (node.type.is_equiv(BASIC_GIR_TYPES) or
+ node.type.is_equiv((TYPE_STRING, TYPE_FILENAME)) or
+ (node.type.is_equiv([TYPE_NONE, TYPE_ANY]) and
+ node.type.is_const)):
+ return PARAM_TRANSFER_NONE
+ elif node.type.target_fundamental:
+ # This looks like just GType right now
+ return None
+ if node.type.target_giname:
+ target = self._transformer.lookup_typenode(node.type)
+ if isinstance(target, GLibBoxed):
+ return PARAM_TRANSFER_FULL
+ if isinstance(target, (Enum, Bitfield)):
+ return PARAM_TRANSFER_NONE
+ if isinstance(target, (Class, Record, Union)):
+ # Explicitly no default for these
+ return None
+ else:
+ return None
+ elif isinstance(node, Field):
+ return PARAM_TRANSFER_NONE
+ elif isinstance(node, Property):
+ return PARAM_TRANSFER_NONE
+ else:
+ raise AssertionError(node)
+
+ def _apply_annotations_param_ret_common(self, parent, node, tag):
+ options = getattr(tag, 'options', {})
+ (node.direction, node.caller_allocates) = \
+ self._extract_direction(node, options)
+ node.transfer = self._get_transfer_default(parent, node, options)
+ self._adjust_container_type(parent, node, options)
+ param_type = options.get(OPT_TYPE)
+ if param_type:
+ node.type = self._resolve(param_type.one(), node.type)
+
+ if (OPT_ALLOW_NONE in options or
+ node.type.target_giname == 'Gio.Cancellable'):
+ node.allow_none = True
+
+ if tag is not None and tag.comment is not None:
+ node.doc = tag.comment
+
+ for key in options:
+ if '.' in key:
+ value = options.get(key)
+ if value:
+ node.attributes.append((key, value.one()))
+
+ def _apply_annotations_annotated(self, node, block):
+ if block is None:
+ return
+
+ node.doc = block.comment
+
+ since_tag = block.get(TAG_SINCE)
+ if since_tag is None:
+ return
+ node.version = since_tag.value
+
+ deprecated_tag = block.get(TAG_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
+
+ annos_tag = block.get(TAG_ATTRIBUTES)
+ if annos_tag is None:
+ return
+ options = AnnotationParser.parse_options(annos_tag.value)
+ for key, value in options.iteritems():
+ if value:
+ node.attributes.append((key, value.one()))
+
+ if OPT_SKIP in block.options:
+ node.skip = True
+
+ if OPT_FOREIGN in block.options:
+ node.foreign = True
+
+ def _apply_annotations_param(self, parent, param, tag):
+ options = getattr(tag, 'options', {})
+ if isinstance(parent, Function):
+ scope = options.get(OPT_SCOPE)
+ if scope:
+ scope = scope.one()
+ if scope not in [PARAM_SCOPE_CALL,
+ PARAM_SCOPE_ASYNC,
+ PARAM_SCOPE_NOTIFIED]:
+ self._transformer.log_warning(parent, "Invalid scope %r for parameter %r" % (scope, param.name))
+ else:
+ param.scope = scope
+ param.transfer = PARAM_TRANSFER_NONE
+
+ destroy = options.get(OPT_DESTROY)
+ if destroy:
+ param.destroy_index = self._get_parameter_index(parent,
+ destroy.one(),
+ param)
+ self._fixup_param_destroy(parent, param)
+ closure = options.get(OPT_CLOSURE)
+ if closure:
+ param.closure_index = self._get_parameter_index(parent,
+ closure.one(),
+ param)
+ self._fixup_param_closure(parent, param)
+ if isinstance(parent, Callback):
+ if OPT_CLOSURE in options:
+ # For callbacks, (closure) appears without an
+ # argument, and tags a parameter that is a closure. We
+ # represent it (weirdly) in the gir and typelib by
+ # setting param.closure_index to its own index.
+ param.closure_index = parent.get_parameter_index(param)
+ self._fixup_param_closure(parent, param)
+
+ self._apply_annotations_param_ret_common(parent, param, tag)
+
+ def _apply_annotations_return(self, parent, return_, block):
+ if not block:
+ return
+ tag = block.get(TAG_RETURNS)
+ self._apply_annotations_param_ret_common(parent, return_, tag)
+
+ def _check_arg_annotations(self, parent, params, block):
+ if block is None:
+ return
+ for tag in block.tags.keys():
+ if tag == TAG_RETURNS:
+ continue
+ for param in params:
+ if param.argname == tag:
+ break
+ else:
+ self._transformer.log_warning("""Annotation for '%s' refers to unknown argument '%s'"""
+ % (parent.name, tag))
+
+ def _apply_annotations_params(self, parent, params, block):
+ self._check_arg_annotations(parent, params, block)
+ if not block:
+ return
+ for param in params:
+ tag = block.get(param.argname)
+ self._apply_annotations_param(parent, param, tag)
+
+ def _apply_annotations_callable(self, node, chain, block):
+ self._apply_annotations_annotated(node, block)
+ self._apply_annotations_params(node, node.parameters, block)
+ self._apply_annotations_return(node, node.retval, block)
+
+ def _check_arg_annotations(self, parent, params, block):
+ if block is None:
+ return
+ for tag in block.tags.keys():
+ if tag == TAG_RETURNS:
+ continue
+ for param in params:
+ if param.argname == tag:
+ break
+ else:
+ self._transformer.log_warning("""Annotation for '%s' refers to unknown argument '%s'"""
+ % (parent.name, tag))
+
+ def _apply_annotations_field(self, parent, block, field):
+ if not block:
+ return
+ tag = block.get(field.name)
+ if not tag:
+ return
+ t = tag.options.get('type')
+ if not t:
+ return
+ field.type = self._transformer.create_and_resolve_type(t.one())
+
+ def _apply_annotations_property(self, parent, prop):
+ block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
+ if not block:
+ return
+ self._apply_annotations_annotated(prop, block)
+ transfer_tag = block.get(TAG_TRANSFER)
+ if transfer_tag is not None:
+ options = {OPT_TRANSFER: Option(transfer_tag.value)}
+ else:
+ options = {}
+ prop.transfer = self._get_transfer_default(parent, prop, options)
+
+ type_tag = block.get(TAG_TYPE)
+ if type_tag:
+ prop.type = self._resolve(type_tag.value, prop.type)
+
+ def _apply_annotations_signal(self, parent, signal):
+ block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
+ self._apply_annotations_annotated(signal, block)
+ # We're only attempting to name the signal parameters if
+ # the number of parameter tags (@foo) is the same or greater
+ # than the number of signal parameters
+ if block and len(block.tags) > len(signal.parameters):
+ names = block.tags.items()
+ else:
+ names = []
+ for i, param in enumerate(signal.parameters):
+ if names:
+ name, tag = names[i+1]
+ param.name = name
+ options = getattr(tag, 'options', {})
+ param_type = options.get(OPT_TYPE)
+ if param_type:
+ param.type = self._resolve(param_type.one(), param.type)
+ else:
+ tag = None
+ self._apply_annotations_param(signal, param, tag)
+ self._apply_annotations_return(signal, signal.retval, block)
+
+ def _pass_type_resolution(self, node, chain):
+ if isinstance(node, Alias):
+ self._transformer.resolve_type(node.target)
+ if isinstance(node, Callable):
+ for parameter in node.parameters:
+ self._transformer.resolve_type(parameter.type)
+ self._transformer.resolve_type(node.retval.type)
+ if isinstance(node, Constant):
+ self._transformer.resolve_type(node.value_type)
+ if isinstance(node, (Class, Interface, Record, Union)):
+ for field in node.fields:
+ if field.anonymous_node:
+ field.anonymous_node.walk(self._pass_type_resolution, chain)
+ else:
+ self._transformer.resolve_type(field.type)
+ if isinstance(node, (Class, Interface)):
+ resolved_parent = None
+ for parent in node.parent_chain:
+ try:
+ self._transformer.resolve_type(parent)
+ except ValueError, e:
+ continue
+ node.parent = parent
+ break
+ for prop in node.properties:
+ self._transformer.resolve_type(prop.type)
+ for sig in node.signals:
+ for param in sig.parameters:
+ self._transformer.resolve_type(param.type)
+ if isinstance(node, Class):
+ for iface in node.interfaces:
+ self._transformer.resolve_type(iface)
+ if isinstance(node, Interface):
+ for iface in node.prerequisites:
+ self._transformer.resolve_type(iface)
+ return True
+
+ def _resolve_quarks(self):
+ # self._uscore_type_names is an authoritative mapping of types
+ # to underscored versions, since it is based on get_type() methods;
+ # but only covers enums that are registered as GObject enums.
+ # Create a fallback mapping based on all known enums in this module.
+ uscore_enums = {}
+ for enum in self._namespace.itervalues():
+ if not isinstance(enum, Enum):
+ continue
+ type_name = enum.symbol
+ uscored = to_underscores(type_name).lower()
+
+ uscore_enums[uscored] = enum
+
+ no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
+ if no_uscore_prefixed not in uscore_enums:
+ uscore_enums[no_uscore_prefixed] = enum
+
+ for node in self._namespace.itervalues():
+ if not isinstance(node, Function):
+ continue
+ if node.retval.type.target_giname != 'GLib.Quark':
+ continue
+ short = node.symbol[:-len('_quark')]
+ if short == "g_io_error":
+ # Special case; GIOError was already taken forcing GIOErrorEnum
+ enum = self._names.type_names["GIOErrorEnum"][1]
+ else:
+ enum = self._uscore_type_names.get(short)
+ if enum is None:
+ enum = uscore_enums.get(short)
+ if enum is not None:
+ enum.error_quark = node.symbol
+ else:
+ self._transformer.log_node_warning(node,
+"""%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
+
+ def _split_uscored_by_type(self, uscored):
+ """'uscored' should be an un-prefixed uscore string. This
+function searches through the namespace for the longest type which
+prefixes uscored, and returns (type, suffix). Example, assuming
+namespace Gtk, type is TextBuffer:
+
+_split_uscored_by_type(text_buffer_try_new) -> (Class(TextBuffer), 'try_new')"""
+ node = None
+ count = 0
+ prev_split_count = -1
+ while True:
+ components = uscored.rsplit('_', count)
+ if len(components) == prev_split_count:
+ return None
+ prev_split_count = len(components)
+ type_string = components[0]
+ node = self._uscore_type_names.get(type_string)
+ if node:
+ return (node, '_'.join(components[1:]))
+ count += 1
+
+ def _pair_function(self, func):
+ """Check to see whether a toplevel function should be a
+method or constructor of some type."""
+ if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
+ return
+ (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
+ assert ns == self._namespace
+ if self._pair_constructor(func, subsymbol):
+ return
+ elif self._pair_method(func, subsymbol):
+ return
+ elif self._pair_static_method(func, subsymbol):
+ return
+
+ def _uscored_identifier_for_type(self, typeval):
+ """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
+ name = typeval.get_giname()
+ return to_underscores_noprefix(name).lower()
+
+ def _pair_method(self, func, subsymbol):
+ if not func.parameters:
+ return False
+ first = func.parameters[0]
+ target = self._transformer.lookup_typenode(first.type)
+ if not isinstance(target, (Class, Interface, Record, Union, GLibBoxedOther)):
+ return False
+ uscored = self._uscored_identifier_for_type(first.type)
+ if not subsymbol.startswith(uscored):
+ return False
+ del func.parameters[0]
+ subsym_idx = func.symbol.find(subsymbol)
+ self._namespace.float(func)
+ func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
+ target.methods.append(func)
+ func.is_method = True
+ return True
+
+ def _pair_static_method(self, func, subsymbol):
+ split = self._split_uscored_by_type(subsymbol)
+ if split is None:
+ return False
+ (node, funcname) = split
+ if not isinstance(node, (Class, Interface, Record, Union, GLibBoxedOther)):
+ return False
+ self._namespace.float(func)
+ func.name = funcname
+ node.static_methods.append(func)
+
+ def _pair_constructor(self, func, subsymbol):
+ if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
+ return False
+ target = self._transformer.lookup_typenode(func.retval.type)
+ if not isinstance(target, (Class, Record, Union, GLibBoxedOther)):
+ return False
+ new_idx = func.symbol.rfind('_new')
+ assert (new_idx >= 0)
+ prefix = func.symbol[:new_idx]
+ split = self._split_uscored_by_type(subsymbol)
+ if split is None:
+ # TODO - need a e.g. (method) annotation
+ self._transformer.log_node_warning(func,
+ "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
+ return False
+ (origin_node, funcname) = split
+ if isinstance(target, Class):
+ parent = origin_node
+ while parent:
+ if parent == target:
+ break
+ parent = self._transformer.lookup_typenode(parent.parent)
+ if parent is None:
+ self._transformer.log_node_warning(func,
+ "Return value is not superclass for constructor; symbol=%r constructed=%r return=%r" % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+ return False
+ else:
+ if origin_node != target:
+ self._transformer.log_node_warning(func,
+ "Constructor return type mismatch symbol=%r constructed=%r return=%r" % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+ return False
+ self._namespace.float(func)
+ func.name = funcname
+ target.constructors.append(func)
+ return True
+
+ def _pair_class_virtuals(self, node):
+ """Look for virtual methods from the class structure."""
+ if not node.glib_type_struct:
+ self._transformer.log_node_warning(node,
+ "Failed to find class structure for %r" % (node.name, ))
+ return
+
+ node_type = node.create_type()
+ class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
+
+ # Object class fields are assumed to be read-only
+ # (see also _introspect_object and transformer.py)
+ for field in class_struct.fields:
+ if isinstance(field, Field):
+ field.writable = False
+
+ # Loop through fields to determine which are virtual
+ # functions and which are signal slots by
+ # assuming everything that doesn't share a name
+ # with a known signal is a virtual slot.
+ for field in class_struct.fields:
+ if not isinstance(field.anonymous_node, Callback):
+ continue
+ callback = field.anonymous_node
+ # Check the first parameter is the object
+ if len(callback.parameters) == 0:
+ continue
+ firstparam_type = callback.parameters[0].type
+ if firstparam_type != node_type:
+ continue
+ # Also double check we don't have a signal with this
+ # name.
+ matched_signal = False
+ for signal in node.signals:
+ if signal.name.replace('-', '_') == callback.name:
+ matched_signal = True
+ break
+ if matched_signal:
+ continue
+ vfunc = VFunction.from_callback(callback)
+ vfunc.inherit_file_positions(callback)
+ node.virtual_methods.append(vfunc)
+
+ # Take the set of virtual methods we found, and try
+ # to pair up with any matching methods using the
+ # name+signature.
+ for vfunc in node.virtual_methods:
+ for method in node.methods:
+ if method.name != vfunc.name:
+ continue
+ if method.retval.type != vfunc.retval.type:
+ continue
+ if len(method.parameters) != len(vfunc.parameters):
+ continue
+ for i in xrange(len(method.parameters)):
+ m_type = method.parameters[i].type
+ v_type = vfunc.parameters[i].type
+ if m_type != v_type:
+ continue
+ vfunc.invoker = method.name
+ # Apply any annotations we have from the invoker to
+ # the vfunc
+ block = self._blocks.get(method.symbol)
+ self._apply_annotations_callable(vfunc, [], block)
+
+ def _pass3(self, node, chain):
+ """Pass 3 is after we've loaded GType data and performed type
+ closure."""
+ if isinstance(node, Callable):
+ self._pass3_callable_callbacks(node)
+ self._pass3_callable_throws(node)
+ return True
+
+ def _pass3_callable_callbacks(self, node):
+ """Check to see if we have anything that looks like a
+ callback+user_data+GDestroyNotify set."""
+
+ def _handle_closure(self, param, closure_idx, closure_param):
+ if (closure_param.type.is_equiv(TYPE_ANY) and
+ closure_param.name.endswith('data')):
+ param.closure_name = closure_param.argname
+ param.closure_index = closure_idx
+ return True
+ return False
+
+ def _handle_destroy(self, param, destroy_idx, destroy_param):
+ if destroy_param.type.target_giname == 'GLib.DestroyNotify':
+ param.destroy_name = destroy_param.argname
+ param.destroy_index = destroy_idx
+ param.scope = PARAM_SCOPE_NOTIFIED
+ param.transfer = PARAM_TRANSFER_NONE
+ return True
+ return False
+
+ params = node.parameters
+ callback_index = -1
+ for i, param in enumerate(params):
+ node = self._transformer.lookup_typenode(param.type)
+ if not isinstance(node, Callback):
+ continue
+ # Special case GAsyncReadyCallback
+ if node.type.target_giname == 'Gio.AsyncReadyCallback':
+ param.scope = OPT_SCOPE_ASYNC
+ param.transfer = PARAM_TRANSFER_NONE
+ 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 _pass3_callable_throws(self, node):
+ """Check to see if we have anything that looks like a
+ callback+user_data+GDestroyNotify set."""
+ if not node.parameters:
+ return
+ last_param = node.parameters[-1]
+ # Checking type.name=='GLib.Error' generates false positives
+ # on methods that take a 'GError *'
+ if last_param.type.ctype == 'GError**':
+ node.parameters.pop()
+ node.throws = True
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 363f6f6..fc59c76 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -21,11 +21,12 @@
#
import subprocess
+import tempfile
import optparse
import os
import sys
-from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
+from giscanner.annotationparser import AnnotationParser
from giscanner.ast import Include
from giscanner.cachestore import CacheStore
from giscanner.dumper import compile_introspection_binary
@@ -34,6 +35,11 @@ from giscanner.minixpath import myxpath, xpath_assert
from giscanner.sourcescanner import SourceScanner
from giscanner.shlibs import resolve_shlibs
from giscanner.transformer import Transformer
+from giscanner.primarytransformer import PrimaryTransformer
+from giscanner.finaltransformer import FinalTransformer
+from giscanner.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
+from giscanner.utils import files_are_identical
def _get_option_parser():
parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +55,12 @@ def _get_option_parser():
parser.add_option("-i", "--include",
action="append", dest="includes", default=[],
help="include types for other gidls")
+ parser.add_option('', "--passthrough-gir",
+ action="store", dest="passthrough_gir", default=None,
+ help="Parse and re-output the specified GIR")
+ parser.add_option('', "--reparse-validate",
+ action="store_true", dest="reparse_validate_gir", default=False,
+ help="After generating the GIR, re-parse it to ensure validity")
parser.add_option("", "--add-include-path",
action="append", dest="include_paths", default=[],
help="include paths for other GIR files")
@@ -101,15 +113,9 @@ def _get_option_parser():
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose",
help="be verbose")
- parser.add_option("", "--noclosure",
- action="store_true", dest="noclosure",
- help="do not delete unknown types")
parser.add_option("", "--typelib-xml",
action="store_true", dest="typelib_xml",
help="Just convert GIR to typelib XML")
- parser.add_option("", "--inject",
- action="store_true", dest="inject",
- help="Inject additional components into GIR XML")
parser.add_option("", "--xpath-assertions",
action="store", dest="xpath_assertions",
help="Use given file to create assertions on GIR content")
@@ -136,54 +142,16 @@ def _get_option_parser():
def _error(msg):
raise SystemExit('ERROR: %s' % (msg, ))
-def typelib_xml_strip(path):
- from giscanner.girparser import GIRParser
- from giscanner.girwriter import GIRWriter
- from giscanner.girparser import C_NS
- from xml.etree.cElementTree import parse
-
- c_ns_key = '{%s}' % (C_NS, )
-
- tree = parse(path)
- root = tree.getroot()
- for node in root.getiterator():
- for attrib in list(node.attrib):
- if attrib.startswith(c_ns_key):
- del node.attrib[attrib]
+def passthrough_gir(path, f):
parser = GIRParser()
- parser.parse_tree(tree)
-
- writer = GIRWriter(parser.get_namespace(),
- parser.get_shared_libraries(),
- parser.get_includes())
- sys.stdout.write(writer.get_xml())
- return 0
-
-def inject(path, additions, outpath):
- from giscanner.girparser import GIRParser
- from giscanner.girwriter import GIRWriter
- from xml.etree.cElementTree import parse
-
- tree = parse(path)
- root = tree.getroot()
- injectDoc = parse(open(additions))
- for node in injectDoc.getroot():
- injectPath = node.attrib['path']
- target = myxpath(root, injectPath)
- if not target:
- raise ValueError("Couldn't find path %r" % (injectPath, ))
- for child in node:
- target.append(child)
+ parser.parse(path)
- parser = GIRParser()
- parser.parse_tree(tree)
writer = GIRWriter(parser.get_namespace(),
parser.get_shared_libraries(),
- parser.get_includes())
- outf = open(outpath, 'w')
- outf.write(writer.get_xml())
- outf.close()
- return 0
+ parser.get_includes(),
+ parser.get_pkgconfig_packages(),
+ parser.get_c_includes())
+ f.write(writer.get_xml())
def validate(assertions, path):
from xml.etree.cElementTree import parse
@@ -237,18 +205,12 @@ def scanner_main(args):
parser = _get_option_parser()
(options, args) = parser.parse_args(args)
+ if options.passthrough_gir:
+ passthrough_gir(options.passthrough_gir, sys.stdout)
+
if len(args) <= 1:
_error('Need at least one filename')
- if options.typelib_xml:
- return typelib_xml_strip(args[1])
-
- if options.inject:
- if len(args) != 4:
- _error('Need three filenames; e.g. g-ir-scanner '
- '--inject Source.gir Additions.xml SourceOut.gir')
- return inject(*args[1:4])
-
if options.xpath_assertions:
return validate(options.xpath_assertions, args[1])
@@ -277,11 +239,8 @@ def scanner_main(args):
cachestore = CacheStore()
transformer = Transformer(cachestore,
options.namespace_name,
- options.namespace_version)
- if options.strip_prefix:
- transformer.set_strip_prefix(options.strip_prefix)
- else:
- transformer.set_strip_prefix(options.namespace_name)
+ options.namespace_version,
+ strip_prefix=options.strip_prefix)
if options.warn_all:
transformer.enable_warnings(True)
transformer.set_include_paths(options.include_paths)
@@ -314,8 +273,7 @@ def scanner_main(args):
# Transform the C AST nodes into higher level
# GLib/GObject nodes
- glibtransformer = GLibTransformer(transformer,
- noclosure=options.noclosure)
+ glibtransformer = GLibTransformer(transformer)
# Do enough parsing that we have the get_type() functions to reference
# when creating the introspection binary
@@ -332,16 +290,16 @@ def scanner_main(args):
shlibs = resolve_shlibs(options, binary, libraries)
glibtransformer.set_introspection_binary(binary)
+ glibtransformer.parse()
- namespace = glibtransformer.parse()
+ ap = AnnotationParser(ss, transformer)
+ blocks = ap.parse()
- ap = AnnotationParser(namespace, ss, transformer)
- try:
- ap.parse()
- except InvalidAnnotationError, e:
- raise SystemExit("ERROR in annotation: %s" % (str(e), ))
+ primary = PrimaryTransformer(transformer, blocks)
+ primary.transform()
- glibtransformer.final_analyze()
+ final = FinalTransformer(transformer)
+ final.validate()
if options.warn_fatal and transformer.did_warn():
return 1
@@ -351,14 +309,25 @@ def scanner_main(args):
exported_packages = options.packages_export
else:
exported_packages = options.packages
- writer = Writer(namespace, shlibs, transformer.get_includes(),
- exported_packages, options.c_includes,
- transformer.get_strip_prefix())
+ writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
+ exported_packages, options.c_includes)
data = writer.get_xml()
if options.output:
- fd = open(options.output, "w")
- fd.write(data)
+ tempdir = os.path.dirname(options.output) or os.getcwd()
+ (tempfd, main_temppath) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+ f = os.fdopen(tempfd, 'w')
+ f.write(data)
+ f.close()
+ if options.reparse_validate_gir:
+ (tempfd, tempgir_path) = tempfile.mkstemp(suffix='.gir', dir=tempdir)
+ temp_f = os.fdopen(tempfd, 'w')
+ passthrough_gir(main_temppath, temp_f)
+ temp_f.close()
+ if not files_are_identical(main_temppath, tempgir_path):
+ raise SystemExit("Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_temppath, tempgir_path))
+ os.unlink(tempgir_path)
+ os.rename(main_temppath, options.output)
else:
- print data
+ sys.stdout.write(data)
return 0
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index ae3d29d..aff9a2f 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -154,10 +154,16 @@ class SourceSymbol(object):
self._symbol = symbol
def __repr__(self):
- return '<%s type=%r ident=%r>' % (
+ src = self.source_filename
+ if src:
+ line = self.line
+ if line:
+ src += ':%r' % (line,)
+ return '<%s type=%r ident=%r src=%r>' % (
self.__class__.__name__,
symbol_type_name(self.type),
- self.ident)
+ self.ident,
+ src)
@property
def const_int(self):
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index b43e188..939e8f4 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -22,13 +22,11 @@ import os
import sys
from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
- Parameter, Return, Struct, Field,
- Type, Array, Alias, Interface, Class, Node, Union,
- Varargs, Constant, type_name_from_ctype,
- type_names, TYPE_ANY, TYPE_STRING,
- BASIC_GIR_TYPES)
+ Parameter, Return, Record, Field,
+ Type, Array, List, Map, Alias, Interface, Class, Node, Union,
+ Varargs, Constant, type_names, basic_type_names,
+ default_array_types, TYPE_STRING, TYPE_ANY)
from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
from .girparser import GIRParser
from .odict import odict
from .sourcescanner import (
@@ -41,55 +39,33 @@ from .sourcescanner import (
TYPE_QUALIFIER_CONST)
from .utils import to_underscores
-_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
- + [DATADIR, '/usr/share'] if x]
-
-
-class SkipError(Exception):
+class TypeResolutionException(Exception):
pass
-
-class Names(object):
- names = property(lambda self: self._names)
- aliases = property(lambda self: self._aliases)
- type_names = property(lambda self: self._type_names)
- ctypes = property(lambda self: self._ctypes)
-
- def __init__(self):
- super(Names, self).__init__()
- self._names = odict() # Maps from GIName -> (namespace, node)
- self._aliases = {} # Maps from GIName -> GIName
- self._type_names = {} # Maps from GTName -> (namespace, node)
- self._ctypes = {} # Maps from CType -> (namespace, node)
-
+_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
+ + [DATADIR, '/usr/share'] if x]
class Transformer(object):
+ namespace = property(lambda self: self._namespace)
- def __init__(self, cachestore, namespace_name, namespace_version):
+ def __init__(self, cachestore, namespace_name, namespace_version,
+ strip_prefix=None):
self._cwd = os.getcwd() + os.sep
self._cachestore = cachestore
self.generator = None
- self._namespace = Namespace(namespace_name, namespace_version)
- self._names = Names()
+ self._namespace = Namespace(namespace_name, namespace_version,
+ c_prefix=strip_prefix)
self._pkg_config_packages = set()
self._typedefs_ns = {}
- self._strip_prefix = ''
+ self._strip_prefix = self._namespace.c_prefix
self._enable_warnings = False
self._warned = False
- self._includes = set()
+ self._includes = {}
+ self._include_names = set()
self._includepaths = []
- def get_names(self):
- return self._names
-
def get_includes(self):
- return self._includes
-
- def set_strip_prefix(self, strip_prefix):
- self._strip_prefix = strip_prefix
-
- def get_strip_prefix(self):
- return self._strip_prefix
+ return self._include_names
def enable_warnings(self, enable):
self._enable_warnings = enable
@@ -106,26 +82,56 @@ class Transformer(object):
def parse(self):
nodes = []
for symbol in self.generator.get_symbols():
- try:
- node = self._traverse_one(symbol)
- except SkipError:
- continue
- self._add_node(node)
- return self._namespace
+ node = self._traverse_one(symbol)
+ if node:
+ try:
+ self._namespace.append(node)
+ except ValueError, e:
+ original = self._namespace.get(node.name)
+ positions = set()
+ positions.update(original.file_positions)
+ positions.update(node.file_positions)
+ self.log_warning("Namespace conflict for '%s'" % (node.name, ),
+ positions)
+ sys.exit(1)
def set_include_paths(self, paths):
self._includepaths = list(paths)
def register_include(self, include):
- if include in self._includes:
+ if include in self._include_names:
return
filename = self._find_include(include)
self._parse_include(filename)
- self._includes.add(include)
+ self._include_names.add(include)
+
+ def lookup_giname(self, name):
+ """Given a name of the form Foo or Bar.Foo,
+return the corresponding Node, or None if none
+available. Will throw KeyError however for unknown
+namespaces."""
+ if '.' not in name:
+ return self._namespace.get(name)
+ else:
+ (ns, name) = name.split('.', 1)
+ if ns == self._namespace.name:
+ return self._namespace.get(name)
+ include = self._includes[ns]
+ return include.get(name)
+
+ def lookup_typenode(self, typeobj):
+ """Given a Type object, if it points to a giname,
+calls lookup_giname() on the name. Otherwise return
+None."""
+ if typeobj.target_giname:
+ return self.lookup_giname(typeobj.target_giname)
+ return None
+
# Private
- def log_warning(self, text, file_positions=None, prefix=None):
+ def log_warning(self, text, file_positions=None, prefix=None,
+ fatal=False):
"""Log a warning, using optional file positioning information.
If the warning is related to a Node type, see log_node_warning()."""
if not self._enable_warnings:
@@ -136,6 +142,7 @@ If the warning is related to a Node type, see log_node_warning()."""
else:
target_file_positions = file_positions
+ position_strings = []
for (filename, line, column) in target_file_positions:
if filename.startswith(self._cwd):
filename = filename[len(self._cwd):]
@@ -145,14 +152,20 @@ If the warning is related to a Node type, see log_node_warning()."""
position = '%s:%d' % (filename, line, )
else:
position = '%s:' % (filename, )
+ position_strings.append(position)
+ for position in position_strings[:-1]:
+ print >>sys.stderr, "%s:" % (position, )
+ last_position = position_strings[-1]
if prefix:
print >>sys.stderr, \
-'''%s: warning: ns=%r %s: %s''' % (position, self._namespace.name,
+'''%s: warning: ns=%r %s: %s''' % (last_position, self._namespace.name,
prefix, text)
else:
print >>sys.stderr, \
-'''%s: warning: ns=%r: %s''' % (position, self._namespace.name, text)
+'''%s: warning: ns=%r: %s''' % (last_position, self._namespace.name, text)
+ if fatal:
+ sys.exit(1)
def log_symbol_warning(self, symbol, text):
"""Log a warning in the context of the given symbol."""
@@ -160,7 +173,7 @@ If the warning is related to a Node type, see log_node_warning()."""
prefix = "symbol=%r" % (symbol.ident, )
self.log_warning(text, file_positions, prefix=prefix)
- def log_node_warning(self, node, text, context=None):
+ def log_node_warning(self, node, text, context=None, fatal=False):
"""Log a warning, using information about file positions from
the given node. The optional context argument, if given, should be
another Node type which will also be displayed. If no file position
@@ -175,7 +188,7 @@ context will be used."""
if context:
text = "context=%r %s" % (context.name, text)
- self.log_warning(text, file_positions)
+ self.log_warning(text, file_positions, fatal=fatal)
def _find_include(self, include):
searchdirs = self._includepaths[:]
@@ -196,7 +209,6 @@ context will be used."""
parser = self._cachestore.load(filename)
if parser is None:
parser = GIRParser()
- parser.set_include_parsing(True)
parser.parse(filename)
self._cachestore.store(filename, parser)
@@ -206,36 +218,56 @@ context will be used."""
for pkg in parser.get_pkgconfig_packages():
self._pkg_config_packages.add(pkg)
namespace = parser.get_namespace()
- nsname = namespace.name
- for node in namespace.nodes:
- if isinstance(node, Alias):
- self._names.aliases[node.name] = (nsname, node)
- elif isinstance(node, (GLibBoxed, Interface, Class)):
- self._names.type_names[node.type_name] = (nsname, node)
- giname = '%s.%s' % (nsname, node.name)
- self._names.names[giname] = (nsname, node)
- if hasattr(node, 'ctype'):
- self._names.ctypes[node.ctype] = (nsname, node)
- elif hasattr(node, 'symbol'):
- self._names.ctypes[node.symbol] = (nsname, node)
-
- def _add_node(self, node):
- if node is None:
- return
- if node.name.startswith('_'):
- return
- self._namespace.nodes.append(node)
- self._names.names[node.name] = (None, node)
-
- def _strip_namespace_func(self, name):
- prefix = self._namespace.name.lower() + '_'
- if name.lower().startswith(prefix):
- name = name[len(prefix):]
- else:
- prefix = to_underscores(self._namespace.name).lower() + '_'
- if name.lower().startswith(prefix):
- name = name[len(prefix):]
- return self.remove_prefix(name, isfunction=True)
+ self._includes[namespace.name] = namespace
+
+ def _iter_namespaces(self):
+ """Return an iterator over all included namespaces; the
+currently-scanned namespace is first."""
+ yield self._namespace
+ for ns in self._includes.itervalues():
+ yield ns
+
+ def split_ctype(self, ident):
+ """Given a StudlyCaps string identifier like FooBar, return a
+pair of (namespace, stripped_identifier) or raise ValueError."""
+ matches = []
+ for ns in self._iter_namespaces():
+ if ns.contains_ident(ident):
+ matches.append((ns, ident[len(ns.c_prefix):]))
+ if matches:
+ matches.sort(lambda x,y : cmp(len(x[0].c_prefix), len(y[0].c_prefix)))
+ return matches[0]
+ raise ValueError("Unknown namespace for identifier %r" % (ident, ))
+
+ def split_csymbol(self, symbol):
+ """Given a C symbol like foo_bar_do_baz, return a pair of
+(namespace, stripped_symbol) or raise ValueError."""
+ matches = []
+ for ns in self._iter_namespaces():
+ prefix = ns.uscore_prefix + '_'
+ if symbol.startswith(prefix):
+ matches.append((ns, symbol[len(prefix):]))
+ if matches:
+ matches.sort(lambda x,y : cmp(len(x[0].uscore_prefix), len(y[0].uscore_prefix)))
+ return matches[0]
+ raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
+
+ def _strip_symbol_or_warn(self, symbol):
+ ident = symbol.ident
+ hidden = ident.startswith('_')
+ if hidden:
+ ident = ident[1:]
+ try:
+ (ns, name) = self.split_csymbol(ident)
+ except ValueError, e:
+ self.log_symbol_warning(symbol, "Unknown namespace")
+ return None
+ if ns != self._namespace:
+ self.log_symbol_warning(symbol, "Skipping foreign symbol from namespace %s" % (ns.name, ))
+ return None
+ if hidden:
+ return '_' + name
+ return name
def remove_prefix(self, name, isfunction=False):
# when --strip-prefix=g:
@@ -264,17 +296,20 @@ context will be used."""
return self._create_struct(symbol)
elif stype == CSYMBOL_TYPE_ENUM:
return self._create_enum(symbol)
- elif stype == CSYMBOL_TYPE_OBJECT:
- return self._create_object(symbol)
elif stype == CSYMBOL_TYPE_MEMBER:
return self._create_member(symbol)
elif stype == CSYMBOL_TYPE_UNION:
return self._create_union(symbol)
+ # FIXME - we need to require an annotation on
+ # #defines to have them be constants, otherwise
+ # namespace explosion can occur
elif stype == CSYMBOL_TYPE_CONST:
- return self._create_const(symbol)
+ pass
+ # Ignore variable declarations in the header
+ elif stype == CSYMBOL_TYPE_OBJECT:
+ pass
else:
- raise NotImplementedError(
- 'Transformer: unhandled symbol: %r' % (symbol, ))
+ print 'transformer: unhandled symbol: %r' % (symbol, )
def _enum_common_prefix(self, symbol):
def common_prefix(a, b):
@@ -324,74 +359,15 @@ context will be used."""
klass = Enum
node = klass(enum_name, symbol.ident, members)
node.add_symbol_reference(symbol)
- self._names.type_names[symbol.ident] = (None, node)
return node
- def _create_object(self, symbol):
- node = Member(symbol.ident, symbol.base_type.name,
- symbol.ident)
- node.add_symbol_reference(symbol)
- return node
-
- def _type_is_callback(self, type):
- if isinstance(type, Callback):
- return True
- node = self._names.names.get(type.name)
- if node and isinstance(node[1], Callback):
- return True
- return False
-
- def _handle_closure(self, param, closure_idx, closure_param):
- if (closure_param.type.name == TYPE_ANY and
- closure_param.name.endswith('data')):
- param.closure_name = closure_param.name
- param.closure_index = closure_idx
- return True
- return False
-
- def _handle_destroy(self, param, destroy_idx, destroy_param):
- if (destroy_param.type.name == 'GLib.DestroyNotify' or
- destroy_param.type.ctype == 'GDestroyNotify'):
- param.destroy_name = destroy_param.name
- param.destroy_index = destroy_idx
- return True
- return False
-
- def _augment_callback_params(self, params):
- for i, param in enumerate(params):
- if not self._type_is_callback(param.type):
- continue
-
- # set a default scope
- if param.scope is None:
- param.scope = 'call'
-
- # 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):
parameters = list(self._create_parameters(symbol.base_type))
return_ = self._create_return(symbol.base_type.base_type)
- self._augment_callback_params(parameters)
- name = self._strip_namespace_func(symbol.ident)
- func = Function(name, return_, parameters, symbol.ident)
+ name = self._strip_symbol_or_warn(symbol)
+ if not name:
+ return None
+ func = Function(name, return_, parameters, False, symbol.ident)
func.add_symbol_reference(symbol)
return func
@@ -409,11 +385,10 @@ context will be used."""
elif source_type.type == CTYPE_POINTER:
value = self._create_source_type(source_type.base_type) + '*'
else:
- value = TYPE_ANY
+ value = 'gpointer'
return value
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)
for child in base_type.child_list:
@@ -438,21 +413,17 @@ context will be used."""
derefed_name = canonical_ctype[:-1]
else:
derefed_name = canonical_ctype
- derefed_name = self.resolve_param_type(derefed_name)
- ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+ ftype = Array(None, self.create_type(ctype), ctype=derefed_name)
child_list = list(symbol.base_type.child_list)
ftype.zeroterminated = False
if child_list:
- ftype.size = '%d' % (child_list[0].const_int, )
+ ftype.size = 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)
+ ftype = self._create_type_from_base(symbol.base_type)
# Fields are assumed to be read-write
# (except for Objects, see also glibtransformer.py)
- node = Field(symbol.ident, ftype, ftype.name,
+ node = Field(symbol.ident, ftype,
readable=True, writable=True, bits=symbol.const_int)
- node.add_symbol_reference(symbol)
return node
def _create_typedef(self, symbol):
@@ -475,9 +446,9 @@ context will be used."""
CTYPE_VOID):
name = self.remove_prefix(symbol.ident)
if symbol.base_type.name:
- target = self.remove_prefix(symbol.base_type.name)
+ target = self.create_type(symbol.base_type.name)
else:
- target = 'none'
+ target = TYPE_ANY
if name in type_names:
return None
return Alias(name, target, ctype=symbol.ident)
@@ -490,22 +461,20 @@ context will be used."""
# First look up the ctype including any pointers;
# a few type names like 'char*' have their own aliases
# and we need pointer information for those.
- firstpass = type_name_from_ctype(ctype)
+ firstpass = type_names.get(ctype)
# If we have a particular alias for this, skip deep
# canonicalization to prevent changing
# e.g. char* -> int8*
- if firstpass != ctype:
- return firstpass
+ if firstpass:
+ return firstpass.target_fundamental
- # We're also done if the type is already a fundamental
- # known type, or there are no pointers.
- if ctype in type_names or not firstpass.endswith('*'):
- return firstpass
+ if not ctype.endswith('*'):
+ return ctype
# We have a pointer type.
# Strip the end pointer, canonicalize our base type
- base = firstpass[:-1]
+ base = ctype[:-1]
canonical_base = self._canonicalize_ctype(base)
# Append the pointer again
@@ -523,52 +492,47 @@ context will be used."""
# Preserve "pointerness" of struct/union members
if (is_member and canonical.endswith('*') and
- derefed_typename in BASIC_GIR_TYPES):
- return TYPE_ANY
+ derefed_typename in basic_type_names):
+ return 'gpointer'
else:
return derefed_typename
- def _create_type(self, source_type, is_param, is_retval):
+ def _create_type_from_base(self, source_type):
ctype = self._create_source_type(source_type)
- if ctype.startswith('va_list'):
- raise SkipError()
- # FIXME: FILE* should not be skipped, it should be handled
- # properly instead
- elif ctype == 'FILE*':
- raise SkipError
-
- 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)
- # Is it a const char * or a const gpointer?
- 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
+ const = ((source_type.type == CTYPE_POINTER) and
+ (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
+ return self.create_type(ctype, is_const=const)
+
+ def create_type(self, ctype, is_const=False):
+ canonical = self._canonicalize_ctype(ctype)
+ base = canonical.replace('*', '')
+
+ fundamental = type_names.get(base)
+ if fundamental is not None:
+ return Type(target_fundamental=fundamental.target_fundamental,
+ ctype=ctype,
+ is_const=is_const)
+ elif canonical in default_array_types:
+ element_type = self.create_type(canonical[:-1])
+ return Array(None, element_type, ctype=ctype,
+ is_const=is_const)
+ elif base in ('GList', 'GSList'):
+ return List('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+ is_const=is_const)
+ elif base in ('GArray', 'GPtrArray', 'GByteArray'):
+ return Array('GLib.' + base[1:], TYPE_ANY, ctype=ctype,
+ is_const=is_const)
+ return Type(ctype=ctype, is_const=is_const)
def _create_parameter(self, symbol):
if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
ptype = Varargs()
else:
- ptype = self._create_type(symbol.base_type,
- is_param=True, is_retval=False)
- ptype = self.resolve_param_type(ptype)
+ ptype = self._create_type_from_base(symbol.base_type)
return Parameter(symbol.ident, ptype)
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)
- return return_
+ return Return(self._create_type_from_base(source_type))
def _create_const(self, symbol):
# Don't create constants for non-public things
@@ -576,15 +540,17 @@ context will be used."""
if (symbol.source_filename is None or
not symbol.source_filename.endswith('.h')):
return None
- name = self._strip_namespace_func(symbol.ident)
+ name = self._strip_symbol_or_warn(symbol)
+ if not name:
+ return None
if symbol.const_string is not None:
- type_name = 'utf8'
+ typeval = TYPE_STRING
value = symbol.const_string
elif symbol.const_int is not None:
- type_name = 'gint'
+ type_name = 'int'
value = symbol.const_int
elif symbol.const_double is not None:
- type_name = 'gdouble'
+ type_name = 'double'
value = symbol.const_double
else:
raise AssertionError()
@@ -595,24 +561,22 @@ context will be used."""
def _create_typedef_struct(self, symbol, disguised=False):
name = self.remove_prefix(symbol.ident)
- struct = Struct(name, symbol.ident, disguised)
+ struct = Record(name, symbol.ident, disguised)
struct.add_symbol_reference(symbol)
self._typedefs_ns[symbol.ident] = struct
- self._create_struct(symbol)
- return struct
+ return None
def _create_typedef_union(self, symbol):
name = self.remove_prefix(symbol.ident)
union = Union(name, symbol.ident)
union.add_symbol_reference(symbol)
self._typedefs_ns[symbol.ident] = union
- self._create_union(symbol)
- return union
+ return None
def _create_typedef_callback(self, symbol):
callback = self._create_callback(symbol)
self._typedefs_ns[callback.name] = callback
- return callback
+ return None
def _create_compound(self, klass, symbol, anonymous):
if symbol.ident is None:
@@ -636,15 +600,19 @@ context will be used."""
compound = klass(name, symbol.ident)
for child in symbol.base_type.child_list:
- field = self._traverse_one(child)
- if field:
- compound.fields.append(field)
+ child_node = self._traverse_one(child)
+ if isinstance(child_node, Field):
+ field = child_node
+ else:
+ field = Field(child.ident, None, True, False,
+ anonymous_node=child_node)
+ compound.fields.append(field)
compound.add_symbol_reference(symbol)
return compound
def _create_struct(self, symbol, anonymous=False):
- return self._create_compound(Struct, symbol, anonymous)
+ return self._create_compound(Record, symbol, anonymous)
def _create_union(self, symbol, anonymous=False):
return self._create_compound(Union, symbol, anonymous)
@@ -655,72 +623,48 @@ context will be used."""
# Mark the 'user_data' arguments
for i, param in enumerate(parameters):
- if (param.type.name == TYPE_ANY and
- param.name == 'user_data'):
+ if (param.type.target_fundamental == 'gpointer' and
+ param.argname == 'user_data'):
param.closure_index = i
if symbol.ident.find('_') > 0:
name = self.remove_prefix(symbol.ident, True)
else:
name = self.remove_prefix(symbol.ident)
- callback = Callback(name, retval, parameters, symbol.ident)
+ callback = Callback(name, retval, parameters, False)
callback.add_symbol_reference(symbol)
return callback
+ def create_and_resolve_type(self, ctype):
+ """Parse a C type string, returning a Type object, and
+ resolve it."""
+ typeval = self.create_type(ctype)
+ self.resolve_type(typeval)
+ return typeval
+
+ def resolve_type(self, typeval):
+ if isinstance(typeval, (Array, List)):
+ self.resolve_type(typeval.element_type)
+ return
+ elif isinstance(typeval, Map):
+ self.resolve_type(typeval.key_type)
+ self.resolve_type(typeval.value_type)
+ return
+ elif not typeval.resolved and typeval.ctype:
+ pointer_stripped = typeval.ctype.replace('*', '')
+ try:
+ (ns, name) = self.split_ctype(pointer_stripped)
+ except ValueError, e:
+ raise TypeResolutionException(e)
+ typeval.target_giname = '%s.%s' % (ns.name, name)
+
def _typepair_to_str(self, item):
nsname, item = item
if nsname is None:
return item.name
return '%s.%s' % (nsname, item.name)
- def _resolve_type_name_1(self, type_name, ctype, names):
- # First look using the built-in names
- if ctype:
- try:
- return type_names[ctype]
- except KeyError, e:
- pass
- try:
- return type_names[type_name]
- except KeyError, e:
- pass
-
- if ctype:
- ctype = ctype.replace('*', '')
- resolved = names.ctypes.get(ctype)
- if resolved:
- return self._typepair_to_str(resolved)
- type_name = self.remove_prefix(type_name)
- resolved = names.aliases.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- resolved = names.names.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- resolved = names.type_names.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- raise KeyError("failed to find %r" % (type_name, ))
-
- def resolve_type_name_full(self, type_name, ctype,
- names, allow_invalid=True):
- try:
- return self._resolve_type_name_1(type_name, ctype, names)
- except KeyError, e:
- try:
- return self._resolve_type_name_1(type_name, ctype, self._names)
- except KeyError, e:
- if not allow_invalid:
- raise
- return type_name
-
- def resolve_type_name(self, type_name, ctype=None):
- try:
- return self.resolve_type_name_full(type_name, ctype, self._names)
- except KeyError, e:
- return type_name
-
def gtypename_to_giname(self, gtname, names):
resolved = names.type_names.get(gtname)
if resolved:
@@ -738,23 +682,6 @@ context will be used."""
else:
return None
- def resolve_param_type_full(self, ptype, names, **kwargs):
- if isinstance(ptype, Node):
- ptype.name = self.resolve_type_name_full(ptype.name,
- self.ctype_of(ptype),
- names, **kwargs)
- elif isinstance(ptype, basestring):
- return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
- else:
- raise AssertionError("Unhandled param: %r" % (ptype, ))
- return ptype
-
- def resolve_param_type(self, ptype):
- try:
- return self.resolve_param_type_full(ptype, self._names)
- except KeyError, e:
- return ptype
-
def follow_aliases(self, type_name, names):
while True:
resolved = names.aliases.get(type_name)
@@ -764,8 +691,3 @@ context will be used."""
else:
break
return type_name
-
- def iter_enums(self):
- for node in self._namespace.nodes:
- if isinstance(node, Enum):
- yield node
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 1bd23fc..dba958e 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -102,3 +102,16 @@ def get_libtool_command(options):
return None
return ['libtool']
+
+
+def files_are_identical(path1, path2):
+ f1 = open(path1)
+ f2 = open(path2)
+ buf1 = f1.read(8192)
+ buf2 = f2.read(8192)
+ while buf1 == buf2 and buf1 != '':
+ buf1 = f1.read(8192)
+ buf2 = f2.read(8192)
+ f1.close()
+ f2.close()
+ return buf1 == buf2
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]