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""" Built-in type nodes tuple/list/set/float/str/unicode etc.
19
20These are all very simple and have predictable properties, because we know their type and
21that should allow some important optimizations.
22"""
23
24from nuitka.specs import BuiltinParameterSpecs
25
26from .ConstantRefNodes import makeConstantRefNode
27from .ExpressionBases import (
28    CompileTimeConstantExpressionBase,
29    ExpressionBuiltinSingleArgBase,
30    ExpressionChildHavingBase,
31    ExpressionChildrenHavingBase,
32    ExpressionSpecBasedComputationMixin,
33)
34from .NodeMakingHelpers import (
35    makeConstantReplacementNode,
36    wrapExpressionWithNodeSideEffects,
37)
38from .shapes.BuiltinTypeShapes import (
39    tshape_bool,
40    tshape_bytearray,
41    tshape_bytes,
42    tshape_bytes_derived,
43    tshape_float_derived,
44    tshape_frozenset,
45    tshape_list,
46    tshape_set,
47    tshape_str_derived,
48    tshape_tuple,
49    tshape_unicode_derived,
50)
51
52
53class ExpressionBuiltinTypeBase(ExpressionBuiltinSingleArgBase):
54    pass
55
56
57class ExpressionBuiltinContainerBase(
58    ExpressionSpecBasedComputationMixin, ExpressionChildHavingBase
59):
60
61    builtin_spec = None
62
63    named_child = "value"
64
65    def __init__(self, value, source_ref):
66        ExpressionChildHavingBase.__init__(self, value=value, source_ref=source_ref)
67
68    def computeExpression(self, trace_collection):
69        value = self.subnode_value
70
71        if value is None:
72            return self.computeBuiltinSpec(
73                trace_collection=trace_collection, given_values=()
74            )
75        elif value.isExpressionConstantXrangeRef():
76            if value.getIterationLength() <= 256:
77                return self.computeBuiltinSpec(
78                    trace_collection=trace_collection, given_values=(value,)
79                )
80            else:
81                return self, None, None
82        else:
83            return self.computeBuiltinSpec(
84                trace_collection=trace_collection, given_values=(value,)
85            )
86
87
88class ExpressionBuiltinTuple(ExpressionBuiltinContainerBase):
89    kind = "EXPRESSION_BUILTIN_TUPLE"
90
91    builtin_spec = BuiltinParameterSpecs.builtin_tuple_spec
92
93    @staticmethod
94    def getTypeShape():
95        return tshape_tuple
96
97
98class ExpressionBuiltinList(ExpressionBuiltinContainerBase):
99    kind = "EXPRESSION_BUILTIN_LIST"
100
101    builtin_spec = BuiltinParameterSpecs.builtin_list_spec
102
103    @staticmethod
104    def getTypeShape():
105        return tshape_list
106
107
108class ExpressionBuiltinSet(ExpressionBuiltinContainerBase):
109    kind = "EXPRESSION_BUILTIN_SET"
110
111    builtin_spec = BuiltinParameterSpecs.builtin_set_spec
112
113    @staticmethod
114    def getTypeShape():
115        return tshape_set
116
117
118class ExpressionBuiltinFrozenset(ExpressionBuiltinContainerBase):
119    kind = "EXPRESSION_BUILTIN_FROZENSET"
120
121    builtin_spec = BuiltinParameterSpecs.builtin_frozenset_spec
122
123    @staticmethod
124    def getTypeShape():
125        return tshape_frozenset
126
127
128class ExpressionBuiltinFloat(ExpressionChildHavingBase):
129    kind = "EXPRESSION_BUILTIN_FLOAT"
130
131    named_child = "value"
132
133    def __init__(self, value, source_ref):
134        ExpressionChildHavingBase.__init__(self, value=value, source_ref=source_ref)
135
136    @staticmethod
137    def getTypeShape():
138        # TODO: Depending on input type shape, we should improve this.
139        return tshape_float_derived
140
141    def computeExpression(self, trace_collection):
142        return self.subnode_value.computeExpressionFloat(
143            float_node=self, trace_collection=trace_collection
144        )
145
146    def mayRaiseException(self, exception_type):
147        return self.subnode_value.mayRaiseExceptionFloat(exception_type)
148
149
150class ExpressionBuiltinBool(ExpressionBuiltinTypeBase):
151    kind = "EXPRESSION_BUILTIN_BOOL"
152
153    builtin_spec = BuiltinParameterSpecs.builtin_bool_spec
154
155    def computeExpression(self, trace_collection):
156        value = self.subnode_value
157
158        truth_value = value.getTruthValue()
159
160        if truth_value is not None:
161            result = wrapExpressionWithNodeSideEffects(
162                new_node=makeConstantReplacementNode(
163                    constant=truth_value, node=self, user_provided=False
164                ),
165                old_node=value,
166            )
167
168            return (
169                result,
170                "new_constant",
171                "Predicted truth value of built-in bool argument",
172            )
173
174        return ExpressionBuiltinTypeBase.computeExpression(self, trace_collection)
175
176    @staticmethod
177    def getTypeShape():
178        # Note: Not allowed to subclass bool.
179        return tshape_bool
180
181
182class ExpressionBuiltinUnicodeBase(
183    ExpressionSpecBasedComputationMixin, ExpressionChildrenHavingBase
184):
185    named_children = ("value", "encoding", "errors")
186
187    def __init__(self, value, encoding, errors, source_ref):
188        ExpressionChildrenHavingBase.__init__(
189            self,
190            values={"value": value, "encoding": encoding, "errors": errors},
191            source_ref=source_ref,
192        )
193
194    def computeExpression(self, trace_collection):
195        args = [self.subnode_value, self.subnode_encoding, self.subnode_errors]
196
197        while args and args[-1] is None:
198            del args[-1]
199
200        # The value of that node escapes and could change its contents.
201        if self.subnode_value is not None:
202            trace_collection.onValueEscapeStr(self.subnode_value)
203
204        # Any code could be run, note that.
205        trace_collection.onControlFlowEscape(self)
206
207        return self.computeBuiltinSpec(
208            trace_collection=trace_collection, given_values=tuple(args)
209        )
210
211
212class ExpressionBuiltinStrP2(ExpressionBuiltinTypeBase):
213    """Python2 built-in str call."""
214
215    kind = "EXPRESSION_BUILTIN_STR_P2"
216
217    builtin_spec = BuiltinParameterSpecs.builtin_str_spec
218
219    def computeExpression(self, trace_collection):
220        (
221            new_node,
222            change_tags,
223            change_desc,
224        ) = ExpressionBuiltinTypeBase.computeExpression(self, trace_collection)
225
226        if new_node is self:
227            str_value = self.subnode_value.getStrValue()
228
229            if str_value is not None:
230                new_node = wrapExpressionWithNodeSideEffects(
231                    new_node=str_value, old_node=self.subnode_value
232                )
233
234                change_tags = "new_expression"
235                change_desc = "Predicted 'str' built-in result"
236
237        return new_node, change_tags, change_desc
238
239    @staticmethod
240    def getTypeShape():
241        return tshape_str_derived
242
243
244class ExpressionBuiltinUnicodeP2(ExpressionBuiltinUnicodeBase):
245    """Python2 built-in unicode call."""
246
247    kind = "EXPRESSION_BUILTIN_UNICODE_P2"
248
249    builtin_spec = BuiltinParameterSpecs.builtin_unicode_p2_spec
250
251    @staticmethod
252    def getTypeShape():
253        return tshape_unicode_derived
254
255
256class ExpressionBuiltinStrP3(ExpressionBuiltinUnicodeBase):
257    """Python3 built-in str call."""
258
259    kind = "EXPRESSION_BUILTIN_STR_P3"
260
261    builtin_spec = BuiltinParameterSpecs.builtin_str_spec
262
263    @staticmethod
264    def getTypeShape():
265        return tshape_str_derived
266
267
268class ExpressionBuiltinBytes3(ExpressionBuiltinUnicodeBase):
269    kind = "EXPRESSION_BUILTIN_BYTES3"
270
271    builtin_spec = BuiltinParameterSpecs.builtin_bytes_p3_spec
272
273    @staticmethod
274    def getTypeShape():
275        return tshape_bytes
276
277
278class ExpressionBuiltinBytes1(ExpressionChildHavingBase):
279    kind = "EXPRESSION_BUILTIN_BYTES1"
280
281    named_child = "value"
282
283    def __init__(self, value, source_ref):
284        ExpressionChildHavingBase.__init__(self, value=value, source_ref=source_ref)
285
286    @staticmethod
287    def getTypeShape():
288        # TODO: Depending on input type shape, we should improve this.
289        return tshape_bytes_derived
290
291    def computeExpression(self, trace_collection):
292        return self.subnode_value.computeExpressionBytes(
293            bytes_node=self, trace_collection=trace_collection
294        )
295
296    def mayRaiseException(self, exception_type):
297        return self.subnode_value.mayRaiseExceptionBytes(exception_type)
298
299
300class ExpressionBuiltinBytearray1(ExpressionBuiltinTypeBase):
301    kind = "EXPRESSION_BUILTIN_BYTEARRAY1"
302
303    builtin_spec = BuiltinParameterSpecs.builtin_bytearray_spec
304
305    def __init__(self, value, source_ref):
306        ExpressionBuiltinTypeBase.__init__(self, value=value, source_ref=source_ref)
307
308    @staticmethod
309    def getTypeShape():
310        return tshape_bytearray
311
312
313class ExpressionBuiltinBytearray3(ExpressionChildrenHavingBase):
314    kind = "EXPRESSION_BUILTIN_BYTEARRAY3"
315
316    named_children = ("string", "encoding", "errors")
317
318    builtin_spec = BuiltinParameterSpecs.builtin_bytearray_spec
319
320    def __init__(self, string, encoding, errors, source_ref):
321        ExpressionChildrenHavingBase.__init__(
322            self,
323            values={"string": string, "encoding": encoding, "errors": errors},
324            source_ref=source_ref,
325        )
326
327    def computeExpression(self, trace_collection):
328        trace_collection.onExceptionRaiseExit(BaseException)
329
330        return self, None, None
331
332    @staticmethod
333    def getTypeShape():
334        return tshape_bytearray
335
336
337class ExpressionConstantGenericAlias(CompileTimeConstantExpressionBase):
338    kind = "EXPRESSION_CONSTANT_GENERIC_ALIAS"
339
340    __slots__ = ("generic_alias",)
341
342    def __init__(self, generic_alias, source_ref):
343        CompileTimeConstantExpressionBase.__init__(self, source_ref=source_ref)
344
345        self.generic_alias = generic_alias
346
347    def finalize(self):
348        del self.parent
349
350    def getDetails(self):
351        return {"generic_alias": self.generic_alias}
352
353    @staticmethod
354    def mayRaiseException(exception_type):
355        return False
356
357    @staticmethod
358    def mayHaveSideEffects():
359        return False
360
361    def getCompileTimeConstant(self):
362        return self.generic_alias
363
364    def getStrValue(self):
365        return makeConstantRefNode(
366            constant=str(self.getCompileTimeConstant()),
367            user_provided=True,
368            source_ref=self.source_ref,
369        )
370
371    def computeExpressionRaw(self, trace_collection):
372        # Nothing much to do.
373        return self, None, None
374