• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 "constantExpressionLowering.h"
17 
18 #include "checker/ETSchecker.h"
19 #include "compiler/lowering/util.h"
20 #include "lexer/token/token.h"
21 
22 namespace ark::es2panda::compiler {
23 
LogError(const diagnostic::DiagnosticKind & diagnostic,const util::DiagnosticMessageParams & diagnosticParams,const lexer::SourcePosition & pos) const24 void ConstantExpressionLowering::LogError(const diagnostic::DiagnosticKind &diagnostic,
25                                           const util::DiagnosticMessageParams &diagnosticParams,
26                                           const lexer::SourcePosition &pos) const
27 {
28     context_->diagnosticEngine->LogDiagnostic(diagnostic, diagnosticParams, pos);
29 }
30 
IsSupportedLiteralForNumeric(ir::Literal * const node)31 static bool IsSupportedLiteralForNumeric(ir::Literal *const node)
32 {
33     return node->IsNumberLiteral() || node->IsCharLiteral() || node->IsBooleanLiteral();
34 }
35 
IsSupportedLiteral(ir::Expression * const node)36 static bool IsSupportedLiteral(ir::Expression *const node)
37 {
38     if (!node->IsLiteral()) {
39         return false;
40     }
41 
42     auto literal = node->AsLiteral();
43     return literal->IsNumberLiteral() || literal->IsCharLiteral() || literal->IsBooleanLiteral() ||
44            literal->IsStringLiteral() || literal->IsUndefinedLiteral() || literal->IsNullLiteral();
45 }
46 
IsStringTypeReference(ir::ETSTypeReference * type)47 static bool IsStringTypeReference(ir::ETSTypeReference *type)
48 {
49     auto name = type->Part()->GetIdent()->Name();
50     return name == "string" || name == "String";
51 }
52 
CheckIsBooleanConstantForUnary(ir::Literal * const unaryLiteral,lexer::TokenType opType)53 static bool CheckIsBooleanConstantForUnary(ir::Literal *const unaryLiteral, lexer::TokenType opType)
54 {
55     if (unaryLiteral->IsBooleanLiteral()) {
56         return true;
57     }
58     return opType == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK;
59 }
60 
CheckIsBooleanConstantForBinary(ir::Literal * lhs,ir::Literal * rhs,lexer::TokenType opType)61 static bool CheckIsBooleanConstantForBinary(ir::Literal *lhs, ir::Literal *rhs, lexer::TokenType opType)
62 {
63     if (lhs->IsBooleanLiteral() && rhs->IsBooleanLiteral()) {
64         return true;
65     }
66     return opType == lexer::TokenType::PUNCTUATOR_GREATER_THAN ||
67            opType == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL ||
68            opType == lexer::TokenType::PUNCTUATOR_LESS_THAN || opType == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL ||
69            opType == lexer::TokenType::PUNCTUATOR_EQUAL || opType == lexer::TokenType::PUNCTUATOR_NOT_EQUAL ||
70            opType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || opType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR;
71 }
72 
CheckIsNumericConstant(ir::Literal * const left,ir::Literal * const right)73 static bool CheckIsNumericConstant(ir::Literal *const left, ir::Literal *const right)
74 {
75     return (left->IsNumberLiteral() || left->IsCharLiteral()) && (right->IsNumberLiteral() || right->IsCharLiteral());
76 }
77 
78 template <typename TargetType>
GetOperand(ir::Literal * const node)79 static TargetType GetOperand(ir::Literal *const node)
80 {
81     if (node->IsBooleanLiteral()) {
82         return node->AsBooleanLiteral()->Value();
83     }
84 
85     if (node->IsNumberLiteral()) {
86         auto numNode = node->AsNumberLiteral();
87         if (numNode->Number().IsInt()) {
88             return numNode->Number().GetInt();
89         }
90         if (numNode->Number().IsLong()) {
91             return numNode->Number().GetLong();
92         }
93         if (numNode->Number().IsFloat()) {
94             return numNode->Number().GetFloat();
95         }
96         if (numNode->Number().IsDouble()) {
97             return numNode->Number().GetDouble();
98         }
99         ES2PANDA_UNREACHABLE();
100     }
101 
102     if (node->IsCharLiteral()) {
103         return node->AsCharLiteral()->Char();
104     }
105 
106     ES2PANDA_UNREACHABLE();
107 }
108 
GetTypeRank(ir::Literal * const literal)109 static TypeRank GetTypeRank(ir::Literal *const literal)
110 {
111     if (literal->IsCharLiteral()) {
112         return TypeRank::CHAR;
113     }
114     if (literal->IsNumberLiteral()) {
115         auto number = literal->AsNumberLiteral()->Number();
116         if (number.IsInt()) {
117             return TypeRank::INT32;
118         }
119         if (number.IsLong()) {
120             return TypeRank::INT64;
121         }
122         if (number.IsDouble()) {
123             return TypeRank::DOUBLE;
124         }
125         return TypeRank::FLOAT;
126     }
127     ES2PANDA_UNREACHABLE();
128 }
129 
TestLiteralIsNotZero(ir::Literal * literal)130 static bool TestLiteralIsNotZero(ir::Literal *literal)
131 {
132     ES2PANDA_ASSERT(literal->IsCharLiteral() || literal->IsNumberLiteral());
133     if (literal->IsCharLiteral()) {
134         return literal->AsCharLiteral()->Char() != 0;
135     }
136 
137     auto number = literal->AsNumberLiteral()->Number();
138     if (number.IsInt()) {
139         return number.GetInt() != 0;
140     }
141     if (number.IsLong()) {
142         return number.GetLong() != 0;
143     }
144     if (number.IsDouble()) {
145         return number.GetDouble() != 0;
146     }
147     if (number.IsFloat()) {
148         return number.GetFloat() != 0;
149     }
150     ES2PANDA_UNREACHABLE();
151 }
152 
FoldTernaryConstant(ir::ConditionalExpression * cond)153 ir::AstNode *ConstantExpressionLowering::FoldTernaryConstant(ir::ConditionalExpression *cond)
154 {
155     ir::AstNode *resNode {};
156 
157     auto const testCond = cond->Test()->AsLiteral();
158     if (testCond->IsBooleanLiteral()) {
159         resNode = testCond->AsBooleanLiteral()->Value() ? cond->Consequent() : cond->Alternate();
160     }
161     // 15.10.1 Extended Conditional Expression
162     if (testCond->IsStringLiteral()) {
163         resNode = !testCond->AsStringLiteral()->Str().Empty() ? cond->Consequent() : cond->Alternate();
164     }
165     if (testCond->IsNullLiteral() || testCond->IsUndefinedLiteral()) {
166         resNode = cond->Alternate();
167     }
168     if (testCond->IsCharLiteral() || testCond->IsNumberLiteral()) {
169         resNode = TestLiteralIsNotZero(testCond) ? cond->Consequent() : cond->Alternate();
170     }
171 
172     if (resNode == nullptr) {
173         return cond;
174     }
175 
176     resNode->SetParent(cond->Parent());
177     resNode->SetRange(cond->Range());
178     return resNode;
179 }
180 
181 template <typename InputType>
PerformRelationOperator(InputType left,InputType right,lexer::TokenType opType)182 bool ConstantExpressionLowering::PerformRelationOperator(InputType left, InputType right, lexer::TokenType opType)
183 {
184     switch (opType) {
185         case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
186             return left > right;
187         }
188         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
189             return left >= right;
190         }
191         case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
192             return left < right;
193         }
194         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
195             return left <= right;
196         }
197         case lexer::TokenType::PUNCTUATOR_EQUAL: {
198             return left == right;
199         }
200         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
201             return left != right;
202         }
203         default: {
204             ES2PANDA_UNREACHABLE();
205         }
206     }
207 }
208 
HandleRelationOperator(ir::Literal * left,ir::Literal * right,lexer::TokenType opType)209 bool ConstantExpressionLowering::HandleRelationOperator(ir::Literal *left, ir::Literal *right, lexer::TokenType opType)
210 {
211     if (left->IsBooleanLiteral()) {
212         return PerformRelationOperator(GetOperand<bool>(left), GetOperand<bool>(right), opType);
213     }
214     if (left->IsStringLiteral()) {
215         return PerformRelationOperator(left->AsStringLiteral()->Str(), right->AsStringLiteral()->Str(), opType);
216     }
217 
218     TypeRank leftRank = GetTypeRank(left);
219     TypeRank rightRank = GetTypeRank(right);
220     TypeRank targetRank = std::max(leftRank, rightRank);
221     switch (targetRank) {
222         case TypeRank::DOUBLE: {
223             return PerformRelationOperator(GetOperand<double>(left), GetOperand<double>(right), opType);
224         }
225         case TypeRank::FLOAT: {
226             return PerformRelationOperator(GetOperand<float>(left), GetOperand<float>(right), opType);
227         }
228         case TypeRank::INT64: {
229             return PerformRelationOperator(GetOperand<int64_t>(left), GetOperand<int64_t>(right), opType);
230         }
231         case TypeRank::INT32:
232         case TypeRank::CHAR: {
233             return PerformRelationOperator(GetOperand<int32_t>(left), GetOperand<int32_t>(right), opType);
234         }
235         default: {
236             ES2PANDA_UNREACHABLE();
237         }
238     }
239 }
240 
HandleBitwiseLogicalOperator(ir::Literal * left,ir::Literal * right,lexer::TokenType opType)241 bool ConstantExpressionLowering::HandleBitwiseLogicalOperator(ir::Literal *left, ir::Literal *right,
242                                                               lexer::TokenType opType)
243 {
244     bool leftValue = left->AsBooleanLiteral()->Value();
245     bool rightValue = right->AsBooleanLiteral()->Value();
246     switch (opType) {
247         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
248             return (static_cast<uint32_t>(leftValue) ^ static_cast<uint32_t>(rightValue)) != 0U;
249         }
250         case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
251             return (static_cast<uint32_t>(leftValue) & static_cast<uint32_t>(rightValue)) != 0U;
252         }
253         case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
254             return (static_cast<uint32_t>(leftValue) | static_cast<uint32_t>(rightValue)) != 0U;
255         }
256         default: {
257             ES2PANDA_UNREACHABLE();
258         }
259     }
260 }
261 
HandleLogicalOperator(ir::BinaryExpression * expr,lexer::TokenType opType)262 ir::AstNode *ConstantExpressionLowering::HandleLogicalOperator(ir::BinaryExpression *expr, lexer::TokenType opType)
263 {
264     auto left = expr->Left();
265     auto right = expr->Right();
266 
267     bool leftBoolValue = false;
268     ir::AstNode *resultValueNode = nullptr;
269 
270     if (left->IsBooleanLiteral()) {
271         leftBoolValue = left->AsBooleanLiteral()->Value();
272     } else if (left->IsNumberLiteral() || left->IsCharLiteral()) {
273         leftBoolValue = GetOperand<int32_t>(left->AsLiteral()) != 0;
274     } else if (left->IsStringLiteral()) {
275         leftBoolValue = left->AsStringLiteral()->Str().Length() != 0;
276     } else if (left->IsNullLiteral() || left->IsUndefinedLiteral()) {
277         leftBoolValue = false;
278     } else {
279         ES2PANDA_UNREACHABLE();
280     }
281 
282     switch (opType) {
283         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
284             if (!leftBoolValue) {
285                 resultValueNode = left;
286                 break;
287             }
288             resultValueNode = right;
289             break;
290         }
291         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
292             if (leftBoolValue) {
293                 resultValueNode = left;
294             } else {
295                 resultValueNode = right;
296             }
297             break;
298         }
299         default: {
300             ES2PANDA_UNREACHABLE();
301         }
302     }
303 
304     resultValueNode->SetParent(expr->Parent());
305     resultValueNode->SetRange({left->Range().start, right->Range().end});
306     return resultValueNode;
307 }
308 
FoldBinaryBooleanConstant(ir::BinaryExpression * expr)309 ir::AstNode *ConstantExpressionLowering::FoldBinaryBooleanConstant(ir::BinaryExpression *expr)
310 {
311     auto left = expr->Left()->AsLiteral();
312     auto right = expr->Right()->AsLiteral();
313 
314     bool result {};
315     switch (expr->OperatorType()) {
316         case lexer::TokenType::PUNCTUATOR_GREATER_THAN:
317         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL:
318         case lexer::TokenType::PUNCTUATOR_LESS_THAN:
319         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL:
320         case lexer::TokenType::PUNCTUATOR_EQUAL:
321         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
322             if ((left->IsBooleanLiteral() && right->IsBooleanLiteral()) || CheckIsNumericConstant(left, right) ||
323                 (left->IsStringLiteral() && right->IsStringLiteral())) {
324                 result = HandleRelationOperator(left, right, expr->OperatorType());
325                 break;
326             }
327             return expr;
328         }
329         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
330         case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
331         case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
332             if (left->IsBooleanLiteral() && right->IsBooleanLiteral()) {
333                 result = HandleBitwiseLogicalOperator(left, right, expr->OperatorType());
334                 break;
335             }
336             return expr;
337         }
338         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND:
339         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
340             // Special because of extended conditional expression
341             return HandleLogicalOperator(expr, expr->OperatorType());
342         }
343         default: {
344             return expr;
345         }
346     }
347 
348     auto resNode = util::NodeAllocator::Alloc<ir::BooleanLiteral>(context_->allocator, result);
349     ES2PANDA_ASSERT(resNode);
350     resNode->SetParent(expr->Parent());
351     resNode->SetRange(expr->Range());
352     return resNode;
353 }
354 
355 template <typename IntegerType>
PerformBitwiseArithmetic(IntegerType left,IntegerType right,lexer::TokenType operationType)356 IntegerType ConstantExpressionLowering::PerformBitwiseArithmetic(IntegerType left, IntegerType right,
357                                                                  lexer::TokenType operationType)
358 {
359     using UnsignedType = std::make_unsigned_t<IntegerType>;
360 
361     UnsignedType result = 0;
362     UnsignedType unsignedLeftValue = left;
363     UnsignedType unsignedRightValue = right;
364 
365     auto mask = std::numeric_limits<UnsignedType>::digits - 1U;
366     auto shift = unsignedRightValue & mask;
367 
368     switch (operationType) {
369         case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
370             result = unsignedLeftValue & unsignedRightValue;
371             break;
372         }
373         case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
374             result = unsignedLeftValue | unsignedRightValue;
375             break;
376         }
377         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
378             result = unsignedLeftValue ^ unsignedRightValue;
379             break;
380         }
381         case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: {
382             static_assert(sizeof(UnsignedType) == 4 || sizeof(UnsignedType) == 8);
383             result = unsignedLeftValue << shift;
384             break;
385         }
386         case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: {
387             static_assert(sizeof(IntegerType) == 4 || sizeof(IntegerType) == 8);
388             result = static_cast<IntegerType>(unsignedLeftValue) >> shift;  // NOLINT(hicpp-signed-bitwise)
389             break;
390         }
391         case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: {
392             static_assert(sizeof(UnsignedType) == 4 || sizeof(UnsignedType) == 8);
393             result = unsignedLeftValue >> shift;
394             break;
395         }
396         default: {
397             ES2PANDA_UNREACHABLE();
398         }
399     }
400 
401     return result;
402 }
403 
404 template <typename TargetType>
HandleBitwiseOperator(TargetType leftNum,TargetType rightNum,lexer::TokenType operationType,TypeRank targetRank)405 lexer::Number ConstantExpressionLowering::HandleBitwiseOperator(TargetType leftNum, TargetType rightNum,
406                                                                 lexer::TokenType operationType, TypeRank targetRank)
407 {
408     switch (targetRank) {
409         case TypeRank::DOUBLE: {
410             return lexer::Number(PerformBitwiseArithmetic<int64_t>(leftNum, rightNum, operationType));
411         }
412         case TypeRank::FLOAT: {
413             return lexer::Number(PerformBitwiseArithmetic<int32_t>(leftNum, rightNum, operationType));
414         }
415         case TypeRank::INT64: {
416             return lexer::Number(PerformBitwiseArithmetic<int64_t>(leftNum, rightNum, operationType));
417         }
418         case TypeRank::INT32:
419         case TypeRank::CHAR: {
420             return lexer::Number(PerformBitwiseArithmetic<int32_t>(leftNum, rightNum, operationType));
421         }
422         default: {
423             ES2PANDA_UNREACHABLE();
424         }
425     }
426 }
427 
428 template <typename TargetType>
HandleArithmeticOperation(TargetType leftNum,TargetType rightNum,ir::BinaryExpression * expr)429 TargetType ConstantExpressionLowering::HandleArithmeticOperation(TargetType leftNum, TargetType rightNum,
430                                                                  ir::BinaryExpression *expr)
431 {
432     auto isForbiddenZeroDivision = [&rightNum]() { return std::is_integral<TargetType>::value && rightNum == 0; };
433     auto operationType = expr->OperatorType();
434     switch (operationType) {
435         case lexer::TokenType::PUNCTUATOR_PLUS: {
436             return leftNum + rightNum;
437         }
438         case lexer::TokenType::PUNCTUATOR_MINUS: {
439             return leftNum - rightNum;
440         }
441         case lexer::TokenType::PUNCTUATOR_DIVIDE: {
442             if (isForbiddenZeroDivision()) {
443                 LogError(diagnostic::DIVISION_BY_ZERO, {}, expr->Start());
444                 return rightNum;
445             }
446             return leftNum / rightNum;
447         }
448         case lexer::TokenType::PUNCTUATOR_MULTIPLY: {
449             return leftNum * rightNum;
450         }
451         case lexer::TokenType::PUNCTUATOR_MOD: {
452             if (isForbiddenZeroDivision()) {
453                 LogError(diagnostic::DIVISION_BY_ZERO, {}, expr->Start());
454                 return rightNum;
455             }
456             if constexpr (std::is_integral_v<TargetType>) {
457                 return leftNum % rightNum;
458             } else {
459                 return std::fmod(leftNum, rightNum);
460             }
461         }
462         default:
463             ES2PANDA_UNREACHABLE();
464     }
465 }
466 
467 template <typename InputType>
FoldBinaryNumericConstantHelper(ir::BinaryExpression * expr,TypeRank targetRank)468 ir::AstNode *ConstantExpressionLowering::FoldBinaryNumericConstantHelper(ir::BinaryExpression *expr,
469                                                                          TypeRank targetRank)
470 {
471     auto const lhs = expr->Left()->AsLiteral();
472     auto const rhs = expr->Right()->AsLiteral();
473     lexer::Number resNum {};
474     auto lhsNumber = GetOperand<InputType>(lhs);
475     auto rhsNumber = GetOperand<InputType>(rhs);
476     switch (expr->OperatorType()) {
477         case lexer::TokenType::PUNCTUATOR_DIVIDE:
478         case lexer::TokenType::PUNCTUATOR_MOD:
479         case lexer::TokenType::PUNCTUATOR_PLUS:
480         case lexer::TokenType::PUNCTUATOR_MINUS:
481         case lexer::TokenType::PUNCTUATOR_MULTIPLY: {
482             auto num = HandleArithmeticOperation(lhsNumber, rhsNumber, expr);
483             resNum = lexer::Number(num);
484             break;
485         }
486         case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
487         case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
488         case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
489         case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
490         case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
491         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
492             resNum = HandleBitwiseOperator<InputType>(lhsNumber, rhsNumber, expr->OperatorType(), targetRank);
493             break;
494         }
495         default: {
496             // Operation might not support.
497             return expr;
498         }
499     }
500 
501     ir::TypedAstNode *resNode = util::NodeAllocator::Alloc<ir::NumberLiteral>(context_->allocator, resNum);
502     resNode->SetParent(expr->Parent());
503     resNode->SetRange(expr->Range());
504     return resNode;
505 }
506 
FoldBinaryNumericConstant(ir::BinaryExpression * expr)507 ir::AstNode *ConstantExpressionLowering::FoldBinaryNumericConstant(ir::BinaryExpression *expr)
508 {
509     auto left = expr->Left()->AsLiteral();
510     auto right = expr->Right()->AsLiteral();
511     if (!IsSupportedLiteralForNumeric(left) && !IsSupportedLiteralForNumeric(right)) {
512         return expr;
513     }
514 
515     TypeRank leftRank = GetTypeRank(left);
516     TypeRank rightRank = GetTypeRank(right);
517     TypeRank targetRank = std::max(leftRank, rightRank);
518     switch (targetRank) {
519         case TypeRank::DOUBLE: {
520             return FoldBinaryNumericConstantHelper<double>(expr, targetRank);
521         }
522         case TypeRank::FLOAT: {
523             return FoldBinaryNumericConstantHelper<float>(expr, targetRank);
524         }
525         case TypeRank::INT64: {
526             return FoldBinaryNumericConstantHelper<int64_t>(expr, targetRank);
527         }
528         case TypeRank::INT32:
529         case TypeRank::CHAR: {
530             return FoldBinaryNumericConstantHelper<int32_t>(expr, targetRank);
531         }
532         default: {
533             ES2PANDA_UNREACHABLE();
534         }
535     }
536 }
537 
FoldBinaryStringConstant(ir::BinaryExpression * const expr)538 ir::AstNode *ConstantExpressionLowering::FoldBinaryStringConstant(ir::BinaryExpression *const expr)
539 {
540     if (expr->OperatorType() != lexer::TokenType::PUNCTUATOR_PLUS) {
541         LogError(diagnostic::UNSUPPORTED_OPERATOR_FOR_STRING, {}, expr->Left()->Start());
542         return expr;
543     }
544 
545     auto const lhs = expr->Left()->AsLiteral();
546     auto const rhs = expr->Right()->AsLiteral();
547     auto const resStr = util::UString(lhs->ToString() + rhs->ToString(), context_->allocator).View();
548     auto resNode = util::NodeAllocator::Alloc<ir::StringLiteral>(context_->allocator, resStr);
549     ES2PANDA_ASSERT(resNode);
550     resNode->SetParent(expr->Parent());
551     resNode->SetRange(expr->Range());
552     return resNode;
553 }
554 
FoldBinaryConstant(ir::BinaryExpression * const expr)555 ir::AstNode *ConstantExpressionLowering::FoldBinaryConstant(ir::BinaryExpression *const expr)
556 {
557     auto const lhs = expr->Left()->AsLiteral();
558     auto const rhs = expr->Right()->AsLiteral();
559 
560     auto isBooleanConstant = CheckIsBooleanConstantForBinary(lhs, rhs, expr->OperatorType());
561     if (isBooleanConstant) {
562         return FoldBinaryBooleanConstant(expr);
563     }
564     if (lhs->IsStringLiteral() || rhs->IsStringLiteral()) {
565         return FoldBinaryStringConstant(expr);
566     }
567     return FoldBinaryNumericConstant(expr);
568 }
569 
570 template <typename InputType>
HandleBitwiseNegate(InputType value,TypeRank rank)571 lexer::Number ConstantExpressionLowering::HandleBitwiseNegate(InputType value, TypeRank rank)
572 {
573     switch (rank) {
574         case TypeRank::DOUBLE:
575         case TypeRank::INT64: {
576             return lexer::Number(static_cast<int64_t>(~static_cast<uint64_t>(value)));
577         }
578         case TypeRank::FLOAT:
579         case TypeRank::INT32:
580         case TypeRank::CHAR: {
581             return lexer::Number(static_cast<int32_t>(~static_cast<uint32_t>(value)));
582         }
583         default: {
584             ES2PANDA_UNREACHABLE();
585         }
586     }
587 }
588 
589 template <typename InputType>
FoldUnaryNumericConstantHelper(ir::UnaryExpression * unary,ir::Literal * node,TypeRank rank)590 ir::AstNode *ConstantExpressionLowering::FoldUnaryNumericConstantHelper(ir::UnaryExpression *unary, ir::Literal *node,
591                                                                         TypeRank rank)
592 {
593     auto value = GetOperand<InputType>(node);
594 
595     lexer::Number resNum {};
596     switch (unary->OperatorType()) {
597         case lexer::TokenType::PUNCTUATOR_PLUS: {
598             resNum = lexer::Number(value);
599             break;
600         }
601         case lexer::TokenType::PUNCTUATOR_MINUS: {
602             resNum = lexer::Number(-value);
603             break;
604         }
605         case lexer::TokenType::PUNCTUATOR_TILDE: {
606             resNum = std::move(HandleBitwiseNegate(value, rank));
607             break;
608         }
609         default: {
610             ES2PANDA_UNREACHABLE();
611         }
612     }
613 
614     ir::TypedAstNode *resNode = util::NodeAllocator::Alloc<ir::NumberLiteral>(context_->allocator, resNum);
615     resNode->SetParent(unary->Parent());
616     resNode->SetRange(unary->Range());
617     return resNode;
618 }
619 
FoldUnaryNumericConstant(ir::UnaryExpression * unary)620 ir::AstNode *ConstantExpressionLowering::FoldUnaryNumericConstant(ir::UnaryExpression *unary)
621 {
622     auto literal = unary->Argument()->AsLiteral();
623     TypeRank rank = GetTypeRank(literal);
624 
625     switch (rank) {
626         case TypeRank::DOUBLE: {
627             return FoldUnaryNumericConstantHelper<double>(unary, literal, rank);
628         }
629         case TypeRank::FLOAT: {
630             return FoldUnaryNumericConstantHelper<float>(unary, literal, rank);
631         }
632         case TypeRank::INT64: {
633             return FoldUnaryNumericConstantHelper<int64_t>(unary, literal, rank);
634         }
635         case TypeRank::INT32:
636         case TypeRank::CHAR: {
637             return FoldUnaryNumericConstantHelper<int32_t>(unary, literal, rank);
638         }
639         default: {
640             ES2PANDA_UNREACHABLE();
641         }
642     }
643 }
644 
FoldUnaryBooleanConstant(ir::UnaryExpression * unary)645 ir::AstNode *ConstantExpressionLowering::FoldUnaryBooleanConstant(ir::UnaryExpression *unary)
646 {
647     bool result {};
648     auto *unaryLiteral = unary->Argument()->AsLiteral();
649 
650     if (unary->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) {
651         // 15.10.1 Extended Conditional Expression
652         if (unaryLiteral->IsUndefinedLiteral() || unaryLiteral->IsNullLiteral()) {
653             result = true;
654         } else {
655             bool value = GetOperand<bool>(unaryLiteral);
656             result = !value;
657         }
658     } else {
659         ES2PANDA_UNREACHABLE();
660     }
661 
662     auto resNode = util::NodeAllocator::Alloc<ir::BooleanLiteral>(context_->allocator, result);
663     ES2PANDA_ASSERT(resNode != nullptr);
664     resNode->SetParent(unary->Parent());
665     resNode->SetRange(unary->Range());
666     return resNode;
667 }
668 
FoldUnaryConstant(ir::UnaryExpression * const unary)669 ir::AstNode *ConstantExpressionLowering::FoldUnaryConstant(ir::UnaryExpression *const unary)
670 {
671     auto unaryLiteral = unary->Argument()->AsLiteral();
672 
673     auto isBooleanConstant = CheckIsBooleanConstantForUnary(unaryLiteral, unary->OperatorType());
674     if (isBooleanConstant) {
675         return FoldUnaryBooleanConstant(unary);
676     }
677 
678     return FoldUnaryNumericConstant(unary);
679 }
680 
TryFoldTSAsExpressionForString(ir::TSAsExpression * expr)681 ir::AstNode *ConstantExpressionLowering::TryFoldTSAsExpressionForString(ir::TSAsExpression *expr)
682 {
683     if (expr->Expr()->IsStringLiteral() && expr->TypeAnnotation()->IsETSTypeReference() &&
684         IsStringTypeReference(expr->TypeAnnotation()->AsETSTypeReference())) {
685         auto res = expr->Expr()->AsStringLiteral();
686         res->SetParent(expr->Parent());
687         res->SetRange(expr->Range());
688         return res;
689     }
690     return expr;
691 }
692 
FoldTSAsExpressionToChar(ir::TSAsExpression * expr)693 ir::AstNode *ConstantExpressionLowering::FoldTSAsExpressionToChar(ir::TSAsExpression *expr)
694 {
695     auto *sourceLiteral = expr->Expr()->AsLiteral();
696     auto resChar = GetOperand<char16_t>(sourceLiteral);
697     ir::TypedAstNode *resNode = util::NodeAllocator::Alloc<ir::CharLiteral>(context_->allocator, resChar);
698     resNode->SetParent(expr->Parent());
699     resNode->SetRange(expr->Range());
700     return resNode;
701 }
702 
FoldTSAsExpressionToBoolean(ir::TSAsExpression * expr)703 ir::AstNode *ConstantExpressionLowering::FoldTSAsExpressionToBoolean(ir::TSAsExpression *expr)
704 {
705     auto *sourceLiteral = expr->Expr()->AsLiteral();
706     auto resBool = GetOperand<bool>(sourceLiteral);
707     ir::TypedAstNode *resNode = util::NodeAllocator::Alloc<ir::BooleanLiteral>(context_->allocator, resBool);
708     resNode->SetParent(expr->Parent());
709     resNode->SetRange(expr->Range());
710     return resNode;
711 }
712 
FoldTSAsExpression(ir::TSAsExpression * const expr)713 ir::AstNode *ConstantExpressionLowering::FoldTSAsExpression(ir::TSAsExpression *const expr)
714 {
715     if (expr->TypeAnnotation()->IsETSPrimitiveType()) {
716         auto *sourceLiteral = expr->Expr()->AsLiteral();
717         lexer::Number resNum;
718         switch (expr->TypeAnnotation()->AsETSPrimitiveType()->GetPrimitiveType()) {
719             case ir::PrimitiveType::CHAR: {
720                 return FoldTSAsExpressionToChar(expr);
721             }
722             case ir::PrimitiveType::BOOLEAN: {
723                 return FoldTSAsExpressionToBoolean(expr);
724             }
725             case ir::PrimitiveType::BYTE: {
726                 resNum = lexer::Number(GetOperand<int8_t>(sourceLiteral));
727                 break;
728             }
729             case ir::PrimitiveType::SHORT: {
730                 resNum = lexer::Number(GetOperand<int16_t>(sourceLiteral));
731                 break;
732             }
733             case ir::PrimitiveType::INT: {
734                 resNum = lexer::Number(GetOperand<int32_t>(sourceLiteral));
735                 break;
736             }
737             case ir::PrimitiveType::LONG: {
738                 resNum = lexer::Number(GetOperand<int64_t>(sourceLiteral));
739                 break;
740             }
741             case ir::PrimitiveType::FLOAT: {
742                 resNum = lexer::Number(GetOperand<float>(sourceLiteral));
743                 break;
744             }
745             case ir::PrimitiveType::DOUBLE: {
746                 resNum = lexer::Number(GetOperand<double>(sourceLiteral));
747                 break;
748             }
749             default: {
750                 return expr;
751             }
752         }
753         ir::TypedAstNode *result = util::NodeAllocator::Alloc<ir::NumberLiteral>(context_->allocator, resNum);
754         result->SetParent(expr->Parent());
755         result->SetRange(expr->Range());
756         return result;
757     }
758     return TryFoldTSAsExpressionForString(expr);
759 }
760 
FoldMultilineString(ir::TemplateLiteral * expr)761 ir::AstNode *ConstantExpressionLowering::FoldMultilineString(ir::TemplateLiteral *expr)
762 {
763     auto *result = util::NodeAllocator::Alloc<ir::StringLiteral>(context_->allocator, expr->GetMultilineString());
764     ES2PANDA_ASSERT(result);
765     result->SetParent(expr->Parent());
766     result->SetRange(expr->Range());
767     return result;
768 }
769 
IsEnumMemberInit(ir::AstNode * node)770 static bool IsEnumMemberInit(ir::AstNode *node)
771 {
772     auto parent = node->Parent();
773     if (node->IsMemberExpression()) {
774         return node->AsMemberExpression()->Object()->IsIdentifier();
775     }
776 
777     if (node->IsIdentifier()) {
778         if (parent->IsTSEnumMember()) {
779             return parent->AsTSEnumMember()->Init() == node;
780         }
781         return !parent->IsMemberExpression() && !parent->IsTSEnumDeclaration() && !parent->IsETSTypeReferencePart();
782     }
783 
784     return false;
785 }
786 
UnFoldEnumMemberExpression(ir::AstNode * constantNode)787 ir::AstNode *ConstantExpressionLowering::UnFoldEnumMemberExpression(ir::AstNode *constantNode)
788 {
789     ir::NodeTransformer handleUnfoldEnumMember = [this, constantNode](ir::AstNode *const node) {
790         if (IsEnumMemberInit(node) && constantNode->IsTSEnumDeclaration()) {
791             return FindAndReplaceEnumMember(node, constantNode);
792         }
793 
794         return node;
795     };
796     constantNode->TransformChildrenRecursivelyPostorder(handleUnfoldEnumMember, Name());
797     return constantNode;
798 }
799 
FindNameInEnumMember(ArenaVector<ir::AstNode * > * members,util::StringView targetName)800 ir::AstNode *ConstantExpressionLowering::FindNameInEnumMember(ArenaVector<ir::AstNode *> *members,
801                                                               util::StringView targetName)
802 {
803     auto it = std::find_if(members->begin(), members->end(), [&targetName](ir::AstNode *member) {
804         return member->AsTSEnumMember()->Key()->AsIdentifier()->Name() == targetName;
805     });
806     return (it != members->end()) ? *it : nullptr;
807 }
808 
FindAndReplaceEnumMember(ir::AstNode * const expr,ir::AstNode * constantNode)809 ir::AstNode *ConstantExpressionLowering::FindAndReplaceEnumMember(ir::AstNode *const expr, ir::AstNode *constantNode)
810 {
811     auto objectName = expr->IsMemberExpression() ? expr->AsMemberExpression()->Object()->AsIdentifier()->Name()
812                                                  : constantNode->AsTSEnumDeclaration()->Key()->AsIdentifier()->Name();
813     auto propertyName = expr->IsMemberExpression() ? expr->AsMemberExpression()->Property()->AsIdentifier()->Name()
814                                                    : expr->AsIdentifier()->Name();
815     for (auto curScope = constantNode->Scope(); curScope != nullptr; curScope = curScope->Parent()) {
816         auto *foundDecl = curScope->FindDecl(objectName);
817         if (foundDecl == nullptr || !foundDecl->Node()->IsTSEnumDeclaration()) {
818             continue;
819         }
820 
821         auto members = foundDecl->Node()->AsTSEnumDeclaration()->Members();
822         auto member = FindNameInEnumMember(&members, propertyName);
823         if (member != nullptr) {
824             auto *transformedInit = member->AsTSEnumMember()->Init();
825             if (transformedInit == nullptr) {
826                 return expr;
827             }
828 
829             auto clonedInit = transformedInit->Clone(context_->allocator, expr->Parent());
830             clonedInit->SetRange(expr->Range());
831             return UnFoldEnumMemberExpression(clonedInit);
832         }
833     }
834     return expr;
835 }
836 
FindIdentifier(ir::Identifier * ident)837 varbinder::Variable *ConstantExpressionLowering::FindIdentifier(ir::Identifier *ident)
838 {
839     auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, NearestScope(ident));
840     auto option = varbinder::ResolveBindingOptions::ALL_VARIABLES;
841     auto localScope = localCtx.GetScope();
842     ES2PANDA_ASSERT(localScope != nullptr);
843     auto *resolved = localScope->FindInFunctionScope(ident->Name(), option).variable;
844     if (resolved == nullptr) {
845         resolved = localScope->FindInGlobal(ident->Name(), option).variable;
846     }
847     return resolved;
848 }
849 
UnfoldConstIdentifier(ir::AstNode * node,ir::AstNode * originNode)850 ir::AstNode *ConstantExpressionLowering::UnfoldConstIdentifier(ir::AstNode *node, ir::AstNode *originNode)
851 {
852     ir::AstNode *resNode = nullptr;
853     if (node->IsClassProperty()) {
854         auto prop = node->AsClassElement();
855         resNode = prop->Value()->Clone(context_->allocator, originNode->Parent());
856         resNode->SetRange(originNode->Range());
857     }
858     if (node->Parent()->IsVariableDeclarator()) {
859         resNode = node->Parent()->AsVariableDeclarator()->Init()->Clone(context_->allocator, originNode->Parent());
860         resNode->SetRange(originNode->Range());
861     }
862     if (resNode == nullptr) {
863         return node;
864     }
865     if (!resNode->IsIdentifier()) {
866         return UnfoldConstIdentifiers(resNode);
867     }
868     auto *ident = resNode->AsIdentifier();
869     auto *resolved = FindIdentifier(ident);
870     if (resolved == nullptr) {
871         return resNode;
872     }
873     if (!resolved->Declaration()->IsConstDecl()) {
874         return resNode;
875     }
876     return UnfoldConstIdentifier(resolved->Declaration()->Node(), resNode);
877 }
878 
UnfoldConstIdentifiers(ir::AstNode * constantNode)879 ir::AstNode *ConstantExpressionLowering::UnfoldConstIdentifiers(ir::AstNode *constantNode)
880 {
881     ir::NodeTransformer handleUnfoldIdentifiers = [this](ir::AstNode *const node) {
882         if (node->IsIdentifier()) {
883             auto *ident = node->AsIdentifier();
884             auto *resolved = FindIdentifier(ident);
885             if (resolved == nullptr) {
886                 return node;
887             }
888             if (!resolved->Declaration()->IsConstDecl()) {
889                 return node;
890             }
891             return UnfoldConstIdentifier(resolved->Declaration()->Node(), node);
892         }
893         return node;
894     };
895     constantNode->TransformChildrenRecursivelyPostorder(handleUnfoldIdentifiers, Name());
896     return constantNode;
897 }
898 
IsPotentialConstant(const ir::AstNodeType type)899 static bool IsPotentialConstant(const ir::AstNodeType type)
900 {
901     return type == ir::AstNodeType::TEMPLATE_LITERAL || type == ir::AstNodeType::TS_AS_EXPRESSION ||
902            type == ir::AstNodeType::UNARY_EXPRESSION || type == ir::AstNodeType::BINARY_EXPRESSION ||
903            type == ir::AstNodeType::CONDITIONAL_EXPRESSION || type == ir::AstNodeType::IDENTIFIER;
904 }
905 
FoldConstant(ir::AstNode * constantNode)906 ir::AstNode *ConstantExpressionLowering::FoldConstant(ir::AstNode *constantNode)
907 {
908     ir::NodeTransformer handleFoldConstant = [this](ir::AstNode *const node) {
909         if (node->IsTemplateLiteral()) {
910             auto tmpLiteral = node->AsTemplateLiteral();
911             if (tmpLiteral->Expressions().empty()) {
912                 return FoldMultilineString(tmpLiteral);
913             }
914             LogError(diagnostic::STRING_INTERPOLATION_NOT_CONSTANT, {}, node->Start());
915         }
916         if (node->IsTSAsExpression()) {
917             auto tsAsExpr = node->AsTSAsExpression();
918             if (IsSupportedLiteral(tsAsExpr->Expr())) {
919                 return FoldTSAsExpression(tsAsExpr);
920             }
921             LogError(diagnostic::ONLY_CONSTANT_EXPRESSION, {}, node->Start());
922         }
923         if (node->IsUnaryExpression()) {
924             auto unaryOp = node->AsUnaryExpression();
925             if (IsSupportedLiteral(unaryOp->Argument())) {
926                 return FoldUnaryConstant(unaryOp);
927             }
928             LogError(diagnostic::ONLY_CONSTANT_EXPRESSION, {}, node->Start());
929         }
930         if (node->IsBinaryExpression()) {
931             auto binop = node->AsBinaryExpression();
932             if (IsSupportedLiteral(binop->Left()) && IsSupportedLiteral(binop->Right())) {
933                 return FoldBinaryConstant(binop);
934             }
935             LogError(diagnostic::ONLY_CONSTANT_EXPRESSION, {}, node->Start());
936         }
937         if (node->IsConditionalExpression()) {
938             auto condExp = node->AsConditionalExpression();
939             if (IsSupportedLiteral(condExp->Test())) {
940                 return FoldTernaryConstant(condExp);
941             }
942             LogError(diagnostic::ONLY_CONSTANT_EXPRESSION, {}, node->Start());
943         }
944         return node;
945     };
946     constantNode->TransformChildrenRecursivelyPostorder(handleFoldConstant, Name());
947     return constantNode;
948 }
949 
950 // Note: memberExpression can be constant when it is enum property access, this check will be enabled after Issue23082.
951 // for package, we need to check whether its every immediate-initializers is const expression.
IsInitByConstant(ir::AstNode * node)952 void ConstantExpressionLowering::IsInitByConstant(ir::AstNode *node)
953 {
954     ir::AstNode *initTobeChecked = nullptr;
955     if (node->IsExpressionStatement() && node->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) {
956         auto assignExpr = node->AsExpressionStatement()->GetExpression()->AsAssignmentExpression();
957         initTobeChecked = assignExpr->Right();
958         if (initTobeChecked->IsExpression() && IsSupportedLiteral(initTobeChecked->AsExpression())) {
959             return;
960         }
961 
962         if (!IsPotentialConstant(initTobeChecked->Type())) {
963             LogError(diagnostic::INVALID_INIT_IN_PACKAGE, {}, initTobeChecked->Start());
964             return;
965         }
966         assignExpr->SetRight(FoldConstant(UnfoldConstIdentifiers(initTobeChecked))->AsExpression());
967     }
968 
969     if (node->IsClassProperty()) {
970         auto classProp = node->AsClassProperty();
971         initTobeChecked = classProp->Value();
972         if (initTobeChecked == nullptr) {
973             return;
974         }
975 
976         if (initTobeChecked->IsExpression() && IsSupportedLiteral(initTobeChecked->AsExpression())) {
977             return;
978         }
979 
980         if (!IsPotentialConstant(initTobeChecked->Type())) {
981             LogError(diagnostic::INVALID_INIT_IN_PACKAGE, {}, initTobeChecked->Start());
982             return;
983         }
984         classProp->SetValue(FoldConstant(UnfoldConstIdentifiers(initTobeChecked))->AsExpression());
985     }
986 }
987 
TryFoldInitializerOfPackage(ir::ClassDefinition * globalClass)988 void ConstantExpressionLowering::TryFoldInitializerOfPackage(ir::ClassDefinition *globalClass)
989 {
990     for (auto element : globalClass->Body()) {
991         if (element->IsMethodDefinition()) {
992             auto const *classMethod = element->AsMethodDefinition();
993             if (!classMethod->Key()->IsIdentifier() ||
994                 !classMethod->Key()->AsIdentifier()->Name().Is(compiler::Signatures::INIT_METHOD)) {
995                 continue;
996             }
997 
998             auto const *methodBody = classMethod->Value()->AsFunctionExpression()->Function()->Body();
999             if (methodBody == nullptr || !methodBody->IsBlockStatement()) {
1000                 continue;
1001             }
1002             auto const &initStatements = methodBody->AsBlockStatement()->Statements();
1003             std::for_each(initStatements.begin(), initStatements.end(),
1004                           [this](ir::AstNode *node) { IsInitByConstant(node); });
1005         }
1006 
1007         if (element->IsClassProperty() && element->AsClassProperty()->IsConst() &&
1008             !element->AsClassProperty()->NeedInitInStaticBlock()) {
1009             IsInitByConstant(element);
1010         }
1011     }
1012 }
1013 
PerformForModule(public_lib::Context * ctx,parser::Program * program)1014 bool ConstantExpressionLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
1015 {
1016     if (program->GetFlag(parser::ProgramFlags::AST_CONSTANT_EXPRESSION_LOWERED)) {
1017         return true;
1018     }
1019 
1020     context_ = ctx;
1021     program_ = program;
1022     varbinder_ = ctx->parserProgram->VarBinder()->AsETSBinder();
1023     program->Ast()->TransformChildrenRecursively(
1024         [this](checker::AstNodePtr const node) -> checker::AstNodePtr {
1025             if (node->IsAnnotationDeclaration() || node->IsAnnotationUsage()) {
1026                 return FoldConstant(UnfoldConstIdentifiers(node));
1027             }
1028             if (node->IsTSEnumDeclaration()) {
1029                 return FoldConstant(UnFoldEnumMemberExpression(UnfoldConstIdentifiers(node)));
1030             }
1031 
1032             // Note: Package need to check whether its immediate initializer is const expression.
1033             if (this->program_->IsPackage() && node->IsClassDefinition() && node->AsClassDefinition()->IsGlobal()) {
1034                 TryFoldInitializerOfPackage(node->AsClassDefinition());
1035             }
1036             return node;
1037         },
1038         Name());
1039 
1040     program->SetFlag(parser::ProgramFlags::AST_CONSTANT_EXPRESSION_LOWERED);
1041     return true;
1042 }
1043 
1044 }  // namespace ark::es2panda::compiler
1045