1#     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2#
3#     Part of "Nuitka", an optimizing Python compiler that is compatible and
4#     integrates with CPython, but also works on its own.
5#
6#     Licensed under the Apache License, Version 2.0 (the "License");
7#     you may not use this file except in compliance with the License.
8#     You may obtain a copy of the License at
9#
10#        http://www.apache.org/licenses/LICENSE-2.0
11#
12#     Unless required by applicable law or agreed to in writing, software
13#     distributed under the License is distributed on an "AS IS" BASIS,
14#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#     See the License for the specific language governing permissions and
16#     limitations under the License.
17#
18""" Reformulation of "exec" statements
19
20Consult the developer manual for information. TODO: Add ability to sync
21source code comments with developer manual sections.
22
23"""
24
25from nuitka.nodes.AssignNodes import (
26    StatementAssignmentVariable,
27    StatementReleaseVariable,
28)
29from nuitka.nodes.BuiltinRefNodes import ExpressionBuiltinExceptionRef
30from nuitka.nodes.ComparisonNodes import ExpressionComparisonIs
31from nuitka.nodes.ConditionalNodes import (
32    ExpressionConditional,
33    makeStatementConditional,
34)
35from nuitka.nodes.ConstantRefNodes import (
36    ExpressionConstantNoneRef,
37    makeConstantRefNode,
38)
39from nuitka.nodes.ExceptionNodes import StatementRaiseException
40from nuitka.nodes.ExecEvalNodes import StatementExec, StatementLocalsDictSync
41from nuitka.nodes.GlobalsLocalsNodes import ExpressionBuiltinGlobals
42from nuitka.nodes.NodeMakingHelpers import makeExpressionBuiltinLocals
43from nuitka.nodes.VariableRefNodes import ExpressionTempVariableRef
44
45from .ReformulationTryFinallyStatements import makeTryFinallyStatement
46from .TreeHelpers import (
47    buildNode,
48    getKind,
49    makeStatementsSequence,
50    makeStatementsSequenceFromStatement,
51    makeStatementsSequenceFromStatements,
52)
53
54
55def wrapEvalGlobalsAndLocals(
56    provider, globals_node, locals_node, temp_scope, source_ref
57):
58    """Wrap the locals and globals arguments for "eval".
59
60    This is called from the outside, and when the node tree
61    already exists.
62    """
63
64    locals_scope = provider.getLocalsScope()
65
66    globals_keeper_variable = provider.allocateTempVariable(
67        temp_scope=temp_scope, name="globals"
68    )
69
70    locals_keeper_variable = provider.allocateTempVariable(
71        temp_scope=temp_scope, name="locals"
72    )
73
74    if locals_node is None:
75        locals_node = ExpressionConstantNoneRef(source_ref=source_ref)
76
77    if globals_node is None:
78        globals_node = ExpressionConstantNoneRef(source_ref=source_ref)
79
80    post_statements = []
81
82    if provider.isExpressionClassBody():
83        post_statements.append(
84            StatementLocalsDictSync(
85                locals_scope=locals_scope,
86                locals_arg=ExpressionTempVariableRef(
87                    variable=locals_keeper_variable, source_ref=source_ref
88                ),
89                source_ref=source_ref.atInternal(),
90            )
91        )
92
93    post_statements += (
94        StatementReleaseVariable(
95            variable=globals_keeper_variable, source_ref=source_ref
96        ),
97        StatementReleaseVariable(
98            variable=locals_keeper_variable, source_ref=source_ref
99        ),
100    )
101
102    # The locals default is dependent on exec_mode, globals or locals.
103    locals_default = ExpressionConditional(
104        condition=ExpressionComparisonIs(
105            left=ExpressionTempVariableRef(
106                variable=globals_keeper_variable, source_ref=source_ref
107            ),
108            right=ExpressionConstantNoneRef(source_ref=source_ref),
109            source_ref=source_ref,
110        ),
111        expression_no=ExpressionTempVariableRef(
112            variable=globals_keeper_variable, source_ref=source_ref
113        ),
114        expression_yes=makeExpressionBuiltinLocals(
115            locals_scope=locals_scope, source_ref=source_ref
116        ),
117        source_ref=source_ref,
118    )
119
120    pre_statements = [
121        # First assign globals and locals temporary the values given.
122        StatementAssignmentVariable(
123            variable=globals_keeper_variable, source=globals_node, source_ref=source_ref
124        ),
125        StatementAssignmentVariable(
126            variable=locals_keeper_variable, source=locals_node, source_ref=source_ref
127        ),
128        makeStatementConditional(
129            condition=ExpressionComparisonIs(
130                left=ExpressionTempVariableRef(
131                    variable=locals_keeper_variable, source_ref=source_ref
132                ),
133                right=ExpressionConstantNoneRef(source_ref=source_ref),
134                source_ref=source_ref,
135            ),
136            yes_branch=StatementAssignmentVariable(
137                variable=locals_keeper_variable,
138                source=locals_default,
139                source_ref=source_ref,
140            ),
141            no_branch=None,
142            source_ref=source_ref,
143        ),
144        makeStatementConditional(
145            condition=ExpressionComparisonIs(
146                left=ExpressionTempVariableRef(
147                    variable=globals_keeper_variable, source_ref=source_ref
148                ),
149                right=ExpressionConstantNoneRef(source_ref=source_ref),
150                source_ref=source_ref,
151            ),
152            yes_branch=StatementAssignmentVariable(
153                variable=globals_keeper_variable,
154                source=ExpressionBuiltinGlobals(source_ref=source_ref),
155                source_ref=source_ref,
156            ),
157            no_branch=None,
158            source_ref=source_ref,
159        ),
160    ]
161
162    return (
163        ExpressionTempVariableRef(
164            variable=globals_keeper_variable,
165            source_ref=source_ref
166            if globals_node is None
167            else globals_node.getSourceReference(),
168        ),
169        ExpressionTempVariableRef(
170            variable=locals_keeper_variable,
171            source_ref=source_ref
172            if locals_node is None
173            else locals_node.getSourceReference(),
174        ),
175        makeStatementsSequence(pre_statements, False, source_ref),
176        makeStatementsSequence(post_statements, False, source_ref),
177    )
178
179
180def buildExecNode(provider, node, source_ref):
181    # "exec" statements, should only occur with Python2.
182
183    # This is using many variables, due to the many details this is
184    # dealing with. The locals and globals need to be dealt with in
185    # temporary variables, and we need handling of indicators, so
186    # that is just the complexity, pylint: disable=too-many-locals
187
188    exec_globals = node.globals
189    exec_locals = node.locals
190    body = node.body
191
192    # Handle exec(a,b,c) to be same as exec a, b, c
193    if exec_locals is None and exec_globals is None and getKind(body) == "Tuple":
194        parts = body.elts
195        body = parts[0]
196
197        if len(parts) > 1:
198            exec_globals = parts[1]
199
200            if len(parts) > 2:
201                exec_locals = parts[2]
202        else:
203            return StatementRaiseException(
204                exception_type=ExpressionBuiltinExceptionRef(
205                    exception_name="TypeError", source_ref=source_ref
206                ),
207                exception_value=makeConstantRefNode(
208                    constant="""\
209exec: arg 1 must be a string, file, or code object""",
210                    source_ref=source_ref,
211                ),
212                exception_trace=None,
213                exception_cause=None,
214                source_ref=source_ref,
215            )
216
217    temp_scope = provider.allocateTempScope("exec")
218
219    locals_value = buildNode(provider, exec_locals, source_ref, True)
220
221    if locals_value is None:
222        locals_value = ExpressionConstantNoneRef(source_ref=source_ref)
223
224    globals_value = buildNode(provider, exec_globals, source_ref, True)
225
226    if globals_value is None:
227        globals_value = ExpressionConstantNoneRef(source_ref=source_ref)
228
229    source_code = buildNode(provider, body, source_ref)
230
231    source_variable = provider.allocateTempVariable(
232        temp_scope=temp_scope, name="exec_source"
233    )
234
235    globals_keeper_variable = provider.allocateTempVariable(
236        temp_scope=temp_scope, name="globals"
237    )
238
239    locals_keeper_variable = provider.allocateTempVariable(
240        temp_scope=temp_scope, name="locals"
241    )
242
243    plain_indicator_variable = provider.allocateTempVariable(
244        temp_scope=temp_scope, name="plain"
245    )
246
247    tried = (
248        # First evaluate the source code expressions.
249        StatementAssignmentVariable(
250            variable=source_variable, source=source_code, source_ref=source_ref
251        ),
252        # Assign globals and locals temporary the values given, then fix it
253        # up, taking note in the "plain" temporary variable, if it was an
254        # "exec" statement with None arguments, in which case the copy back
255        # will be necessary.
256        StatementAssignmentVariable(
257            variable=globals_keeper_variable,
258            source=globals_value,
259            source_ref=source_ref,
260        ),
261        StatementAssignmentVariable(
262            variable=locals_keeper_variable, source=locals_value, source_ref=source_ref
263        ),
264        StatementAssignmentVariable(
265            variable=plain_indicator_variable,
266            source=makeConstantRefNode(constant=False, source_ref=source_ref),
267            source_ref=source_ref,
268        ),
269        makeStatementConditional(
270            condition=ExpressionComparisonIs(
271                left=ExpressionTempVariableRef(
272                    variable=globals_keeper_variable, source_ref=source_ref
273                ),
274                right=ExpressionConstantNoneRef(source_ref=source_ref),
275                source_ref=source_ref,
276            ),
277            yes_branch=makeStatementsSequenceFromStatements(
278                StatementAssignmentVariable(
279                    variable=globals_keeper_variable,
280                    source=ExpressionBuiltinGlobals(source_ref=source_ref),
281                    source_ref=source_ref,
282                ),
283                makeStatementConditional(
284                    condition=ExpressionComparisonIs(
285                        left=ExpressionTempVariableRef(
286                            variable=locals_keeper_variable, source_ref=source_ref
287                        ),
288                        right=ExpressionConstantNoneRef(source_ref=source_ref),
289                        source_ref=source_ref,
290                    ),
291                    yes_branch=makeStatementsSequenceFromStatements(
292                        StatementAssignmentVariable(
293                            variable=locals_keeper_variable,
294                            source=makeExpressionBuiltinLocals(
295                                locals_scope=provider.getLocalsScope(),
296                                source_ref=source_ref,
297                            ),
298                            source_ref=source_ref,
299                        ),
300                        StatementAssignmentVariable(
301                            variable=plain_indicator_variable,
302                            source=makeConstantRefNode(
303                                constant=True, source_ref=source_ref
304                            ),
305                            source_ref=source_ref,
306                        ),
307                    ),
308                    no_branch=None,
309                    source_ref=source_ref,
310                ),
311            ),
312            no_branch=makeStatementsSequenceFromStatements(
313                makeStatementConditional(
314                    condition=ExpressionComparisonIs(
315                        left=ExpressionTempVariableRef(
316                            variable=locals_keeper_variable, source_ref=source_ref
317                        ),
318                        right=ExpressionConstantNoneRef(source_ref=source_ref),
319                        source_ref=source_ref,
320                    ),
321                    yes_branch=makeStatementsSequenceFromStatement(
322                        statement=StatementAssignmentVariable(
323                            variable=locals_keeper_variable,
324                            source=ExpressionTempVariableRef(
325                                variable=globals_keeper_variable, source_ref=source_ref
326                            ),
327                            source_ref=source_ref,
328                        )
329                    ),
330                    no_branch=None,
331                    source_ref=source_ref,
332                )
333            ),
334            source_ref=source_ref,
335        ),
336        makeTryFinallyStatement(
337            provider=provider,
338            tried=StatementExec(
339                source_code=ExpressionTempVariableRef(
340                    variable=source_variable, source_ref=source_ref
341                ),
342                globals_arg=ExpressionTempVariableRef(
343                    variable=globals_keeper_variable, source_ref=source_ref
344                ),
345                locals_arg=ExpressionTempVariableRef(
346                    variable=locals_keeper_variable, source_ref=source_ref
347                ),
348                source_ref=source_ref,
349            ),
350            final=makeStatementConditional(
351                condition=ExpressionComparisonIs(
352                    left=ExpressionTempVariableRef(
353                        variable=plain_indicator_variable, source_ref=source_ref
354                    ),
355                    right=makeConstantRefNode(constant=True, source_ref=source_ref),
356                    source_ref=source_ref,
357                ),
358                yes_branch=StatementLocalsDictSync(
359                    locals_scope=provider.getLocalsScope(),
360                    locals_arg=ExpressionTempVariableRef(
361                        variable=locals_keeper_variable, source_ref=source_ref
362                    ),
363                    source_ref=source_ref,
364                ),
365                no_branch=None,
366                source_ref=source_ref,
367            ),
368            source_ref=source_ref,
369        ),
370    )
371
372    final = (
373        StatementReleaseVariable(variable=source_variable, source_ref=source_ref),
374        StatementReleaseVariable(
375            variable=globals_keeper_variable, source_ref=source_ref
376        ),
377        StatementReleaseVariable(
378            variable=locals_keeper_variable, source_ref=source_ref
379        ),
380        StatementReleaseVariable(
381            variable=plain_indicator_variable, source_ref=source_ref
382        ),
383    )
384
385    return makeTryFinallyStatement(
386        provider=provider, tried=tried, final=final, source_ref=source_ref
387    )
388
389
390# This is here, to make sure it can register, pylint: disable=W0611
391import nuitka.optimizations.OptimizeBuiltinCalls  # isort:skip
392