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""" Tree nodes for built-in references.
19
20There is 2 major types of built-in references. One is the values from
21built-ins, the other is built-in exceptions. They work differently and
22mean different things, but they have similar origin, that is, access
23to variables only ever read.
24
25"""
26
27
28from nuitka.Builtins import (
29    builtin_anon_names,
30    builtin_exception_names,
31    builtin_exception_values,
32    builtin_names,
33    builtin_type_names,
34)
35from nuitka.Options import hasPythonFlagNoAsserts
36from nuitka.PythonVersions import python_version
37from nuitka.specs import BuiltinParameterSpecs
38
39from .ConstantRefNodes import makeConstantRefNode
40from .ExceptionNodes import (
41    ExpressionBuiltinMakeException,
42    ExpressionBuiltinMakeExceptionImportError,
43)
44from .ExpressionBases import CompileTimeConstantExpressionBase
45from .shapes.BuiltinTypeShapes import tshape_exception_class
46
47
48class ExpressionBuiltinRefBase(CompileTimeConstantExpressionBase):
49    # Base classes can be abstract, pylint: disable=abstract-method
50
51    __slots__ = ("builtin_name",)
52
53    def __init__(self, builtin_name, source_ref):
54        CompileTimeConstantExpressionBase.__init__(self, source_ref=source_ref)
55
56        self.builtin_name = builtin_name
57
58    def finalize(self):
59        del self.parent
60
61    def getDetails(self):
62        return {"builtin_name": self.builtin_name}
63
64    def getBuiltinName(self):
65        return self.builtin_name
66
67    @staticmethod
68    def isKnownToBeHashable():
69        return True
70
71    @staticmethod
72    def mayRaiseException(exception_type):
73        return False
74
75    @staticmethod
76    def mayHaveSideEffects():
77        return False
78
79    def getStrValue(self):
80        return makeConstantRefNode(
81            constant=str(self.getCompileTimeConstant()),
82            user_provided=True,
83            source_ref=self.source_ref,
84        )
85
86
87def makeExpressionBuiltinTypeRef(builtin_name, source_ref):
88    return makeConstantRefNode(
89        constant=__builtins__[builtin_name], source_ref=source_ref
90    )
91
92
93quick_names = {"None": None, "True": True, "False": False, "Ellipsis": Ellipsis}
94
95
96def makeExpressionBuiltinRef(builtin_name, locals_scope, source_ref):
97    assert builtin_name in builtin_names, builtin_name
98
99    if builtin_name in quick_names:
100        return makeConstantRefNode(
101            constant=quick_names[builtin_name], source_ref=source_ref
102        )
103    elif builtin_name == "__debug__":
104        return makeConstantRefNode(
105            constant=not hasPythonFlagNoAsserts(), source_ref=source_ref
106        )
107    elif builtin_name in builtin_type_names:
108        return makeExpressionBuiltinTypeRef(
109            builtin_name=builtin_name, source_ref=source_ref
110        )
111    elif builtin_name in ("dir", "eval", "exec", "execfile", "locals", "vars"):
112        return ExpressionBuiltinWithContextRef(
113            builtin_name=builtin_name, locals_scope=locals_scope, source_ref=source_ref
114        )
115    else:
116        return ExpressionBuiltinRef(builtin_name=builtin_name, source_ref=source_ref)
117
118
119class ExpressionBuiltinRef(ExpressionBuiltinRefBase):
120    kind = "EXPRESSION_BUILTIN_REF"
121
122    __slots__ = ()
123
124    # For overload
125    locals_scope = None
126
127    @staticmethod
128    def isExpressionBuiltinRef():
129        return True
130
131    def __init__(self, builtin_name, source_ref):
132        ExpressionBuiltinRefBase.__init__(
133            self, builtin_name=builtin_name, source_ref=source_ref
134        )
135
136    def getCompileTimeConstant(self):
137        return __builtins__[self.builtin_name]
138
139    def computeExpressionRaw(self, trace_collection):
140        return self, None, None
141
142    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
143        from nuitka.optimizations.OptimizeBuiltinCalls import (
144            computeBuiltinCall,
145        )
146
147        # Anything may happen. On next pass, if replaced, we might be better
148        # but not now.
149        trace_collection.onExceptionRaiseExit(BaseException)
150
151        new_node, tags, message = computeBuiltinCall(
152            builtin_name=self.builtin_name, call_node=call_node
153        )
154
155        if self.builtin_name in ("dir", "eval", "exec", "execfile", "locals", "vars"):
156            # Just inform the collection that all has escaped.
157            trace_collection.onLocalsUsage(locals_scope=self.getLocalsScope())
158
159        return new_node, tags, message
160
161    @staticmethod
162    def isKnownToBeIterable(count):
163        # TODO: Why yes, some may be, could be told here.
164        return None
165
166
167class ExpressionBuiltinWithContextRef(ExpressionBuiltinRef):
168    """Same as ExpressionBuiltinRef, but with a context it refers to."""
169
170    kind = "EXPRESSION_BUILTIN_WITH_CONTEXT_REF"
171
172    __slots__ = ("locals_scope",)
173
174    def __init__(self, builtin_name, locals_scope, source_ref):
175        ExpressionBuiltinRef.__init__(
176            self, builtin_name=builtin_name, source_ref=source_ref
177        )
178
179        self.locals_scope = locals_scope
180
181    def getDetails(self):
182        return {"builtin_name": self.builtin_name, "locals_scope": self.locals_scope}
183
184    def getLocalsScope(self):
185        return self.locals_scope
186
187
188class ExpressionBuiltinAnonymousRef(ExpressionBuiltinRefBase):
189    kind = "EXPRESSION_BUILTIN_ANONYMOUS_REF"
190
191    __slots__ = ()
192
193    def __init__(self, builtin_name, source_ref):
194        assert builtin_name in builtin_anon_names, builtin_name
195
196        ExpressionBuiltinRefBase.__init__(
197            self, builtin_name=builtin_name, source_ref=source_ref
198        )
199
200    def getCompileTimeConstant(self):
201        return builtin_anon_names[self.builtin_name]
202
203    def computeExpressionRaw(self, trace_collection):
204        return self, None, None
205
206
207class ExpressionBuiltinExceptionRef(ExpressionBuiltinRefBase):
208    kind = "EXPRESSION_BUILTIN_EXCEPTION_REF"
209
210    __slots__ = ()
211
212    def __init__(self, exception_name, source_ref):
213        assert exception_name in builtin_exception_names, exception_name
214
215        ExpressionBuiltinRefBase.__init__(
216            self, builtin_name=exception_name, source_ref=source_ref
217        )
218
219    def getDetails(self):
220        return {"exception_name": self.builtin_name}
221
222    getExceptionName = ExpressionBuiltinRefBase.getBuiltinName
223
224    @staticmethod
225    def getTypeShape():
226        return tshape_exception_class
227
228    @staticmethod
229    def mayRaiseException(exception_type):
230        return False
231
232    def getCompileTimeConstant(self):
233        return builtin_exception_values[self.builtin_name]
234
235    def computeExpressionRaw(self, trace_collection):
236        # Not much that can be done here.
237        return self, None, None
238
239    def computeExpressionCall(self, call_node, call_args, call_kw, trace_collection):
240        exception_name = self.getExceptionName()
241
242        def createBuiltinMakeException(args, name=None, path=None, source_ref=None):
243            if exception_name == "ImportError" and python_version >= 0x300:
244                return ExpressionBuiltinMakeExceptionImportError(
245                    exception_name=exception_name,
246                    args=args,
247                    name=name,
248                    path=path,
249                    source_ref=source_ref,
250                )
251            else:
252                # We expect to only get the star arguments for these.
253                assert name is None
254                assert path is None
255
256                return ExpressionBuiltinMakeException(
257                    exception_name=exception_name, args=args, source_ref=source_ref
258                )
259
260        new_node = BuiltinParameterSpecs.extractBuiltinArgs(
261            node=call_node,
262            builtin_class=createBuiltinMakeException,
263            builtin_spec=BuiltinParameterSpecs.makeBuiltinExceptionParameterSpec(
264                exception_name=exception_name
265            ),
266        )
267
268        assert new_node is not None
269
270        return new_node, "new_expression", "Detected built-in exception making."
271