1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ES2PANDA_COMPILER_CHECKER_ETS_ARITHMETIC_H
17#define ES2PANDA_COMPILER_CHECKER_ETS_ARITHMETIC_H
18
19#include "checker/ETSchecker.h"
20
21namespace ark::es2panda::checker {
22
23template <typename TargetType>
24typename TargetType::UType ETSChecker::GetOperand(Type *type)
25{
26    switch (ETSType(type)) {
27        case TypeFlag::BYTE: {
28            return type->AsByteType()->GetValue();
29        }
30        case TypeFlag::CHAR: {
31            return type->AsCharType()->GetValue();
32        }
33        case TypeFlag::SHORT: {
34            return type->AsShortType()->GetValue();
35        }
36        case TypeFlag::INT: {
37            return type->AsIntType()->GetValue();
38        }
39        case TypeFlag::LONG: {
40            return type->AsLongType()->GetValue();
41        }
42        case TypeFlag::FLOAT: {
43            return type->AsFloatType()->GetValue();
44        }
45        case TypeFlag::DOUBLE: {
46            return type->AsDoubleType()->GetValue();
47        }
48        case TypeFlag::ETS_BOOLEAN: {
49            return type->AsETSBooleanType()->GetValue();
50        }
51        default: {
52            UNREACHABLE();
53        }
54    }
55}
56
57template <typename TargetType>
58Type *ETSChecker::PerformRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
59{
60    using UType = typename TargetType::UType;
61
62    UType leftValue = GetOperand<TargetType>(left);
63    UType rightValue = GetOperand<TargetType>(right);
64
65    bool result {};
66    switch (operationType) {
67        case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
68            result = leftValue < rightValue;
69            break;
70        }
71        case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
72            result = leftValue <= rightValue;
73            break;
74        }
75        case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
76            result = leftValue > rightValue;
77            break;
78        }
79        case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
80            result = leftValue >= rightValue;
81            break;
82        }
83        case lexer::TokenType::PUNCTUATOR_EQUAL: {
84            result = leftValue == rightValue;
85            break;
86        }
87        case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
88            result = leftValue != rightValue;
89            break;
90        }
91        default: {
92            UNREACHABLE();
93        }
94    }
95
96    return CreateETSBooleanType(result);
97}
98
99template <typename TargetType>
100Type *ETSChecker::PerformArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
101{
102    using UType = typename TargetType::UType;
103
104    UType leftValue = GetOperand<TargetType>(left);
105    UType rightValue = GetOperand<TargetType>(right);
106    auto result = leftValue;
107    auto isForbiddenZeroDivision = [&rightValue]() { return std::is_integral<UType>::value && rightValue == 0; };
108
109    switch (operationType) {
110        case lexer::TokenType::PUNCTUATOR_PLUS:
111        case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
112            result = leftValue + rightValue;
113            break;
114        }
115        case lexer::TokenType::PUNCTUATOR_MINUS:
116        case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: {
117            result = leftValue - rightValue;
118            break;
119        }
120        case lexer::TokenType::PUNCTUATOR_DIVIDE:
121        case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: {
122            if (isForbiddenZeroDivision()) {
123                return nullptr;
124            }
125            result = leftValue / rightValue;
126            break;
127        }
128        case lexer::TokenType::PUNCTUATOR_MULTIPLY:
129        case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: {
130            result = leftValue * rightValue;
131            break;
132        }
133        case lexer::TokenType::PUNCTUATOR_MOD:
134        case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: {
135            if (isForbiddenZeroDivision()) {
136                return nullptr;
137            }
138            result = HandleModulo<UType>(leftValue, rightValue);
139            break;
140        }
141        default: {
142            UNREACHABLE();
143        }
144    }
145
146    return Allocator()->New<TargetType>(result);
147}
148
149template <>
150inline IntType::UType ark::es2panda::checker::ETSChecker::HandleModulo<IntType::UType>(IntType::UType leftValue,
151                                                                                       IntType::UType rightValue)
152{
153    ASSERT(rightValue != 0);
154    return leftValue % rightValue;
155}
156
157template <>
158inline LongType::UType ark::es2panda::checker::ETSChecker::HandleModulo<LongType::UType>(LongType::UType leftValue,
159                                                                                         LongType::UType rightValue)
160{
161    ASSERT(rightValue != 0);
162    return leftValue % rightValue;
163}
164
165template <>
166inline FloatType::UType ark::es2panda::checker::ETSChecker::HandleModulo<FloatType::UType>(FloatType::UType leftValue,
167                                                                                           FloatType::UType rightValue)
168{
169    return std::fmod(leftValue, rightValue);
170}
171
172template <>
173inline DoubleType::UType ark::es2panda::checker::ETSChecker::HandleModulo<DoubleType::UType>(
174    DoubleType::UType leftValue, DoubleType::UType rightValue)
175{
176    return std::fmod(leftValue, rightValue);
177}
178
179template <typename IntegerUType, typename FloatOrIntegerUType>
180inline IntegerUType CastIfFloat(FloatOrIntegerUType num)
181{
182    if constexpr (std::is_floating_point_v<FloatOrIntegerUType>) {
183        return CastFloatToInt<FloatOrIntegerUType, IntegerUType>(num);
184    } else {
185        return num;
186    }
187}
188
189template <typename FloatOrIntegerType, typename IntegerType>
190Type *ETSChecker::HandleBitWiseArithmetic(Type *left, Type *right, lexer::TokenType operationType)
191{
192    using IntegerUType = typename IntegerType::UType;
193    using UnsignedUType = std::make_unsigned_t<IntegerUType>;
194
195    UnsignedUType result = 0;
196    UnsignedUType unsignedLeftValue = CastIfFloat<IntegerUType>(GetOperand<FloatOrIntegerType>(left));
197    UnsignedUType unsignedRightValue = CastIfFloat<IntegerUType>(GetOperand<FloatOrIntegerType>(right));
198
199    auto mask = std::numeric_limits<UnsignedUType>::digits - 1U;
200    auto shift = unsignedRightValue & mask;
201
202    switch (operationType) {
203        case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
204        case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: {
205            result = unsignedLeftValue & unsignedRightValue;
206            break;
207        }
208        case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
209        case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
210            result = unsignedLeftValue | unsignedRightValue;
211            break;
212        }
213        case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
214        case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: {
215            result = unsignedLeftValue ^ unsignedRightValue;
216            break;
217        }
218        case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
219        case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: {
220            static_assert(sizeof(UnsignedUType) == 4 || sizeof(UnsignedUType) == 8);
221            result = unsignedLeftValue << shift;
222            break;
223        }
224        case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
225        case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: {
226            static_assert(sizeof(IntegerUType) == 4 || sizeof(IntegerUType) == 8);
227            result = static_cast<IntegerUType>(unsignedLeftValue) >> shift;  // NOLINT(hicpp-signed-bitwise)
228            break;
229        }
230        case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
231        case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: {
232            static_assert(sizeof(UnsignedUType) == 4 || sizeof(UnsignedUType) == 8);
233            result = unsignedLeftValue >> shift;
234            break;
235        }
236        default: {
237            UNREACHABLE();
238        }
239    }
240
241    return Allocator()->New<IntegerType>(result);
242}
243}  // namespace ark::es2panda::checker
244
245#endif
246