• 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 "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 &param : 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