# GObject-Introspection - a framework for introspecting GObject libraries
# Copyright (C) 2008 Johan Dahlin
#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# This 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 program is distributed in the hope that it will be useful,
+# 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 General Public License for more details.
+# 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 General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# 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 ctypes
-from ctypes.util import find_library
-
-from . import cgobject
-from .ast import (Callback, Constant, Enum, Function, Member, Namespace,
- Parameter, Property, Return, Struct, Type, Alias,
- Union, Field, type_name_from_ctype,
- default_array_types, TYPE_UINT8, PARAM_DIRECTION_IN)
+import tempfile
+import shutil
+import subprocess
+
+from .ast import (Alias, Bitfield, Callback, Constant, Enum, Function, Member,
+ Namespace, Parameter, Property, Record, Return, Type, Union,
+ Field, VFunction, type_name_from_ctype,
+ default_array_types, TYPE_UINT8, PARAM_TRANSFER_FULL)
from .transformer import Names
from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther, type_names)
-from .utils import extract_libtool, to_underscores, to_underscores_noprefix
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord,
+ type_names)
+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',
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, transformer, noclosure=False):
self._transformer = transformer
- self._transformer.set_container_types(['GList*', 'GSList*'],
- ['GHashtable*'])
+ self._noclosure = noclosure
self._namespace_name = None
self._names = Names()
self._uscore_type_names = {}
- self._libraries = []
+ 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._noclosure = noclosure
self._validating = False
# Public API
- def add_library(self, libname):
- # For testing mainly.
- libtool_libname = 'lib' + libname + '.la'
- if os.path.exists(libtool_libname):
- found_libname = extract_libtool(libtool_libname)
- elif libname.endswith('.la'):
- found_libname = extract_libtool(libname)
- else:
- found_libname = find_library(libname)
- if not found_libname:
- raise ValueError("Failed to find library: %r" % (libname, ))
- self._libraries.append(ctypes.cdll.LoadLibrary(found_libname))
-
def _print_statistics(self):
nodes = list(self._names.names.itervalues())
print " %d nodes; %d objects, %d interfaces, %d enums" \
% (len(nodes), objectcount, ifacecount, enumcount)
- def parse(self):
+ 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:
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
try:
self._resolve_node(node)
except KeyError, e:
- print "WARNING: DELETING node %s: %s" % (node.name, e)
+ #print "WARNING: DELETING node %s: %s" % (node.name, 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, Struct):
- self._pair_class_struct(node)
+ if isinstance(node, Record):
+ self._pair_class_record(node)
for (ns, alias) in self._names.aliases.itervalues():
self._resolve_alias(alias)
-
- self._print_statistics()
+ self._resolve_quarks()
# Fourth pass: ensure all types are known
if not self._noclosure:
- self._validate(nodes)
+ self._resolve_types(nodes)
+
+ #self._validate(nodes)
# Create a new namespace with what we found
- namespace = Namespace(namespace.name, namespace.version)
+ 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 node[1]
return None
- def _register_internal_type(self, type_name, node):
- self._names.type_names[type_name] = (None, node)
- uscored = to_underscores(type_name).lower()
- self._uscore_type_names[uscored] = node
+ 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)]
- no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
- self._uscore_type_names[no_uscore_prefixed] = node
+ return (prefix + '_' + to_underscores(suffix)).lower()
- # Helper functions
+ 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:
+ print "WARNING: " + \
+ "Couldn't find corresponding enumeration for %s" % \
+ (node.symbol, )
- def _type_from_gtype(self, type_id):
- ctype = cgobject.type_name(type_id)
- type_name = type_name_from_ctype(ctype)
- type_name = type_name.replace('*', '')
- type_name = self._resolve_type_name(type_name)
- return Type(type_name, ctype)
+ # Helper functions
def _resolve_gtypename(self, gtype_name):
try:
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'
- else:
- type_id = cgobject.type_from_name(type_name)
- parent_type_name = cgobject.type_name(
- cgobject.type_parent(type_id))
+ elif type_name == 'GInitiallyUnowned':
+ parent_type_name = 'GObject'
parent_gitype = self._resolve_gtypename(parent_type_name)
- symbol = to_underscores(type_name).lower() + '_get_type'
- node = GLibObject(node.name, parent_gitype, type_name, symbol, True)
- type_id = cgobject.TYPE_OBJECT
- self._introspect_properties(node, type_id)
- self._introspect_signals(node, type_id)
- self._add_attribute(node)
- self._register_internal_type(type_name, node)
+ 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, Struct):
- self._parse_struct(node)
+ elif isinstance(node, Record):
+ self._parse_record(node)
elif isinstance(node, Callback):
self._parse_callback(node)
elif isinstance(node, 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)
return
if self._parse_get_type_function(func):
return
+ if self._parse_error_quark_function(func):
+ return
self._add_attribute(func)
if self._namespace_name == 'GLib':
# No GObjects in GLib
return False
+ if (self._namespace_name == 'GObject' and
+ symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
+ # 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',
print ("Warning: *_get_type function returns '%r'"
", not GObject.Type") % (func.retval.type.name, )
return False
- if func.parameters:
- return False
- if not self._libraries:
- print "Warning: No libraries loaded, cannot call %s" % (symbol, )
- return False
+ self._get_type_functions.append(symbol)
+ return True
- for library in self._libraries:
- try:
- func = getattr(library, symbol)
- break
- except AttributeError:
- continue
- else:
- print 'Warning: could not find symbol: %s' % symbol
- name = symbol.replace('_get_type', '')
- self._failed_types[name] = 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
- func.restype = cgobject.GType
- func.argtypes = []
- type_id = func()
- self._introspect_type(type_id, symbol)
+ self._error_quark_functions.append(func)
return True
def _name_is_internal_gtype(self, giname):
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
target_arg = func.retval
else:
target_arg = func.parameters[0]
- target_arg.type = self._resolve_param_type(target_arg.type)
if is_method:
# Methods require their first arg to be a known class
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
return None
prefix = func.symbol[:new_idx]
- klass = None
-
- def valid_matching_klass(tclass):
- if tclass is None:
- return False
- elif isinstance(klass, (GLibEnum, GLibFlags)):
- return False
- elif not isinstance(tclass, (GLibObject, GLibBoxed,
- GLibInterface)):
- return False
- else:
- return True
-
klass = self._uscore_type_names.get(prefix)
if klass is None:
#print "NOTE: No valid matching class for likely "+\
# The _uscore_type_names member holds the plain GLibBoxed
# object; we want to actually use the struct/record associated
- if isinstance(klass, GLibBoxed):
- name = self._transformer.remove_prefix(klass.type_name)
- klass = self._get_attribute(name)
+ 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
# class from the prefix
# But for now, ensure that constructor returns are always
# the most concrete class
- func.retval.type = Type(klass.name,
- self._transformer.ctype_of(klass)+'*')
+ 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
# 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_struct(self, struct):
+ 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
- struct.name in internal_names):
- self._create_gobject(struct)
+ record.name in internal_names):
+ self._create_gobject(record)
return
- elif struct.name in g_internal_names:
+ elif record.name in g_internal_names:
# Avoid duplicates
return
- node = self._names.names.get(struct.name)
+ 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(struct, replace=True)
+ self._add_attribute(record, replace=True)
+ self._register_internal_type(record.symbol, record)
return
(ns, node) = node
- node.fields = struct.fields[:]
+ 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[:]
return True
return False
- def _pair_class_struct(self, maybe_class):
+ def _pair_class_record(self, maybe_class):
name = self._strip_class_suffix(maybe_class.name)
if name == maybe_class.name:
return
- if self._arg_is_failed(maybe_class):
- print "WARNING: deleting no-type %r" % (maybe_class.name, )
- del self._names.names[maybe_class.name]
+ 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
if isinstance(field, Field):
field.writable = False
- name = self._resolve_type_name(name)
- resolved = self._transformer.remove_prefix(name)
- pair_class = self._get_attribute(resolved)
- if pair_class and isinstance(pair_class,
- (GLibObject, GLibInterface)):
- for field in maybe_class.fields[1:]:
- pair_class.fields.append(field)
- return
- name = self._transformer.remove_prefix(maybe_class.name)
- pair_class = self._get_attribute(name)
- if pair_class and isinstance(pair_class,
- (GLibObject, GLibInterface)):
-
- del self._names.names[maybe_class.name]
+ # 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)
+ 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.name
+
+ 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
+ gclass_struct.is_gtype_struct_for = name
# Introspection
- def _introspect_type(self, type_id, symbol):
- fundamental_type_id = cgobject.type_fundamental(type_id)
- if (fundamental_type_id == cgobject.TYPE_ENUM or
- fundamental_type_id == cgobject.TYPE_FLAGS):
- self._introspect_enum(fundamental_type_id, type_id, symbol)
- elif fundamental_type_id == cgobject.TYPE_OBJECT:
- self._introspect_object(type_id, symbol)
- elif fundamental_type_id == cgobject.TYPE_INTERFACE:
- self._introspect_interface(type_id, symbol)
- elif fundamental_type_id == cgobject.TYPE_BOXED:
- self._introspect_boxed(type_id, symbol)
- elif fundamental_type_id == cgobject.TYPE_BOXED:
- self._introspect_boxed(type_id, symbol)
- elif fundamental_type_id == cgobject.TYPE_POINTER:
- # FIXME: Should we do something about these?
- # GHashTable, GValue and a few other fundamentals are
- # covered here
- return
+ 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)
else:
- print 'unhandled GType: %s(%d)' % (cgobject.type_name(type_id),
- type_id)
-
- def _introspect_enum(self, ftype_id, type_id, symbol):
- type_class = cgobject.type_class_ref(type_id)
- if type_class is None:
- return
+ raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
+ def _introspect_enum(self, node):
members = []
- for enum_value in type_class.get_values():
- members.append(GLibEnumMember(enum_value.value_nick,
- enum_value.value,
- enum_value.value_name,
- enum_value.value_nick))
-
- klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
- type_name = cgobject.type_name(type_id)
+ 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, symbol)
+ 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, type_id, symbol):
- type_name = cgobject.type_name(type_id)
+ 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
- parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
- parent_gitype = self._resolve_gtypename(parent_type_name)
- is_abstract = cgobject.type_is_abstract(type_id)
+ # 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 = not not xmlnode.attrib.get('abstract', False)
node = GLibObject(
self._transformer.remove_prefix(type_name),
parent_gitype,
type_name,
- symbol, is_abstract)
- self._introspect_properties(node, type_id)
- self._introspect_signals(node, type_id)
- self._introspect_implemented_interfaces(node, type_id)
-
- # add struct fields
- struct = self._get_attribute(node.name)
- if struct is not None:
- node.fields = struct.fields
+ xmlnode.attrib['get-type'], is_abstract)
+ self._introspect_properties(node, xmlnode)
+ self._introspect_signals(node, xmlnode)
+ self._introspect_implemented_interfaces(node, xmlnode)
+
+ # add record fields
+ record = self._get_attribute(node.name)
+ if record is not None:
+ 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_struct and transformer.py)
+ # (see also _pair_class_record and transformer.py)
field.writable = False
self._add_attribute(node, replace=True)
self._register_internal_type(type_name, node)
- def _introspect_interface(self, type_id, symbol):
- type_name = cgobject.type_name(type_id)
- parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
- if parent_type_name == 'GInterface':
- parent_gitype = None
- else:
- parent_gitype = self._resolve_gtypename(parent_type_name)
+ def _introspect_interface(self, xmlnode):
+ type_name = xmlnode.attrib['name']
node = GLibInterface(
self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name, symbol)
- self._introspect_properties(node, type_id)
- self._introspect_signals(node, type_id)
+ 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 symbol.startswith('_'):
+ 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, type_id, symbol):
- type_name = cgobject.type_name(type_id)
+ 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, symbol)
+ 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, type_id):
- fundamental_type_id = cgobject.type_fundamental(type_id)
- if fundamental_type_id != cgobject.TYPE_OBJECT:
- raise AssertionError
- interfaces = cgobject.type_interfaces(type_id)
+ def _introspect_implemented_interfaces(self, node, xmlnode):
gt_interfaces = []
- for interface_typeid in interfaces:
- iname = cgobject.type_name(interface_typeid)
- gitype = self._resolve_gtypename(iname)
+ 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, type_id):
- fundamental_type_id = cgobject.type_fundamental(type_id)
- if fundamental_type_id == cgobject.TYPE_OBJECT:
- pspecs = cgobject.object_class_list_properties(type_id)
- elif fundamental_type_id == cgobject.TYPE_INTERFACE:
- pspecs = cgobject.object_interface_list_properties(type_id)
- else:
- raise AssertionError
-
- for pspec in pspecs:
- if pspec.owner_type != type_id:
- continue
- ctype = cgobject.type_name(pspec.value_type)
- readable = (pspec.flags & 1) != 0
- writable = (pspec.flags & 2) != 0
- construct = (pspec.flags & 4) != 0
- construct_only = (pspec.flags & 8) != 0
+ node.interfaces = sorted(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.name,
+ pspec.attrib['name'],
type_name_from_ctype(ctype),
readable, writable, construct, construct_only,
ctype,
))
-
- def _introspect_signals(self, node, type_id):
- for signal_info in cgobject.signal_list(type_id):
- rtype = self._type_from_gtype(signal_info.return_type)
- return_ = Return(rtype)
- signal = GLibSignal(signal_info.signal_name, return_)
- for i, parameter in enumerate(signal_info.get_params()):
+ node.properties = sorted(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, )
- ptype = self._type_from_gtype(parameter)
+ 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 = sorted(node.signals)
# Resolver
# 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)
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"
return self._transformer.resolve_param_type_full(ptype,
self._names,
**kwargs)
self._resolve_glib_object(node)
elif isinstance(node, GLibInterface):
self._resolve_glib_interface(node)
- elif isinstance(node, Struct):
- self._resolve_struct(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):
- newfunc = self._parse_constructor(func)
- if not newfunc:
- newfunc = self._parse_method(func)
- if not newfunc:
- self._resolve_function(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(newfunc)
+ self._resolve_function(func)
def _pair_boxed_type(self, boxed):
name = self._transformer.remove_prefix(boxed.type_name)
if not pair_node:
boxed_item = GLibBoxedOther(name, boxed.type_name,
boxed.get_type)
- elif isinstance(pair_node, Struct):
+ elif isinstance(pair_node, Record):
boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
boxed.get_type)
boxed_item.fields = pair_node.fields
return False
self._add_attribute(boxed_item, replace=True)
- def _resolve_struct(self, node):
+ def _resolve_record(self, node):
for field in node.fields:
self._resolve_field(field)
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):
- node.parent = self._force_resolve(node.parent)
+ # 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:
except KeyError, e:
failed.append(prop)
for fail in failed:
- print ("WARNING: Deleting object property %r (of %r)" +\
- "with unknown type") % (fail, context)
+ #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_transfer(self, param):
- # Do GLib/GObject-specific type transformations here
+ def _adjust_throws(self, func):
+ if func.parameters == []:
+ return
+
+ last_param = func.parameters.pop()
- # Default to full transfer for GObjects
- if isinstance(param, Parameter):
- is_out = (param.direction != PARAM_DIRECTION_IN)
+ # Checking type.name=='GLib.Error' generates false positives
+ # on methods that take a 'GError *'
+ if last_param.type.ctype == 'GError**':
+ func.throws = True
else:
- is_out = True
- if (is_out and
- param.transfer is None and
- (param.type.name == 'GObject.Object' or
- (self._namespace_name == 'GObject'
- and param.type.name == 'Object'))):
- param.transfer = 'full'
+ 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_transfer(func.retval)
+ self._adjust_throws(func)
def _resolve_parameters(self, parameters):
for parameter in parameters:
parameter.type = self._resolve_param_type(parameter.type)
- self._adjust_transfer(parameter)
def _resolve_field(self, field):
if isinstance(field, Callback):
self._resolve_function(field)
- return
- field.type = self._resolve_param_type(field.type)
+ 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)
- # Validation
-
- def _validate(self, nodes):
+ def _resolve_types(self, nodes):
nodes = list(self._names.names.itervalues())
i = 0
self._validating = True
while True:
initlen = len(nodes)
- print "Type resolution; pass=%d" % (i, )
+ #print "Type resolution; pass=%d" % (i, )
nodes = list(self._names.names.itervalues())
for node in nodes:
try:
i += 1
self._print_statistics()
self._validating = False
+
+ # Validation
+
+ def _validate_interface(self, iface):
+ for vfunc in iface.virtual_methods:
+ if not vfunc.invoker:
+ print ("warning: Interface %r virtual function %r " + \
+ "has no known invoker") % (iface.name, vfunc.name)
+
+ # This function is called at the very end, before we hand back the
+ # completed namespace to the writer. Add static analysis checks here.
+ def _validate(self, nodes):
+ for (name, node) in nodes:
+ if isinstance(node, GLibInterface):
+ self._validate_interface(node)