diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index ec729cf22942eacc61d2ca5a674062973d981b88..32459f100c7a8e6ef597fcd311fcd378f73ca6cc 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 d3e96e821ffd6bb149a2337acaa909ed200d7dca..a36b0c9d8f5004b2f619ad254f53ff7eef8f29ba 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 eb23fa2fc5c919df6b4b822e49429c8c3775b349..bfe617c6a44a16110d815a11e342fcc65f2afe41 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 46dd096ae82d9c812db8af28704dee330951bdcd..9032db84591d2aebc3f0b2a7e8cb669fa88f5ec7 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 10a894d364243fe8b74d0bd55d96c5d90df2551c..92afffddbd9cde8d11a966a0944fedc60e414a00 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 82a52458f67c586989cd81b76cccedc736d56c51..ff8a2a4872c2cd7da20601ef00f56e5723b35847 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 4bfd27544111932efed650ee506898500715312c..354aeb656873aa1fe870797db1cd8d20ab89ab71 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 2f6ced1150f5be10b5062bbd0193e929d5722955..5a39af9ae0719d8e4df2db080a41e9878ccdf81d 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 1e2fb24c0b3e260332e46efe3022f22b27dd2987..5471c17d711585cc827ef6fa492968d93aab11ca 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 ab088aaab7aea733cb17e4cace70d632773a3238..28a3e2f54b1bbb6ce116c9c6bac617044b8ad53d 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 dff234ec250d002201d7ad518cf8cdcd3651b0a6..cba26107ea3ad9710ed053e2b12c37e1f4d727df 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 b0026edcf642e5aa3351378ea182c3fef9fe7325..f325b681e959c93e98533dbeb08ac146cd2755cf 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 af69c1eb9d91f8f33a9ee79f4244c548b90e706b..79c58b6caf042ee36e1e6a16aa42f7b75734b781 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_;