summaryrefslogtreecommitdiff
path: root/giscanner/glibtransformer.py
diff options
context:
space:
mode:
Diffstat (limited to 'giscanner/glibtransformer.py')
-rw-r--r--giscanner/glibtransformer.py1205
1 files changed, 0 insertions, 1205 deletions
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
deleted file mode 100644
index 560be915..00000000
--- a/giscanner/glibtransformer.py
+++ /dev/null
@@ -1,1205 +0,0 @@
-# -*- Mode: Python -*-
-# GObject-Introspection - a framework for introspecting GObject libraries
-# Copyright (C) 2008 Johan Dahlin
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-#
-
-import os
-import sys
-import re
-import tempfile
-import shutil
-import subprocess
-
-from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
- Function, Interface, Member, Namespace, Node, Parameter,
- Property, Record, Return, Type, TypeContainer, Union,
- Field, VFunction, type_name_from_ctype, default_array_types,
- TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
- TYPE_LONG_LONG, TYPE_LONG_DOUBLE,
- Map, Varargs, type_names)
-from .transformer import Names
-from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
- GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther, GLibRecord)
-from .utils import to_underscores, to_underscores_noprefix
-
-default_array_types['guchar*'] = TYPE_UINT8
-
-# GParamFlags
-G_PARAM_READABLE = 1 << 0
-G_PARAM_WRITABLE = 1 << 1
-G_PARAM_CONSTRUCT = 1 << 2
-G_PARAM_CONSTRUCT_ONLY = 1 << 3
-G_PARAM_LAX_VALIDATION = 1 << 4
-G_PARAM_STATIC_NAME = 1 << 5
-G_PARAM_STATIC_NICK = 1 << 6
-G_PARAM_STATIC_BLURB = 1 << 7
-
-SYMBOL_BLACKLIST = [
- # These ones break GError conventions
- 'g_simple_async_result_new_from_error',
- 'g_simple_async_result_set_from_error',
- 'g_simple_async_result_propagate_error',
- 'g_simple_async_result_report_error_in_idle',
- 'gtk_print_operation_get_error',
-]
-
-SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
- [r'\w+_marshal_[A-Z]+__', ]]
-
-GET_TYPE_OVERRIDES = {
- # this is a special case, from glibtransforer.py:create_gobject
- 'intern': 'g_object_get_type',
- # this is presumably a typo, should be fixed upstream
- 'g_gstring_get_type': 'g_string_get_type',
- # this is historical cruft: there's a deprecated
- # #define gdk_window_get_type gdk_window_get_window_type
- # upstream; this method can be renamed properly upstream once
- # that deprecated alias is removed (in some future release)
- 'gdk_window_object_get_type': 'gdk_window_get_type',
-}
-
-
-class IntrospectionBinary(object):
-
- def __init__(self, args, tmpdir=None):
- self.args = args
- if tmpdir is None:
- self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
- else:
- self.tmpdir = tmpdir
-
-
-class Unresolved(object):
-
- def __init__(self, target):
- self.target = target
-
-
-class UnknownTypeError(Exception):
- pass
-
-
-class GLibTransformer(object):
-
- def __init__(self, transformer, noclosure=False):
- self._transformer = transformer
- self._noclosure = noclosure
- self._namespace_name = None
- self._names = Names()
- self._uscore_type_names = {}
- self._binary = None
- self._get_type_functions = []
- self._error_quark_functions = []
- self._gtype_data = {}
- self._failed_types = {}
- self._boxed_types = {}
- self._private_internal_types = {}
- self._validating = False
-
- # Public API
-
- def init_parse(self):
- """Do parsing steps that don't involve the introspection binary
-
- This does enough work that get_type_functions() can be called.
-
- """
-
- namespace = self._transformer.parse()
- self._namespace_name = namespace.name
- self._namespace_version = namespace.version
-
- # First pass: parsing
- for node in namespace.nodes:
- self._parse_node(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']
-
- def get_get_type_functions(self):
- return self._get_type_functions
-
- def set_introspection_binary(self, binary):
- self._binary = binary
-
- def parse(self):
- """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
-
- # Second pass: pair boxed structures
- for boxed in self._boxed_types.itervalues():
- self._pair_boxed_type(boxed)
- # Third pass: delete class structures, resolve
- # all types we now know about
- nodes = list(self._names.names.itervalues())
- for (ns, node) in nodes:
- try:
- self._resolve_node(node)
- except KeyError, e:
- self._transformer.log_node_warning(node,
-"""Unresolvable entry %r""" % (e, ))
- self._remove_attribute(node.name)
- # Another pass, since we need to have the methods parsed
- # in order to correctly modify them after class/record
- # pairing
- for (ns, node) in nodes:
- # associate GtkButtonClass with GtkButton
- if isinstance(node, Record):
- self._pair_class_record(node)
- for (ns, alias) in self._names.aliases.itervalues():
- self._resolve_alias(alias)
-
- self._resolve_quarks()
-
- # Our final pass replacing types
- self._resolve_types(nodes)
-
- # Create a new namespace with what we found
- namespace = Namespace(self._namespace_name, self._namespace_version)
- namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
- for (ns, x) in self._names.names.itervalues():
- namespace.nodes.append(x)
- return namespace
-
- # Private
-
- def _add_attribute(self, node, replace=False):
- node_name = node.name
- if (not replace) and node_name in self._names.names:
- return
- self._names.names[node_name] = (None, node)
-
- def _remove_attribute(self, name):
- del self._names.names[name]
-
- def _get_attribute(self, name):
- node = self._names.names.get(name)
- if node:
- return node[1]
- return None
-
- def _lookup_node(self, name):
- if name in type_names:
- return None
- node = self._get_attribute(name)
- if node is None:
- node = self._transformer.get_names().names.get(name)
- if node:
- return node[1]
- return node
-
- def _get_no_uscore_prefixed_name(self, type_name):
- # Besides the straight underscore conversion, we also try
- # removing the underscores from the namespace as a possible C
- # mapping; e.g. it's webkit_web_view, not web_kit_web_view
- suffix = self._transformer.remove_prefix(type_name)
- prefix = type_name[:-len(suffix)]
- return (prefix + '_' + to_underscores(suffix)).lower()
-
- def _register_internal_type(self, type_name, node):
- self._names.type_names[type_name] = (None, node)
- uscored = to_underscores(type_name).lower()
- # prefer the prefix of the get_type method, if there is one
- if hasattr(node, 'get_type'):
- uscored = GET_TYPE_OVERRIDES.get(node.get_type, node.get_type)
- uscored = uscored[:-len('_get_type')]
- self._uscore_type_names[uscored] = node
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- # since this is a guess, don't overwrite any 'real' prefix
- if no_uscore_prefixed not in self._uscore_type_names:
- self._uscore_type_names[no_uscore_prefixed] = node
-
- def _resolve_quarks(self):
- # self._uscore_type_names is an authoritative mapping of types
- # to underscored versions, since it is based on get_type() methods;
- # but only covers enums that are registered as GObject enums.
- # Create a fallback mapping based on all known enums in this module.
- uscore_enums = {}
- for enum in self._transformer.iter_enums():
- type_name = enum.symbol
- uscored = to_underscores(type_name).lower()
-
- uscore_enums[uscored] = enum
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- if no_uscore_prefixed not in uscore_enums:
- uscore_enums[no_uscore_prefixed] = enum
-
- for node in self._error_quark_functions:
- short = node.symbol[:-len('_quark')]
- if short == "g_io_error":
- # Special case; GIOError was already taken forcing GIOErrorEnum
- enum = self._names.type_names["GIOErrorEnum"][1]
- else:
- enum = self._uscore_type_names.get(short)
- if enum is None:
- enum = uscore_enums.get(short)
- if enum is not None:
- enum.error_quark = node.symbol
- else:
- self._transformer.log_node_warning(node,
-"""Couldn't find corresponding enumeration""")
-
- # Helper functions
-
- def _resolve_gtypename(self, gtype_name):
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- return Unresolved(gtype_name)
-
- def _resolve_gtypename_chain(self, gtype_names):
- """Like _resolve_gtypename, but grab the first one that resolves.
- If none of them do, return an Unresolved for the first."""
- for gtype_name in gtype_names:
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- continue
- return Unresolved(gtype_names[0])
-
- def _execute_binary(self):
- in_path = os.path.join(self._binary.tmpdir, 'types.txt')
- f = open(in_path, 'w')
- # TODO: Introspect GQuark functions
- for func in self._get_type_functions:
- f.write(func)
- f.write('\n')
- f.close()
- out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
-
- args = []
- args.extend(self._binary.args)
- args.append('--introspect-dump=%s,%s' % (in_path, out_path))
-
- # 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)
-
- def _create_gobject(self, node):
- type_name = 'G' + node.name
- if type_name == 'GObject':
- parent_gitype = None
- symbol = 'intern'
- elif type_name == 'GInitiallyUnowned':
- parent_type_name = 'GObject'
- parent_gitype = self._resolve_gtypename(parent_type_name)
- symbol = 'g_initially_unowned_get_type'
- else:
- assert False
- gnode = GLibObject(node.name, parent_gitype, type_name, symbol, True)
- if type_name == 'GObject':
- gnode.fields.extend(node.fields)
- else:
- # http://bugzilla.gnome.org/show_bug.cgi?id=569408
- # GInitiallyUnowned is actually a typedef for GObject, but
- # that's not reflected in the GIR, where it appears as a
- # subclass (as it appears in the GType hierarchy). So
- # what we do here is copy all of the GObject fields into
- # GInitiallyUnowned so that struct offset computation
- # works correctly.
- gnode.fields = self._names.names['Object'][1].fields
- self._add_attribute(gnode)
- self._register_internal_type(type_name, gnode)
-
- # 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):
- return
-
- 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':
- # No GObjects in GLib
- return False
- 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
- if func.parameters:
- return False
- # GType *_get_type(void)
- if func.retval.type.name not in ['Type',
- 'GType',
- 'GObject.Type',
- 'Gtk.Type']:
- self._transformer.log_("Warning: *_get_type function returns '%r'"
- ", not GObject.Type") % (func.retval.type.name, )
- return False
-
- self._get_type_functions.append(symbol)
- return True
-
- def _parse_error_quark_function(self, func):
- if not func.symbol.endswith('_error_quark'):
- return False
- if func.parameters:
- return False
- if (func.retval.type.name != 'GLib.Quark' and
- func.retval.type.ctype != 'GQuark'):
- return False
-
- self._error_quark_functions.append(func)
- return True
-
- def _name_is_internal_gtype(self, giname):
- try:
- node = self._get_attribute(giname)
- return isinstance(node, (GLibObject, GLibInterface,
- GLibBoxed, GLibEnum, GLibFlags))
- except KeyError, e:
- return False
-
- def _parse_static_method(self, func):
- components = func.symbol.split('_')
- if len(components) < 2:
- return None
- target_klass = None
- prefix_components = None
- methname = None
- for i in xrange(1, len(components)):
- prefix_components = '_'.join(components[0:-i])
- methname = '_'.join(components[-i:])
- target_klass = self._uscore_type_names.get(prefix_components)
- if target_klass and isinstance(target_klass, GLibObject):
- break
- target_klass = None
- if not target_klass:
- return None
- self._remove_attribute(func.name)
- func.name = methname
- target_klass.static_methods.append(func)
- func.is_method = True
- return func
-
- def _parse_method(self, func):
- if not func.parameters:
- return False
- return self._parse_method_common(func, True)
-
- def _parse_constructor(self, func):
- return self._parse_method_common(func, False)
-
- def _parse_method_common(self, func, is_method):
- # Skip _get_type functions, we processed them
- # already
- if func.symbol.endswith('_get_type'):
- return None
-
- if not is_method:
- target_arg = func.retval
- else:
- target_arg = func.parameters[0]
-
- if is_method:
- # Methods require their first arg to be a known class
- # Look at the original C type (before namespace stripping), without
- # pointers: GtkButton -> gtk_button_, so we can figure out the
- # method name
- argtype = target_arg.type.ctype.replace('*', '')
- name = self._transformer.remove_prefix(argtype)
- name_uscore = to_underscores_noprefix(name).lower()
- # prefer the prefix of the _get_type method, if there is one
- if argtype in self._names.type_names:
- node = self._names.type_names[argtype][1]
- if hasattr(node, 'get_type'):
- name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
- node.get_type)
- name_uscore = name_uscore[:-len('_get_type')]
- name_offset = func.symbol.find(name_uscore + '_')
- if name_offset < 0:
- return None
- prefix = func.symbol[:name_offset+len(name_uscore)]
- else:
- # Constructors must have _new
- # Take everything before that as class name
- new_idx = func.symbol.find('_new')
- if new_idx < 0:
- return None
- # Constructors don't return basic types
- derefed = self._transformer.follow_aliases(target_arg.type.name,
- self._names)
- if derefed in type_names:
- #print "NOTE: Rejecting constructor returning basic: %r" \
- # % (func.symbol, )
- return None
- prefix = func.symbol[:new_idx]
-
- klass = self._uscore_type_names.get(prefix)
- if klass is None:
- #print "NOTE: No valid matching class for likely "+\
- # "method or constructor: %r" % (func.symbol, )
- return None
- # Enums can't have ctors or methods
- if isinstance(klass, (GLibEnum, GLibFlags)):
- return None
-
- # The _uscore_type_names member holds the plain GLibBoxed
- # object; we want to actually use the struct/record associated
- if isinstance(klass, (Record, Union)):
- remove_prefix = klass.symbol
- else:
- remove_prefix = klass.type_name
-
- name = self._transformer.remove_prefix(remove_prefix)
- klass = self._get_attribute(name)
- if klass is None:
- return
-
- if not is_method:
- # Interfaces can't have constructors, punt to global scope
- if isinstance(klass, GLibInterface):
- #print "NOTE: Rejecting constructor for"+\
- # " interface type: %r" % (func.symbol, )
- return None
- # TODO - check that the return type is a subclass of the
- # class from the prefix
- # But for now, ensure that constructor returns are always
- # the most concrete class
- name = self._transformer.remove_prefix(remove_prefix)
- func.retval.type = Type(name, func.retval.type.ctype)
-
- self._remove_attribute(func.name)
- # Strip namespace and object prefix: gtk_window_new -> new
- func.name = func.symbol[len(prefix)+1:]
- if is_method:
- # We don't need the "this" parameter
- del func.parameters[0]
- klass.methods.append(func)
- func.is_method = True
- else:
- klass.constructors.append(func)
- return func
-
- def _parse_record(self, record):
- # This is a hack, but GObject is a rather fundamental piece so.
- internal_names = ["Object", 'InitiallyUnowned']
- g_internal_names = ["G" + x for x in internal_names]
- if (self._namespace_name == 'GObject' and
- record.name in internal_names):
- 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)
-
- def _strip_class_suffix(self, name):
- if (name.endswith('Class') or
- name.endswith('Iface')):
- return name[:-5]
- elif name.endswith('Interface'):
- return name[:-9]
- else:
- return name
-
- def _arg_is_failed(self, param):
- ctype = self._transformer.ctype_of(param).replace('*', '')
- uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
- if uscored in self._failed_types:
- print "Warning: failed type: %r" % (param, )
- return True
- return False
-
- def _pair_class_record(self, maybe_class):
- name = self._strip_class_suffix(maybe_class.name)
- if name == maybe_class.name:
- return
-
- class_struct = maybe_class
- if self._arg_is_failed(class_struct):
- print "WARNING: deleting no-type %r" % (class_struct.name, )
- del self._names.names[class_struct.name]
- return
-
- pair_class = self._get_attribute(name)
- if (not pair_class or
- not isinstance(pair_class, (GLibObject, GLibInterface))):
- return
-
- # Object class fields are assumed to be read-only
- # (see also _introspect_object and transformer.py)
- for field in maybe_class.fields:
- if isinstance(field, Field):
- field.writable = False
-
- # Loop through fields to determine which are virtual
- # functions and which are signal slots by
- # assuming everything that doesn't share a name
- # with a known signal is a virtual slot.
- for field in maybe_class.fields:
- if not isinstance(field, Callback):
- continue
- # Check the first parameter is the object
- if len(field.parameters) == 0:
- continue
- firstparam_type = field.parameters[0].type
- if firstparam_type != pair_class:
- continue
- # Also double check we don't have a signal with this
- # name.
- matched_signal = False
- for signal in pair_class.signals:
- if signal.name.replace('-', '_') == field.name:
- matched_signal = True
- break
- if matched_signal:
- continue
- vfunc = VFunction.from_callback(field)
- vfunc.inherit_file_positions(field)
- pair_class.virtual_methods.append(vfunc)
-
- # Take the set of virtual methods we found, and try
- # to pair up with any matching methods using the
- # name+signature.
- for vfunc in pair_class.virtual_methods:
- for method in pair_class.methods:
- if (method.name != vfunc.name or
- method.retval != vfunc.retval or
- method.parameters != vfunc.parameters):
- continue
- vfunc.invoker = method
-
- gclass_struct = GLibRecord.from_record(class_struct)
- self._remove_attribute(class_struct.name)
- self._add_attribute(gclass_struct, True)
- pair_class.glib_type_struct = gclass_struct
- pair_class.inherit_file_positions(class_struct)
- gclass_struct.is_gtype_struct_for = name
-
- # Introspection
-
- def _introspect_type(self, xmlnode):
- if xmlnode.tag in ('enum', 'flags'):
- self._introspect_enum(xmlnode)
- elif xmlnode.tag == 'class':
- self._introspect_object(xmlnode)
- elif xmlnode.tag == 'interface':
- self._introspect_interface(xmlnode)
- elif xmlnode.tag == 'boxed':
- self._introspect_boxed(xmlnode)
- elif xmlnode.tag == 'fundamental':
- self._introspect_fundamental(xmlnode)
- else:
- raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
-
- def _introspect_enum(self, node):
- members = []
- for member in node.findall('member'):
- # Keep the name closer to what we'd take from C by default;
- # see http://bugzilla.gnome.org/show_bug.cgi?id=575613
- name = member.attrib['nick'].replace('-', '_')
- members.append(GLibEnumMember(name,
- member.attrib['value'],
- member.attrib['name'],
- member.attrib['nick']))
-
- klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
- 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)
-
- def _introspect_object(self, xmlnode):
- type_name = xmlnode.attrib['name']
- # We handle this specially above; in 2.16 and below there
- # was no g_object_get_type, for later versions we need
- # to skip it
- if type_name == 'GObject':
- return
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- #
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
- is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
- 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)
-
- 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'])
- self._introspect_properties(node, xmlnode)
- self._introspect_signals(node, xmlnode)
- for child in xmlnode.findall('prerequisite'):
- name = child.attrib['name']
- prereq = self._resolve_gtypename(name)
- node.prerequisites.append(prereq)
- # GtkFileChooserEmbed is an example of a private interface, we
- # just filter them out
- if xmlnode.attrib['get-type'].startswith('_'):
- print "NOTICE: Marking %s as internal type" % (type_name, )
- self._private_internal_types[type_name] = node
- else:
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _introspect_boxed(self, xmlnode):
- type_name = xmlnode.attrib['name']
- # This one doesn't go in the main namespace; we associate it with
- # the struct or union
- node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
- self._boxed_types[node.type_name] = node
- self._register_internal_type(type_name, node)
-
- def _introspect_implemented_interfaces(self, node, xmlnode):
- gt_interfaces = []
- for interface in xmlnode.findall('implements'):
- gitype = self._resolve_gtypename(interface.attrib['name'])
- gt_interfaces.append(gitype)
- node.interfaces = gt_interfaces
-
- def _introspect_properties(self, node, xmlnode):
- for pspec in xmlnode.findall('property'):
- ctype = pspec.attrib['type']
- flags = int(pspec.attrib['flags'])
- readable = (flags & G_PARAM_READABLE) != 0
- writable = (flags & G_PARAM_WRITABLE) != 0
- construct = (flags & G_PARAM_CONSTRUCT) != 0
- construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
- node.properties.append(Property(
- pspec.attrib['name'],
- type_name_from_ctype(ctype),
- readable, writable, construct, construct_only,
- ctype,
- ))
- node.properties = node.properties
-
- 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
- signal = GLibSignal(signal_info.attrib['name'], return_)
- for i, parameter in enumerate(signal_info.findall('param')):
- if i == 0:
- name = 'object'
- else:
- name = 'p%s' % (i-1, )
- pctype = parameter.attrib['type']
- ptype = Type(self._transformer.parse_ctype(pctype), pctype)
- param = Parameter(name, ptype)
- param.transfer = 'none'
- signal.parameters.append(param)
- node.signals.append(signal)
- node.signals = node.signals
-
- def _introspect_fundamental(self, xmlnode):
- # We only care about types that can be instantiatable, other
- # fundamental types such as the Clutter.Fixed/CoglFixed registers
- # are not yet interesting from an introspection perspective and
- # are ignored
- if not xmlnode.attrib.get('instantiatable', False):
- return
-
- type_name = xmlnode.attrib['name']
-
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- if 'parents' in xmlnode.attrib:
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
- else:
- parent_gitype = None
- is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
- node.fundamental = True
- self._introspect_implemented_interfaces(node, xmlnode)
-
- self._add_record_fields(node)
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _add_record_fields(self, node):
- # add record fields
- record = self._get_attribute(node.name)
- if record is None:
- return
- node.fields = record.fields
- for field in node.fields:
- if isinstance(field, Field):
- # Object instance fields are assumed to be read-only
- # (see also _pair_class_record and transformer.py)
- field.writable = False
-
- def _pair_boxed_type(self, boxed):
- name = self._transformer.remove_prefix(boxed.type_name)
- pair_node = self._get_attribute(name)
- if not pair_node:
- boxed_item = GLibBoxedOther(name, boxed.type_name,
- boxed.get_type)
- elif isinstance(pair_node, Record):
- boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
- boxed.get_type)
- boxed_item.inherit_file_positions(pair_node)
- boxed_item.fields = pair_node.fields
- elif isinstance(pair_node, Union):
- boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
- boxed.get_type)
- boxed_item.inherit_file_positions(pair_node)
- boxed_item.fields = pair_node.fields
- else:
- return False
- self._add_attribute(boxed_item, replace=True)
-
- # Node walking
-
- def _walk(self, node, callback, chain):
- if not isinstance(node, Node):
- return
- 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):
- for ctor in node.constructors:
- _subwalk(ctor)
- for func in node.methods:
- _subwalk(func)
- elif isinstance(node, Field):
- _subwalk(node.type)
- elif isinstance(node, Class):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for meth in node.static_methods:
- _subwalk(meth)
- for ctor in node.constructors:
- _subwalk(ctor)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Interface):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Constant):
- _subwalk(node.type)
- elif isinstance(node, Union):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
- elif isinstance(node, GLibBoxed):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
-
- if isinstance(node, (GLibObject, GLibInterface)):
- for sig in node.signals:
- _subwalk(sig)
-
- chain.pop()
-
- # Resolver
-
- def _resolve_type_name(self, type_name, ctype=None):
- # Workaround glib bug #548689, to be included in 2.18.0
- if type_name == "GParam":
- type_name = "GObject.ParamSpec"
- res = self._transformer.resolve_type_name_full
- try:
- return res(type_name, ctype, self._names)
- except KeyError, e:
- return self._transformer.resolve_type_name(type_name, ctype)
-
- def _resolve_param_type(self, ptype, **kwargs):
- # Workaround glib bug #548689, to be included in 2.18.0
- if ptype.name == "GParam":
- ptype.name = "GObject.ParamSpec"
- elif ptype.name == "GObject.Strv":
- return Array(None, ptype.ctype, Type('utf8'))
-
- return self._transformer.resolve_param_type_full(ptype,
- self._names,
- **kwargs)
-
- def _resolve_node(self, node):
- if isinstance(node, Function):
- self._resolve_function_toplevel(node)
-
- elif isinstance(node, Callback):
- self._resolve_function(node)
- elif isinstance(node, GLibObject):
- self._resolve_glib_object(node)
- elif isinstance(node, GLibInterface):
- self._resolve_glib_interface(node)
- elif isinstance(node, Record):
- self._resolve_record(node)
- elif isinstance(node, Union):
- self._resolve_union(node)
- elif isinstance(node, Alias):
- self._resolve_alias(node)
-
- def _resolve_function_toplevel(self, func):
- for parser in [self._parse_constructor,
- self._parse_method,
- self._parse_static_method]:
- newfunc = parser(func)
- if newfunc:
- self._resolve_function(newfunc)
- return
- self._resolve_function(func)
-
- def _resolve_record(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_union(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _force_resolve(self, item, allow_unknown=False):
- if isinstance(item, Unresolved):
- if item.target in self._private_internal_types:
- return None
- try:
- return self._transformer.gtypename_to_giname(item.target,
- self._names)
- except KeyError, e:
- if allow_unknown:
- self._transformer.log_warning(
-"""Skipping unknown interface %s""" % (item.target, ))
- return None
- else:
- raise
- if item in self._private_internal_types:
- return None
- return item
-
- def _resolve_glib_interface(self, node):
- node.parent = self._force_resolve(node.parent)
- self._resolve_methods(node.methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- node.prerequisites = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.prerequisites])
-
- def _resolve_glib_object(self, node):
- # If we can't find the parent class, just drop back to GObject.
- # This supports hidden parent classes.
- # http://bugzilla.gnome.org/show_bug.cgi?id=561360
- try:
- node.parent = self._force_resolve(node.parent)
- except KeyError, e:
- #print ("WARNING: Parent %r of class %r" +\
- # " not found; using GObject") % (node.parent.target,
- # node.name)
- node.parent = self._transformer.gtypename_to_giname("GObject",
- self._names)
- node.interfaces = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.interfaces])
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
- self._resolve_methods(node.static_methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_glib_boxed(self, node):
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
-
- def _resolve_constructors(self, constructors):
- for ctor in constructors:
- self._resolve_function(ctor)
-
- def _resolve_methods(self, methods):
- for method in methods:
- self._resolve_function(method)
-
- def _resolve_signals(self, signals):
- for signal in signals:
- self._resolve_function(signal)
-
- def _resolve_properties(self, properties, context):
- failed = []
- for prop in properties:
- try:
- self._resolve_property(prop)
- except KeyError, e:
- failed.append(prop)
- for fail in failed:
- #print ("WARNING: Deleting object property %r (of %r) "
- # "with unknown type") % (fail, context)
- properties.remove(fail)
-
- def _resolve_property(self, prop):
- prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
-
- def _adjust_throws(self, func):
- if func.parameters == []:
- return
-
- last_param = func.parameters.pop()
-
- # Checking type.name=='GLib.Error' generates false positives
- # on methods that take a 'GError *'
- if last_param.type.ctype == 'GError**':
- func.throws = True
- else:
- func.parameters.append(last_param)
-
- def _resolve_function(self, func):
- self._resolve_parameters(func.parameters)
- func.retval.type = self._resolve_param_type(func.retval.type)
- self._adjust_throws(func)
-
- def _resolve_parameters(self, parameters):
- for parameter in parameters:
- parameter.type = self._resolve_param_type(parameter.type)
-
- def _resolve_field(self, field):
- if isinstance(field, Callback):
- self._resolve_function(field)
- elif isinstance(field, Record): # non-typedef'd struct
- self._resolve_record(field)
- elif isinstance(field, Union): # non-typedef'd union
- self._resolve_union(field)
- else:
- field.type = self._resolve_param_type(field.type)
-
- def _resolve_alias(self, alias):
- alias.target = self._resolve_type_name(alias.target, alias.target)
-
- def _resolve_types(self, nodes):
- nodes = list(self._names.names.itervalues())
- i = 0
- self._validating = True
- while True:
- initlen = len(nodes)
-
- nodes = list(self._names.names.itervalues())
- for node in nodes:
- try:
- self._resolve_node(node)
- except UnknownTypeError, e:
- print "WARNING: %s: Deleting %r" % (e, node)
- self._remove_attribute(node.name)
- if len(nodes) == initlen:
- break
- i += 1
- self._validating = False
-
- # Validation
-
- def _interface_vfunc_check(self, node, stack):
- if isinstance(node, GLibInterface):
- for vfunc in node.virtual_methods:
- if not vfunc.invoker:
- self._transformer.log_node_warning(vfunc,
-"""Virtual function %r has no known invoker""" % (vfunc.name, ),
- context=node)
-
- def _is_unannotated_list(self, node):
- # Already annotated
- if isinstance(node.type, List):
- return False
- if (node.type.name == 'GLib.List' or
- node.type.name == 'GLib.SList'):
- return True
- if (self._transformer._namespace.name == 'GLib' and
- (node.type.name == 'List' or
- node.type.name == 'SList')):
- return True
- return False
-
- def _introspectable_analysis(self, node, stack):
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- if node.type.name in [TYPE_LONG_LONG, TYPE_LONG_DOUBLE]:
- parent.introspectable = False
- elif isinstance(node.type, Varargs):
- parent.introspectable = False
- elif self._is_unannotated_list(node):
- if isinstance(node, Parameter):
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on argument %r""" % (node.name, ),
- context=parent)
- else:
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on return value""", context=parent)
- parent.introspectable = False
-
- def _analyze_node(self, node, stack):
- if node.skip:
- return False
- # Combine one-pass checks here
- self._interface_vfunc_check(node, stack)
- # Our first pass for scriptability
- self._introspectable_analysis(node, stack)
- return True
-
- def _introspectable_pass2(self, node, stack):
- if node.skip:
- return False
- # In the second introspectable pass, we propagate introspectablity;
- # for example, a varargs callback as an argument to a function
- # makes the whole function unintrospectable
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- target = self._lookup_node(node.type.name)
- if target and not target.introspectable:
- parent.introspectable = False
- return True
-
- # This function is called at the very end, before we hand back the
- # completed namespace to the writer. Add static analysis checks here.
- def final_analyze(self):
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._analyze_node, [])
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._introspectable_pass2, [])