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 #include "arithmetic.h"
17 
18 namespace ark::es2panda::checker {
19 
RepairTypeErrorsInOperands(Type **left, Type **right)20 static inline void RepairTypeErrorsInOperands(Type **left, Type **right)
21 {
22     if (IsTypeError(*left)) {
23         *left = *right;
24     }
25     if (IsTypeError(*right)) {
26         *right = *left;
27     }
28 }
29 
RepairTypeErrorWithDefault(Type **type, Type *dflt)30 static inline void RepairTypeErrorWithDefault(Type **type, Type *dflt)
31 {
32     if (IsTypeError(*type)) {
33         *type = dflt;
34     }
35 }
36 
NegateNumericType(Type *type, ir::Expression *node)37 Type *ETSChecker::NegateNumericType(Type *type, ir::Expression *node)
38 {
39     ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
40 
41     TypeFlag typeKind = ETSType(type);
42     Type *result = nullptr;
43 
44     switch (typeKind) {
45         case TypeFlag::BYTE: {
46             result = CreateByteType(-(type->AsByteType()->GetValue()));
47             break;
48         }
49         case TypeFlag::CHAR: {
50             result = CreateCharType(-(type->AsCharType()->GetValue()));
51             break;
52         }
53         case TypeFlag::SHORT: {
54             result = CreateShortType(-(type->AsShortType()->GetValue()));
55             break;
56         }
57         case TypeFlag::INT: {
58             result = CreateIntType(-(type->AsIntType()->GetValue()));
59             break;
60         }
61         case TypeFlag::LONG: {
62             result = CreateLongType(-(type->AsLongType()->GetValue()));
63             break;
64         }
65         case TypeFlag::FLOAT: {
66             result = CreateFloatType(-(type->AsFloatType()->GetValue()));
67             break;
68         }
69         case TypeFlag::DOUBLE: {
70             result = CreateDoubleType(-(type->AsDoubleType()->GetValue()));
71             break;
72         }
73         default: {
74             UNREACHABLE();
75         }
76     }
77 
78     node->SetTsType(result);
79     return result;
80 }
81 
BitwiseNegateNumericType(Type *type, ir::Expression *node)82 Type *ETSChecker::BitwiseNegateNumericType(Type *type, ir::Expression *node)
83 {
84     ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_INTEGRAL));
85 
86     TypeFlag typeKind = ETSType(type);
87 
88     Type *result = nullptr;
89 
90     switch (typeKind) {
91         case TypeFlag::BYTE: {
92             result = CreateByteType(static_cast<int8_t>(~static_cast<uint8_t>(type->AsByteType()->GetValue())));
93             break;
94         }
95         case TypeFlag::CHAR: {
96             result = CreateCharType(~(type->AsCharType()->GetValue()));
97             break;
98         }
99         case TypeFlag::SHORT: {
100             result = CreateShortType(static_cast<int16_t>(~static_cast<uint16_t>(type->AsShortType()->GetValue())));
101             break;
102         }
103         case TypeFlag::INT: {
104             result = CreateIntType(static_cast<int32_t>(~static_cast<uint32_t>(type->AsIntType()->GetValue())));
105             break;
106         }
107         case TypeFlag::LONG: {
108             result = CreateLongType(static_cast<int64_t>(~static_cast<uint64_t>(type->AsLongType()->GetValue())));
109             break;
110         }
111         case TypeFlag::FLOAT: {
112             result = CreateIntType(
113                 ~static_cast<uint32_t>(CastFloatToInt<FloatType::UType, int32_t>(type->AsFloatType()->GetValue())));
114             break;
115         }
116         case TypeFlag::DOUBLE: {
117             result = CreateLongType(
118                 ~static_cast<uint64_t>(CastFloatToInt<DoubleType::UType, int64_t>(type->AsDoubleType()->GetValue())));
119             break;
120         }
121         default: {
122             UNREACHABLE();
123         }
124     }
125 
126     node->SetTsType(result);
127     return result;
128 }
129 
HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)130 Type *ETSChecker::HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
131 {
132     ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
133            right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
134 
135     if (left->IsDoubleType() || right->IsDoubleType()) {
136         return PerformRelationOperationOnTypes<DoubleType>(left, right, operationType);
137     }
138 
139     if (left->IsFloatType() || right->IsFloatType()) {
140         return PerformRelationOperationOnTypes<FloatType>(left, right, operationType);
141     }
142 
143     if (left->IsLongType() || right->IsLongType()) {
144         return PerformRelationOperationOnTypes<LongType>(left, right, operationType);
145     }
146 
147     return PerformRelationOperationOnTypes<IntType>(left, right, operationType);
148 }
149 
CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::TokenType op)150 bool ETSChecker::CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::TokenType op)
151 {
152     if ((left == nullptr) || (right == nullptr)) {
153         return false;
154     }
155 
156     if (!left->IsETSBigIntType()) {
157         return false;
158     }
159 
160     if (!right->IsETSBigIntType()) {
161         return false;
162     }
163 
164     switch (op) {
165         case lexer::TokenType::PUNCTUATOR_EQUAL:
166         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
167         case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
168         case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
169         case lexer::TokenType::KEYW_INSTANCEOF:
170             // This is handled in the main CheckBinaryOperator function
171             return false;
172         default:
173             break;
174     }
175 
176     return true;
177 }
178 
CheckBinaryPlusMultDivOperandsForUnionType(const Type *leftType, const Type *rightType, const ir::Expression *left, const ir::Expression *right)179 bool ETSChecker::CheckBinaryPlusMultDivOperandsForUnionType(const Type *leftType, const Type *rightType,
180                                                             const ir::Expression *left, const ir::Expression *right)
181 {
182     std::stringstream ss;
183     if (leftType->IsETSUnionType()) {
184         leftType->AsETSUnionType()->ToString(ss, false);
185         LogTypeError("Bad operand type: multiple types left in the normalized union type (" + ss.str() +
186                          "). Unions are not allowed in binary expressions except equality.",
187                      left->Start());
188         return false;
189     }
190     if (rightType->IsETSUnionType()) {
191         rightType->AsETSUnionType()->ToString(ss, false);
192         LogTypeError("Bad operand type: multiple types left in the normalized union type (" + ss.str() +
193                          "). Unions are not allowed in binary expressions except equality.",
194                      right->Start());
195         return false;
196     }
197     return true;
198 }
199 
CheckBinaryOperatorMulDivMod( std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)200 checker::Type *ETSChecker::CheckBinaryOperatorMulDivMod(
201     std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
202     std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
203 {
204     auto [left, right, operationType, pos] = op;
205     auto [leftType, rightType, unboxedL, unboxedR] = types;
206 
207     // Try to handle errors on a lower level
208     RepairTypeErrorsInOperands(&leftType, &rightType);
209     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
210     if (leftType->IsTypeError()) {  // both are errors
211         return GlobalTypeError();
212     }
213 
214     checker::Type *tsType {};
215     auto [promotedType, bothConst] =
216         ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
217 
218     FlagExpressionWithUnboxing(leftType, unboxedL, left);
219     FlagExpressionWithUnboxing(rightType, unboxedR, right);
220 
221     if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) {
222         return GlobalTypeError();
223     }
224 
225     if (promotedType == nullptr && !bothConst) {
226         LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
227         return GlobalTypeError();
228     }
229 
230     if (bothConst) {
231         tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType);
232     }
233 
234     tsType = (tsType != nullptr) ? tsType : promotedType;
235     return tsType;
236 }
237 
CheckBinaryOperatorPlusForEnums(const checker::Type *const leftType, const checker::Type *const rightType)238 checker::Type *ETSChecker::CheckBinaryOperatorPlusForEnums(const checker::Type *const leftType,
239                                                            const checker::Type *const rightType)
240 {
241     if (leftType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
242         rightType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
243         if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) {
244             return GlobalIntType();
245         }
246         if (leftType->IsFloatType() || rightType->IsFloatType()) {
247             return GlobalFloatType();
248         }
249         if (leftType->IsLongType() || rightType->IsLongType()) {
250             return GlobalLongType();
251         }
252         return GlobalIntType();
253     }
254     if ((leftType->IsETSStringEnumType() && (rightType->IsETSStringType() || rightType->IsETSStringEnumType())) ||
255         (rightType->IsETSStringEnumType() && (leftType->IsETSStringType() || leftType->IsETSStringEnumType()))) {
256         return GlobalETSStringLiteralType();
257     }
258     return nullptr;
259 }
260 
CheckBinaryOperatorPlus( std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)261 checker::Type *ETSChecker::CheckBinaryOperatorPlus(
262     std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
263     std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
264 {
265     auto [left, right, operationType, pos] = op;
266     auto [leftType, rightType, unboxedL, unboxedR] = types;
267 
268     // Try to handle errors on a lower level
269     RepairTypeErrorsInOperands(&leftType, &rightType);
270     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
271     if (leftType->IsTypeError()) {  // both are errors
272         return GlobalTypeError();
273     }
274 
275     if (leftType->IsETSStringType() || rightType->IsETSStringType()) {
276         if (operationType == lexer::TokenType::PUNCTUATOR_MINUS ||
277             operationType == lexer::TokenType::PUNCTUATOR_MINUS_EQUAL) {
278             LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
279             return GlobalTypeError();
280         }
281 
282         return HandleStringConcatenation(leftType, rightType);
283     }
284 
285     if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) {
286         return GlobalTypeError();
287     }
288 
289     auto [promotedType, bothConst] =
290         ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
291 
292     FlagExpressionWithUnboxing(leftType, unboxedL, left);
293     FlagExpressionWithUnboxing(rightType, unboxedR, right);
294 
295     if (promotedType == nullptr && !bothConst) {
296         auto type = CheckBinaryOperatorPlusForEnums(leftType, rightType);
297         if (type != nullptr) {
298             return type;
299         }
300         LogTypeError("Bad operand type, the types of the operands must be numeric type, enum or String.", pos);
301         return GlobalTypeError();
302     }
303 
304     if (bothConst) {
305         return HandleArithmeticOperationOnTypes(leftType, rightType, operationType);
306     }
307 
308     return promotedType;
309 }
310 
CheckBinaryOperatorShift( std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)311 checker::Type *ETSChecker::CheckBinaryOperatorShift(
312     std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
313     std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
314 {
315     auto [left, right, operationType, pos] = op;
316     auto [leftType, rightType, unboxedL, unboxedR] = types;
317 
318     RepairTypeErrorWithDefault(&leftType, GlobalIntType());
319     RepairTypeErrorWithDefault(&rightType, GlobalIntType());
320     RepairTypeErrorWithDefault(&unboxedL, GlobalIntType());
321     RepairTypeErrorWithDefault(&unboxedR, GlobalIntType());
322 
323     if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
324         LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
325         return GlobalTypeError();
326     }
327 
328     auto promotedLeftType = ApplyUnaryOperatorPromotion(unboxedL, false, !isEqualOp);
329     auto promotedRightType = ApplyUnaryOperatorPromotion(unboxedR, false, !isEqualOp);
330 
331     FlagExpressionWithUnboxing(leftType, unboxedL, left);
332     FlagExpressionWithUnboxing(rightType, unboxedR, right);
333 
334     if (promotedLeftType == nullptr || !promotedLeftType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) ||
335         promotedRightType == nullptr ||
336         !promotedRightType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
337         LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
338         return GlobalTypeError();
339     }
340 
341     if (promotedLeftType->HasTypeFlag(TypeFlag::CONSTANT) && promotedRightType->HasTypeFlag(TypeFlag::CONSTANT)) {
342         return HandleBitwiseOperationOnTypes(promotedLeftType, promotedRightType, operationType);
343     }
344 
345     switch (ETSType(promotedLeftType)) {
346         case TypeFlag::BYTE: {
347             return GlobalByteType();
348         }
349         case TypeFlag::SHORT: {
350             return GlobalShortType();
351         }
352         case TypeFlag::CHAR: {
353             return GlobalCharType();
354         }
355         case TypeFlag::INT:
356         case TypeFlag::FLOAT: {
357             return GlobalIntType();
358         }
359         case TypeFlag::LONG:
360         case TypeFlag::DOUBLE: {
361             return GlobalLongType();
362         }
363         default: {
364             UNREACHABLE();
365         }
366     }
367     return nullptr;
368 }
369 
CheckBinaryOperatorBitwise( std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)370 checker::Type *ETSChecker::CheckBinaryOperatorBitwise(
371     std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp,
372     std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
373 {
374     auto [left, right, operationType, pos] = op;
375     auto [leftType, rightType, unboxedL, unboxedR] = types;
376 
377     RepairTypeErrorsInOperands(&leftType, &rightType);
378     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
379     if (leftType->IsTypeError()) {  // both are errors
380         return GlobalTypeError();
381     }
382 
383     if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
384         LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
385         return GlobalTypeError();
386     }
387 
388     if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr &&
389         unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) {
390         FlagExpressionWithUnboxing(leftType, unboxedL, left);
391         FlagExpressionWithUnboxing(rightType, unboxedR, right);
392         return HandleBooleanLogicalOperators(unboxedL, unboxedR, operationType);
393     }
394 
395     auto [promotedType, bothConst] =
396         ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC, !isEqualOp);
397 
398     FlagExpressionWithUnboxing(leftType, unboxedL, left);
399     FlagExpressionWithUnboxing(rightType, unboxedR, right);
400 
401     if (promotedType == nullptr && !bothConst) {
402         LogTypeError("Bad operand type, the types of the operands must be numeric type.", pos);
403         return GlobalTypeError();
404     }
405 
406     if (bothConst) {
407         return HandleBitwiseOperationOnTypes(leftType, rightType, operationType);
408     }
409 
410     return SelectGlobalIntegerTypeForNumeric(promotedType);
411 }
412 
CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, ir::Expression *expr, lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType, Type *unboxedL, Type *unboxedR)413 checker::Type *ETSChecker::CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, ir::Expression *expr,
414                                                       lexer::SourcePosition pos, checker::Type *leftType,
415                                                       checker::Type *rightType, Type *unboxedL, Type *unboxedR)
416 {
417     RepairTypeErrorsInOperands(&leftType, &rightType);
418     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
419     if (leftType->IsTypeError()) {  // both are errors
420         return GlobalTypeError();
421     }
422 
423     if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
424         LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
425         return GlobalTypeError();
426     }
427 
428     if (unboxedL == nullptr || !unboxedL->IsConditionalExprType() || unboxedR == nullptr ||
429         !unboxedR->IsConditionalExprType()) {
430         LogTypeError("Bad operand type, the types of the operands must be of possible condition type.", pos);
431         return GlobalTypeError();
432     }
433 
434     if (unboxedL->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
435         FlagExpressionWithUnboxing(leftType, unboxedL, left);
436     }
437 
438     if (unboxedR->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
439         FlagExpressionWithUnboxing(rightType, unboxedR, right);
440     }
441 
442     if (expr->IsBinaryExpression()) {
443         return HandleBooleanLogicalOperatorsExtended(unboxedL, unboxedR, expr->AsBinaryExpression());
444     }
445 
446     UNREACHABLE();
447 }
448 
LogOperatorCannotBeApplied(lexer::TokenType operationType, checker::Type *const leftType, checker::Type *const rightType, lexer::SourcePosition pos)449 void ETSChecker::LogOperatorCannotBeApplied(lexer::TokenType operationType, checker::Type *const leftType,
450                                             checker::Type *const rightType, lexer::SourcePosition pos)
451 {
452     LogTypeError({"Operator '", operationType, "' cannot be applied to types '", leftType, "' and '", rightType, "'."},
453                  pos);
454 }
455 
CheckValidEqualReferenceType(checker::Type *const leftType, checker::Type *const rightType)456 bool ETSChecker::CheckValidEqualReferenceType(checker::Type *const leftType, checker::Type *const rightType)
457 {
458     auto isGlobalObjectType {[](checker::Type *const type) -> bool {
459         return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType();
460     }};
461 
462     // Equality expression is always allowed for Object, undefined and null
463     if (isGlobalObjectType(leftType) || isGlobalObjectType(rightType) || leftType->IsETSUndefinedType() ||
464         rightType->IsETSUndefinedType() || leftType->IsETSNullType() || rightType->IsETSNullType()) {
465         return true;
466     }
467 
468     // NOTE (mxlgv): Skip for unions. Required implementation of the specification section:
469     // 7.25.6 Reference Equality Based on Actual Type (Union Equality Operators)
470     if (leftType->IsETSUnionType()) {
471         return leftType->AsETSUnionType()->IsOverlapWith(Relation(), rightType);
472     }
473     if (rightType->IsETSUnionType()) {
474         return rightType->AsETSUnionType()->IsOverlapWith(Relation(), leftType);
475     }
476 
477     // NOTE (mxlgv): Skip for generic. Required implementation of the specification section:
478     // 7.25.6 Reference Equality Based on Actual Type (Type Parameter Equality Operators)
479     if (leftType->HasTypeFlag(TypeFlag::GENERIC) || rightType->HasTypeFlag(TypeFlag::GENERIC)) {
480         return true;
481     }
482 
483     // Equality expression can only be applied to String and String, and BigInt and BigInt
484     if (leftType->IsETSStringType() || rightType->IsETSStringType() || leftType->IsETSBigIntType() ||
485         rightType->IsETSBigIntType()) {
486         auto *const nonConstLhs = GetNonConstantType(leftType);
487         auto *const nonConstRhs = GetNonConstantType(rightType);
488         if (!Relation()->IsIdenticalTo(nonConstLhs, nonConstRhs) &&
489             !Relation()->IsIdenticalTo(nonConstRhs, nonConstLhs)) {
490             return false;
491         }
492     }
493 
494     return true;
495 }
496 
CheckBinaryOperatorStrictEqual(ir::Expression *left, lexer::TokenType operationType, lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType)497 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expression *left,
498                                                                       lexer::TokenType operationType,
499                                                                       lexer::SourcePosition pos,
500                                                                       checker::Type *leftType, checker::Type *rightType)
501 {
502     RepairTypeErrorsInOperands(&leftType, &rightType);
503     if (leftType->IsTypeError()) {  // both are errors
504         // We still know that operation result should be boolean, so recover.
505         return {GlobalETSBooleanType(), GlobalETSObjectType()};
506     }
507 
508     checker::Type *tsType {};
509     if (!IsReferenceType(leftType) || !IsReferenceType(rightType)) {
510         LogTypeError("Both operands have to be reference types", pos);
511         return {GlobalETSBooleanType(), GlobalETSObjectType()};
512     }
513 
514     Relation()->SetNode(left);
515     if (!CheckValidEqualReferenceType(leftType, rightType)) {
516         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
517         return {GlobalETSBooleanType(), GlobalETSObjectType()};
518     }
519 
520     if (!Relation()->IsCastableTo(leftType, rightType) && !Relation()->IsCastableTo(rightType, leftType)) {
521         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
522         return {GlobalETSBooleanType(), GlobalETSObjectType()};
523     }
524 
525     tsType = GlobalETSBooleanType();
526     if (rightType->IsETSDynamicType() && leftType->IsETSDynamicType()) {
527         return {tsType, GlobalBuiltinJSValueType()};
528     }
529 
530     return {tsType, GlobalETSObjectType()};
531 }
532 
CheckBinaryOperatorEqualError(checker::Type *const leftType, checker::Type *const rightType, checker::Type *tsType, lexer::SourcePosition pos)533 std::optional<std::tuple<Type *, Type *>> ETSChecker::CheckBinaryOperatorEqualError(checker::Type *const leftType,
534                                                                                     checker::Type *const rightType,
535                                                                                     checker::Type *tsType,
536                                                                                     lexer::SourcePosition pos)
537 {
538     if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) {
539         if (!leftType->AsETSIntEnumType()->IsSameEnumType(rightType->AsETSIntEnumType())) {
540             // We still know that operation result should be boolean, so recover.
541             LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
542             return {{GlobalETSBooleanType(), leftType}};
543         }
544 
545         tsType = GlobalETSBooleanType();
546         return std::make_tuple(tsType, leftType);
547     }
548 
549     if (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType()) {
550         if (!leftType->AsETSStringEnumType()->IsSameEnumType(rightType->AsETSStringEnumType())) {
551             LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
552             return {{GlobalETSBooleanType(), leftType}};
553         }
554 
555         tsType = GlobalETSBooleanType();
556         return std::make_tuple(tsType, leftType);
557     }
558     return std::nullopt;
559 }
560 
CheckBinaryOperatorEqual(ir::Expression *left, ir::Expression *right, lexer::TokenType operationType, lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType, Type *unboxedL, Type *unboxedR)561 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqual(ir::Expression *left, ir::Expression *right,
562                                                                 lexer::TokenType operationType,
563                                                                 lexer::SourcePosition pos, checker::Type *leftType,
564                                                                 checker::Type *rightType, Type *unboxedL,
565                                                                 Type *unboxedR)
566 {
567     RepairTypeErrorsInOperands(&leftType, &rightType);
568     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
569     if (leftType->IsTypeError()) {  // both are errors
570         return {GlobalETSBooleanType(), GlobalTypeError()};
571     }
572 
573     checker::Type *tsType {};
574 
575     auto checkError = CheckBinaryOperatorEqualError(leftType, rightType, tsType, pos);
576     if (checkError.has_value()) {
577         return checkError.value();
578     }
579     if (leftType->IsETSDynamicType() || rightType->IsETSDynamicType()) {
580         return CheckBinaryOperatorEqualDynamic(left, right, pos);
581     }
582 
583     if (IsReferenceType(leftType) && IsReferenceType(rightType)) {
584         Relation()->SetNode(left);
585         if (!CheckValidEqualReferenceType(leftType, rightType)) {
586             LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
587             return {GlobalETSBooleanType(), leftType};
588         }
589 
590         tsType = GlobalETSBooleanType();
591         return {tsType, CreateETSUnionType({leftType, rightType})};
592     }
593 
594     if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr &&
595         unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) {
596         if (unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
597             bool res = unboxedL->AsETSBooleanType()->GetValue() == unboxedR->AsETSBooleanType()->GetValue();
598 
599             tsType = CreateETSBooleanType(operationType == lexer::TokenType::PUNCTUATOR_EQUAL ? res : !res);
600             return {tsType, tsType};
601         }
602 
603         FlagExpressionWithUnboxing(leftType, unboxedL, left);
604         FlagExpressionWithUnboxing(rightType, unboxedR, right);
605 
606         tsType = GlobalETSBooleanType();
607         return {tsType, tsType};
608     }
609 
610     // Temporary workaround before == and === refactoring
611     if ((rightType->IsETSNullType() && leftType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) ||
612         (leftType->IsETSNullType() && rightType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE))) {
613         return {GlobalETSBooleanType(), CreateETSUnionType({leftType, rightType})};
614     }
615 
616     return {nullptr, nullptr};
617 }
618 
CheckBinaryOperatorEqualDynamic(ir::Expression *left, ir::Expression *right, lexer::SourcePosition pos)619 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqualDynamic(ir::Expression *left, ir::Expression *right,
620                                                                        lexer::SourcePosition pos)
621 {
622     // NOTE: vpukhov. enforce intrinsic call in any case?
623     // canonicalize
624     auto *const dynExp = left->TsType()->IsETSDynamicType() ? left : right;
625     auto *const otherExp = dynExp == left ? right : left;
626 
627     if (otherExp->TsType()->IsETSDynamicType()) {
628         return {GlobalETSBooleanType(), GlobalBuiltinJSValueType()};
629     }
630     if (dynExp->TsType()->AsETSDynamicType()->IsConvertible(otherExp->TsType())) {
631         // NOTE: vpukhov. boxing flags are not set in dynamic values
632         return {GlobalETSBooleanType(), otherExp->TsType()};
633     }
634     if (IsReferenceType(otherExp->TsType())) {
635         // have to prevent casting dyn_exp via ApplyCast without nullish flag
636         return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
637     }
638     LogTypeError("Unimplemented case in dynamic type comparison.", pos);
639     return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
640 }
641 
642 // Satisfying the Chinese checker
NonNumericTypesAreAppropriateForComparison(Type *leftType, Type *rightType)643 static bool NonNumericTypesAreAppropriateForComparison(Type *leftType, Type *rightType)
644 {
645     return (rightType->IsETSStringType() && leftType->IsETSStringType()) ||
646            (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
647              (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
648             leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType()));
649 }
650 
CheckBinaryOperatorLessGreater(ir::Expression *left, ir::Expression *right, lexer::TokenType operationType, lexer::SourcePosition pos, bool isEqualOp, checker::Type *leftType, checker::Type *rightType, Type *unboxedL, Type *unboxedR)651 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorLessGreater(ir::Expression *left, ir::Expression *right,
652                                                                       lexer::TokenType operationType,
653                                                                       lexer::SourcePosition pos, bool isEqualOp,
654                                                                       checker::Type *leftType, checker::Type *rightType,
655                                                                       Type *unboxedL, Type *unboxedR)
656 {
657     RepairTypeErrorsInOperands(&leftType, &rightType);
658     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
659     if (leftType->IsTypeError()) {  // both are errors
660         return {GlobalETSBooleanType(), GlobalTypeError()};
661     }
662 
663     if ((leftType->IsETSUnionType() || rightType->IsETSUnionType()) &&
664         operationType != lexer::TokenType::PUNCTUATOR_EQUAL &&
665         operationType != lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
666         LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
667         return {GlobalETSBooleanType(), leftType};
668     }
669 
670     checker::Type *tsType {};
671     auto [promotedType, bothConst] =
672         ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_PRIMITIVE, !isEqualOp);
673 
674     FlagExpressionWithUnboxing(leftType, unboxedL, left);
675     FlagExpressionWithUnboxing(rightType, unboxedR, right);
676 
677     if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
678         return {GlobalETSBooleanType(), CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)})};
679     }
680 
681     if ((unboxedL != nullptr) && (unboxedR != nullptr) &&
682         (unboxedL->IsETSBooleanType() != unboxedR->IsETSBooleanType())) {
683         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
684         return {GlobalETSBooleanType(), leftType};
685     }
686 
687     if (promotedType == nullptr && !bothConst) {
688         if (NonNumericTypesAreAppropriateForComparison(leftType, rightType)) {
689             return {GlobalETSBooleanType(), GlobalETSBooleanType()};
690         }
691         if (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
692              (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
693             leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType())) {
694             return {GlobalETSBooleanType(), GlobalETSBooleanType()};
695         }
696 
697         LogTypeError("Bad operand type, the types of the operands must be numeric, same enumeration, or boolean type.",
698                      pos);
699         return {GlobalETSBooleanType(), GlobalETSBooleanType()};
700     }
701 
702     if (bothConst) {
703         tsType = HandleRelationOperationOnTypes(leftType, rightType, operationType);
704         return {tsType, tsType};
705     }
706 
707     tsType = GlobalETSBooleanType();
708     auto *opType = promotedType;
709     return {tsType, opType};
710 }
711 
CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType)712 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos, checker::Type *leftType,
713                                                                      checker::Type *rightType)
714 {
715     RepairTypeErrorsInOperands(&leftType, &rightType);
716     if (leftType->IsTypeError()) {  // both are errors
717         return {GlobalETSBooleanType(), GlobalTypeError()};
718     }
719 
720     checker::Type *tsType {};
721     if (!IsReferenceType(leftType) || (!IsReferenceType(rightType) && !rightType->IsETSEnumType())) {
722         LogTypeError("Bad operand type, the types of the operands must be same type.", pos);
723         return {GlobalETSBooleanType(), leftType};
724     }
725 
726     if (rightType->IsETSDynamicType() && !rightType->AsETSDynamicType()->HasDecl()) {
727         LogTypeError("Right-hand side of instanceof expression must represent a type.", pos);
728         return {GlobalETSBooleanType(), leftType};
729     }
730 
731     tsType = GlobalETSBooleanType();
732     checker::Type *opType = rightType->IsETSDynamicType() ? GlobalBuiltinJSValueType() : GlobalETSObjectType();
733     ComputeApparentType(rightType);
734     RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
735 
736     return {tsType, opType};
737 }
738 
AdjustNumberLiteralType(ir::NumberLiteral *const literal, Type *literalType, Type *const otherType)739 bool ETSChecker::AdjustNumberLiteralType(ir::NumberLiteral *const literal, Type *literalType, Type *const otherType)
740 {
741     if (otherType->IsETSObjectType()) {
742         auto *const objectType = otherType->AsETSObjectType();
743         if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) && !objectType->IsETSStringType()) {
744             literal->RemoveBoxingUnboxingFlags(GetBoxingFlag(literalType));
745             literalType = ETSBuiltinTypeAsPrimitiveType(objectType);
746             literal->SetTsType(literalType);
747             literal->AddBoxingUnboxingFlags(GetBoxingFlag(literalType));
748             return true;
749         }
750     }
751     return false;
752 }
753 
CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right, lexer::SourcePosition pos)754 Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right,
755                                                        lexer::SourcePosition pos)
756 {
757     auto *leftType = left->TsType();
758     if (!IsReferenceType(leftType) && !leftType->IsETSEnumType()) {
759         LogTypeError("Left-hand side of nullish-coalescing expression must be a reference or enum type.", pos);
760         return leftType;
761     }
762 
763     auto *rightType = right->TsType();
764 
765     if (leftType->IsETSEnumType()) {
766         left->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
767         leftType = leftType->AsETSEnumType()->GetDecl()->BoxedClass()->TsType();
768     } else {
769         leftType = GetNonNullishType(leftType);
770     }
771 
772     if (rightType->IsETSEnumType()) {
773         right->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
774         rightType = rightType->AsETSEnumType()->GetDecl()->BoxedClass()->TsType();
775     } else {
776         rightType = MaybeBoxExpression(right);
777     }
778 
779     if (IsTypeIdenticalTo(leftType, rightType)) {
780         return leftType;
781     }
782 
783     //  If possible and required update number literal type to the proper value (identical to left-side type)
784     if (right->IsNumberLiteral() && AdjustNumberLiteralType(right->AsNumberLiteral(), rightType, leftType)) {
785         return leftType;
786     }
787 
788     return CreateETSUnionType({leftType, rightType});
789 }
790 
791 using CheckBinaryFunction = std::function<checker::Type *(
792     ETSChecker *, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op,
793     bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)>;
794 
GetCheckMap()795 std::map<lexer::TokenType, CheckBinaryFunction> &GetCheckMap()
796 {
797     static std::map<lexer::TokenType, CheckBinaryFunction> checkMap = {
798         {lexer::TokenType::PUNCTUATOR_MULTIPLY, &ETSChecker::CheckBinaryOperatorMulDivMod},
799         {lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
800         {lexer::TokenType::PUNCTUATOR_DIVIDE, &ETSChecker::CheckBinaryOperatorMulDivMod},
801         {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
802         {lexer::TokenType::PUNCTUATOR_MOD, &ETSChecker::CheckBinaryOperatorMulDivMod},
803         {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
804 
805         {lexer::TokenType::PUNCTUATOR_MINUS, &ETSChecker::CheckBinaryOperatorPlus},
806         {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
807         {lexer::TokenType::PUNCTUATOR_PLUS, &ETSChecker::CheckBinaryOperatorPlus},
808         {lexer::TokenType::PUNCTUATOR_PLUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
809 
810         {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
811         {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
812         {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
813         {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
814         {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
815         {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
816 
817         {lexer::TokenType::PUNCTUATOR_BITWISE_OR, &ETSChecker::CheckBinaryOperatorBitwise},
818         {lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
819         {lexer::TokenType::PUNCTUATOR_BITWISE_AND, &ETSChecker::CheckBinaryOperatorBitwise},
820         {lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
821         {lexer::TokenType::PUNCTUATOR_BITWISE_XOR, &ETSChecker::CheckBinaryOperatorBitwise},
822         {lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
823     };
824 
825     return checkMap;
826 }
827 
828 struct BinaryOperatorParams {
829     ir::Expression *left;
830     ir::Expression *right;
831     ir::Expression *expr;
832     lexer::TokenType operationType;
833     lexer::SourcePosition pos;
834     bool isEqualOp;
835 };
836 
837 struct TypeParams {
838     checker::Type *leftType;
839     checker::Type *rightType;
840     Type *unboxedL;
841     Type *unboxedR;
842 };
843 
CheckBinaryOperatorHelper(ETSChecker *checker, const BinaryOperatorParams &binaryParams, const TypeParams &typeParams)844 static std::tuple<Type *, Type *> CheckBinaryOperatorHelper(ETSChecker *checker,
845                                                             const BinaryOperatorParams &binaryParams,
846                                                             const TypeParams &typeParams)
847 {
848     ir::Expression *left = binaryParams.left;
849     ir::Expression *right = binaryParams.right;
850     lexer::SourcePosition pos = binaryParams.pos;
851     checker::Type *const leftType = typeParams.leftType;
852     checker::Type *const rightType = typeParams.rightType;
853     checker::Type *tsType {};
854     switch (binaryParams.operationType) {
855         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND:
856         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
857             tsType = checker->CheckBinaryOperatorLogical(left, right, binaryParams.expr, pos, leftType, rightType,
858                                                          typeParams.unboxedL, typeParams.unboxedR);
859             break;
860         }
861         case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
862         case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
863             return checker->CheckBinaryOperatorStrictEqual(left, binaryParams.operationType, pos, leftType, rightType);
864         }
865         case lexer::TokenType::PUNCTUATOR_EQUAL:
866         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
867             std::tuple<Type *, Type *> res =
868                 checker->CheckBinaryOperatorEqual(left, right, binaryParams.operationType, pos, leftType, rightType,
869                                                   typeParams.unboxedL, typeParams.unboxedR);
870             if (!(std::get<0>(res) == nullptr && std::get<1>(res) == nullptr)) {
871                 return res;
872             }
873             [[fallthrough]];
874         }
875         case lexer::TokenType::PUNCTUATOR_LESS_THAN:
876         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
877         case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
878         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
879             return checker->CheckBinaryOperatorLessGreater(left, right, binaryParams.operationType, pos,
880                                                            binaryParams.isEqualOp, leftType, rightType,
881                                                            typeParams.unboxedL, typeParams.unboxedR);
882         }
883         case lexer::TokenType::KEYW_INSTANCEOF: {
884             return checker->CheckBinaryOperatorInstanceOf(pos, leftType, rightType);
885         }
886         case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: {
887             tsType = checker->CheckBinaryOperatorNullishCoalescing(left, right, pos);
888             break;
889         }
890         default: {
891             UNREACHABLE();
892             break;
893         }
894     }
895 
896     return {tsType, tsType};
897 }
898 
899 namespace {
IsStringEnum(const ir::Expression *expr)900 bool IsStringEnum(const ir::Expression *expr)
901 {
902     if (expr == nullptr) {
903         return false;
904     }
905     if (expr->TsTypeOrError()->IsTypeError()) {
906         return false;
907     }
908 
909     auto type = expr->TsType();
910     if (type == nullptr) {
911         return false;
912     }
913 
914     return type->IsETSStringEnumType();
915 }
916 
IsIntEnum(const ir::Expression *expr)917 bool IsIntEnum(const ir::Expression *expr)
918 {
919     if (expr == nullptr) {
920         return false;
921     }
922     if (expr->TsTypeOrError()->IsTypeError()) {
923         return false;
924     }
925 
926     auto type = expr->TsType();
927     if (type == nullptr) {
928         return false;
929     }
930 
931     return type->IsETSIntEnumType();
932 }
933 
CheckNumericOperatorContext(ir::Expression *expression, lexer::TokenType op)934 bool CheckNumericOperatorContext(ir::Expression *expression, lexer::TokenType op)
935 {
936     const bool isMultiplicative = op == lexer::TokenType::PUNCTUATOR_MULTIPLY ||
937                                   op == lexer::TokenType::PUNCTUATOR_DIVIDE || op == lexer::TokenType::PUNCTUATOR_MOD;
938     const bool isAdditive = op == lexer::TokenType::PUNCTUATOR_PLUS || op == lexer::TokenType::PUNCTUATOR_MINUS;
939     const bool isShift = op == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT ||
940                          op == lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT ||
941                          op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT;
942     const bool isRelational =
943         op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
944         op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
945     const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
946     const bool isBitwise = op == lexer::TokenType::PUNCTUATOR_BITWISE_AND ||
947                            op == lexer::TokenType::PUNCTUATOR_BITWISE_OR ||
948                            op == lexer::TokenType::PUNCTUATOR_BITWISE_XOR;
949     const bool isConditionalAndOr =
950         op == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || op == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
951 
952     if (IsIntEnum(expression)) {
953         if (isMultiplicative || isAdditive || isShift || isRelational || isEquality || isBitwise ||
954             isConditionalAndOr) {
955             expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
956         }
957         return true;
958     }
959     return false;
960 }
961 
CheckStringOperatorContext(ir::Expression *expression, checker::Type *otherType, lexer::TokenType op)962 void CheckStringOperatorContext(ir::Expression *expression, checker::Type *otherType, lexer::TokenType op)
963 {
964     if (IsStringEnum(expression) && (otherType->IsETSStringType() || otherType->IsETSStringEnumType())) {
965         if (op == lexer::TokenType::PUNCTUATOR_PLUS) {
966             expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
967         }
968     }
969 }
970 
CheckRelationalOperatorsBetweenEnums(ir::Expression *left, ir::Expression *right, lexer::TokenType op)971 bool CheckRelationalOperatorsBetweenEnums(ir::Expression *left, ir::Expression *right, lexer::TokenType op)
972 {
973     const bool isRelational =
974         op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
975         op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
976     const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
977 
978     if (((IsStringEnum(left) && IsStringEnum(right)) ||
979          (IsIntEnum(left) && IsIntEnum(right)))) {  // NOTE(psiket) In case of int enums it has been already checked in
980                                                     // the CheckNumericOperatorContext function
981         if (isRelational || isEquality) {
982             left->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
983             right->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
984             return true;
985         }
986     }
987     return false;
988 }
989 
CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression *expression)990 void CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression *expression)
991 {
992     if (!expression->IsBinaryExpression()) {
993         return;
994     }
995 
996     auto binaryExpression = expression->AsBinaryExpression();
997     auto op = binaryExpression->OperatorType();
998     auto leftExp = binaryExpression->Left();
999     auto rightExp = binaryExpression->Right();
1000 
1001     // Numeric Operator Context
1002     auto leftIsIntEnum = CheckNumericOperatorContext(leftExp, op);
1003     auto rightIsIntEnum = CheckNumericOperatorContext(rightExp, op);
1004     if (leftIsIntEnum || rightIsIntEnum) {
1005         return;
1006     }
1007 
1008     // String Operator Context
1009     CheckStringOperatorContext(leftExp, rightExp->TsTypeOrError(), op);
1010     CheckStringOperatorContext(rightExp, leftExp->TsTypeOrError(), op);
1011 
1012     // Relational operators if both are enumeration Types
1013     if (CheckRelationalOperatorsBetweenEnums(leftExp, rightExp, op)) {
1014         return;
1015     }
1016 }
1017 }  // namespace
1018 
CheckArithmeticOperations( ir::Expression *expr, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op, bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)1019 std::tuple<Type *, Type *> ETSChecker::CheckArithmeticOperations(
1020     ir::Expression *expr, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op,
1021     bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)
1022 {
1023     auto [left, right, operationType, pos] = op;
1024     auto [leftType, rightType, unboxedL, unboxedR] = types;
1025 
1026     if (leftType->IsETSUnionType()) {
1027         leftType = GetNonConstantType(leftType);
1028     }
1029 
1030     if (rightType->IsETSUnionType()) {
1031         rightType = GetNonConstantType(rightType);
1032     }
1033 
1034     auto checkMap = GetCheckMap();
1035     if (checkMap.find(operationType) != checkMap.end()) {
1036         auto check = checkMap[operationType];
1037         auto tsType = check(this, std::make_tuple(left, right, operationType, pos), isEqualOp,
1038                             std::make_tuple(leftType, rightType, unboxedL, unboxedR));
1039         return {tsType, tsType};
1040     }
1041 
1042     return CheckBinaryOperatorHelper(this, {left, right, expr, operationType, pos, isEqualOp},
1043                                      {leftType, rightType, unboxedL, unboxedR});
1044 }
1045 
CheckBinaryOperator(ir::Expression *left, ir::Expression *right, ir::Expression *expr, lexer::TokenType operationType, lexer::SourcePosition pos, bool forcePromotion)1046 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperator(ir::Expression *left, ir::Expression *right,
1047                                                            ir::Expression *expr, lexer::TokenType operationType,
1048                                                            lexer::SourcePosition pos, bool forcePromotion)
1049 {
1050     checker::Type *leftType = left->Check(this);
1051 
1052     if (leftType == nullptr) {
1053         LogTypeError("Unexpected type error in binary expression", left->Start());
1054         auto rightType = right->Check(this);
1055         return {rightType, rightType};
1056     }
1057 
1058     if (operationType == lexer::TokenType::KEYW_INSTANCEOF) {
1059         AddStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
1060     }
1061 
1062     Context().CheckTestSmartCastCondition(operationType);
1063 
1064     checker::Type *rightType = right->Check(this);
1065     if (right->IsTypeNode()) {
1066         rightType = right->AsTypeNode()->GetType(this);
1067     }
1068 
1069     if (rightType == nullptr) {
1070         LogTypeError("Unexpected type error in binary expression", pos);
1071         return {leftType, leftType};
1072     }
1073 
1074     CheckNeedToGenerateGetValueForBinaryExpression(expr);
1075 
1076     const bool isLogicalExtendedOperator = (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) ||
1077                                            (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
1078     Type *unboxedL =
1079         isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(leftType) : ETSBuiltinTypeAsPrimitiveType(leftType);
1080     Type *unboxedR = isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(rightType)
1081                                                : ETSBuiltinTypeAsPrimitiveType(rightType);
1082 
1083     bool isEqualOp = (operationType > lexer::TokenType::PUNCTUATOR_SUBSTITUTION &&
1084                       operationType < lexer::TokenType::PUNCTUATOR_ARROW) &&
1085                      !forcePromotion;
1086 
1087     if (CheckBinaryOperatorForBigInt(leftType, rightType, operationType)) {
1088         switch (operationType) {
1089             case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
1090             case lexer::TokenType::PUNCTUATOR_LESS_THAN:
1091             case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL:
1092             case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
1093                 return {GlobalETSBooleanType(), GlobalETSBooleanType()};
1094             default:
1095                 return {leftType, rightType};
1096         }
1097     }
1098 
1099     return CheckArithmeticOperations(expr, std::make_tuple(left, right, operationType, pos), isEqualOp,
1100                                      std::make_tuple(leftType, rightType, unboxedL, unboxedR));
1101 }
1102 
HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)1103 Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1104 {
1105     ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1106            right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1107 
1108     if (left->IsDoubleType() || right->IsDoubleType()) {
1109         return PerformArithmeticOperationOnTypes<DoubleType>(left, right, operationType);
1110     }
1111 
1112     if (left->IsFloatType() || right->IsFloatType()) {
1113         return PerformArithmeticOperationOnTypes<FloatType>(left, right, operationType);
1114     }
1115 
1116     if (left->IsLongType() || right->IsLongType()) {
1117         return PerformArithmeticOperationOnTypes<LongType>(left, right, operationType);
1118     }
1119 
1120     return PerformArithmeticOperationOnTypes<IntType>(left, right, operationType);
1121 }
1122 
HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)1123 Type *ETSChecker::HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1124 {
1125     ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1126            right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1127 
1128     if (left->IsDoubleType() || right->IsDoubleType()) {
1129         return HandleBitWiseArithmetic<DoubleType, LongType>(left, right, operationType);
1130     }
1131 
1132     if (left->IsFloatType() || right->IsFloatType()) {
1133         return HandleBitWiseArithmetic<FloatType, IntType>(left, right, operationType);
1134     }
1135 
1136     if (left->IsLongType() || right->IsLongType()) {
1137         return HandleBitWiseArithmetic<LongType>(left, right, operationType);
1138     }
1139 
1140     return HandleBitWiseArithmetic<IntType>(left, right, operationType);
1141 }
1142 
FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression)1143 void ETSChecker::FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression)
1144 {
1145     if (type->IsETSObjectType() && (unboxedType != nullptr)) {
1146         typeExpression->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedType));
1147     }
1148 }
1149 
1150 }  // namespace ark::es2panda::checker
1151