From 6a2deffda2554bc9446f7b946a9bbfaaaf2cf78d Mon Sep 17 00:00:00 2001 From: Soma Simon Date: Thu, 12 Oct 2023 13:48:25 +0200 Subject: [PATCH] Move Compile and check logic from ASTNode classes Compile logic is moved to JSCompiler and ETSCompiler classes. Check logic with their helper functions is moved to TSAnalyzer and ETSAnalyzer classes. TemplateLiteral, ThisExpression, ThrowStatement, TryStatement, UnaryExpression Linked Internal issue: 13840 Change-Id: I356d5f1c700281d08969a3708d57423f773ef399 Signed-off-by: Soma Simon --- ets2panda/checker/ETSAnalyzer.cpp | 208 ++++++++++++++-- ets2panda/checker/TSAnalyzer.cpp | 128 +++++++++- ets2panda/checker/TSAnalyzer.h | 3 + ets2panda/compiler/core/ETSCompiler.cpp | 58 ++++- ets2panda/compiler/core/JSCompiler.cpp | 245 +++++++++++++++++- ets2panda/ir/expressions/templateLiteral.cpp | 59 +---- ets2panda/ir/expressions/thisExpression.cpp | 59 +---- ets2panda/ir/expressions/unaryExpression.cpp | 249 +------------------ ets2panda/ir/expressions/unaryExpression.h | 7 +- ets2panda/ir/statements/throwStatement.cpp | 16 +- ets2panda/ir/statements/throwStatement.h | 6 + ets2panda/ir/statements/tryStatement.cpp | 193 +------------- ets2panda/ir/statements/tryStatement.h | 18 +- 13 files changed, 634 insertions(+), 615 deletions(-) diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index ec729cf229..32459f100c 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -20,14 +20,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 { @@ -480,20 +472,159 @@ checker::Type *ETSAnalyzer::Check(ir::TaggedTemplateExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::TemplateLiteral *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + if (expr->Quasis().size() != expr->Expressions().size() + 1U) { + checker->ThrowTypeError("Invalid string template expression", expr->Start()); + } + + for (auto *it : expr->Expressions()) { + it->Check(checker); + } + + for (auto *it : expr->Quasis()) { + it->Check(checker); + } + + expr->SetTsType(checker->GlobalBuiltinETSStringType()); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::ThisExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + /* + example code: + ``` + class A { + prop + } + function A.method() { + let a = () => { + console.println(this.prop) + } + } + is identical to + function method(this: A) { + let a = () => { + console.println(this.prop) + } + } + ``` + here when "this" is used inside an extension function, we need to bind "this" to the first + parameter(MANDATORY_PARAM_THIS), and capture the paramter's variable other than containing class's variable + */ + auto *variable = checker->AsETSChecker()->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable; + if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + ASSERT(variable != nullptr); + expr->SetTsType(variable->TsType()); + } else { + expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass(), "this")); + } + + if (checker->HasStatus(checker::CheckerStatus::IN_LAMBDA)) { + if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + checker->Context().AddCapturedVar(variable, expr->Start()); + } else { + checker->Context().AddCapturedVar(checker->Context().ContainingClass()->Variable(), expr->Start()); + } + } + + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + auto arg_type = expr->argument_->Check(checker); + const auto is_cond_expr = expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK; + checker::Type *operand_type = checker->ApplyUnaryOperatorPromotion(arg_type, true, true, is_cond_expr); + auto unboxed_operand_type = is_cond_expr ? checker->ETSBuiltinTypeAsConditionalType(arg_type) + : checker->ETSBuiltinTypeAsPrimitiveType(arg_type); + + switch (expr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_PLUS: { + if (operand_type == nullptr || !operand_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + checker->ThrowTypeError("Bad operand type, the type of the operand must be numeric type.", + expr->Argument()->Start()); + } + + if (operand_type->HasTypeFlag(checker::TypeFlag::CONSTANT) && + expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) { + expr->SetTsType(checker->NegateNumericType(operand_type, expr)); + break; + } + + expr->SetTsType(operand_type); + break; + } + case lexer::TokenType::PUNCTUATOR_TILDE: { + if (operand_type == nullptr || !operand_type->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)) { + checker->ThrowTypeError("Bad operand type, the type of the operand must be integral type.", + expr->Argument()->Start()); + } + + if (operand_type->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + expr->SetTsType(checker->BitwiseNegateIntegralType(operand_type, expr)); + break; + } + + expr->SetTsType(operand_type); + break; + } + case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { + if (checker->IsNullLikeOrVoidExpression(expr->Argument())) { + auto ts_type = checker->CreateETSBooleanType(true); + ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); + expr->SetTsType(ts_type); + break; + } + + if (operand_type == nullptr || !operand_type->IsConditionalExprType()) { + checker->ThrowTypeError("Bad operand type, the type of the operand must be boolean type.", + expr->Argument()->Start()); + } + + auto expr_res = operand_type->ResolveConditionExpr(); + if (std::get<0>(expr_res)) { + auto ts_type = checker->CreateETSBooleanType(!std::get<1>(expr_res)); + ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); + expr->SetTsType(ts_type); + break; + } + + expr->SetTsType(operand_type); + break; + } + case lexer::TokenType::PUNCTUATOR_DOLLAR_DOLLAR: { + expr->SetTsType(arg_type); + break; + } + default: { + UNREACHABLE(); + break; + } + } + + if (arg_type->IsETSObjectType() && (unboxed_operand_type != nullptr) && + unboxed_operand_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + expr->Argument()->AddBoxingUnboxingFlag(checker->GetUnboxingFlag(unboxed_operand_type)); + } + + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const @@ -921,14 +1052,55 @@ checker::Type *ETSAnalyzer::Check(ir::SwitchStatement *st) const checker::Type *ETSAnalyzer::Check(ir::ThrowStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + auto *arg_type = st->argument_->Check(checker); + checker->CheckExceptionOrErrorType(arg_type, st->Start()); + + if (checker->Relation()->IsAssignableTo(arg_type, checker->GlobalBuiltinExceptionType())) { + checker->CheckThrowingStatements(st); + } + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::TryStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + std::vector exceptions; + st->Block()->Check(checker); + + for (auto *catch_clause : st->CatchClauses()) { + auto exception_type = catch_clause->Check(checker); + + if ((exception_type != nullptr) && (catch_clause->Param() != nullptr)) { + auto *clause_type = exception_type->AsETSObjectType(); + + for (auto *exception : exceptions) { + checker->Relation()->IsIdenticalTo(clause_type, exception); + if (checker->Relation()->IsTrue()) { + checker->ThrowTypeError("Redeclaration of exception type", catch_clause->Start()); + } + } + + exceptions.push_back(clause_type); + } + } + + bool default_catch_found = false; + + for (auto *catch_clause : st->CatchClauses()) { + if (default_catch_found) { + checker->ThrowTypeError("Default catch clause should be the last in the try statement", + catch_clause->Start()); + } + + default_catch_found = catch_clause->IsDefaultCatchClause(); + } + + if (st->HasFinalizer()) { + st->finalizer_->Check(checker); + } + + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::VariableDeclarator *st) const diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index d3e96e821f..a36b0c9d8f 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -375,22 +375,115 @@ checker::Type *TSAnalyzer::Check(ir::TaggedTemplateExpression *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::TemplateLiteral *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TemplateLiteral *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // TODO(aszilagyi) + return checker->GlobalAnyType(); } -checker::Type *TSAnalyzer::Check(ir::ThisExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ThisExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // NOTE: aszilagyi + return checker->GlobalAnyType(); +} + +checker::Type *TSAnalyzer::CheckDeleteKeyword([[maybe_unused]] checker::TSChecker *checker, + ir::UnaryExpression *expr) const +{ + checker::Type *prop_type = expr->argument_->Check(checker); + if (!expr->Argument()->IsMemberExpression()) { + checker->ThrowTypeError("The operand of a delete operator must be a property reference.", + expr->Argument()->Start()); + } + if (prop_type->Variable()->HasFlag(varbinder::VariableFlags::READONLY)) { + checker->ThrowTypeError("The operand of a delete operator cannot be a readonly property.", + expr->Argument()->Start()); + } + if (!prop_type->Variable()->HasFlag(varbinder::VariableFlags::OPTIONAL)) { + checker->ThrowTypeError("The operand of a delete operator must be a optional.", expr->Argument()->Start()); + } + return checker->GlobalBooleanType(); +} + +checker::Type *TSAnalyzer::CheckLiteral([[maybe_unused]] checker::TSChecker *checker, ir::UnaryExpression *expr) const +{ + if (!expr->Argument()->IsLiteral()) { + return nullptr; + } + + const ir::Literal *lit = expr->Argument()->AsLiteral(); + if (lit->IsNumberLiteral()) { + auto number_value = lit->AsNumberLiteral()->Number().GetDouble(); + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) { + return checker->CreateNumberLiteralType(number_value); + } + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) { + return checker->CreateNumberLiteralType(-number_value); + } + } else if (lit->IsBigIntLiteral() && expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) { + return checker->CreateBigintLiteralType(lit->AsBigIntLiteral()->Str(), true); + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::UnaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *operand_type = expr->argument_->Check(checker); + + if (expr->operator_ == lexer::TokenType::KEYW_TYPEOF) { + return operand_type; + } + + if (expr->operator_ == lexer::TokenType::KEYW_DELETE) { + return CheckDeleteKeyword(checker, expr); + } + + auto *res = CheckLiteral(checker, expr); + if (res != nullptr) { + return res; + } + + switch (expr->operator_) { + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_TILDE: { + checker->CheckNonNullType(operand_type, expr->Start()); + // NOTE: aszilagyi. check Symbol like types + + if (expr->operator_ == lexer::TokenType::PUNCTUATOR_PLUS) { + if (checker::TSChecker::MaybeTypeOfKind(operand_type, checker::TypeFlag::BIGINT_LIKE)) { + checker->ThrowTypeError({"Operator '+' cannot be applied to type '", operand_type, "'"}, + expr->Start()); + } + + return checker->GlobalNumberType(); + } + + return checker->GetUnaryResultType(operand_type); + } + case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { + checker->CheckTruthinessOfType(operand_type, expr->Start()); + auto facts = operand_type->GetTypeFacts(); + if ((facts & checker::TypeFacts::TRUTHY) != 0) { + return checker->GlobalFalseType(); + } + + if ((facts & checker::TypeFacts::FALSY) != 0) { + return checker->GlobalTrueType(); + } + + return checker->GlobalBooleanType(); + } + default: { + UNREACHABLE(); + } + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::UpdateExpression *expr) const @@ -635,16 +728,27 @@ checker::Type *TSAnalyzer::Check(ir::SwitchStatement *st) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ThrowStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ThrowStatement *st) const { - (void)st; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::TryStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + st->Block()->Check(checker); + + for (auto *catch_clause : st->CatchClauses()) { + if (catch_clause != nullptr) { + catch_clause->Check(checker); + } + } + + if (st->HasFinalizer()) { + st->finalizer_->Check(checker); + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const diff --git a/ets2panda/checker/TSAnalyzer.h b/ets2panda/checker/TSAnalyzer.h index eb23fa2fc5..bfe617c6a4 100644 --- a/ets2panda/checker/TSAnalyzer.h +++ b/ets2panda/checker/TSAnalyzer.h @@ -34,6 +34,9 @@ public: AST_NODE_REINTERPRET_MAPPING(DECLARE_TSANALYZER_CHECK_METHOD) #undef DECLARE_TSANALYZER_CHECK_METHOD + checker::Type *CheckDeleteKeyword([[maybe_unused]] checker::TSChecker *checker, ir::UnaryExpression *expr) const; + checker::Type *CheckLiteral([[maybe_unused]] checker::TSChecker *checker, ir::UnaryExpression *expr) const; + private: TSChecker *GetTSChecker() const; }; diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 46dd096ae8..9032db8459 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -16,11 +16,11 @@ #include "ETSCompiler.h" #include "checker/types/ets/etsDynamicFunctionType.h" +#include "compiler/base/catchTable.h" #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" #include "compiler/function/functionBuilder.h" - namespace panda::es2panda::compiler { ETSGen *ETSCompiler::GetETSGen() const @@ -378,20 +378,25 @@ void ETSCompiler::Compile(const ir::TaggedTemplateExpression *expr) const void ETSCompiler::Compile(const ir::TemplateLiteral *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->BuildTemplateString(expr); } void ETSCompiler::Compile(const ir::ThisExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->LoadThis(expr); } void ETSCompiler::Compile(const ir::UnaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + if (!etsg->TryLoadConstantExpression(expr->Argument())) { + expr->Argument()->Compile(etsg); + } + etsg->ApplyConversion(expr->Argument(), nullptr); + etsg->Unary(expr, expr->OperatorType()); } void ETSCompiler::Compile(const ir::UpdateExpression *expr) const @@ -650,14 +655,45 @@ void ETSCompiler::Compile(const ir::SwitchStatement *st) const void ETSCompiler::Compile(const ir::ThrowStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->ThrowException(st->Argument()); } void ETSCompiler::Compile(const ir::TryStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + + compiler::ETSTryContext try_ctx(etsg, etsg->Allocator(), st, st->FinallyBlock() != nullptr); + + compiler::LabelPair try_label_pair(etsg->AllocLabel(), etsg->AllocLabel()); + + for (ir::CatchClause *clause : st->CatchClauses()) { + try_ctx.AddNewCathTable(clause->TsType()->AsETSObjectType()->AssemblerName(), try_label_pair); + } + + compiler::Label *statement_end = etsg->AllocLabel(); + auto catch_tables = try_ctx.GetETSCatchTable(); + + etsg->SetLabel(st, try_label_pair.Begin()); + st->Block()->Compile(etsg); + etsg->Branch(st, statement_end); + etsg->SetLabel(st, try_label_pair.End()); + + ASSERT(st->CatchClauses().size() == catch_tables.size()); + + for (uint32_t i = 0; i < st->CatchClauses().size(); i++) { + etsg->SetLabel(st, catch_tables.at(i)->LabelSet().CatchBegin()); + + st->CatchClauses().at(i)->Compile(etsg); + + etsg->Branch(st, statement_end); + } + + etsg->SetLabel(st, statement_end); + + auto trycatch_label_pair = compiler::LabelPair(try_label_pair.Begin(), statement_end); + + try_ctx.EmitFinalizer(trycatch_label_pair, st->finalizer_insertions_); } void ETSCompiler::Compile(const ir::VariableDeclarator *st) const diff --git a/ets2panda/compiler/core/JSCompiler.cpp b/ets2panda/compiler/core/JSCompiler.cpp index 10a894d364..92afffddbd 100644 --- a/ets2panda/compiler/core/JSCompiler.cpp +++ b/ets2panda/compiler/core/JSCompiler.cpp @@ -15,12 +15,13 @@ #include "JSCompiler.h" +#include "varbinder/varbinder.h" +#include "compiler/base/catchTable.h" #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" #include "compiler/function/functionBuilder.h" #include "util/helpers.h" - namespace panda::es2panda::compiler { PandaGen *JSCompiler::GetPandaGen() const @@ -680,20 +681,134 @@ void JSCompiler::Compile(const ir::TaggedTemplateExpression *expr) const void JSCompiler::Compile(const ir::TemplateLiteral *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + auto quasis_it = expr->Quasis().begin(); + auto expression_it = expr->Expressions().begin(); + + pg->LoadAccumulatorString(expr, (*quasis_it)->Raw()); + + quasis_it++; + + bool is_quais = false; + size_t total = expr->Quasis().size() + expr->Expressions().size(); + + compiler::RegScope rs(pg); + compiler::VReg lhs = pg->AllocReg(); + + while (total != 1) { + const ir::AstNode *node = nullptr; + + if (is_quais) { + pg->StoreAccumulator(*quasis_it, lhs); + pg->LoadAccumulatorString(expr, (*quasis_it)->Raw()); + + node = *quasis_it; + quasis_it++; + } else { + const ir::Expression *element = *expression_it; + pg->StoreAccumulator(element, lhs); + + element->Compile(pg); + + node = element; + expression_it++; + } + + pg->Binary(node, lexer::TokenType::PUNCTUATOR_PLUS, lhs); + + is_quais = !is_quais; + total--; + } } void JSCompiler::Compile(const ir::ThisExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + auto res = pg->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS); + + ASSERT(res.variable && res.variable->IsLocalVariable()); + pg->LoadAccFromLexEnv(expr, res); + + const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(expr); + + if (func != nullptr) { + pg->ThrowIfSuperNotCorrectCall(expr, 0); + } } void JSCompiler::Compile(const ir::UnaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + switch (expr->OperatorType()) { + case lexer::TokenType::KEYW_DELETE: { + if (expr->Argument()->IsIdentifier()) { + auto result = pg->Scope()->Find(expr->Argument()->AsIdentifier()->Name()); + if (result.variable == nullptr || + (result.scope->IsGlobalScope() && result.variable->IsGlobalVariable())) { + compiler::RegScope rs(pg); + compiler::VReg variable = pg->AllocReg(); + compiler::VReg global = pg->AllocReg(); + + pg->LoadConst(expr, compiler::Constant::JS_GLOBAL); + pg->StoreAccumulator(expr, global); + + pg->LoadAccumulatorString(expr, expr->Argument()->AsIdentifier()->Name()); + pg->StoreAccumulator(expr, variable); + + pg->DeleteObjProperty(expr, global, variable); + } else { + // Otherwise it is a local variable which can't be deleted and we just + // return false. + pg->LoadConst(expr, compiler::Constant::JS_FALSE); + } + } else if (expr->Argument()->IsMemberExpression()) { + compiler::RegScope rs(pg); + compiler::VReg object = pg->AllocReg(); + compiler::VReg property = pg->AllocReg(); + + expr->Argument()->AsMemberExpression()->CompileToRegs(pg, object, property); + pg->DeleteObjProperty(expr, object, property); + } else { + // compile the delete operand. + expr->Argument()->Compile(pg); + // Deleting any value or a result of an expression returns True. + pg->LoadConst(expr, compiler::Constant::JS_TRUE); + } + break; + } + case lexer::TokenType::KEYW_TYPEOF: { + if (expr->Argument()->IsIdentifier()) { + const auto *ident = expr->Argument()->AsIdentifier(); + + auto res = pg->Scope()->Find(ident->Name()); + if (res.variable == nullptr) { + pg->LoadConst(expr, compiler::Constant::JS_GLOBAL); + pg->LoadObjByName(expr, ident->Name()); + } else { + pg->LoadVar(ident, res); + } + } else { + expr->Argument()->Compile(pg); + } + + pg->TypeOf(expr); + break; + } + case lexer::TokenType::KEYW_VOID: { + expr->Argument()->Compile(pg); + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + break; + } + default: { + expr->Argument()->Compile(pg); + + compiler::RegScope rs(pg); + compiler::VReg operand_reg = pg->AllocReg(); + pg->StoreAccumulator(expr, operand_reg); + pg->Unary(expr, expr->OperatorType(), operand_reg); + break; + } + } } void JSCompiler::Compile(const ir::UpdateExpression *expr) const @@ -935,14 +1050,122 @@ void JSCompiler::Compile(const ir::SwitchStatement *st) const void JSCompiler::Compile(const ir::ThrowStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + st->Argument()->Compile(pg); + pg->EmitThrow(st); +} + +static void CompileTryCatch(compiler::PandaGen *pg, const ir::TryStatement *st) +{ + ASSERT(st->CatchClauses().size() == 1); + ASSERT(st->CatchClauses().front() && !st->FinallyBlock()); + + compiler::TryContext try_ctx(pg, st); + const auto &label_set = try_ctx.LabelSet(); + + pg->SetLabel(st, label_set.TryBegin()); + st->Block()->Compile(pg); + pg->SetLabel(st, label_set.TryEnd()); + + pg->Branch(st, label_set.CatchEnd()); + + pg->SetLabel(st, label_set.CatchBegin()); + st->CatchClauses().front()->Compile(pg); + pg->SetLabel(st, label_set.CatchEnd()); +} + +static void CompileFinally(compiler::PandaGen *pg, compiler::TryContext *try_ctx, + const compiler::TryLabelSet &label_set, const ir::TryStatement *st) +{ + compiler::RegScope rs(pg); + compiler::VReg exception = pg->AllocReg(); + pg->StoreConst(st, exception, compiler::Constant::JS_HOLE); + pg->Branch(st, label_set.CatchEnd()); + + pg->SetLabel(st, label_set.CatchBegin()); + pg->StoreAccumulator(st, exception); + + pg->SetLabel(st, label_set.CatchEnd()); + + compiler::Label *label = pg->AllocLabel(); + pg->LoadAccumulator(st, try_ctx->FinalizerRun()); + + pg->BranchIfNotUndefined(st, label); + pg->StoreAccumulator(st, try_ctx->FinalizerRun()); + try_ctx->EmitFinalizer(); + pg->SetLabel(st, label); + + pg->LoadAccumulator(st, exception); + pg->EmitRethrow(st); +} + +static void CompileTryCatchFinally(compiler::PandaGen *pg, const ir::TryStatement *st) +{ + ASSERT(st->CatchClauses().size() == 1); + ASSERT(st->CatchClauses().front() && st->FinallyBlock()); + + compiler::TryContext try_ctx(pg, st); + const auto &label_set = try_ctx.LabelSet(); + + pg->SetLabel(st, label_set.TryBegin()); + { + compiler::TryContext inner_try_ctx(pg, st, false); + const auto &inner_label_set = inner_try_ctx.LabelSet(); + + pg->SetLabel(st, inner_label_set.TryBegin()); + st->Block()->Compile(pg); + pg->SetLabel(st, inner_label_set.TryEnd()); + + pg->Branch(st, inner_label_set.CatchEnd()); + + pg->SetLabel(st, inner_label_set.CatchBegin()); + st->CatchClauses().front()->Compile(pg); + pg->SetLabel(st, inner_label_set.CatchEnd()); + } + pg->SetLabel(st, label_set.TryEnd()); + + CompileFinally(pg, &try_ctx, label_set, st); +} + +static void CompileTryFinally(compiler::PandaGen *pg, const ir::TryStatement *st) +{ + ASSERT(st->CatchClauses().empty() && st->FinallyBlock()); + + compiler::TryContext try_ctx(pg, st); + const auto &label_set = try_ctx.LabelSet(); + + pg->SetLabel(st, label_set.TryBegin()); + { + compiler::TryContext inner_try_ctx(pg, st, false); + const auto &inner_label_set = inner_try_ctx.LabelSet(); + + pg->SetLabel(st, inner_label_set.TryBegin()); + st->Block()->Compile(pg); + pg->SetLabel(st, inner_label_set.TryEnd()); + + pg->Branch(st, inner_label_set.CatchEnd()); + + pg->SetLabel(st, inner_label_set.CatchBegin()); + pg->EmitThrow(st); + pg->SetLabel(st, inner_label_set.CatchEnd()); + } + pg->SetLabel(st, label_set.TryEnd()); + + CompileFinally(pg, &try_ctx, label_set, st); } void JSCompiler::Compile(const ir::TryStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + if (st->finalizer_ != nullptr) { + if (!st->CatchClauses().empty()) { + CompileTryCatchFinally(pg, st); + } else { + CompileTryFinally(pg, st); + } + } else { + CompileTryCatch(pg, st); + } } void JSCompiler::Compile(const ir::VariableDeclarator *st) const diff --git a/ets2panda/ir/expressions/templateLiteral.cpp b/ets2panda/ir/expressions/templateLiteral.cpp index 82a52458f6..ff8a2a4872 100644 --- a/ets2panda/ir/expressions/templateLiteral.cpp +++ b/ets2panda/ir/expressions/templateLiteral.cpp @@ -79,43 +79,7 @@ void TemplateLiteral::Dump(ir::AstDumper *dumper) const void TemplateLiteral::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - auto quasis_it = quasis_.begin(); - auto expression_it = expressions_.begin(); - - pg->LoadAccumulatorString(this, (*quasis_it)->Raw()); - - quasis_it++; - - bool is_quais = false; - size_t total = quasis_.size() + expressions_.size(); - - compiler::RegScope rs(pg); - compiler::VReg lhs = pg->AllocReg(); - - while (total != 1) { - const ir::AstNode *node = nullptr; - - if (is_quais) { - pg->StoreAccumulator(*quasis_it, lhs); - pg->LoadAccumulatorString(this, (*quasis_it)->Raw()); - - node = *quasis_it; - quasis_it++; - } else { - const ir::Expression *element = *expression_it; - pg->StoreAccumulator(element, lhs); - - element->Compile(pg); - - node = element; - expression_it++; - } - - pg->Binary(node, lexer::TokenType::PUNCTUATOR_PLUS, lhs); - - is_quais = !is_quais; - total--; - } + pg->GetAstCompiler()->Compile(this); } checker::Type *TemplateLiteral::Check([[maybe_unused]] checker::TSChecker *checker) @@ -126,28 +90,11 @@ checker::Type *TemplateLiteral::Check([[maybe_unused]] checker::TSChecker *check void TemplateLiteral::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - etsg->BuildTemplateString(this); + etsg->GetAstCompiler()->Compile(this); } checker::Type *TemplateLiteral::Check([[maybe_unused]] checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - if (quasis_.size() != expressions_.size() + 1U) { - checker->ThrowTypeError("Invalid string template expression", this->Start()); - } - - for (auto *it : expressions_) { - it->Check(checker); - } - - for (auto *it : quasis_) { - it->Check(checker); - } - - SetTsType(checker->GlobalBuiltinETSStringType()); - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/thisExpression.cpp b/ets2panda/ir/expressions/thisExpression.cpp index 4bfd275441..354aeb6568 100644 --- a/ets2panda/ir/expressions/thisExpression.cpp +++ b/ets2panda/ir/expressions/thisExpression.cpp @@ -39,73 +39,22 @@ void ThisExpression::Dump(ir::AstDumper *dumper) const void ThisExpression::Compile(compiler::PandaGen *pg) const { - auto res = pg->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS); - - ASSERT(res.variable && res.variable->IsLocalVariable()); - pg->LoadAccFromLexEnv(this, res); - - const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(this); - - if (func != nullptr) { - pg->ThrowIfSuperNotCorrectCall(this, 0); - } + pg->GetAstCompiler()->Compile(this); } void ThisExpression::Compile(compiler::ETSGen *etsg) const { - etsg->LoadThis(this); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ThisExpression::Check(checker::TSChecker *checker) { - // NOTE: aszilagyi - return checker->GlobalAnyType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *ThisExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - /* - example code: - ``` - class A { - prop - } - function A.method() { - let a = () => { - console.println(this.prop) - } - } - is identical to - function method(this: A) { - let a = () => { - console.println(this.prop) - } - } - ``` - here when "this" is used inside an extension function, we need to bind "this" to the first - parameter(MANDATORY_PARAM_THIS), and capture the paramter's variable other than containing class's variable - */ - auto *variable = checker->AsETSChecker()->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable; - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { - ASSERT(variable != nullptr); - SetTsType(variable->TsType()); - } else { - SetTsType(checker->CheckThisOrSuperAccess(this, checker->Context().ContainingClass(), "this")); - } - - if (checker->HasStatus(checker::CheckerStatus::IN_LAMBDA)) { - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { - checker->Context().AddCapturedVar(variable, this->Start()); - } else { - checker->Context().AddCapturedVar(checker->Context().ContainingClass()->Variable(), this->Start()); - } - } - - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/unaryExpression.cpp b/ets2panda/ir/expressions/unaryExpression.cpp index 2f6ced1150..5a39af9ae0 100644 --- a/ets2panda/ir/expressions/unaryExpression.cpp +++ b/ets2panda/ir/expressions/unaryExpression.cpp @@ -45,263 +45,22 @@ void UnaryExpression::Dump(ir::AstDumper *dumper) const void UnaryExpression::Compile(compiler::PandaGen *pg) const { - switch (operator_) { - case lexer::TokenType::KEYW_DELETE: { - if (argument_->IsIdentifier()) { - auto result = pg->Scope()->Find(argument_->AsIdentifier()->Name()); - if (result.variable == nullptr || - (result.scope->IsGlobalScope() && result.variable->IsGlobalVariable())) { - compiler::RegScope rs(pg); - compiler::VReg variable = pg->AllocReg(); - compiler::VReg global = pg->AllocReg(); - - pg->LoadConst(this, compiler::Constant::JS_GLOBAL); - pg->StoreAccumulator(this, global); - - pg->LoadAccumulatorString(this, argument_->AsIdentifier()->Name()); - pg->StoreAccumulator(this, variable); - - pg->DeleteObjProperty(this, global, variable); - } else { - // Otherwise it is a local variable which can't be deleted and we just - // return false. - pg->LoadConst(this, compiler::Constant::JS_FALSE); - } - } else if (argument_->IsMemberExpression()) { - compiler::RegScope rs(pg); - compiler::VReg object = pg->AllocReg(); - compiler::VReg property = pg->AllocReg(); - - argument_->AsMemberExpression()->CompileToRegs(pg, object, property); - pg->DeleteObjProperty(this, object, property); - } else { - // compile the delete operand. - argument_->Compile(pg); - // Deleting any value or a result of an expression returns True. - pg->LoadConst(this, compiler::Constant::JS_TRUE); - } - break; - } - case lexer::TokenType::KEYW_TYPEOF: { - if (argument_->IsIdentifier()) { - const auto *ident = argument_->AsIdentifier(); - - auto res = pg->Scope()->Find(ident->Name()); - if (res.variable == nullptr) { - pg->LoadConst(this, compiler::Constant::JS_GLOBAL); - pg->LoadObjByName(this, ident->Name()); - } else { - pg->LoadVar(ident, res); - } - } else { - argument_->Compile(pg); - } - - pg->TypeOf(this); - break; - } - case lexer::TokenType::KEYW_VOID: { - argument_->Compile(pg); - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - break; - } - default: { - argument_->Compile(pg); - - compiler::RegScope rs(pg); - compiler::VReg operand_reg = pg->AllocReg(); - pg->StoreAccumulator(this, operand_reg); - pg->Unary(this, operator_, operand_reg); - break; - } - } + pg->GetAstCompiler()->Compile(this); } void UnaryExpression::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - if (!etsg->TryLoadConstantExpression(argument_)) { - argument_->Compile(etsg); - } - etsg->ApplyConversion(argument_, nullptr); - etsg->Unary(this, operator_); -} - -checker::Type *UnaryExpression::CheckDeleteKeyword([[maybe_unused]] checker::TSChecker *checker) -{ - checker::Type *prop_type = argument_->Check(checker); - if (!argument_->IsMemberExpression()) { - checker->ThrowTypeError("The operand of a delete operator must be a property reference.", argument_->Start()); - } - if (prop_type->Variable()->HasFlag(varbinder::VariableFlags::READONLY)) { - checker->ThrowTypeError("The operand of a delete operator cannot be a readonly property.", argument_->Start()); - } - if (!prop_type->Variable()->HasFlag(varbinder::VariableFlags::OPTIONAL)) { - checker->ThrowTypeError("The operand of a delete operator must be a optional.", argument_->Start()); - } - return checker->GlobalBooleanType(); -} - -checker::Type *UnaryExpression::CheckLiteral([[maybe_unused]] checker::TSChecker *checker) -{ - if (!argument_->IsLiteral()) { - return nullptr; - } - - const ir::Literal *lit = argument_->AsLiteral(); - if (lit->IsNumberLiteral()) { - auto number_value = lit->AsNumberLiteral()->Number().GetDouble(); - if (operator_ == lexer::TokenType::PUNCTUATOR_PLUS) { - return checker->CreateNumberLiteralType(number_value); - } - if (operator_ == lexer::TokenType::PUNCTUATOR_MINUS) { - return checker->CreateNumberLiteralType(-number_value); - } - } else if (lit->IsBigIntLiteral() && operator_ == lexer::TokenType::PUNCTUATOR_MINUS) { - return checker->CreateBigintLiteralType(lit->AsBigIntLiteral()->Str(), true); - } - - return nullptr; + etsg->GetAstCompiler()->Compile(this); } checker::Type *UnaryExpression::Check([[maybe_unused]] checker::TSChecker *checker) { - checker::Type *operand_type = argument_->Check(checker); - - if (operator_ == lexer::TokenType::KEYW_TYPEOF) { - return operand_type; - } - - if (operator_ == lexer::TokenType::KEYW_DELETE) { - return CheckDeleteKeyword(checker); - } - - auto *res = CheckLiteral(checker); - if (res != nullptr) { - return res; - } - - switch (operator_) { - case lexer::TokenType::PUNCTUATOR_PLUS: - case lexer::TokenType::PUNCTUATOR_MINUS: - case lexer::TokenType::PUNCTUATOR_TILDE: { - checker->CheckNonNullType(operand_type, Start()); - // NOTE: aszilagyi. check Symbol like types - - if (operator_ == lexer::TokenType::PUNCTUATOR_PLUS) { - if (checker::TSChecker::MaybeTypeOfKind(operand_type, checker::TypeFlag::BIGINT_LIKE)) { - checker->ThrowTypeError({"Operator '+' cannot be applied to type '", operand_type, "'"}, Start()); - } - - return checker->GlobalNumberType(); - } - - return checker->GetUnaryResultType(operand_type); - } - case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { - checker->CheckTruthinessOfType(operand_type, Start()); - auto facts = operand_type->GetTypeFacts(); - if ((facts & checker::TypeFacts::TRUTHY) != 0) { - return checker->GlobalFalseType(); - } - - if ((facts & checker::TypeFacts::FALSY) != 0) { - return checker->GlobalTrueType(); - } - - return checker->GlobalBooleanType(); - } - default: { - UNREACHABLE(); - } - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *UnaryExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - auto arg_type = argument_->Check(checker); - const auto is_cond_expr = operator_ == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK; - checker::Type *operand_type = checker->ApplyUnaryOperatorPromotion(arg_type, true, true, is_cond_expr); - auto unboxed_operand_type = is_cond_expr ? checker->ETSBuiltinTypeAsConditionalType(arg_type) - : checker->ETSBuiltinTypeAsPrimitiveType(arg_type); - - switch (operator_) { - case lexer::TokenType::PUNCTUATOR_MINUS: - case lexer::TokenType::PUNCTUATOR_PLUS: { - if (operand_type == nullptr || !operand_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { - checker->ThrowTypeError("Bad operand type, the type of the operand must be numeric type.", - argument_->Start()); - } - - if (operand_type->HasTypeFlag(checker::TypeFlag::CONSTANT) && - operator_ == lexer::TokenType::PUNCTUATOR_MINUS) { - SetTsType(checker->NegateNumericType(operand_type, this)); - break; - } - - SetTsType(operand_type); - break; - } - case lexer::TokenType::PUNCTUATOR_TILDE: { - if (operand_type == nullptr || !operand_type->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)) { - checker->ThrowTypeError("Bad operand type, the type of the operand must be integral type.", - argument_->Start()); - } - - if (operand_type->HasTypeFlag(checker::TypeFlag::CONSTANT)) { - SetTsType(checker->BitwiseNegateIntegralType(operand_type, this)); - break; - } - - SetTsType(operand_type); - break; - } - case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { - if (checker->IsNullLikeOrVoidExpression(argument_)) { - auto ts_type = checker->CreateETSBooleanType(true); - ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); - SetTsType(ts_type); - break; - } - - if (operand_type == nullptr || !operand_type->IsConditionalExprType()) { - checker->ThrowTypeError("Bad operand type, the type of the operand must be boolean type.", - argument_->Start()); - } - - auto expr_res = operand_type->ResolveConditionExpr(); - if (std::get<0>(expr_res)) { - auto ts_type = checker->CreateETSBooleanType(!std::get<1>(expr_res)); - ts_type->AddTypeFlag(checker::TypeFlag::CONSTANT); - SetTsType(ts_type); - break; - } - - SetTsType(operand_type); - break; - } - case lexer::TokenType::PUNCTUATOR_DOLLAR_DOLLAR: { - SetTsType(arg_type); - break; - } - default: { - UNREACHABLE(); - break; - } - } - - if (arg_type->IsETSObjectType() && (unboxed_operand_type != nullptr) && - unboxed_operand_type->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { - argument_->AddBoxingUnboxingFlag(checker->GetUnboxingFlag(unboxed_operand_type)); - } - - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/unaryExpression.h b/ets2panda/ir/expressions/unaryExpression.h index 1e2fb24c0b..5471c17d71 100644 --- a/ets2panda/ir/expressions/unaryExpression.h +++ b/ets2panda/ir/expressions/unaryExpression.h @@ -27,6 +27,8 @@ class ETSGen; namespace panda::es2panda::checker { class TSChecker; class Type; +class ETSAnalyzer; +class TSAnalyzer; } // namespace panda::es2panda::checker namespace panda::es2panda::ir { @@ -42,6 +44,9 @@ public: : Expression(AstNodeType::UNARY_EXPRESSION), argument_(argument), operator_(unary_operator) { } + // TODO (somasimon): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + friend class checker::TSAnalyzer; [[nodiscard]] lexer::TokenType OperatorType() const noexcept { @@ -63,8 +68,6 @@ public: 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 *CheckDeleteKeyword([[maybe_unused]] checker::TSChecker *checker); - checker::Type *CheckLiteral([[maybe_unused]] checker::TSChecker *checker); private: Expression *argument_; diff --git a/ets2panda/ir/statements/throwStatement.cpp b/ets2panda/ir/statements/throwStatement.cpp index ab088aaab7..28a3e2f54b 100644 --- a/ets2panda/ir/statements/throwStatement.cpp +++ b/ets2panda/ir/statements/throwStatement.cpp @@ -15,6 +15,7 @@ #include "throwStatement.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" #include "ir/astDump.h" @@ -38,28 +39,21 @@ void ThrowStatement::Dump(ir::AstDumper *dumper) const void ThrowStatement::Compile(compiler::PandaGen *pg) const { - argument_->Compile(pg); - pg->EmitThrow(this); + pg->GetAstCompiler()->Compile(this); } void ThrowStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - etsg->ThrowException(argument_); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ThrowStatement::Check([[maybe_unused]] checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ThrowStatement::Check([[maybe_unused]] checker::ETSChecker *checker) { - auto *arg_type = argument_->Check(checker); - checker->CheckExceptionOrErrorType(arg_type, Start()); - - if (checker->Relation()->IsAssignableTo(arg_type, checker->GlobalBuiltinExceptionType())) { - checker->CheckThrowingStatements(this); - } - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/statements/throwStatement.h b/ets2panda/ir/statements/throwStatement.h index dff234ec25..cba26107ea 100644 --- a/ets2panda/ir/statements/throwStatement.h +++ b/ets2panda/ir/statements/throwStatement.h @@ -18,6 +18,10 @@ #include "ir/statement.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class Expression; @@ -30,6 +34,8 @@ public: return argument_; } + friend class checker::ETSAnalyzer; + void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; diff --git a/ets2panda/ir/statements/tryStatement.cpp b/ets2panda/ir/statements/tryStatement.cpp index b0026edcf6..f325b681e9 100644 --- a/ets2panda/ir/statements/tryStatement.cpp +++ b/ets2panda/ir/statements/tryStatement.cpp @@ -15,6 +15,7 @@ #include "tryStatement.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" #include "compiler/core/dynamicContext.h" @@ -63,208 +64,24 @@ bool TryStatement::HasDefaultCatchClause() const return (!catch_clauses_.empty() && catch_clauses_.back()->IsDefaultCatchClause()); } -void TryStatement::CompileTryCatch(compiler::PandaGen *pg) const -{ - ASSERT(catch_clauses_.size() == 1); - ASSERT(catch_clauses_.front() && !finalizer_); - - compiler::TryContext try_ctx(pg, this); - const auto &label_set = try_ctx.LabelSet(); - - pg->SetLabel(this, label_set.TryBegin()); - block_->Compile(pg); - pg->SetLabel(this, label_set.TryEnd()); - - pg->Branch(this, label_set.CatchEnd()); - - pg->SetLabel(this, label_set.CatchBegin()); - catch_clauses_.front()->Compile(pg); - pg->SetLabel(this, label_set.CatchEnd()); -} - -void TryStatement::CompileTryCatchFinally(compiler::PandaGen *pg) const -{ - ASSERT(catch_clauses_.size() == 1); - ASSERT(catch_clauses_.front() && finalizer_); - - compiler::TryContext try_ctx(pg, this); - const auto &label_set = try_ctx.LabelSet(); - - pg->SetLabel(this, label_set.TryBegin()); - { - compiler::TryContext inner_try_ctx(pg, this, false); - const auto &inner_label_set = inner_try_ctx.LabelSet(); - - pg->SetLabel(this, inner_label_set.TryBegin()); - block_->Compile(pg); - pg->SetLabel(this, inner_label_set.TryEnd()); - - pg->Branch(this, inner_label_set.CatchEnd()); - - pg->SetLabel(this, inner_label_set.CatchBegin()); - catch_clauses_.front()->Compile(pg); - pg->SetLabel(this, inner_label_set.CatchEnd()); - } - pg->SetLabel(this, label_set.TryEnd()); - - CompileFinally(pg, &try_ctx, label_set); -} - -void TryStatement::CompileFinally(compiler::PandaGen *pg, compiler::TryContext *try_ctx, - const compiler::TryLabelSet &label_set) const -{ - compiler::RegScope rs(pg); - compiler::VReg exception = pg->AllocReg(); - pg->StoreConst(this, exception, compiler::Constant::JS_HOLE); - pg->Branch(this, label_set.CatchEnd()); - - pg->SetLabel(this, label_set.CatchBegin()); - pg->StoreAccumulator(this, exception); - - pg->SetLabel(this, label_set.CatchEnd()); - - compiler::Label *label = pg->AllocLabel(); - pg->LoadAccumulator(this, try_ctx->FinalizerRun()); - - pg->BranchIfNotUndefined(this, label); - pg->StoreAccumulator(this, try_ctx->FinalizerRun()); - try_ctx->EmitFinalizer(); - pg->SetLabel(this, label); - - pg->LoadAccumulator(this, exception); - pg->EmitRethrow(this); -} - -void TryStatement::CompileTryFinally(compiler::PandaGen *pg) const -{ - ASSERT(catch_clauses_.empty() && finalizer_); - - compiler::TryContext try_ctx(pg, this); - const auto &label_set = try_ctx.LabelSet(); - - pg->SetLabel(this, label_set.TryBegin()); - { - compiler::TryContext inner_try_ctx(pg, this, false); - const auto &inner_label_set = inner_try_ctx.LabelSet(); - - pg->SetLabel(this, inner_label_set.TryBegin()); - block_->Compile(pg); - pg->SetLabel(this, inner_label_set.TryEnd()); - - pg->Branch(this, inner_label_set.CatchEnd()); - - pg->SetLabel(this, inner_label_set.CatchBegin()); - pg->EmitThrow(this); - pg->SetLabel(this, inner_label_set.CatchEnd()); - } - pg->SetLabel(this, label_set.TryEnd()); - - CompileFinally(pg, &try_ctx, label_set); -} - void TryStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - if (finalizer_ != nullptr) { - if (!catch_clauses_.empty()) { - CompileTryCatchFinally(pg); - } else { - CompileTryFinally(pg); - } - } else { - CompileTryCatch(pg); - } + pg->GetAstCompiler()->Compile(this); } void TryStatement::Compile(compiler::ETSGen *etsg) const { - compiler::ETSTryContext try_ctx(etsg, etsg->Allocator(), this, finalizer_ != nullptr); - - compiler::LabelPair try_label_pair(etsg->AllocLabel(), etsg->AllocLabel()); - - for (CatchClause *clause : catch_clauses_) { - try_ctx.AddNewCathTable(clause->TsType()->AsETSObjectType()->AssemblerName(), try_label_pair); - } - - compiler::Label *statement_end = etsg->AllocLabel(); - auto catch_tables = try_ctx.GetETSCatchTable(); - - etsg->SetLabel(this, try_label_pair.Begin()); - block_->Compile(etsg); - etsg->Branch(this, statement_end); - etsg->SetLabel(this, try_label_pair.End()); - - ASSERT(catch_clauses_.size() == catch_tables.size()); - - for (uint32_t i = 0; i < catch_clauses_.size(); i++) { - etsg->SetLabel(this, catch_tables.at(i)->LabelSet().CatchBegin()); - - catch_clauses_.at(i)->Compile(etsg); - - etsg->Branch(this, statement_end); - } - - etsg->SetLabel(this, statement_end); - - auto trycatch_label_pair = compiler::LabelPair(try_label_pair.Begin(), statement_end); - - try_ctx.EmitFinalizer(trycatch_label_pair, finalizer_insertions_); + etsg->GetAstCompiler()->Compile(this); } checker::Type *TryStatement::Check([[maybe_unused]] checker::TSChecker *checker) { - block_->Check(checker); - - for (auto *catch_clause : catch_clauses_) { - if (catch_clause != nullptr) { - catch_clause->Check(checker); - } - } - - if (finalizer_ != nullptr) { - finalizer_->Check(checker); - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *TryStatement::Check([[maybe_unused]] checker::ETSChecker *checker) { - std::vector exceptions; - block_->Check(checker); - - for (auto *catch_clause : catch_clauses_) { - auto exception_type = catch_clause->Check(checker); - - if ((exception_type != nullptr) && (catch_clause->Param() != nullptr)) { - auto *clause_type = exception_type->AsETSObjectType(); - - for (auto *exception : exceptions) { - checker->Relation()->IsIdenticalTo(clause_type, exception); - if (checker->Relation()->IsTrue()) { - checker->ThrowTypeError("Redeclaration of exception type", catch_clause->Start()); - } - } - - exceptions.push_back(clause_type); - } - } - - bool default_catch_found = false; - - for (auto *catch_clause : catch_clauses_) { - if (default_catch_found) { - checker->ThrowTypeError("Default catch clause should be the last in the try statement", - catch_clause->Start()); - } - - default_catch_found = catch_clause->IsDefaultCatchClause(); - } - - if (finalizer_ != nullptr) { - finalizer_->Check(checker); - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } void TryStatement::SetReturnType(checker::ETSChecker *checker, checker::Type *type) diff --git a/ets2panda/ir/statements/tryStatement.h b/ets2panda/ir/statements/tryStatement.h index af69c1eb9d..79c58b6caf 100644 --- a/ets2panda/ir/statements/tryStatement.h +++ b/ets2panda/ir/statements/tryStatement.h @@ -19,7 +19,14 @@ #include "compiler/core/labelPair.h" #include "ir/statement.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::compiler { +class JSCompiler; +class ETSCompiler; class PandaGen; class TryLabelSet; class TryContext; @@ -41,6 +48,11 @@ public: { } + friend class checker::TSAnalyzer; + friend class checker::ETSAnalyzer; + friend class compiler::JSCompiler; + friend class compiler::ETSCompiler; + const BlockStatement *FinallyBlock() const { return finalizer_; @@ -81,12 +93,6 @@ public: checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; private: - void CompileTryCatch(compiler::PandaGen *pg) const; - void CompileTryFinally(compiler::PandaGen *pg) const; - void CompileTryCatchFinally(compiler::PandaGen *pg) const; - void CompileFinally(compiler::PandaGen *pg, compiler::TryContext *try_ctx, - const compiler::TryLabelSet &label_set) const; - BlockStatement *block_; ArenaVector catch_clauses_; BlockStatement *finalizer_; -- Gitee