* 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.
*/
#include "classProperty.h"
#include "checker/ETSchecker.h"
#include "checker/TSchecker.h"
#include "compiler/core/ETSGen.h"
#include "compiler/core/pandagen.h"
#include "compiler/lowering/util.h"
#include "public/public.h"
namespace ark::es2panda::ir {
void ClassProperty::SetTypeAnnotation(TypeNode *typeAnnotation)
{
this->GetOrCreateHistoryNodeAs<ClassProperty>()->typeAnnotation_ = typeAnnotation;
}
void ClassProperty::SetDefaultAccessModifier(bool isDefault)
{
this->GetOrCreateHistoryNodeAs<ClassProperty>()->isDefault_ = isDefault;
}
void ClassProperty::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
{
auto *key = Key();
if (key != nullptr) {
if (auto *transformedNode = cb(key); key != transformedNode) {
key->SetTransformedNode(transformationName, transformedNode);
SetKey(transformedNode->AsExpression());
}
}
auto *value = Value();
if (value != nullptr) {
if (auto *transformedNode = cb(value); value != transformedNode) {
value->SetTransformedNode(transformationName, transformedNode);
SetValue(transformedNode->AsExpression());
}
}
auto *typeAnnotation = TypeAnnotation();
if (typeAnnotation != nullptr) {
if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
typeAnnotation->SetTransformedNode(transformationName, transformedNode);
SetTypeAnnotation(static_cast<TypeNode *>(transformedNode));
}
}
TransformAnnotations(cb, transformationName);
}
void ClassProperty::Iterate(const NodeTraverser &cb) const
{
auto const key = GetHistoryNode()->AsClassProperty()->key_;
cb(key);
auto const value = GetHistoryNode()->AsClassProperty()->value_;
if (value != nullptr) {
cb(value);
}
if (TypeAnnotation() != nullptr) {
cb(TypeAnnotation());
}
IterateAnnotations(cb);
}
void ClassProperty::Dump(ir::AstDumper *dumper) const
{
dumper->Add({{"type", "ClassProperty"},
{"key", Key()},
{"value", AstDumper::Optional(Value())},
{"accessibility", AstDumper::Optional(AstDumper::ModifierToString(Modifiers()))},
{"static", IsStatic()},
{"readonly", IsReadonly()},
{"declare", IsDeclare()},
{"optional", IsOptionalDeclaration()},
{"computed", IsComputed()},
{"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
{"definite", IsDefinite()},
{"annotations", AstDumper::Optional(Annotations())}});
}
void ClassProperty::DumpModifiers(ir::SrcDumper *dumper) const
{
ES2PANDA_ASSERT(key_);
if (Parent() != nullptr) {
if (compiler::HasGlobalClassParent(this)) {
if (IsExported()) {
dumper->Add("export ");
}
if (dumper->IsDeclgen()) {
dumper->Add("declare ");
}
if (IsConst()) {
dumper->Add("const ");
} else {
dumper->Add("let ");
}
return;
}
if (Parent()->IsClassDefinition() && !Parent()->AsClassDefinition()->IsLocal()) {
if (IsPrivate()) {
dumper->Add("private ");
} else if (IsProtected()) {
dumper->Add("protected ");
} else {
dumper->Add("public ");
}
}
}
if (IsStatic()) {
dumper->Add("static ");
}
if (IsReadonly()) {
dumper->Add("readonly ");
}
}
bool ClassProperty::DumpNamespaceForDeclGen(ir::SrcDumper *dumper) const
{
if (!dumper->IsDeclgen()) {
return false;
}
if (Parent() == nullptr) {
return false;
}
bool isNamespaceTransformed =
Parent()->IsClassDefinition() && Parent()->AsClassDefinition()->IsNamespaceTransformed();
if (isNamespaceTransformed) {
dumper->Add("let ");
return true;
}
return false;
}
void ClassProperty::DumpPrefix(ir::SrcDumper *dumper) const
{
DumpAnnotations(dumper);
if (DumpNamespaceForDeclGen(dumper)) {
return;
}
DumpModifiers(dumper);
}
void ClassProperty::DumpCheckerTypeForDeclGen(ir::SrcDumper *dumper) const
{
ES2PANDA_ASSERT(dumper->IsDeclgen());
if (TsType() == nullptr) {
return;
}
dumper->Add(": ");
if (TsType()->Variable() != nullptr) {
ES2PANDA_ASSERT(TsType()->Variable()->Declaration() != nullptr &&
TsType()->Variable()->Declaration()->Node() != nullptr &&
TsType()->Variable()->Declaration()->Node()->Scope() != nullptr);
varbinder::Scope *tsTypeParentScope = TsType()->Variable()->Declaration()->Node()->Scope()->Parent();
auto scopes = compiler::DiffClassScopes(compiler::NearestScope(this), tsTypeParentScope);
std::stringstream namespaces;
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
namespaces << (*it)->Node()->AsClassDefinition()->Ident()->Name() << ".";
}
dumper->Add(namespaces.str());
}
dumper->GetDeclgen()->Dump(dumper, TsType());
}
bool ClassProperty::RegisterUnexportedForDeclGen(ir::SrcDumper *dumper) const
{
ES2PANDA_ASSERT(key_);
ES2PANDA_ASSERT(dumper->IsDeclgen());
auto name = key_->AsIdentifier()->Name().Mutf8();
if (name.rfind('#', 0) == 0) {
return true;
}
if (IsPrivate()) {
return true;
}
if (!compiler::HasGlobalClassParent(this)) {
return false;
}
if (dumper->GetDeclgen()->IsPostDumpIndirectDepsPhase()) {
return false;
}
if (key_->Parent()->IsExported() || key_->Parent()->IsDefaultExported()) {
return false;
}
dumper->GetDeclgen()->AddNode(name, this);
return true;
}
void ClassProperty::Dump(ir::SrcDumper *dumper) const
{
bool isNamespaceTransformed =
Parent()->IsClassDefinition() && Parent()->AsClassDefinition()->IsNamespaceTransformed();
if (dumper->IsDeclgen() && !(IsExported() || IsDefaultExported() || (!isNamespaceTransformed && !IsPrivate()))) {
return;
}
if (dumper->IsDeclgen() && RegisterUnexportedForDeclGen(dumper)) {
return;
}
ForceDump(dumper);
}
void ClassProperty::ForceDump(ir::SrcDumper *dumper) const
{
dumper->DumpJsdocBeforeTargetNode(this);
DumpPrefix(dumper);
if (Key() != nullptr) {
Key()->Dump(dumper);
}
if (IsOptionalDeclaration()) {
dumper->Add("?");
}
if (IsDefinite()) {
dumper->Add("!");
}
if (typeAnnotation_ != nullptr) {
dumper->Add(": ");
TypeAnnotation()->Dump(dumper);
} else if (dumper->IsDeclgen()) {
DumpCheckerTypeForDeclGen(dumper);
}
if (value_ != nullptr) {
if (!dumper->IsDeclgen() || (Parent() != nullptr && Parent()->IsAnnotationDeclaration())) {
dumper->Add(" = ");
Value()->Dump(dumper);
}
}
dumper->Add(";");
}
void ClassProperty::Compile(compiler::PandaGen *pg) const
{
pg->GetAstCompiler()->Compile(this);
}
void ClassProperty::Compile(compiler::ETSGen *etsg) const
{
etsg->GetAstCompiler()->Compile(this);
}
checker::Type *ClassProperty::Check(checker::TSChecker *checker)
{
return checker->GetAnalyzer()->Check(this);
}
checker::VerifiedType ClassProperty::Check(checker::ETSChecker *checker)
{
return {this, checker->GetAnalyzer()->Check(this)};
}
ClassProperty *ClassProperty::Clone(ArenaAllocator *const allocator, AstNode *const parent)
{
auto *const key = Key()->Clone(allocator, nullptr)->AsExpression();
auto *const value = Value() != nullptr ? Value()->Clone(allocator, nullptr)->AsExpression() : nullptr;
auto *const typeAnnotation = TypeAnnotation() != nullptr ? TypeAnnotation()->Clone(allocator, nullptr) : nullptr;
auto *const clone = allocator->New<ClassProperty>(key, value, typeAnnotation, Modifiers(), allocator, IsComputed());
if (parent != nullptr) {
clone->SetParent(parent);
}
key->SetParent(clone);
if (value != nullptr) {
value->SetTsType(Value()->TsType());
value->SetParent(clone);
}
if (typeAnnotation != nullptr) {
typeAnnotation->SetTsType(typeAnnotation->TsType());
typeAnnotation->SetParent(clone);
}
if (HasAnnotations()) {
clone->SetAnnotations(Annotations());
}
clone->SetBasePropertyVar(BasePropertyVar());
clone->SetRange(Range());
return clone;
}
ClassProperty *ClassProperty::Construct(ArenaAllocator *allocator)
{
return allocator->New<ClassProperty>(nullptr, nullptr, nullptr, ModifierFlags::NONE, allocator, false);
}
void ClassProperty::CopyTo(AstNode *other) const
{
auto otherImpl = other->AsClassProperty();
otherImpl->typeAnnotation_ = typeAnnotation_;
otherImpl->basePropertyVar_ = basePropertyVar_;
otherImpl->isDefault_ = isDefault_;
otherImpl->initMode_ = initMode_;
AnnotationAllowed<ClassElement>::CopyTo(other);
}
}