Imported Upstream version 1.39.3
[platform/upstream/gobject-introspection.git] / giscanner / introspectablepass.py
1 # -*- Mode: Python -*-
2 # Copyright (C) 2010 Red Hat, Inc.
3 #
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.
8 #
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.
13 #
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.
18 #
19
20 from . import ast
21 from . import message
22 from .annotationparser import TAG_RETURNS
23
24
25 class IntrospectablePass(object):
26
27     def __init__(self, transformer, blocks):
28         self._transformer = transformer
29         self._namespace = transformer.namespace
30         self._blocks = blocks
31
32     # Public API
33
34     def validate(self):
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)
42
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)):
47             return
48
49         block = None
50         if hasattr(parent, 'symbol'):
51             prefix = '%s: ' % (parent.symbol, )
52             block = self._blocks.get(parent.symbol)
53             if block:
54                 position = block.position
55         else:
56             prefix = ''
57         if isinstance(param, ast.Parameter):
58             context = "argument %s: " % (param.argname, )
59         else:
60             context = "return value: "
61             if block:
62                 return_tag = block.tags.get(TAG_RETURNS)
63                 if return_tag:
64                     position = return_tag.position
65         message.warn_node(parent, prefix + context + text,
66                           positions=position)
67
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
72
73         if node.type.target_giname is not None:
74             target = self._transformer.lookup_typenode(node.type)
75         else:
76             target = None
77
78         if node.skip:
79             return
80
81         if not node.type.resolved:
82             self._parameter_warning(parent, node,
83                                     "Unresolved type: %r" % (node.type.unresolved_string, ))
84             parent.introspectable = False
85             return
86
87         if isinstance(node.type, ast.Varargs):
88             parent.introspectable = False
89             return
90
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
95             return
96
97         if (is_parameter
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(
102                 parent,
103                 node,
104                 "Missing (scope) annotation for callback without "
105                 "GDestroyNotify (valid: %s, %s)" % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
106
107             parent.introspectable = False
108             return
109
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
113             return
114
115         if (is_return
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(
121                     parent, node,
122                     "Invalid non-constant return of bare structure or union; "
123                     "register as boxed type or (skip)")
124                 parent.introspectable = False
125             return
126
127         if node.transfer is None:
128             self._parameter_warning(parent, node, "Missing (transfer) annotation")
129             parent.introspectable = False
130             return
131
132     def _type_is_introspectable(self, typeval, warn=False):
133         if not typeval.resolved:
134             return False
135         if isinstance(typeval, ast.TypeUnknown):
136             return False
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:
143             return True
144         if typeval.target_fundamental:
145             if typeval.is_equiv(ast.TYPE_VALIST):
146                 return False
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)):
151                 return False
152             else:
153                 return True
154         target = self._transformer.lookup_typenode(typeval)
155         if not target:
156             return False
157         return target.introspectable and (not target.skip)
158
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)
162             if target is None:
163                 return
164         else:
165             return
166
167         if target.skip:
168             parent.skip = True
169
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
174         return True
175
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)
181         return True
182
183     def _analyze_node(self, obj, stack):
184         if obj.skip:
185             return False
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:
193                 if field.type:
194                     if not self._type_is_introspectable(field.type):
195                         field.introspectable = False
196         return True
197
198     def _introspectable_callable_analysis(self, obj, stack):
199         if obj.skip:
200             return False
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
206                     return True
207             if not self._type_is_introspectable(obj.retval.type):
208                 obj.introspectable = False
209                 return True
210         return True
211
212     def _introspectable_pass3(self, obj, stack):
213         if obj.skip:
214             return False
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
221                 else:
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])
231         return True
232
233     def _remove_non_reachable_backcompat_copies(self, obj, stack):
234         if obj.skip:
235             return False
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
240         return True