• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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