• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() || rightType->IsETSUnionType()) {
471         return true;
472     }
473 
474     // NOTE (mxlgv): Skip for generic. Required implementation of the specification section:
475     // 7.25.6 Reference Equality Based on Actual Type (Type Parameter Equality Operators)
476     if (leftType->HasTypeFlag(TypeFlag::GENERIC) || rightType->HasTypeFlag(TypeFlag::GENERIC)) {
477         return true;
478     }
479 
480     // Equality expression can only be applied to String and String, and BigInt and BigInt
481     if (leftType->IsETSStringType() || rightType->IsETSStringType() || leftType->IsETSBigIntType() ||
482         rightType->IsETSBigIntType()) {
483         if (!Relation()->IsIdenticalTo(leftType, rightType) && !Relation()->IsIdenticalTo(rightType, leftType)) {
484             return false;
485         }
486     }
487 
488     return true;
489 }
490 
CheckBinaryOperatorStrictEqual(ir::Expression * left,lexer::TokenType operationType,lexer::SourcePosition pos,checker::Type * leftType,checker::Type * rightType)491 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expression *left,
492                                                                       lexer::TokenType operationType,
493                                                                       lexer::SourcePosition pos,
494                                                                       checker::Type *leftType, checker::Type *rightType)
495 {
496     RepairTypeErrorsInOperands(&leftType, &rightType);
497     if (leftType->IsTypeError()) {  // both are errors
498         // We still know that operation result should be boolean, so recover.
499         return {GlobalETSBooleanType(), GlobalETSObjectType()};
500     }
501 
502     checker::Type *tsType {};
503     if (!IsReferenceType(leftType) || !IsReferenceType(rightType)) {
504         LogTypeError("Both operands have to be reference types", pos);
505         return {GlobalETSBooleanType(), GlobalETSObjectType()};
506     }
507 
508     Relation()->SetNode(left);
509     if (!CheckValidEqualReferenceType(leftType, rightType)) {
510         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
511         return {GlobalETSBooleanType(), GlobalETSObjectType()};
512     }
513 
514     if (!Relation()->IsCastableTo(leftType, rightType) && !Relation()->IsCastableTo(rightType, leftType)) {
515         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
516         return {GlobalETSBooleanType(), GlobalETSObjectType()};
517     }
518 
519     tsType = GlobalETSBooleanType();
520     if (rightType->IsETSDynamicType() && leftType->IsETSDynamicType()) {
521         return {tsType, GlobalBuiltinJSValueType()};
522     }
523 
524     return {tsType, GlobalETSObjectType()};
525 }
526 
CheckBinaryOperatorEqualError(checker::Type * const leftType,checker::Type * const rightType,checker::Type * tsType,lexer::SourcePosition pos)527 std::optional<std::tuple<Type *, Type *>> ETSChecker::CheckBinaryOperatorEqualError(checker::Type *const leftType,
528                                                                                     checker::Type *const rightType,
529                                                                                     checker::Type *tsType,
530                                                                                     lexer::SourcePosition pos)
531 {
532     if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) {
533         if (!leftType->AsETSIntEnumType()->IsSameEnumType(rightType->AsETSIntEnumType())) {
534             // We still know that operation result should be boolean, so recover.
535             LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
536             return {{GlobalETSBooleanType(), leftType}};
537         }
538 
539         tsType = GlobalETSBooleanType();
540         return std::make_tuple(tsType, leftType);
541     }
542 
543     if (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType()) {
544         if (!leftType->AsETSStringEnumType()->IsSameEnumType(rightType->AsETSStringEnumType())) {
545             LogTypeError("Bad operand type, the types of the operands must be the same enum type.", pos);
546             return {{GlobalETSBooleanType(), leftType}};
547         }
548 
549         tsType = GlobalETSBooleanType();
550         return std::make_tuple(tsType, leftType);
551     }
552     return std::nullopt;
553 }
554 
CheckBinaryOperatorEqual(ir::Expression * left,ir::Expression * right,lexer::TokenType operationType,lexer::SourcePosition pos,checker::Type * leftType,checker::Type * rightType,Type * unboxedL,Type * unboxedR)555 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqual(ir::Expression *left, ir::Expression *right,
556                                                                 lexer::TokenType operationType,
557                                                                 lexer::SourcePosition pos, checker::Type *leftType,
558                                                                 checker::Type *rightType, Type *unboxedL,
559                                                                 Type *unboxedR)
560 {
561     RepairTypeErrorsInOperands(&leftType, &rightType);
562     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
563     if (leftType->IsTypeError()) {  // both are errors
564         return {GlobalETSBooleanType(), GlobalTypeError()};
565     }
566 
567     checker::Type *tsType {};
568 
569     auto checkError = CheckBinaryOperatorEqualError(leftType, rightType, tsType, pos);
570     if (checkError.has_value()) {
571         return checkError.value();
572     }
573     if (leftType->IsETSDynamicType() || rightType->IsETSDynamicType()) {
574         return CheckBinaryOperatorEqualDynamic(left, right, pos);
575     }
576 
577     if (IsReferenceType(leftType) && IsReferenceType(rightType)) {
578         Relation()->SetNode(left);
579         if (!CheckValidEqualReferenceType(leftType, rightType)) {
580             LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
581             return {GlobalETSBooleanType(), leftType};
582         }
583 
584         tsType = GlobalETSBooleanType();
585         return {tsType, CreateETSUnionType({leftType, rightType})};
586     }
587 
588     if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr &&
589         unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) {
590         if (unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
591             bool res = unboxedL->AsETSBooleanType()->GetValue() == unboxedR->AsETSBooleanType()->GetValue();
592 
593             tsType = CreateETSBooleanType(operationType == lexer::TokenType::PUNCTUATOR_EQUAL ? res : !res);
594             return {tsType, tsType};
595         }
596 
597         FlagExpressionWithUnboxing(leftType, unboxedL, left);
598         FlagExpressionWithUnboxing(rightType, unboxedR, right);
599 
600         tsType = GlobalETSBooleanType();
601         return {tsType, tsType};
602     }
603 
604     // Temporary workaround before == and === refactoring
605     if ((rightType->IsETSNullType() && leftType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) ||
606         (leftType->IsETSNullType() && rightType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE))) {
607         return {GlobalETSBooleanType(), CreateETSUnionType({leftType, rightType})};
608     }
609 
610     return {nullptr, nullptr};
611 }
612 
CheckBinaryOperatorEqualDynamic(ir::Expression * left,ir::Expression * right,lexer::SourcePosition pos)613 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorEqualDynamic(ir::Expression *left, ir::Expression *right,
614                                                                        lexer::SourcePosition pos)
615 {
616     // NOTE: vpukhov. enforce intrinsic call in any case?
617     // canonicalize
618     auto *const dynExp = left->TsType()->IsETSDynamicType() ? left : right;
619     auto *const otherExp = dynExp == left ? right : left;
620 
621     if (otherExp->TsType()->IsETSDynamicType()) {
622         return {GlobalETSBooleanType(), GlobalBuiltinJSValueType()};
623     }
624     if (dynExp->TsType()->AsETSDynamicType()->IsConvertible(otherExp->TsType())) {
625         // NOTE: vpukhov. boxing flags are not set in dynamic values
626         return {GlobalETSBooleanType(), otherExp->TsType()};
627     }
628     if (IsReferenceType(otherExp->TsType())) {
629         // have to prevent casting dyn_exp via ApplyCast without nullish flag
630         return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
631     }
632     LogTypeError("Unimplemented case in dynamic type comparison.", pos);
633     return {GlobalETSBooleanType(), GlobalETSNullishObjectType()};
634 }
635 
636 // Satisfying the Chinese checker
NonNumericTypesAreAppropriateForComparison(Type * leftType,Type * rightType)637 static bool NonNumericTypesAreAppropriateForComparison(Type *leftType, Type *rightType)
638 {
639     return (rightType->IsETSStringType() && leftType->IsETSStringType()) ||
640            (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
641              (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
642             leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType()));
643 }
644 
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)645 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorLessGreater(ir::Expression *left, ir::Expression *right,
646                                                                       lexer::TokenType operationType,
647                                                                       lexer::SourcePosition pos, bool isEqualOp,
648                                                                       checker::Type *leftType, checker::Type *rightType,
649                                                                       Type *unboxedL, Type *unboxedR)
650 {
651     RepairTypeErrorsInOperands(&leftType, &rightType);
652     RepairTypeErrorsInOperands(&unboxedL, &unboxedR);
653     if (leftType->IsTypeError()) {  // both are errors
654         return {GlobalETSBooleanType(), GlobalTypeError()};
655     }
656 
657     if ((leftType->IsETSUnionType() || rightType->IsETSUnionType()) &&
658         operationType != lexer::TokenType::PUNCTUATOR_EQUAL &&
659         operationType != lexer::TokenType::PUNCTUATOR_NOT_EQUAL) {
660         LogTypeError("Bad operand type, unions are not allowed in binary expressions except equality.", pos);
661         return {GlobalETSBooleanType(), leftType};
662     }
663 
664     checker::Type *tsType {};
665     auto [promotedType, bothConst] =
666         ApplyBinaryOperatorPromotion(unboxedL, unboxedR, TypeFlag::ETS_PRIMITIVE, !isEqualOp);
667 
668     FlagExpressionWithUnboxing(leftType, unboxedL, left);
669     FlagExpressionWithUnboxing(rightType, unboxedR, right);
670 
671     if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) {
672         return {GlobalETSBooleanType(), CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)})};
673     }
674 
675     if ((unboxedL != nullptr) && (unboxedR != nullptr) &&
676         (unboxedL->IsETSBooleanType() != unboxedR->IsETSBooleanType())) {
677         LogOperatorCannotBeApplied(operationType, leftType, rightType, pos);
678         return {GlobalETSBooleanType(), leftType};
679     }
680 
681     if (promotedType == nullptr && !bothConst) {
682         if (NonNumericTypesAreAppropriateForComparison(leftType, rightType)) {
683             return {GlobalETSBooleanType(), GlobalETSBooleanType()};
684         }
685         if (((leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) ||
686              (leftType->IsETSStringEnumType() && rightType->IsETSStringEnumType())) &&
687             leftType->AsETSEnumType()->IsSameEnumType(rightType->AsETSEnumType())) {
688             return {GlobalETSBooleanType(), GlobalETSBooleanType()};
689         }
690 
691         LogTypeError("Bad operand type, the types of the operands must be numeric, same enumeration, or boolean type.",
692                      pos);
693         return {GlobalETSBooleanType(), GlobalETSBooleanType()};
694     }
695 
696     if (bothConst) {
697         tsType = HandleRelationOperationOnTypes(leftType, rightType, operationType);
698         return {tsType, tsType};
699     }
700 
701     tsType = GlobalETSBooleanType();
702     auto *opType = promotedType;
703     return {tsType, opType};
704 }
705 
CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos,checker::Type * leftType,checker::Type * rightType)706 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos, checker::Type *leftType,
707                                                                      checker::Type *rightType)
708 {
709     RepairTypeErrorsInOperands(&leftType, &rightType);
710     if (leftType->IsTypeError()) {  // both are errors
711         return {GlobalETSBooleanType(), GlobalTypeError()};
712     }
713 
714     checker::Type *tsType {};
715     if (!IsReferenceType(leftType) || (!IsReferenceType(rightType) && !rightType->IsETSEnumType())) {
716         LogTypeError("Bad operand type, the types of the operands must be same type.", pos);
717         return {GlobalETSBooleanType(), leftType};
718     }
719 
720     if (rightType->IsETSDynamicType() && !rightType->AsETSDynamicType()->HasDecl()) {
721         LogTypeError("Right-hand side of instanceof expression must represent a type.", pos);
722         return {GlobalETSBooleanType(), leftType};
723     }
724 
725     tsType = GlobalETSBooleanType();
726     checker::Type *opType = rightType->IsETSDynamicType() ? GlobalBuiltinJSValueType() : GlobalETSObjectType();
727     ComputeApparentType(rightType);
728     RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
729 
730     return {tsType, opType};
731 }
732 
AdjustNumberLiteralType(ir::NumberLiteral * const literal,Type * literalType,Type * const otherType)733 bool ETSChecker::AdjustNumberLiteralType(ir::NumberLiteral *const literal, Type *literalType, Type *const otherType)
734 {
735     if (otherType->IsETSObjectType()) {
736         auto *const objectType = otherType->AsETSObjectType();
737         if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) && !objectType->IsETSStringType()) {
738             literal->RemoveBoxingUnboxingFlags(GetBoxingFlag(literalType));
739             literalType = ETSBuiltinTypeAsPrimitiveType(objectType);
740             literal->SetTsType(literalType);
741             literal->AddBoxingUnboxingFlags(GetBoxingFlag(literalType));
742             return true;
743         }
744     }
745     return false;
746 }
747 
CheckBinaryOperatorNullishCoalescing(ir::Expression * left,ir::Expression * right,lexer::SourcePosition pos)748 Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right,
749                                                        lexer::SourcePosition pos)
750 {
751     auto *leftType = left->TsType();
752     if (!IsReferenceType(leftType)) {
753         LogTypeError("Left-hand side of nullish-coalescing expression must be a reference type.", pos);
754         return leftType;
755     }
756 
757     leftType = GetNonNullishType(leftType);
758     auto *rightType = MaybeBoxExpression(right);
759 
760     if (IsTypeIdenticalTo(leftType, rightType)) {
761         return leftType;
762     }
763 
764     //  If possible and required update number literal type to the proper value (identical to left-side type)
765     if (right->IsNumberLiteral() && AdjustNumberLiteralType(right->AsNumberLiteral(), rightType, leftType)) {
766         return leftType;
767     }
768 
769     return CreateETSUnionType({leftType, rightType});
770 }
771 
772 using CheckBinaryFunction = std::function<checker::Type *(
773     ETSChecker *, std::tuple<ir::Expression *, ir::Expression *, lexer::TokenType, lexer::SourcePosition> op,
774     bool isEqualOp, std::tuple<checker::Type *, checker::Type *, Type *, Type *> types)>;
775 
GetCheckMap()776 std::map<lexer::TokenType, CheckBinaryFunction> &GetCheckMap()
777 {
778     static std::map<lexer::TokenType, CheckBinaryFunction> checkMap = {
779         {lexer::TokenType::PUNCTUATOR_MULTIPLY, &ETSChecker::CheckBinaryOperatorMulDivMod},
780         {lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
781         {lexer::TokenType::PUNCTUATOR_DIVIDE, &ETSChecker::CheckBinaryOperatorMulDivMod},
782         {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
783         {lexer::TokenType::PUNCTUATOR_MOD, &ETSChecker::CheckBinaryOperatorMulDivMod},
784         {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod},
785 
786         {lexer::TokenType::PUNCTUATOR_MINUS, &ETSChecker::CheckBinaryOperatorPlus},
787         {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
788         {lexer::TokenType::PUNCTUATOR_PLUS, &ETSChecker::CheckBinaryOperatorPlus},
789         {lexer::TokenType::PUNCTUATOR_PLUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus},
790 
791         {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
792         {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
793         {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
794         {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
795         {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT, &ETSChecker::CheckBinaryOperatorShift},
796         {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, &ETSChecker::CheckBinaryOperatorShift},
797 
798         {lexer::TokenType::PUNCTUATOR_BITWISE_OR, &ETSChecker::CheckBinaryOperatorBitwise},
799         {lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
800         {lexer::TokenType::PUNCTUATOR_BITWISE_AND, &ETSChecker::CheckBinaryOperatorBitwise},
801         {lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
802         {lexer::TokenType::PUNCTUATOR_BITWISE_XOR, &ETSChecker::CheckBinaryOperatorBitwise},
803         {lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL, &ETSChecker::CheckBinaryOperatorBitwise},
804     };
805 
806     return checkMap;
807 }
808 
809 struct BinaryOperatorParams {
810     ir::Expression *left;
811     ir::Expression *right;
812     ir::Expression *expr;
813     lexer::TokenType operationType;
814     lexer::SourcePosition pos;
815     bool isEqualOp;
816 };
817 
818 struct TypeParams {
819     checker::Type *leftType;
820     checker::Type *rightType;
821     Type *unboxedL;
822     Type *unboxedR;
823 };
824 
CheckBinaryOperatorHelper(ETSChecker * checker,const BinaryOperatorParams & binaryParams,const TypeParams & typeParams)825 static std::tuple<Type *, Type *> CheckBinaryOperatorHelper(ETSChecker *checker,
826                                                             const BinaryOperatorParams &binaryParams,
827                                                             const TypeParams &typeParams)
828 {
829     ir::Expression *left = binaryParams.left;
830     ir::Expression *right = binaryParams.right;
831     lexer::SourcePosition pos = binaryParams.pos;
832     checker::Type *const leftType = typeParams.leftType;
833     checker::Type *const rightType = typeParams.rightType;
834     checker::Type *tsType {};
835     switch (binaryParams.operationType) {
836         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND:
837         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
838             tsType = checker->CheckBinaryOperatorLogical(left, right, binaryParams.expr, pos, leftType, rightType,
839                                                          typeParams.unboxedL, typeParams.unboxedR);
840             break;
841         }
842         case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
843         case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
844             return checker->CheckBinaryOperatorStrictEqual(left, binaryParams.operationType, pos, leftType, rightType);
845         }
846         case lexer::TokenType::PUNCTUATOR_EQUAL:
847         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
848             std::tuple<Type *, Type *> res =
849                 checker->CheckBinaryOperatorEqual(left, right, binaryParams.operationType, pos, leftType, rightType,
850                                                   typeParams.unboxedL, typeParams.unboxedR);
851             if (!(std::get<0>(res) == nullptr && std::get<1>(res) == nullptr)) {
852                 return res;
853             }
854             [[fallthrough]];
855         }
856         case lexer::TokenType::PUNCTUATOR_LESS_THAN:
857         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
858         case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
859         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
860             return checker->CheckBinaryOperatorLessGreater(left, right, binaryParams.operationType, pos,
861                                                            binaryParams.isEqualOp, leftType, rightType,
862                                                            typeParams.unboxedL, typeParams.unboxedR);
863         }
864         case lexer::TokenType::KEYW_INSTANCEOF: {
865             return checker->CheckBinaryOperatorInstanceOf(pos, leftType, rightType);
866         }
867         case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: {
868             tsType = checker->CheckBinaryOperatorNullishCoalescing(left, right, pos);
869             break;
870         }
871         default: {
872             UNREACHABLE();
873             break;
874         }
875     }
876 
877     return {tsType, tsType};
878 }
879 
880 namespace {
IsStringEnum(const ir::Expression * expr)881 bool IsStringEnum(const ir::Expression *expr)
882 {
883     if (expr == nullptr) {
884         return false;
885     }
886     auto type = expr->TsType();
887     if (type == nullptr) {
888         return false;
889     }
890 
891     return type->IsETSStringEnumType();
892 }
893 
IsIntEnum(const ir::Expression * expr)894 bool IsIntEnum(const ir::Expression *expr)
895 {
896     if (expr == nullptr) {
897         return false;
898     }
899 
900     auto type = expr->TsType();
901     if (type == nullptr) {
902         return false;
903     }
904 
905     return type->IsETSIntEnumType();
906 }
907 
CheckNumericOperatorContext(ir::Expression * expression,lexer::TokenType op)908 bool CheckNumericOperatorContext(ir::Expression *expression, lexer::TokenType op)
909 {
910     const bool isMultiplicative = op == lexer::TokenType::PUNCTUATOR_MULTIPLY ||
911                                   op == lexer::TokenType::PUNCTUATOR_DIVIDE || op == lexer::TokenType::PUNCTUATOR_MOD;
912     const bool isAdditive = op == lexer::TokenType::PUNCTUATOR_PLUS || op == lexer::TokenType::PUNCTUATOR_MINUS;
913     const bool isShift = op == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT ||
914                          op == lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT ||
915                          op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT;
916     const bool isRelational =
917         op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
918         op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
919     const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
920     const bool isBitwise = op == lexer::TokenType::PUNCTUATOR_BITWISE_AND ||
921                            op == lexer::TokenType::PUNCTUATOR_BITWISE_OR ||
922                            op == lexer::TokenType::PUNCTUATOR_BITWISE_XOR;
923     const bool isConditionalAndOr =
924         op == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || op == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
925 
926     if (IsIntEnum(expression)) {
927         if (isMultiplicative || isAdditive || isShift || isRelational || isEquality || isBitwise ||
928             isConditionalAndOr) {
929             expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
930         }
931         return true;
932     }
933     return false;
934 }
935 
CheckStringOperatorContext(ir::Expression * expression,checker::Type * otherType,lexer::TokenType op)936 void CheckStringOperatorContext(ir::Expression *expression, checker::Type *otherType, lexer::TokenType op)
937 {
938     if (IsStringEnum(expression) && (otherType->IsETSStringType() || otherType->IsETSStringEnumType())) {
939         if (op == lexer::TokenType::PUNCTUATOR_PLUS) {
940             expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
941         }
942     }
943 }
944 
CheckRelationalOperatorsBetweenEnums(ir::Expression * left,ir::Expression * right,lexer::TokenType op)945 bool CheckRelationalOperatorsBetweenEnums(ir::Expression *left, ir::Expression *right, lexer::TokenType op)
946 {
947     const bool isRelational =
948         op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
949         op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL;
950     const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL;
951 
952     if (((IsStringEnum(left) && IsStringEnum(right)) ||
953          (IsIntEnum(left) && IsIntEnum(right)))) {  // NOTE(psiket) In case of int enums it has been already checked in
954                                                     // the CheckNumericOperatorContext function
955         if (isRelational || isEquality) {
956             left->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
957             right->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
958             return true;
959         }
960     }
961     return false;
962 }
963 
CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression * expression)964 void CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression *expression)
965 {
966     if (!expression->IsBinaryExpression()) {
967         return;
968     }
969 
970     auto binaryExpression = expression->AsBinaryExpression();
971     auto op = binaryExpression->OperatorType();
972     auto leftExp = binaryExpression->Left();
973     auto rightExp = binaryExpression->Right();
974 
975     // Numeric Operator Context
976     auto leftIsIntEnum = CheckNumericOperatorContext(leftExp, op);
977     auto rightIsIntEnum = CheckNumericOperatorContext(rightExp, op);
978     if (leftIsIntEnum || rightIsIntEnum) {
979         return;
980     }
981 
982     // String Operator Context
983     CheckStringOperatorContext(leftExp, rightExp->TsType(), op);
984     CheckStringOperatorContext(rightExp, leftExp->TsType(), op);
985 
986     // Relational operators if both are enumeration Types
987     if (CheckRelationalOperatorsBetweenEnums(leftExp, rightExp, op)) {
988         return;
989     }
990 }
991 }  // namespace
992 
CheckBinaryOperator(ir::Expression * left,ir::Expression * right,ir::Expression * expr,lexer::TokenType operationType,lexer::SourcePosition pos,bool forcePromotion)993 std::tuple<Type *, Type *> ETSChecker::CheckBinaryOperator(ir::Expression *left, ir::Expression *right,
994                                                            ir::Expression *expr, lexer::TokenType operationType,
995                                                            lexer::SourcePosition pos, bool forcePromotion)
996 {
997     checker::Type *const leftType = left->Check(this);
998 
999     if (leftType == nullptr) {
1000         LogTypeError("Unexpected type error in binary expression", left->Start());
1001         auto rightType = right->Check(this);
1002         return {rightType, rightType};
1003     }
1004 
1005     if (operationType == lexer::TokenType::KEYW_INSTANCEOF) {
1006         AddStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT);
1007     }
1008 
1009     Context().CheckTestSmartCastCondition(operationType);
1010 
1011     checker::Type *rightType = right->Check(this);
1012     if (right->IsTypeNode()) {
1013         rightType = right->AsTypeNode()->GetType(this);
1014     }
1015 
1016     if (rightType == nullptr) {
1017         LogTypeError("Unexpected type error in binary expression", pos);
1018         return {leftType, leftType};
1019     }
1020 
1021     CheckNeedToGenerateGetValueForBinaryExpression(expr);
1022 
1023     const bool isLogicalExtendedOperator = (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) ||
1024                                            (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
1025     Type *unboxedL =
1026         isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(leftType) : ETSBuiltinTypeAsPrimitiveType(leftType);
1027     Type *unboxedR = isLogicalExtendedOperator ? ETSBuiltinTypeAsConditionalType(rightType)
1028                                                : ETSBuiltinTypeAsPrimitiveType(rightType);
1029 
1030     bool isEqualOp = (operationType > lexer::TokenType::PUNCTUATOR_SUBSTITUTION &&
1031                       operationType < lexer::TokenType::PUNCTUATOR_ARROW) &&
1032                      !forcePromotion;
1033 
1034     if (CheckBinaryOperatorForBigInt(leftType, rightType, operationType)) {
1035         switch (operationType) {
1036             case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
1037             case lexer::TokenType::PUNCTUATOR_LESS_THAN:
1038             case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL:
1039             case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
1040                 return {GlobalETSBooleanType(), GlobalETSBooleanType()};
1041             default:
1042                 return {leftType, rightType};
1043         }
1044     }
1045 
1046     auto checkMap = GetCheckMap();
1047     if (checkMap.find(operationType) != checkMap.end()) {
1048         auto check = checkMap[operationType];
1049         auto tsType = check(this, std::make_tuple(left, right, operationType, pos), isEqualOp,
1050                             std::make_tuple(leftType, rightType, unboxedL, unboxedR));
1051         return {tsType, tsType};
1052     }
1053 
1054     return CheckBinaryOperatorHelper(this, {left, right, expr, operationType, pos, isEqualOp},
1055                                      {leftType, rightType, unboxedL, unboxedR});
1056 }
1057 
HandleArithmeticOperationOnTypes(Type * left,Type * right,lexer::TokenType operationType)1058 Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1059 {
1060     ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1061            right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1062 
1063     if (left->IsDoubleType() || right->IsDoubleType()) {
1064         return PerformArithmeticOperationOnTypes<DoubleType>(left, right, operationType);
1065     }
1066 
1067     if (left->IsFloatType() || right->IsFloatType()) {
1068         return PerformArithmeticOperationOnTypes<FloatType>(left, right, operationType);
1069     }
1070 
1071     if (left->IsLongType() || right->IsLongType()) {
1072         return PerformArithmeticOperationOnTypes<LongType>(left, right, operationType);
1073     }
1074 
1075     return PerformArithmeticOperationOnTypes<IntType>(left, right, operationType);
1076 }
1077 
HandleBitwiseOperationOnTypes(Type * left,Type * right,lexer::TokenType operationType)1078 Type *ETSChecker::HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType)
1079 {
1080     ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
1081            right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC));
1082 
1083     if (left->IsDoubleType() || right->IsDoubleType()) {
1084         return HandleBitWiseArithmetic<DoubleType, LongType>(left, right, operationType);
1085     }
1086 
1087     if (left->IsFloatType() || right->IsFloatType()) {
1088         return HandleBitWiseArithmetic<FloatType, IntType>(left, right, operationType);
1089     }
1090 
1091     if (left->IsLongType() || right->IsLongType()) {
1092         return HandleBitWiseArithmetic<LongType>(left, right, operationType);
1093     }
1094 
1095     return HandleBitWiseArithmetic<IntType>(left, right, operationType);
1096 }
1097 
FlagExpressionWithUnboxing(Type * type,Type * unboxedType,ir::Expression * typeExpression)1098 void ETSChecker::FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression)
1099 {
1100     if (type->IsETSObjectType() && (unboxedType != nullptr)) {
1101         typeExpression->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedType));
1102     }
1103 }
1104 
1105 }  // namespace ark::es2panda::checker
1106