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 ¶m : 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 *¶m : 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 ¶ms = 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