[gobject-introspection/wip/transformer] We can scan GLib
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gobject-introspection/wip/transformer] We can scan GLib
- Date: Wed, 14 Jul 2010 21:36:52 +0000 (UTC)
commit fc9188b03db88ded5f4c9cdadeda043f510d914b
Author: Colin Walters <walters verbum org>
Date: Thu Jun 17 15:55:37 2010 -0400
We can scan GLib
docs/g-ir-scanner.1 | 3 -
gir/Makefile.am | 9 +-
gir/glib-2.0.c | 2 +-
giscanner/annotationparser.py | 149 ++++------
giscanner/ast.py | 627 +++++++++++++++++++++++------------------
giscanner/girparser.py | 306 +++++++++++++-------
giscanner/girwriter.py | 143 +++++-----
giscanner/glibast.py | 55 +---
giscanner/glibtransformer.py | 634 ++++++++++++-----------------------------
giscanner/scannermain.py | 86 ++-----
giscanner/sourcescanner.py | 10 +-
giscanner/transformer.py | 397 +++++++++-----------------
tests/scanner/annotation.c | 2 +-
13 files changed, 1043 insertions(+), 1380 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..f9de713 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 = --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 = --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 = --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 --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 \
diff --git a/gir/glib-2.0.c b/gir/glib-2.0.c
index 6155dee..d91e74d 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/annotationparser.py b/giscanner/annotationparser.py
index c642d56..b135b2d 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -36,7 +36,7 @@ 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 .odict import odict
from .glibast import GLibBoxed
@@ -327,7 +327,7 @@ class AnnotationApplier(object):
def parse(self, namespace):
self._namespace = namespace
- for node in namespace.nodes[:]:
+ for node in namespace.itervalues():
self._parse_node(node)
del self._namespace
@@ -472,8 +472,7 @@ class AnnotationApplier(object):
def _parse_callable(self, callable, block):
self._parse_node_common(callable, block)
for i, param in enumerate(callable.parameters):
- if (param.type.ctype != 'GDestroyNotify' and
- param.type.name != 'GLib.DestroyNotify'):
+ if param.type.target_giname != 'GLib.DestroyNotify':
continue
if i < 2:
break
@@ -554,7 +553,7 @@ class AnnotationApplier(object):
t = tag.options.get('type')
if not t:
return
- field.type.name = self._transformer.resolve_type_name(t.one())
+ field.type = self._transformer.create_and_resolve_type(t.one())
def _check_arg_annotations(self, parent, params, block):
if block is None:
@@ -563,29 +562,32 @@ class AnnotationApplier(object):
if tag == TAG_RETURNS:
continue
for param in params:
- if param.name == tag:
+ if param.argname == tag:
break
else:
- return
- #print 'WARNING: annotation for "%s" refers to unknown ' \
- # 'argument "%s"' % (parent.name, tag)
+ self._transformer.log_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)
+ tag = self._get_tag(block, param.argname)
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):
+ 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'
raise InvalidAnnotationError(
- "can't find parameter %s referenced by parameter %s of %r"
- % (param_name, location_name, parent.name))
+ "can't find parameter %s referenced by %s of %r"
+ % (param_name, origin_name, parent.name))
return index
@@ -603,8 +605,7 @@ class AnnotationApplier(object):
"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'):
+ elif param.type.target_giname == 'Gio.AsyncReadyCallback':
param.scope = OPT_SCOPE_ASYNC
param.transfer = PARAM_TRANSFER_NONE
@@ -612,13 +613,13 @@ class AnnotationApplier(object):
if destroy:
param.destroy_index = self._get_parameter_index(parent,
destroy.one(),
- param.name)
+ 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.name)
+ param)
self._fixup_param_closure(parent, param)
if isinstance(parent, Callback):
if OPT_CLOSURE in options:
@@ -626,7 +627,7 @@ class AnnotationApplier(object):
# 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)
+ param.closure_index = parent.get_parameter_index(param)
self._fixup_param_closure(parent, param)
self._parse_param_ret_common(parent, param, tag)
@@ -645,10 +646,7 @@ class AnnotationApplier(object):
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
+ self._adjust_container_type(parent, node, options)
if node.direction is None:
node.direction = self._guess_direction(node)
node.caller_allocates = False
@@ -682,7 +680,7 @@ class AnnotationApplier(object):
subtype = subtype.one()
direction = PARAM_DIRECTION_OUT
if subtype in (None, ''):
- if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
+ if (not node.type.is_equiv(BASIC_GIR_TYPES)) and node.type.ctype:
caller_allocates = '**' not in node.type.ctype
else:
caller_allocates = False
@@ -705,25 +703,14 @@ class AnnotationApplier(object):
return False
if not ctype.endswith('*'):
return False
- if node.type.canonical in default_array_types:
+ if node.type 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):
+ def _adjust_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
@@ -734,13 +721,9 @@ class AnnotationApplier(object):
has_array = True
if has_array:
- container_type = self._parse_array(parent, node, options)
+ 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
+ self._parse_element_type(parent, node, options)
def _parse_array(self, parent, node, options):
array_opt = options.get(OPT_ARRAY)
@@ -749,43 +732,39 @@ class AnnotationApplier(object):
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
+ # 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.name)
+ 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.name == 'utf8' and
+ node.type.is_equiv(TYPE_STRING) 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('int8')
- container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
- return container_type
+ 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 _resolve(self, type_str, orig_node=None):
def grab_one(type_str, resolver, top_combiner, combiner):
@@ -804,7 +783,7 @@ class AnnotationApplier(object):
rest = sep + rest
return top_combiner(*args), rest
def resolver(ident):
- return self._transformer.resolve_param_type(Type(ident))
+ return self._transformer.create_and_resolve_type(ident)
def combiner(base, *rest):
if not rest:
return base
@@ -831,28 +810,17 @@ class AnnotationApplier(object):
def _parse_element_type(self, parent, node, options):
element_type_opt = options.get(OPT_ELEMENT_TYPE)
element_type = element_type_opt.flat()
- if (node.type.name in ['GLib.List', 'GLib.SList'] or
- node.type.ctype in ['GList*', 'GSList*']):
+ if isinstance(node.type, List):
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*']):
+ node.element_type = self._resolve(element_type[0])
+ elif isinstance(node.type, Map):
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]))
+ 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:
- print 'FIXME: unhandled element-type container:', node
- return container_type
+ self._transformer.log_node_warning(node, "Unknown container for element-type annotation")
def _extract_transfer(self, parent, node, options):
transfer_opt = options.get(OPT_TRANSFER)
@@ -927,6 +895,8 @@ class AnnotationApplier(object):
node.get_value_func = tag.value if tag else None
def _parse_rename_to_func(self, node, block):
+ # FIXME - this needs rethinking
+ return
rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
if rename_to_tag is None:
return
@@ -964,7 +934,7 @@ class AnnotationApplier(object):
if node.type.ctype:
is_pointer = '*' in node.type.ctype
- if is_pointer and node.type.name in BASIC_GIR_TYPES:
+ if is_pointer and node.type.is_equiv(BASIC_GIR_TYPES):
return PARAM_DIRECTION_OUT
return PARAM_DIRECTION_IN
@@ -976,7 +946,7 @@ class AnnotationApplier(object):
# Anything with 'const' gets none
if node.type.is_const:
return PARAM_TRANSFER_NONE
- elif node.type.name in [TYPE_NONE, TYPE_ANY]:
+ elif node.type in [TYPE_NONE, TYPE_ANY]:
return PARAM_TRANSFER_NONE
elif isinstance(node.type, Varargs):
return PARAM_TRANSFER_NONE
@@ -988,18 +958,17 @@ class AnnotationApplier(object):
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':
+ elif node.type.is_equiv(TYPE_STRING):
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'):
+ node.type.element_type.is_equiv(TYPE_STRING)):
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)):
+ elif (node.type.is_equiv(BASIC_GIR_TYPES) or
+ (node.type.is_equiv([TYPE_NONE, TYPE_ANY]) and
+ node.type.is_const)):
return PARAM_TRANSFER_NONE
else:
return PARAM_TRANSFER_FULL
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 3678e8c..d6fe47f 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,82 +19,127 @@
# 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
+
+class Type(object):
+ resolved = property(lambda self: (self.target_fundamental or
+ self.target_giname or
+ self.target_foreign))
+ """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.
"""
-
-##
-## Basic types, modeled on GITypeTag but not equivalent
-##
-
-TYPE_NONE = 'none' # We differ from repository on these first two
-TYPE_ANY = 'any'
-TYPE_BOOLEAN = 'boolean'
-TYPE_INT8 = 'int8'
-TYPE_UINT8 = 'uint8'
-TYPE_SHORT = 'short'
-TYPE_USHORT = 'ushort'
-TYPE_INT16 = 'int16'
-TYPE_UINT16 = 'uint16'
-TYPE_INT = 'int'
-TYPE_UINT = 'uint'
-TYPE_INT32 = 'int32'
-TYPE_UINT32 = 'uint32'
-TYPE_INT64 = 'int64'
-TYPE_UINT64 = 'uint64'
-TYPE_LONG = 'long'
-TYPE_ULONG = 'ulong'
-TYPE_SIZET = 'gsize'
-TYPE_SSIZET = 'gssize'
-TYPE_GTYPE = 'GType'
-TYPE_FLOAT = 'float'
-TYPE_DOUBLE = 'double'
-TYPE_STRING = 'utf8' # requires zero-terminated
-TYPE_FILENAME = 'filename'
+ def __init__(self,
+ ctype=None,
+ target_fundamental=None,
+ target_giname=None,
+ target_foreign=None,
+ is_const=False,
+ origin_symbol=None):
+ self.ctype = ctype
+ self.origin_symbol = origin_symbol
+ if target_giname == 'signed int':
+ raise ValueError()
+ if 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 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):
+ for val in typeval:
+ if self.is_equiv(val):
+ return True
+ return False
+ if not (self.resolved and typeval.resolved):
+ return False
+ if self.target_fundamental:
+ return typeval.target_fundamental == self.target_fundamental
+ elif self.target_giname:
+ return typeval.target_giname == self.target_giname
+ elif self.target_foreign:
+ return typeval.target_foreign == self.target_foreign
+
+ 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)
+
+######
+## Fundamental types
+######
+# Two special ones
+TYPE_NONE = Type(target_fundamental='void', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
+# "Basic" types
+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 = 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 = Type(target_fundamental='long double', ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
+
+# C types with semantics overlaid
+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_SHORT, TYPE_USHORT, TYPE_INT,
- TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_SSIZET,
- TYPE_SIZET, TYPE_FLOAT, TYPE_DOUBLE,
- TYPE_GTYPE]
+ 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_UNICHAR, TYPE_GTYPE]
GIR_TYPES = [TYPE_NONE, TYPE_ANY]
GIR_TYPES.extend(BASIC_GIR_TYPES)
GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
-# Higher-level data types
-TYPE_SEQUENCE = 'sequence' # Sequence of something
-
-# Wide/Unicode
-TYPE_UCHAR = 'uchar'
-TYPE_USTRING = 'ustring'
-
-##
-## Parameters
-##
-
-PARAM_DIRECTION_IN = 'in'
-PARAM_DIRECTION_OUT = 'out'
-PARAM_DIRECTION_INOUT = 'inout'
-
-PARAM_SCOPE_CALL = 'call'
-PARAM_SCOPE_ASYNC = 'async'
-PARAM_SCOPE_NOTIFIED = 'notified'
-
-PARAM_TRANSFER_NONE = 'none'
-PARAM_TRANSFER_CONTAINER = 'container'
-PARAM_TRANSFER_FULL = 'full'
-
type_names = {}
-for name in GIR_TYPES:
- type_names[name] = name
+for typeval in GIR_TYPES:
+ type_names[typeval.target_fundamental] = typeval
# C builtin
-type_names['char'] = TYPE_INT8
+type_names['char'] = TYPE_CHAR
type_names['signed char'] = TYPE_INT8
type_names['unsigned char'] = TYPE_UINT8
type_names['short'] = TYPE_SHORT
@@ -115,8 +160,24 @@ type_names['double'] = TYPE_DOUBLE
type_names['char*'] = TYPE_STRING
type_names['void*'] = TYPE_ANY
type_names['void'] = TYPE_NONE
+# Also alias the signed one here
+type_names['signed long long'] = TYPE_LONG_LONG
+
+# A few additional GLib type aliases
+type_names['gchararray'] = TYPE_STRING
+type_names['gchar*'] = TYPE_STRING
+type_names['gsize'] = TYPE_ULONG
+type_names['gssize'] = TYPE_LONG
+type_names['gconstpointer'] = TYPE_ANY
-# Random C unix type definitions; note some of these may be GNU Libc
+# Some special C types that aren't scriptable, and we just squash
+type_names['va_list'] = TYPE_ANY
+
+# C stdio, used in GLib public headers; squash this for now here
+# until we move scanning into GLib and can (skip)
+type_names['FILE*'] = TYPE_ANY
+
+# One off C unix type definitions; note some of these may be GNU Libc
# specific. If someone is actually bitten by this, feel free to do
# the required configure goop to determine their size and replace
# here.
@@ -127,58 +188,187 @@ type_names['void'] = TYPE_NONE
# methods are added under #ifdefs inside GLib itself. We could just (skip)
# the relevant methods, but on the other hand, since these types are just
# integers it's easy enough to expand them.
-type_names['size_t'] = TYPE_SIZET
+type_names['size_t'] = type_names['gsize']
type_names['time_t'] = TYPE_LONG
-type_names['off_t'] = TYPE_SIZET
+type_names['off_t'] = type_names['gsize']
type_names['pid_t'] = TYPE_INT
type_names['uid_t'] = TYPE_UINT
type_names['gid_t'] = TYPE_UINT
type_names['dev_t'] = TYPE_INT
type_names['socklen_t'] = TYPE_INT32
+type_names['size_t'] = TYPE_ULONG
+type_names['ssize_t'] = TYPE_LONG
# Obj-C
type_names['id'] = TYPE_ANY
-# Suppress some GLib names
-type_names['uchar'] = TYPE_UINT8
-type_names['ushort'] = TYPE_USHORT
-type_names['size'] = TYPE_SIZET
-type_names['ssize'] = TYPE_SSIZET
-type_names['pointer'] = TYPE_ANY
-type_names['constpointer'] = 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['uint8*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
+default_array_types['guint8*'] = TYPE_UINT8
+default_array_types['guchar*'] = TYPE_UINT8
+default_array_types['gchar**'] = TYPE_STRING
# These types, when seen by reference, are interpreted as out parameters
default_out_types = (TYPE_SHORT, TYPE_USHORT, TYPE_INT, TYPE_UINT,
- TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
- TYPE_SIZET, TYPE_SSIZET)
+ TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE)
+##
+## Parameters
+##
+
+PARAM_DIRECTION_IN = 'in'
+PARAM_DIRECTION_OUT = 'out'
+PARAM_DIRECTION_INOUT = 'inout'
+
+PARAM_SCOPE_CALL = 'call'
+PARAM_SCOPE_ASYNC = 'async'
+PARAM_SCOPE_NOTIFIED = 'notified'
-def type_name_from_ctype(ctype):
- return type_names.get(ctype, ctype)
+PARAM_TRANSFER_NONE = 'none'
+PARAM_TRANSFER_CONTAINER = 'container'
+PARAM_TRANSFER_FULL = 'full'
+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 = 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 GIR_TYPES:
+ 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 SystemExit(
+ "transformer: conflict: ns=%r orig=%r new=%r" %
+ (self.name, previous, node))
+ 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]
+ node.namespace = None
+ if hasattr(node, 'ctype'):
+ del self._ctypes[node.ctype]
+ if hasattr(node, 'symbol'):
+ del self._ctypes[node.symbol]
+
+ 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)
+
+
+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."""
+
+ 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 __cmp__(self, other):
+ nscmp = cmp(self.namespace, other.namespace)
+ if nscmp != 0:
+ return nscmp
return cmp(self.name, other.name)
def __repr__(self):
@@ -197,49 +387,6 @@ class Node(object):
if symbol.source_filename:
self.add_file_position(symbol.source_filename, symbol.line, -1)
-class Namespace(Node):
-
- 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)
class Callable(Node):
@@ -248,29 +395,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
@@ -287,59 +438,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):
@@ -348,14 +496,12 @@ 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]:
@@ -365,25 +511,32 @@ class TypeContainer(Node):
class Parameter(TypeContainer):
+ """An argument to a function."""
- def __init__(self, name, typenode, direction=None,
- transfer=None, allow_none=False, scope=None):
- TypeContainer.__init__(self, name, typenode, transfer)
+ 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
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
+ self.caller_allocates = caller_allocates
self.allow_none = allow_none
self.scope = scope
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):
@@ -392,10 +545,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):
@@ -404,22 +553,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):
@@ -429,7 +572,6 @@ class Record(Node):
self.constructors = []
self.symbol = symbol
self.disguised = disguised
- self.doc = None
self.methods = []
def remove_matching_children(self, pred):
@@ -437,36 +579,18 @@ class Record(Node):
self.constructors = filter(pred, self.constructors)
self.methods = filter(pred, self.methods)
-# BW compat, remove
-Struct = Record
+class Field(Annotated):
-class Field(Node):
-
- 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,7 +608,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)
@@ -492,10 +615,6 @@ 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)
class Interface(Node):
@@ -509,70 +628,34 @@ class Interface(Node):
self.properties = []
self.fields = []
self.prerequisites = []
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r)' % (
- self.__class__.__name__,
- self.name, self.methods)
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)
+ self.transfer = PARAM_TRANSFER_NONE
-# FIXME: Inherit from Function
+class Callback(Callable):
-
-class Callback(Node):
-
- 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):
@@ -583,7 +666,3 @@ class Union(Node):
self.constructors = []
self.methods = []
self.symbol = symbol
- self.doc = None
-
- def __repr__(self):
- return 'Union(%r, %r)' % (self.name, self.fields, )
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 17cf630..113ca49 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -23,11 +23,12 @@ 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)
+ Namespace, Parameter, Property, Return, Union, Type, List,
+ VFunction, Record, Varargs, Include, PARAM_DIRECTION_IN,
+ Annotated)
from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
GLibInterface, GLibObject, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther)
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord)
from .girwriter import COMPATIBLE_GIR_VERSION
@@ -51,7 +52,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 +72,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 +85,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 +99,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 +123,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 +136,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,19 +173,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'],
- node.attrib['target'],
+ 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'):
@@ -181,38 +222,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']
@@ -221,29 +273,43 @@ 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):
@@ -252,24 +318,26 @@ class GIRParser(object):
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')
+ self._parse_generic_attribs(node, struct)
+ 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 ctor in self._find_children(node, _corens('constructor')):
struct.constructors.append(
self._parse_function_common(ctor, Function))
@@ -282,50 +350,67 @@ class GIRParser(object):
else:
union = Union(node.attrib['name'],
node.attrib.get(_cns('type')))
- self._add_node(union)
+ 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))
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['name']
+ ctype = typenode.attrib.get(_cns('type'))
+ 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))
@@ -333,58 +418,69 @@ 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'))
+ callback_node = self._find_first_child(node, _corens('callback'))
+ if callback_node:
+ anonymous_node = self._parse_function_common(callback_node, Callback)
+ type_node = None
+ else:
+ anonymous_node = None
+ 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:
@@ -397,10 +493,10 @@ class GIRParser(object):
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 2e0a6cd..d3c95be 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,7 +22,7 @@
from __future__ import with_statement
from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
- Function, Interface, List, Map, Member, Struct, Union,
+ Function, Interface, List, Map, Member, Record, Union,
Varargs, Type)
from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
GLibFlags, GLibObject, GLibInterface,
@@ -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)
@@ -153,10 +155,11 @@ and/or use gtk-doc annotations. ''')
attrs.append(('throws', '1'))
def _write_alias(self, alias):
- attrs = [('name', alias.name), ('target', alias.target)]
+ attrs = [('name', alias.name)]
if alias.ctype is not None:
attrs.append(('c:type', alias.ctype))
- self.write_tag('alias', attrs)
+ with self.tagcontext('alias', attrs):
+ self._write_type(alias.target)
def _write_callable(self, callable, tag_name, extra_attrs):
attrs = [('name', callable.name)]
@@ -170,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):
@@ -205,8 +210,8 @@ and/or use gtk-doc annotations. ''')
assert parameter.transfer is not None, parameter
attrs = []
- if parameter.name is not None:
- attrs.append(('name', parameter.name))
+ if parameter.argname is not None:
+ attrs.append(('name', parameter.argname))
if parameter.direction != 'in':
attrs.append(('direction', parameter.direction))
attrs.append(('caller-allocates',
@@ -225,62 +230,55 @@ 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 _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)
+ 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('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('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, ('ref', ntype.target_giname))
+ prefix = self._namespace.name + '.'
+ if ntype.target_giname.startswith(prefix):
+ attrs.insert(0, ('name', ntype.target_giname[len(prefix):]))
+ else:
+ attrs.insert(0, ('name', ntype.target_giname))
+ 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'))
+ else:
+ raise AssertionError("Caught unresolved type %r (ctype=%r)" % (ntype, ntype.ctype))
+ # Not a special type, just write it out
+ self.write_tag('type', attrs)
def _write_enum(self, enum):
attrs = [('name', enum.name)]
@@ -324,10 +322,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),
@@ -337,7 +334,7 @@ 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', node.parent.target_giname))
if node.is_abstract:
attrs.append(('abstract', '1'))
else:
@@ -347,7 +344,7 @@ 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', node.glib_type_struct.target_giname))
if isinstance(node, GLibObject):
if node.fundamental:
attrs.append(('glib:fundamental', '1'))
@@ -363,10 +360,10 @@ 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', iface.target_giname)])
if isinstance(node, Interface):
for iface in node.prerequisites:
- self.write_tag('prerequisite', [('name', iface)])
+ self.write_tag('prerequisite', [('name', iface.target_giname)])
if isinstance(node, Class):
for method in node.constructors:
self._write_constructor(method)
@@ -415,7 +412,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):
@@ -441,7 +438,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))
+ record.is_gtype_struct_for.target_giname))
self._append_version(record, attrs)
self._append_node_generic(record, attrs)
if isinstance(record, GLibBoxed):
@@ -477,20 +474,16 @@ and/or use gtk-doc annotations. ''')
self._write_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)
else:
- attrs = [('name', 'any'), ('c:type', 'pointer')]
- self.write_tag('type', attrs)
- elif isinstance(field, Struct):
+ raise AssertionError("Unknown field anonymous: %r" \
+ % (field.anonymous_node, ))
+ elif isinstance(field, Record):
self._write_record(field)
elif isinstance(field, Union):
self._write_union(field)
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index c712988..dd6079d 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -19,48 +19,7 @@
#
from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
- Property, Struct, Union, Record)
-from .ast import (
- type_names, default_array_types,
- TYPE_STRING, TYPE_INT8, TYPE_UINT8, TYPE_SHORT, TYPE_USHORT,
- TYPE_INT16, TYPE_UINT16, TYPE_INT, TYPE_UINT, TYPE_UINT32,
- TYPE_INT32, TYPE_LONG, TYPE_ULONG, TYPE_INT64, TYPE_UINT64,
- TYPE_FLOAT, TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_ANY, TYPE_SSIZET,
- TYPE_SIZET)
-
-
-# Glib type names
-type_names['gchararray'] = TYPE_STRING
-type_names['gint8'] = TYPE_INT8
-type_names['guint8'] = TYPE_UINT8
-type_names['gint16'] = TYPE_INT16
-type_names['guint16'] = TYPE_UINT16
-type_names['gint'] = TYPE_INT
-type_names['guint'] = TYPE_UINT
-type_names['gint32'] = TYPE_INT32
-type_names['guint32'] = TYPE_UINT32
-type_names['glong'] = TYPE_LONG
-type_names['gulong'] = TYPE_ULONG
-type_names['gint64'] = TYPE_INT64
-type_names['guint64'] = TYPE_UINT64
-type_names['gfloat'] = TYPE_FLOAT
-type_names['gdouble'] = TYPE_DOUBLE
-type_names['gchar*'] = TYPE_STRING
-type_names['gboolean'] = TYPE_BOOLEAN
-type_names['gpointer'] = TYPE_ANY
-type_names['gconstpointer'] = TYPE_ANY
-type_names['gsize'] = TYPE_SIZET
-type_names['gssize'] = TYPE_SSIZET
-type_names['gchar'] = TYPE_INT8
-type_names['guchar'] = TYPE_UINT8
-type_names['gshort'] = TYPE_SHORT
-type_names['gushort'] = TYPE_USHORT
-
-# It's not very nice to duplicate the array types from ast.py,
-# but a clean fix is hard without essentially hardcoding
-# char * again inside transformer.py
-default_array_types['guint8*'] = TYPE_UINT8
-default_array_types['gchar**'] = TYPE_STRING
+ Property, Union, Record)
class GLibRecord(Record):
def __init__(self, *args, **kwargs):
@@ -68,7 +27,7 @@ class GLibRecord(Record):
@classmethod
def from_record(cls, record):
- obj = cls(record.name, record.symbol)
+ obj = cls(record.namespace, record.name, record.symbol)
obj.fields = record.fields
obj.constructors = record.constructors
obj.disguised = record.disguised
@@ -127,6 +86,8 @@ class GLibObject(Class):
self.get_value_func = None
self.signals = []
self.ctype = ctype or type_name
+ # Unresolved state
+ self.parent_gtype_names = []
class GLibBoxed:
@@ -136,10 +97,10 @@ class GLibBoxed:
self.get_type = get_type
-class GLibBoxedStruct(Struct, GLibBoxed):
+class GLibBoxedStruct(Record, GLibBoxed):
def __init__(self, name, type_name, get_type, ctype=None):
- Struct.__init__(self, name, ctype or type_name)
+ Record.__init__(self, name, ctype or type_name)
GLibBoxed.__init__(self, type_name, get_type)
@@ -175,10 +136,10 @@ class GLibProperty(Property):
pass
-class GLibSignal(Node):
+class GLibSignal(object):
def __init__(self, name, retval):
- Node.__init__(self, name)
+ self.name = name
self.retval = retval
self.parameters = []
self.doc = None
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
index 1178c5f..0fcec82 100644
--- a/giscanner/glibtransformer.py
+++ b/giscanner/glibtransformer.py
@@ -28,14 +28,12 @@ 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,
- Map, Varargs)
-from .transformer import Names
+ Field, VFunction, default_array_types,
+ TYPE_ANY, 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,
- type_names)
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord)
from .utils import to_underscores, to_underscores_noprefix
default_array_types['guchar*'] = TYPE_UINT8
@@ -50,18 +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',
@@ -97,11 +83,9 @@ 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 = []
@@ -110,7 +94,6 @@ class GLibTransformer(object):
self._failed_types = {}
self._boxed_types = {}
self._private_internal_types = {}
- self._validating = False
# Public API
@@ -121,18 +104,22 @@ 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):
+ print "doing initparse of %r" % (node, )
+ 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,73 +131,38 @@ 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
+ # Pair boxed structures; doesn't depend on anything else.
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):
- 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
+ # Second pass; Initial type resolution
+ for node in self._namespace.itervalues():
+ self.walk_node(node, self._pass_type_resolution, [])
- # Private
+ # Now move around the functions that are actually methods
+ #for node in self._namespace:
+ # if isinstance(node, Function):
+ # self._pair_function(node)
- 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]
+ # Third pass
+ for node in self._namespace.itervalues():
+ if isinstance(node, Record):
+ self._pass3_pair_class_record(node)
+ elif isinstance(node, Callable):
+ self._pass3_callable(node)
+ self._resolve_quarks()
- def _get_attribute(self, name):
- node = self._names.names.get(name)
- if node:
- return node[1]
- return None
+ return self._namespace
- 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
+ # Private
def _get_no_uscore_prefixed_name(self, type_name):
# Besides the straight underscore conversion, we also try
@@ -234,13 +186,28 @@ class GLibTransformer(object):
if no_uscore_prefixed not in self._uscore_type_names:
self._uscore_type_names[no_uscore_prefixed] = node
+ def _pass_type_resolution(self, node, chain):
+ if isinstance(node, Alias):
+ self._transformer.resolve_type(node.target)
+ elif isinstance(node, Callable):
+ for parameter in node.parameters:
+ self._transformer.resolve_type(parameter.type)
+ self._transformer.resolve_type(node.retval.type)
+ elif isinstance(node, Constant):
+ self._transformer.resolve_type(node.value_type)
+ elif isinstance(node, (Class, Interface, Record, Union)):
+ for field in node.fields:
+ self._transformer.resolve_type(field.type)
+
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():
+ for enum in self._namespace.itervalues():
+ if not isinstance(enum, Enum):
+ continue
type_name = enum.symbol
uscored = to_underscores(type_name).lower()
@@ -285,7 +252,11 @@ class GLibTransformer(object):
continue
return Unresolved(gtype_names[0])
- def _execute_binary(self):
+ 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
+
in_path = os.path.join(self._binary.tmpdir, 'types.txt')
f = open(in_path, 'w')
# TODO: Introspect GQuark functions
@@ -301,22 +272,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 +287,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 = 'GLib.Object'
symbol = 'g_initially_unowned_get_type'
else:
assert False
@@ -341,69 +303,25 @@ class GLibTransformer(object):
# GInitiallyUnowned so that struct offset computation
# works correctly.
gnode.fields = self._names.names['Object'][1].fields
- self._add_attribute(gnode)
+ self._namespace.append(gnode, replace=True)
self._register_internal_type(type_name, gnode)
# 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)
+ elif symbol.endswith('_error_quark'):
+ self._initparse_error_quark_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
+ if (self._namespace.name == 'GObject' and
symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
# We handle these internally, see _create_gobject
return True
@@ -421,13 +339,10 @@ class GLibTransformer(object):
self._get_type_functions.append(symbol)
return True
- def _parse_error_quark_function(self, func):
- if not func.symbol.endswith('_error_quark'):
- return False
+ def _initparse_error_quark_function(self, func):
if func.parameters:
return False
- if (func.retval.type.name != 'GLib.Quark' and
- func.retval.type.ctype != 'GQuark'):
+ if func.retval.type.target_giname != 'GLib.Quark':
return False
self._error_quark_functions.append(func)
@@ -457,7 +372,7 @@ class GLibTransformer(object):
target_klass = None
if not target_klass:
return None
- self._remove_attribute(func.name)
+ self._namespace.remove(func)
func.name = methname
target_klass.static_methods.append(func)
func.is_method = True
@@ -491,7 +406,7 @@ class GLibTransformer(object):
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:
+ if argtype in self._namespace.type_names:
node = self._names.type_names[argtype][1]
if hasattr(node, 'get_type'):
name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
@@ -550,7 +465,7 @@ class GLibTransformer(object):
name = self._transformer.remove_prefix(remove_prefix)
func.retval.type = Type(name, func.retval.type.ctype)
- self._remove_attribute(func.name)
+ self._namespace.remove(func)
# Strip namespace and object prefix: gtk_window_new -> new
func.name = func.symbol[len(prefix)+1:]
if is_method:
@@ -562,38 +477,15 @@ class GLibTransformer(object):
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[:]
-
- def _parse_callback(self, callback):
- self._add_attribute(callback)
+ self._namespace.append(record, replace=True)
def _strip_class_suffix(self, name):
if (name.endswith('Class') or
@@ -612,7 +504,7 @@ class GLibTransformer(object):
return True
return False
- def _pair_class_record(self, maybe_class):
+ def _pass3_pair_class_record(self, maybe_class):
name = self._strip_class_suffix(maybe_class.name)
if name == maybe_class.name:
return
@@ -672,13 +564,13 @@ class GLibTransformer(object):
vfunc.invoker = method
gclass_struct = GLibRecord.from_record(class_struct)
- self._remove_attribute(class_struct.name)
- self._add_attribute(gclass_struct, True)
+ self._namespace.append(gclass_struct, replace=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 +601,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']
@@ -722,28 +613,24 @@ class GLibTransformer(object):
# 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)
+ node.parent_gtype_names = xmlnode.attrib['parents'].split(',')
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'):
@@ -753,11 +640,9 @@ class GLibTransformer(object):
# 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']
@@ -784,7 +669,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,9 +678,8 @@ 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:
@@ -803,8 +687,8 @@ class GLibTransformer(object):
else:
name = '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(name, ptype, PARAM_TRANSFER_NONE)
param.transfer = 'none'
signal.parameters.append(param)
node.signals.append(signal)
@@ -870,30 +754,18 @@ 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 _walk(self, node, callback, chain):
- if not isinstance(node, Node):
- return
+ def walk_node(self, node, callback, chain):
+ assert isinstance(node, Node)
if not callback(node, chain):
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):
+ if isinstance(node, Record):
for ctor in node.constructors:
_subwalk(ctor)
for func in node.methods:
@@ -909,21 +781,11 @@ class GLibTransformer(object):
_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)
@@ -935,53 +797,9 @@ class GLibTransformer(object):
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):
+ def _pair_function(self, func):
for parser in [self._parse_constructor,
self._parse_method,
self._parse_static_method]:
@@ -991,148 +809,70 @@ class GLibTransformer(object):
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 _handle_closure(self, param, closure_idx, closure_param):
+ if (closure_param.type is TYPE_ANY and
+ closure_param.name.endswith('data')):
+ param.closure_name = closure_param.argname
+ param.closure_index = closure_idx
+ return True
+ return False
- 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 _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
+ return True
+ return False
- def _resolve_property(self, prop):
- prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
+ def _pass3_callable(self, node):
+ self._pass3_callable_callbacks(node)
+ self._pass3_callable_throws(node)
+
+ def _pass3_callable_callbacks(self, node):
+ """Check to see if we have anything that looks like a
+ callback+user_data+GDestroyNotify set."""
+ params = node.parameters
+ for i, param in enumerate(params):
+ node = self._transformer.lookup_typenode(param.type)
+ if not isinstance(node, Callback):
+ continue
- def _adjust_throws(self, func):
- if func.parameters == []:
+ # 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 _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 = func.parameters.pop()
-
+ last_param = node.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
+ node.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
+ node.parameters.append(last_param)
# Validation
@@ -1150,9 +890,7 @@ class GLibTransformer(object):
if isinstance(node.type, Varargs):
parent.introspectable = False
elif not isinstance(node.type, List) and \
- (node.type.name == 'GLib.List' or
- (self._transformer._namespace.name == 'GLib'
- and node.type.name == 'List')):
+ (node.type.target_giname == 'GLib.List'):
if isinstance(node, Parameter):
self._transformer.log_node_warning(parent,
"""Missing (element-type) annotation on argument %r""" % (node.name, ),
@@ -1162,24 +900,24 @@ class GLibTransformer(object):
"""Missing (element-type) annotation on return value""", context=parent)
parent.introspectable = False
- def _analyze_node(self, node, stack):
- if node.skip:
+ def _analyze_node(self, obj, stack):
+ if isinstance(obj, Node) and obj.skip:
return False
# Combine one-pass checks here
- self._interface_vfunc_check(node, stack)
+ self._interface_vfunc_check(obj, stack)
# Our first pass for scriptability
- self._introspectable_analysis(node, stack)
+ self._introspectable_analysis(obj, stack)
return True
- def _introspectable_pass2(self, node, stack):
- if node.skip:
+ 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(node, TypeContainer):
+ if isinstance(obj, TypeContainer):
parent = stack[-1]
- target = self._lookup_node(node.type.name)
+ target = self._transformer.lookup_typenode(obj.type)
if target and not target.introspectable:
parent.introspectable = False
return True
@@ -1187,7 +925,7 @@ class GLibTransformer(object):
# 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, [])
+ for node in self._namespace.itervalues():
+ self.walk_node(node, self._analyze_node, [])
+ for node in self._namespace.itervalues():
+ self.walk_node(node, self._introspectable_pass2, [])
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 363f6f6..4eaed05 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -34,6 +34,8 @@ 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.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
def _get_option_parser():
parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +51,9 @@ 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("", "--add-include-path",
action="append", dest="include_paths", default=[],
help="include paths for other GIR files")
@@ -101,15 +106,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 +135,18 @@ 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):
parser = GIRParser()
- parser.parse_tree(tree)
+ parser.parse(path)
writer = GIRWriter(parser.get_namespace(),
parser.get_shared_libraries(),
- parser.get_includes())
+ parser.get_includes(),
+ parser.get_pkgconfig_packages(),
+ parser.get_c_includes(),
+ parser.get_c_prefix())
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 = 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
+ sys.exit(0)
def validate(assertions, path):
from xml.etree.cElementTree import parse
@@ -237,18 +200,12 @@ def scanner_main(args):
parser = _get_option_parser()
(options, args) = parser.parse_args(args)
+ if options.passthrough_gir:
+ return passthrough_gir(options.passthrough_gir)
+
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 +234,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 +268,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
@@ -352,8 +305,7 @@ def scanner_main(args):
else:
exported_packages = options.packages
writer = Writer(namespace, shlibs, transformer.get_includes(),
- exported_packages, options.c_includes,
- transformer.get_strip_prefix())
+ exported_packages, options.c_includes)
data = writer.get_xml()
if options.output:
fd = open(options.output, "w")
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index acfc048..b024baf 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 319e43c..f23bf3c 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -22,12 +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_STRING, BASIC_GIR_TYPES)
+ Parameter, Return, Record, Field,
+ Type, Array, List, Map, Alias, Interface, Class, Node, Union,
+ Varargs, Constant, type_names, default_array_types, TYPE_STRING, TYPE_ANY,
+ BASIC_GIR_TYPES)
from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
from .girparser import GIRParser
from .odict import odict
from .sourcescanner import (
@@ -43,52 +42,27 @@ 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):
- 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)
-
-
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
@@ -105,22 +79,44 @@ class Transformer(object):
def parse(self):
nodes = []
for symbol in self.generator.get_symbols():
- try:
- node = self._traverse_one(symbol)
- except SkipError:
+ if symbol.ident.startswith('_'):
continue
- self._add_node(node)
- return self._namespace
+ node = self._traverse_one(symbol)
+ if node:
+ self._namespace.append(node)
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
@@ -195,7 +191,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)
@@ -205,26 +200,26 @@ 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)
+ 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_studly_identifier_namespace(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 _strip_namespace_func(self, name):
prefix = self._namespace.name.lower() + '_'
@@ -263,17 +258,16 @@ 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)
- elif stype == CSYMBOL_TYPE_CONST:
- return self._create_const(symbol)
+ # FIXME - we should require an annotation on
+ # #defines to have them be constants, really
+ #elif stype == CSYMBOL_TYPE_CONST:
+ # return self._create_const(symbol)
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):
@@ -323,74 +317,13 @@ 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 == '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)
+ func = Function(name, return_, parameters, False, symbol.ident)
func.add_symbol_reference(symbol)
return func
@@ -408,11 +341,10 @@ context will be used."""
elif source_type.type == CTYPE_POINTER:
value = self._create_source_type(source_type.base_type) + '*'
else:
- value = '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,20 +370,17 @@ context will be used."""
else:
derefed_name = canonical_ctype
derefed_name = self.resolve_param_type(derefed_name)
- ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+ ftype = Array(ctype, self.parse_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):
@@ -474,9 +403,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)
@@ -489,22 +418,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,51 +450,46 @@ context will be used."""
# Preserve "pointerness" of struct/union members
if (is_member and canonical.endswith('*') and
derefed_typename in BASIC_GIR_TYPES):
- return 'any'
+ 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
@@ -577,7 +499,7 @@ context will be used."""
return None
name = self._strip_namespace_func(symbol.ident)
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 = 'int'
@@ -594,7 +516,7 @@ 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)
@@ -643,7 +565,7 @@ context will be used."""
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)
@@ -654,72 +576,45 @@ context will be used."""
# Mark the 'user_data' arguments
for i, param in enumerate(parameters):
- if (param.type.name == '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:
+ pointer_stripped = typeval.ctype.replace('*', '')
+ (ns, name) = self._split_studly_identifier_namespace(pointer_stripped)
+ 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:
@@ -737,23 +632,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)
@@ -763,8 +641,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/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 497d1ee..015fd83 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -520,7 +520,7 @@ annotation_object_set_data2 (AnnotationObject *object,
/**
* annotation_object_set_data3:
* @object: a #AnnotationObject
- * @data: (array length=length) (element-type uint8): The data
+ * @data: (array length=length) (element-type guint8): The data
* @length: Length of the data
*
* Test taking a gchar * with a length, overriding the array element
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]