• 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 "spreadLowering.h"
17 #include "checker/ETSchecker.h"
18 #include "checker/types/ets/etsTupleType.h"
19 #include "compiler/lowering/util.h"
20 
21 namespace ark::es2panda::compiler {
22 
23 using AstNodePtr = ir::AstNode *;
24 
SetPossibleTupleType(ir::Identifier * arrIdent,ir::Expression * spreadArgument)25 void SetPossibleTupleType(ir::Identifier *arrIdent, ir::Expression *spreadArgument)
26 {
27     // Tuple types are used when referenceing the generated identifier of the spread argument node, as tuples don't have
28     // 'length' property, and cannot be iterated by for-of statements
29     auto *const spreadType = spreadArgument->TsType();
30     if (spreadType->IsETSTupleType()) {
31         arrIdent->SetTsType(spreadType);
32     }
33 }
34 
CreateSpreadArrayDeclareStatements(public_lib::Context * ctx,ir::ArrayExpression * array,std::vector<ir::Identifier * > & spreadArrayIds,ArenaVector<ir::Statement * > & statements)35 void CreateSpreadArrayDeclareStatements(public_lib::Context *ctx, ir::ArrayExpression *array,
36                                         std::vector<ir::Identifier *> &spreadArrayIds,
37                                         ArenaVector<ir::Statement *> &statements)
38 {
39     auto *const allocator = ctx->allocator;
40     auto *const parser = ctx->parser->AsETSParser();
41     for (auto element : array->Elements()) {
42         if (element->Type() != ir::AstNodeType::SPREAD_ELEMENT) {
43             continue;
44         }
45         ir::Identifier *const arrIdent = Gensym(allocator);
46         auto *const spreadArgument = element->AsSpreadElement()->Argument();
47         auto *const initExpr = spreadArgument->Clone(allocator, nullptr);
48         SetPossibleTupleType(arrIdent, spreadArgument);
49         spreadArrayIds.emplace_back(arrIdent);
50         statements.emplace_back(parser->CreateFormattedStatement("let @@I1 = (@@E2);", arrIdent, initExpr));
51     }
52 }
53 
CreateNewArrayLengthStatement(public_lib::Context * ctx,ir::ArrayExpression * array,std::vector<ir::Identifier * > & spreadArrayIds,ArenaVector<ir::Statement * > & statements)54 ir::Identifier *CreateNewArrayLengthStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
55                                               std::vector<ir::Identifier *> &spreadArrayIds,
56                                               ArenaVector<ir::Statement *> &statements)
57 {
58     auto *const allocator = ctx->allocator;
59     auto *const parser = ctx->parser->AsETSParser();
60     ir::Identifier *newArrayLengthId = Gensym(allocator);
61     ES2PANDA_ASSERT(newArrayLengthId != nullptr);
62     std::vector<ir::AstNode *> nodesWaitingInsert {newArrayLengthId->Clone(allocator, nullptr)};
63     size_t argumentCount = 1;
64     std::stringstream lengthString;
65     const size_t normalElementCount = array->Elements().size() - spreadArrayIds.size();
66     lengthString << "let @@I" << (argumentCount++) << " : int = " << normalElementCount << " + ";
67     for (auto *const spaId : spreadArrayIds) {
68         if (spaId->TsType() != nullptr && spaId->TsType()->IsETSTupleType()) {
69             lengthString << "(" << spaId->TsType()->AsETSTupleType()->GetTupleSize() << ") + ";
70         } else {
71             lengthString << "(@@I" << (argumentCount++) << ".length as int) + ";
72             nodesWaitingInsert.emplace_back(spaId->Clone(allocator, nullptr));
73         }
74     }
75     lengthString << "0;";
76 
77     ir::Statement *newArrayLengthStatement = parser->CreateFormattedStatement(lengthString.str(), nodesWaitingInsert);
78     statements.emplace_back(newArrayLengthStatement);
79     return newArrayLengthId;
80 }
81 
CreateNewArrayDeclareStatement(public_lib::Context * ctx,ir::ArrayExpression * array,ArenaVector<ir::Statement * > & statements,ir::Identifier * newArrayLengthId)82 static ir::Identifier *CreateNewArrayDeclareStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
83                                                       ArenaVector<ir::Statement *> &statements,
84                                                       ir::Identifier *newArrayLengthId)
85 {
86     auto *const checker = ctx->checker->AsETSChecker();
87     auto *const allocator = ctx->allocator;
88     auto *const parser = ctx->parser->AsETSParser();
89     ir::Identifier *newArrayId = Gensym(allocator);
90     checker::Type *arrayElementType = checker->GetElementTypeOfArray(array->TsType());
91 
92     // NOTE: If arrayElementType is ETSUnionType(String|Int) or ETSObjectType(private constructor) or ..., we cannot
93     //       use "new Type[]" to declare an array, so we generate a new UnionType "arrayElementType|null" to solve
94     //       array initialization problems temporarily.
95     //       We probably need to use cast Expression in the end of the generated source code to remove "|null", such as
96     //       "newArrayName as arrayType[]".
97     //       But now cast Expression doesn't support built-in array (cast fatherType[] to sonType[]), so "newArrayName
98     //       as arrayType" should be added after cast Expression is implemented completely.
99     //       Related issue: #issue20162
100     if (checker::ETSChecker::IsReferenceType(arrayElementType)) {
101         arrayElementType = checker->CreateETSUnionType({arrayElementType, checker->GlobalETSUndefinedType()});
102     }
103 
104     std::stringstream newArrayDeclareStr;
105     if (array->TsType()->IsETSResizableArrayType()) {
106         newArrayDeclareStr << "let @@I1: Array<@@T2> = new Array<@@T3>(@@I4);" << std::endl;
107     } else {
108         newArrayDeclareStr << "let @@I1: FixedArray<@@T2> = new (@@T3)[@@I4];" << std::endl;
109     }
110 
111     ES2PANDA_ASSERT(newArrayLengthId != nullptr);
112     ir::Statement *newArrayDeclareSt = parser->CreateFormattedStatement(
113         newArrayDeclareStr.str(), newArrayId->Clone(allocator, nullptr), arrayElementType, arrayElementType,
114         newArrayLengthId->Clone(allocator, nullptr));
115     statements.emplace_back(newArrayDeclareSt);
116 
117     return newArrayId;
118 }
119 
GenerateNewTupleInitList(ArenaAllocator * allocator,ir::ArrayExpression * array,std::vector<ir::AstNode * > & elementNodes)120 static std::string GenerateNewTupleInitList(ArenaAllocator *allocator, ir::ArrayExpression *array,
121                                             std::vector<ir::AstNode *> &elementNodes)
122 {
123     std::string tupleInitList {};
124 
125     for (auto *element : array->Elements()) {
126         if (element->IsSpreadElement()) {
127             // Only a tuple type can be spread into a new tuple type
128             ES2PANDA_ASSERT(element->AsSpreadElement()->Argument()->TsType()->IsETSTupleType());
129             auto *const argumentTupleType = element->AsSpreadElement()->Argument()->TsType()->AsETSTupleType();
130 
131             // NOTE (smartin): make a distinct variable for the spread argument. Now if the argument is a function call
132             // (that returns a tuple), it'll run every time when inserted into the new initializer. It should however
133             // run once, and index the element from that result.
134 
135             for (std::size_t idx = 0; idx < argumentTupleType->GetTupleSize(); idx++) {
136                 tupleInitList +=
137                     "@@E" + std::to_string(elementNodes.size() + 1) + "[" + std::to_string(idx) + "]" + ", ";
138                 elementNodes.emplace_back(element->Clone(allocator, nullptr)->AsSpreadElement()->Argument());
139             }
140         } else {
141             tupleInitList += "@@E" + std::to_string(elementNodes.size() + 1) + ", ";
142             elementNodes.emplace_back(element->Clone(allocator, nullptr));
143         }
144     }
145 
146     tupleInitList.pop_back();
147     return tupleInitList;
148 }
149 
GenerateTupleInitExpr(public_lib::Context * ctx,ir::ArrayExpression * array)150 static ir::Expression *GenerateTupleInitExpr(public_lib::Context *ctx, ir::ArrayExpression *array)
151 {
152     auto *const parser = ctx->parser->AsETSParser();
153     auto *const allocator = ctx->allocator;
154 
155     std::vector<ir::AstNode *> arrayExprElementNodes {};
156     std::stringstream newTupleExpr;
157 
158     newTupleExpr << "[";
159     newTupleExpr << GenerateNewTupleInitList(allocator, array, arrayExprElementNodes);
160     newTupleExpr << "];";
161 
162     return parser->CreateFormattedExpression(newTupleExpr.str(), arrayExprElementNodes);
163 }
164 
CreateNewTupleDeclareStatement(public_lib::Context * ctx,ir::ArrayExpression * array,ArenaVector<ir::Statement * > & statements)165 static ir::Identifier *CreateNewTupleDeclareStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
166                                                       ArenaVector<ir::Statement *> &statements)
167 {
168     auto *const allocator = ctx->allocator;
169     auto *const parser = ctx->parser->AsETSParser();
170     ir::Identifier *newTupleId = Gensym(allocator);
171     ES2PANDA_ASSERT(newTupleId != nullptr);
172     checker::ETSTupleType *tupleType = array->TsType()->AsETSTupleType();
173 
174     std::stringstream newArrayDeclareStr;
175     newArrayDeclareStr << "let @@I1: (@@T2) = (@@E3);" << std::endl;
176 
177     ir::Expression *tupleCreationExpr = GenerateTupleInitExpr(ctx, array);
178 
179     ir::Statement *newTupleInitStmt = parser->CreateFormattedStatement(
180         newArrayDeclareStr.str(), newTupleId->Clone(allocator, nullptr), tupleType, tupleCreationExpr);
181     statements.emplace_back(newTupleInitStmt);
182 
183     return newTupleId;
184 }
185 
CreateElementsAssignStatementBySpreadArr(public_lib::Context * ctx,ir::Identifier * spId,std::vector<ir::AstNode * > & newArrayAndIndex,ir::Identifier * spreadArrIterator,checker::Type * arrayElementType)186 static ir::Statement *CreateElementsAssignStatementBySpreadArr(public_lib::Context *ctx, ir::Identifier *spId,
187                                                                std::vector<ir::AstNode *> &newArrayAndIndex,
188                                                                ir::Identifier *spreadArrIterator,
189                                                                checker::Type *arrayElementType)
190 {
191     auto *const allocator = ctx->allocator;
192     auto *const parser = ctx->parser->AsETSParser();
193     auto *const newArrayId = newArrayAndIndex[0];
194     auto *const newArrayIndexId = newArrayAndIndex[1];
195 
196     std::stringstream elementsAssignStr;
197     elementsAssignStr << "for (let @@I1 of @@I2) {";
198     elementsAssignStr << "@@I3[@@I4] = @@I5 as @@T6;";
199     elementsAssignStr << "@@I7++;";
200     elementsAssignStr << "}";
201 
202     ES2PANDA_ASSERT(spreadArrIterator != nullptr);
203     ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement(
204         elementsAssignStr.str(), spreadArrIterator->Clone(allocator, nullptr), spId->Clone(allocator, nullptr),
205         newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr),
206         spreadArrIterator->Clone(allocator, nullptr), arrayElementType, newArrayIndexId->Clone(allocator, nullptr));
207 
208     return elementsAssignStatement;
209 }
210 
CreateElementsAssignStatementBySingle(public_lib::Context * ctx,ir::AstNode * element,std::vector<ir::AstNode * > & newArrayAndIndex)211 static ir::Statement *CreateElementsAssignStatementBySingle(public_lib::Context *ctx, ir::AstNode *element,
212                                                             std::vector<ir::AstNode *> &newArrayAndIndex)
213 {
214     auto *const allocator = ctx->allocator;
215     auto *const parser = ctx->parser->AsETSParser();
216     auto *const newArrayId = newArrayAndIndex[0];
217     auto *const newArrayIndexId = newArrayAndIndex[1];
218     std::stringstream elementsAssignStr;
219     elementsAssignStr << "@@I1[@@I2] = (@@E3);";
220     elementsAssignStr << "@@I4++;";
221 
222     ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement(
223         elementsAssignStr.str(), newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr),
224         element->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr));
225 
226     return elementsAssignStatement;
227 }
228 
CreateElementsAssignForTupleElements(public_lib::Context * ctx,ir::Identifier * spId,std::vector<ir::AstNode * > & newArrayAndIndex)229 static std::vector<ir::Statement *> CreateElementsAssignForTupleElements(public_lib::Context *ctx, ir::Identifier *spId,
230                                                                          std::vector<ir::AstNode *> &newArrayAndIndex)
231 {
232     auto *const allocator = ctx->allocator;
233     auto *const parser = ctx->parser->AsETSParser();
234     auto *const newArrayId = newArrayAndIndex[0];
235     auto *const newArrayIndexId = newArrayAndIndex[1];
236 
237     ES2PANDA_ASSERT(spId->TsType()->IsETSTupleType());
238     const auto *const spreadType = spId->TsType()->AsETSTupleType();
239     std::vector<ir::Statement *> tupleAssignmentStatements {};
240 
241     for (size_t idx = 0; idx < spreadType->GetTupleTypesList().size(); ++idx) {
242         std::stringstream tupleAssignmentsStr {};
243         auto *elementType = spreadType->GetTupleTypesList()[idx];
244         tupleAssignmentsStr << "@@I1[@@I2] = (@@I3[" << idx << "] as @@T4);";
245         tupleAssignmentsStr << "@@I5++;";
246         tupleAssignmentStatements.emplace_back(parser->CreateFormattedStatement(
247             tupleAssignmentsStr.str(), newArrayId->Clone(allocator, nullptr),
248             newArrayIndexId->Clone(allocator, nullptr), spId->Clone(allocator, nullptr), elementType,
249             newArrayIndexId->Clone(allocator, nullptr)));
250     }
251 
252     return tupleAssignmentStatements;
253 }
254 
CreateReturnStatement(public_lib::Context * ctx,ir::AstNode * newArrayId,ir::ArrayExpression * array)255 static ir::Statement *CreateReturnStatement(public_lib::Context *ctx, ir::AstNode *newArrayId,
256                                             ir::ArrayExpression *array)
257 {
258     auto *const allocator = ctx->allocator;
259     auto *const parser = ctx->parser->AsETSParser();
260 
261     std::vector<ir::AstNode *> nodes {newArrayId};
262     std::stringstream ss;
263     ss << "@@I1 ";
264 
265     if (array->TsType()->IsETSResizableArrayType()) {
266         ss << "as Object as Array<@@T2>;" << std::endl;
267         nodes.emplace_back(
268             allocator->New<ir::OpaqueTypeNode>(array->TsType()->AsETSResizableArrayType()->ElementType(), allocator));
269     }
270 
271     ir::Statement *returnStatement = parser->CreateFormattedStatement(ss.str(), nodes);
272     return returnStatement;
273 }
274 
CreateNewArrayElementsAssignStatement(public_lib::Context * ctx,ir::ArrayExpression * array,std::vector<ir::Identifier * > & spArrIds,ArenaVector<ir::Statement * > & statements,std::vector<ir::AstNode * > & newArrayAndIndex)275 static void CreateNewArrayElementsAssignStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
276                                                   std::vector<ir::Identifier *> &spArrIds,
277                                                   ArenaVector<ir::Statement *> &statements,
278                                                   std::vector<ir::AstNode *> &newArrayAndIndex)
279 {
280     auto *const allocator = ctx->allocator;
281     auto *const newArrayId = newArrayAndIndex[0];
282     size_t spArrIdx = 0;
283 
284     for (auto *element : array->Elements()) {
285         if (element->IsSpreadElement()) {
286             if (element->AsSpreadElement()->Argument()->TsType()->IsETSTupleType()) {
287                 const auto newTupleAssignmentStatements =
288                     CreateElementsAssignForTupleElements(ctx, spArrIds[spArrIdx++], newArrayAndIndex);
289                 statements.insert(statements.cend(), newTupleAssignmentStatements.cbegin(),
290                                   newTupleAssignmentStatements.cend());
291             } else {
292                 ir::Identifier *spreadArrIterator = Gensym(allocator);
293                 checker::Type *arrayElementType = ctx->checker->AsETSChecker()->GetElementTypeOfArray(array->TsType());
294                 statements.emplace_back(CreateElementsAssignStatementBySpreadArr(
295                     ctx, spArrIds[spArrIdx++], newArrayAndIndex, spreadArrIterator, arrayElementType));
296             }
297         } else {
298             statements.emplace_back(CreateElementsAssignStatementBySingle(ctx, element, newArrayAndIndex));
299         }
300     }
301 
302     statements.emplace_back(CreateReturnStatement(ctx, newArrayId->Clone(allocator, nullptr), array));
303 }
304 
305 /*
306  * NOTE: Expand the SpreadExpr to BlockExpr, the rules as follows :
307  * let spreadArrayId1 = spreadExpr1
308  * let length : int = normalExprCount + spreadArrayId1.length + 0
309  * type typeOfNewArray = arrayType
310  * let newArray: typeOfNewArray[] = new typeOfNewArray[length]
311  * let newArrayIndex = 0
312  * let elementOfSpreadArray1: arrayType
313  * for (elementOfSpreadArray1 of spreadArray1) {
314  *     newArray[newArrayIndex] = elementOfSpreadArray1
315  *     newArrayIndex++
316  * }
317  * newArray[newArrayIndex] = normalExpr1
318  * newArrayIndex++
319  * ...
320  * newArray;
321  */
CreateLoweredExpressionForArray(public_lib::Context * ctx,ir::ArrayExpression * array)322 static ir::BlockExpression *CreateLoweredExpressionForArray(public_lib::Context *ctx, ir::ArrayExpression *array)
323 {
324     auto *const parser = ctx->parser->AsETSParser();
325     auto *const allocator = ctx->allocator;
326     ArenaVector<ir::Statement *> statements(allocator->Adapter());
327     std::vector<ir::Identifier *> spreadArrayIds = {};
328 
329     CreateSpreadArrayDeclareStatements(ctx, array, spreadArrayIds, statements);
330 
331     ir::Identifier *newArrayLengthId = CreateNewArrayLengthStatement(ctx, array, spreadArrayIds, statements);
332 
333     ir::Identifier *newArrayId = CreateNewArrayDeclareStatement(ctx, array, statements, newArrayLengthId);
334     ES2PANDA_ASSERT(newArrayId != nullptr);
335     ir::Identifier *newArrayIndexId = Gensym(allocator);
336     statements.emplace_back(
337         parser->CreateFormattedStatement("let @@I1 = 0", newArrayIndexId->Clone(allocator, nullptr)));
338     std::vector<ir::AstNode *> newArrayAndIndex {newArrayId->Clone(allocator, nullptr),
339                                                  newArrayIndexId->Clone(allocator, nullptr)};
340 
341     CreateNewArrayElementsAssignStatement(ctx, array, spreadArrayIds, statements, newArrayAndIndex);
342     return ctx->AllocNode<ir::BlockExpression>(std::move(statements));
343 }
344 
345 /*
346  * NOTE: Expand the SpreadExpr to BlockExpr, the rules as follows :
347  * let newTuple: typeOfNewTuple = new std.core.TupleN(normalExpr1, ..., normalExprN);
348  */
CreateLoweredExpressionForTuple(public_lib::Context * ctx,ir::ArrayExpression * array)349 static ir::BlockExpression *CreateLoweredExpressionForTuple(public_lib::Context *ctx, ir::ArrayExpression *array)
350 {
351     auto *const checker = ctx->checker->AsETSChecker();
352     auto *const parser = ctx->parser->AsETSParser();
353     auto *const allocator = ctx->allocator;
354 
355     ArenaVector<ir::Statement *> statements(allocator->Adapter());
356     ir::Identifier *newTupleId = CreateNewTupleDeclareStatement(ctx, array, statements);
357     ES2PANDA_ASSERT(newTupleId != nullptr);
358     statements.emplace_back(parser->CreateFormattedStatement("@@I1;", newTupleId->Clone(allocator, nullptr)));
359     return checker->AllocNode<ir::BlockExpression>(std::move(statements));
360 }
361 
PerformForModule(public_lib::Context * ctx,parser::Program * program)362 bool SpreadConstructionPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
363 {
364     checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
365     varbinder::ETSBinder *const varbinder = checker->VarBinder()->AsETSBinder();
366 
367     program->Ast()->TransformChildrenRecursively(
368         [&checker, &varbinder, &ctx](ir::AstNode *const node) -> AstNodePtr {
369             if (node->IsArrayExpression() &&
370                 std::any_of(node->AsArrayExpression()->Elements().begin(), node->AsArrayExpression()->Elements().end(),
371                             [](const auto *param) { return param->Type() == ir::AstNodeType::SPREAD_ELEMENT; })) {
372                 auto scopeCtx =
373                     varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), NearestScope(node));
374 
375                 const auto *const arrayExprType = node->AsArrayExpression()->TsType();
376                 ir::BlockExpression *blockExpression =
377                     arrayExprType->IsETSArrayType() || arrayExprType->IsETSResizableArrayType()
378                         ? CreateLoweredExpressionForArray(ctx, node->AsArrayExpression())
379                         : CreateLoweredExpressionForTuple(ctx, node->AsArrayExpression());
380                 blockExpression->SetParent(node->Parent());
381 
382                 // NOTE: this blockExpression is a kind of formatted-dummy code, which is invisible to users,
383                 //       so, its source range should be same as the original code([element1, element2, ...spreadExpr])
384                 blockExpression->SetRange(node->Range());
385                 for (auto st : blockExpression->Statements()) {
386                     SetSourceRangesRecursively(st, node->Range());
387                 }
388                 Recheck(ctx->phaseManager, varbinder, checker, blockExpression);
389 
390                 return blockExpression;
391             }
392 
393             return node;
394         },
395         Name());
396     return true;
397 }
398 
399 }  // namespace ark::es2panda::compiler
400