diff --git a/ets2panda/checker/TSchecker.h b/ets2panda/checker/TSchecker.h index 6879b76e214f9e2c42bad5406f266a730d37865a..c3bb88f34a81a9c91646943a84a2a27223318c54 100644 --- a/ets2panda/checker/TSchecker.h +++ b/ets2panda/checker/TSchecker.h @@ -389,6 +389,8 @@ private: // Binary like expression void CheckBooleanLikeType(Type *leftType, Type *rightType, ir::AstNode *expr, lexer::TokenType op); + + void CheckExtendsBases(ObjectType *&baseObj, InterfaceType *&type, varbinder::InterfaceDecl *&decl); }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ts/object.cpp b/ets2panda/checker/ts/object.cpp index 793103f5a1a4bad31b8c44503bbc36d0e7328d1c..7116a8a5e3e67df92e466929abe3fab5ad75bbaa 100644 --- a/ets2panda/checker/ts/object.cpp +++ b/ets2panda/checker/ts/object.cpp @@ -478,13 +478,7 @@ ArenaVector TSChecker::GetBaseTypes(InterfaceType *type) continue; } - ArenaVector extendsBases = GetBaseTypes(baseObj->AsInterfaceType()); - for (auto *extendBase : extendsBases) { - if (extendBase == type) { - ThrowTypeError({"Type ", type->Name(), " recursively references itself as a base type."}, - decl->Node()->AsTSInterfaceDeclaration()->Id()->Start()); - } - } + CheckExtendsBases(baseObj, type, decl); } } @@ -492,6 +486,17 @@ ArenaVector TSChecker::GetBaseTypes(InterfaceType *type) return type->Bases(); } +void TSChecker::CheckExtendsBases(ObjectType *&baseObj, InterfaceType *&type, varbinder::InterfaceDecl *&decl) +{ + ArenaVector extendsBases = GetBaseTypes(baseObj->AsInterfaceType()); + for (auto *extendBase : extendsBases) { + if (extendBase == type) { + ThrowTypeError({"Type ", type->Name(), " recursively references itself as a base type."}, + decl->Node()->AsTSInterfaceDeclaration()->Id()->Start()); + } + } +} + void TSChecker::ResolveDeclaredMembers(InterfaceType *type) { if (type->HasObjectFlag(ObjectFlags::RESOLVED_DECLARED_MEMBERS)) { diff --git a/ets2panda/checker/types/globalTypesHolder.cpp b/ets2panda/checker/types/globalTypesHolder.cpp index afb9eb0ecea2f44ace50318915c36070c780cab1..1948e5c421dbc09857cc232747ec76c4e9b81d62 100644 --- a/ets2panda/checker/types/globalTypesHolder.cpp +++ b/ets2panda/checker/types/globalTypesHolder.cpp @@ -50,9 +50,25 @@ #include "util/helpers.h" namespace ark::es2panda::checker { -GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) : builtinNameMappings_(allocator->Adapter()) + +void GlobalTypesHolder::AddETSEscompatLayer() +{ + // ETS escompat layer + builtinNameMappings_.emplace("Array", GlobalTypeId::ETS_ARRAY_BUILTIN); + builtinNameMappings_.emplace("Date", GlobalTypeId::ETS_DATE_BUILTIN); + builtinNameMappings_.emplace("Error", GlobalTypeId::ETS_ERROR_BUILTIN); + builtinNameMappings_.emplace("OutOfMemoryError", GlobalTypeId::ETS_OUT_OF_MEMORY_ERROR_BUILTIN); + builtinNameMappings_.emplace("NoSuchMethodError", GlobalTypeId::ETS_NO_SUCH_METHOD_ERROR_BUILTIN); + builtinNameMappings_.emplace("DivideByZeroError", GlobalTypeId::ETS_DIVIDE_BY_ZERO_ERROR_BUILTIN); + builtinNameMappings_.emplace("NullPointerError", GlobalTypeId::ETS_NULL_POINTER_ERROR_BUILTIN); + builtinNameMappings_.emplace("UncaughtExceptionError", GlobalTypeId::ETS_UNCAUGHT_EXCEPTION_ERROR_BUILTIN); + builtinNameMappings_.emplace("Map", GlobalTypeId::ETS_MAP_BUILTIN); + builtinNameMappings_.emplace("RegExp", GlobalTypeId::ETS_REGEXP_BUILTIN); + builtinNameMappings_.emplace("Set", GlobalTypeId::ETS_SET_BUILTIN); +} + +void GlobalTypesHolder::AddTSSpecificTypes(ArenaAllocator *allocator) { - // TS specific types globalTypes_[static_cast(GlobalTypeId::NUMBER)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ANY)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::STRING)] = allocator->New(); @@ -81,6 +97,12 @@ GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) : builtinNameMap globalTypes_[static_cast(GlobalTypeId::EMPTY_OBJECT)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::RESOLVING_RETURN_TYPE)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ERROR_TYPE)] = allocator->New(); +} + +GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) : builtinNameMappings_(allocator->Adapter()) +{ + // TS specific types + AddTSSpecificTypes(allocator); // ETS specific types globalTypes_[static_cast(GlobalTypeId::BYTE)] = allocator->New(); @@ -137,17 +159,7 @@ GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) : builtinNameMap builtinNameMappings_.emplace("never", GlobalTypeId::ETS_NEVER_BUILTIN); // ETS escompat layer - builtinNameMappings_.emplace("Array", GlobalTypeId::ETS_ARRAY_BUILTIN); - builtinNameMappings_.emplace("Date", GlobalTypeId::ETS_DATE_BUILTIN); - builtinNameMappings_.emplace("Error", GlobalTypeId::ETS_ERROR_BUILTIN); - builtinNameMappings_.emplace("OutOfMemoryError", GlobalTypeId::ETS_OUT_OF_MEMORY_ERROR_BUILTIN); - builtinNameMappings_.emplace("NoSuchMethodError", GlobalTypeId::ETS_NO_SUCH_METHOD_ERROR_BUILTIN); - builtinNameMappings_.emplace("DivideByZeroError", GlobalTypeId::ETS_DIVIDE_BY_ZERO_ERROR_BUILTIN); - builtinNameMappings_.emplace("NullPointerError", GlobalTypeId::ETS_NULL_POINTER_ERROR_BUILTIN); - builtinNameMappings_.emplace("UncaughtExceptionError", GlobalTypeId::ETS_UNCAUGHT_EXCEPTION_ERROR_BUILTIN); - builtinNameMappings_.emplace("Map", GlobalTypeId::ETS_MAP_BUILTIN); - builtinNameMappings_.emplace("RegExp", GlobalTypeId::ETS_REGEXP_BUILTIN); - builtinNameMappings_.emplace("Set", GlobalTypeId::ETS_SET_BUILTIN); + AddETSEscompatLayer(); // ETS functional types for (size_t id = static_cast(GlobalTypeId::ETS_FUNCTION0_CLASS), nargs = 0; diff --git a/ets2panda/checker/types/globalTypesHolder.h b/ets2panda/checker/types/globalTypesHolder.h index 6e899448c12436d1fe33aeffcbdebcadf5e89f8e..9f6d37f94b188c3694fc60f4793401ed5c038372 100644 --- a/ets2panda/checker/types/globalTypesHolder.h +++ b/ets2panda/checker/types/globalTypesHolder.h @@ -141,6 +141,9 @@ public: NO_COPY_SEMANTIC(GlobalTypesHolder); NO_MOVE_SEMANTIC(GlobalTypesHolder); + void AddETSEscompatLayer(); + void AddTSSpecificTypes(ArenaAllocator *allocator); + // TS specific types Type *GlobalNumberType(); Type *GlobalAnyType(); diff --git a/ets2panda/checker/types/signature.cpp b/ets2panda/checker/types/signature.cpp index 7587a5c3d11cf5536aad1063f5f6d3da1f3bd8ec..082bd8e3b2ae72f69d7ec734bcf80c053199fc7f 100644 --- a/ets2panda/checker/types/signature.cpp +++ b/ets2panda/checker/types/signature.cpp @@ -73,12 +73,10 @@ Signature *Signature::Substitute(TypeRelation *relation, const Substitution *sub } auto *newReturnType = returnType_->Substitute(relation, substitution); - if (newReturnType != returnType_) { - anyChange = true; - } - if (!anyChange) { + if (newReturnType == returnType_ && !anyChange) { return this; } + auto *result = allocator->New(newSigInfo, newReturnType); result->func_ = func_; result->flags_ = flags_; diff --git a/ets2panda/checker/types/ts/interfaceType.cpp b/ets2panda/checker/types/ts/interfaceType.cpp index 0fb31225778217423599660992173c0a25cf88be..4ec9eaf491b5de91a4488acfa979e00f8e221396 100644 --- a/ets2panda/checker/types/ts/interfaceType.cpp +++ b/ets2panda/checker/types/ts/interfaceType.cpp @@ -42,6 +42,29 @@ void InterfaceType::ToString(std::stringstream &ss, [[maybe_unused]] bool precis } } +bool InterfaceType::CheckVarType(TypeRelation *relation, + const ArenaVector &targetProperties, + const ArenaVector &sourceProperties) +{ + for (auto *targetProp : targetProperties) { + bool foundProp = std::any_of(sourceProperties.begin(), sourceProperties.end(), + [targetProp, relation](varbinder::LocalVariable *sourceProp) { + if (targetProp->Name() == sourceProp->Name()) { + Type *targetType = relation->GetChecker()->GetTypeOfVariable(targetProp); + Type *sourceType = relation->GetChecker()->GetTypeOfVariable(sourceProp); + return relation->IsIdenticalTo(targetType, sourceType); + } + + return false; + }); + if (!foundProp) { + relation->Result(false); + return true; + } + } + return false; +} + void InterfaceType::Identical(TypeRelation *relation, Type *other) { if (!other->IsObjectType() || !other->AsObjectType()->IsInterfaceType()) { @@ -58,21 +81,8 @@ void InterfaceType::Identical(TypeRelation *relation, Type *other) return; } - for (auto *targetProp : targetProperties) { - bool foundProp = std::any_of(sourceProperties.begin(), sourceProperties.end(), - [targetProp, relation](varbinder::LocalVariable *sourceProp) { - if (targetProp->Name() == sourceProp->Name()) { - Type *targetType = relation->GetChecker()->GetTypeOfVariable(targetProp); - Type *sourceType = relation->GetChecker()->GetTypeOfVariable(sourceProp); - return relation->IsIdenticalTo(targetType, sourceType); - } - - return false; - }); - if (!foundProp) { - relation->Result(false); - return; - } + if (CheckVarType(relation, targetProperties, sourceProperties)) { + return; } const ArenaVector &targetCallSignatures = CallSignatures(); @@ -111,6 +121,11 @@ void InterfaceType::Identical(TypeRelation *relation, Type *other) relation->IsIdenticalTo(targetNumberInfo, sourceNumberInfo); + CheckStringInfo(relation, otherInterface); +} + +void InterfaceType::CheckStringInfo(TypeRelation *relation, InterfaceType *otherInterface) +{ if (relation->IsTrue()) { IndexInfo *targetStringInfo = StringIndexInfo(); IndexInfo *sourceStringInfo = otherInterface->StringIndexInfo(); diff --git a/ets2panda/checker/types/ts/interfaceType.h b/ets2panda/checker/types/ts/interfaceType.h index e0ff3a1ae1d51f4f89ffdbdc715b0fdd82a355b9..ced99ea53aa61563a61209011503ba8ffb044b01 100644 --- a/ets2panda/checker/types/ts/interfaceType.h +++ b/ets2panda/checker/types/ts/interfaceType.h @@ -130,6 +130,9 @@ public: void ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const override; TypeFacts GetTypeFacts() const override; + void CheckStringInfo(TypeRelation *relation, InterfaceType *otherInterface); + bool CheckVarType(TypeRelation *relation, const ArenaVector &targetProperties, + const ArenaVector &sourceProperties); void Identical(TypeRelation *relation, Type *other) override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; diff --git a/ets2panda/checker/types/ts/objectType.cpp b/ets2panda/checker/types/ts/objectType.cpp index a50001c4c12417c4566fb46a1272ac9f7b08dac4..c50eeeb98ae62435986f760dbec599f29b7918f0 100644 --- a/ets2panda/checker/types/ts/objectType.cpp +++ b/ets2panda/checker/types/ts/objectType.cpp @@ -47,6 +47,29 @@ bool ObjectType::SignatureRelatedToSomeSignature(TypeRelation *relation, Signatu return false; } +bool ObjectType::FindPropertyAndCheckIdentical(TypeRelation *relation, ObjectType *otherObj) +{ + for (auto *it : desc_->properties) { + varbinder::LocalVariable *found = otherObj->Desc()->FindProperty(it->Name()); + if (found == nullptr) { + relation->Result(false); + return true; + } + + relation->IsIdenticalTo(it->TsType(), found->TsType()); + + if (!relation->IsTrue()) { + return true; + } + + if (it->Flags() != found->Flags()) { + relation->Result(false); + return true; + } + } + return false; +} + void ObjectType::Identical(TypeRelation *relation, Type *other) { if (!other->IsObjectType() || kind_ != other->AsObjectType()->Kind()) { @@ -66,23 +89,8 @@ void ObjectType::Identical(TypeRelation *relation, Type *other) return; } - for (auto *it : desc_->properties) { - varbinder::LocalVariable *found = otherObj->Desc()->FindProperty(it->Name()); - if (found == nullptr) { - relation->Result(false); - return; - } - - relation->IsIdenticalTo(it->TsType(), found->TsType()); - - if (!relation->IsTrue()) { - return; - } - - if (it->Flags() != found->Flags()) { - relation->Result(false); - return; - } + if (FindPropertyAndCheckIdentical(relation, otherObj)) { + return; } if (!EachSignatureRelatedToSomeSignature(relation, CallSignatures(), otherObj->CallSignatures()) || diff --git a/ets2panda/checker/types/ts/objectType.h b/ets2panda/checker/types/ts/objectType.h index 15c17683d38d37fce36574644cbe4e808b22c86d..73322313994d80b69d8f7fc7bdd09ed6e35852f8 100644 --- a/ets2panda/checker/types/ts/objectType.h +++ b/ets2panda/checker/types/ts/objectType.h @@ -193,6 +193,7 @@ public: const ArenaVector &sourceSignatures, const ArenaVector &targetSignatures); + bool FindPropertyAndCheckIdentical(TypeRelation *relation, ObjectType *otherObj); void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 42ab4ece2074447bad5bccc98ca4ce48e967f1ae..2e0e177454e25f6292c51fb59d964b5e4b137022 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -300,7 +300,7 @@ static void CreateDynamicObject(const ir::AstNode *node, compiler::ETSGen *etsg, etsg->StoreAccumulator(node, objReg); auto [qnameStart, qnameLen] = LoadDynamicName(etsg, node, callInfo.name, true); - etsg->CallDynamic(node, objReg, qnameStart, qnameLen, signature, arguments); + etsg->CallDynamic(ETSGen::CallDynamicData {node, objReg, qnameStart}, qnameLen, signature, arguments); } static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::ETSNewClassInstanceExpression *expr) @@ -857,7 +857,8 @@ void ETSCompiler::CompileDynamic(const ir::CallExpression *expr, compiler::VReg if (!callInfo.name.empty()) { auto [qnameStart, qnameLen] = LoadDynamicName(etsg, expr, callInfo.name, false); - etsg->CallDynamic(expr, calleeReg, qnameStart, qnameLen, expr->Signature(), expr->Arguments()); + etsg->CallDynamic(ETSGen::CallDynamicData {expr, calleeReg, qnameStart}, qnameLen, expr->Signature(), + expr->Arguments()); } else { compiler::VReg dynParam2 = etsg->AllocReg(); @@ -866,7 +867,7 @@ void ETSCompiler::CompileDynamic(const ir::CallExpression *expr, compiler::VReg : expr->Callee()->TsType()->AsETSDynamicType()->Language(); etsg->LoadUndefinedDynamic(expr, lang); etsg->StoreAccumulator(expr, dynParam2); - etsg->CallDynamic(expr, calleeReg, dynParam2, expr->Signature(), expr->Arguments()); + etsg->CallDynamic(ETSGen::CallDynamicData {expr, calleeReg, dynParam2}, expr->Signature(), expr->Arguments()); } etsg->SetAccumulatorType(expr->Signature()->ReturnType()); @@ -1210,7 +1211,7 @@ void ETSCompiler::Compile(const ir::ObjectExpression *expr) const auto *createObjSig = etsg->Allocator()->New( signatureInfo, nullptr, compiler::Signatures::BUILTIN_JSRUNTIME_CREATE_OBJECT); compiler::VReg dummyReg = compiler::VReg::RegStart(); - etsg->CallDynamic(expr, dummyReg, dummyReg, createObjSig, + etsg->CallDynamic(ETSGen::CallDynamicData {expr, dummyReg, dummyReg}, createObjSig, ArenaVector(etsg->Allocator()->Adapter())); etsg->SetAccumulatorType(expr->TsType()); diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index 0d89ea03ca6f3835c5ebb6d7eaf4756972e22790..1b7a2ab00d7104fdcb80c7474609aaf34630c956 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -609,16 +609,22 @@ public: Ra().Emit(node, name, athis, arg0); } - void CallDynamic(const ir::AstNode *node, VReg obj, VReg param2, checker::Signature *signature, + struct CallDynamicData { + const ir::AstNode *node = nullptr; + VReg obj; + VReg param2; + }; + + void CallDynamic(CallDynamicData data, checker::Signature *signature, const ArenaVector &arguments) { - CallDynamicImpl(node, obj, param2, signature, arguments); + CallDynamicImpl(data, signature, arguments); } - void CallDynamic(const ir::AstNode *node, VReg obj, VReg param2, VReg param3, checker::Signature *signature, + void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature, const ArenaVector &arguments) { - CallDynamicImpl(node, obj, param2, param3, signature, arguments); + CallDynamicImpl(data, param3, signature, arguments); } #ifdef PANDA_WITH_ETS @@ -1079,7 +1085,7 @@ private: ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) template - void CallDynamicImpl(const ir::AstNode *node, VReg &obj, VReg ¶m2, checker::Signature *signature, + void CallDynamicImpl(CallDynamicData data, checker::Signature *signature, const ArenaVector &arguments) { RegScope rs(this); @@ -1087,18 +1093,18 @@ private: switch (arguments.size()) { case 0U: { - Ra().Emit(node, name, obj, param2); + Ra().Emit(data.node, name, data.obj, data.param2); break; } case 1U: { COMPILE_ARG(0, 2U); - Ra().Emit(node, name, obj, param2, arg0, dummyReg_); + Ra().Emit(data.node, name, data.obj, data.param2, arg0, dummyReg_); break; } case 2U: { COMPILE_ARG(0, 2U); COMPILE_ARG(1, 2U); - Ra().Emit(node, name, obj, param2, arg0, arg1); + Ra().Emit(data.node, name, data.obj, data.param2, arg0, arg1); break; } default: { @@ -1106,14 +1112,14 @@ private: COMPILE_ARG(idx, 2U); } - Rra().Emit(node, obj, arguments.size() + 2U, name, obj); + Rra().Emit(data.node, data.obj, arguments.size() + 2U, name, data.obj); break; } } } template - void CallDynamicImpl(const ir::AstNode *node, VReg obj, VReg param2, VReg param3, checker::Signature *signature, + void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature, const ArenaVector &arguments) { RegScope rs(this); @@ -1121,12 +1127,12 @@ private: switch (arguments.size()) { case 0U: { - Ra().Emit(node, name, obj, param2, param3, dummyReg_); + Ra().Emit(data.node, name, data.obj, data.param2, param3, dummyReg_); break; } case 1U: { COMPILE_ARG(0, 3U); - Ra().Emit(node, name, obj, param2, param3, arg0); + Ra().Emit(data.node, name, data.obj, data.param2, param3, arg0); break; } default: { @@ -1134,7 +1140,7 @@ private: COMPILE_ARG(idx, 3U); } - Rra().Emit(node, obj, arguments.size() + 3U, name, obj); + Rra().Emit(data.node, data.obj, arguments.size() + 3U, name, data.obj); break; } } @@ -1147,6 +1153,8 @@ private: template void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType); + template + void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number); void InitializeContainingClass(); util::StringView FormDynamicModulePropReference(const varbinder::Variable *var); @@ -1160,13 +1168,8 @@ private: }; template -void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType) +void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number) { - auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() && - !targetType_->IsETSArrayType()) - ? checker::ETSChecker::TypeKind(targetType_) - : targetType; - switch (typeKind) { case checker::TypeFlag::ETS_BOOLEAN: case checker::TypeFlag::BYTE: { @@ -1215,6 +1218,17 @@ void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::T UNREACHABLE(); } } +} + +template +void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType) +{ + auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() && + !targetType_->IsETSArrayType()) + ? checker::ETSChecker::TypeKind(targetType_) + : targetType; + + SetAccumulatorTargetType(node, typeKind, number); if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) { ApplyConversion(node, targetType_); diff --git a/ets2panda/compiler/core/function.cpp b/ets2panda/compiler/core/function.cpp index 98abf21803ea3e39fbf91b9c22bce1c0c11e894b..9118189783bd2e4049ef73f5a5de73d49265b234 100644 --- a/ets2panda/compiler/core/function.cpp +++ b/ets2panda/compiler/core/function.cpp @@ -143,32 +143,10 @@ void Function::LoadClassContexts(const ir::AstNode *node, PandaGen *pg, VReg cto } while (classDef != nullptr); } -void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl) +void Function::IterateOverElements(const ArenaVector &elements, PandaGen *pg, VReg &ctor, VReg &thisReg, + VReg &computedInstanceFieldsArray) { - const auto klass = util::Helpers::GetClassDefiniton(decl); - const auto &elements = klass->Body(); - - RegScope rs(pg); - auto thisReg = pg->AllocReg(); - auto ctor = pg->AllocReg(); - pg->GetThis(decl); - pg->StoreAccumulator(decl, thisReg); - pg->GetFunctionObject(decl); - pg->StoreAccumulator(decl, ctor); - - VReg computedInstanceFieldsArray {}; uint32_t computedInstanceFieldsIndex = 0; - - if (klass->HasPrivateMethod()) { - pg->ClassPrivateMethodOrAccessorAdd(decl, ctor, thisReg); - } - - if (klass->HasComputedInstanceField()) { - computedInstanceFieldsArray = pg->AllocReg(); - pg->LoadClassComputedInstanceFields(klass, ctor); - pg->StoreAccumulator(klass, computedInstanceFieldsArray); - } - for (auto const &element : elements) { if (!element->IsClassProperty()) { continue; @@ -216,6 +194,34 @@ void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *dec } } +void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl) +{ + const auto klass = util::Helpers::GetClassDefiniton(decl); + const auto &elements = klass->Body(); + + RegScope rs(pg); + auto thisReg = pg->AllocReg(); + auto ctor = pg->AllocReg(); + pg->GetThis(decl); + pg->StoreAccumulator(decl, thisReg); + pg->GetFunctionObject(decl); + pg->StoreAccumulator(decl, ctor); + + VReg computedInstanceFieldsArray {}; + + if (klass->HasPrivateMethod()) { + pg->ClassPrivateMethodOrAccessorAdd(decl, ctor, thisReg); + } + + if (klass->HasComputedInstanceField()) { + computedInstanceFieldsArray = pg->AllocReg(); + pg->LoadClassComputedInstanceFields(klass, ctor); + pg->StoreAccumulator(klass, computedInstanceFieldsArray); + } + + IterateOverElements(elements, pg, ctor, thisReg, computedInstanceFieldsArray); +} + static void CompileFunction(PandaGen *pg) { const auto *decl = pg->RootNode()->AsScriptFunction(); diff --git a/ets2panda/compiler/core/function.h b/ets2panda/compiler/core/function.h index 4f642afd4f365546eafb66af3d7b71682ec98551..1d51abf7cd7ec8ffb59f88caf85842280d81b95a 100644 --- a/ets2panda/compiler/core/function.h +++ b/ets2panda/compiler/core/function.h @@ -30,6 +30,8 @@ class Function { public: Function() = delete; + static void IterateOverElements(const ArenaVector &elements, PandaGen *pg, VReg &ctor, VReg &thisReg, + VReg &computedInstanceFieldsArray); static void Compile(PandaGen *pg); static void CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl); diff --git a/ets2panda/es2panda.h b/ets2panda/es2panda.h index af9dbf4432039e1ed7955536bc298ea00d7e9660..fa07e32171fe21f0c17046d7e962463eefa0f471 100644 --- a/ets2panda/es2panda.h +++ b/ets2panda/es2panda.h @@ -32,7 +32,7 @@ struct Program; namespace ark::es2panda { -static constexpr std::string_view ES2PANDA_VERSION = "0.1"; +constexpr std::string_view ES2PANDA_VERSION = "0.1"; namespace util { class Options; } // namespace util diff --git a/ets2panda/linter/lib/LinterRunner.ts b/ets2panda/linter/lib/LinterRunner.ts index 3820a942451d173cf94fc6cc7700167fe66a325d..d9eedbe1165cf937222278fa2ba216f33975d234 100644 --- a/ets2panda/linter/lib/LinterRunner.ts +++ b/ets2panda/linter/lib/LinterRunner.ts @@ -13,18 +13,18 @@ * limitations under the License. */ +import * as path from 'node:path'; import type * as ts from 'typescript'; -import type { ProblemInfo } from './ProblemInfo'; -import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; -import { InteropTypescriptLinter } from './InteropTypescriptLinter'; -import { FaultID } from './Problems'; -import { faultDesc } from './FaultDesc'; +import type { CommandLineOptions } from './CommandLineOptions'; import { faultsAttrs } from './FaultAttrs'; -import type { LintRunResult } from './LintRunResult'; -import * as path from 'node:path'; +import { faultDesc } from './FaultDesc'; +import { InteropTypescriptLinter } from './InteropTypescriptLinter'; import type { LintOptions } from './LintOptions'; -import type { CommandLineOptions } from './CommandLineOptions'; -import { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; +import type { LintRunResult } from './LintRunResult'; +import type { ProblemInfo } from './ProblemInfo'; +import { ProblemSeverity } from './ProblemSeverity'; +import { FaultID } from './Problems'; +import { TypeScriptLinter, consoleLog } from './TypeScriptLinter'; import { getTscDiagnostics } from './ts-diagnostics/GetTscDiagnostics'; import { transformTscDiagnostics } from './ts-diagnostics/TransformTscDiagnostics'; import { @@ -32,33 +32,36 @@ import { ARKTS_IGNORE_DIRS_OH_MODULES, ARKTS_IGNORE_FILES } from './utils/consts/ArktsIgnorePaths'; +import { mergeArrayMaps } from './utils/functions/MergeArrayMaps'; import { pathContainsDirectory } from './utils/functions/PathHelper'; -import { ProblemSeverity } from './ProblemSeverity'; function prepareInputFilesList(cmdOptions: CommandLineOptions): string[] { let inputFiles = cmdOptions.inputFiles; - if (cmdOptions.parsedConfigFile) { - inputFiles = cmdOptions.parsedConfigFile.fileNames; - if (cmdOptions.inputFiles.length > 0) { + if (!cmdOptions.parsedConfigFile) { + return inputFiles; + } - /* - * Apply linter only to the project source files that are specified - * as a command-line arguments. Other source files will be discarded. - */ - const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => { - return path.resolve(x); - }); - const configInputsResolvedPaths = inputFiles.map((x) => { - return path.resolve(x); - }); - inputFiles = configInputsResolvedPaths.filter((x) => { - return cmdInputsResolvedPaths.some((y) => { - return x === y; - }); - }); - } + inputFiles = cmdOptions.parsedConfigFile.fileNames; + if (cmdOptions.inputFiles.length <= 0) { + return inputFiles; } + /* + * Apply linter only to the project source files that are specified + * as a command-line arguments. Other source files will be discarded. + */ + const cmdInputsResolvedPaths = cmdOptions.inputFiles.map((x) => { + return path.resolve(x); + }); + const configInputsResolvedPaths = inputFiles.map((x) => { + return path.resolve(x); + }); + inputFiles = configInputsResolvedPaths.filter((x) => { + return cmdInputsResolvedPaths.some((y) => { + return x === y; + }); + }); + return inputFiles; } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index fb33ff4d75bcf0947cd4095b3625c07a72b71d31..7a2948568bab9243ace87f15a5e2ebe1a616d07e 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -44,6 +44,7 @@ import { } from './utils/consts/NonInitializablePropertyDecorators'; import { NON_RETURN_FUNCTION_DECORATORS } from './utils/consts/NonReturnFunctionDecorators'; import { PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE } from './utils/consts/PropertyHasNoInitializerErrorCode'; +import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES } from './utils/consts/SendableAPI'; import type { DiagnosticChecker } from './utils/functions/DiagnosticChecker'; import { forEachNodeInSubtree } from './utils/functions/ForEachNodeInSubtree'; import { hasPredecessor } from './utils/functions/HasPredecessor'; @@ -59,7 +60,6 @@ import { import { SupportedStdCallApiChecker } from './utils/functions/SupportedStdCallAPI'; import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext'; import { isAssignmentOperator } from './utils/functions/isAssignmentOperator'; -import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES } from './utils/consts/SendableAPI'; export function consoleLog(...args: unknown[]): void { if (TypeScriptLinter.ideMode) { @@ -1829,19 +1829,23 @@ export class TypeScriptLinter { } private handleImportCall(tsCallExpr: ts.CallExpression): void { - if (tsCallExpr.expression.kind === ts.SyntaxKind.ImportKeyword) { - // relax rule#133 "arkts-no-runtime-import" - const tsArgs = tsCallExpr.arguments; - if (tsArgs.length > 1 && ts.isObjectLiteralExpression(tsArgs[1])) { - for (const tsProp of tsArgs[1].properties) { - if ( - (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) && - tsProp.name.getText() === 'assert' - ) { - this.incrementCounters(tsProp, FaultID.ImportAssertion); - break; - } - } + if (tsCallExpr.expression.kind !== ts.SyntaxKind.ImportKeyword) { + return; + } + + // relax rule#133 "arkts-no-runtime-import" + const tsArgs = tsCallExpr.arguments; + if (tsArgs.length <= 1 || !ts.isObjectLiteralExpression(tsArgs[1])) { + return; + } + + for (const tsProp of tsArgs[1].properties) { + if ( + (ts.isPropertyAssignment(tsProp) || ts.isShorthandPropertyAssignment(tsProp)) && + tsProp.name.getText() === 'assert' + ) { + this.incrementCounters(tsProp, FaultID.ImportAssertion); + break; } } } diff --git a/ets2panda/linter/lib/autofixes/Autofixer.ts b/ets2panda/linter/lib/autofixes/Autofixer.ts index eb926134ac49a33181ce7445a7a5f5485a31034a..37e6285c6970b9052baf8fada3ab439632c8570e 100644 --- a/ets2panda/linter/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/lib/autofixes/Autofixer.ts @@ -483,6 +483,36 @@ export class Autofixer { return text; } + private getEnumMembers(node: ts.Node, enumDeclsInFile: ts.Declaration[], result: Autofix[] | undefined): void { + if (result === undefined || !ts.isEnumDeclaration(node)) { + return; + } + + if (result.length) { + result.push({ start: node.getStart(), end: node.getEnd(), replacementText: '' }); + return; + } + + const members: ts.EnumMember[] = []; + for (const decl of enumDeclsInFile) { + for (const member of (decl as ts.EnumDeclaration).members) { + if ( + member.initializer && + member.initializer.kind !== ts.SyntaxKind.NumericLiteral && + member.initializer.kind !== ts.SyntaxKind.StringLiteral + ) { + result = undefined; + return; + } + } + members.push(...(decl as ts.EnumDeclaration).members); + } + + const fullEnum = ts.factory.createEnumDeclaration(node.modifiers, node.name, members); + const fullText = this.printer.printNode(ts.EmitHint.Unspecified, fullEnum, node.getSourceFile()); + result.push({ start: node.getStart(), end: node.getEnd(), replacementText: fullText }); + } + fixEnumMerging(enumSymbol: ts.Symbol, enumDeclsInFile: ts.Declaration[]): Autofix[] | undefined { if (this.enumMergingCache.has(enumSymbol)) { return this.enumMergingCache.get(enumSymbol); @@ -495,33 +525,7 @@ export class Autofixer { let result: Autofix[] | undefined = []; this.symbolCache.getReferences(enumSymbol).forEach((node) => { - if (result === undefined || !ts.isEnumDeclaration(node)) { - return; - } - - if (result.length) { - result.push({ start: node.getStart(), end: node.getEnd(), replacementText: '' }); - return; - } - - const members: ts.EnumMember[] = []; - for (const decl of enumDeclsInFile) { - for (const member of (decl as ts.EnumDeclaration).members) { - if ( - member.initializer && - member.initializer.kind !== ts.SyntaxKind.NumericLiteral && - member.initializer.kind !== ts.SyntaxKind.StringLiteral - ) { - result = undefined; - return; - } - } - members.push(...(decl as ts.EnumDeclaration).members); - } - - const fullEnum = ts.factory.createEnumDeclaration(node.modifiers, node.name, members); - const fullText = this.printer.printNode(ts.EmitHint.Unspecified, fullEnum, node.getSourceFile()); - result.push({ start: node.getStart(), end: node.getEnd(), replacementText: fullText }); + this.getEnumMembers(node, enumDeclsInFile, result); }); if (!result?.length) { result = undefined; diff --git a/ets2panda/linter/lib/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts index 77eb76f7ec6616dbbca679575106b041b7b22345..fa9a546620cfbec022d8bdc7d9eca5e1d2918987 100644 --- a/ets2panda/linter/lib/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -756,15 +756,18 @@ export class TsUtils { let hasDefaultCtor: boolean = false; type.symbol.members.forEach((value) => { - if ((value.flags & ts.SymbolFlags.Constructor) !== 0) { - hasCtor = true; - - if (value.declarations !== undefined && value.declarations.length > 0) { - const declCtor = value.declarations[0] as ts.ConstructorDeclaration; - if (declCtor.parameters.length === 0) { - hasDefaultCtor = true; - } - } + if ((value.flags & ts.SymbolFlags.Constructor) === 0) { + return; + } + hasCtor = true; + + if (value.declarations === undefined || value.declarations.length <= 0) { + return; + } + + const declCtor = value.declarations[0] as ts.ConstructorDeclaration; + if (declCtor.parameters.length === 0) { + hasDefaultCtor = true; } }); @@ -2383,27 +2386,29 @@ export class TsUtils { static declarationNameExists(srcFile: ts.SourceFile, name: string): boolean { return srcFile.statements.some((stmt) => { - if (ts.isImportDeclaration(stmt)) { - if (!stmt.importClause) { - return false; - } - if (stmt.importClause.namedBindings) { - if (ts.isNamespaceImport(stmt.importClause.namedBindings)) { - return stmt.importClause.namedBindings.name.text === name; - } - return stmt.importClause.namedBindings.elements.some((x) => { - return x.name.text === name; - }); - } + if (!ts.isImportDeclaration(stmt)) { + return ( + TsUtils.isDeclarationStatement(stmt) && + stmt.name !== undefined && + ts.isIdentifier(stmt.name) && + stmt.name.text === name + ); + } + + if (!stmt.importClause) { + return false; + } + + if (!stmt.importClause.namedBindings) { return stmt.importClause.name?.text === name; } - return ( - TsUtils.isDeclarationStatement(stmt) && - stmt.name !== undefined && - ts.isIdentifier(stmt.name) && - stmt.name.text === name - ); + if (ts.isNamespaceImport(stmt.importClause.namedBindings)) { + return stmt.importClause.namedBindings.name.text === name; + } + return stmt.importClause.namedBindings.elements.some((x) => { + return x.name.text === name; + }); }); } diff --git a/ets2panda/linter/src/LinterCLI.ts b/ets2panda/linter/src/LinterCLI.ts index b6b18c81e70bee84e1c2651e2433ebe73180cef2..1f8a238d98dd4724d310ff1c5ea8caeacb2b0048 100644 --- a/ets2panda/linter/src/LinterCLI.ts +++ b/ets2panda/linter/src/LinterCLI.ts @@ -13,15 +13,16 @@ * limitations under the License. */ -import { TypeScriptLinter } from '../lib/TypeScriptLinter'; -import { parseCommandLine } from './CommandLineParser'; -import { Logger } from '../lib/Logger'; import * as fs from 'node:fs'; import * as os from 'node:os'; -import * as readline from 'node:readline'; import * as path from 'node:path'; +import * as readline from 'node:readline'; import type { CommandLineOptions } from '../lib/CommandLineOptions'; import { lint } from '../lib/LinterRunner'; +import { Logger } from '../lib/Logger'; +import type { ProblemInfo } from '../lib/ProblemInfo'; +import { TypeScriptLinter } from '../lib/TypeScriptLinter'; +import { parseCommandLine } from './CommandLineParser'; import { compileLintOptions } from './Compiler'; export function run(): void { @@ -51,6 +52,23 @@ function getTempFileName(): string { return path.join(os.tmpdir(), Math.floor(Math.random() * 10000000).toString() + '_linter_tmp_file.ts'); } +function showJSONMessage(problems: ProblemInfo[][]): void { + const jsonMessage = problems[0].map((x) => { + return { + line: x.line, + column: x.column, + start: x.start, + end: x.end, + type: x.type, + suggest: x.suggest, + rule: x.rule, + severity: x.severity, + autofix: x.autofix + }; + }); + Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); +} + function runIDEMode(cmdOptions: CommandLineOptions): void { TypeScriptLinter.ideMode = true; const tmpFileName = getTempFileName(); @@ -75,20 +93,7 @@ function runIDEMode(cmdOptions: CommandLineOptions): void { const result = lint(compileLintOptions(cmdOptions)); const problems = Array.from(result.problemsInfos.values()); if (problems.length === 1) { - const jsonMessage = problems[0].map((x) => { - return { - line: x.line, - column: x.column, - start: x.start, - end: x.end, - type: x.type, - suggest: x.suggest, - rule: x.rule, - severity: x.severity, - autofix: x.autofix - }; - }); - Logger.info(`{"linter messages":${JSON.stringify(jsonMessage)}}`); + showJSONMessage(problems); } else { Logger.error('Unexpected error: could not lint file'); }