from .ast import (Alias, Bitfield, Callback, Constant, Enum, Function, Member,
Namespace, Parameter, Property, Record, Return, Type, Union,
- Field, type_name_from_ctype,
+ 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, GLibRecord, type_names)
+ GLibBoxedUnion, GLibBoxedOther, GLibRecord,
+ type_names)
from .utils import to_underscores, to_underscores_noprefix
default_array_types['guchar*'] = TYPE_UINT8
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):
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, Record):
self._pair_class_record(node)
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(self._namespace_name, self._namespace_version)
return node[1]
return node
- 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 _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()
+
+ 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')]
- enum = self._uscore_type_names.get(short)
+ 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, )
# Helper functions
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')
return False
if func.parameters:
return False
- if func.retval.type.name not in ['GLib.Quark',
- 'GQuark']:
+ if (func.retval.type.name != 'GLib.Quark' and
+ func.retval.type.ctype != 'GQuark'):
return False
self._error_quark_functions.append(func)
target_klass = None
prefix_components = None
methname = None
- for i in xrange(1, len(components)-1):
+ 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)
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
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)
for field in maybe_class.fields:
if isinstance(field, Field):
field.writable = False
- # TODO: remove this, we should be computing vfuncs instead
- if isinstance(pair_class, GLibInterface):
- for field in maybe_class.fields[1:]:
- pair_class.fields.append(field)
+
+ # 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)
def _introspect_enum(self, node):
members = []
for member in node.findall('member'):
- members.append(GLibEnumMember(member.attrib['nick'],
+ # 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']))
# to skip it
if type_name == 'GObject':
return
- parent_type_name = xmlnode.attrib['parent']
- parent_gitype = self._resolve_gtypename(parent_type_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.
+ #
+ 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),
for interface in xmlnode.findall('implements'):
gitype = self._resolve_gtypename(interface.attrib['name'])
gt_interfaces.append(gitype)
- node.interfaces = gt_interfaces
+ node.interfaces = sorted(gt_interfaces)
def _introspect_properties(self, node, xmlnode):
for pspec in xmlnode.findall('property'):
readable, writable, construct, construct_only,
ctype,
))
+ node.properties = sorted(node.properties)
def _introspect_signals(self, node, xmlnode):
for signal_info in xmlnode.findall('signal'):
param.transfer = 'none'
signal.parameters.append(param)
node.signals.append(signal)
+ node.signals = sorted(node.signals)
# Resolver
last_param = func.parameters.pop()
- if (last_param.type.name == 'GLib.Error' or
- (self._namespace_name == 'GLib' and
- last_param.type.name == 'Error')):
+ # 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_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
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)