1 /**
2 * Copyright (c) 2021-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 "methodDefinition.h"
17
18 #include "checker/TSchecker.h"
19 #include "compiler/core/ETSGen.h"
20 #include "compiler/core/pandagen.h"
21 #include "classDefinition.h"
22 #include "ir/ts/tsInterfaceBody.h"
23 #include "compiler/lowering/util.h"
24
25 namespace ark::es2panda::ir {
26
Function()27 ScriptFunction *MethodDefinition::Function()
28 {
29 return value_->IsFunctionExpression() ? value_->AsFunctionExpression()->Function() : nullptr;
30 }
31
Function() const32 const ScriptFunction *MethodDefinition::Function() const
33 {
34 return value_->IsFunctionExpression() ? value_->AsFunctionExpression()->Function() : nullptr;
35 }
36
ToPrivateFieldKind(bool const isStatic) const37 PrivateFieldKind MethodDefinition::ToPrivateFieldKind(bool const isStatic) const
38 {
39 switch (kind_) {
40 case MethodDefinitionKind::METHOD: {
41 return isStatic ? PrivateFieldKind::STATIC_METHOD : PrivateFieldKind::METHOD;
42 }
43 case MethodDefinitionKind::GET: {
44 return isStatic ? PrivateFieldKind::STATIC_GET : PrivateFieldKind::GET;
45 }
46 case MethodDefinitionKind::SET: {
47 return isStatic ? PrivateFieldKind::STATIC_SET : PrivateFieldKind::SET;
48 }
49 default: {
50 ES2PANDA_UNREACHABLE();
51 }
52 }
53 }
54
ResolveReferences(const NodeTraverser & cb) const55 void MethodDefinition::ResolveReferences(const NodeTraverser &cb) const
56 {
57 cb(key_);
58 cb(value_);
59
60 for (auto *it : VectorIterationGuard(overloads_)) {
61 cb(it);
62 }
63
64 for (auto *it : VectorIterationGuard(decorators_)) {
65 cb(it);
66 }
67 }
68
Iterate(const NodeTraverser & cb) const69 void MethodDefinition::Iterate(const NodeTraverser &cb) const
70 {
71 cb(key_);
72 cb(value_);
73
74 for (auto *it : overloads_) {
75 if (it->Parent() == this) {
76 cb(it);
77 }
78 }
79
80 for (auto *it : VectorIterationGuard(decorators_)) {
81 cb(it);
82 }
83 }
84
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)85 void MethodDefinition::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
86 {
87 if (auto *transformedNode = cb(key_); key_ != transformedNode) {
88 key_->SetTransformedNode(transformationName, transformedNode);
89 key_ = transformedNode->AsExpression();
90 }
91
92 if (auto *transformedNode = cb(value_); value_ != transformedNode) {
93 value_->SetTransformedNode(transformationName, transformedNode);
94 value_ = transformedNode->AsExpression();
95 }
96
97 for (auto *&it : VectorIterationGuard(overloads_)) {
98 if (auto *transformedNode = cb(it); it != transformedNode) {
99 it->SetTransformedNode(transformationName, transformedNode);
100 it = transformedNode->AsMethodDefinition();
101 }
102 }
103
104 for (auto *&it : VectorIterationGuard(decorators_)) {
105 if (auto *transformedNode = cb(it); it != transformedNode) {
106 it->SetTransformedNode(transformationName, transformedNode);
107 it = transformedNode->AsDecorator();
108 }
109 }
110 }
111
Dump(ir::AstDumper * dumper) const112 void MethodDefinition::Dump(ir::AstDumper *dumper) const
113 {
114 const char *kind = nullptr;
115
116 switch (kind_) {
117 case MethodDefinitionKind::CONSTRUCTOR: {
118 kind = "constructor";
119 break;
120 }
121 case MethodDefinitionKind::METHOD: {
122 kind = "method";
123 break;
124 }
125 case MethodDefinitionKind::EXTENSION_METHOD: {
126 kind = "extensionmethod";
127 break;
128 }
129 case MethodDefinitionKind::GET: {
130 kind = "get";
131 break;
132 }
133 case MethodDefinitionKind::SET: {
134 kind = "set";
135 break;
136 }
137 case MethodDefinitionKind::EXTENSION_GET: {
138 kind = "extensionget";
139 break;
140 }
141 case MethodDefinitionKind::EXTENSION_SET: {
142 kind = "extensionset";
143 break;
144 }
145 default: {
146 ES2PANDA_UNREACHABLE();
147 }
148 }
149
150 dumper->Add({{"type", "MethodDefinition"},
151 {"key", key_},
152 {"kind", kind},
153 {"accessibility", AstDumper::Optional(AstDumper::ModifierToString(flags_))},
154 {"static", IsStatic()},
155 {"optional", IsOptionalDeclaration()},
156 {"computed", isComputed_},
157 {"value", value_},
158 {"overloads", overloads_},
159 {"decorators", decorators_}});
160 }
161
DumpModifierPrefix(ir::SrcDumper * dumper) const162 void MethodDefinition::DumpModifierPrefix(ir::SrcDumper *dumper) const
163 {
164 if (compiler::HasGlobalClassParent(this)) {
165 return;
166 }
167 if (IsStatic()) {
168 dumper->Add("static ");
169 }
170
171 if (IsAbstract() && !(Parent()->IsTSInterfaceBody() ||
172 (BaseOverloadMethod() != nullptr && BaseOverloadMethod()->Parent()->IsTSInterfaceBody()))) {
173 dumper->Add("abstract ");
174 }
175 if (IsFinal()) {
176 dumper->Add("final ");
177 }
178 if (IsNative()) {
179 dumper->Add("native ");
180 }
181 if (IsAsync()) {
182 dumper->Add("async ");
183 }
184 if (IsOverride()) {
185 dumper->Add("override ");
186 }
187
188 if (IsGetter()) {
189 dumper->Add("get ");
190 } else if (IsSetter()) {
191 dumper->Add("set ");
192 }
193 }
194
DumpNamespaceForDeclGen(ir::SrcDumper * dumper) const195 bool MethodDefinition::DumpNamespaceForDeclGen(ir::SrcDumper *dumper) const
196 {
197 if (!dumper->IsDeclgen()) {
198 return false;
199 }
200
201 if (Parent() == nullptr) {
202 return false;
203 }
204
205 bool isNamespaceTransformed =
206 Parent()->IsClassDefinition() && Parent()->AsClassDefinition()->IsNamespaceTransformed();
207 if (isNamespaceTransformed) {
208 dumper->Add("function ");
209 return true;
210 }
211
212 return false;
213 }
214
DumpPrefixForDeclGen(ir::SrcDumper * dumper) const215 void MethodDefinition::DumpPrefixForDeclGen(ir::SrcDumper *dumper) const
216 {
217 if (!dumper->IsDeclgen()) {
218 return;
219 }
220
221 if (key_ == nullptr) {
222 return;
223 }
224
225 if (key_->Parent()->IsExported()) {
226 dumper->Add("export declare function ");
227 } else if (key_->Parent()->IsDefaultExported()) {
228 dumper->Add("export default declare function ");
229 }
230 }
231
DumpPrefix(ir::SrcDumper * dumper) const232 void MethodDefinition::DumpPrefix(ir::SrcDumper *dumper) const
233 {
234 if (DumpNamespaceForDeclGen(dumper)) {
235 return;
236 }
237
238 if (compiler::HasGlobalClassParent(this) && !dumper->IsDeclgen()) {
239 if (IsExported()) {
240 dumper->Add("export ");
241 }
242 dumper->Add("function ");
243 return;
244 }
245
246 DumpPrefixForDeclGen(dumper);
247
248 if (Parent() != nullptr && Parent()->IsClassDefinition() && !Parent()->AsClassDefinition()->IsLocal() &&
249 !compiler::HasGlobalClassParent(this)) {
250 if (IsPrivate()) {
251 dumper->Add("private ");
252 } else if (IsProtected()) {
253 dumper->Add("protected ");
254 } else if (IsInternal()) {
255 dumper->Add("internal ");
256 } else {
257 dumper->Add("public ");
258 }
259 }
260 DumpModifierPrefix(dumper);
261 }
262
FilterForDeclGen(ir::SrcDumper * dumper) const263 bool MethodDefinition::FilterForDeclGen(ir::SrcDumper *dumper) const
264 {
265 if (!dumper->IsDeclgen()) {
266 return false;
267 }
268
269 if (key_ == nullptr) {
270 return false;
271 }
272
273 if (compiler::HasGlobalClassParent(this) && !key_->Parent()->IsExported() && !key_->Parent()->IsDefaultExported()) {
274 return true;
275 }
276
277 ES2PANDA_ASSERT(Id() != nullptr);
278 auto name = Id()->Name().Mutf8();
279 if (name.find("$asyncimpl") != std::string::npos || name == compiler::Signatures::INITIALIZER_BLOCK_INIT ||
280 name == compiler::Signatures::INIT_METHOD) {
281 return true;
282 }
283
284 if (name.rfind('#', 0) == 0) {
285 return true;
286 }
287
288 if (name == compiler::Signatures::CCTOR) {
289 return true;
290 }
291
292 if (name == compiler::Signatures::GET_INDEX_METHOD || name == compiler::Signatures::SET_INDEX_METHOD) {
293 return true;
294 }
295
296 return false;
297 }
298
Dump(ir::SrcDumper * dumper) const299 void MethodDefinition::Dump(ir::SrcDumper *dumper) const
300 {
301 if (FilterForDeclGen(dumper)) {
302 return;
303 }
304
305 if (compiler::HasGlobalClassParent(this) && Id() != nullptr && Id()->Name().Is(compiler::Signatures::INIT_METHOD) &&
306 Function() != nullptr) {
307 Function()->Body()->Dump(dumper);
308 return;
309 }
310
311 for (auto method : overloads_) {
312 method->Dump(dumper);
313 dumper->Endl();
314 }
315
316 for (auto *anno : value_->AsFunctionExpression()->Function()->Annotations()) {
317 // NOTE(zhelyapov): workaround, see #26031
318 if (anno->GetBaseName()->Name() != compiler::Signatures::DEFAULT_ANNO_FOR_FUNC) {
319 anno->Dump(dumper);
320 }
321 }
322 DumpPrefix(dumper);
323
324 if (key_ != nullptr) {
325 key_->Dump(dumper);
326 }
327
328 if (value_ != nullptr) {
329 value_->Dump(dumper);
330 }
331 }
332
Compile(compiler::PandaGen * pg) const333 void MethodDefinition::Compile(compiler::PandaGen *pg) const
334 {
335 pg->GetAstCompiler()->Compile(this);
336 }
337
Compile(compiler::ETSGen * etsg) const338 void MethodDefinition::Compile(compiler::ETSGen *etsg) const
339 {
340 etsg->GetAstCompiler()->Compile(this);
341 }
342
Check(checker::TSChecker * checker)343 checker::Type *MethodDefinition::Check(checker::TSChecker *checker)
344 {
345 return checker->GetAnalyzer()->Check(this);
346 }
347
Check(checker::ETSChecker * checker)348 checker::VerifiedType MethodDefinition::Check(checker::ETSChecker *checker)
349 {
350 return {this, checker->GetAnalyzer()->Check(this)};
351 }
352
Clone(ArenaAllocator * const allocator,AstNode * const parent)353 MethodDefinition *MethodDefinition::Clone(ArenaAllocator *const allocator, AstNode *const parent)
354 {
355 auto *const key = key_->Clone(allocator, nullptr)->AsExpression();
356 auto *const value = value_->Clone(allocator, nullptr)->AsExpression();
357 auto *const clone = allocator->New<MethodDefinition>(kind_, key, value, flags_, allocator, isComputed_);
358
359 if (parent != nullptr) {
360 clone->SetParent(parent);
361 }
362
363 key->SetParent(clone);
364 value->SetParent(clone);
365
366 for (auto *const decorator : decorators_) {
367 clone->AddDecorator(decorator->Clone(allocator, clone));
368 }
369
370 clone->baseOverloadMethod_ = baseOverloadMethod_;
371
372 for (auto *const overloads : overloads_) {
373 clone->AddOverload(overloads->Clone(allocator, clone));
374 }
375
376 return clone;
377 }
378
InitializeOverloadInfo()379 void MethodDefinition::InitializeOverloadInfo()
380 {
381 ES2PANDA_ASSERT(this->Function() != nullptr);
382
383 overloadInfo_ = {this->Function()->Signature()->MinArgCount(),
384 this->Function()->Signature()->ArgCount(),
385 false,
386 this->IsDeclare(),
387 (this->Function()->Signature()->RestVar() != nullptr),
388 this->Function()->Signature()->ReturnType()->IsETSVoidType()};
389 }
390
ResetOverloads()391 void MethodDefinition::ResetOverloads()
392 {
393 auto baseOverloadMethod = baseOverloadMethod_;
394 baseOverloadMethod_ = nullptr;
395 for (auto *overload : overloads_) {
396 overload->CleanUp();
397 }
398 ClearOverloads();
399
400 if ((Function() == nullptr) || !Function()->IsOverload()) {
401 return;
402 }
403
404 Function()->ClearFlag(ir::ScriptFunctionFlags::OVERLOAD);
405 /*
406 * if this method and it's baseOverloadMethod are in two different files,
407 * no need to move it to the body of baseOverloadMethod's contianing class in cleanup.
408 */
409 if (GetTopStatement() != baseOverloadMethod->GetTopStatement()) {
410 return;
411 }
412
413 auto parent = baseOverloadMethod->Parent();
414 ES2PANDA_ASSERT(parent->IsClassDefinition() || parent->IsTSInterfaceBody());
415 auto &body =
416 parent->IsClassDefinition() ? parent->AsClassDefinition()->Body() : parent->AsTSInterfaceBody()->Body();
417
418 for (auto *elem : body) {
419 if (elem == this) {
420 return;
421 }
422 }
423
424 body.emplace_back(this);
425 }
426
CleanUp()427 void MethodDefinition::CleanUp()
428 {
429 AstNode::CleanUp();
430 ResetOverloads();
431 }
432
Construct(ArenaAllocator * allocator)433 MethodDefinition *MethodDefinition::Construct(ArenaAllocator *allocator)
434 {
435 return allocator->New<MethodDefinition>(MethodDefinitionKind::NONE, nullptr, nullptr, ModifierFlags::NONE,
436 allocator, false);
437 }
438
CopyTo(AstNode * other) const439 void MethodDefinition::CopyTo(AstNode *other) const
440 {
441 auto otherImpl = other->AsMethodDefinition();
442
443 otherImpl->isDefault_ = isDefault_;
444 otherImpl->kind_ = kind_;
445 otherImpl->overloads_ = overloads_;
446 otherImpl->baseOverloadMethod_ = baseOverloadMethod_;
447 otherImpl->asyncPairMethod_ = asyncPairMethod_;
448 otherImpl->overloadInfo_ = overloadInfo_;
449
450 ClassElement::CopyTo(other);
451 }
452
453 } // namespace ark::es2panda::ir
454