1 /*
2 * Copyright (c) 2023-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 "expandBrackets.h"
17
18 #include "checker/ETSchecker.h"
19 #include "compiler/lowering/util.h"
20 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
21
22 namespace ark::es2panda::compiler {
23
24 // NOLINTBEGIN(modernize-avoid-c-arrays)
25 static constexpr char const FORMAT_NEW_MULTI_DIM_ARRAY_EXPRESSION[] =
26 "let @@I1: @@T2 = (@@E3);"
27 "if (!isSafeInteger(@@I4)) {"
28 " throw new TypeError(\"Fractional part of index expression should be zero.\");"
29 "};";
30 static constexpr char const FORMAT_NEW_ARRAY_EXPRESSION[] =
31 "let @@I1: @@T2 = (@@E3);"
32 "if (!isSafeInteger(@@I4)) {"
33 " throw new TypeError(\"Fractional part of index expression should be zero.\");"
34 "};"
35 "(@@E5);";
36 static constexpr char const CAST_NEW_DIMENSION_EXPRESSION[] = "@@I1 as int";
37 static constexpr char const CAST_OLD_DIMENSION_EXPRESSION[] = "(@@E1) as int";
38 // NOLINTEND(modernize-avoid-c-arrays)
39
ProcessNewArrayInstanceExpression(public_lib::Context * ctx,ir::ETSNewArrayInstanceExpression * newInstanceExpression) const40 ir::Expression *ExpandBracketsPhase::ProcessNewArrayInstanceExpression(
41 public_lib::Context *ctx, ir::ETSNewArrayInstanceExpression *newInstanceExpression) const
42 {
43 auto *const parser = ctx->parser->AsETSParser();
44 ES2PANDA_ASSERT(parser != nullptr);
45 auto *const checker = ctx->checker->AsETSChecker();
46 ES2PANDA_ASSERT(checker != nullptr);
47 auto *dimension = newInstanceExpression->Dimension();
48 auto *dimType = dimension->TsType();
49 if (auto *unboxed = checker->MaybeUnboxInRelation(dimType); unboxed != nullptr) {
50 dimType = unboxed;
51 }
52 if (dimType == nullptr || !dimType->HasTypeFlag(checker::TypeFlag::ETS_FLOATING_POINT)) {
53 return newInstanceExpression;
54 }
55
56 auto *scope = NearestScope(newInstanceExpression);
57 if (scope == nullptr) {
58 scope = checker->VarBinder()->VarScope() != nullptr ? checker->VarBinder()->VarScope()
59 : checker->VarBinder()->TopScope();
60 }
61 auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
62
63 auto const identName = GenName(ctx->Allocator());
64 auto *exprType = ctx->AllocNode<ir::OpaqueTypeNode>(dimType, ctx->Allocator());
65 auto *const newInstanceParent = newInstanceExpression->Parent();
66
67 auto *blockExpression = parser->CreateFormattedExpression(FORMAT_NEW_ARRAY_EXPRESSION, identName, exprType,
68 dimension, identName, newInstanceExpression);
69 blockExpression->SetParent(newInstanceParent);
70
71 auto *castedDimension = parser->CreateFormattedExpression(CAST_NEW_DIMENSION_EXPRESSION, identName);
72 newInstanceExpression->SetDimension(castedDimension);
73
74 newInstanceExpression->SetTsType(nullptr);
75 InitScopesPhaseETS::RunExternalNode(blockExpression, checker->VarBinder());
76 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpression, NearestScope(blockExpression));
77 blockExpression->Check(checker);
78
79 return blockExpression;
80 }
81
ProcessNewMultiDimArrayInstanceExpression(public_lib::Context * ctx,ir::ETSNewMultiDimArrayInstanceExpression * newInstanceExpression) const82 ir::Expression *ExpandBracketsPhase::ProcessNewMultiDimArrayInstanceExpression(
83 public_lib::Context *ctx, ir::ETSNewMultiDimArrayInstanceExpression *newInstanceExpression) const
84 {
85 auto *const parser = ctx->parser->AsETSParser();
86 ES2PANDA_ASSERT(parser != nullptr);
87 auto *const checker = ctx->checker->AsETSChecker();
88 ES2PANDA_ASSERT(checker != nullptr);
89 ir::BlockExpression *returnExpression = nullptr;
90
91 auto *scope = NearestScope(newInstanceExpression);
92 if (scope == nullptr) {
93 scope = checker->VarBinder()->VarScope() != nullptr ? checker->VarBinder()->VarScope()
94 : checker->VarBinder()->TopScope();
95 }
96 auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
97
98 for (std::size_t i = 0U; i < newInstanceExpression->Dimensions().size(); ++i) {
99 auto *dimension = newInstanceExpression->Dimensions()[i];
100 auto *dimType = dimension->TsType();
101 if (auto *unboxed = checker->MaybeUnboxInRelation(dimType); unboxed != nullptr) {
102 dimType = unboxed;
103 }
104 if (dimType == nullptr || !dimType->HasTypeFlag(checker::TypeFlag::ETS_FLOATING_POINT)) {
105 continue;
106 }
107
108 if (dimension->IsNumberLiteral()) {
109 auto *castedDimension = parser->CreateFormattedExpression(CAST_OLD_DIMENSION_EXPRESSION, dimension);
110 castedDimension->SetParent(newInstanceExpression);
111 newInstanceExpression->Dimensions()[i] = castedDimension;
112 } else {
113 auto const identName = GenName(ctx->Allocator());
114 auto *exprType = ctx->AllocNode<ir::OpaqueTypeNode>(dimType, ctx->Allocator());
115
116 auto *blockExpression = parser
117 ->CreateFormattedExpression(FORMAT_NEW_MULTI_DIM_ARRAY_EXPRESSION, identName,
118 exprType, dimension, identName)
119 ->AsBlockExpression();
120
121 if (returnExpression == nullptr) {
122 returnExpression = blockExpression;
123 } else {
124 returnExpression->AddStatements(blockExpression->Statements());
125 }
126
127 auto *castedDimension = parser->CreateFormattedExpression(CAST_NEW_DIMENSION_EXPRESSION, identName);
128 castedDimension->SetParent(newInstanceExpression);
129 newInstanceExpression->Dimensions()[i] = castedDimension;
130 }
131 }
132
133 if (returnExpression != nullptr) {
134 return CreateNewMultiDimArrayInstanceExpression(ctx, newInstanceExpression, returnExpression);
135 }
136
137 return newInstanceExpression;
138 }
139
140 // NOTE: Just to reduce the size of 'ProcessNewMultiDimArrayInstanceExpression' method
CreateNewMultiDimArrayInstanceExpression(public_lib::Context * ctx,ir::ETSNewMultiDimArrayInstanceExpression * newInstanceExpression,ir::BlockExpression * blockExpression) const141 ir::Expression *ExpandBracketsPhase::CreateNewMultiDimArrayInstanceExpression(
142 public_lib::Context *ctx, ir::ETSNewMultiDimArrayInstanceExpression *newInstanceExpression,
143 ir::BlockExpression *blockExpression) const
144 {
145 blockExpression->SetParent(newInstanceExpression->Parent());
146 newInstanceExpression->SetTsType(nullptr);
147 blockExpression->AddStatement(ctx->AllocNode<ir::ExpressionStatement>(newInstanceExpression));
148
149 auto *checker = ctx->checker->AsETSChecker();
150 InitScopesPhaseETS::RunExternalNode(blockExpression, checker->VarBinder());
151 checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(blockExpression, NearestScope(blockExpression));
152 blockExpression->Check(checker);
153
154 return blockExpression;
155 }
156
PerformForModule(public_lib::Context * ctx,parser::Program * program)157 bool ExpandBracketsPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
158 {
159 program->Ast()->TransformChildrenRecursively(
160 [this, ctx](checker::AstNodePtr const ast) -> checker::AstNodePtr {
161 if (ast->IsETSNewArrayInstanceExpression()) {
162 return ProcessNewArrayInstanceExpression(ctx, ast->AsETSNewArrayInstanceExpression());
163 }
164
165 if (ast->IsETSNewMultiDimArrayInstanceExpression()) {
166 return ProcessNewMultiDimArrayInstanceExpression(ctx, ast->AsETSNewMultiDimArrayInstanceExpression());
167 }
168
169 return ast;
170 },
171 Name());
172
173 return true;
174 }
175
176 } // namespace ark::es2panda::compiler
177