• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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