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""" Codes for operations.
19
20There are unary and binary operations. Many of them have specializations and
21of course types could play into it. Then there is also the added difficulty of
22in-place assignments, which have other operation variants.
23"""
24
25from . import HelperDefinitions, OperatorCodes
26from .CodeHelpers import (
27    generateChildExpressionsCode,
28    pickCodeHelper,
29    withObjectCodeTemporaryAssignment,
30)
31from .ErrorCodes import (
32    getErrorExitBoolCode,
33    getErrorExitCode,
34    getTakeReferenceCode,
35)
36
37
38def generateOperationBinaryCode(to_name, expression, emit, context):
39    left_arg_name, right_arg_name = generateChildExpressionsCode(
40        expression=expression, emit=emit, context=context
41    )
42
43    # TODO: Decide and use one single spelling, inplace or in_place
44    inplace = expression.isInplaceSuspect()
45
46    _getBinaryOperationCode(
47        to_name=to_name,
48        expression=expression,
49        operator=expression.getOperator(),
50        arg_names=(left_arg_name, right_arg_name),
51        in_place=inplace,
52        emit=emit,
53        context=context,
54    )
55
56
57def generateOperationNotCode(to_name, expression, emit, context):
58    (arg_name,) = generateChildExpressionsCode(
59        expression=expression, emit=emit, context=context
60    )
61
62    res_name = context.getIntResName()
63
64    emit("%s = CHECK_IF_TRUE(%s);" % (res_name, arg_name))
65
66    getErrorExitBoolCode(
67        condition="%s == -1" % res_name,
68        release_name=arg_name,
69        needs_check=expression.subnode_operand.mayRaiseExceptionBool(BaseException),
70        emit=emit,
71        context=context,
72    )
73
74    to_name.getCType().emitAssignmentCodeFromBoolCondition(
75        to_name=to_name, condition="%s == 0" % res_name, emit=emit
76    )
77
78
79def generateOperationUnaryCode(to_name, expression, emit, context):
80    (arg_name,) = generateChildExpressionsCode(
81        expression=expression, emit=emit, context=context
82    )
83
84    _getUnaryOperationCode(
85        to_name=to_name,
86        expression=expression,
87        operator=expression.getOperator(),
88        arg_name=arg_name,
89        needs_check=expression.mayRaiseException(BaseException),
90        emit=emit,
91        context=context,
92    )
93
94
95def _getBinaryOperationCode(
96    to_name, expression, operator, arg_names, in_place, emit, context
97):
98    left = expression.subnode_left
99
100    ref_count = 1
101    needs_check = expression.mayRaiseExceptionOperation()
102
103    helper = pickCodeHelper(
104        prefix="BINARY_OPERATION_%s"
105        % HelperDefinitions.getCodeNameForOperation(operator),
106        suffix="INPLACE" if operator[0] == "I" else "",
107        target_type=None if operator[0] == "I" else to_name.getCType(),
108        left_shape=left.getTypeShape(),
109        right_shape=expression.subnode_right.getTypeShape(),
110        helpers=HelperDefinitions.getSpecializedOperations(operator),
111        nonhelpers=HelperDefinitions.getNonSpecializedOperations(operator),
112        source_ref=expression.source_ref,
113    )
114
115    # We must assume to write to a variable is "in_place" is active, not e.g.
116    # a constant reference. That was asserted before calling us.
117    if in_place or "INPLACE" in helper.helper_name:
118        res_name = context.getBoolResName()
119
120        # For module variable C type to reference later.
121        if left.isExpressionVariableRef() and left.getVariable().isModuleVariable():
122            emit("%s = %s;" % (context.getInplaceLeftName(), arg_names[0]))
123
124        if (
125            not left.isExpressionVariableRef()
126            and not left.isExpressionTempVariableRef()
127        ):
128            if not context.needsCleanup(arg_names[0]):
129                getTakeReferenceCode(arg_names[0], emit)
130
131        emit("%s = %s(&%s, %s);" % (res_name, helper, arg_names[0], arg_names[1]))
132
133        getErrorExitBoolCode(
134            condition="%s == false" % res_name,
135            release_names=arg_names,
136            needs_check=needs_check,
137            emit=emit,
138            context=context,
139        )
140
141        emit("%s = %s;" % (to_name, arg_names[0]))
142
143        if (
144            not left.isExpressionVariableRef()
145            and not left.isExpressionTempVariableRef()
146        ):
147            context.addCleanupTempName(to_name)
148    else:
149        helper.emitHelperCall(
150            to_name=to_name,
151            arg_names=arg_names,
152            ref_count=ref_count,
153            needs_check=needs_check,
154            emit=emit,
155            context=context,
156        )
157
158
159def _getUnaryOperationCode(
160    to_name, expression, operator, arg_name, needs_check, emit, context
161):
162    impl_helper, ref_count = OperatorCodes.unary_operator_codes[operator]
163
164    helper = "UNARY_OPERATION"
165    prefix_args = (impl_helper,)
166
167    with withObjectCodeTemporaryAssignment(
168        to_name, "op_%s_res" % operator.lower(), expression, emit, context
169    ) as value_name:
170
171        emit(
172            "%s = %s(%s);"
173            % (
174                value_name,
175                helper,
176                ", ".join(str(arg_name) for arg_name in prefix_args + (arg_name,)),
177            )
178        )
179
180        getErrorExitCode(
181            check_name=value_name,
182            release_name=arg_name,
183            needs_check=needs_check,
184            emit=emit,
185            context=context,
186        )
187
188        if ref_count:
189            context.addCleanupTempName(value_name)
190