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.
22 from .annotationparser import TAG_RETURNS
25 class IntrospectablePass(object):
27 def __init__(self, transformer, blocks):
28 self._transformer = transformer
29 self._namespace = transformer.namespace
35 self._namespace.walk(self._introspectable_alias_analysis)
36 self._namespace.walk(self._propagate_callable_skips)
37 self._namespace.walk(self._analyze_node)
38 self._namespace.walk(self._introspectable_callable_analysis)
39 self._namespace.walk(self._introspectable_callable_analysis)
40 self._namespace.walk(self._introspectable_pass3)
41 self._namespace.walk(self._remove_non_reachable_backcompat_copies)
43 def _parameter_warning(self, parent, param, text, position=None):
44 # Suppress VFunctions and Callbacks warnings for now
45 # they cause more problems then they are worth
46 if isinstance(parent, (ast.VFunction, ast.Callback)):
50 if hasattr(parent, 'symbol'):
51 prefix = '%s: ' % (parent.symbol, )
52 block = self._blocks.get(parent.symbol)
54 position = block.position
57 if isinstance(param, ast.Parameter):
58 context = "argument %s: " % (param.argname, )
60 context = "return value: "
62 return_tag = block.tags.get(TAG_RETURNS)
64 position = return_tag.position
65 message.warn_node(parent, prefix + context + text,
68 def _introspectable_param_analysis(self, parent, node):
69 is_return = isinstance(node, ast.Return)
70 is_parameter = isinstance(node, ast.Parameter)
71 assert is_return or is_parameter
73 if node.type.target_giname is not None:
74 target = self._transformer.lookup_typenode(node.type)
81 if not node.type.resolved:
82 self._parameter_warning(parent, node,
83 "Unresolved type: %r" % (node.type.unresolved_string, ))
84 parent.introspectable = False
87 if isinstance(node.type, ast.Varargs):
88 parent.introspectable = False
91 if (isinstance(node.type, (ast.List, ast.Array))
92 and node.type.element_type == ast.TYPE_ANY):
93 self._parameter_warning(parent, node, "Missing (element-type) annotation")
94 parent.introspectable = False
98 and isinstance(target, ast.Callback)
99 and not node.type.target_giname in ('GLib.DestroyNotify', 'Gio.AsyncReadyCallback')
100 and node.scope is None):
101 self._parameter_warning(
104 "Missing (scope) annotation for callback without "
105 "GDestroyNotify (valid: %s, %s)" % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
107 parent.introspectable = False
110 if is_return and isinstance(target, ast.Callback):
111 self._parameter_warning(parent, node, "Callbacks cannot be return values; use (skip)")
112 parent.introspectable = False
116 and isinstance(target, (ast.Record, ast.Union))
117 and target.get_type is None
118 and not target.foreign):
119 if node.transfer != ast.PARAM_TRANSFER_NONE:
120 self._parameter_warning(
122 "Invalid non-constant return of bare structure or union; "
123 "register as boxed type or (skip)")
124 parent.introspectable = False
127 if node.transfer is None:
128 self._parameter_warning(parent, node, "Missing (transfer) annotation")
129 parent.introspectable = False
132 def _type_is_introspectable(self, typeval, warn=False):
133 if not typeval.resolved:
135 if isinstance(typeval, ast.TypeUnknown):
137 if isinstance(typeval, (ast.Array, ast.List)):
138 return self._type_is_introspectable(typeval.element_type)
139 elif isinstance(typeval, ast.Map):
140 return (self._type_is_introspectable(typeval.key_type)
141 and self._type_is_introspectable(typeval.value_type))
142 if typeval.target_foreign:
144 if typeval.target_fundamental:
145 if typeval.is_equiv(ast.TYPE_VALIST):
147 # These are not introspectable pending us adding
148 # larger type tags to the typelib (in theory these could
149 # be 128 bit or larger)
150 elif typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG, ast.TYPE_LONG_DOUBLE)):
154 target = self._transformer.lookup_typenode(typeval)
157 return target.introspectable and (not target.skip)
159 def _propagate_parameter_skip(self, parent, node):
160 if node.type.target_giname is not None:
161 target = self._transformer.lookup_typenode(node.type)
170 def _introspectable_alias_analysis(self, obj, stack):
171 if isinstance(obj, ast.Alias):
172 if not self._type_is_introspectable(obj.target):
173 obj.introspectable = False
176 def _propagate_callable_skips(self, obj, stack):
177 if isinstance(obj, ast.Callable):
178 for param in obj.parameters:
179 self._propagate_parameter_skip(obj, param)
180 self._propagate_parameter_skip(obj, obj.retval)
183 def _analyze_node(self, obj, stack):
186 # Our first pass for scriptability
187 if isinstance(obj, ast.Callable):
188 for param in obj.parameters:
189 self._introspectable_param_analysis(obj, param)
190 self._introspectable_param_analysis(obj, obj.retval)
191 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
192 for field in obj.fields:
194 if not self._type_is_introspectable(field.type):
195 field.introspectable = False
198 def _introspectable_callable_analysis(self, obj, stack):
201 # Propagate introspectability of parameters to entire functions
202 if isinstance(obj, ast.Callable):
203 for param in obj.parameters:
204 if not self._type_is_introspectable(param.type):
205 obj.introspectable = False
207 if not self._type_is_introspectable(obj.retval.type):
208 obj.introspectable = False
212 def _introspectable_pass3(self, obj, stack):
215 # Propagate introspectability for fields
216 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
217 for field in obj.fields:
218 if field.anonymous_node:
219 if not field.anonymous_node.introspectable:
220 field.introspectable = False
222 if not self._type_is_introspectable(field.type):
223 field.introspectable = False
224 # Propagate introspectability for properties
225 if isinstance(obj, (ast.Class, ast.Interface)):
226 for prop in obj.properties:
227 if not self._type_is_introspectable(prop.type):
228 prop.introspectable = False
229 for sig in obj.signals:
230 self._introspectable_callable_analysis(sig, [obj])
233 def _remove_non_reachable_backcompat_copies(self, obj, stack):
236 if (isinstance(obj, ast.Function) and obj.moved_to is not None):
237 # remove functions that are not introspectable
238 if not obj.introspectable:
239 obj.internal_skipped = True