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