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""" The type1 node.
19
20This one just determines types. It's great for optimization. We may be able to
21predict its value, but knowing it. In that case, we have a built-in name
22reference for that type to convert to, or when checking the result of it, we
23will then know it's limited after the fact.
24
25"""
26
27from nuitka.Builtins import builtin_names
28
29from .BuiltinRefNodes import (
30    ExpressionBuiltinAnonymousRef,
31    ExpressionBuiltinRef,
32    makeExpressionBuiltinRef,
33)
34from .ExpressionBases import (
35    ExpressionBuiltinSingleArgBase,
36    ExpressionChildHavingBase,
37    ExpressionChildrenHavingBase,
38)
39from .NodeBases import SideEffectsFromChildrenMixin
40from .NodeMakingHelpers import wrapExpressionWithNodeSideEffects
41from .shapes.BuiltinTypeShapes import tshape_bool, tshape_type
42
43
44class ExpressionBuiltinType1(ExpressionBuiltinSingleArgBase):
45    kind = "EXPRESSION_BUILTIN_TYPE1"
46
47    def computeExpression(self, trace_collection):
48        value = self.subnode_value
49
50        type_shape = value.getTypeShape()
51
52        if type_shape is not None:
53            type_name = type_shape.getTypeName()
54
55            if type_name is not None and type_name in __builtins__:
56                result = ExpressionBuiltinRef(
57                    builtin_name=type_name, source_ref=value.getSourceReference()
58                )
59
60                result = wrapExpressionWithNodeSideEffects(
61                    new_node=result, old_node=value
62                )
63
64                return (
65                    result,
66                    "new_builtin",
67                    "Replaced predictable type lookup with builtin type '%s'."
68                    % (type_name),
69                )
70
71        if value.isCompileTimeConstant():
72            # The above code is supposed to catch these in a better way.
73            value = value.getCompileTimeConstant()
74
75            type_name = value.__class__.__name__
76
77            if type_name in builtin_names:
78                new_node = makeExpressionBuiltinRef(
79                    builtin_name=type_name,
80                    locals_scope=None,
81                    source_ref=self.source_ref,
82                )
83            else:
84                new_node = ExpressionBuiltinAnonymousRef(
85                    builtin_name=type_name, source_ref=self.source_ref
86                )
87
88            return (
89                new_node,
90                "new_builtin",
91                "Replaced predictable type lookup with builtin type '%s'."
92                % (type_name),
93            )
94
95        return self, None, None
96
97    @staticmethod
98    def getTypeShape():
99        return tshape_type
100
101    def computeExpressionDrop(self, statement, trace_collection):
102        from .NodeMakingHelpers import (
103            makeStatementExpressionOnlyReplacementNode,
104        )
105
106        result = makeStatementExpressionOnlyReplacementNode(
107            expression=self.subnode_value, node=statement
108        )
109
110        return (
111            result,
112            "new_statements",
113            """\
114Removed type taking for unused result.""",
115        )
116
117    def mayRaiseException(self, exception_type):
118        return self.subnode_value.mayRaiseException(exception_type)
119
120    def mayHaveSideEffects(self):
121        return self.subnode_value.mayHaveSideEffects()
122
123
124class ExpressionBuiltinSuper2(ExpressionChildrenHavingBase):
125    """Two arguments form of super."""
126
127    kind = "EXPRESSION_BUILTIN_SUPER2"
128
129    named_children = ("type_arg", "object_arg")
130
131    def __init__(self, type_arg, object_arg, source_ref):
132        ExpressionChildrenHavingBase.__init__(
133            self,
134            values={"type_arg": type_arg, "object_arg": object_arg},
135            source_ref=source_ref,
136        )
137
138    def computeExpression(self, trace_collection):
139        trace_collection.onExceptionRaiseExit(BaseException)
140
141        # TODO: Quite some cases should be possible to predict.
142        return self, None, None
143
144
145class ExpressionBuiltinSuper0(ExpressionChildrenHavingBase):
146    """Python3 form of super, arguments determined from cells and function arguments."""
147
148    kind = "EXPRESSION_BUILTIN_SUPER0"
149
150    named_children = ("type_arg", "object_arg")
151
152    def __init__(self, type_arg, object_arg, source_ref):
153        ExpressionChildrenHavingBase.__init__(
154            self,
155            values={"type_arg": type_arg, "object_arg": object_arg},
156            source_ref=source_ref,
157        )
158
159    def computeExpression(self, trace_collection):
160        trace_collection.onExceptionRaiseExit(BaseException)
161
162        # TODO: Quite some cases should be possible to predict.
163        return self, None, None
164
165
166class ExpressionBuiltinIsinstance(ExpressionChildrenHavingBase):
167    kind = "EXPRESSION_BUILTIN_ISINSTANCE"
168
169    named_children = ("instance", "classes")
170
171    def __init__(self, instance, classes, source_ref):
172        ExpressionChildrenHavingBase.__init__(
173            self,
174            values={"instance": instance, "classes": classes},
175            source_ref=source_ref,
176        )
177
178    def computeExpression(self, trace_collection):
179        # TODO: Quite some cases should be possible to predict.
180
181        instance = self.subnode_instance
182
183        # TODO: Should be possible to query run time type instead, but we don't
184        # have that method yet. Later this will be essential.
185        if not instance.isCompileTimeConstant():
186            trace_collection.onExceptionRaiseExit(BaseException)
187
188            return self, None, None
189
190        classes = self.subnode_classes
191
192        if not classes.isCompileTimeConstant():
193            trace_collection.onExceptionRaiseExit(BaseException)
194
195            return self, None, None
196
197        # So if both are compile time constant, we are able to compute it.
198        return trace_collection.getCompileTimeComputationResult(
199            node=self,
200            computation=lambda: isinstance(
201                instance.getCompileTimeConstant(), classes.getCompileTimeConstant()
202            ),
203            description="Built-in call to 'isinstance' computed.",
204        )
205
206
207class ExpressionBuiltinIssubclass(ExpressionChildrenHavingBase):
208    kind = "EXPRESSION_BUILTIN_ISSUBCLASS"
209
210    named_children = ("cls", "classes")
211
212    def __init__(self, cls, classes, source_ref):
213        ExpressionChildrenHavingBase.__init__(
214            self,
215            values={"cls": cls, "classes": classes},
216            source_ref=source_ref,
217        )
218
219    def computeExpression(self, trace_collection):
220        # TODO: Quite some cases should be possible to predict.
221
222        cls = self.subnode_cls
223
224        # TODO: Should be possible to query run time type instead, but we don't
225        # have that method yet. Later this will be essential.
226        if not cls.isCompileTimeConstant():
227            trace_collection.onExceptionRaiseExit(BaseException)
228
229            return self, None, None
230
231        classes = self.subnode_classes
232
233        if not classes.isCompileTimeConstant():
234            trace_collection.onExceptionRaiseExit(BaseException)
235
236            return self, None, None
237
238        # So if both are compile time constant, we are able to compute it.
239        return trace_collection.getCompileTimeComputationResult(
240            node=self,
241            computation=lambda: issubclass(
242                cls.getCompileTimeConstant(), classes.getCompileTimeConstant()
243            ),
244            description="Built-in call to 'issubclass' computed.",
245        )
246
247
248class ExpressionTypeCheck(SideEffectsFromChildrenMixin, ExpressionChildHavingBase):
249    kind = "EXPRESSION_TYPE_CHECK"
250
251    named_child = "cls"
252
253    def __init__(self, cls, source_ref):
254        ExpressionChildHavingBase.__init__(
255            self,
256            value=cls,
257            source_ref=source_ref,
258        )
259
260    @staticmethod
261    def getTypeShape():
262        return tshape_bool
263
264    def computeExpression(self, trace_collection):
265        # TODO: Quite some cases should be possible to predict, but I am not aware of
266        # 100% true Python equivalent at this time.
267        return self, None, None
268