• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "compiler/lowering/scopesInit/scopesInitPhase.h"
19 #include "compiler/lowering/util.h"
20 #include "ir/expressions/literals/numberLiteral.h"
21 
22 namespace ark::es2panda::compiler {
23 
24 using AstNodePtr = ir::AstNode *;
25 
SetSourceRangesRecursively(ir::AstNode * node)26 static ir::AstNode *SetSourceRangesRecursively(ir::AstNode *node)
27 {
28     auto const refine = [](ir::AstNode *n) { n->SetRange(n->Parent()->Range()); };
29 
30     refine(node);
31     node->IterateRecursively(refine);
32     return node;
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 initExpr = element->AsSpreadElement()->Argument()->Clone(allocator, nullptr);
47         spreadArrayIds.emplace_back(arrIdent);
48         statements.emplace_back(parser->CreateFormattedStatement("let @@I1 = (@@E2);", arrIdent, initExpr));
49     }
50 }
51 
CreateNewArrayLengthStatement(public_lib::Context * ctx,ir::ArrayExpression * array,std::vector<ir::Identifier * > & spreadArrayIds,ArenaVector<ir::Statement * > & statements,ir::Identifier * newArrayLengthId)52 void CreateNewArrayLengthStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
53                                    std::vector<ir::Identifier *> &spreadArrayIds,
54                                    ArenaVector<ir::Statement *> &statements, ir::Identifier *newArrayLengthId)
55 {
56     auto *const allocator = ctx->allocator;
57     auto *const parser = ctx->parser->AsETSParser();
58     std::vector<ir::AstNode *> nodesWaitingInsert {newArrayLengthId};
59     int argumentCount = 1;
60     std::stringstream lengthString;
61     int normalElementCount = array->Elements().size() - spreadArrayIds.size();
62     lengthString.clear();
63     lengthString << "let @@I" << (argumentCount++) << " : int = " << normalElementCount << " + ";
64     for (auto *spaId : spreadArrayIds) {
65         lengthString << "@@I" << (argumentCount++) << ".length + ";
66         nodesWaitingInsert.emplace_back(spaId->Clone(allocator, nullptr));
67     }
68     lengthString << "0;";
69     statements.emplace_back(parser->CreateFormattedStatement(lengthString.str(), nodesWaitingInsert));
70 }
71 
ClearIdentifierAnnotation(ir::Identifier * node)72 ir::Identifier *ClearIdentifierAnnotation(ir::Identifier *node)
73 {
74     if (!node->IsAnnotatedExpression() || (node->AsAnnotatedExpression()->TypeAnnotation() == nullptr)) {
75         return node;
76     }
77 
78     node->AsAnnotatedExpression()->SetTsTypeAnnotation(nullptr);
79     return node;
80 }
81 
CreateNewArrayDeclareStatement(public_lib::Context * ctx,ir::ArrayExpression * array,ArenaVector<ir::Statement * > & statements,ir::Identifier * newArrayLengthId)82 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     auto arrayType = array->TsType()->AsETSArrayType();
91     auto initElemType = arrayType->ElementType();
92     checker::ETSArrayType *newArrayType = arrayType;
93     if (checker->IsReferenceType(initElemType)) {
94         newArrayType = checker->Allocator()->New<checker::ETSArrayType>(
95             checker->CreateETSUnionType({initElemType, checker->GlobalETSNullType()}));
96     }
97     std::stringstream newArrayDeclareStr;
98     // NOTE: For ETSUnionType(String|Int) or ETSObjectType(private constructor) or ..., we canot use "new Type[]" to
99     //       declare an array, so we add "|null" to solve it temporarily.
100     //       We might need to use cast Expression in the end of the generated source code to remove "|null", such as
101     //       "newArrayName as arrayType[]".
102     //       But now cast Expression doesn't support built-in array (cast fatherType[] to sonType[]), so "newArrayName
103     //       as arrayType" should be added after cast Expression is implemented completely.
104     newArrayDeclareStr.clear();
105     newArrayDeclareStr << "let @@I1: @@T2 = new @@T3 [@@I4];";
106 
107     ir::Statement *newArrayDeclareSt = parser->CreateFormattedStatement(
108         newArrayDeclareStr.str(), newArrayId, newArrayType, newArrayType->ElementType(),
109         ClearIdentifierAnnotation(newArrayLengthId->Clone(allocator, nullptr)));
110     statements.emplace_back(newArrayDeclareSt);
111     return newArrayId;
112 }
113 
CreateSpreadArrIteratorStatement(public_lib::Context * ctx,ir::ArrayExpression * array,ir::Identifier * spreadArrIterator)114 ir::Statement *CreateSpreadArrIteratorStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
115                                                 ir::Identifier *spreadArrIterator)
116 {
117     auto *const parser = ctx->parser->AsETSParser();
118     auto elemType = array->TsType()->AsETSArrayType()->ElementType();
119 
120     std::stringstream spArrIterDeclareStr;
121     spArrIterDeclareStr.clear();
122     spArrIterDeclareStr << "let @@I1: @@T2;";
123     ir::Statement *spArrIterDeclareSt =
124         parser->CreateFormattedStatement(spArrIterDeclareStr.str(), spreadArrIterator, elemType);
125 
126     return spArrIterDeclareSt;
127 }
128 
CreateElementsAssignStatementBySpreadArr(public_lib::Context * ctx,ir::Identifier * spId,std::vector<ir::AstNode * > & newArrayAndIndex,ir::Identifier * spreadArrIterator)129 ir::Statement *CreateElementsAssignStatementBySpreadArr(public_lib::Context *ctx, ir::Identifier *spId,
130                                                         std::vector<ir::AstNode *> &newArrayAndIndex,
131                                                         ir::Identifier *spreadArrIterator)
132 {
133     auto *const allocator = ctx->allocator;
134     auto *const parser = ctx->parser->AsETSParser();
135     auto *const newArrayId = newArrayAndIndex[0];
136     auto *const newArrayIndexId = newArrayAndIndex[1];
137 
138     std::stringstream elementsAssignStr;
139     elementsAssignStr.clear();
140     elementsAssignStr << "for (@@I2 of @@I3) {";
141     elementsAssignStr << "@@I4[@@I5] = @@I6;";
142     elementsAssignStr << "@@I7++;";
143     elementsAssignStr << "}";
144 
145     ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement(
146         elementsAssignStr.str(), ClearIdentifierAnnotation(spreadArrIterator->Clone(allocator, nullptr)),
147         ClearIdentifierAnnotation(spreadArrIterator->Clone(allocator, nullptr)), spId->Clone(allocator, nullptr),
148         newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr),
149         ClearIdentifierAnnotation(spreadArrIterator->Clone(allocator, nullptr)),
150         newArrayIndexId->Clone(allocator, nullptr));
151 
152     return elementsAssignStatement;
153 }
154 
CreateElementsAssignStatementBySingle(public_lib::Context * ctx,ir::AstNode * element,std::vector<ir::AstNode * > & newArrayAndIndex)155 ir::Statement *CreateElementsAssignStatementBySingle(public_lib::Context *ctx, ir::AstNode *element,
156                                                      std::vector<ir::AstNode *> &newArrayAndIndex)
157 {
158     auto *const allocator = ctx->allocator;
159     auto *const parser = ctx->parser->AsETSParser();
160     auto *const newArrayId = newArrayAndIndex[0];
161     auto *const newArrayIndexId = newArrayAndIndex[1];
162     std::stringstream elementsAssignStr;
163     elementsAssignStr.clear();
164     elementsAssignStr << "@@I1[@@I2] = (@@E3);";
165     elementsAssignStr << "@@I4++;";
166 
167     ir::Statement *elementsAssignStatement = parser->CreateFormattedStatement(
168         elementsAssignStr.str(), newArrayId->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr),
169         element->Clone(allocator, nullptr), newArrayIndexId->Clone(allocator, nullptr));
170 
171     return elementsAssignStatement;
172 }
173 
CreateNewArrayElementsAssignStatement(public_lib::Context * ctx,ir::ArrayExpression * array,std::vector<ir::Identifier * > & spArrIds,ArenaVector<ir::Statement * > & statements,std::vector<ir::AstNode * > & newArrayAndIndex)174 void CreateNewArrayElementsAssignStatement(public_lib::Context *ctx, ir::ArrayExpression *array,
175                                            std::vector<ir::Identifier *> &spArrIds,
176                                            ArenaVector<ir::Statement *> &statements,
177                                            std::vector<ir::AstNode *> &newArrayAndIndex)
178 {
179     auto *const allocator = ctx->allocator;
180     auto *const parser = ctx->parser->AsETSParser();
181     auto *const newArrayId = newArrayAndIndex[0];
182     int spArrIdx = 0;
183 
184     for (auto *element : array->Elements()) {
185         if (element->Type() == ir::AstNodeType::SPREAD_ELEMENT) {
186             ir::Identifier *spreadArrIterator = Gensym(allocator);
187             statements.emplace_back(CreateSpreadArrIteratorStatement(ctx, array, spreadArrIterator));
188             statements.emplace_back(CreateElementsAssignStatementBySpreadArr(ctx, spArrIds[spArrIdx++],
189                                                                              newArrayAndIndex, spreadArrIterator));
190         } else {
191             statements.emplace_back(CreateElementsAssignStatementBySingle(ctx, element, newArrayAndIndex));
192         }
193     }
194 
195     statements.emplace_back(parser->CreateFormattedStatement("@@I1;", newArrayId->Clone(allocator, nullptr)));
196 }
197 
198 /*
199  * NOTE: Expand the SpreadExpr to BlockExpr, the rules as follows :
200  * let spreadArrayId1 = spreadExpr1
201  * let length : int = normalExprCount + spreadArrayId1.length + 0
202  * type typeOfNewArray = arrayType
203  * let newArray: typeOfNewArray[] = new typeOfNewArray[length]
204  * let newArrayIndex = 0
205  * let elementOfSpreadArray1: arrayType
206  * for (elementOfSpreadArray1 of spreadArray1) {
207  *     newArray[newArrayIndex] = elementOfSpreadArray1
208  *     newArrayIndex++
209  * }
210  * newArray[newArrayIndex] = normalExpr1
211  * newArrayIndex++
212  * ...
213  * newArray;
214  */
CreateLoweredExpression(public_lib::Context * ctx,ir::ArrayExpression * array)215 ir::BlockExpression *CreateLoweredExpression(public_lib::Context *ctx, ir::ArrayExpression *array)
216 {
217     auto *const checker = ctx->checker->AsETSChecker();
218     auto *const parser = ctx->parser->AsETSParser();
219     auto *const allocator = ctx->allocator;
220     ArenaVector<ir::Statement *> statements(allocator->Adapter());
221     std::vector<ir::Identifier *> spreadArrayIds = {};
222     ir::Identifier *newArrayLengthId = Gensym(allocator);
223 
224     CreateSpreadArrayDeclareStatements(ctx, array, spreadArrayIds, statements);
225     CreateNewArrayLengthStatement(ctx, array, spreadArrayIds, statements, newArrayLengthId);
226 
227     ir::Identifier *newArrayId = CreateNewArrayDeclareStatement(ctx, array, statements, newArrayLengthId);
228     ir::Identifier *newArrayIndexId = Gensym(allocator);
229     statements.emplace_back(parser->CreateFormattedStatement("let @@I1 = 0;", newArrayIndexId));
230     std::vector<ir::AstNode *> newArrayAndIndex {ClearIdentifierAnnotation(newArrayId->Clone(allocator, nullptr)),
231                                                  newArrayIndexId->Clone(allocator, nullptr)};
232 
233     CreateNewArrayElementsAssignStatement(ctx, array, spreadArrayIds, statements, newArrayAndIndex);
234     return checker->AllocNode<ir::BlockExpression>(std::move(statements));
235 }
236 
Perform(public_lib::Context * ctx,parser::Program * program)237 bool SpreadConstructionPhase::Perform(public_lib::Context *ctx, parser::Program *program)
238 {
239     for (auto &[_, ext_programs] : program->ExternalSources()) {
240         (void)_;
241         for (auto *extProg : ext_programs) {
242             Perform(ctx, extProg);
243         }
244     }
245 
246     checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
247 
248     program->Ast()->TransformChildrenRecursively(
249         [&checker, &ctx](ir::AstNode *const node) -> AstNodePtr {
250             if (node->IsArrayExpression() &&
251                 std::any_of(node->AsArrayExpression()->Elements().begin(), node->AsArrayExpression()->Elements().end(),
252                             [](const auto *param) { return param->Type() == ir::AstNodeType::SPREAD_ELEMENT; })) {
253                 auto scopeCtx =
254                     varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), NearestScope(node));
255 
256                 ir::BlockExpression *blockExpression = CreateLoweredExpression(ctx, node->AsArrayExpression());
257                 blockExpression->SetParent(node->Parent());
258 
259                 // NOTE: this blockExpression is a kind of formatted-dummy code, which is invisible to users,
260                 //       so, its source range should be same as the original code([element1, element2, ...spreadExpr])
261                 blockExpression->SetRange(node->Range());
262                 for (auto st : blockExpression->Statements()) {
263                     SetSourceRangesRecursively(st);
264                 }
265 
266                 Recheck(checker->VarBinder()->AsETSBinder(), checker, blockExpression);
267 
268                 return blockExpression;
269             }
270 
271             return node;
272         },
273         Name());
274     return true;
275 }
276 
Postcondition(public_lib::Context * ctx,const parser::Program * program)277 bool SpreadConstructionPhase::Postcondition(public_lib::Context *ctx, const parser::Program *program)
278 {
279     for (auto &[_, ext_programs] : program->ExternalSources()) {
280         (void)_;
281         for (auto *extProg : ext_programs) {
282             if (!Postcondition(ctx, extProg)) {
283                 return false;
284             }
285         }
286     }
287     return true;
288 }
289 
290 }  // namespace ark::es2panda::compiler
291