diff options
Diffstat (limited to 'giscanner/glibtransformer.py')
-rw-r--r-- | giscanner/glibtransformer.py | 1205 |
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, []) |