summaryrefslogtreecommitdiff
path: root/giscanner/introspectablepass.py
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2010-07-27 06:16:37 -0400
committerColin Walters <walters@verbum.org>2010-08-31 16:05:56 -0400
commit36aa515f1036978ced8d4ffb808260844f7229e0 (patch)
tree93e06fb105a513a394365232bb632256f109dada /giscanner/introspectablepass.py
parent552c1f1525e37a30376790151c1ba437776682c5 (diff)
downloadgobject-introspection-36aa515f1036978ced8d4ffb808260844f7229e0.tar.gz
Major rewrite
One of the first big changes in this rewrite is changing the Type object to have separate target_fundamental and target_giname properties, rather than just being strings. Previously in the scanner, it was awful because we used heuristics around strings. The ast.py is refactored so that not everything is a Node - that was a rather useless abstraction. Now, only things which can have a GIName are Node. E.g. Type and Field are no longer Node. More things were merged from glibast.py into ast.py, since it isn't a very useful split. transformer.py gains more intelligence and will e.g. turn GLib.List into a List() object earlier. The namespace processing is a lot cleaner now; since we parse the included .girs, we know the C prefix for each namespace, and have functions to parse both C type names (GtkFooBar) and symbols gtk_foo_bar into their symbols cleanly. Type resolution is much, much saner because we know Type(target_giname=Gtk.Foo) maps to the namespace Gtk. glibtransformer.py now just handles the XML processing from the dump, and a few miscellaneous things. The major heavy lifting now lives in primarytransformer.py, which is a combination of most of annotationparser.py and half of glibtransformer.py. annotationparser.py now literally just parses annotations; it's no longer in the business of e.g. guessing transfer too. finaltransformer.py is a new file which does post-analysis for "introspectability" mainly. girparser.c is fixed for some introspectable=0 processing.
Diffstat (limited to 'giscanner/introspectablepass.py')
-rw-r--r--giscanner/introspectablepass.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/giscanner/introspectablepass.py b/giscanner/introspectablepass.py
new file mode 100644
index 00000000..a8809120
--- /dev/null
+++ b/giscanner/introspectablepass.py
@@ -0,0 +1,159 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# 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.
+#
+
+from . import ast
+from . import glibast
+
+class IntrospectablePass(object):
+
+ def __init__(self, transformer):
+ self._transformer = transformer
+ self._namespace = transformer.namespace
+
+ # Public API
+
+ def validate(self):
+ self._namespace.walk(self._analyze_node)
+ self._namespace.walk(self._introspectable_callable_analysis)
+ self._namespace.walk(self._introspectable_callable_analysis)
+ self._namespace.walk(self._introspectable_pass3)
+
+ def _interface_vfunc_check(self, node, stack):
+ if isinstance(node, glibast.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 _parameter_warning(self, parent, param, text, *args):
+ if hasattr(parent, 'symbol'):
+ prefix = '%s: ' % (parent.symbol, )
+ else:
+ prefix = ''
+ if isinstance(param, ast.Parameter):
+ context = "argument %s: " % (param.argname, )
+ else:
+ context = "return value: "
+ self._transformer.log_node_warning(parent, prefix + context + text, *args)
+
+ def _introspectable_param_analysis(self, parent, node):
+ if not node.type.resolved:
+ self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+ parent.introspectable = False
+ elif isinstance(node.type, ast.Varargs):
+ parent.introspectable = False
+ elif not isinstance(node.type, ast.List) and \
+ (node.type.target_giname == 'GLib.List'):
+ self._parameter_warning(parent, node, "Missing (element-type) annotation")
+ parent.introspectable = False
+ elif node.transfer is None:
+ self._parameter_warning(parent, node, "Missing (transfer) annotation")
+ parent.introspectable = False
+
+ if isinstance(node, ast.Parameter) and node.type.target_giname:
+ target = self._transformer.lookup_typenode(node.type)
+ if (isinstance(target, ast.Callback)
+ and not target.create_type().target_giname in ('GLib.DestroyNotify',
+ 'Gio.AsyncReadyCallback')
+ and node.scope is None):
+ self._parameter_warning(parent, node,
+ ("Missing (scope) annotation for callback" +
+ " without GDestroyNotify (valid: %s, %s)")
+ % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
+ parent.introspectable = False
+
+ def _type_is_introspectable(self, typeval, warn=False):
+ if not typeval.resolved:
+ return False
+ if isinstance(typeval, (ast.Array, ast.List)):
+ return self._type_is_introspectable(typeval.element_type)
+ elif isinstance(typeval, ast.Map):
+ return (self._type_is_introspectable(typeval.key_type)
+ and self._type_is_introspectable(typeval.value_type))
+ if typeval.target_foreign:
+ return True
+ if typeval.target_fundamental:
+ if typeval.is_equiv(ast.TYPE_VALIST):
+ return False
+ # Mark UCHAR as not introspectable temporarily until
+ # we're ready to land the typelib changes
+ if typeval.is_equiv(ast.TYPE_UNICHAR):
+ return False
+ # These are not introspectable pending us adding
+ # larger type tags to the typelib (in theory these could
+ # be 128 bit or larger)
+ if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
+ ast.TYPE_LONG_DOUBLE)):
+ return False
+ return True
+ target = self._transformer.lookup_typenode(typeval)
+ if not target:
+ return False
+ return target.introspectable
+
+ def _analyze_node(self, obj, stack):
+ if obj.skip:
+ return False
+ # Combine one-pass checks here
+ self._interface_vfunc_check(obj, stack)
+ # Our first pass for scriptability
+ if isinstance(obj, ast.Callable):
+ for param in obj.parameters:
+ self._introspectable_param_analysis(obj, param)
+ self._introspectable_param_analysis(obj, obj.retval)
+ if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in obj.fields:
+ if field.type:
+ if not self._type_is_introspectable(field.type):
+ field.introspectable = False
+ return True
+
+ def _introspectable_callable_analysis(self, obj, stack):
+ if obj.skip:
+ return True
+ # Propagate introspectability of parameters to entire functions
+ if isinstance(obj, ast.Callable):
+ for param in obj.parameters:
+ if not self._type_is_introspectable(param.type):
+ obj.introspectable = False
+ return True
+ if not self._type_is_introspectable(obj.retval.type):
+ obj.introspectable = False
+ return True
+ return True
+
+ def _introspectable_pass3(self, obj, stack):
+ if obj.skip:
+ return True
+ # Propagate introspectability for fields
+ if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in obj.fields:
+ if field.anonymous_node:
+ if not field.anonymous_node.introspectable:
+ field.introspectable = False
+ else:
+ if not self._type_is_introspectable(field.type):
+ field.introspectable = False
+ # Propagate introspectability for properties
+ if isinstance(obj, (ast.Class, ast.Interface)):
+ for prop in obj.properties:
+ if not self._type_is_introspectable(prop.type):
+ prop.introspectable = False
+ return True