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.
24 class IntrospectablePass(object):
26 def __init__(self, transformer):
27 self._transformer = transformer
28 self._namespace = transformer.namespace
33 self._namespace.walk(self._analyze_node)
34 self._namespace.walk(self._introspectable_callable_analysis)
35 self._namespace.walk(self._introspectable_callable_analysis)
36 self._namespace.walk(self._introspectable_pass3)
38 def _interface_vfunc_check(self, node, stack):
39 if isinstance(node, glibast.GLibInterface):
40 for vfunc in node.virtual_methods:
42 message.warn_node(vfunc,
43 """Virtual function %r has no known invoker""" % (vfunc.name, ),
46 def _parameter_warning(self, parent, param, text, *args):
47 if hasattr(parent, 'symbol'):
48 prefix = '%s: ' % (parent.symbol, )
51 if isinstance(param, ast.Parameter):
52 context = "argument %s: " % (param.argname, )
54 context = "return value: "
55 message.warn_node(parent, prefix + context + text, *args)
57 def _introspectable_param_analysis(self, parent, node):
58 is_return = isinstance(node, ast.Return)
59 is_parameter = isinstance(node, ast.Parameter)
60 assert is_return or is_parameter
62 if node.type.target_giname is not None:
63 target = self._transformer.lookup_typenode(node.type)
67 if not node.type.resolved:
68 self._parameter_warning(parent, node,
69 "Unresolved type: %r" % (node.type.unresolved_string, ))
70 parent.introspectable = False
73 if isinstance(node.type, ast.Varargs):
74 parent.introspectable = False
77 if not isinstance(node.type, ast.List) and \
78 (node.type.target_giname == 'GLib.List'):
79 self._parameter_warning(parent, node, "Missing (element-type) annotation")
80 parent.introspectable = False
84 and isinstance(target, ast.Callback)
85 and not node.type.target_giname in ('GLib.DestroyNotify',
86 'Gio.AsyncReadyCallback')
87 and node.scope is None):
88 self._parameter_warning(parent, node,
89 ("Missing (scope) annotation for callback" +
90 " without GDestroyNotify (valid: %s, %s)")
91 % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
92 parent.introspectable = False
95 if is_return and isinstance(target, ast.Callback):
96 self._parameter_warning(parent, node, "Callbacks cannot be return values; use (skip)")
97 parent.introspectable = False
101 and isinstance(target, (ast.Record, ast.Union))
102 and not target.foreign
103 and not isinstance(target, glibast.GLibBoxed)):
104 if node.transfer != ast.PARAM_TRANSFER_NONE:
105 self._parameter_warning(parent, node,
106 "Invalid non-constant return of bare structure or union; register as boxed type or (skip)")
107 parent.introspectable = False
110 if node.transfer is None:
111 self._parameter_warning(parent, node, "Missing (transfer) annotation")
112 parent.introspectable = False
115 def _type_is_introspectable(self, typeval, warn=False):
116 if not typeval.resolved:
118 if isinstance(typeval, ast.TypeUnknown):
120 if isinstance(typeval, (ast.Array, ast.List)):
121 return self._type_is_introspectable(typeval.element_type)
122 elif isinstance(typeval, ast.Map):
123 return (self._type_is_introspectable(typeval.key_type)
124 and self._type_is_introspectable(typeval.value_type))
125 if typeval.target_foreign:
127 if typeval.target_fundamental:
128 if typeval.is_equiv(ast.TYPE_VALIST):
130 # Mark UCHAR as not introspectable temporarily until
131 # we're ready to land the typelib changes
132 if typeval.is_equiv(ast.TYPE_UNICHAR):
134 # These are not introspectable pending us adding
135 # larger type tags to the typelib (in theory these could
136 # be 128 bit or larger)
137 if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
138 ast.TYPE_LONG_DOUBLE)):
141 target = self._transformer.lookup_typenode(typeval)
144 return target.introspectable and (not target.skip)
146 def _analyze_node(self, obj, stack):
149 # Combine one-pass checks here
150 self._interface_vfunc_check(obj, stack)
151 # Our first pass for scriptability
152 if isinstance(obj, ast.Callable):
153 for param in obj.parameters:
154 self._introspectable_param_analysis(obj, param)
155 self._introspectable_param_analysis(obj, obj.retval)
156 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
157 for field in obj.fields:
159 if not self._type_is_introspectable(field.type):
160 field.introspectable = False
163 def _introspectable_callable_analysis(self, obj, stack):
166 # Propagate introspectability of parameters to entire functions
167 if isinstance(obj, ast.Callable):
168 for param in obj.parameters:
169 if not self._type_is_introspectable(param.type):
170 obj.introspectable = False
172 if not self._type_is_introspectable(obj.retval.type):
173 obj.introspectable = False
177 def _introspectable_pass3(self, obj, stack):
180 # Propagate introspectability for fields
181 if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
182 for field in obj.fields:
183 if field.anonymous_node:
184 if not field.anonymous_node.introspectable:
185 field.introspectable = False
187 if not self._type_is_introspectable(field.type):
188 field.introspectable = False
189 # Propagate introspectability for properties
190 if isinstance(obj, (ast.Class, ast.Interface)):
191 for prop in obj.properties:
192 if not self._type_is_introspectable(prop.type):
193 prop.introspectable = False
194 for sig in obj.signals:
195 self._introspectable_callable_analysis(sig, [obj])