diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 1b18db86c4a0cd71e901844ad228d72eebb6614b..ae037a0cefb61a679059bb859d7c6301974dfed4 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -19,14 +19,6 @@ #include "checker/ETSchecker.h" #include "checker/ets/castingContext.h" #include "checker/ets/typeRelationContext.h" -#include "ir/base/catchClause.h" -#include "ir/base/classProperty.h" -#include "ir/base/classStaticBlock.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/objectExpression.h" -#include "ir/expressions/arrayExpression.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" #include "util/helpers.h" namespace panda::es2panda::checker { @@ -419,25 +411,148 @@ checker::Type *ETSAnalyzer::Check(ir::FunctionExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + expr->SetTsType(checker->ResolveIdentifier(expr)); + if (expr->TsType()->IsETSFunctionType()) { + for (auto *sig : expr->TsType()->AsETSFunctionType()->CallSignatures()) { + if (sig->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) { + sig->OwnerVar()->Declaration()->Node()->Check(checker); + } + } + } + return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::ImportExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ImportExpression *expr) const { - (void)expr; UNREACHABLE(); } +static checker::Type *CheckEnumMember(checker::ETSChecker *checker, checker::Type *type, ir::MemberExpression *expr) +{ + auto const *const enum_interface = [type]() -> checker::ETSEnumInterface const * { + if (type->IsETSEnumType()) { + return type->AsETSEnumType(); + } + return type->AsETSStringEnumType(); + }(); + + if (expr->Parent()->Type() == ir::AstNodeType::CALL_EXPRESSION && + expr->Parent()->AsCallExpression()->Callee() == expr) { + auto *const enum_method_type = + enum_interface->LookupMethod(checker, expr->Object(), expr->Property()->AsIdentifier()); + expr->SetTsType(enum_method_type); + return expr->TsType(); + } + + auto *const enum_literal_type = + enum_interface->LookupConstant(checker, expr->Object(), expr->Property()->AsIdentifier()); + expr->SetTsType(enum_literal_type); + expr->SetPropVar(enum_literal_type->GetMemberVar()); + return expr->TsType(); +} + +static checker::Type *CheckObjectMember(checker::ETSChecker *checker, ir::MemberExpression *expr) +{ + auto resolve_res = checker->ResolveMemberReference(expr, expr->ObjType()); + ASSERT(!resolve_res.empty()); + checker::Type *type_to_set = nullptr; + switch (resolve_res.size()) { + case 1: { + if (resolve_res[0]->Kind() == checker::ResolvedKind::PROPERTY) { + expr->SetPropVar(resolve_res[0]->Variable()->AsLocalVariable()); + checker->ValidatePropertyAccess(expr->PropVar(), expr->ObjType(), expr->Property()->Start()); + type_to_set = checker->GetTypeOfVariable(expr->PropVar()); + } else { + type_to_set = checker->GetTypeOfVariable(resolve_res[0]->Variable()); + } + break; + } + case 2: { + // ETSExtensionFuncHelperType(class_method_type, extension_method_type) + type_to_set = checker->CreateETSExtensionFuncHelperType( + checker->GetTypeOfVariable(resolve_res[1]->Variable())->AsETSFunctionType(), + checker->GetTypeOfVariable(resolve_res[0]->Variable())->AsETSFunctionType()); + break; + } + default: { + UNREACHABLE(); + } + } + expr->SetTsType(type_to_set); + if (expr->PropVar() != nullptr && expr->PropVar()->TsType() != nullptr && + expr->PropVar()->TsType()->IsETSFunctionType()) { + for (auto *sig : expr->PropVar()->TsType()->AsETSFunctionType()->CallSignatures()) { + if (sig->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) { + sig->OwnerVar()->Declaration()->Node()->Check(checker); + } + } + } + return expr->TsType(); +} + checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + if (expr->IsComputed()) { + expr->SetTsType(checker->CheckArrayElementAccess(expr)); + return expr->TsType(); + } + + checker::Type *const base_type = expr->Object()->Check(checker); + + if (!base_type->IsETSObjectType()) { + if (base_type->IsETSArrayType() && expr->Property()->AsIdentifier()->Name().Is("length")) { + expr->SetTsType(checker->GlobalIntType()); + return expr->TsType(); + } + + if (base_type->IsETSUnionType()) { + auto *const union_type = base_type->AsETSUnionType(); + checker::Type *member_type = nullptr; + auto check_member_type = [expr, checker, &member_type]() { + if (member_type != nullptr && member_type != expr->TsType()) { + checker->ThrowTypeError("Member type must be the same for all union objects.", expr->Start()); + } + member_type = expr->TsType(); + }; + for (auto *type : union_type->ConstituentTypes()) { + if (type->IsETSObjectType()) { + expr->SetObjectType(type->AsETSObjectType()); + CheckObjectMember(checker, expr); + check_member_type(); + } + + if (type->IsETSEnumType() || base_type->IsETSStringEnumType()) { + CheckEnumMember(checker, type, expr); + check_member_type(); + } + } + expr->SetObjectType(union_type->GetLeastUpperBoundType(checker)->AsETSObjectType()); + return expr->TsType(); + } + + if (base_type->IsETSEnumType() || base_type->IsETSStringEnumType()) { + return CheckEnumMember(checker, base_type, expr); + } + + checker->ThrowTypeError({"Cannot access property of non-object or non-enum type"}, expr->Object()->Start()); + } + + expr->SetObjectType(base_type->AsETSObjectType()); + return CheckObjectMember(checker, expr); } -checker::Type *ETSAnalyzer::Check(ir::NewExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::NewExpression *expr) const { - (void)expr; UNREACHABLE(); } @@ -447,9 +562,8 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::OmittedExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::OmittedExpression *expr) const { - (void)expr; UNREACHABLE(); } @@ -461,14 +575,26 @@ checker::Type *ETSAnalyzer::Check(ir::OpaqueTypeNode *expr) const checker::Type *ETSAnalyzer::Check(ir::SequenceExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + for (auto *it : expr->Sequence()) { + it->Check(checker); + } + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::SuperExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass()->SuperType(), "super")); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::TaggedTemplateExpression *expr) const @@ -507,9 +633,8 @@ checker::Type *ETSAnalyzer::Check(ir::YieldExpression *expr) const UNREACHABLE(); } // compile methods for LITERAL EXPRESSIONS in alphabetical order -checker::Type *ETSAnalyzer::Check(ir::BigIntLiteral *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::BigIntLiteral *expr) const { - (void)expr; UNREACHABLE(); } diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index 6b42c2deb681f0d36cb4402187745b155a66a41c..424f58a784e72e7a1556b5b732bef74f830f6655 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -317,26 +317,120 @@ checker::Type *TSAnalyzer::Check(ir::FunctionExpression *expr) const checker::Type *TSAnalyzer::Check(ir::Identifier *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + if (expr->Variable() == nullptr) { + if (expr->Name().Is("undefined")) { + return checker->GlobalUndefinedType(); + } + + checker->ThrowTypeError({"Cannot find name ", expr->Name()}, expr->Start()); + } + + const varbinder::Decl *decl = expr->Variable()->Declaration(); + + if (decl->IsTypeAliasDecl() || decl->IsInterfaceDecl()) { + checker->ThrowTypeError({expr->Name(), " only refers to a type, but is being used as a value here."}, + expr->Start()); + } + + expr->SetTsType(checker->GetTypeOfVariable(expr->Variable())); + return expr->TsType(); } -checker::Type *TSAnalyzer::Check(ir::ImportExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ImportExpression *expr) const { - (void)expr; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::MemberExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *base_type = checker->CheckNonNullType(expr->Object()->Check(checker), expr->Object()->Start()); + + if (expr->IsComputed()) { + checker::Type *index_type = expr->Property()->Check(checker); + checker::Type *indexed_access_type = checker->GetPropertyTypeForIndexType(base_type, index_type); + + if (indexed_access_type != nullptr) { + return indexed_access_type; + } + + if (!index_type->HasTypeFlag(checker::TypeFlag::STRING_LIKE | checker::TypeFlag::NUMBER_LIKE)) { + checker->ThrowTypeError({"Type ", index_type, " cannot be used as index type"}, expr->Property()->Start()); + } + + if (index_type->IsNumberType()) { + checker->ThrowTypeError("No index signature with a parameter of type 'string' was found on type this type", + expr->Start()); + } + + if (index_type->IsStringType()) { + checker->ThrowTypeError("No index signature with a parameter of type 'number' was found on type this type", + expr->Start()); + } + + switch (expr->Property()->Type()) { + case ir::AstNodeType::IDENTIFIER: { + checker->ThrowTypeError( + {"Property ", expr->Property()->AsIdentifier()->Name(), " does not exist on this type."}, + expr->Property()->Start()); + } + case ir::AstNodeType::NUMBER_LITERAL: { + checker->ThrowTypeError( + {"Property ", expr->Property()->AsNumberLiteral()->Str(), " does not exist on this type."}, + expr->Property()->Start()); + } + case ir::AstNodeType::STRING_LITERAL: { + checker->ThrowTypeError( + {"Property ", expr->Property()->AsStringLiteral()->Str(), " does not exist on this type."}, + expr->Property()->Start()); + } + default: { + UNREACHABLE(); + } + } + } + varbinder::Variable *prop = checker->GetPropertyOfType(base_type, expr->Property()->AsIdentifier()->Name()); + + if (prop != nullptr) { + checker::Type *prop_type = checker->GetTypeOfVariable(prop); + if (prop->HasFlag(varbinder::VariableFlags::READONLY)) { + prop_type->AddTypeFlag(checker::TypeFlag::READONLY); + } + + return prop_type; + } + + if (base_type->IsObjectType()) { + checker::ObjectType *obj_type = base_type->AsObjectType(); + + if (obj_type->StringIndexInfo() != nullptr) { + checker::Type *index_type = obj_type->StringIndexInfo()->GetType(); + if (obj_type->StringIndexInfo()->Readonly()) { + index_type->AddTypeFlag(checker::TypeFlag::READONLY); + } + + return index_type; + } + } + + checker->ThrowTypeError({"Property ", expr->Property()->AsIdentifier()->Name(), " does not exist on this type."}, + expr->Property()->Start()); + return nullptr; } checker::Type *TSAnalyzer::Check(ir::NewExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *callee_type = expr->callee_->Check(checker); + + if (callee_type->IsObjectType()) { + checker::ObjectType *callee_obj = callee_type->AsObjectType(); + return checker->ResolveCallOrNewExpression(callee_obj->ConstructSignatures(), expr->Arguments(), expr->Start()); + } + + checker->ThrowTypeError("This expression is not callable.", expr->Start()); + return nullptr; } checker::Type *TSAnalyzer::Check(ir::ObjectExpression *expr) const @@ -345,10 +439,10 @@ checker::Type *TSAnalyzer::Check(ir::ObjectExpression *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::OmittedExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::OmittedExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + return checker->GlobalUndefinedType(); } checker::Type *TSAnalyzer::Check(ir::OpaqueTypeNode *expr) const @@ -357,16 +451,18 @@ checker::Type *TSAnalyzer::Check(ir::OpaqueTypeNode *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::SequenceExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SequenceExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // NOTE: aszilagyi. + return checker->GlobalAnyType(); } -checker::Type *TSAnalyzer::Check(ir::SuperExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SuperExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // NOTE: aszilagyi. + return checker->GlobalAnyType(); } checker::Type *TSAnalyzer::Check(ir::TaggedTemplateExpression *expr) const @@ -407,8 +503,15 @@ checker::Type *TSAnalyzer::Check(ir::YieldExpression *expr) const // compile methods for LITERAL EXPRESSIONS in alphabetical order checker::Type *TSAnalyzer::Check(ir::BigIntLiteral *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + auto search = checker->BigintLiteralMap().find(expr->Str()); + if (search != checker->BigintLiteralMap().end()) { + return search->second; + } + + auto *new_bigint_literal_type = checker->Allocator()->New(expr->Str(), false); + checker->BigintLiteralMap().insert({expr->Str(), new_bigint_literal_type}); + return new_bigint_literal_type; } checker::Type *TSAnalyzer::Check(ir::BooleanLiteral *expr) const diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 4770e12ab3c3b6adab796ada779aa83caaa6a554..462ca5a4c78ebe7dcab1ff2a4e8d87b408d1241d 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -317,25 +317,193 @@ void ETSCompiler::Compile(const ir::FunctionExpression *expr) const void ETSCompiler::Compile(const ir::Identifier *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto lambda = etsg->VarBinder()->LambdaObjects().find(expr); + if (lambda != etsg->VarBinder()->LambdaObjects().end()) { + etsg->CreateLambdaObjectFromIdentReference(expr, lambda->second.first); + return; + } + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + + ASSERT(expr->Variable() != nullptr); + if (!expr->Variable()->HasFlag(varbinder::VariableFlags::TYPE_ALIAS)) { + etsg->LoadVar(expr, expr->Variable()); + } else { + etsg->LoadVar(expr, expr->TsType()->Variable()); + } } -void ETSCompiler::Compile(const ir::ImportExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ImportExpression *expr) const { - (void)expr; UNREACHABLE(); } +static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr) +{ + if (expr->IsComputed()) { + auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); + expr->Object()->Compile(etsg); + + if (etsg->GetAccumulatorType()->IsETSNullType()) { + if (expr->IsOptional()) { + return true; + } + + etsg->EmitNullPointerException(expr); + return true; + } + + // Helper function to avoid branching in non optional cases + auto compile_and_load_elements = [expr, etsg]() { + compiler::VReg obj_reg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, obj_reg); + auto pttctx = compiler::TargetTypeContext(etsg, expr->Property()->TsType()); + expr->Property()->Compile(etsg); + etsg->ApplyConversion(expr->Property()); + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + + if (expr->TsType()->IsETSDynamicType()) { + auto lang = expr->TsType()->AsETSDynamicType()->Language(); + etsg->LoadElementDynamic(expr, obj_reg, lang); + } else { + etsg->LoadArrayElement(expr, obj_reg); + } + + etsg->ApplyConversion(expr); + }; + + if (expr->IsOptional()) { + compiler::Label *end_label = etsg->AllocLabel(); + etsg->BranchIfNull(expr, end_label); + compile_and_load_elements(); + etsg->SetLabel(expr, end_label); + } else { + compile_and_load_elements(); + } + + return true; + } + return false; +} + void ETSCompiler::Compile(const ir::MemberExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto lambda = etsg->VarBinder()->LambdaObjects().find(expr); + if (lambda != etsg->VarBinder()->LambdaObjects().end()) { + etsg->CreateLambdaObjectFromMemberReference(expr, expr->object_, lambda->second.first); + return; + } + + compiler::RegScope rs(etsg); + if (CompileComputed(etsg, expr)) { + return; + } + + auto &prop_name = expr->Property()->AsIdentifier()->Name(); + auto const *const object_type = expr->Object()->TsType(); + + if (object_type->IsETSArrayType() && prop_name.Is("length")) { + auto ottctx = compiler::TargetTypeContext(etsg, object_type); + expr->Object()->Compile(etsg); + compiler::VReg obj_reg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, obj_reg); + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + etsg->LoadArrayLength(expr, obj_reg); + etsg->ApplyConversion(expr); + return; + } + + if (object_type->IsETSEnumType() || object_type->IsETSStringEnumType()) { + auto const *const enum_interface = [object_type, expr]() -> checker::ETSEnumInterface const * { + if (object_type->IsETSEnumType()) { + return expr->TsType()->AsETSEnumType(); + } + return expr->TsType()->AsETSStringEnumType(); + }(); + + auto ottctx = compiler::TargetTypeContext(etsg, object_type); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + etsg->LoadAccumulatorInt(expr, enum_interface->GetOrdinal()); + return; + } + + if (etsg->Checker()->IsVariableStatic(expr->PropVar())) { + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + + if (expr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { + checker::Signature *sig = expr->PropVar()->TsType()->AsETSFunctionType()->FindGetter(); + etsg->CallStatic0(expr, sig->InternalName()); + etsg->SetAccumulatorType(sig->ReturnType()); + return; + } + + util::StringView full_name = + etsg->FormClassPropReference(expr->Object()->TsType()->AsETSObjectType(), prop_name); + etsg->LoadStaticProperty(expr, expr->TsType(), full_name); + etsg->ApplyConversion(expr); + return; + } + + auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); + expr->Object()->Compile(etsg); + + // NOTE: rsipka. it should be CTE if object type is non nullable type + + if (etsg->GetAccumulatorType()->IsETSNullType()) { + if (expr->IsOptional()) { + etsg->LoadAccumulatorNull(expr, etsg->Checker()->GlobalETSNullType()); + return; + } + + etsg->EmitNullPointerException(expr); + etsg->LoadAccumulatorNull(expr, etsg->Checker()->GlobalETSNullType()); + return; + } + + etsg->ApplyConversion(expr->Object()); + compiler::VReg obj_reg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, obj_reg); + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + + auto load_property = [expr, etsg, obj_reg, prop_name]() { + if (expr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { + checker::Signature *sig = expr->PropVar()->TsType()->AsETSFunctionType()->FindGetter(); + etsg->CallThisVirtual0(expr, obj_reg, sig->InternalName()); + etsg->SetAccumulatorType(sig->ReturnType()); + } else if (expr->Object()->TsType()->IsETSDynamicType()) { + auto lang = expr->Object()->TsType()->AsETSDynamicType()->Language(); + etsg->LoadPropertyDynamic(expr, expr->TsType(), obj_reg, prop_name, lang); + } else if (expr->Object()->TsType()->IsETSUnionType()) { + etsg->LoadUnionProperty(expr, expr->TsType(), obj_reg, prop_name); + } else { + const auto full_name = etsg->FormClassPropReference(expr->Object()->TsType()->AsETSObjectType(), prop_name); + etsg->LoadProperty(expr, expr->TsType(), obj_reg, full_name); + } + etsg->ApplyConversion(expr); + }; + + if (expr->IsOptional()) { + compiler::Label *if_not_null = etsg->AllocLabel(); + compiler::Label *end_label = etsg->AllocLabel(); + + etsg->BranchIfNotNull(expr, if_not_null); + etsg->LoadAccumulatorNull(expr, expr->TsType()); + etsg->Branch(expr, end_label); + etsg->SetLabel(expr, if_not_null); + load_property(); + etsg->SetLabel(expr, end_label); + } else { + load_property(); + } } -void ETSCompiler::Compile(const ir::NewExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::NewExpression *expr) const { - (void)expr; UNREACHABLE(); } @@ -345,9 +513,8 @@ void ETSCompiler::Compile(const ir::ObjectExpression *expr) const UNREACHABLE(); } -void ETSCompiler::Compile(const ir::OmittedExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::OmittedExpression *expr) const { - (void)expr; UNREACHABLE(); } @@ -359,14 +526,16 @@ void ETSCompiler::Compile(const ir::OpaqueTypeNode *node) const void ETSCompiler::Compile(const ir::SequenceExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + for (const auto *it : expr->Sequence()) { + it->Compile(etsg); + } } void ETSCompiler::Compile(const ir::SuperExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->LoadThis(expr); } void ETSCompiler::Compile(const ir::TaggedTemplateExpression *expr) const @@ -405,9 +574,8 @@ void ETSCompiler::Compile(const ir::YieldExpression *expr) const UNREACHABLE(); } // compile methods for LITERAL EXPRESSIONS in alphabetical order -void ETSCompiler::Compile(const ir::BigIntLiteral *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::BigIntLiteral *expr) const { - (void)expr; UNREACHABLE(); } diff --git a/ets2panda/compiler/core/JSCompiler.cpp b/ets2panda/compiler/core/JSCompiler.cpp index 23fc28f983e779a35e8f1d8cd8a810190aa0c3b6..fc3ec2f02e8d1ee94b7edd9bda3c37259c8bb1a8 100644 --- a/ets2panda/compiler/core/JSCompiler.cpp +++ b/ets2panda/compiler/core/JSCompiler.cpp @@ -620,26 +620,83 @@ void JSCompiler::Compile(const ir::FunctionExpression *expr) const void JSCompiler::Compile(const ir::Identifier *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + auto res = pg->Scope()->Find(expr->Name()); + if (res.variable != nullptr) { + pg->LoadVar(expr, res); + return; + } + + if (pg->IsDirectEval()) { + pg->LoadEvalVariable(expr, expr->Name()); + return; + } + + if (expr->Name().Is("NaN")) { + pg->LoadConst(expr, compiler::Constant::JS_NAN); + return; + } + + if (expr->Name().Is("Infinity")) { + pg->LoadConst(expr, compiler::Constant::JS_INFINITY); + return; + } + + if (expr->Name().Is("globalThis")) { + pg->LoadConst(expr, compiler::Constant::JS_GLOBAL); + return; + } + + if (expr->Name().Is("undefined")) { + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + return; + } + + pg->TryLoadGlobalByName(expr, expr->Name()); } -void JSCompiler::Compile(const ir::ImportExpression *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ImportExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->Unimplemented(); } void JSCompiler::Compile(const ir::MemberExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + expr->Object()->Compile(pg); + pg->OptionalChainCheck(expr->IsOptional(), compiler::VReg::Invalid()); + expr->LoadRhs(pg); } void JSCompiler::Compile(const ir::NewExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + compiler::VReg ctor = pg->AllocReg(); + compiler::VReg new_target = pg->AllocReg(); + + expr->Callee()->Compile(pg); + pg->StoreAccumulator(expr, ctor); + + // new.Target will be the same as ctor + pg->StoreAccumulator(expr, new_target); + + if (!util::Helpers::ContainSpreadElement(expr->Arguments()) && + expr->Arguments().size() < compiler::PandaGen::MAX_RANGE_CALL_ARG) { + for (const auto *it : expr->Arguments()) { + compiler::VReg arg = pg->AllocReg(); + it->Compile(pg); + pg->StoreAccumulator(expr, arg); + } + + pg->NewObject(expr, ctor, expr->Arguments().size() + 2); + } else { + compiler::VReg args_obj = pg->AllocReg(); + + pg->CreateArray(expr, expr->Arguments(), args_obj); + pg->NewObjSpread(expr, ctor, new_target); + } } void JSCompiler::Compile(const ir::ObjectExpression *expr) const @@ -654,22 +711,29 @@ void JSCompiler::Compile(const ir::OpaqueTypeNode *node) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::OmittedExpression *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::OmittedExpression *expr) const { - (void)expr; UNREACHABLE(); } void JSCompiler::Compile(const ir::SequenceExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + for (const auto *it : expr->Sequence()) { + it->Compile(pg); + } } void JSCompiler::Compile(const ir::SuperExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->GetThis(expr); + + const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(expr); + + if (func != nullptr) { + pg->ThrowIfSuperNotCorrectCall(expr, 0); + } } void JSCompiler::Compile(const ir::TaggedTemplateExpression *expr) const @@ -710,8 +774,8 @@ void JSCompiler::Compile(const ir::YieldExpression *expr) const // Compile methods for LITERAL EXPRESSIONS in alphabetical order void JSCompiler::Compile(const ir::BigIntLiteral *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->LoadAccumulatorBigInt(expr, expr->Str()); } void JSCompiler::Compile(const ir::BooleanLiteral *expr) const diff --git a/ets2panda/ir/expressions/identifier.cpp b/ets2panda/ir/expressions/identifier.cpp index abe13da98b3373b09249ed821d19aa7e49372001..6a3754ac71c546d35c5a837e58f0c740b38267b7 100644 --- a/ets2panda/ir/expressions/identifier.cpp +++ b/ets2panda/ir/expressions/identifier.cpp @@ -16,14 +16,10 @@ #include "identifier.h" #include "varbinder/scope.h" +#include "checker/ETSchecker.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" -#include "checker/TSchecker.h" -#include "checker/ETSchecker.h" -#include "ir/astDump.h" -#include "ir/typeNode.h" -#include "ir/base/decorator.h" -#include "ir/expression.h" namespace panda::es2panda::ir { Identifier::Identifier([[maybe_unused]] Tag const tag, Identifier const &other, ArenaAllocator *const allocator) @@ -98,92 +94,21 @@ void Identifier::Dump(ir::AstDumper *dumper) const void Identifier::Compile(compiler::PandaGen *pg) const { - auto res = pg->Scope()->Find(name_); - if (res.variable != nullptr) { - pg->LoadVar(this, res); - return; - } - - if (pg->IsDirectEval()) { - pg->LoadEvalVariable(this, name_); - return; - } - - if (name_.Is("NaN")) { - pg->LoadConst(this, compiler::Constant::JS_NAN); - return; - } - - if (name_.Is("Infinity")) { - pg->LoadConst(this, compiler::Constant::JS_INFINITY); - return; - } - - if (name_.Is("globalThis")) { - pg->LoadConst(this, compiler::Constant::JS_GLOBAL); - return; - } - - if (name_.Is("undefined")) { - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - return; - } - - pg->TryLoadGlobalByName(this, name_); + pg->GetAstCompiler()->Compile(this); } void Identifier::Compile(compiler::ETSGen *etsg) const { - auto lambda = etsg->VarBinder()->LambdaObjects().find(this); - if (lambda != etsg->VarBinder()->LambdaObjects().end()) { - etsg->CreateLambdaObjectFromIdentReference(this, lambda->second.first); - return; - } - - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - ASSERT(variable_ != nullptr); - if (!variable_->HasFlag(varbinder::VariableFlags::TYPE_ALIAS)) { - etsg->LoadVar(this, variable_); - } else { - etsg->LoadVar(this, TsType()->Variable()); - } + etsg->GetAstCompiler()->Compile(this); } checker::Type *Identifier::Check(checker::TSChecker *checker) { - if (Variable() == nullptr) { - if (name_.Is("undefined")) { - return checker->GlobalUndefinedType(); - } - - checker->ThrowTypeError({"Cannot find name ", name_}, Start()); - } - - const varbinder::Decl *decl = Variable()->Declaration(); - - if (decl->IsTypeAliasDecl() || decl->IsInterfaceDecl()) { - checker->ThrowTypeError({name_, " only refers to a type, but is being used as a value here."}, Start()); - } - - SetTsType(checker->GetTypeOfVariable(Variable())); - return TsType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *Identifier::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - SetTsType(checker->ResolveIdentifier(this)); - if (TsType()->IsETSFunctionType()) { - for (auto *sig : TsType()->AsETSFunctionType()->CallSignatures()) { - if (sig->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) { - sig->OwnerVar()->Declaration()->Node()->Check(checker); - } - } - } - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/identifier.h b/ets2panda/ir/expressions/identifier.h index a6ee3dd78ef7aaa4e9400a3bb702c9cd6a3ddc0b..289806701a66905f94d6167acdd1ead2f2da3249 100644 --- a/ets2panda/ir/expressions/identifier.h +++ b/ets2panda/ir/expressions/identifier.h @@ -193,10 +193,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: util::StringView name_; diff --git a/ets2panda/ir/expressions/importExpression.cpp b/ets2panda/ir/expressions/importExpression.cpp index e812123c13e5fbc49ccd6910fa2868d0bbde7c13..1f7c3620722bd09190d40988e4abe2c7a1197fae 100644 --- a/ets2panda/ir/expressions/importExpression.cpp +++ b/ets2panda/ir/expressions/importExpression.cpp @@ -15,8 +15,9 @@ #include "importExpression.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" -#include "ir/astDump.h" namespace panda::es2panda::ir { void ImportExpression::TransformChildren(const NodeTransformer &cb) @@ -34,19 +35,23 @@ void ImportExpression::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ImportExpression"}, {"source", source_}}); } -void ImportExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ImportExpression::Compile(compiler::PandaGen *pg) const { - pg->Unimplemented(); + pg->GetAstCompiler()->Compile(this); } -checker::Type *ImportExpression::Check([[maybe_unused]] checker::TSChecker *checker) +void ImportExpression::Compile(compiler::ETSGen *etsg) const { - return nullptr; + etsg->GetAstCompiler()->Compile(this); +} +checker::Type *ImportExpression::Check(checker::TSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); } -checker::Type *ImportExpression::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ImportExpression::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/importExpression.h b/ets2panda/ir/expressions/importExpression.h index d275dd9a9a4e87384f2455bf2f8a0fa934ec7ca4..5d33ba3666cc7475b8e977dbca07bb7b437470d9 100644 --- a/ets2panda/ir/expressions/importExpression.h +++ b/ets2panda/ir/expressions/importExpression.h @@ -35,9 +35,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *source_; diff --git a/ets2panda/ir/expressions/literals/bigIntLiteral.cpp b/ets2panda/ir/expressions/literals/bigIntLiteral.cpp index b59d2f83c9ae771c68c70ca915af97c031505b6d..47e43c721e3bd1818498d435077c549277b788aa 100644 --- a/ets2panda/ir/expressions/literals/bigIntLiteral.cpp +++ b/ets2panda/ir/expressions/literals/bigIntLiteral.cpp @@ -15,8 +15,9 @@ #include "bigIntLiteral.h" -#include "compiler/core/pandagen.h" #include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" namespace panda::es2panda::ir { @@ -30,24 +31,22 @@ void BigIntLiteral::Dump(ir::AstDumper *dumper) const void BigIntLiteral::Compile(compiler::PandaGen *pg) const { - pg->LoadAccumulatorBigInt(this, src_); + pg->GetAstCompiler()->Compile(this); } -checker::Type *BigIntLiteral::Check(checker::TSChecker *checker) +void BigIntLiteral::Compile(compiler::ETSGen *etsg) const { - auto search = checker->BigintLiteralMap().find(src_); - if (search != checker->BigintLiteralMap().end()) { - return search->second; - } + etsg->GetAstCompiler()->Compile(this); +} - auto *new_bigint_literal_type = checker->Allocator()->New(src_, false); - checker->BigintLiteralMap().insert({src_, new_bigint_literal_type}); - return new_bigint_literal_type; +checker::Type *BigIntLiteral::Check(checker::TSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); } checker::Type *BigIntLiteral::Check([[maybe_unused]] checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/literals/bigIntLiteral.h b/ets2panda/ir/expressions/literals/bigIntLiteral.h index a017c4912ac80e3d975aa02b7b965edbae1793a1..a0b72c484fd532be0f758ff14c017e7f74ff800d 100644 --- a/ets2panda/ir/expressions/literals/bigIntLiteral.h +++ b/ets2panda/ir/expressions/literals/bigIntLiteral.h @@ -41,9 +41,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: util::StringView const src_; diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index e0167d4363a8b7745b9fb37e1b5cff9d09573609..91cdeb5aee91b1ea28acab33265a652c2958ca89 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -15,24 +15,9 @@ #include "memberExpression.h" -#include "checker/types/typeRelation.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" -#include "compiler/core/function.h" #include "checker/TSchecker.h" -#include "checker/ETSchecker.h" -#include "checker/types/ets/etsExtensionFuncHelperType.h" -#include "checker/types/ets/etsFunctionType.h" -#include "checker/types/signature.h" -#include "ir/astDump.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/callExpression.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/literals/numberLiteral.h" -#include "ir/expressions/literals/stringLiteral.h" -#include "ir/ts/tsEnumMember.h" -#include "util/helpers.h" namespace panda::es2panda::ir { MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, Expression *const object, Expression *const property) @@ -113,9 +98,7 @@ void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg obje void MemberExpression::Compile(compiler::PandaGen *pg) const { - object_->Compile(pg); - pg->OptionalChainCheck(optional_, compiler::VReg::Invalid()); - LoadRhs(pg); + pg->GetAstCompiler()->Compile(this); } void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg obj_reg) const @@ -126,356 +109,19 @@ void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg obj_r LoadRhs(pg); } -bool MemberExpression::CompileComputed(compiler::ETSGen *etsg) const -{ - if (computed_) { - auto ottctx = compiler::TargetTypeContext(etsg, object_->TsType()); - object_->Compile(etsg); - - if (etsg->GetAccumulatorType()->IsETSNullType()) { - if (optional_) { - return true; - } - - etsg->EmitNullPointerException(this); - return true; - } - - // Helper function to avoid branching in non optional cases - auto compile_and_load_elements = [this, etsg]() { - compiler::VReg obj_reg = etsg->AllocReg(); - etsg->StoreAccumulator(this, obj_reg); - auto pttctx = compiler::TargetTypeContext(etsg, property_->TsType()); - property_->Compile(etsg); - etsg->ApplyConversion(property_); - - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - if (TsType()->IsETSDynamicType()) { - auto lang = TsType()->AsETSDynamicType()->Language(); - etsg->LoadElementDynamic(this, obj_reg, lang); - } else { - etsg->LoadArrayElement(this, obj_reg); - } - - etsg->ApplyConversion(this); - }; - - if (optional_) { - compiler::Label *end_label = etsg->AllocLabel(); - etsg->BranchIfNull(this, end_label); - compile_and_load_elements(); - etsg->SetLabel(this, end_label); - } else { - compile_and_load_elements(); - } - - return true; - } - return false; -} - void MemberExpression::Compile(compiler::ETSGen *etsg) const { - auto lambda = etsg->VarBinder()->LambdaObjects().find(this); - if (lambda != etsg->VarBinder()->LambdaObjects().end()) { - etsg->CreateLambdaObjectFromMemberReference(this, object_, lambda->second.first); - return; - } - - compiler::RegScope rs(etsg); - if (CompileComputed(etsg)) { - return; - } - - auto &prop_name = property_->AsIdentifier()->Name(); - auto const *const object_type = object_->TsType(); - - if (object_type->IsETSArrayType() && prop_name.Is("length")) { - auto ottctx = compiler::TargetTypeContext(etsg, object_type); - object_->Compile(etsg); - compiler::VReg obj_reg = etsg->AllocReg(); - etsg->StoreAccumulator(this, obj_reg); - - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - etsg->LoadArrayLength(this, obj_reg); - etsg->ApplyConversion(this); - return; - } - - if (object_type->IsETSEnumType() || object_type->IsETSStringEnumType()) { - auto const *const enum_interface = [object_type, this]() -> checker::ETSEnumInterface const * { - if (object_type->IsETSEnumType()) { - return TsType()->AsETSEnumType(); - } - return TsType()->AsETSStringEnumType(); - }(); - - auto ottctx = compiler::TargetTypeContext(etsg, object_type); - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - etsg->LoadAccumulatorInt(this, enum_interface->GetOrdinal()); - return; - } - - if (etsg->Checker()->IsVariableStatic(prop_var_)) { - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - if (prop_var_->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { - checker::Signature *sig = prop_var_->TsType()->AsETSFunctionType()->FindGetter(); - etsg->CallStatic0(this, sig->InternalName()); - etsg->SetAccumulatorType(sig->ReturnType()); - return; - } - - util::StringView full_name = etsg->FormClassPropReference(object_->TsType()->AsETSObjectType(), prop_name); - etsg->LoadStaticProperty(this, TsType(), full_name); - etsg->ApplyConversion(this); - return; - } - - auto ottctx = compiler::TargetTypeContext(etsg, object_->TsType()); - object_->Compile(etsg); - - // NOTE: rsipka. it should be CTE if object type is non nullable type - - if (etsg->GetAccumulatorType()->IsETSNullType()) { - if (optional_) { - etsg->LoadAccumulatorNull(this, etsg->Checker()->GlobalETSNullType()); - return; - } - - etsg->EmitNullPointerException(this); - etsg->LoadAccumulatorNull(this, etsg->Checker()->GlobalETSNullType()); - return; - } - - etsg->ApplyConversion(object_); - compiler::VReg obj_reg = etsg->AllocReg(); - etsg->StoreAccumulator(this, obj_reg); - - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - auto load_property = [this, etsg, obj_reg, prop_name]() { - if (prop_var_->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { - checker::Signature *sig = prop_var_->TsType()->AsETSFunctionType()->FindGetter(); - etsg->CallThisVirtual0(this, obj_reg, sig->InternalName()); - etsg->SetAccumulatorType(sig->ReturnType()); - } else if (object_->TsType()->IsETSDynamicType()) { - auto lang = object_->TsType()->AsETSDynamicType()->Language(); - etsg->LoadPropertyDynamic(this, TsType(), obj_reg, prop_name, lang); - } else if (object_->TsType()->IsETSUnionType()) { - etsg->LoadUnionProperty(this, TsType(), obj_reg, prop_name); - } else { - const auto full_name = etsg->FormClassPropReference(object_->TsType()->AsETSObjectType(), prop_name); - etsg->LoadProperty(this, TsType(), obj_reg, full_name); - } - etsg->ApplyConversion(this); - }; - - if (optional_) { - compiler::Label *if_not_null = etsg->AllocLabel(); - compiler::Label *end_label = etsg->AllocLabel(); - - etsg->BranchIfNotNull(this, if_not_null); - etsg->LoadAccumulatorNull(this, TsType()); - etsg->Branch(this, end_label); - etsg->SetLabel(this, if_not_null); - load_property(); - etsg->SetLabel(this, end_label); - } else { - load_property(); - } + etsg->GetAstCompiler()->Compile(this); } checker::Type *MemberExpression::Check(checker::TSChecker *checker) { - checker::Type *base_type = checker->CheckNonNullType(object_->Check(checker), object_->Start()); - - if (computed_) { - checker::Type *index_type = property_->Check(checker); - checker::Type *indexed_access_type = checker->GetPropertyTypeForIndexType(base_type, index_type); - - if (indexed_access_type != nullptr) { - return indexed_access_type; - } - - if (!index_type->HasTypeFlag(checker::TypeFlag::STRING_LIKE | checker::TypeFlag::NUMBER_LIKE)) { - checker->ThrowTypeError({"Type ", index_type, " cannot be used as index type"}, property_->Start()); - } - - if (index_type->IsNumberType()) { - checker->ThrowTypeError("No index signature with a parameter of type 'string' was found on type this type", - Start()); - } - - if (index_type->IsStringType()) { - checker->ThrowTypeError("No index signature with a parameter of type 'number' was found on type this type", - Start()); - } - - switch (property_->Type()) { - case ir::AstNodeType::IDENTIFIER: { - checker->ThrowTypeError( - {"Property ", property_->AsIdentifier()->Name(), " does not exist on this type."}, - property_->Start()); - } - case ir::AstNodeType::NUMBER_LITERAL: { - checker->ThrowTypeError( - {"Property ", property_->AsNumberLiteral()->Str(), " does not exist on this type."}, - property_->Start()); - } - case ir::AstNodeType::STRING_LITERAL: { - checker->ThrowTypeError( - {"Property ", property_->AsStringLiteral()->Str(), " does not exist on this type."}, - property_->Start()); - } - default: { - UNREACHABLE(); - } - } - } - - varbinder::Variable *prop = checker->GetPropertyOfType(base_type, property_->AsIdentifier()->Name()); - - if (prop != nullptr) { - checker::Type *prop_type = checker->GetTypeOfVariable(prop); - if (prop->HasFlag(varbinder::VariableFlags::READONLY)) { - prop_type->AddTypeFlag(checker::TypeFlag::READONLY); - } - - return prop_type; - } - - if (base_type->IsObjectType()) { - checker::ObjectType *obj_type = base_type->AsObjectType(); - - if (obj_type->StringIndexInfo() != nullptr) { - checker::Type *index_type = obj_type->StringIndexInfo()->GetType(); - if (obj_type->StringIndexInfo()->Readonly()) { - index_type->AddTypeFlag(checker::TypeFlag::READONLY); - } - - return index_type; - } - } - - checker->ThrowTypeError({"Property ", property_->AsIdentifier()->Name(), " does not exist on this type."}, - property_->Start()); - return nullptr; -} - -checker::Type *MemberExpression::CheckEnumMember(checker::ETSChecker *checker, checker::Type *type) -{ - auto const *const enum_interface = [type]() -> checker::ETSEnumInterface const * { - if (type->IsETSEnumType()) { - return type->AsETSEnumType(); - } - return type->AsETSStringEnumType(); - }(); - - if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) { - auto *const enum_method_type = enum_interface->LookupMethod(checker, object_, property_->AsIdentifier()); - SetTsType(enum_method_type); - return TsType(); - } - - auto *const enum_literal_type = enum_interface->LookupConstant(checker, object_, property_->AsIdentifier()); - SetTsType(enum_literal_type); - SetPropVar(enum_literal_type->GetMemberVar()); - return TsType(); -} - -checker::Type *MemberExpression::CheckObjectMember(checker::ETSChecker *checker) -{ - auto resolve_res = checker->ResolveMemberReference(this, obj_type_); - ASSERT(!resolve_res.empty()); - checker::Type *type_to_set = nullptr; - switch (resolve_res.size()) { - case 1: { - if (resolve_res[0]->Kind() == checker::ResolvedKind::PROPERTY) { - prop_var_ = resolve_res[0]->Variable()->AsLocalVariable(); - checker->ValidatePropertyAccess(prop_var_, obj_type_, property_->Start()); - type_to_set = checker->GetTypeOfVariable(prop_var_); - } else { - type_to_set = checker->GetTypeOfVariable(resolve_res[0]->Variable()); - } - break; - } - case 2U: { - // ETSExtensionFuncHelperType(class_method_type, extension_method_type) - type_to_set = checker->CreateETSExtensionFuncHelperType( - checker->GetTypeOfVariable(resolve_res[1]->Variable())->AsETSFunctionType(), - checker->GetTypeOfVariable(resolve_res[0]->Variable())->AsETSFunctionType()); - break; - } - default: { - UNREACHABLE(); - } - } - SetTsType(type_to_set); - if (prop_var_ != nullptr && prop_var_->TsType() != nullptr && prop_var_->TsType()->IsETSFunctionType()) { - for (auto *sig : prop_var_->TsType()->AsETSFunctionType()->CallSignatures()) { - if (sig->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) { - sig->OwnerVar()->Declaration()->Node()->Check(checker); - } - } - } - return TsType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *MemberExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - if (computed_) { - SetTsType(checker->CheckArrayElementAccess(this)); - return TsType(); - } - - checker::Type *const base_type = object_->Check(checker); - - if (!base_type->IsETSObjectType()) { - if (base_type->IsETSArrayType() && property_->AsIdentifier()->Name().Is("length")) { - SetTsType(checker->GlobalIntType()); - return TsType(); - } - - if (base_type->IsETSUnionType()) { - auto *const union_type = base_type->AsETSUnionType(); - checker::Type *member_type = nullptr; - auto check_member_type = [this, checker, &member_type]() { - if (member_type != nullptr && member_type != TsType()) { - checker->ThrowTypeError("Member type must be the same for all union objects.", Start()); - } - member_type = TsType(); - }; - for (auto *type : union_type->ConstituentTypes()) { - if (type->IsETSObjectType()) { - obj_type_ = type->AsETSObjectType(); - CheckObjectMember(checker); - check_member_type(); - } - - if (type->IsETSEnumType() || base_type->IsETSStringEnumType()) { - CheckEnumMember(checker, type); - check_member_type(); - } - } - obj_type_ = union_type->GetLeastUpperBoundType(checker)->AsETSObjectType(); - return TsType(); - } - - if (base_type->IsETSEnumType() || base_type->IsETSStringEnumType()) { - return CheckEnumMember(checker, base_type); - } - - checker->ThrowTypeError({"Cannot access property of non-object or non-enum type"}, object_->Start()); - } - - obj_type_ = base_type->AsETSObjectType(); - return CheckObjectMember(checker); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/memberExpression.h b/ets2panda/ir/expressions/memberExpression.h index 074a218537625cdc8d1bedc631b791ff34b23a7f..d70916a906af7f574bab924a1b976dee10082cc7 100644 --- a/ets2panda/ir/expressions/memberExpression.h +++ b/ets2panda/ir/expressions/memberExpression.h @@ -21,6 +21,11 @@ #include "ir/expression.h" #include "ir/irnode.h" +namespace panda::es2panda::compiler { +class JSCompiler; +class ETSCompiler; +} // namespace panda::es2panda::compiler + namespace panda::es2panda::checker { class ETSObjectType; } // namespace panda::es2panda::checker @@ -60,6 +65,10 @@ public: explicit MemberExpression(Tag tag, Expression *object, Expression *property); + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class compiler::JSCompiler; + friend class compiler::ETSCompiler; + [[nodiscard]] Expression *Object() noexcept { return object_; @@ -147,9 +156,6 @@ public: [[nodiscard]] bool IsPrivateReference() const noexcept; - checker::Type *CheckEnumMember(checker::ETSChecker *checker, checker::Type *type); - checker::Type *CheckObjectMember(checker::ETSChecker *checker); - // NOLINTNEXTLINE(google-default-arguments) [[nodiscard]] Expression *Clone(ArenaAllocator *allocator, AstNode *parent = nullptr) override; @@ -162,7 +168,7 @@ public: void CompileToReg(compiler::PandaGen *pg, compiler::VReg obj_reg) const; void CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const; checker::Type *Check(checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; protected: MemberExpression(MemberExpression const &other) : Expression(static_cast(other)) diff --git a/ets2panda/ir/expressions/newExpression.cpp b/ets2panda/ir/expressions/newExpression.cpp index 565ba8caa42aac382506643a66d32de7706a6e23..afd01242f22aab6b680c40a9412c6ef91a41662a 100644 --- a/ets2panda/ir/expressions/newExpression.cpp +++ b/ets2panda/ir/expressions/newExpression.cpp @@ -15,12 +15,10 @@ #include "newExpression.h" -#include "util/helpers.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" -#include "compiler/core/regScope.h" #include "checker/TSchecker.h" -#include "ir/astDump.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "util/helpers.h" namespace panda::es2panda::ir { NewExpression::NewExpression([[maybe_unused]] Tag const tag, NewExpression const &other, @@ -73,53 +71,21 @@ void NewExpression::Dump(ir::AstDumper *dumper) const void NewExpression::Compile(compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - compiler::VReg ctor = pg->AllocReg(); - compiler::VReg new_target = pg->AllocReg(); - - callee_->Compile(pg); - pg->StoreAccumulator(this, ctor); - - // new.Target will be the same as ctor - pg->StoreAccumulator(this, new_target); - - if (!util::Helpers::ContainSpreadElement(arguments_) && - arguments_.size() < compiler::PandaGen::MAX_RANGE_CALL_ARG) { - for (const auto *it : arguments_) { - compiler::VReg arg = pg->AllocReg(); - it->Compile(pg); - pg->StoreAccumulator(this, arg); - } - - pg->NewObject(this, ctor, arguments_.size() + 2U); - } else { - compiler::VReg args_obj = pg->AllocReg(); - - pg->CreateArray(this, arguments_, args_obj); - pg->NewObjSpread(this, ctor, new_target); - } + pg->GetAstCompiler()->Compile(this); } -void NewExpression::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void NewExpression::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *NewExpression::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *NewExpression::Check(checker::TSChecker *checker) { - checker::Type *callee_type = callee_->Check(checker); - - if (callee_type->IsObjectType()) { - checker::ObjectType *callee_obj = callee_type->AsObjectType(); - return checker->ResolveCallOrNewExpression(callee_obj->ConstructSignatures(), arguments_, Start()); - } - - checker->ThrowTypeError("This expression is not callable.", Start()); - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *NewExpression::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *NewExpression::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/newExpression.h b/ets2panda/ir/expressions/newExpression.h index 2cefd3ce6d746341f26e6e2d26aca857bb0219c0..2183cb9576991af01200bcf18734425d49dd385b 100644 --- a/ets2panda/ir/expressions/newExpression.h +++ b/ets2panda/ir/expressions/newExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class NewExpression : public Expression { private: @@ -34,9 +38,11 @@ public: : Expression(AstNodeType::NEW_EXPRESSION), callee_(callee), arguments_(std::move(arguments)) { } - explicit NewExpression(Tag tag, NewExpression const &other, ArenaAllocator *allocator); + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::TSAnalyzer; + [[nodiscard]] const Expression *Callee() const noexcept { return callee_; @@ -56,7 +62,7 @@ public: void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *callee_ = nullptr; diff --git a/ets2panda/ir/expressions/omittedExpression.cpp b/ets2panda/ir/expressions/omittedExpression.cpp index b24ea45bebab63c11dc55a32528f0a71ea030e1e..6b08d81d02c93b1abb2cda91494fcf73b7722d25 100644 --- a/ets2panda/ir/expressions/omittedExpression.cpp +++ b/ets2panda/ir/expressions/omittedExpression.cpp @@ -15,8 +15,10 @@ #include "omittedExpression.h" -#include "ir/astDump.h" #include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +// #include "ir/astDump.h" namespace panda::es2panda::ir { void OmittedExpression::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -27,16 +29,24 @@ void OmittedExpression::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "OmittedExpression"}}); } -void OmittedExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void OmittedExpression::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} + +void OmittedExpression::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} -checker::Type *OmittedExpression::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *OmittedExpression::Check(checker::TSChecker *checker) { - return checker->GlobalUndefinedType(); + return checker->GetAnalyzer()->Check(this); } -checker::Type *OmittedExpression::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *OmittedExpression::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/omittedExpression.h b/ets2panda/ir/expressions/omittedExpression.h index ee722bf0c336ee9e36d8d58fc9d017e7f02ca9db..5ff6b4fe99b941493d8c87dab0432308d95105b0 100644 --- a/ets2panda/ir/expressions/omittedExpression.h +++ b/ets2panda/ir/expressions/omittedExpression.h @@ -35,9 +35,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; }; } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/sequenceExpression.cpp b/ets2panda/ir/expressions/sequenceExpression.cpp index cde77a042f784b3d3574295e4c64f03e31ef02f6..edf20e42de20cc8780480a94b5e72ebc7ae43662 100644 --- a/ets2panda/ir/expressions/sequenceExpression.cpp +++ b/ets2panda/ir/expressions/sequenceExpression.cpp @@ -15,8 +15,10 @@ #include "sequenceExpression.h" +#include "checker/ETSchecker.h" #include "checker/TSchecker.h" -#include "ir/astDump.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { SequenceExpression::SequenceExpression([[maybe_unused]] Tag const tag, SequenceExpression const &other, @@ -59,35 +61,23 @@ void SequenceExpression::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "SequenceExpression"}, {"expressions", sequence_}}); } -void SequenceExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void SequenceExpression::Compile(compiler::PandaGen *pg) const { - for (const auto *it : sequence_) { - it->Compile(pg); - } + pg->GetAstCompiler()->Compile(this); } void SequenceExpression::Compile(compiler::ETSGen *etsg) const { - for (const auto *it : sequence_) { - it->Compile(etsg); - } + etsg->GetAstCompiler()->Compile(this); } -checker::Type *SequenceExpression::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *SequenceExpression::Check(checker::TSChecker *checker) { - // NOTE: aszilagyi. - return checker->GlobalAnyType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *SequenceExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - for (auto *it : sequence_) { - it->Check(checker); - } - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/sequenceExpression.h b/ets2panda/ir/expressions/sequenceExpression.h index 29a1fd2aec43f64f48f98152123afb97c7f8a0d6..50cf4e4a3fc86df599b18b710a9954d99a0e5b7f 100644 --- a/ets2panda/ir/expressions/sequenceExpression.h +++ b/ets2panda/ir/expressions/sequenceExpression.h @@ -53,9 +53,9 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; checker::Type *Check(checker::ETSChecker *checker) override; private: diff --git a/ets2panda/ir/expressions/superExpression.cpp b/ets2panda/ir/expressions/superExpression.cpp index c70a15e50f7fe4a640774ef7e2c768c52f7e198d..234cedbfc15511a5523e2f1551f8eaf7577ff2da 100644 --- a/ets2panda/ir/expressions/superExpression.cpp +++ b/ets2panda/ir/expressions/superExpression.cpp @@ -15,12 +15,11 @@ #include "superExpression.h" -#include "util/helpers.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" -#include "ir/astDump.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "util/helpers.h" namespace panda::es2panda::ir { void SuperExpression::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -33,34 +32,22 @@ void SuperExpression::Dump(ir::AstDumper *dumper) const void SuperExpression::Compile(compiler::PandaGen *pg) const { - pg->GetThis(this); - - const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(this); - - if (func != nullptr) { - pg->ThrowIfSuperNotCorrectCall(this, 0); - } + pg->GetAstCompiler()->Compile(this); } void SuperExpression::Compile(compiler::ETSGen *etsg) const { - etsg->LoadThis(this); + etsg->GetAstCompiler()->Compile(this); } checker::Type *SuperExpression::Check(checker::TSChecker *checker) { - // NOTE: aszilagyi. - return checker->GlobalAnyType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *SuperExpression::Check([[maybe_unused]] checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - SetTsType(checker->CheckThisOrSuperAccess(this, checker->Context().ContainingClass()->SuperType(), "super")); - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/superExpression.h b/ets2panda/ir/expressions/superExpression.h index 31b2835975906406105493339f80923749dc2e28..fdbfd9146eea3c536507eaae5d56bd75bb660b27 100644 --- a/ets2panda/ir/expressions/superExpression.h +++ b/ets2panda/ir/expressions/superExpression.h @@ -37,7 +37,7 @@ public: void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: };