* Copyright (c) 2021-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ES2PANDA_PARSER_INCLUDE_AST_CLASS_DEFINITION_H
#define ES2PANDA_PARSER_INCLUDE_AST_CLASS_DEFINITION_H
#include "varbinder/scope.h"
#include "varbinder/variable.h"
#include "ir/srcDump.h"
#include "ir/annotationAllowed.h"
#include "ir/astNode.h"
#include "ir/astNodeHistory.h"
#include "ir/expressions/identifier.h"
#include "ir/statements/annotationUsage.h"
#include "ir/statements/classDeclaration.h"
#include "util/language.h"
#include "util/nameMangler.h"
namespace ark::es2panda::ir {
class ClassElement;
class Identifier;
class MethodDefinition;
class TSTypeParameterDeclaration;
class TSTypeParameterInstantiation;
class TSClassImplements;
class TSIndexSignature;
using ENUMBITOPS_OPERATORS;
enum class ClassDefinitionModifiers : uint32_t {
NONE = 0,
DECLARATION = 1U << 0U,
ID_REQUIRED = 1U << 1U,
GLOBAL = 1U << 2U,
HAS_SUPER = 1U << 3U,
SET_CTOR_ID = 1U << 4U,
EXTERN = 1U << 5U,
ANONYMOUS = 1U << 6U,
GLOBAL_INITIALIZED = 1U << 7U,
CLASS_DECL = 1U << 8U,
INNER = 1U << 9U,
FROM_EXTERNAL = 1U << 10U,
LOCAL = 1U << 11U,
CLASSDEFINITION_CHECKED = 1U << 12U,
NAMESPACE_TRANSFORMED = 1U << 13U,
STRING_ENUM_TRANSFORMED = 1U << 14U,
NUMERIC_ENUM_TRANSFORMED = 1U << 15U,
FROM_STRUCT = 1U << 16U,
FUNCTIONAL_REFERENCE = 1U << 17U,
LAZY_IMPORT_OBJECT_CLASS = 1U << 18U,
INIT_IN_CCTOR = 1U << 19U,
DECLARATION_ID_REQUIRED = DECLARATION | ID_REQUIRED,
ETS_MODULE = NAMESPACE_TRANSFORMED | GLOBAL
};
}
template <>
struct enumbitops::IsAllowedType<ark::es2panda::ir::ClassDefinitionModifiers> : std::true_type {
};
namespace ark::es2panda::ir {
class ClassDefinition : public AnnotationAllowed<TypedAstNode> {
public:
ClassDefinition() = delete;
~ClassDefinition() override = default;
NO_COPY_SEMANTIC(ClassDefinition);
NO_MOVE_SEMANTIC(ClassDefinition);
explicit ClassDefinition(ArenaAllocator *allocator, Identifier *ident, TSTypeParameterDeclaration *typeParams,
TSTypeParameterInstantiation *superTypeParams,
ArenaVector<TSClassImplements *> &&implements, MethodDefinition *ctor,
Expression *superClass, ArenaVector<AstNode *> &&body, ClassDefinitionModifiers modifiers,
ModifierFlags flags, Language lang)
: AnnotationAllowed<TypedAstNode>(AstNodeType::CLASS_DEFINITION, flags, allocator),
ident_(ident),
typeParams_(typeParams),
superTypeParams_(superTypeParams),
implements_(std::move(implements)),
ctor_(ctor),
superClass_(superClass),
body_(std::move(body)),
modifiers_(modifiers),
lang_(lang),
capturedVars_(body_.get_allocator()),
localVariableIsNeeded_(body_.get_allocator()),
localIndex_(classCounter_++),
localPrefix_("$" + std::to_string(localIndex_)),
exportedClasses_(body_.get_allocator())
{
InitHistory();
}
explicit ClassDefinition(ArenaAllocator *allocator, Identifier *ident, ArenaVector<AstNode *> &&body,
ClassDefinitionModifiers modifiers, ModifierFlags flags, Language lang)
: AnnotationAllowed<TypedAstNode>(AstNodeType::CLASS_DEFINITION, flags, allocator),
ident_(ident),
implements_(allocator->Adapter()),
body_(std::move(body)),
modifiers_(modifiers),
lang_(lang),
capturedVars_(allocator->Adapter()),
localVariableIsNeeded_(allocator->Adapter()),
localIndex_(classCounter_++),
localPrefix_("$" + std::to_string(localIndex_)),
exportedClasses_(body_.get_allocator())
{
InitHistory();
}
explicit ClassDefinition(ArenaAllocator *allocator, Identifier *ident, ClassDefinitionModifiers modifiers,
ModifierFlags flags, Language lang)
: AnnotationAllowed<TypedAstNode>(AstNodeType::CLASS_DEFINITION, flags, allocator),
ident_(ident),
implements_(allocator->Adapter()),
body_(allocator->Adapter()),
modifiers_(modifiers),
lang_(lang),
capturedVars_(allocator->Adapter()),
localVariableIsNeeded_(allocator->Adapter()),
localIndex_(classCounter_++),
localPrefix_("$" + std::to_string(localIndex_)),
exportedClasses_(body_.get_allocator())
{
InitHistory();
}
explicit ClassDefinition(ArenaAllocator *allocator, Identifier *ident, ArenaVector<AstNode *> &&body,
ClassDefinitionModifiers modifiers, ModifierFlags flags, Language lang,
AstNodeHistory *history)
: AnnotationAllowed<TypedAstNode>(AstNodeType::CLASS_DEFINITION, flags, allocator),
ident_(ident),
implements_(allocator->Adapter()),
body_(std::move(body)),
modifiers_(modifiers),
lang_(lang),
capturedVars_(allocator->Adapter()),
localVariableIsNeeded_(allocator->Adapter()),
localIndex_(classCounter_++),
localPrefix_("$" + std::to_string(localIndex_)),
exportedClasses_(body_.get_allocator())
{
if (history != nullptr) {
SetHistoryInternal(history);
} else {
InitHistory();
}
}
[[nodiscard]] bool IsScopeBearer() const noexcept override
{
return true;
}
[[nodiscard]] varbinder::LocalScope *Scope() const noexcept override
{
return GetHistoryNodeAs<ClassDefinition>()->scope_;
}
void ClearScope() noexcept override
{
SetScope(nullptr);
}
[[nodiscard]] const Identifier *Ident() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->ident_;
}
[[nodiscard]] Identifier *Ident() noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->ident_;
}
void SetIdent(ir::Identifier *ident) noexcept;
[[nodiscard]] const util::StringView &InternalName() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->internalName_;
}
[[nodiscard]] Expression *Super() noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->superClass_;
}
[[nodiscard]] const Expression *Super() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->superClass_;
}
void SetSuper(Expression *superClass)
{
auto newNode = this->GetOrCreateHistoryNodeAs<ClassDefinition>();
newNode->superClass_ = superClass;
if (newNode->superClass_ != nullptr) {
newNode->superClass_->SetParent(this);
}
}
[[nodiscard]] bool IsGlobal() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::GLOBAL) != 0;
}
[[nodiscard]] bool IsLocal() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::LOCAL) != 0;
}
[[nodiscard]] bool IsExtern() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::EXTERN) != 0;
}
[[nodiscard]] bool IsFromExternal() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::FROM_EXTERNAL) != 0;
}
[[nodiscard]] bool IsInner() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::INNER) != 0;
}
[[nodiscard]] bool IsGlobalInitialized() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::GLOBAL_INITIALIZED) != 0;
}
[[nodiscard]] bool IsClassDefinitionChecked() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::CLASSDEFINITION_CHECKED) != 0;
}
[[nodiscard]] bool IsAnonymous() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::ANONYMOUS) != 0;
}
[[nodiscard]] bool IsNumericEnumTransformed() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::NUMERIC_ENUM_TRANSFORMED) != 0;
}
[[nodiscard]] bool IsStringEnumTransformed() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::STRING_ENUM_TRANSFORMED) != 0;
}
[[nodiscard]] bool IsEnumTransformed() const noexcept
{
return IsNumericEnumTransformed() || IsStringEnumTransformed();
}
[[nodiscard]] bool IsNamespaceTransformed() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::NAMESPACE_TRANSFORMED) != 0;
}
[[nodiscard]] bool IsLazyImportObjectClass() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS) != 0;
}
[[nodiscard]] bool IsFromStruct() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::FROM_STRUCT) != 0;
}
[[nodiscard]] bool IsInitInCctor() const noexcept
{
return (Modifiers() & ClassDefinitionModifiers::INIT_IN_CCTOR) != 0;
}
[[nodiscard]] bool IsModule() const noexcept
{
return IsGlobal() || IsNamespaceTransformed();
}
[[nodiscard]] bool IsFromLambda() const noexcept
{
return Ident()->Name().StartsWith(util::LAMBDA_CLASS_PREFIX);
}
[[nodiscard]] es2panda::Language Language() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->lang_;
}
void SetGlobalInitialized() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::GLOBAL_INITIALIZED);
}
void SetInnerModifier() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::INNER);
}
void SetClassDefinitionChecked() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::CLASSDEFINITION_CHECKED);
}
void SetAnonymousModifier() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::ANONYMOUS);
}
void SetNamespaceTransformed() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::NAMESPACE_TRANSFORMED);
}
void SetLazyImportObjectClass() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS);
}
void SetFromStructModifier() noexcept
{
AddClassModifiers(ClassDefinitionModifiers::FROM_STRUCT);
}
void SetInitInCctor()
{
AddClassModifiers(ClassDefinitionModifiers::INIT_IN_CCTOR);
}
[[nodiscard]] ClassDefinitionModifiers Modifiers() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->modifiers_;
}
void AddProperties(ArenaVector<AstNode *> &&body)
{
for (auto *prop : body) {
prop->SetParent(this);
}
auto newNode = GetOrCreateHistoryNode()->AsClassDefinition();
newNode->body_.insert(newNode->body_.end(), body.begin(), body.end());
}
[[nodiscard]] const ArenaVector<AstNode *> &Body() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->body_;
}
[[nodiscard]] MethodDefinition *Ctor() noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->ctor_;
}
[[nodiscard]] const ArenaVector<ir::TSClassImplements *> &Implements() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->implements_;
}
[[nodiscard]] const ir::TSTypeParameterDeclaration *TypeParams() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->typeParams_;
}
[[nodiscard]] ir::TSTypeParameterDeclaration *TypeParams() noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->typeParams_;
}
const TSTypeParameterInstantiation *SuperTypeParams() const
{
return GetHistoryNodeAs<ClassDefinition>()->superTypeParams_;
}
TSTypeParameterInstantiation *SuperTypeParams()
{
return GetHistoryNodeAs<ClassDefinition>()->superTypeParams_;
}
void SetSuperTypeParams(TSTypeParameterInstantiation *superTypeParams);
[[nodiscard]] static int LocalTypeCounter() noexcept
{
return classCounter_;
}
[[nodiscard]] int LocalIndex() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->localIndex_;
}
[[nodiscard]] MethodDefinition *FunctionalReferenceReferencedMethod() const noexcept
{
return functionalReferenceReferencedMethod_;
}
void SetFunctionalReferenceReferencedMethod(MethodDefinition *functionalReferenceReferencedMethod)
{
functionalReferenceReferencedMethod_ = functionalReferenceReferencedMethod;
}
[[nodiscard]] const std::string &LocalPrefix() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->localPrefix_;
}
bool CaptureVariable(varbinder::Variable *var)
{
auto newNode = GetOrCreateHistoryNode()->AsClassDefinition();
return newNode->capturedVars_.insert(var).second;
}
bool AddToLocalVariableIsNeeded(varbinder::Variable *var)
{
auto newNode = GetOrCreateHistoryNode()->AsClassDefinition();
return newNode->localVariableIsNeeded_.insert(var).second;
}
bool IsLocalVariableNeeded(varbinder::Variable *var) const
{
auto const newNode = GetHistoryNode()->AsClassDefinition();
return newNode->localVariableIsNeeded_.find(var) != newNode->localVariableIsNeeded_.end();
}
[[nodiscard]] const ArenaSet<varbinder::Variable *> &CapturedVariables() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->capturedVars_;
}
bool EraseCapturedVariable(varbinder::Variable *var)
{
auto newNode = GetOrCreateHistoryNode()->AsClassDefinition();
return newNode->capturedVars_.erase(var) != 0;
}
ir::TSEnumDeclaration *OrigEnumDecl() const
{
return GetHistoryNodeAs<ClassDefinition>()->origEnumDecl_;
}
ClassDeclaration *GetAnonClass() noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->anonClass_;
}
const FunctionExpression *Ctor() const;
bool HasPrivateMethod() const;
bool HasNativeMethod() const;
bool HasComputedInstanceField() const;
bool HasMatchingPrivateKey(const util::StringView &name) const;
void TransformBase(const NodeTransformer &cb, std::string_view transformationName);
void TransformChildren(const NodeTransformer &cb, std::string_view transformationName) override;
void Iterate(const NodeTraverser &cb) const override;
void Dump(ir::AstDumper *dumper) const override;
void Dump(ir::SrcDumper *dumper) const override;
void Compile(compiler::PandaGen *pg) const override;
void Compile(compiler::ETSGen *etsg) const override;
checker::Type *Check(checker::TSChecker *checker) override;
checker::VerifiedType Check(checker::ETSChecker *checker) override;
void Accept(ASTVisitorT *v) override
{
v->Accept(this);
}
template <typename T>
static void DumpItems(ir::SrcDumper *dumper, const std::string &prefix, const ArenaVector<T *> &items)
{
if (items.empty()) {
return;
}
dumper->Add(prefix);
for (size_t i = 0; i < items.size(); ++i) {
items[i]->Dump(dumper);
if (i < items.size() - 1) {
dumper->Add(", ");
}
}
}
void CleanUp() override
{
AstNode::CleanUp();
ClearClassModifiers(ClassDefinitionModifiers::CLASSDEFINITION_CHECKED);
}
void AddToExportedClasses(const ir::ClassDeclaration *cls)
{
ES2PANDA_ASSERT(cls->IsExported() || cls->Definition()->IsGlobal());
auto newNode = reinterpret_cast<ClassDefinition *>(this->GetOrCreateHistoryNode());
newNode->exportedClasses_.emplace_back(cls);
}
void BatchAddToExportedClasses(const ArenaVector<const ir::ClassDeclaration *> &classes)
{
for (const auto cls : classes) {
AddToExportedClasses(cls);
}
}
[[nodiscard]] const ArenaVector<const ir::ClassDeclaration *> &ExportedClasses() const noexcept
{
return GetHistoryNodeAs<ClassDefinition>()->exportedClasses_;
}
void SetScope(varbinder::LocalScope *scope);
void SetModifiers(ClassDefinitionModifiers modifiers);
void EmplaceBody(AstNode *body);
void ClearBody();
void SetValueBody(AstNode *body, size_t index);
const ArenaVector<AstNode *> &Body();
[[nodiscard]] ArenaVector<AstNode *> &BodyForUpdate();
void EmplaceImplements(TSClassImplements *implements);
void ClearImplements();
void SetValueImplements(TSClassImplements *implements, size_t index);
void SetImplements(ArenaVector<TSClassImplements *> &&implementsList);
const ArenaVector<TSClassImplements *> &Implements();
ArenaVector<TSClassImplements *> &ImplementsForUpdate();
void SetCtor(MethodDefinition *ctor);
void SetTypeParams(TSTypeParameterDeclaration *typeParams);
void SetOrigEnumDecl(TSEnumDeclaration *origEnumDecl);
void SetAnonClass(ClassDeclaration *anonClass);
void SetInternalName(util::StringView internalName);
protected:
ClassDefinition *Construct(ArenaAllocator *allocator) override;
void AddClassModifiers(ClassDefinitionModifiers const flags) noexcept
{
if (!All(Modifiers(), flags)) {
GetOrCreateHistoryNodeAs<ClassDefinition>()->modifiers_ |= flags;
}
}
void ClearClassModifiers(ClassDefinitionModifiers const flags) noexcept
{
if (Any(Modifiers(), flags)) {
GetOrCreateHistoryNodeAs<ClassDefinition>()->modifiers_ &= ~flags;
}
}
void CopyTo(AstNode *other) const override;
private:
void SetSuperClass(Expression *superClass);
[[nodiscard]] Expression *SuperClass()
{
return GetHistoryNodeAs<ClassDefinition>()->superClass_;
}
[[nodiscard]] const Expression *SuperClass() const
{
return GetHistoryNodeAs<ClassDefinition>()->superClass_;
}
void CompileStaticFieldInitializers(compiler::PandaGen *pg, compiler::VReg classReg,
const std::vector<compiler::VReg> &staticComputedFieldKeys) const;
void DumpBody(ir::SrcDumper *dumper) const;
void DumpGlobalClass(ir::SrcDumper *dumper) const;
void DumpPrefix(ir::SrcDumper *dumper) const;
bool RegisterUnexportedForDeclGen(ir::SrcDumper *dumper) const;
friend class SizeOfNodeTest;
EPtr<varbinder::LocalScope> scope_ {nullptr};
util::StringView internalName_ {};
EPtr<Identifier> ident_ {};
EPtr<TSTypeParameterDeclaration> typeParams_ {};
EPtr<TSTypeParameterInstantiation> superTypeParams_ {};
ArenaVector<TSClassImplements *> implements_;
EPtr<MethodDefinition> ctor_ {};
EPtr<Expression> superClass_ {};
ArenaVector<AstNode *> body_;
ClassDefinitionModifiers modifiers_;
es2panda::Language lang_;
ArenaSet<varbinder::Variable *> capturedVars_;
ArenaSet<varbinder::Variable *> localVariableIsNeeded_;
EPtr<TSEnumDeclaration> origEnumDecl_ {};
EPtr<ClassDeclaration> anonClass_ {nullptr};
static std::atomic<int> classCounter_;
int localIndex_ {};
std::string localPrefix_ {};
EPtr<MethodDefinition> functionalReferenceReferencedMethod_ {};
ArenaVector<const ir::ClassDeclaration *> exportedClasses_;
};
}
#endif