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