2 # Copyright (C) 2010 Red Hat, Inc.
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the
16 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 # Boston, MA 02111-1307, USA.
23 class IntrospectablePass(object):
25 def __init__(self, transformer):
26 self._transformer = transformer
27 self._namespace = transformer.namespace
32 self._namespace.walk(self._analyze_node)
33 self._namespace.walk(self._introspectable_callable_analysis)
34 self._namespace.walk(self._introspectable_callable_analysis)
35 self._namespace.walk(self._introspectable_pass3)
37 def _interface_vfunc_check(self, node, stack):
38 if isinstance(node, glibast.GLibInterface):
39 for vfunc in node.virtual_methods:
41 self._transformer.log_node_warning(vfunc,
42 """Virtual function %r has no known invoker""" % (vfunc.name, ),
45 def _parameter_warning(self, parent, param, text, *args):
46 if hasattr(parent, 'symbol'):
47 prefix = '%s: ' % (parent.symbol, )
50 if isinstance(param, ast.Parameter):
51 context = "argument %s: " % (param.argname, )
53 context = "return value: "
54 self._transformer.log_node_warning(parent, prefix + context + text, *args)
56 def _introspectable_param_analysis(self, parent, node):
57 is_return = isinstance(node, ast.Return)
58 is_parameter = isinstance(node, ast.Parameter)
59 assert is_return or is_parameter
61 if node.type.target_giname is not None:
62 target = self._transformer.lookup_typenode(node.type)
66 if not node.type.resolved:
67 self._parameter_warning(parent, node,
68 "Unresolved type: %r" % (node.type.unresolved_string, ))
69 parent.introspectable = False
72 if isinstance(node.type, ast.Varargs):
73 parent.introspectable = False
76 if not isinstance(node.type, ast.List) and \
77 (node.type.target_giname == 'GLib.List'):
78 self._parameter_warning(parent, node, "Missing (element-type) annotation")
79 parent.introspectable = False
83 and isinstance(target, ast.Callback)
84 and not node.type.target_giname in ('GLib.DestroyNotify',
85 'Gio.AsyncReadyCallback')
86 and node.scope is None):
87 self._parameter_warning(parent, node,
88 ("Missing (scope) annotation for callback" +
89 " without GDestroyNotify (valid: %s, %s)")
90 % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
91 parent.introspectable = False
94 if is_return and isinstance(target, ast.Callback):
95 self._parameter_warning(parent, node, "Callbacks cannot be return values; use (skip)")
96 parent.introspectable = False
100 and isinstance(target, (ast.Record, ast.Union))
101 and not target.foreign
102 and not isinstance(target, glibast.GLibBoxed)):
103 if node.transfer != ast.PARAM_TRANSFER_NONE:
104 self._parameter_warning(parent, node,
105 "Invalid non-constant return of bare structure or union; register as boxed type or (skip)")
106 parent.introspectable = False
109 if node.transfer is None:
110 self._parameter_warning(parent, node, "Missing (transfer) annotation")
111 parent.introspectable = False
114 def _type_is_introspectable(self, typeval, warn=False):
115 if not typeval.resolved:
117 if isinstance(typeval, ast.TypeUnknown):
119 if isinstance(typeval, (ast.Array, ast.List)):
120 return self._type_is_introspectable(typeval.element_type)
121 elif isinstance(typeval, ast.Map):
122 return (self._type_is_introspectable(typeval.key_type)
123 and self._type_is_introspectable(typeval.value_type))
124 if typeval.target_foreign:
126 if typeval.target_fundamental:
127 if typeval.is_equiv(ast.TYPE_VALIST):
129 # Mark UCHAR as not introspectable temporarily until
130 # we're ready to land the typelib changes
131 if typeval.is_equiv(ast.TYPE_UNICHAR):
133 # These are not introspectable pending us adding
134 # larger type tags to the typelib (in theory these could
135 # be 128 bit or larger)
136 if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
137 ast.TYPE_LONG_DOUBLE)):
140 target = self._transformer.lookup_typenode(typeval)
143 return target.introspectable and (not target.skip)
145 def _analyze_node(self, obj, stack):
148 # Combine one-pass checks here
149 self._interface_vfunc_check(obj, stack)
150 # Our first pass for scriptability
151 if isinstance(obj, ast.Callable):
152 for param in obj.parameters:
153 self._introspectable_param_analysis(obj, param)
154 self._introspectable_param_analysis(obj, obj.retval)
155 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
156 for field in obj.fields:
158 if not self._type_is_introspectable(field.type):
159 field.introspectable = False
162 def _introspectable_callable_analysis(self, obj, stack):
165 # Propagate introspectability of parameters to entire functions
166 if isinstance(obj, ast.Callable):
167 for param in obj.parameters:
168 if not self._type_is_introspectable(param.type):
169 obj.introspectable = False
171 if not self._type_is_introspectable(obj.retval.type):
172 obj.introspectable = False
176 def _introspectable_pass3(self, obj, stack):
179 # Propagate introspectability for fields
180 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
181 for field in obj.fields:
182 if field.anonymous_node:
183 if not field.anonymous_node.introspectable:
184 field.introspectable = False
186 if not self._type_is_introspectable(field.type):
187 field.introspectable = False
188 # Propagate introspectability for properties
189 if isinstance(obj, (ast.Class, ast.Interface)):
190 for prop in obj.properties:
191 if not self._type_is_introspectable(prop.type):
192 prop.introspectable = False
193 for sig in obj.signals:
194 self._introspectable_callable_analysis(sig, [obj])