1#
2# Nodes used as utilities and support for transforms etc.
3# These often make up sets including both Nodes and ExprNodes
4# so it is convenient to have them in a seperate module.
5#
6
7import Nodes
8import ExprNodes
9from Nodes import Node
10from ExprNodes import AtomicExprNode
11from PyrexTypes import c_ptr_type
12
13class TempHandle(object):
14    # THIS IS DEPRECATED, USE LetRefNode instead
15    temp = None
16    needs_xdecref = False
17    def __init__(self, type, needs_cleanup=None):
18        self.type = type
19        if needs_cleanup is None:
20            self.needs_cleanup = type.is_pyobject
21        else:
22            self.needs_cleanup = needs_cleanup
23
24    def ref(self, pos):
25        return TempRefNode(pos, handle=self, type=self.type)
26
27    def cleanup_ref(self, pos):
28        return CleanupTempRefNode(pos, handle=self, type=self.type)
29
30class TempRefNode(AtomicExprNode):
31    # THIS IS DEPRECATED, USE LetRefNode instead
32    # handle   TempHandle
33
34    def analyse_types(self, env):
35        assert self.type == self.handle.type
36        return self
37
38    def analyse_target_types(self, env):
39        assert self.type == self.handle.type
40        return self
41
42    def analyse_target_declaration(self, env):
43        pass
44
45    def calculate_result_code(self):
46        result = self.handle.temp
47        if result is None: result = "<error>" # might be called and overwritten
48        return result
49
50    def generate_result_code(self, code):
51        pass
52
53    def generate_assignment_code(self, rhs, code):
54        if self.type.is_pyobject:
55            rhs.make_owned_reference(code)
56            # TODO: analyse control flow to see if this is necessary
57            code.put_xdecref(self.result(), self.ctype())
58        code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
59        rhs.generate_post_assignment_code(code)
60        rhs.free_temps(code)
61
62class CleanupTempRefNode(TempRefNode):
63    # THIS IS DEPRECATED, USE LetRefNode instead
64    # handle   TempHandle
65
66    def generate_assignment_code(self, rhs, code):
67        pass
68
69    def generate_execution_code(self, code):
70        if self.type.is_pyobject:
71            code.put_decref_clear(self.result(), self.type)
72            self.handle.needs_cleanup = False
73
74class TempsBlockNode(Node):
75    # THIS IS DEPRECATED, USE LetNode instead
76
77    """
78    Creates a block which allocates temporary variables.
79    This is used by transforms to output constructs that need
80    to make use of a temporary variable. Simply pass the types
81    of the needed temporaries to the constructor.
82
83    The variables can be referred to using a TempRefNode
84    (which can be constructed by calling get_ref_node).
85    """
86
87    # temps   [TempHandle]
88    # body    StatNode
89
90    child_attrs = ["body"]
91
92    def generate_execution_code(self, code):
93        for handle in self.temps:
94            handle.temp = code.funcstate.allocate_temp(
95                handle.type, manage_ref=handle.needs_cleanup)
96        self.body.generate_execution_code(code)
97        for handle in self.temps:
98            if handle.needs_cleanup:
99                if handle.needs_xdecref:
100                    code.put_xdecref_clear(handle.temp, handle.type)
101                else:
102                    code.put_decref_clear(handle.temp, handle.type)
103            code.funcstate.release_temp(handle.temp)
104
105    def analyse_declarations(self, env):
106        self.body.analyse_declarations(env)
107
108    def analyse_expressions(self, env):
109        self.body = self.body.analyse_expressions(env)
110        return self
111
112    def generate_function_definitions(self, env, code):
113        self.body.generate_function_definitions(env, code)
114
115    def annotate(self, code):
116        self.body.annotate(code)
117
118
119class ResultRefNode(AtomicExprNode):
120    # A reference to the result of an expression.  The result_code
121    # must be set externally (usually a temp name).
122
123    subexprs = []
124    lhs_of_first_assignment = False
125
126    def __init__(self, expression=None, pos=None, type=None, may_hold_none=True, is_temp=False):
127        self.expression = expression
128        self.pos = None
129        self.may_hold_none = may_hold_none
130        if expression is not None:
131            self.pos = expression.pos
132            if hasattr(expression, "type"):
133                self.type = expression.type
134        if pos is not None:
135            self.pos = pos
136        if type is not None:
137            self.type = type
138        if is_temp:
139            self.is_temp = True
140        assert self.pos is not None
141
142    def clone_node(self):
143        # nothing to do here
144        return self
145
146    def type_dependencies(self, env):
147        if self.expression:
148            return self.expression.type_dependencies(env)
149        else:
150            return ()
151
152    def analyse_types(self, env):
153        if self.expression is not None:
154            self.type = self.expression.type
155        return self
156
157    def infer_type(self, env):
158        if self.type is not None:
159            return self.type
160        if self.expression is not None:
161            if self.expression.type is not None:
162                return self.expression.type
163            return self.expression.infer_type(env)
164        assert False, "cannot infer type of ResultRefNode"
165
166    def may_be_none(self):
167        if not self.type.is_pyobject:
168            return False
169        return self.may_hold_none
170
171    def _DISABLED_may_be_none(self):
172        # not sure if this is safe - the expression may not be the
173        # only value that gets assigned
174        if self.expression is not None:
175            return self.expression.may_be_none()
176        if self.type is not None:
177            return self.type.is_pyobject
178        return True # play safe
179
180    def is_simple(self):
181        return True
182
183    def result(self):
184        try:
185            return self.result_code
186        except AttributeError:
187            if self.expression is not None:
188                self.result_code = self.expression.result()
189        return self.result_code
190
191    def generate_evaluation_code(self, code):
192        pass
193
194    def generate_result_code(self, code):
195        pass
196
197    def generate_disposal_code(self, code):
198        pass
199
200    def generate_assignment_code(self, rhs, code):
201        if self.type.is_pyobject:
202            rhs.make_owned_reference(code)
203            if not self.lhs_of_first_assignment:
204                code.put_decref(self.result(), self.ctype())
205        code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
206        rhs.generate_post_assignment_code(code)
207        rhs.free_temps(code)
208
209    def allocate_temps(self, env):
210        pass
211
212    def release_temp(self, env):
213        pass
214
215    def free_temps(self, code):
216        pass
217
218
219class LetNodeMixin:
220    def set_temp_expr(self, lazy_temp):
221        self.lazy_temp = lazy_temp
222        self.temp_expression = lazy_temp.expression
223
224    def setup_temp_expr(self, code):
225        self.temp_expression.generate_evaluation_code(code)
226        self.temp_type = self.temp_expression.type
227        if self.temp_type.is_array:
228            self.temp_type = c_ptr_type(self.temp_type.base_type)
229        self._result_in_temp = self.temp_expression.result_in_temp()
230        if self._result_in_temp:
231            self.temp = self.temp_expression.result()
232        else:
233            self.temp_expression.make_owned_reference(code)
234            self.temp = code.funcstate.allocate_temp(
235                self.temp_type, manage_ref=True)
236            code.putln("%s = %s;" % (self.temp, self.temp_expression.result()))
237            self.temp_expression.generate_disposal_code(code)
238            self.temp_expression.free_temps(code)
239        self.lazy_temp.result_code = self.temp
240
241    def teardown_temp_expr(self, code):
242        if self._result_in_temp:
243            self.temp_expression.generate_disposal_code(code)
244            self.temp_expression.free_temps(code)
245        else:
246            if self.temp_type.is_pyobject:
247                code.put_decref_clear(self.temp, self.temp_type)
248            code.funcstate.release_temp(self.temp)
249
250class EvalWithTempExprNode(ExprNodes.ExprNode, LetNodeMixin):
251    # A wrapper around a subexpression that moves an expression into a
252    # temp variable and provides it to the subexpression.
253
254    subexprs = ['temp_expression', 'subexpression']
255
256    def __init__(self, lazy_temp, subexpression):
257        self.set_temp_expr(lazy_temp)
258        self.pos = subexpression.pos
259        self.subexpression = subexpression
260        # if called after type analysis, we already know the type here
261        self.type = self.subexpression.type
262
263    def infer_type(self, env):
264        return self.subexpression.infer_type(env)
265
266    def result(self):
267        return self.subexpression.result()
268
269    def analyse_types(self, env):
270        self.temp_expression = self.temp_expression.analyse_types(env)
271        self.subexpression = self.subexpression.analyse_types(env)
272        self.type = self.subexpression.type
273        return self
274
275    def free_subexpr_temps(self, code):
276        self.subexpression.free_temps(code)
277
278    def generate_subexpr_disposal_code(self, code):
279        self.subexpression.generate_disposal_code(code)
280
281    def generate_evaluation_code(self, code):
282        self.setup_temp_expr(code)
283        self.subexpression.generate_evaluation_code(code)
284        self.teardown_temp_expr(code)
285
286LetRefNode = ResultRefNode
287
288class LetNode(Nodes.StatNode, LetNodeMixin):
289    # Implements a local temporary variable scope. Imagine this
290    # syntax being present:
291    # let temp = VALUE:
292    #     BLOCK (can modify temp)
293    #     if temp is an object, decref
294    #
295    # Usually used after analysis phase, but forwards analysis methods
296    # to its children
297
298    child_attrs = ['temp_expression', 'body']
299
300    def __init__(self, lazy_temp, body):
301        self.set_temp_expr(lazy_temp)
302        self.pos = body.pos
303        self.body = body
304
305    def analyse_declarations(self, env):
306        self.temp_expression.analyse_declarations(env)
307        self.body.analyse_declarations(env)
308
309    def analyse_expressions(self, env):
310        self.temp_expression = self.temp_expression.analyse_expressions(env)
311        self.body = self.body.analyse_expressions(env)
312        return self
313
314    def generate_execution_code(self, code):
315        self.setup_temp_expr(code)
316        self.body.generate_execution_code(code)
317        self.teardown_temp_expr(code)
318
319    def generate_function_definitions(self, env, code):
320        self.temp_expression.generate_function_definitions(env, code)
321        self.body.generate_function_definitions(env, code)
322
323
324class TempResultFromStatNode(ExprNodes.ExprNode):
325    # An ExprNode wrapper around a StatNode that executes the StatNode
326    # body.  Requires a ResultRefNode that it sets up to refer to its
327    # own temp result.  The StatNode must assign a value to the result
328    # node, which then becomes the result of this node.
329
330    subexprs = []
331    child_attrs = ['body']
332
333    def __init__(self, result_ref, body):
334        self.result_ref = result_ref
335        self.pos = body.pos
336        self.body = body
337        self.type = result_ref.type
338        self.is_temp = 1
339
340    def analyse_declarations(self, env):
341        self.body.analyse_declarations(env)
342
343    def analyse_types(self, env):
344        self.body = self.body.analyse_expressions(env)
345        return self
346
347    def generate_result_code(self, code):
348        self.result_ref.result_code = self.result()
349        self.body.generate_execution_code(code)
350