1 /**
2 * Copyright (c) 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 "arrayLiteralLowering.h"
17 #include <sstream>
18 #include <utility>
19 #include <vector>
20
21 #include "compiler/lowering/util.h"
22 #include "ir/astNode.h"
23 #include "ir/expressions/literals/numberLiteral.h"
24 #include "lexer/token/number.h"
25 #include "util/es2pandaMacros.h"
26 #include "checker/ETSchecker.h"
27 #include "utils/arena_containers.h"
28
29 namespace ark::es2panda::compiler {
30
31 using AstNodePtr = ir::AstNode *;
32
Name() const33 std::string_view ArrayLiteralLowering::Name() const
34 {
35 return "ArrayLiteralLowering";
36 }
37
GenerateDefaultCallToConstructor(ir::Identifier * arraySymbol,checker::Type * eleType)38 ArenaVector<ir::Statement *> ArrayLiteralLowering::GenerateDefaultCallToConstructor(ir::Identifier *arraySymbol,
39 checker::Type *eleType)
40 {
41 std::stringstream ss;
42 std::vector<ir::AstNode *> newStmts;
43 if (!eleType->IsETSUnionType() && !eleType->IsETSAnyType()) {
44 auto *indexSymbol = Gensym(Allocator());
45 auto *lengthSymbol = Gensym(Allocator());
46 auto *typeNode = checker_->AllocNode<ir::OpaqueTypeNode>(eleType, Allocator());
47 ss << "let @@I1 : int = @@I2.length as int;";
48 newStmts.emplace_back(lengthSymbol);
49 newStmts.emplace_back(arraySymbol->Clone(Allocator(), nullptr));
50 ss << "for (let @@I3 = 0; @@I4 < @@E5; @@I6 = @@I7 + 1) {";
51 newStmts.emplace_back(indexSymbol);
52 newStmts.emplace_back(indexSymbol->Clone(Allocator(), nullptr));
53 newStmts.emplace_back(lengthSymbol->Clone(Allocator(), nullptr));
54 newStmts.emplace_back(indexSymbol->Clone(Allocator(), nullptr));
55 newStmts.emplace_back(indexSymbol->Clone(Allocator(), nullptr));
56 ss << "@@I8[@@I9] = new @@T10() }";
57 newStmts.emplace_back(arraySymbol->Clone(Allocator(), nullptr));
58 newStmts.emplace_back(indexSymbol->Clone(Allocator(), nullptr));
59 ES2PANDA_ASSERT(typeNode != nullptr);
60 newStmts.emplace_back(typeNode->Clone(Allocator(), nullptr));
61 } else {
62 ArenaVector<ir::Statement *> emptyStatement(Allocator()->Adapter());
63 return emptyStatement;
64 }
65
66 return parser_->CreateFormattedStatements(ss.str(), newStmts);
67 }
68
IsInAnnotationContext(ir::AstNode * node)69 static bool IsInAnnotationContext(ir::AstNode *node)
70 {
71 while (node != nullptr && !(node->IsClassDefinition() && node->AsClassDefinition()->IsGlobal())) {
72 if (node->IsAnnotationDeclaration() || node->IsAnnotationUsage()) {
73 return true;
74 }
75 node = node->Parent();
76 }
77 return false;
78 }
79
TryTransformLiteralArrayToRefArray(ir::ArrayExpression * literalArray)80 ir::AstNode *ArrayLiteralLowering::TryTransformLiteralArrayToRefArray(ir::ArrayExpression *literalArray)
81 {
82 auto literalArrayType =
83 literalArray->TsType() != nullptr ? literalArray->TsType() : literalArray->GetPreferredType();
84 if (literalArrayType->IsETSArrayType() || literalArrayType->IsETSTupleType() ||
85 !literalArrayType->IsETSResizableArrayType() || IsInAnnotationContext(literalArray)) {
86 return literalArray;
87 }
88 auto *arrayType = literalArrayType->AsETSResizableArrayType()->ElementType();
89 std::vector<ir::AstNode *> newStmts;
90 std::stringstream ss;
91 auto *genSymIdent = Gensym(Allocator());
92 auto *type = checker_->AllocNode<ir::OpaqueTypeNode>(arrayType, Allocator());
93 ss << "let @@I1 : FixedArray<@@T2> = @@E3;";
94 ss << "Array.from<@@T4>(@@I5);";
95 newStmts.emplace_back(genSymIdent);
96 newStmts.emplace_back(type);
97 literalArray->SetTsType(nullptr);
98 newStmts.emplace_back(literalArray);
99 newStmts.emplace_back(type->Clone(Allocator(), nullptr));
100 newStmts.emplace_back(genSymIdent->Clone(Allocator(), nullptr));
101
102 auto *parent = literalArray->Parent();
103 auto *loweringResult = parser_->CreateFormattedExpression(ss.str(), newStmts);
104 ES2PANDA_ASSERT(loweringResult != nullptr);
105 loweringResult->SetRange(literalArray->Range());
106 loweringResult->SetParent(parent);
107
108 auto *scope = NearestScope(parent);
109 auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
110 CheckLoweredNode(varbinder_, checker_, loweringResult);
111 return loweringResult;
112 }
113
TryTransformNewArrayExprToRefArray(ir::ETSNewArrayInstanceExpression * newExpr)114 ir::AstNode *ArrayLiteralLowering::TryTransformNewArrayExprToRefArray(ir::ETSNewArrayInstanceExpression *newExpr)
115 {
116 if (newExpr->TsType()->IsETSArrayType()) {
117 return newExpr;
118 }
119 ES2PANDA_ASSERT(newExpr->TsType()->IsETSResizableArrayType());
120
121 auto *arrayType = newExpr->TsType()->AsETSResizableArrayType()->ElementType();
122 std::vector<ir::AstNode *> newStmts;
123 auto *genSymIdent = Gensym(Allocator());
124
125 std::stringstream ss;
126 ss << "let @@I1 = new Array<@@T2>(@@E3 as number);";
127 auto *type = checker_->AllocNode<ir::OpaqueTypeNode>(arrayType, Allocator());
128 auto *dimension = newExpr->Dimension()->Clone(Allocator(), nullptr);
129 newStmts.emplace_back(genSymIdent);
130 newStmts.emplace_back(type);
131 newStmts.emplace_back(dimension);
132
133 ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
134 auto *newArrStatement = parser_->CreateFormattedStatement(ss.str(), newStmts);
135 statements.emplace_back(newArrStatement);
136 auto newArrElementStatements = GenerateDefaultCallToConstructor(genSymIdent, arrayType);
137 statements.insert(statements.end(), newArrElementStatements.begin(), newArrElementStatements.end());
138 auto returnStmt = parser_->CreateFormattedStatement("@@I1", genSymIdent->Clone(Allocator(), nullptr));
139 statements.emplace_back(returnStmt);
140 auto *loweringResult = checker_->AllocNode<ir::BlockExpression>(std::move(statements));
141 loweringResult->SetRange(newExpr->Range());
142 loweringResult->SetParent(newExpr->Parent());
143 auto *scope = NearestScope(loweringResult->Parent());
144 auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
145 CheckLoweredNode(varbinder_, checker_, loweringResult);
146 return loweringResult;
147 }
148
CreateNestedArrayCreationStatement(ArenaVector<ir::Identifier * > & identDims,size_t currentDim,checker::Type * type,ir::Expression * expr)149 ir::Statement *ArrayLiteralLowering::CreateNestedArrayCreationStatement(ArenaVector<ir::Identifier *> &identDims,
150 size_t currentDim, checker::Type *type,
151 ir::Expression *expr)
152 {
153 auto *genSymIdent = Gensym(Allocator());
154 auto *arraySymbol = Gensym(Allocator());
155 auto *lastDimIdent = identDims[currentDim - 1];
156 auto *currentDimIdent = identDims[currentDim];
157 auto *arrayType = type->AsETSResizableArrayType()->ElementType();
158 auto typeNode = checker_->AllocNode<ir::OpaqueTypeNode>(arrayType, Allocator());
159 auto arrayAccessExpr = checker_->AllocNode<ir::MemberExpression>(
160 expr->Clone(Allocator(), nullptr)->AsExpression(), genSymIdent->Clone(Allocator(), nullptr),
161 ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
162
163 std::string creationTemplate =
164 "for (let @@I1 = 0; @@I2 < @@I3; @@I4 = @@I5 + 1) { let @@I6 = new Array<@@T7>(@@E8 as number); @@E9 = @@I10}";
165 ir::Statement *forUpdateStmt = parser_->CreateFormattedStatement(
166 creationTemplate, genSymIdent, genSymIdent->Clone(Allocator(), nullptr),
167 lastDimIdent->Clone(Allocator(), nullptr), genSymIdent->Clone(Allocator(), nullptr),
168 genSymIdent->Clone(Allocator(), nullptr), arraySymbol, typeNode, currentDimIdent->Clone(Allocator(), nullptr),
169 arrayAccessExpr, arraySymbol->Clone(Allocator(), nullptr));
170 if (identDims.size() > currentDim + 1) {
171 auto consequentStmt = CreateNestedArrayCreationStatement(identDims, currentDim + 1, arrayType, arrayAccessExpr);
172 forUpdateStmt->AsForUpdateStatement()->Body()->AsBlockStatement()->AddStatement(consequentStmt);
173 } else if (identDims.size() == currentDim + 1) {
174 // For last dim, initialize the array elements.
175 auto newArrElementStatements = GenerateDefaultCallToConstructor(arraySymbol, arrayType);
176 forUpdateStmt->AsForUpdateStatement()->Body()->AsBlockStatement()->AddStatements(newArrElementStatements);
177 }
178
179 return forUpdateStmt;
180 }
181
TransformDimVectorToIdentVector(ArenaVector<ir::Expression * > & dimVector,ArenaVector<ir::Statement * > & stmts)182 ArenaVector<ir::Identifier *> ArrayLiteralLowering::TransformDimVectorToIdentVector(
183 ArenaVector<ir::Expression *> &dimVector, ArenaVector<ir::Statement *> &stmts)
184 {
185 std::vector<ir::AstNode *> statements;
186 ArenaVector<ir::Identifier *> idents(Allocator()->Adapter());
187 auto addNode = [&statements](ir::AstNode *node) -> size_t {
188 statements.emplace_back(node);
189 return statements.size();
190 };
191
192 std::stringstream ss;
193 for (size_t i = 0; i < dimVector.size(); ++i) {
194 idents.emplace_back(Gensym(Allocator()));
195 ss << "let @@I" << addNode(idents[i]) << " = @@E" << addNode(dimVector[i]->Clone(Allocator(), nullptr)) << ";";
196 }
197 auto parsedStatement = parser_->CreateFormattedStatements(ss.str(), statements);
198 stmts.insert(stmts.end(), parsedStatement.begin(), parsedStatement.end());
199 return idents;
200 }
201
TryTransformNewMultiDimArrayToRefArray(ir::ETSNewMultiDimArrayInstanceExpression * newExpr)202 ir::AstNode *ArrayLiteralLowering::TryTransformNewMultiDimArrayToRefArray(
203 ir::ETSNewMultiDimArrayInstanceExpression *newExpr)
204 {
205 if (newExpr->TsType()->IsETSArrayType()) {
206 return newExpr;
207 }
208 ES2PANDA_ASSERT(newExpr->TsType()->IsETSResizableArrayType());
209 ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
210 // Create outer forloop
211 auto arrayType = newExpr->TsType()->AsETSResizableArrayType()->ElementType();
212 auto *type = checker_->AllocNode<ir::OpaqueTypeNode>(arrayType, Allocator());
213 auto *genSymIdent = Gensym(Allocator());
214 std::string newArray = "let @@I1 = new Array<@@T2>(@@I3 as number)";
215 auto idents = TransformDimVectorToIdentVector(newExpr->Dimensions(), statements);
216 auto newArraystatement =
217 parser_->CreateFormattedStatements(newArray, genSymIdent, type, idents[0]->Clone(Allocator(), nullptr));
218 auto nestedArrayCreationStmt = CreateNestedArrayCreationStatement(idents, 1, arrayType, genSymIdent);
219 auto returnStmt = parser_->CreateFormattedStatement("@@I1", genSymIdent->Clone(Allocator(), nullptr));
220 statements.insert(statements.end(), newArraystatement.begin(), newArraystatement.end());
221 statements.push_back(nestedArrayCreationStmt);
222 statements.push_back(returnStmt);
223 auto loweringResult = checker_->AllocNode<ir::BlockExpression>(std::move(statements));
224 loweringResult->SetRange(newExpr->Range());
225 loweringResult->SetParent(newExpr->Parent());
226 auto *scope = NearestScope(loweringResult->Parent());
227 auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
228 CheckLoweredNode(varbinder_, checker_, loweringResult);
229 return loweringResult;
230 }
231
PerformForModule(public_lib::Context * ctx,parser::Program * program)232 bool ArrayLiteralLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
233 {
234 parser_ = ctx->parser->AsETSParser();
235 varbinder_ = ctx->parserProgram->VarBinder()->AsETSBinder();
236 checker_ = ctx->checker->AsETSChecker();
237 program->Ast()->TransformChildrenRecursively(
238 [this](ir::AstNode *ast) -> AstNodePtr {
239 if (ast->IsArrayExpression()) {
240 return TryTransformLiteralArrayToRefArray(ast->AsArrayExpression());
241 }
242 if (ast->IsETSNewArrayInstanceExpression()) {
243 return TryTransformNewArrayExprToRefArray(ast->AsETSNewArrayInstanceExpression());
244 }
245 if (ast->IsETSNewMultiDimArrayInstanceExpression()) {
246 return TryTransformNewMultiDimArrayToRefArray(ast->AsETSNewMultiDimArrayInstanceExpression());
247 }
248 return ast;
249 },
250 Name());
251
252 return true;
253 }
254
Allocator()255 ArenaAllocator *ArrayLiteralLowering::Allocator()
256 {
257 return checker_->Allocator();
258 }
259
260 } // namespace ark::es2panda::compiler