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 "restTupleLowering.h"
17 #include "checker/ETSchecker.h"
18 #include "ir/base/scriptFunction.h"
19 #include "compiler/lowering/util.h"
20 #include "checker/types/type.h"
21 #include "varbinder/ETSBinder.h"
22 #include "varbinder/variable.h"
23 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
24
25 namespace ark::es2panda::compiler {
26
27 using AstNodePtr = ir::AstNode *;
28
MethodDefinitionHasRestTuple(const ir::AstNode * def)29 bool MethodDefinitionHasRestTuple(const ir::AstNode *def)
30 {
31 auto pred = [](const auto *param) {
32 return param->IsETSParameterExpression() && param->AsETSParameterExpression()->IsRestParameter() &&
33 param->AsETSParameterExpression()->TypeAnnotation() &&
34 param->AsETSParameterExpression()->TypeAnnotation()->IsETSTuple();
35 };
36
37 bool isScriptFunction = def->IsMethodDefinition() && (def->AsMethodDefinition()->Value() != nullptr) &&
38 def->AsMethodDefinition()->Value()->AsFunctionExpression()->Function()->IsScriptFunction();
39 if (isScriptFunction) {
40 auto params =
41 def->AsMethodDefinition()->Value()->AsFunctionExpression()->Function()->AsScriptFunction()->Params();
42 return std::any_of(params.begin(), params.end(), pred);
43 }
44 return false;
45 }
46
IsClassDefinitionWithTupleRest(ir::AstNode * node)47 bool IsClassDefinitionWithTupleRest(ir::AstNode *node)
48 {
49 bool isClassDefinition = node->IsClassDefinition();
50 if (isClassDefinition) {
51 auto definitions = node->AsClassDefinition()->Body();
52 return std::any_of(definitions.begin(), definitions.end(),
53 [](const auto *def) { return MethodDefinitionHasRestTuple(def); });
54 }
55
56 return false;
57 }
58
CreateMemberOrThisExpression(public_lib::Context * ctx,ir::Expression * funcExpr,ir::AstNode * definition)59 ir::Expression *CreateMemberOrThisExpression(public_lib::Context *ctx, ir::Expression *funcExpr,
60 ir::AstNode *definition)
61 {
62 auto *allocator = ctx->allocator;
63
64 if (definition->IsConstructor()) {
65 return ctx->AllocNode<ir::ThisExpression>();
66 }
67
68 auto scriptFunc = funcExpr->AsFunctionExpression()->Function()->AsScriptFunction();
69
70 ir::Expression *ident = nullptr;
71
72 if (definition->AsMethodDefinition()->IsStatic()) {
73 auto *parentClass = util::Helpers::FindAncestorGivenByType(definition, ir::AstNodeType::CLASS_DEFINITION);
74 ES2PANDA_ASSERT(parentClass != nullptr);
75 ident = parentClass->AsClassDefinition()->Ident()->AsIdentifier()->Clone(allocator, parentClass->Parent());
76 } else {
77 ident = ctx->AllocNode<ir::ThisExpression>();
78 }
79
80 auto *newPropertyId = scriptFunc->Id()->Clone(allocator, nullptr);
81 auto *memberExpr = ctx->AllocNode<ir::MemberExpression>(ident, newPropertyId,
82 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
83
84 return memberExpr;
85 }
86
CreateTypeParameterInstantiation(public_lib::Context * ctx,ir::TSTypeParameterDeclaration * paramDeclaration)87 ir::TSTypeParameterInstantiation *CreateTypeParameterInstantiation(public_lib::Context *ctx,
88 ir::TSTypeParameterDeclaration *paramDeclaration)
89 {
90 auto const allocator = ctx->allocator;
91
92 if (paramDeclaration == nullptr || paramDeclaration->Params().empty()) {
93 return nullptr;
94 }
95 ArenaVector<ir::TypeNode *> selfParams(allocator->Adapter());
96 ir::ETSTypeReferencePart *referencePart = nullptr;
97
98 for (const auto ¶m : paramDeclaration->Params()) {
99 auto *identRef = util::NodeAllocator::ForceSetParent<ir::Identifier>(
100 allocator, param->AsTSTypeParameter()->Name()->Name(), allocator);
101
102 referencePart = util::NodeAllocator::ForceSetParent<ir::ETSTypeReferencePart>(allocator, identRef, nullptr,
103 nullptr, allocator);
104
105 auto *typeReference =
106 util::NodeAllocator::ForceSetParent<ir::ETSTypeReference>(allocator, referencePart, allocator);
107
108 selfParams.push_back(typeReference);
109 }
110
111 return util::NodeAllocator::ForceSetParent<ir::TSTypeParameterInstantiation>(allocator, std::move(selfParams));
112 }
113
CreateNewCallExpression(public_lib::Context * ctx,ir::Expression * funcExpr,ir::AstNode * definition,ir::TSAsExpression * asExpression)114 ir::CallExpression *CreateNewCallExpression(public_lib::Context *ctx, ir::Expression *funcExpr, ir::AstNode *definition,
115 ir::TSAsExpression *asExpression)
116 {
117 auto *allocator = ctx->allocator;
118
119 ArenaVector<ir::Expression *> callArguments({}, allocator->Adapter());
120 for (auto arg : funcExpr->AsFunctionExpression()->Function()->AsScriptFunction()->Params()) {
121 if (!arg->AsETSParameterExpression()->IsRestParameter()) {
122 auto *id = arg->AsETSParameterExpression()->Ident()->Clone(allocator, nullptr);
123 id->SetTsTypeAnnotation(nullptr);
124 callArguments.push_back(id);
125 } else {
126 auto spreadElement =
127 ctx->AllocNode<ir::SpreadElement>(ir::AstNodeType::SPREAD_ELEMENT, allocator, asExpression);
128 callArguments.push_back(spreadElement);
129 }
130 }
131
132 // Create member expression if method is not a constructor
133 ir::Expression *memberExpr = CreateMemberOrThisExpression(ctx, funcExpr, definition);
134
135 ir::TSTypeParameterInstantiation *typeParamInst = nullptr;
136 if (funcExpr->AsFunctionExpression()->Function()->AsScriptFunction()->TypeParams() != nullptr) {
137 auto typeParams = funcExpr->AsFunctionExpression()
138 ->Function()
139 ->AsScriptFunction()
140 ->TypeParams()
141 ->AsTSTypeParameterDeclaration();
142 typeParamInst = CreateTypeParameterInstantiation(ctx, typeParams);
143 }
144
145 auto *newCallExpr = ctx->AllocNode<ir::CallExpression>(memberExpr, std::move(callArguments), typeParamInst, false);
146
147 for (auto *arg : newCallExpr->Arguments()) {
148 arg->SetParent(newCallExpr);
149 }
150
151 return newCallExpr;
152 }
153
CreateFunctionRestParams(public_lib::Context * ctx,ir::AstNode * funcExpr)154 ArenaVector<ir::Expression *> CreateFunctionRestParams(public_lib::Context *ctx, ir::AstNode *funcExpr)
155 {
156 auto *allocator = ctx->allocator;
157
158 ArenaVector<ir::Expression *> params {allocator->Adapter()};
159 for (auto param : funcExpr->AsFunctionExpression()->Function()->AsScriptFunction()->Params()) {
160 if (param->AsETSParameterExpression()->IsRestParameter()) {
161 for (auto tupleTypeAnno :
162 param->AsETSParameterExpression()->TypeAnnotation()->AsETSTuple()->GetTupleTypeAnnotationsList()) {
163 ir::Identifier *id = Gensym(allocator);
164 auto *newParam = ctx->AllocNode<ir::ETSParameterExpression>(id, false, allocator);
165 auto newAnnotation = tupleTypeAnno->Clone(allocator, id);
166 id->SetTsTypeAnnotation(newAnnotation);
167 params.push_back(newParam);
168 }
169 }
170 }
171 return params;
172 }
173
CreateFunctionNormalParams(public_lib::Context * ctx,ir::AstNode * funcExpr)174 ArenaVector<ir::Expression *> CreateFunctionNormalParams(public_lib::Context *ctx, ir::AstNode *funcExpr)
175 {
176 auto *allocator = ctx->allocator;
177
178 ArenaVector<ir::Expression *> params {allocator->Adapter()};
179 for (auto param : funcExpr->AsFunctionExpression()->Function()->AsScriptFunction()->Params()) {
180 if (!param->AsETSParameterExpression()->IsRestParameter()) {
181 auto newParam = param->AsETSParameterExpression()->Clone(allocator, nullptr);
182 params.push_back(newParam);
183 }
184 }
185 return params;
186 }
187
MergeParams(public_lib::Context * ctx,const ArenaVector<ir::Expression * > & newNormalParams,const ArenaVector<ir::Expression * > & newRestParams)188 ArenaVector<ir::Expression *> MergeParams(public_lib::Context *ctx,
189 const ArenaVector<ir::Expression *> &newNormalParams,
190 const ArenaVector<ir::Expression *> &newRestParams)
191 {
192 auto *allocator = ctx->allocator;
193
194 ArenaVector<ir::Expression *> params {allocator->Adapter()};
195 for (auto newNormalParam : newNormalParams) {
196 params.push_back(newNormalParam);
197 }
198 for (auto newRestParam : newRestParams) {
199 params.push_back(newRestParam);
200 }
201 return params;
202 }
203
CreateArrayExpression(public_lib::Context * ctx,const ArenaVector<ir::Expression * > & newRestParams)204 ir::ArrayExpression *CreateArrayExpression(public_lib::Context *ctx, const ArenaVector<ir::Expression *> &newRestParams)
205 {
206 auto *allocator = ctx->allocator;
207
208 ArenaVector<ir::Expression *> elementsInit(ctx->Allocator()->Adapter());
209 ArenaVector<ir::Expression *> elements(ctx->Allocator()->Adapter());
210
211 auto *arrayExpr = ctx->AllocNode<ir::ArrayExpression>(std::move(elementsInit), ctx->Allocator());
212 ES2PANDA_ASSERT(arrayExpr != nullptr);
213 for (auto tupleElementAnno : newRestParams) {
214 auto &tupleElementName = tupleElementAnno->AsETSParameterExpression()->Ident()->AsIdentifier()->Name();
215 ir::Expression *arg = ctx->AllocNode<ir::Identifier>(tupleElementName, allocator);
216 arg->SetParent(arrayExpr);
217 elements.push_back(arg);
218 }
219 arrayExpr->SetElements(std::move(elements));
220 return arrayExpr;
221 }
222
CreateNewParameterDeclaration(public_lib::Context * ctx,ir::TSTypeParameterDeclaration * paramDeclaration)223 ir::TSTypeParameterDeclaration *CreateNewParameterDeclaration(public_lib::Context *ctx,
224 ir::TSTypeParameterDeclaration *paramDeclaration)
225 {
226 auto const allocator = ctx->allocator;
227 if (paramDeclaration == nullptr || paramDeclaration->Params().empty()) {
228 return nullptr;
229 }
230
231 ArenaVector<ir::TSTypeParameter *> typeParams(allocator->Adapter());
232
233 auto parentParams = paramDeclaration->Params();
234 std::for_each(parentParams.begin(), parentParams.end(), [&typeParams, allocator](ir::TSTypeParameter *par) {
235 ir::Identifier *ident = par->Name()->Clone(allocator, nullptr)->AsIdentifier();
236 auto *constraint =
237 par->Constraint() != nullptr ? par->Constraint()->Clone(allocator, nullptr)->AsTypeNode() : nullptr;
238 auto *defaultType =
239 par->DefaultType() != nullptr ? par->DefaultType()->Clone(allocator, nullptr)->AsTypeNode() : nullptr;
240 auto *typeParam = util::NodeAllocator::ForceSetParent<ir::TSTypeParameter>(allocator, ident, constraint,
241 defaultType, allocator);
242 typeParams.push_back(typeParam);
243 });
244 size_t paramsSize = typeParams.size();
245 return util::NodeAllocator::ForceSetParent<ir::TSTypeParameterDeclaration>(allocator, std::move(typeParams),
246 paramsSize);
247 }
248
CreateNewScriptFunction(public_lib::Context * ctx,ir::ScriptFunction * scriptFunc,ArenaVector<ir::Expression * > newParams)249 ir::ScriptFunction *CreateNewScriptFunction(public_lib::Context *ctx, ir::ScriptFunction *scriptFunc,
250 ArenaVector<ir::Expression *> newParams)
251 {
252 auto *allocator = ctx->allocator;
253
254 ArenaVector<ir::Statement *> statements(allocator->Adapter());
255 auto *body = ctx->AllocNode<ir::BlockStatement>(allocator, std::move(statements));
256 ir::TypeNode *newReturnTypeAnno = nullptr;
257 if (scriptFunc->ReturnTypeAnnotation() != nullptr) {
258 newReturnTypeAnno = scriptFunc->ReturnTypeAnnotation()->Clone(allocator, nullptr);
259 }
260 auto *newParamDeclaration = CreateNewParameterDeclaration(ctx, scriptFunc->TypeParams());
261
262 auto *newScriptFunc = ctx->AllocNode<ir::ScriptFunction>(
263 allocator, ir::ScriptFunction::ScriptFunctionData {
264 body, ir::FunctionSignature(newParamDeclaration, std::move(newParams), newReturnTypeAnno),
265 scriptFunc->Flags()});
266 ES2PANDA_ASSERT(newScriptFunc != nullptr);
267 newScriptFunc->AddModifier(scriptFunc->AsScriptFunction()->Modifiers());
268
269 ArenaVector<ir::AnnotationUsage *> annotationUsages {allocator->Adapter()};
270 for (auto *annotationUsage : scriptFunc->Annotations()) {
271 annotationUsages.push_back(annotationUsage->Clone(allocator, newScriptFunc)->AsAnnotationUsage());
272 }
273
274 newScriptFunc->SetAnnotations(std::move(annotationUsages));
275
276 ir::Identifier *newScriptFuncId = scriptFunc->Id()->Clone(allocator, newScriptFunc);
277 newScriptFunc->SetIdent(newScriptFuncId);
278
279 return newScriptFunc;
280 }
281
CreateNewVariableDeclaration(public_lib::Context * ctx,ir::ETSParameterExpression * restParam,ir::ArrayExpression * newTuple)282 ir::VariableDeclaration *CreateNewVariableDeclaration(public_lib::Context *ctx, ir::ETSParameterExpression *restParam,
283 ir::ArrayExpression *newTuple)
284 {
285 auto *allocator = ctx->allocator;
286
287 util::StringView tupleIdentName = restParam->Ident()->Name();
288 auto *newId = ctx->AllocNode<ir::Identifier>(tupleIdentName, allocator);
289 ES2PANDA_ASSERT(newId != nullptr);
290 ir::TypeNode *typeAnnotation = restParam->TypeAnnotation()->Clone(allocator, newId);
291 newId->SetTsTypeAnnotation(typeAnnotation);
292 newTuple->SetParent(typeAnnotation);
293
294 auto *const declarator = ctx->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, newId, newTuple);
295 ArenaVector<ir::VariableDeclarator *> declarators(ctx->Allocator()->Adapter());
296 declarators.push_back(declarator);
297
298 auto *const declaration = ctx->AllocNode<ir::VariableDeclaration>(
299 ir::VariableDeclaration::VariableDeclarationKind::LET, ctx->Allocator(), std::move(declarators));
300 return declaration;
301 }
302
CreateReturnOrExpressionStatement(public_lib::Context * ctx,ir::ScriptFunction * scriptFunc,ir::CallExpression * callExpr)303 ArenaVector<ir::Statement *> CreateReturnOrExpressionStatement(public_lib::Context *ctx, ir::ScriptFunction *scriptFunc,
304 ir::CallExpression *callExpr)
305 {
306 ArenaVector<ir::Statement *> statements(ctx->Allocator()->Adapter());
307
308 if (scriptFunc->ReturnTypeAnnotation() != nullptr) {
309 auto returnStatement = ctx->AllocNode<ir::ReturnStatement>(callExpr);
310 statements.insert(statements.end(), returnStatement);
311 } else {
312 auto expressionStatement = ctx->AllocNode<ir::ExpressionStatement>(callExpr);
313 statements.insert(statements.end(), expressionStatement);
314 callExpr->SetParent(expressionStatement);
315 }
316 return statements;
317 }
318
CreateNewMethodDefinition(public_lib::Context * ctx,ir::MethodDefinition * definition,ir::FunctionExpression * function)319 ir::MethodDefinition *CreateNewMethodDefinition(public_lib::Context *ctx, ir::MethodDefinition *definition,
320 ir::FunctionExpression *function)
321 {
322 auto *allocator = ctx->allocator;
323
324 auto *methodKey = definition->AsMethodDefinition()->Key()->AsIdentifier()->Clone(allocator, nullptr);
325 auto *const methodDef =
326 ctx->AllocNode<ir::MethodDefinition>(definition->AsMethodDefinition()->Kind(), methodKey, function,
327 definition->AsMethodDefinition()->Modifiers(), allocator, false);
328 ES2PANDA_ASSERT(methodDef != nullptr);
329 methodDef->SetParent(definition->Parent());
330
331 return methodDef;
332 }
333
CreateNewMethod(public_lib::Context * ctx,ir::AstNode * node)334 void CreateNewMethod(public_lib::Context *ctx, ir::AstNode *node)
335 {
336 auto *allocator = ctx->allocator;
337
338 for (auto definition : node->AsClassDefinition()->Body()) {
339 if (definition->IsMethodDefinition() && MethodDefinitionHasRestTuple(definition->AsMethodDefinition())) {
340 auto funcExpr = definition->AsMethodDefinition()->Value();
341 ir::ETSParameterExpression *restParam = funcExpr->AsFunctionExpression()
342 ->Function()
343 ->AsScriptFunction()
344 ->Params()
345 .back()
346 ->AsETSParameterExpression();
347 ir::ScriptFunction *scriptFunc = funcExpr->AsFunctionExpression()->Function()->AsScriptFunction();
348
349 ArenaVector<ir::Expression *> newNormalParams = CreateFunctionNormalParams(ctx, funcExpr);
350 ArenaVector<ir::Expression *> newRestParams = CreateFunctionRestParams(ctx, funcExpr);
351 ArenaVector<ir::Expression *> mergedParams = MergeParams(ctx, newNormalParams, newRestParams);
352
353 ir::ScriptFunction *newScriptFunc = CreateNewScriptFunction(ctx, scriptFunc, mergedParams);
354
355 ir::ArrayExpression *const newArrayExpr = CreateArrayExpression(ctx, newRestParams);
356
357 ir::TypeNode *newTypeAnnotation = restParam->TypeAnnotation()->Clone(allocator, nullptr);
358
359 auto *asExpression = ctx->AllocNode<ir::TSAsExpression>(newArrayExpr, newTypeAnnotation, false);
360
361 auto *callExpr = CreateNewCallExpression(ctx, funcExpr, definition, asExpression);
362
363 ArenaVector<ir::Statement *> statements = CreateReturnOrExpressionStatement(ctx, scriptFunc, callExpr);
364
365 // Build new script function
366 newScriptFunc->AsScriptFunction()->Body()->AsBlockStatement()->SetStatements(std::move(statements));
367
368 // Build new functionExpression
369 auto *function = ctx->AllocNode<ir::FunctionExpression>(newScriptFunc);
370 function->SetParent(funcExpr->Parent());
371
372 // Build new methodDefinition
373 auto *const methodDef = CreateNewMethodDefinition(ctx, definition->AsMethodDefinition(), function);
374
375 node->AsClassDefinition()->Body().push_back(methodDef);
376 }
377 }
378 }
379
PerformForModule(public_lib::Context * ctx,parser::Program * program)380 bool RestTupleConstructionPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
381 {
382 program->Ast()->TransformChildrenRecursively(
383 [ctx](ir::AstNode *const node) -> AstNodePtr {
384 if (IsClassDefinitionWithTupleRest(node)) {
385 CreateNewMethod(ctx, node);
386 }
387 return node;
388 },
389 Name());
390 return true;
391 }
392
393 } // namespace ark::es2panda::compiler
394