diff options
author | Colin Walters <walters@verbum.org> | 2010-06-16 20:34:18 -0400 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2010-06-16 21:58:18 -0400 |
commit | 074192b89c6afcdd7f062f03989972e44334b8bf (patch) | |
tree | b3334fc386176b9d3adb1579319e6661604a3710 /giscanner/glibtransformer.py | |
parent | 59b084e18e8826613de47fa69b791c72574a3900 (diff) | |
download | gobject-introspection-074192b89c6afcdd7f062f03989972e44334b8bf.tar.gz |
Support introspectable=no attribute, add warnings framework
This work allows us to move closer to replacing gtk-doc, among other
things. We add a generic attribute "introspectable", and inside the
typelib compiler if we see "introspectable=no", we don't put it in the
typelib. This replaces the hackish pre-filter for varargs with a much
more generic mechanism.
The varargs is now handled in the scanner, and we emit
introspectable=no for them.
Add generic metadata to Node with references to file/line/column,
which currently comes from symbols.
Add scanner options --Wall and --Werror.
Diffstat (limited to 'giscanner/glibtransformer.py')
-rw-r--r-- | giscanner/glibtransformer.py | 210 |
1 files changed, 154 insertions, 56 deletions
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py index 42488251..192690ce 100644 --- a/giscanner/glibtransformer.py +++ b/giscanner/glibtransformer.py @@ -25,10 +25,12 @@ 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, Array) +from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum, + Function, Interface, Member, Namespace, Node, Parameter, + Property, Record, Return, Type, TypeContainer, Union, + Field, VFunction, type_name_from_ctype, default_array_types, + TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List, + Map, Varargs) from .transformer import Names from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags, GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct, @@ -112,18 +114,6 @@ class GLibTransformer(object): # Public API - def _print_statistics(self): - nodes = list(self._names.names.itervalues()) - - def count_type(otype): - return len([x for x in nodes - if isinstance(x[1], otype)]) - objectcount = count_type(GLibObject) - ifacecount = count_type(GLibInterface) - enumcount = count_type(GLibEnum) - print " %d nodes; %d objects, %d interfaces, %d enums" \ - % (len(nodes), objectcount, ifacecount, enumcount) - def init_parse(self): """Do parsing steps that don't involve the introspection binary @@ -170,7 +160,8 @@ class GLibTransformer(object): try: self._resolve_node(node) except KeyError, e: - #print "WARNING: DELETING node %s: %s" % (node.name, 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 @@ -181,12 +172,11 @@ class GLibTransformer(object): self._pair_class_record(node) for (ns, alias) in self._names.aliases.itervalues(): self._resolve_alias(alias) + self._resolve_quarks() - # Fourth pass: ensure all types are known - if not self._noclosure: - self._resolve_types(nodes) - #self._validate(nodes) + # 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) @@ -272,9 +262,8 @@ class GLibTransformer(object): if enum is not None: enum.error_quark = node.symbol else: - print "WARNING: " + \ - "Couldn't find corresponding enumeration for %s" % \ - (node.symbol, ) + self._transformer._log_node_warning(node, +"""Couldn't find corresponding enumeration""") # Helper functions @@ -425,7 +414,7 @@ class GLibTransformer(object): 'GType', 'GObject.Type', 'Gtk.Type']: - print ("Warning: *_get_type function returns '%r'" + self._transformer.log_("Warning: *_get_type function returns '%r'" ", not GObject.Type") % (func.retval.type.name, ) return False @@ -668,6 +657,7 @@ class GLibTransformer(object): 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 @@ -685,6 +675,7 @@ class GLibTransformer(object): 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 @@ -829,6 +820,90 @@ class GLibTransformer(object): # (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) + + if isinstance(node, (GLibObject, GLibInterface)): + for sig in node.signals: + _subwalk(sig) + + chain.pop() + # Resolver def _resolve_type_name(self, type_name, ctype=None): @@ -879,24 +954,6 @@ class GLibTransformer(object): return self._resolve_function(func) - 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.fields = pair_node.fields - elif isinstance(pair_node, Union): - boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name, - boxed.get_type) - boxed_item.fields = pair_node.fields - else: - return False - self._add_attribute(boxed_item, replace=True) - def _resolve_record(self, node): for field in node.fields: self._resolve_field(field) @@ -914,8 +971,8 @@ class GLibTransformer(object): self._names) except KeyError, e: if allow_unknown: - print "WARNING: Skipping unknown interface %s" % \ - (item.target, ) + self._transformer._log_warning( +"""Skipping unknown interface %s""" % (item.target, )) return None else: raise @@ -1028,7 +1085,6 @@ class GLibTransformer(object): while True: initlen = len(nodes) - #print "Type resolution; pass=%d" % (i, ) nodes = list(self._names.names.itervalues()) for node in nodes: try: @@ -1039,20 +1095,62 @@ class GLibTransformer(object): if len(nodes) == initlen: break 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) + 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 _introspectable_analysis(self, node, stack): + if isinstance(node, TypeContainer): + parent = stack[-1] + if isinstance(node.type, Varargs): + parent.introspectable = False + elif not isinstance(node.type, List) and \ + (node.type.name == 'GLib.List' or + (self._transformer._namespace.name == 'GLib' + and node.type.name == 'List')): + 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 _validate(self, nodes): - for (name, node) in nodes: - if isinstance(node, GLibInterface): - self._validate_interface(node) + 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, []) |