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
24 class IntrospectablePass(object):
26 def __init__(self, transformer, blocks):
27 self._transformer = transformer
28 self._namespace = transformer.namespace
34 self._namespace.walk(self._introspectable_alias_analysis)
35 self._namespace.walk(self._propagate_callable_skips)
36 self._namespace.walk(self._analyze_node)
37 self._namespace.walk(self._introspectable_callable_analysis)
38 self._namespace.walk(self._introspectable_callable_analysis)
39 self._namespace.walk(self._introspectable_pass3)
40 self._namespace.walk(self._remove_non_reachable_backcompat_copies)
42 def _parameter_warning(self, parent, param, text, position=None):
43 # Suppress VFunctions and Callbacks warnings for now
44 # they cause more problems then they are worth
45 if isinstance(parent, (ast.VFunction, ast.Callback)):
49 if hasattr(parent, 'symbol'):
50 prefix = '%s: ' % (parent.symbol, )
51 block = self._blocks.get(parent.symbol)
53 position = block.position
56 if isinstance(param, ast.Parameter):
57 context = "argument %s: " % (param.argname, )
59 context = "return value: "
61 return_tag = block.get_tag(TAG_RETURNS)
63 position = return_tag.position
64 message.warn_node(parent, prefix + context + text,
67 def _introspectable_param_analysis(self, parent, node):
68 is_return = isinstance(node, ast.Return)
69 is_parameter = isinstance(node, ast.Parameter)
70 assert is_return or is_parameter
72 if node.type.target_giname is not None:
73 target = self._transformer.lookup_typenode(node.type)
80 if not node.type.resolved:
81 self._parameter_warning(parent, node,
82 "Unresolved type: %r" % (node.type.unresolved_string, ))
83 parent.introspectable = False
86 if isinstance(node.type, ast.Varargs):
87 parent.introspectable = False
90 if (isinstance(node.type, (ast.List, ast.Array))
91 and node.type.element_type == ast.TYPE_ANY):
92 self._parameter_warning(parent, node, "Missing (element-type) annotation")
93 parent.introspectable = False
97 and isinstance(target, ast.Callback)
98 and not node.type.target_giname in ('GLib.DestroyNotify',
99 'Gio.AsyncReadyCallback')
100 and node.scope is None):
101 self._parameter_warning(parent, node,
102 ("Missing (scope) annotation for callback" +
103 " without GDestroyNotify (valid: %s, %s)")
104 % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
105 parent.introspectable = False
108 if is_return and isinstance(target, ast.Callback):
109 self._parameter_warning(parent, node, "Callbacks cannot be return values; use (skip)")
110 parent.introspectable = False
114 and isinstance(target, (ast.Record, ast.Union))
115 and target.get_type is None
116 and not target.foreign):
117 if node.transfer != ast.PARAM_TRANSFER_NONE:
118 self._parameter_warning(parent, node,
119 "Invalid non-constant return of bare structure or union; register as boxed type or (skip)")
120 parent.introspectable = False
123 if node.transfer is None:
124 self._parameter_warning(parent, node, "Missing (transfer) annotation")
125 parent.introspectable = False
128 def _type_is_introspectable(self, typeval, warn=False):
129 if not typeval.resolved:
131 if isinstance(typeval, ast.TypeUnknown):
133 if isinstance(typeval, (ast.Array, ast.List)):
134 return self._type_is_introspectable(typeval.element_type)
135 elif isinstance(typeval, ast.Map):
136 return (self._type_is_introspectable(typeval.key_type)
137 and self._type_is_introspectable(typeval.value_type))
138 if typeval.target_foreign:
140 if typeval.target_fundamental:
141 if typeval.is_equiv(ast.TYPE_VALIST):
143 # These are not introspectable pending us adding
144 # larger type tags to the typelib (in theory these could
145 # be 128 bit or larger)
146 if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
147 ast.TYPE_LONG_DOUBLE)):
150 target = self._transformer.lookup_typenode(typeval)
153 return target.introspectable and (not target.skip)
155 def _propagate_parameter_skip(self, parent, node):
156 if node.type.target_giname is not None:
157 target = self._transformer.lookup_typenode(node.type)
166 def _introspectable_alias_analysis(self, obj, stack):
167 if isinstance(obj, ast.Alias):
168 if not self._type_is_introspectable(obj.target):
169 obj.introspectable = False
172 def _propagate_callable_skips(self, obj, stack):
173 if isinstance(obj, ast.Callable):
174 for param in obj.parameters:
175 self._propagate_parameter_skip(obj, param)
176 self._propagate_parameter_skip(obj, obj.retval)
179 def _analyze_node(self, obj, stack):
182 # Our first pass for scriptability
183 if isinstance(obj, ast.Callable):
184 for param in obj.parameters:
185 self._introspectable_param_analysis(obj, param)
186 self._introspectable_param_analysis(obj, obj.retval)
187 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
188 for field in obj.fields:
190 if not self._type_is_introspectable(field.type):
191 field.introspectable = False
194 def _introspectable_callable_analysis(self, obj, stack):
197 # Propagate introspectability of parameters to entire functions
198 if isinstance(obj, ast.Callable):
199 for param in obj.parameters:
200 if not self._type_is_introspectable(param.type):
201 obj.introspectable = False
203 if not self._type_is_introspectable(obj.retval.type):
204 obj.introspectable = False
208 def _introspectable_pass3(self, obj, stack):
211 # Propagate introspectability for fields
212 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
213 for field in obj.fields:
214 if field.anonymous_node:
215 if not field.anonymous_node.introspectable:
216 field.introspectable = False
218 if not self._type_is_introspectable(field.type):
219 field.introspectable = False
220 # Propagate introspectability for properties
221 if isinstance(obj, (ast.Class, ast.Interface)):
222 for prop in obj.properties:
223 if not self._type_is_introspectable(prop.type):
224 prop.introspectable = False
225 for sig in obj.signals:
226 self._introspectable_callable_analysis(sig, [obj])
229 def _remove_non_reachable_backcompat_copies(self, obj, stack):
232 if (isinstance(obj, ast.Function)
233 and not obj.introspectable
234 and obj.moved_to is not None):
235 self._namespace.remove(obj)