• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-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 "defaultParametersInConstructorLowering.h"
17 #include "ir/expression.h"
18 #include "ir/expressions/literals/undefinedLiteral.h"
19 #include "ir/ets/etsUnionType.h"
20 
21 namespace ark::es2panda::compiler {
22 
23 // #23080 code was moved to lowering for no reason
HasDefaultParameters(const ir::ScriptFunction * function,util::DiagnosticEngine & diagnosticEngine)24 static bool HasDefaultParameters(const ir::ScriptFunction *function, util::DiagnosticEngine &diagnosticEngine)
25 {
26     bool hasDefaultParameter = false;
27     bool hasRestParameter = false;
28 
29     for (auto *const it : function->Params()) {
30         if (it->IsBrokenExpression()) {
31             continue;
32         }
33         auto const *const param = it->AsETSParameterExpression();
34 
35         if (param->IsRestParameter()) {
36             hasRestParameter = true;
37             continue;
38         }
39 
40         if (hasRestParameter) {
41             util::DiagnosticMessageParams diagnosticParams = {};
42             diagnosticEngine.LogDiagnostic(diagnostic::REST_PARAM_LAST, std::move(diagnosticParams), param->Start());
43         }
44 
45         if (param->IsOptional()) {
46             hasDefaultParameter = true;
47             continue;
48         }
49 
50         if (hasDefaultParameter) {
51             util::DiagnosticMessageParams diagnosticParams = {};
52             diagnosticEngine.LogDiagnostic(diagnostic::REQUIRED_PARAM_AFTER_DEFAULT, std::move(diagnosticParams),
53                                            param->Start());
54         }
55     }
56 
57     if (hasDefaultParameter && hasRestParameter) {
58         util::DiagnosticMessageParams diagnosticParams = {};
59         diagnosticEngine.LogDiagnostic(diagnostic::REST_AND_DEFAULT_SAME_TIME, std::move(diagnosticParams),
60                                        function->Start());
61     }
62 
63     return hasDefaultParameter;
64 }
65 
CreateParameterDeclaraion(ir::MethodDefinition * method,public_lib::Context * ctx)66 static ir::TSTypeParameterDeclaration *CreateParameterDeclaraion(ir::MethodDefinition *method, public_lib::Context *ctx)
67 {
68     auto const allocator = ctx->allocator;
69     ES2PANDA_ASSERT(method->Function());
70     if (method->Function()->TypeParams() == nullptr || method->Function()->TypeParams()->Params().empty()) {
71         return nullptr;
72     }
73 
74     ArenaVector<ir::TSTypeParameter *> typeParams(allocator->Adapter());
75 
76     auto parentParams = method->Function()->TypeParams()->Params();
77     std::for_each(parentParams.begin(), parentParams.end(), [&typeParams, allocator](ir::TSTypeParameter *par) {
78         ir::Identifier *ident = par->Name()->Clone(allocator, nullptr)->AsIdentifier();
79         auto *constraint =
80             par->Constraint() != nullptr ? par->Constraint()->Clone(allocator, nullptr)->AsTypeNode() : nullptr;
81         auto *defaultType =
82             par->DefaultType() != nullptr ? par->DefaultType()->Clone(allocator, nullptr)->AsTypeNode() : nullptr;
83         auto *typeParam = util::NodeAllocator::ForceSetParent<ir::TSTypeParameter>(allocator, ident, constraint,
84                                                                                    defaultType, allocator);
85         typeParams.push_back(typeParam);
86     });
87     return util::NodeAllocator::ForceSetParent<ir::TSTypeParameterDeclaration>(allocator, std::move(typeParams),
88                                                                                typeParams.size());
89 }
90 
CreateFunctionSignature(ir::MethodDefinition * method,ArenaVector<ir::Expression * > funcParam,public_lib::Context * ctx)91 static ir::FunctionSignature CreateFunctionSignature(ir::MethodDefinition *method,
92                                                      ArenaVector<ir::Expression *> funcParam, public_lib::Context *ctx)
93 {
94     auto const allocator = ctx->allocator;
95 
96     ir::TSTypeParameterDeclaration *typeParamDecl = CreateParameterDeclaraion(method, ctx);
97     ES2PANDA_ASSERT(method->Function());
98     auto *returnTypeAnnotation =
99         method->Function()->ReturnTypeAnnotation() != nullptr
100             ? method->Function()->ReturnTypeAnnotation()->Clone(allocator, nullptr)->AsTypeNode()
101             : nullptr;
102 
103     return ir::FunctionSignature(typeParamDecl, std::move(funcParam), returnTypeAnnotation);
104 }
105 
CreateTypeParameterInstantiation(ir::MethodDefinition * method,public_lib::Context * ctx)106 static ir::TSTypeParameterInstantiation *CreateTypeParameterInstantiation(ir::MethodDefinition *method,
107                                                                           public_lib::Context *ctx)
108 {
109     auto const allocator = ctx->allocator;
110 
111     if ((method->Function() != nullptr && method->Function()->TypeParams() == nullptr) ||
112         method->Function()->TypeParams()->Params().empty()) {
113         return nullptr;
114     }
115     ArenaVector<ir::TypeNode *> selfParams(allocator->Adapter());
116     ir::ETSTypeReferencePart *referencePart = nullptr;
117 
118     for (const auto &param : method->Function()->TypeParams()->Params()) {
119         auto *identRef = util::NodeAllocator::ForceSetParent<ir::Identifier>(
120             allocator, param->AsTSTypeParameter()->Name()->Name(), allocator);
121 
122         referencePart = util::NodeAllocator::ForceSetParent<ir::ETSTypeReferencePart>(allocator, identRef, nullptr,
123                                                                                       nullptr, allocator);
124 
125         auto *typeReference =
126             util::NodeAllocator::ForceSetParent<ir::ETSTypeReference>(allocator, referencePart, allocator);
127 
128         selfParams.push_back(typeReference);
129     }
130 
131     return util::NodeAllocator::ForceSetParent<ir::TSTypeParameterInstantiation>(allocator, std::move(selfParams));
132 }
133 
CreateFunctionBody(ir::MethodDefinition * method,public_lib::Context * ctx,ArenaVector<ir::Expression * > funcCallArgs)134 static ir::BlockStatement *CreateFunctionBody(ir::MethodDefinition *method, public_lib::Context *ctx,
135                                               ArenaVector<ir::Expression *> funcCallArgs)
136 {
137     auto const allocator = ctx->allocator;
138     ArenaVector<ir::Statement *> funcStatements(allocator->Adapter());
139 
140     ES2PANDA_ASSERT(method->Id() != nullptr);
141     auto *const callee =
142         util::NodeAllocator::ForceSetParent<ir::Identifier>(allocator, method->Id()->Name(), allocator);
143 
144     ir::Expression *accessor = util::NodeAllocator::ForceSetParent<ir::ThisExpression>(allocator);
145     auto *paramInst = CreateTypeParameterInstantiation(method, ctx);
146     auto *callExpression = util::NodeAllocator::ForceSetParent<ir::CallExpression>(
147         allocator, accessor != nullptr ? accessor : callee, std::move(funcCallArgs), paramInst, false, false);
148     ES2PANDA_ASSERT(callExpression != nullptr);
149     callExpression->SetRange(method->Range());  // NOTE: Used to locate the original node when an error occurs
150     funcStatements.push_back(util::NodeAllocator::ForceSetParent<ir::ExpressionStatement>(allocator, callExpression));
151 
152     return util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(funcStatements));
153 }
154 
CreateFunctionExpression(ir::MethodDefinition * method,public_lib::Context * ctx,ArenaVector<ir::Expression * > funcDefinitionArgs,ArenaVector<ir::Expression * > funcCallArgs)155 static ir::FunctionExpression *CreateFunctionExpression(ir::MethodDefinition *method, public_lib::Context *ctx,
156                                                         ArenaVector<ir::Expression *> funcDefinitionArgs,
157                                                         ArenaVector<ir::Expression *> funcCallArgs)
158 {
159     lexer::SourcePosition startLoc(method->Start());
160     lexer::SourcePosition endLoc = startLoc;
161     ir::FunctionSignature signature = CreateFunctionSignature(method, std::move(funcDefinitionArgs), ctx);
162 
163     auto const allocator = ctx->allocator;
164     ir::Identifier *id = nullptr;
165 
166     ir::BlockStatement *body = nullptr;
167     if (!(method->IsNative() || method->IsDeclare() || method->IsAbstract())) {
168         body = CreateFunctionBody(method, ctx, std::move(funcCallArgs));
169     }
170     auto *funcNode = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>(
171         allocator, allocator,
172         ir::ScriptFunction::ScriptFunctionData {
173             body, std::move(signature), method->Function()->Flags(), {}, method->Function()->Language()});
174     ES2PANDA_ASSERT(method->Function());
175     funcNode->AddModifier(method->Function()->Modifiers());
176     funcNode->SetRange({startLoc, endLoc});
177 
178     id = method->Id()->Clone(allocator, nullptr)->AsIdentifier();
179     funcNode->SetIdent(id);
180     return util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, funcNode);
181 }
182 
CreateFunctionOverload(ir::MethodDefinition * method,ArenaVector<ir::Expression * > && funcCallArgs,ArenaVector<ir::Expression * > && funcDefinitionArgs,public_lib::Context * ctx)183 static void CreateFunctionOverload(ir::MethodDefinition *method, ArenaVector<ir::Expression *> &&funcCallArgs,
184                                    ArenaVector<ir::Expression *> &&funcDefinitionArgs, public_lib::Context *ctx)
185 {
186     auto const allocator = ctx->allocator;
187     auto *funcExpression =
188         CreateFunctionExpression(method, ctx, std::move(funcDefinitionArgs), std::move(funcCallArgs));
189     ES2PANDA_ASSERT(funcExpression != nullptr);
190     auto *ident = funcExpression->Function()->Id()->Clone(allocator, nullptr);
191     auto *const overloadMethod = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
192         allocator, method->Kind(), ident, funcExpression, method->Modifiers(), allocator, false);
193 
194     ES2PANDA_ASSERT(overloadMethod != nullptr && overloadMethod->Function() != nullptr);
195     overloadMethod->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
196     overloadMethod->SetRange(funcExpression->Range());
197 
198     if (!method->IsDeclare() && method->Parent()->IsTSInterfaceBody()) {
199         overloadMethod->Function()->Body()->AsBlockStatement()->Statements().clear();
200     }
201 
202     method->AddOverload(overloadMethod);
203     overloadMethod->SetStart(method->Start());  // NOTE: Used to locate the original node when an error occurs
204     overloadMethod->SetParent(method);          // NOTE(aleksisch): It's incorrect and don't exist in class body
205 }
206 
ExpandOptionalParameterAnnotationsToUnions(public_lib::Context * ctx,ir::ScriptFunction * function)207 static void ExpandOptionalParameterAnnotationsToUnions(public_lib::Context *ctx, ir::ScriptFunction *function)
208 {
209     ES2PANDA_ASSERT(function);
210     auto allocator = ctx->allocator;
211 
212     for (auto p : function->Params()) {
213         auto param = p->AsETSParameterExpression();
214         if (param->IsOptional() && param->Initializer() == nullptr) {
215             param->SetTypeAnnotation(util::NodeAllocator::ForceSetParent<ir::ETSUnionType>(
216                 allocator,
217                 ArenaVector<ir::TypeNode *>({param->TypeAnnotation(), allocator->New<ir::ETSUndefinedType>(allocator)},
218                                             allocator->Adapter()),
219                 allocator));
220             param->TypeAnnotation()->SetParent(param->Ident());
221         }
222     }
223 }
224 
ClearOptionalParameters(public_lib::Context * ctx,ir::ScriptFunction * function)225 static void ClearOptionalParameters(public_lib::Context *ctx, ir::ScriptFunction *function)
226 {
227     auto allocator = ctx->allocator;
228 
229     for (auto *&param : function->Params()) {
230         auto oldParam = param->AsETSParameterExpression();
231         if (oldParam->IsOptional()) {
232             param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, oldParam->Ident(), false,
233                                                                                     allocator);
234             ES2PANDA_ASSERT(param);
235             param->SetParent(function);
236         }
237         ES2PANDA_ASSERT(!param->AsETSParameterExpression()->IsOptional());
238     }
239 }
240 
ProcessGlobalFunctionDefinition(ir::MethodDefinition * method,public_lib::Context * ctx)241 static void ProcessGlobalFunctionDefinition(ir::MethodDefinition *method, public_lib::Context *ctx)
242 {
243     ES2PANDA_ASSERT(method->Function() != nullptr);
244     auto allocator = ctx->allocator;
245     ExpandOptionalParameterAnnotationsToUnions(ctx, method->Function());
246     auto const &params = method->Function()->Params();
247 
248     for (size_t paramsToCut = 1;; paramsToCut++) {
249         if (paramsToCut > params.size() ||
250             !params[params.size() - paramsToCut]->AsETSParameterExpression()->IsOptional()) {
251             break;
252         }
253 
254         ArenaVector<ir::Expression *> functionParams(allocator->Adapter());
255         ArenaVector<ir::Expression *> callArgs(allocator->Adapter());
256         functionParams.reserve(params.size() - paramsToCut);
257         callArgs.reserve(params.size());
258 
259         for (size_t i = 0; i < params.size() - paramsToCut; ++i) {
260             auto param = params[i]->AsETSParameterExpression();
261             auto cloneRef = param->Ident()->CloneReference(allocator, nullptr);
262             auto clone = param->Ident()->Clone(allocator, nullptr);
263             ES2PANDA_ASSERT(cloneRef != nullptr && clone != nullptr);
264             callArgs.push_back(cloneRef->AsIdentifier());
265             functionParams.push_back(
266                 allocator->New<ir::ETSParameterExpression>(clone->AsIdentifier(), false, allocator));
267         }
268 
269         for (size_t i = params.size() - paramsToCut; i < params.size(); ++i) {
270             auto param = params[i]->AsETSParameterExpression();
271             ir::Expression *init;
272             if (param->Initializer() == nullptr) {
273                 ES2PANDA_ASSERT(param->IsOptional());
274                 init = allocator->New<ir::UndefinedLiteral>();
275             } else if (param->Initializer()->IsArrowFunctionExpression()) {
276                 init = param->Initializer();
277             } else {
278                 init = param->Initializer()->Clone(allocator, nullptr)->AsExpression();
279             }
280             callArgs.push_back(init);
281         }
282         CreateFunctionOverload(method, std::move(callArgs), std::move(functionParams), ctx);
283     }
284 
285     ClearOptionalParameters(ctx, method->Function());
286 }
287 
PerformForModule(public_lib::Context * ctx,parser::Program * program)288 bool DefaultParametersInConstructorLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
289 {
290     util::DiagnosticEngine *logger = ctx->diagnosticEngine;
291     std::vector<ir::MethodDefinition *> foundNodes {};
292 
293     program->Ast()->IterateRecursively([&foundNodes, logger](ir::AstNode *ast) {
294         if (ast->IsMethodDefinition() && ast->AsMethodDefinition()->IsConstructor()) {
295             if (HasDefaultParameters(ast->AsMethodDefinition()->Function(), *logger)) {
296                 // store all nodes (which is function definition with default/optional parameters)
297                 // to specific list, to process them later, as for now we can't modify AST in the
298                 // middle of walking through it
299                 foundNodes.push_back(ast->AsMethodDefinition());
300             }
301         }
302     });
303 
304     for (auto &it : foundNodes) {
305         ProcessGlobalFunctionDefinition(it, ctx);
306     }
307     return true;
308 }
309 
PostconditionForModule(public_lib::Context * ctx,parser::Program const * program)310 bool DefaultParametersInConstructorLowering::PostconditionForModule([[maybe_unused]] public_lib::Context *ctx,
311                                                                     parser::Program const *program)
312 {
313     return !program->Ast()->IsAnyChild([](ir::AstNode const *node) {
314         if (!node->IsMethodDefinition() || !node->AsMethodDefinition()->IsConstructor()) {
315             return false;
316         }
317         for (auto *const it : node->AsMethodDefinition()->Function()->Params()) {
318             if (it->IsBrokenExpression()) {
319                 return false;
320             }
321             auto const *const param = it->AsETSParameterExpression();
322             if (param->IsOptional()) {
323                 return true;
324             }
325         }
326         return false;
327     });
328 }
329 
330 }  // namespace ark::es2panda::compiler
331