* Copyright (c) 2021-2026 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 "classDefinition.h"
#include "compiler/base/literals.h"
#include "compiler/base/lreference.h"
#include "compiler/core/pandagen.h"
#include "ir/astDump.h"
#include "ir/base/scriptFunction.h"
#include "ir/expressions/assignmentExpression.h"
#include "ir/expressions/functionExpression.h"
#include "ir/expressions/literals/numberLiteral.h"
#include "ir/expressions/literals/stringLiteral.h"
#include "ir/expressions/literals/taggedLiteral.h"
#include "ir/expressions/memberExpression.h"
#include "ir/statements/blockStatement.h"
#include "ir/statements/expressionStatement.h"
#include "ir/ts/tsClassImplements.h"
#include "ir/ts/tsIndexSignature.h"
#include "ir/ts/tsTypeParameter.h"
#include "ir/ts/tsTypeParameterDeclaration.h"
#include "ir/ts/tsTypeParameterInstantiation.h"
#include "ir/ts/tsUnionType.h"
namespace panda::es2panda::ir {
const FunctionExpression *ClassDefinition::Ctor() const
{
ASSERT(ctor_ != nullptr);
return ctor_->Value();
}
util::StringView ClassDefinition::GetName() const
{
if (ident_) {
return ident_->Name();
}
if (exportDefault_) {
return parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME;
}
return "";
}
void ClassDefinition::Iterate(const NodeTraverser &cb) const
{
if (ident_) {
cb(ident_);
}
if (typeParams_) {
cb(typeParams_);
}
if (superClass_) {
cb(superClass_);
}
if (superTypeParams_) {
cb(superTypeParams_);
}
for (auto *it : implements_) {
cb(it);
}
cb(ctor_);
for (auto *it : body_) {
cb(it);
}
for (auto *it : indexSignatures_) {
cb(it);
}
}
void ClassDefinition::Dump(ir::AstDumper *dumper) const
{
dumper->Add({{"id", AstDumper::Nullable(ident_)},
{"typeParameters", AstDumper::Optional(typeParams_)},
{"superClass", AstDumper::Nullable(superClass_)},
{"superTypeParameters", AstDumper::Optional(superTypeParams_)},
{"implements", implements_},
{"constructor", ctor_},
{"body", body_},
{"indexSignatures", indexSignatures_}});
}
compiler::VReg ClassDefinition::CompileHeritageClause(compiler::PandaGen *pg) const
{
compiler::VReg baseReg = pg->AllocReg();
if (superClass_) {
superClass_->Compile(pg);
} else {
pg->LoadConst(this, compiler::Constant::JS_HOLE);
}
pg->StoreAccumulator(this, baseReg);
return baseReg;
}
void ClassDefinition::InitializeClassName(compiler::PandaGen *pg) const
{
if (!ident_) {
return;
}
compiler::LReference lref = compiler::LReference::CreateLRef(pg, ident_, true);
lref.SetValue();
}
int32_t ClassDefinition::CreateClassPublicBuffer(compiler::PandaGen *pg, util::BitSet &compiled,
int32_t fieldTypeBufIdx) const
{
auto *buf = pg->NewLiteralBuffer();
compiler::LiteralBuffer staticBuf(pg->Allocator());
uint32_t instancePropertyCount = 0;
std::unordered_map<util::StringView, size_t> propNameMap;
std::unordered_map<util::StringView, size_t> staticPropNameMap;
const auto &properties = body_;
for (size_t i = 0; i < properties.size(); i++) {
if (!properties[i]->IsMethodDefinition()) {
continue;
}
const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
* If not, break at getters/setters to be compatible with api10. */
if (prop->IsPrivate()) {
continue;
}
if (prop->Computed()) {
break;
}
if (prop->IsAccessor() && !isSendable_) {
break;
}
if (prop->IsAbstract()) {
compiled.Set(i);
continue;
}
if (prop->Value()->Function()->IsOverload()) {
compiled.Set(i);
continue;
}
util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key());
compiler::LiteralBuffer *literalBuf = prop->IsStatic() ? &staticBuf : buf;
auto &nameMap = prop->IsStatic() ? staticPropNameMap : propNameMap;
size_t bufferPos = literalBuf->Literals().size();
auto res = nameMap.insert({name, bufferPos});
if (res.second || isSendable_) {
if (!prop->IsStatic()) {
instancePropertyCount++;
}
literalBuf->Add(pg->Allocator()->New<StringLiteral>(name));
literalBuf->Add(nullptr);
literalBuf->Add(nullptr);
} else {
bufferPos = res.first->second;
}
const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
const util::StringView &internalName = func->Function()->Scope()->InternalName();
LiteralTag litTag = (prop->Kind() == MethodDefinitionKind::METHOD) ? LiteralTag::METHOD :
((prop->Kind() == MethodDefinitionKind::SET) ? LiteralTag::SETTER : LiteralTag::GETTER);
Literal *value = pg->Allocator()->New<TaggedLiteral>(litTag, internalName);
literalBuf->ResetLiteral(bufferPos + 1, value);
Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE,
func->Function()->FormalParamsLength());
literalBuf->ResetLiteral(bufferPos + 2, methodAffiliate);
compiled.Set(i);
}
buf->Insert(&staticBuf);
* is divided by 2 as key/value pairs count as one. */
buf->Add(pg->Allocator()->New<NumberLiteral>(instancePropertyCount));
if (IsSendable()) {
std::string recordName = std::string(pg->Binder()->Program()->RecordName());
std::string fieldTypeIdxStr = recordName + "_" + std::to_string(fieldTypeBufIdx);
util::UString fieldTypeLitId(fieldTypeIdxStr, pg->Allocator());
buf->Add(pg->Allocator()->New<TaggedLiteral>(LiteralTag::LITERALARRAY, fieldTypeLitId.View()));
}
if (IsImplementFromEts()) {
buf->Add(pg->Allocator()->New<TaggedLiteral>(LiteralTag::ETS_IMPLEMENTS, etsImplementsMessage_));
}
return pg->AddLiteralBuffer(buf);
}
int32_t ClassDefinition::CreateClassPrivateBuffer(compiler::PandaGen *pg) const
{
auto *buf = pg->NewLiteralBuffer();
compiler::LiteralBuffer staticBuf(pg->Allocator());
uint32_t instancePropertyCount = 0;
for (const auto *prop : body_) {
if (!prop->IsMethodDefinition()) {
continue;
}
const auto *methodDef = prop->AsMethodDefinition();
if (!methodDef->IsPrivate()) {
continue;
}
compiler::LiteralBuffer *literalBuf = methodDef->IsStatic() ? &staticBuf : (instancePropertyCount++, buf);
const ir::FunctionExpression *func = methodDef->Value()->AsFunctionExpression();
const util::StringView &internalName = func->Function()->Scope()->InternalName();
Literal *value = nullptr;
Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE,
func->Function()->FormalParamsLength());
switch (methodDef->Kind()) {
case MethodDefinitionKind::METHOD: {
value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHOD, internalName);
break;
}
case MethodDefinitionKind::GET: {
value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::GETTER, internalName);
break;
}
case MethodDefinitionKind::SET: {
value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::SETTER, internalName);
break;
}
default: {
UNREACHABLE();
}
}
literalBuf->Add(value);
literalBuf->Add(methodAffiliate);
}
buf->Insert(&staticBuf);
buf->Add(pg->Allocator()->New<NumberLiteral>(instancePropertyCount));
return pg->AddLiteralBuffer(buf);
}
void ClassDefinition::CompileMissingProperties(compiler::PandaGen *pg, const util::BitSet &compiled,
compiler::VReg classReg) const
{
const auto &properties = body_;
compiler::VReg protoReg = pg->AllocReg();
pg->LoadObjByName(this, classReg, "prototype");
pg->StoreAccumulator(this, protoReg);
for (size_t i = 0; i < properties.size(); i++) {
if (!properties[i]->IsMethodDefinition() || compiled.Test(i)) {
continue;
}
const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
if (prop->IsOptional() && prop->Value()->Function()->IsOverload()) {
continue;
}
if (prop->IsPrivate() || prop->IsAbstract()) {
continue;
}
compiler::VReg dest = prop->IsStatic() ? classReg : protoReg;
compiler::RegScope rs(pg);
switch (prop->Kind()) {
case ir::MethodDefinitionKind::METHOD: {
compiler::Operand key = prop->Computed() ? prop->KeyReg() :
pg->ToPropertyKey(prop->Key(), false);
pg->LoadAccumulator(this, dest);
const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
func->Compile(pg);
pg->StoreOwnProperty(prop->Value()->Parent(), dest, key, prop->Computed());
break;
}
case ir::MethodDefinitionKind::GET:
case ir::MethodDefinitionKind::SET: {
CompileGetterOrSetter(pg, dest, prop);
break;
}
default: {
UNREACHABLE();
}
}
}
if (NeedInstanceInitializer()) {
InstanceInitialize(pg, protoReg);
}
}
void ClassDefinition::StaticInitialize(compiler::PandaGen *pg, compiler::VReg classReg) const
{
compiler::VReg callee = pg->AllocReg();
compiler::VReg thisReg = pg->AllocReg();
const ir::FunctionExpression *func = staticInitializer_->Value();
func->Compile(pg);
pg->StoreAccumulator(this, callee);
pg->MoveVreg(this, thisReg, classReg);
pg->CallThis(this, callee, 1);
pg->LoadAccumulator(this, classReg);
}
void ClassDefinition::InstanceInitialize(compiler::PandaGen *pg, compiler::VReg protoReg) const
{
pg->StoreAccumulator(this, protoReg);
instanceInitializer_->Value()->Compile(pg);
pg->StoreLexicalVar(instanceInitializer_, 0, GetSlot(instanceInitializer_->Key()));
}
void ClassDefinition::CompileComputedKeys(compiler::PandaGen *pg) const
{
for (const auto &stmt : body_) {
if (stmt->IsClassProperty()) {
const ir::ClassProperty *prop = stmt->AsClassProperty();
if (!prop->IsStatic() && !pg->Binder()->Program()->UseDefineSemantic()) {
continue;
}
if (prop->IsComputed() && prop->NeedCompileKey()) {
prop->Key()->Compile(pg);
pg->ToComputedPropertyKey(prop->Key());
pg->StoreLexicalVar(prop->Key(), 0, GetSlot(prop->Key()));
}
} else if (stmt->IsMethodDefinition()) {
auto *methodDef = stmt->AsMethodDefinition();
if (methodDef->Computed()) {
compiler::VReg keyReg = pg->AllocReg();
methodDef->SetKeyReg(keyReg);
methodDef->Key()->Compile(pg);
pg->ToComputedPropertyKey(methodDef->Key());
pg->StoreAccumulator(methodDef->Key(), keyReg);
}
}
}
}
void ClassDefinition::Compile(compiler::PandaGen *pg) const
{
if (declare_) {
return;
}
if (isSendable_) {
CompileSendableClass(pg);
return;
}
compiler::RegScope rs(pg);
compiler::VReg classReg = pg->AllocReg();
compiler::LabelTarget target(pg);
compiler::VariableEnvScope classEnvScope(pg, scope_, target);
compiler::VReg baseReg = CompileHeritageClause(pg);
util::StringView ctorId = ctor_->Function()->Scope()->InternalName();
util::BitSet compiled(body_.size());
if (hasComputedKey_) {
CompileComputedKeys(pg);
}
int32_t bufIdx = CreateClassPublicBuffer(pg, compiled);
pg->DefineClassWithBuffer(this, ctorId, bufIdx, baseReg);
pg->StoreAccumulator(this, classReg);
if (HasStaticPrivateMethod()) {
pg->StoreLexicalVar(this, 0, scope_->staticMethodValidation_);
}
InitializeClassName(pg);
CompileMissingProperties(pg, compiled, classReg);
if (hasPrivateElement_) {
int32_t bufIdx = CreateClassPrivateBuffer(pg);
pg->CreatePrivateProperty(this, scope_->privateFieldCnt_, bufIdx);
}
pg->LoadAccumulator(this, classReg);
if (NeedStaticInitializer()) {
StaticInitialize(pg, classReg);
}
}
void ClassDefinition::UpdateSelf(const NodeUpdater &cb, binder::Binder *binder)
{
auto scopeCtx = binder::LexicalScope<binder::ClassScope>::Enter(binder, scope_);
if (ident_) {
ident_ = std::get<ir::AstNode *>(cb(ident_))->AsIdentifier();
}
if (typeParams_) {
typeParams_ = std::get<ir::AstNode *>(cb(typeParams_))->AsTSTypeParameterDeclaration();
}
if (superClass_) {
superClass_ = std::get<ir::AstNode *>(cb(superClass_))->AsExpression();
}
if (superTypeParams_) {
superTypeParams_ = std::get<ir::AstNode *>(cb(superTypeParams_))->AsTSTypeParameterInstantiation();
}
for (auto iter = implements_.begin(); iter != implements_.end(); iter++) {
*iter = std::get<ir::AstNode *>(cb(*iter))->AsTSClassImplements();
}
ctor_ = std::get<ir::AstNode *>(cb(ctor_))->AsMethodDefinition();
for (auto iter = body_.begin(); iter != body_.end(); iter++) {
*iter = std::get<ir::AstNode *>(cb(*iter))->AsStatement();
}
for (auto iter = indexSignatures_.begin(); iter != indexSignatures_.end(); iter++) {
*iter = std::get<ir::AstNode *>(cb(*iter))->AsTSIndexSignature();
}
}
void ClassDefinition::BuildClassEnvironment(bool useDefineSemantic)
{
int instancePrivateMethodCnt = 0;
int staticPrivateMethodCnt = 0;
int privateFieldCnt = 0;
std::vector<const Statement *> privateProperties;
for (const auto *stmt : body_) {
if (stmt->IsMethodDefinition()) {
auto *methodDef = stmt->AsMethodDefinition();
if (methodDef->IsPrivate()) {
privateProperties.push_back(stmt);
methodDef->IsStatic() ? staticPrivateMethodCnt ++ : instancePrivateMethodCnt++;
} else if (methodDef->Computed()) {
hasComputedKey_ = true;
}
continue;
}
if (stmt->IsClassStaticBlock()) {
needStaticInitializer_ = true;
continue;
}
ASSERT(stmt->IsClassProperty());
const auto *prop = stmt->AsClassProperty();
if (!prop->IsPrivate() && !prop->IsStatic() && !useDefineSemantic) {
continue;
}
prop->IsStatic() ? needStaticInitializer_ = true : needInstanceInitializer_ = true;
if (prop->IsComputed() && prop->NeedCompileKey()) {
hasComputedKey_ = true;
scope_->AddClassVariable(prop->Key());
} else if (prop->IsPrivate()) {
privateFieldCnt++;
privateProperties.push_back(stmt);
}
}
if (!privateProperties.empty()) {
hasPrivateElement_ = true;
scope_->AddPrivateName(privateProperties, privateFieldCnt, instancePrivateMethodCnt, staticPrivateMethodCnt);
}
if (instancePrivateMethodCnt > 0) {
needInstanceInitializer_ = true;
}
if (NeedInstanceInitializer()) {
scope_->AddClassVariable(instanceInitializer_->Key());
}
}
void ClassDefinition::AddFieldType(FieldType &fieldType, const Expression *typeAnnotation,
compiler::PandaGen *pg) const
{
switch (typeAnnotation->Type()) {
case AstNodeType::TS_NUMBER_KEYWORD: {
fieldType |= FieldType::NUMBER;
break;
}
case AstNodeType::TS_STRING_KEYWORD: {
fieldType |= FieldType::STRING;
break;
}
case AstNodeType::TS_BOOLEAN_KEYWORD: {
fieldType |= FieldType::BOOLEAN;
break;
}
case AstNodeType::TS_TYPE_REFERENCE: {
AddFieldTypeForTypeReference(typeAnnotation->AsTSTypeReference(), fieldType, pg);
break;
}
case AstNodeType::TS_BIGINT_KEYWORD: {
fieldType |= FieldType::BIGINT;
break;
}
case AstNodeType::TS_NULL_KEYWORD: {
fieldType |= FieldType::TS_NULL;
break;
}
case AstNodeType::TS_UNDEFINED_KEYWORD: {
fieldType |= FieldType::TS_UNDEFINED;
break;
}
default: {
UNREACHABLE();
}
}
}
void ClassDefinition::AddFieldTypeForTypeReference(const TSTypeReference *typeReference, FieldType &fieldType,
compiler::PandaGen *pg) const
{
auto typeName = typeReference->TypeName();
ASSERT(typeName != nullptr);
if (!typeName->IsIdentifier()) {
fieldType |= FieldType::GENERIC;
return;
}
util::StringView propertyName = typeName->AsIdentifier()->Name();
binder::ScopeFindResult result = scope_->Find(propertyName);
parser::SourceTextModuleRecord *moduleRecord = pg->Binder()->Program()->TypeModuleRecord();
const auto ®ularImportEntries = moduleRecord->GetRegularImportEntries();
if (regularImportEntries.find(propertyName) != regularImportEntries.end()) {
fieldType |= FieldType::GENERIC;
return;
}
if (IsTypeParam(propertyName) || (result.variable != nullptr && result.variable->IsModuleVariable())) {
fieldType |= FieldType::GENERIC;
return;
}
const ir::AstNode *declNode = GetDeclNodeFromIdentifier(typeName->AsIdentifier());
if (declNode != nullptr && declNode->IsTSEnumDeclaration()) {
fieldType |= FieldType::STRING | FieldType::NUMBER;
return;
}
fieldType |= FieldType::TS_TYPE_REF;
}
const ir::AstNode *ClassDefinition::GetDeclNodeFromIdentifier(const ir::Identifier *identifier) const
{
if (identifier == nullptr) {
return nullptr;
}
for (const auto &v : identifier->TSVariables()) {
if (v == nullptr || v->Declaration() == nullptr || v->Declaration()->Node() == nullptr) {
continue;
}
auto res = v->Declaration()->Node();
return res;
}
if (identifier->Variable() && identifier->Variable()->Declaration() &&
identifier->Variable()->Declaration()->Node()) {
return identifier->Variable()->Declaration()->Node()->Original();
}
return nullptr;
}
bool ClassDefinition::IsTypeParam(const util::StringView &propertyName) const
{
if (typeParams_ == nullptr) {
return false;
}
for (auto param : typeParams_->Params()) {
util::StringView paramName = param->Name()->AsIdentifier()->Name();
if (paramName == propertyName) {
return true;
}
}
return false;
}
int32_t ClassDefinition::CreateFieldTypeBuffer(compiler::PandaGen *pg) const
{
ASSERT(IsSendable());
auto *instanceBuf = pg->NewLiteralBuffer();
compiler::LiteralBuffer staticBuf(pg->Allocator());
uint32_t instanceFieldCnt {0};
for (auto *prop : body_) {
if (!prop->IsClassProperty()) {
continue;
}
auto *classProp = prop->AsClassProperty();
auto *buf = classProp->IsStatic() ? &staticBuf : (++instanceFieldCnt, instanceBuf);
auto name = util::Helpers::LiteralToPropName(pg->Allocator(), classProp->Key());
buf->Add(pg->Allocator()->New<StringLiteral>(name));
FieldType fieldType = FieldType::NONE;
const auto *typeAnnotation = classProp->TypeAnnotation();
if (typeAnnotation == nullptr) {
util::Helpers::ThrowError(ErrorType::GENERIC, pg->Binder()->Program(), prop->Start(),
"Field in sendable class must have type annotation");
}
if (typeAnnotation->IsTSUnionType()) {
for (const auto *type : typeAnnotation->AsTSUnionType()->Types()) {
AddFieldType(fieldType, type, pg);
}
} else {
AddFieldType(fieldType, typeAnnotation, pg);
}
buf->Add(pg->Allocator()->New<NumberLiteral>(static_cast<uint8_t>(fieldType)));
}
instanceBuf->Insert(&staticBuf);
instanceBuf->Add(pg->Allocator()->New<NumberLiteral>(instanceFieldCnt));
return pg->AddLiteralBuffer(instanceBuf);
}
void ClassDefinition::CompileSendableClass(compiler::PandaGen *pg) const
{
compiler::RegScope rs(pg);
compiler::VReg classReg = pg->AllocReg();
compiler::LocalRegScope lrs(pg, scope_);
compiler::VReg baseReg = CompileHeritageClause(pg);
util::StringView ctorId = ctor_->Function()->Scope()->InternalName();
util::BitSet compiled(body_.size());
int32_t fieldTypeBufIdx = CreateFieldTypeBuffer(pg);
int32_t bufIdx = CreateClassPublicBuffer(pg, compiled, fieldTypeBufIdx);
pg->DefineSendableClass(this, ctorId, bufIdx, baseReg);
pg->StoreAccumulator(this, classReg);
InitializeClassName(pg);
if (NeedStaticInitializer()) {
StaticInitialize(pg, classReg);
}
}
void ClassDefinition::CompileGetterOrSetter(compiler::PandaGen *pg, compiler::VReg dest,
const MethodDefinition *prop) const
{
compiler::VReg keyReg = prop->Computed() ? prop->KeyReg() : pg->LoadPropertyKey(prop->Key(), false);
compiler::VReg undef = pg->AllocReg();
pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
pg->StoreAccumulator(this, undef);
compiler::VReg getter = undef;
compiler::VReg setter = undef;
pg->LoadAccumulator(this, dest);
compiler::VReg accessor = pg->AllocReg();
prop->Value()->Compile(pg);
pg->StoreAccumulator(prop->Value(), accessor);
if (prop->Kind() == ir::MethodDefinitionKind::GET) {
getter = accessor;
} else {
setter = accessor;
}
pg->DefineGetterSetterByValue(this, dest, keyReg, getter, setter, prop->Computed());
}
void ClassDefinition::CalculateClassExpectedPropertyCount()
{
std::unordered_set<util::StringView> propertyNames;
auto addPropertyName = [this, &propertyNames](const util::StringView &name) {
if (propertyNames.find(name) == propertyNames.end()) {
propertyNames.insert(name);
IncreasePropertyCount();
}
};
for (const auto &stmt : Body()) {
if (stmt->IsClassProperty() && !stmt->AsClassProperty()->IsStatic()) {
ProcessClassProperty(stmt->AsClassProperty(), addPropertyName);
}
}
auto *ctorFunc = ctor_->Function();
if (ctorFunc && ctorFunc->Body()) {
ProcessConstructorBody(ctorFunc->Body()->AsBlockStatement(), addPropertyName);
}
}
void ClassDefinition::ProcessClassProperty(const ClassProperty *prop,
const std::function<void(const util::StringView&)>& addPropertyName)
{
* Private fields must be explicitly declared in the class and cannot be directly initialized in the constructor,
* so they only need to be accounted for in the class properties.
*/
if (prop->IsPrivate()) {
IncreasePropertyCount();
} else {
ProcessPropertyKey(prop->Key(), addPropertyName);
}
}
void ClassDefinition::ProcessConstructorBody(const BlockStatement *body,
const std::function<void(const util::StringView&)>& addPropertyName)
{
for (const auto &stmt : body->Statements()) {
if (!stmt->IsExpressionStatement()) {
continue;
}
auto *expr = stmt->AsExpressionStatement()->GetExpression();
if (!expr->IsAssignmentExpression()) {
continue;
}
auto *assignExpr = expr->AsAssignmentExpression();
if (!assignExpr->Left()->IsMemberExpression()) {
continue;
}
auto *memberExpr = assignExpr->Left()->AsMemberExpression();
if (memberExpr->Object()->IsThisExpression()) {
ProcessPropertyKey(memberExpr->Property(), addPropertyName);
}
}
}
void ClassDefinition::ProcessPropertyKey(const Expression* key,
const std::function<void(const util::StringView&)>& addPropertyName)
{
if (key->IsIdentifier()) {
addPropertyName(key->AsIdentifier()->Name());
} else if (key->IsStringLiteral()) {
addPropertyName(key->AsStringLiteral()->Str());
} else if (key->IsNumberLiteral()) {
addPropertyName(key->AsNumberLiteral()->Str());
} else {
* Currently, other situations of property keys are not counted.
* 1. Private properties (PrivateIdentifier)
* 2. Template literals (TemplateLiteral)
* 3. Binary expressions (BinaryExpression)
* 4. Other expressions that can be evaluated to a property key
*/
}
}
}