/** * Copyright (c) 2021 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 "binder/binder.h" #include "binder/scope.h" #include "compiler/base/literals.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" #include "typescript/checker.h" #include "ir/astDump.h" #include "ir/base/classProperty.h" #include "ir/base/methodDefinition.h" #include "ir/base/scriptFunction.h" #include "ir/expression.h" #include "ir/expressions/functionExpression.h" #include "ir/expressions/identifier.h" #include "ir/expressions/literals/nullLiteral.h" #include "ir/expressions/literals/numberLiteral.h" #include "ir/expressions/literals/stringLiteral.h" #include "ir/expressions/literals/taggedLiteral.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" #include "util/helpers.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(); } // NOLINTNEXTLINE(google-runtime-references) 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 propNameMap; std::unordered_map 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 it's sendable, put the getters/setters into literal buffer. * 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(name)); literalBuf->Add(nullptr); // save for method internalname literalBuf->Add(nullptr); // save for method affiliate } 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(litTag, internalName); literalBuf->ResetLiteral(bufferPos + 1, value); Literal *methodAffiliate = pg->Allocator()->New(LiteralTag::METHODAFFILIATE, func->Function()->FormalParamsLength()); literalBuf->ResetLiteral(bufferPos + 2, methodAffiliate); // bufferPos + 2 is saved for method affiliate compiled.Set(i); } /* Static items are stored at the end of the buffer */ buf->Insert(&staticBuf); /* The last literal item represents the offset of the first static property. The regular property literal count * is divided by 2 as key/value pairs count as one. */ buf->Add(pg->Allocator()->New(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(LiteralTag::LITERALARRAY, fieldTypeLitId.View())); } 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(LiteralTag::METHODAFFILIATE, func->Function()->FormalParamsLength()); switch (methodDef->Kind()) { case MethodDefinitionKind::METHOD: { value = pg->Allocator()->New(LiteralTag::METHOD, internalName); break; } case MethodDefinitionKind::GET: { value = pg->Allocator()->New(LiteralTag::GETTER, internalName); break; } case MethodDefinitionKind::SET: { value = pg->Allocator()->New(LiteralTag::SETTER, internalName); break; } default: { UNREACHABLE(); } } literalBuf->Add(value); literalBuf->Add(methodAffiliate); } buf->Insert(&staticBuf); buf->Add(pg->Allocator()->New(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(); // Do not process non-static public fields when not using define semantic. 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); } } checker::Type *ClassDefinition::Check(checker::Checker *checker) const { return checker->GlobalAnyType(); } void ClassDefinition::UpdateSelf(const NodeUpdater &cb, binder::Binder *binder) { auto scopeCtx = binder::LexicalScope::Enter(binder, scope_); if (ident_) { ident_ = std::get(cb(ident_))->AsIdentifier(); } if (typeParams_) { typeParams_ = std::get(cb(typeParams_))->AsTSTypeParameterDeclaration(); } if (superClass_) { superClass_ = std::get(cb(superClass_))->AsExpression(); } if (superTypeParams_) { superTypeParams_ = std::get(cb(superTypeParams_))->AsTSTypeParameterInstantiation(); } for (auto iter = implements_.begin(); iter != implements_.end(); iter++) { *iter = std::get(cb(*iter))->AsTSClassImplements(); } ctor_ = std::get(cb(ctor_))->AsMethodDefinition(); for (auto iter = body_.begin(); iter != body_.end(); iter++) { *iter = std::get(cb(*iter))->AsStatement(); } for (auto iter = indexSignatures_.begin(); iter != indexSignatures_.end(); iter++) { *iter = std::get(cb(*iter))->AsTSIndexSignature(); } } void ClassDefinition::BuildClassEnvironment(bool useDefineSemantic) { int instancePrivateMethodCnt = 0; int staticPrivateMethodCnt = 0; int privateFieldCnt = 0; std::vector 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(); // Do not process non-static public fields when not using define semantic. 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); // identify import type 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; } // ts enum type const ir::AstNode *declNode = GetDeclNodeFromIdentifier(typeName->AsIdentifier()); if (declNode != nullptr) { // If the result of "declNode->Original()" is nullptr, it means the declNode is original and not transformed. auto originalNode = (declNode->Original() != nullptr) ? declNode->Original() : declNode; if (originalNode->Type() == ir::AstNodeType::TS_ENUM_DECLARATION) { fieldType |= FieldType::STRING | FieldType::NUMBER; return; } } // sendable class and sendable fuction fieldType |= FieldType::TS_TYPE_REF; } const ir::AstNode *ClassDefinition::GetDeclNodeFromIdentifier(const ir::Identifier *identifier) const { if (identifier == nullptr) { return nullptr; } std::vector variables; variables.reserve(identifier->TSVariables().size() + 1U); variables.emplace_back(identifier->Variable()); for (const auto &v : identifier->TSVariables()) { variables.emplace_back(v); } for (const auto &v : variables) { if (v == nullptr || v->Declaration() == nullptr || v->Declaration()->Node() == nullptr) { continue; } auto res = v->Declaration()->Node(); return res; } 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(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(static_cast(fieldType))); } instanceBuf->Insert(&staticBuf); instanceBuf->Add(pg->Allocator()->New(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()); } } // namespace panda::es2panda::ir