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 "classDefinition.h"
17
18 #include "checker/TSchecker.h"
19 #include "checker/ETSchecker.h"
20 #include "compiler/core/ETSGen.h"
21 #include "compiler/core/pandagen.h"
22 #include "ir/astDump.h"
23 #include "ir/srcDump.h"
24 #include "ir/base/classStaticBlock.h"
25 #include "ir/base/methodDefinition.h"
26 #include "ir/base/scriptFunction.h"
27 #include "ir/expressions/identifier.h"
28 #include "ir/ts/tsClassImplements.h"
29
30 namespace ark::es2panda::ir {
Ctor() const31 const FunctionExpression *ClassDefinition::Ctor() const
32 {
33 return ctor_ != nullptr ? ctor_->Value()->AsFunctionExpression() : nullptr;
34 }
35
HasPrivateMethod() const36 bool ClassDefinition::HasPrivateMethod() const
37 {
38 return std::any_of(body_.cbegin(), body_.cend(), [](auto *element) {
39 return element->IsMethodDefinition() && element->AsClassElement()->IsPrivateElement();
40 });
41 }
42
HasNativeMethod() const43 bool ClassDefinition::HasNativeMethod() const
44 {
45 return std::any_of(body_.cbegin(), body_.cend(), [](auto *element) {
46 return element->IsMethodDefinition() && element->AsMethodDefinition()->IsNative();
47 });
48 }
49
HasComputedInstanceField() const50 bool ClassDefinition::HasComputedInstanceField() const
51 {
52 return std::any_of(body_.cbegin(), body_.cend(), [](auto *element) {
53 return element->IsClassProperty() && element->AsClassElement()->IsComputed() &&
54 !(element->AsClassElement()->Modifiers() & ir::ModifierFlags::STATIC);
55 });
56 }
57
HasMatchingPrivateKey(const util::StringView & name) const58 bool ClassDefinition::HasMatchingPrivateKey(const util::StringView &name) const
59 {
60 return std::any_of(body_.cbegin(), body_.cend(), [&name](auto *element) {
61 return element->AsClassElement()->IsPrivateElement() && element->AsClassElement()->Id()->Name() == name;
62 });
63 }
64
TransformChildren(const NodeTransformer & cb,std::string_view transformationName)65 void ClassDefinition::TransformChildren(const NodeTransformer &cb, std::string_view transformationName)
66 {
67 if (ident_ != nullptr) {
68 if (auto *transformedNode = cb(ident_); ident_ != transformedNode) {
69 ident_->SetTransformedNode(transformationName, transformedNode);
70 ident_ = transformedNode->AsIdentifier();
71 }
72 }
73
74 if (typeParams_ != nullptr) {
75 if (auto *transformedNode = cb(typeParams_); typeParams_ != transformedNode) {
76 typeParams_->SetTransformedNode(transformationName, transformedNode);
77 typeParams_ = transformedNode->AsTSTypeParameterDeclaration();
78 }
79 }
80
81 if (superClass_ != nullptr) {
82 if (auto *transformedNode = cb(superClass_); superClass_ != transformedNode) {
83 superClass_->SetTransformedNode(transformationName, transformedNode);
84 superClass_ = transformedNode->AsExpression();
85 }
86 }
87
88 if (superTypeParams_ != nullptr) {
89 if (auto *transformedNode = cb(superTypeParams_); superTypeParams_ != transformedNode) {
90 superTypeParams_->SetTransformedNode(transformationName, transformedNode);
91 superTypeParams_ = transformedNode->AsTSTypeParameterInstantiation();
92 }
93 }
94
95 for (auto *&it : VectorIterationGuard(implements_)) {
96 if (auto *transformedNode = cb(it); it != transformedNode) {
97 it->SetTransformedNode(transformationName, transformedNode);
98 it = transformedNode->AsTSClassImplements();
99 }
100 }
101
102 for (auto *&it : VectorIterationGuard(Annotations())) {
103 if (auto *transformedNode = cb(it); it != transformedNode) {
104 it->SetTransformedNode(transformationName, transformedNode);
105 it = transformedNode->AsAnnotationUsage();
106 }
107 }
108
109 if (ctor_ != nullptr) {
110 if (auto *transformedNode = cb(ctor_); ctor_ != transformedNode) {
111 ctor_->SetTransformedNode(transformationName, transformedNode);
112 ctor_ = transformedNode->AsMethodDefinition();
113 }
114 }
115
116 // Survives adding new elements to the end
117 // NOLINTNEXTLINE(modernize-loop-convert)
118 for (size_t ix = 0; ix < body_.size(); ix++) {
119 if (auto *transformedNode = cb(body_[ix]); body_[ix] != transformedNode) {
120 body_[ix]->SetTransformedNode(transformationName, transformedNode);
121 body_[ix] = transformedNode;
122 }
123 }
124 }
125
Iterate(const NodeTraverser & cb) const126 void ClassDefinition::Iterate(const NodeTraverser &cb) const
127 {
128 if (ident_ != nullptr) {
129 cb(ident_);
130 }
131
132 if (typeParams_ != nullptr) {
133 cb(typeParams_);
134 }
135
136 if (superClass_ != nullptr) {
137 cb(superClass_);
138 }
139
140 if (superTypeParams_ != nullptr) {
141 cb(superTypeParams_);
142 }
143
144 // Survives adding new elements to the end
145 // NOLINTNEXTLINE(modernize-loop-convert)
146 for (size_t ix = 0; ix < implements_.size(); ix++) {
147 cb(implements_[ix]);
148 }
149
150 for (auto *it : VectorIterationGuard(Annotations())) {
151 cb(it);
152 }
153
154 if (ctor_ != nullptr) {
155 cb(ctor_);
156 }
157
158 // NOLINTNEXTLINE(modernize-loop-convert)
159 for (size_t ix = 0; ix < body_.size(); ix++) {
160 cb(body_[ix]);
161 }
162 }
163
SetIdent(ir::Identifier * ident)164 void ClassDefinition::SetIdent(ir::Identifier *ident) noexcept
165 {
166 ident_ = ident;
167 if (ident_ != nullptr) {
168 ident_->SetParent(this);
169 }
170 }
171
Dump(ir::AstDumper * dumper) const172 void ClassDefinition::Dump(ir::AstDumper *dumper) const
173 {
174 auto propFilter = [](AstNode *prop) -> bool {
175 return !prop->IsClassStaticBlock() || !prop->AsClassStaticBlock()->Function()->IsHidden();
176 };
177 dumper->Add({{"id", AstDumper::Nullish(ident_)},
178 {"typeParameters", AstDumper::Optional(typeParams_)},
179 {"superClass", AstDumper::Nullish(superClass_)},
180 {"superTypeParameters", AstDumper::Optional(superTypeParams_)},
181 {"implements", implements_},
182 {"annotations", AstDumper::Optional(Annotations())},
183 {"constructor", AstDumper::Optional(ctor_)},
184 {"body", body_, propFilter}});
185 }
186
DumpGlobalClass(ir::SrcDumper * dumper) const187 void ClassDefinition::DumpGlobalClass(ir::SrcDumper *dumper) const
188 {
189 ES2PANDA_ASSERT(IsGlobal());
190 for (auto elem : body_) {
191 if (elem->IsClassProperty()) {
192 elem->Dump(dumper);
193 dumper->Endl();
194 }
195 }
196 for (auto elem : body_) {
197 if (elem->IsMethodDefinition()) {
198 elem->Dump(dumper);
199 dumper->Endl();
200 }
201 }
202 }
203
204 // This method is needed by OHOS CI code checker
DumpBody(ir::SrcDumper * dumper) const205 void ClassDefinition::DumpBody(ir::SrcDumper *dumper) const
206 {
207 dumper->Add(" {");
208 if (!body_.empty()) {
209 dumper->IncrIndent();
210 dumper->Endl();
211 for (auto elem : body_) {
212 elem->Dump(dumper);
213 if (elem == body_.back()) {
214 dumper->DecrIndent();
215 }
216 dumper->Endl();
217 }
218 }
219 dumper->Add("}");
220 }
221
DumpPrefix(ir::SrcDumper * dumper) const222 void ClassDefinition::DumpPrefix(ir::SrcDumper *dumper) const
223 {
224 if (IsExported()) {
225 dumper->Add("export ");
226 } else if (IsDefaultExported()) {
227 dumper->Add("export default ");
228 }
229
230 if (IsDeclare() || dumper->IsDeclgen()) {
231 dumper->Add("declare ");
232 }
233
234 if (IsFinal()) {
235 dumper->Add("final ");
236 }
237
238 if (IsAbstract() && !IsNamespaceTransformed()) {
239 dumper->Add("abstract ");
240 }
241
242 if (parent_->IsETSStructDeclaration() || IsFromStruct()) {
243 dumper->Add("struct ");
244 } else if (IsNamespaceTransformed()) {
245 dumper->Add("namespace ");
246 } else {
247 dumper->Add("class ");
248 }
249 }
250
RegisterUnexportedForDeclGen(ir::SrcDumper * dumper) const251 bool ClassDefinition::RegisterUnexportedForDeclGen(ir::SrcDumper *dumper) const
252 {
253 if (!dumper->IsDeclgen()) {
254 return false;
255 }
256
257 if (dumper->IsIndirectDepPhase()) {
258 return false;
259 }
260
261 if (IsExported() || IsDefaultExported()) {
262 return false;
263 }
264
265 const auto className = ident_->Name().Mutf8();
266 dumper->AddNode(className, this);
267 return true;
268 }
269
Dump(ir::SrcDumper * dumper) const270 void ClassDefinition::Dump(ir::SrcDumper *dumper) const
271 {
272 // NOTE: plugin API fails
273 if ((ident_->Name().StartsWith("$dynmodule")) || (ident_->Name().StartsWith("$jscall"))) {
274 return;
275 }
276
277 if (IsGlobal()) {
278 DumpGlobalClass(dumper);
279 return;
280 }
281
282 ES2PANDA_ASSERT(ident_ != nullptr);
283
284 if (RegisterUnexportedForDeclGen(dumper)) {
285 return;
286 }
287
288 for (auto *anno : Annotations()) {
289 anno->Dump(dumper);
290 }
291
292 DumpPrefix(dumper);
293 ident_->Dump(dumper);
294
295 if (typeParams_ != nullptr) {
296 dumper->Add("<");
297 typeParams_->Dump(dumper);
298 dumper->Add("> ");
299 }
300
301 if (superClass_ != nullptr) {
302 dumper->Add(" extends ");
303 superClass_->Dump(dumper);
304 }
305
306 DumpItems(dumper, " implements ", implements_);
307 if (!IsDeclare() || !body_.empty()) {
308 DumpBody(dumper);
309 }
310 if (IsLocal()) {
311 dumper->Add(";");
312 }
313 dumper->Endl();
314 }
315
Compile(compiler::PandaGen * pg) const316 void ClassDefinition::Compile(compiler::PandaGen *pg) const
317 {
318 pg->GetAstCompiler()->Compile(this);
319 }
320
Compile(compiler::ETSGen * etsg) const321 void ClassDefinition::Compile(compiler::ETSGen *etsg) const
322 {
323 etsg->GetAstCompiler()->Compile(this);
324 }
325
Check(checker::TSChecker * checker)326 checker::Type *ClassDefinition::Check(checker::TSChecker *checker)
327 {
328 return checker->GetAnalyzer()->Check(this);
329 }
330
Check(checker::ETSChecker * checker)331 checker::VerifiedType ClassDefinition::Check(checker::ETSChecker *checker)
332 {
333 return {this, checker->GetAnalyzer()->Check(this)};
334 }
335
Construct(ArenaAllocator * allocator)336 ClassDefinition *ClassDefinition::Construct(ArenaAllocator *allocator)
337 {
338 ArenaVector<AstNode *> body {allocator->Adapter()};
339 return allocator->New<ClassDefinition>(allocator, nullptr, std::move(body), ClassDefinitionModifiers::NONE,
340 ModifierFlags::NONE, Language::Id::COUNT);
341 }
342
CopyTo(AstNode * other) const343 void ClassDefinition::CopyTo(AstNode *other) const
344 {
345 auto otherImpl = other->AsClassDefinition();
346
347 otherImpl->scope_ = scope_;
348 otherImpl->internalName_ = internalName_;
349 otherImpl->ident_ = ident_;
350 otherImpl->typeParams_ = typeParams_;
351 otherImpl->superTypeParams_ = superTypeParams_;
352 otherImpl->implements_ = implements_;
353 otherImpl->ctor_ = ctor_;
354 otherImpl->superClass_ = superClass_;
355 otherImpl->body_ = body_;
356 otherImpl->modifiers_ = modifiers_;
357 otherImpl->lang_ = lang_;
358 otherImpl->capturedVars_ = capturedVars_;
359 otherImpl->localVariableIsNeeded_ = localVariableIsNeeded_;
360 otherImpl->origEnumDecl_ = origEnumDecl_;
361 otherImpl->anonClass_ = anonClass_;
362 otherImpl->localIndex_ = localIndex_;
363 otherImpl->localPrefix_ = localPrefix_;
364 otherImpl->functionalReferenceReferencedMethod_ = functionalReferenceReferencedMethod_;
365 otherImpl->exportedClasses_ = exportedClasses_;
366
367 JsDocAllowed<AnnotationAllowed<TypedAstNode>>::CopyTo(other);
368 }
369
370 int ClassDefinition::classCounter_ = 0;
371
372 } // namespace ark::es2panda::ir
373