From 8bb1d8a98beb9f8b4e1ef139c619bbdd9c66ba96 Mon Sep 17 00:00:00 2001 From: Tatiana Date: Thu, 12 Oct 2023 16:36:22 +0300 Subject: [PATCH 1/3] Add AST Verifier feature Signed-off-by: Tatiana --- BUILD.gn | 1 + CMakeLists.txt | 1 + compiler/core/ASTVerifier.cpp | 409 ++++++++++++++++++++++++++++++ compiler/core/ASTVerifier.h | 55 ++++ compiler/lowering/phase.cpp | 9 + ir/astNode.h | 10 + test/CMakeLists.txt | 12 + test/public/ast_verifier_test.cpp | 71 ++++++ 8 files changed, 568 insertions(+) create mode 100644 compiler/core/ASTVerifier.cpp create mode 100644 compiler/core/ASTVerifier.h create mode 100644 test/public/ast_verifier_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 99b4a26ef..8663df872 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -138,6 +138,7 @@ libes2panda_sources = [ "compiler/core/JSCompiler.cpp", "compiler/core/JSemitter.cpp", "compiler/core/codeGen.cpp", + "compiler/core/ASTVerifier.cpp", "compiler/core/compileJob.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", diff --git a/CMakeLists.txt b/CMakeLists.txt index e49880048..b34882d04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(ES2PANDA_LIB_SRC compiler/base/literals.cpp compiler/base/lreference.cpp compiler/base/optionalChain.cpp + compiler/core/ASTVerifier.cpp compiler/core/codeGen.cpp compiler/core/compileJob.cpp compiler/core/compileQueue.cpp diff --git a/compiler/core/ASTVerifier.cpp b/compiler/core/ASTVerifier.cpp new file mode 100644 index 000000000..f59438c30 --- /dev/null +++ b/compiler/core/ASTVerifier.cpp @@ -0,0 +1,409 @@ +/** + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ASTVerifier.h" + +#include "es2panda.h" +#include "binder/variableFlags.h" +#include "binder/scope.h" +#include "ir/astNode.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/scriptFunction.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameterInstantiation.h" + +namespace panda::es2panda::compiler { + +bool ASTVerifier::IsCorrectProgram(const parser::Program *program) +{ + bool is_correct = true; + error_messages_.clear(); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveParents(statement); + } + is_correct &= HaveParents(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveTypes(statement); + } + is_correct &= HaveTypes(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveVariables(statement); + } + is_correct &= HaveVariables(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveScopes(statement); + } + is_correct &= HaveScopes(program->GlobalClass()); + +#ifndef NDEBUG + std::for_each(error_messages_.begin(), error_messages_.end(), [](auto const msg) { LOG(INFO, COMMON) << msg; }); +#endif // NDEBUG + return is_correct; +} + +std::string ToStringHelper(const binder::ScopeType type) +{ + switch (type) { + case binder::ScopeType::CATCH: { + return "CATCH"; + } + case binder::ScopeType::CATCH_PARAM: { + return "CATCH_PARAM"; + } + case binder::ScopeType::CLASS: { + return "CLASS"; + } + case binder::ScopeType::FUNCTION: { + return "FUNCTION"; + } + case binder::ScopeType::FUNCTION_PARAM: { + return "FUNCTION_PARAM"; + } + case binder::ScopeType::GLOBAL: { + return "GLOBAL"; + } + case binder::ScopeType::LOCAL: { + return "LOCAL"; + } + case binder::ScopeType::LOOP: { + return "LOOP"; + } + case binder::ScopeType::LOOP_DECL: { + return "LOOP_DECL"; + } + case binder::ScopeType::MODULE: { + return "MODULE"; + } + case binder::ScopeType::PARAM: { + return "PARAM"; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const util::StringView &name) +{ + return name == nullptr ? "" : name.Mutf8(); +} + +std::string ToStringHelper(const binder::Scope *scope) +{ + if (scope == nullptr) { + return ""; + } + + switch (scope->Type()) { + case binder::ScopeType::FUNCTION: { + return "FUNC_SCOPE " + ToStringHelper(scope->AsFunctionScope()->Name()); + } + case binder::ScopeType::LOCAL: { + return "LOCAL_SCOPE "; + } + case binder::ScopeType::CATCH: { + return "CATCH_SCOPE "; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const binder::Variable *var) +{ + if (var == nullptr) { + return ""; + } + + switch (var->Type()) { + case binder::VariableType::LOCAL: { + return "LOCAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::MODULE: { + return "MODULE_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::GLOBAL: { + return "GLOBAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::ENUM: { + return "ENUM_VAR " + ToStringHelper(var->Name()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +template +std::string ToStringParamsHelper(const ir::AstNode *parent, const ArenaVector ¶ms) +{ + std::string name; + if (parent != nullptr) { + name = ToStringHelper(parent) + " "; + } + + name += "("; + for (auto const *param : params) { + name += ToStringHelper(param); + } + + return name + ")"; +} + +std::string ToStringHelper(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return ""; + } + + switch (ast->Type()) { + case ir::AstNodeType::IDENTIFIER: { + return "ID " + ToStringHelper(ast->AsIdentifier()->Name()); + } + case ir::AstNodeType::CLASS_DEFINITION: { + return "CLS_DEF " + ToStringHelper(ast->AsClassDefinition()->Ident()); + } + case ir::AstNodeType::CLASS_DECLARATION: { + return "CLS_DECL " + ToStringHelper(ast->AsClassDeclaration()->Definition()); + } + case ir::AstNodeType::BLOCK_STATEMENT: { + return "BLOCK " + ToStringHelper(ast->AsBlockStatement()->Scope()); + } + case ir::AstNodeType::SCRIPT_FUNCTION: { + auto const *sf = ast->AsScriptFunction(); + return "SCRIPT_FUN " + ToStringHelper(sf->Scope()) + "::" + ToStringHelper(sf->Id()); + } + case ir::AstNodeType::FUNCTION_EXPRESSION: { + return "FUN_EXPR " + ToStringHelper(ast->AsFunctionExpression()->Function()); + } + case ir::AstNodeType::METHOD_DEFINITION: { + return "METHOD_DEF " + ToStringHelper(ast->AsMethodDefinition()->Value()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE_PART: { + return "TYPE_REF_PART " + ToStringHelper(ast->AsETSTypeReferencePart()->Name()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE: { + return "TYPE_REF " + ToStringHelper(ast->AsETSTypeReference()->Part()); + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + return "VAR_DECLARATOR " + ToStringHelper(ast->AsVariableDeclarator()->Id()); + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + if (ast->AsVariableDeclaration()->Declarators().empty()) { + return "VAR_DECLARATION "; + } + return "VAR_DECLARATION " + ToStringHelper(ast->AsVariableDeclaration()->Declarators().at(0)); + } + case ir::AstNodeType::CALL_EXPRESSION: { + return "CALL_EXPR " + ToStringHelper(ast->AsCallExpression()->Callee()) + "(...)"; + } + case ir::AstNodeType::EXPRESSION_STATEMENT: { + return "EXPR_STMT " + ToStringHelper(ast->AsExpressionStatement()->GetExpression()); + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + auto const *me = ast->AsMemberExpression(); + return "MEMBER_EXPR " + ToStringHelper(me->Object()) + "." + ToStringHelper(me->Property()); + } + case ir::AstNodeType::CLASS_STATIC_BLOCK: { + return "CLS_STATIC_BLOCK " + ToStringHelper(ast->AsClassStaticBlock()->Function()); + } + case ir::AstNodeType::ETS_PACKAGE_DECLARATION: { + return "PKG_DECL "; + } + case ir::AstNodeType::TS_TYPE_PARAMETER_DECLARATION: { + return "PARAM_DECL " + ToStringParamsHelper( + ast->Parent(), ast->AsTSTypeParameterDeclaration()->Params()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER: { + return "TYPE_PARAM " + ToStringHelper(ast->AsTSTypeParameter()->Name()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER_INSTANTIATION: { + return "PARAM_INSTANTIATION " + + ToStringParamsHelper(ast->Parent(), ast->AsTSTypeParameterInstantiation()->Params()); + } + case ir::AstNodeType::THROW_STATEMENT: { + return "THROW_STMT " + ToStringHelper(ast->AsThrowStatement()->Argument()); + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + return "NEW_CLS_INSTANCE " + ToStringHelper(ast->AsETSNewClassInstanceExpression()->GetTypeRef()); + } + case ir::AstNodeType::STRING_LITERAL: { + return "STR_LITERAL " + ToStringHelper(ast->AsStringLiteral()->Str()); + } + case ir::AstNodeType::TRY_STATEMENT: { + return "TRY_STMT " + ToStringHelper(ast->AsTryStatement()->Block()); + } + case ir::AstNodeType::CATCH_CLAUSE: { + return "CATCH_CLAUSE "; + } + case ir::AstNodeType::NUMBER_LITERAL: { + return "NUMBER_LITERAL " + ToStringHelper(ast->AsNumberLiteral()->Str()); + } + case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: { + return "ETS_PARAM_EXPR " + ToStringHelper(ast->AsETSParameterExpression()->Ident()); + } + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + return "TS_INTERFACE_DECL " + ToStringHelper(ast->AsTSInterfaceDeclaration()->Id()); + } + case ir::AstNodeType::TS_INTERFACE_BODY: { + return "TS_INTERFACE_BODY "; + } + case ir::AstNodeType::ETS_FUNCTION_TYPE: { + return "ETS_FUNC_TYPE " + + ToStringParamsHelper(ast->Parent(), ast->AsETSFunctionType()->Params()); + } + case ir::AstNodeType::TS_CLASS_IMPLEMENTS: { + return "TS_CLASS_IMPL " + ToStringHelper(ast->AsTSClassImplements()->Expr()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +bool ASTVerifier::HasParent(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->Parent() == nullptr) { + error_messages_.push_back("NULL_PARENT: " + ToStringHelper(ast)); + return false; + } + + return true; +} + +bool ASTVerifier::HaveParents(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_parent = HasParent(ast); + ast->IterateRecursively([this, &has_parent](ir::AstNode *child) { has_parent &= HasParent(child); }); + return has_parent; +} + +bool ASTVerifier::HasType(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->IsTyped() && static_cast(ast)->TsType() == nullptr) { + error_messages_.push_back("NULL_TS_TYPE: " + ToStringHelper(ast)); + return false; + } + return true; +} + +bool ASTVerifier::HaveTypes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_type = HasType(ast); + ast->IterateRecursively([this, &has_type](ir::AstNode *child) { has_type &= HasType(child); }); + return has_type; +} + +bool ASTVerifier::HasVariable(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier() || ast->AsIdentifier()->Variable() != nullptr) { + return true; + } + + error_messages_.push_back("NULL_VARIABLE: " + ToStringHelper(ast->AsIdentifier())); + return false; +} + +bool ASTVerifier::HaveVariables(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_variable = HasVariable(ast); + ast->IterateRecursively([this, &has_variable](ir::AstNode *child) { has_variable &= HasVariable(child); }); + return has_variable; +} + +bool ASTVerifier::HasScope(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier()) { + return true; // we will check only Identifier + } + // we will check only local variables of identifiers + if (HasVariable(ast) && ast->AsIdentifier()->Variable()->IsLocalVariable() && + ast->AsIdentifier()->Variable()->AsLocalVariable()->GetScope() == nullptr) { + error_messages_.push_back("NULL_SCOPE_LOCAL_VAR: " + ToStringHelper(ast)); + return false; + } + // TODO(tatiana): Add check that the scope enclose this identifier + return true; +} + +bool ASTVerifier::HaveScopes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_scope = HasScope(ast); + ast->IterateRecursively([this, &has_scope](ir::AstNode *child) { has_scope &= HasScope(child); }); + return has_scope; +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ASTVerifier.h b/compiler/core/ASTVerifier.h new file mode 100644 index 000000000..d9bdba118 --- /dev/null +++ b/compiler/core/ASTVerifier.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_CORE_ASTVERIFIER_H +#define ES2PANDA_COMPILER_CORE_ASTVERIFIER_H + +#include "parser/program/program.h" + +namespace panda::es2panda::compiler { + +class ASTVerifier { +public: + using ErrorMessages = std::vector; + NO_COPY_SEMANTIC(ASTVerifier); + NO_MOVE_SEMANTIC(ASTVerifier); + + ASTVerifier() = default; + ~ASTVerifier() = default; + + bool IsCorrectProgram(const parser::Program *program); + bool HaveParents(const ir::AstNode *ast); + bool HasParent(const ir::AstNode *ast); + bool HaveTypes(const ir::AstNode *ast); + bool HasType(const ir::AstNode *ast); + bool HaveVariables(const ir::AstNode *ast); + bool HasVariable(const ir::AstNode *ast); + bool HasScope(const ir::AstNode *ast); + bool HaveScopes(const ir::AstNode *ast); + + ErrorMessages GetErrorMessages() + { + return error_messages_; + } + +private: + ErrorMessages error_messages_; +}; + +std::string ToStringHelper(const ir::AstNode *ast); + +} // namespace panda::es2panda::compiler + +#endif // ES2PANDA_COMPILER_CORE_ASTVERIFIER_H diff --git a/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 5877c6e49..0b7c30057 100644 --- a/compiler/lowering/phase.cpp +++ b/compiler/lowering/phase.cpp @@ -15,6 +15,7 @@ #include "phase.h" #include "checker/checker.h" +#include "compiler/core/ASTVerifier.h" #include "compiler/core/compilerContext.h" #include "lexer/token/sourceLocation.h" #include "compiler/lowering/checkerPhase.h" @@ -57,6 +58,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_before; + if (!ast_before.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Precondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); } @@ -72,6 +77,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_after; + if (!ast_after.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Postcondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); } diff --git a/ir/astNode.h b/ir/astNode.h index b9558663a..5accd1e72 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -227,6 +227,11 @@ public: return false; } + virtual bool IsTyped() const + { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_AS_CASTS(nodeType, className) \ className *As##className() \ @@ -587,6 +592,11 @@ public: ts_type_ = ts_type; } + bool IsTyped() const override + { + return true; + } + protected: explicit Typed(AstNodeType const type) : T(type) {} explicit Typed(AstNodeType const type, ModifierFlags const flags) : T(type, flags) {} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f40aed24b..ff14df7c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,5 +92,17 @@ if(PANDA_WITH_ETS) add_dependencies(ets_tests es2panda_tests) endif() + panda_add_gtest( + NAME es2panda_astverifier_tests + SOURCES + public/ast_verifier_test.cpp + LIBRARIES + es2panda-lib + INCLUDE_DIRS + ${ES2PANDA_PATH} + SANITIZERS + ${PANDA_SANITIZERS_LIST} + ) + add_subdirectory(tsconfig) endif() diff --git a/test/public/ast_verifier_test.cpp b/test/public/ast_verifier_test.cpp new file mode 100644 index 000000000..dd36a1937 --- /dev/null +++ b/test/public/ast_verifier_test.cpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "macros.h" + +#include "compiler/core/ASTVerifier.h" +#include "ir/astDump.h" +#include "ir/expressions/literals/stringLiteral.h" + +class ASTVerifierTest : public testing::Test { +public: + ASTVerifierTest() = default; + ~ASTVerifierTest() override = default; + + NO_COPY_SEMANTIC(ASTVerifierTest); + NO_MOVE_SEMANTIC(ASTVerifierTest); + +private: +}; + +TEST_F(ASTVerifierTest, NullParent) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_parent = verifier.HasParent(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_parent, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_PARENT: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, NullType) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_type = verifier.HasType(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_type, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_TS_TYPE: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, WithoutScope) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_scope = verifier.HasScope(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_scope, true); + ASSERT_EQ(messages.size(), 0); +} -- Gitee From 3bbd75fe73c352d5a5ee373b596440cd1d16af54 Mon Sep 17 00:00:00 2001 From: Csaba Hurton Date: Fri, 13 Oct 2023 16:02:57 +0200 Subject: [PATCH 2/3] Move Compile and Check Logic from ASTNode classes Round #4 Linked Internal Issue 13840 Signed-off-by: Csaba Hurton --- checker/ETSAnalyzer.cpp | 112 +++++++++++++--- checker/SemanticAnalyzer.h | 146 +++++++++++++++++++++ checker/TSAnalyzer.cpp | 56 +++++--- compiler/core/ASTCompiler.h | 146 +++++++++++++++++++++ compiler/core/ETSCompiler.cpp | 51 ++++--- compiler/core/JSCompiler.cpp | 34 ++--- ir/ets/etsParameterExpression.cpp | 37 ++---- ir/ets/etsParameterExpression.h | 7 + ir/ets/etsPrimitiveType.cpp | 20 +-- ir/ets/etsPrimitiveType.h | 8 +- ir/ets/etsStructDeclaration.cpp | 16 +-- ir/ets/etsStructDeclaration.h | 8 +- ir/ets/etsTypeReference.cpp | 21 +-- ir/ets/etsTypeReference.h | 8 +- ir/ets/etsTypeReferencePart.cpp | 22 ++-- ir/ets/etsTypeReferencePart.h | 8 +- ir/ets/etsWildcardType.cpp | 24 ++-- ir/ets/etsWildcardType.h | 8 +- ir/expressions/arrowFunctionExpression.cpp | 111 +--------------- ir/expressions/arrowFunctionExpression.h | 12 +- ir/statements/tryStatement.h | 2 +- 21 files changed, 576 insertions(+), 281 deletions(-) diff --git a/checker/ETSAnalyzer.cpp b/checker/ETSAnalyzer.cpp index 2e05e528f..33a317b03 100644 --- a/checker/ETSAnalyzer.cpp +++ b/checker/ETSAnalyzer.cpp @@ -16,6 +16,8 @@ #include "binder/binder.h" #include "binder/ETSBinder.h" +#include "checker/ETSchecker.h" +#include "checker/ets/castingContext.h" #include "checker/ets/typeRelationContext.h" #include "ir/base/catchClause.h" #include "ir/base/classProperty.h" @@ -233,37 +235,55 @@ checker::Type *ETSAnalyzer::Check(ir::ETSPackageDeclaration *st) const checker::Type *ETSAnalyzer::Check(ir::ETSParameterExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() == nullptr) { + checker::Type *param_type; + + if (expr->Ident()->TsType() != nullptr) { + param_type = expr->Ident()->TsType(); + } else { + param_type = !expr->IsRestParameter() ? expr->Ident()->Check(checker) : expr->spread_->Check(checker); + if (expr->IsDefault()) { + [[maybe_unused]] auto *const init_type = expr->Initializer()->Check(checker); + // TODO(ttamas) : fix this aftet nullable fix + // const checker::AssignmentContext ctx(checker->Relation(), initializer_, init_type, name_type, + // initializer_->Start(), + // {"Initializers type is not assignable to the target type"}); + } + } + + expr->SetTsType(param_type); + } + + return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::ETSPrimitiveType *node) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const { - (void)node; - UNREACHABLE(); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ETSStructDeclaration *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + node->Definition()->Check(checker); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ETSTypeReference *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + return node->GetType(checker); } checker::Type *ETSAnalyzer::Check(ir::ETSTypeReferencePart *node) const { - (void)node; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + return node->GetType(checker); } -checker::Type *ETSAnalyzer::Check(ir::ETSWildcardType *node) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) const { - (void)node; UNREACHABLE(); } // compile methods for EXPRESSIONS in alphabetical order @@ -275,8 +295,70 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + auto *func_type = checker->BuildFunctionSignature(expr->Function(), false); + + if (expr->Function()->IsAsyncFunc()) { + auto *ret_type = static_cast(expr->Function()->Signature()->ReturnType()); + if (ret_type->AssemblerName() != checker->GlobalBuiltinPromiseType()->AssemblerName()) { + checker->ThrowTypeError("Return type of async lambda must be 'Promise'", expr->Function()->Start()); + } + } + + checker::ScopeContext scope_ctx(checker, expr->Function()->Scope()); + + if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { + /* + example code: + ``` + class A { + prop:number + } + function A.method() { + let a = () => { + console.println(this.prop) + } + } + ``` + here the enclosing class of arrow function should be Class A + */ + checker->Context().SetContainingClass( + checker->Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType()); + } + + checker::SavedCheckerContext saved_context(checker, checker->Context().Status(), + checker->Context().ContainingClass()); + checker->AddStatus(checker::CheckerStatus::IN_LAMBDA); + checker->Context().SetContainingSignature(func_type->CallSignatures()[0]); + + auto *body_type = expr->Function()->Body()->Check(checker); + + if (expr->Function()->Body()->IsExpression()) { + if (expr->Function()->ReturnTypeAnnotation() == nullptr) { + func_type->CallSignatures()[0]->SetReturnType(body_type); + } + + checker::AssignmentContext( + checker->Relation(), expr->Function()->Body()->AsExpression(), body_type, + func_type->CallSignatures()[0]->ReturnType(), expr->Function()->Start(), + {"Return statements return type is not compatible with the containing functions return type"}, + checker::TypeRelationFlag::DIRECT_RETURN); + } + + checker->Context().SetContainingSignature(nullptr); + checker->CheckCapturedVariables(); + + for (auto [var, _] : checker->Context().CapturedVars()) { + (void)_; + expr->CapturedVars().push_back(var); + } + + expr->SetTsType(func_type); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const diff --git a/checker/SemanticAnalyzer.h b/checker/SemanticAnalyzer.h index 72cfe3735..8cc27451f 100644 --- a/checker/SemanticAnalyzer.h +++ b/checker/SemanticAnalyzer.h @@ -17,6 +17,152 @@ #define ES2PANDA_CHECKER_SEMANTICANALYZER_H #include "compiler/core/dynamicContext.h" +#include "ir/opaqueTypeNode.h" +#include "ir/as/namedType.h" +#include "ir/as/prefixAssertionExpression.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classProperty.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/decorator.h" +#include "ir/base/metaProperty.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/property.h" +#include "ir/base/scriptFunction.h" +#include "ir/base/spreadElement.h" +#include "ir/base/templateElement.h" +#include "ir/base/tsIndexSignature.h" +#include "ir/base/tsMethodSignature.h" +#include "ir/base/tsPropertySignature.h" +#include "ir/base/tsSignatureDeclaration.h" +#include "ir/ets/etsClassLiteral.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsImportDeclaration.h" +#include "ir/ets/etsLaunchExpression.h" +#include "ir/ets/etsNewArrayInstanceExpression.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsNewMultiDimArrayInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsPrimitiveType.h" +#include "ir/ets/etsScript.h" +#include "ir/ets/etsStructDeclaration.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/ets/etsWildcardType.h" +#include "ir/expressions/arrayExpression.h" +#include "ir/expressions/arrowFunctionExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/awaitExpression.h" +#include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/chainExpression.h" +#include "ir/expressions/classExpression.h" +#include "ir/expressions/conditionalExpression.h" +#include "ir/expressions/directEvalExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/importExpression.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/newExpression.h" +#include "ir/expressions/objectExpression.h" +#include "ir/expressions/omittedExpression.h" +#include "ir/expressions/sequenceExpression.h" +#include "ir/expressions/superExpression.h" +#include "ir/expressions/taggedTemplateExpression.h" +#include "ir/expressions/templateLiteral.h" +#include "ir/expressions/thisExpression.h" +#include "ir/expressions/unaryExpression.h" +#include "ir/expressions/updateExpression.h" +#include "ir/expressions/yieldExpression.h" +#include "ir/expressions/literals/bigIntLiteral.h" +#include "ir/expressions/literals/booleanLiteral.h" +#include "ir/expressions/literals/charLiteral.h" +#include "ir/expressions/literals/nullLiteral.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/regExpLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/module/exportAllDeclaration.h" +#include "ir/module/exportDefaultDeclaration.h" +#include "ir/module/exportNamedDeclaration.h" +#include "ir/module/exportSpecifier.h" +#include "ir/module/importDeclaration.h" +#include "ir/module/importDefaultSpecifier.h" +#include "ir/module/importNamespaceSpecifier.h" +#include "ir/module/importSpecifier.h" +#include "ir/statements/assertStatement.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/breakStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/continueStatement.h" +#include "ir/statements/debuggerStatement.h" +#include "ir/statements/doWhileStatement.h" +#include "ir/statements/emptyStatement.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/forInStatement.h" +#include "ir/statements/forOfStatement.h" +#include "ir/statements/forUpdateStatement.h" +#include "ir/statements/functionDeclaration.h" +#include "ir/statements/ifStatement.h" +#include "ir/statements/labelledStatement.h" +#include "ir/statements/returnStatement.h" +#include "ir/statements/switchCaseStatement.h" +#include "ir/statements/switchStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/statements/whileStatement.h" +#include "ir/ts/tsAnyKeyword.h" +#include "ir/ts/tsArrayType.h" +#include "ir/ts/tsAsExpression.h" +#include "ir/ts/tsBigintKeyword.h" +#include "ir/ts/tsBooleanKeyword.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsConditionalType.h" +#include "ir/ts/tsConstructorType.h" +#include "ir/ts/tsEnumDeclaration.h" +#include "ir/ts/tsEnumMember.h" +#include "ir/ts/tsExternalModuleReference.h" +#include "ir/ts/tsFunctionType.h" +#include "ir/ts/tsImportEqualsDeclaration.h" +#include "ir/ts/tsImportType.h" +#include "ir/ts/tsIndexedAccessType.h" +#include "ir/ts/tsInferType.h" +#include "ir/ts/tsInterfaceBody.h" +#include "ir/ts/tsInterfaceDeclaration.h" +#include "ir/ts/tsInterfaceHeritage.h" +#include "ir/ts/tsIntersectionType.h" +#include "ir/ts/tsLiteralType.h" +#include "ir/ts/tsMappedType.h" +#include "ir/ts/tsModuleBlock.h" +#include "ir/ts/tsModuleDeclaration.h" +#include "ir/ts/tsNamedTupleMember.h" +#include "ir/ts/tsNeverKeyword.h" +#include "ir/ts/tsNonNullExpression.h" +#include "ir/ts/tsNullKeyword.h" +#include "ir/ts/tsNumberKeyword.h" +#include "ir/ts/tsObjectKeyword.h" +#include "ir/ts/tsParameterProperty.h" +#include "ir/ts/tsParenthesizedType.h" +#include "ir/ts/tsQualifiedName.h" +#include "ir/ts/tsStringKeyword.h" +#include "ir/ts/tsThisType.h" +#include "ir/ts/tsTupleType.h" +#include "ir/ts/tsTypeAliasDeclaration.h" +#include "ir/ts/tsTypeAssertion.h" +#include "ir/ts/tsTypeLiteral.h" +#include "ir/ts/tsTypeOperator.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterInstantiation.h" +#include "ir/ts/tsTypePredicate.h" +#include "ir/ts/tsTypeQuery.h" +#include "ir/ts/tsTypeReference.h" +#include "ir/ts/tsUndefinedKeyword.h" +#include "ir/ts/tsUnionType.h" +#include "ir/ts/tsUnknownKeyword.h" +#include "ir/ts/tsVoidKeyword.h" namespace panda::es2panda::checker { class Checker; diff --git a/checker/TSAnalyzer.cpp b/checker/TSAnalyzer.cpp index f5bfa79b2..62f2f79a9 100644 --- a/checker/TSAnalyzer.cpp +++ b/checker/TSAnalyzer.cpp @@ -16,12 +16,7 @@ #include "TSAnalyzer.h" #include "checker/TSchecker.h" -#include "ir/base/catchClause.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" -#include "ir/typeNode.h" +#include "checker/ts/destructuringContext.h" #include "util/helpers.h" namespace panda::es2panda::checker { @@ -191,39 +186,33 @@ checker::Type *TSAnalyzer::Check(ir::ETSPackageDeclaration *st) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSParameterExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSParameterExpression *expr) const { - (void)expr; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSPrimitiveType *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSStructDeclaration *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSTypeReference *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSTypeReference *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSTypeReferencePart *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSTypeReferencePart *node) const { - (void)node; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ETSWildcardType *node) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) const { - (void)node; UNREACHABLE(); } // compile methods for EXPRESSIONS in alphabetical order @@ -235,8 +224,35 @@ checker::Type *TSAnalyzer::Check(ir::ArrayExpression *expr) const checker::Type *TSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + binder::Variable *func_var = nullptr; + + if (expr->Function()->Parent()->Parent() != nullptr && + expr->Function()->Parent()->Parent()->IsVariableDeclarator() && + expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) { + func_var = expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable(); + } + + checker::ScopeContext scope_ctx(checker, expr->Function()->Scope()); + + auto *signature_info = checker->Allocator()->New(checker->Allocator()); + checker->CheckFunctionParameterDeclarations(expr->Function()->Params(), signature_info); + + auto *signature = checker->Allocator()->New( + signature_info, checker->GlobalResolvingReturnType(), expr->Function()); + checker::Type *func_type = checker->CreateFunctionTypeWithSignature(signature); + + if (func_var != nullptr && func_var->TsType() == nullptr) { + func_var->SetTsType(func_type); + } + + signature->SetReturnType(checker->HandleFunctionReturn(expr->Function())); + + if (!expr->Function()->Body()->IsExpression()) { + expr->Function()->Body()->Check(checker); + } + + return func_type; } checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const diff --git a/compiler/core/ASTCompiler.h b/compiler/core/ASTCompiler.h index b5bd66617..03fdde033 100644 --- a/compiler/core/ASTCompiler.h +++ b/compiler/core/ASTCompiler.h @@ -17,6 +17,152 @@ #define ES2PANDA_COMPILER_CORE_ASTCOMPILER_H #include "compiler/core/dynamicContext.h" +#include "ir/opaqueTypeNode.h" +#include "ir/as/namedType.h" +#include "ir/as/prefixAssertionExpression.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classProperty.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/decorator.h" +#include "ir/base/metaProperty.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/property.h" +#include "ir/base/scriptFunction.h" +#include "ir/base/spreadElement.h" +#include "ir/base/templateElement.h" +#include "ir/base/tsIndexSignature.h" +#include "ir/base/tsMethodSignature.h" +#include "ir/base/tsPropertySignature.h" +#include "ir/base/tsSignatureDeclaration.h" +#include "ir/ets/etsClassLiteral.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsImportDeclaration.h" +#include "ir/ets/etsLaunchExpression.h" +#include "ir/ets/etsNewArrayInstanceExpression.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsNewMultiDimArrayInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsPrimitiveType.h" +#include "ir/ets/etsScript.h" +#include "ir/ets/etsStructDeclaration.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/ets/etsWildcardType.h" +#include "ir/expressions/arrayExpression.h" +#include "ir/expressions/arrowFunctionExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/awaitExpression.h" +#include "ir/expressions/binaryExpression.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/chainExpression.h" +#include "ir/expressions/classExpression.h" +#include "ir/expressions/conditionalExpression.h" +#include "ir/expressions/directEvalExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/importExpression.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/newExpression.h" +#include "ir/expressions/objectExpression.h" +#include "ir/expressions/omittedExpression.h" +#include "ir/expressions/sequenceExpression.h" +#include "ir/expressions/superExpression.h" +#include "ir/expressions/taggedTemplateExpression.h" +#include "ir/expressions/templateLiteral.h" +#include "ir/expressions/thisExpression.h" +#include "ir/expressions/unaryExpression.h" +#include "ir/expressions/updateExpression.h" +#include "ir/expressions/yieldExpression.h" +#include "ir/expressions/literals/bigIntLiteral.h" +#include "ir/expressions/literals/booleanLiteral.h" +#include "ir/expressions/literals/charLiteral.h" +#include "ir/expressions/literals/nullLiteral.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/regExpLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/module/exportAllDeclaration.h" +#include "ir/module/exportDefaultDeclaration.h" +#include "ir/module/exportNamedDeclaration.h" +#include "ir/module/exportSpecifier.h" +#include "ir/module/importDeclaration.h" +#include "ir/module/importDefaultSpecifier.h" +#include "ir/module/importNamespaceSpecifier.h" +#include "ir/module/importSpecifier.h" +#include "ir/statements/assertStatement.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/breakStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/continueStatement.h" +#include "ir/statements/debuggerStatement.h" +#include "ir/statements/doWhileStatement.h" +#include "ir/statements/emptyStatement.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/forInStatement.h" +#include "ir/statements/forOfStatement.h" +#include "ir/statements/forUpdateStatement.h" +#include "ir/statements/functionDeclaration.h" +#include "ir/statements/ifStatement.h" +#include "ir/statements/labelledStatement.h" +#include "ir/statements/returnStatement.h" +#include "ir/statements/switchCaseStatement.h" +#include "ir/statements/switchStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/statements/whileStatement.h" +#include "ir/ts/tsAnyKeyword.h" +#include "ir/ts/tsArrayType.h" +#include "ir/ts/tsAsExpression.h" +#include "ir/ts/tsBigintKeyword.h" +#include "ir/ts/tsBooleanKeyword.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsConditionalType.h" +#include "ir/ts/tsConstructorType.h" +#include "ir/ts/tsEnumDeclaration.h" +#include "ir/ts/tsEnumMember.h" +#include "ir/ts/tsExternalModuleReference.h" +#include "ir/ts/tsFunctionType.h" +#include "ir/ts/tsImportEqualsDeclaration.h" +#include "ir/ts/tsImportType.h" +#include "ir/ts/tsIndexedAccessType.h" +#include "ir/ts/tsInferType.h" +#include "ir/ts/tsInterfaceBody.h" +#include "ir/ts/tsInterfaceDeclaration.h" +#include "ir/ts/tsInterfaceHeritage.h" +#include "ir/ts/tsIntersectionType.h" +#include "ir/ts/tsLiteralType.h" +#include "ir/ts/tsMappedType.h" +#include "ir/ts/tsModuleBlock.h" +#include "ir/ts/tsModuleDeclaration.h" +#include "ir/ts/tsNamedTupleMember.h" +#include "ir/ts/tsNeverKeyword.h" +#include "ir/ts/tsNonNullExpression.h" +#include "ir/ts/tsNullKeyword.h" +#include "ir/ts/tsNumberKeyword.h" +#include "ir/ts/tsObjectKeyword.h" +#include "ir/ts/tsParameterProperty.h" +#include "ir/ts/tsParenthesizedType.h" +#include "ir/ts/tsQualifiedName.h" +#include "ir/ts/tsStringKeyword.h" +#include "ir/ts/tsThisType.h" +#include "ir/ts/tsTupleType.h" +#include "ir/ts/tsTypeAliasDeclaration.h" +#include "ir/ts/tsTypeAssertion.h" +#include "ir/ts/tsTypeLiteral.h" +#include "ir/ts/tsTypeOperator.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterInstantiation.h" +#include "ir/ts/tsTypePredicate.h" +#include "ir/ts/tsTypeQuery.h" +#include "ir/ts/tsTypeReference.h" +#include "ir/ts/tsUndefinedKeyword.h" +#include "ir/ts/tsUnionType.h" +#include "ir/ts/tsUnknownKeyword.h" +#include "ir/ts/tsVoidKeyword.h" namespace panda::es2panda::compiler { class CodeGen; diff --git a/compiler/core/ETSCompiler.cpp b/compiler/core/ETSCompiler.cpp index c7f91c0d7..ffff9fe9d 100644 --- a/compiler/core/ETSCompiler.cpp +++ b/compiler/core/ETSCompiler.cpp @@ -15,13 +15,11 @@ #include "ETSCompiler.h" +#include "checker/types/ets/etsDynamicFunctionType.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classProperty.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" namespace panda::es2panda::compiler { @@ -198,38 +196,36 @@ void ETSCompiler::Compile(const ir::ETSPackageDeclaration *st) const void ETSCompiler::Compile(const ir::ETSParameterExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + expr->Ident()->Identifier::Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSPrimitiveType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *node) const { - (void)node; UNREACHABLE(); } -void ETSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } void ETSCompiler::Compile(const ir::ETSTypeReference *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Part()->Compile(etsg); } void ETSCompiler::Compile(const ir::ETSTypeReferencePart *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Name()->Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSWildcardType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->Unimplemented(); } // compile methods for EXPRESSIONS in alphabetical order void ETSCompiler::Compile(const ir::ArrayExpression *expr) const @@ -240,8 +236,23 @@ void ETSCompiler::Compile(const ir::ArrayExpression *expr) const void ETSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + ASSERT(expr->ResolvedLambda() != nullptr); + auto *ctor = expr->ResolvedLambda()->TsType()->AsETSObjectType()->ConstructSignatures()[0]; + std::vector arguments; + + for (auto *it : expr->CapturedVars()) { + if (it->HasFlag(binder::VariableFlags::LOCAL)) { + arguments.push_back(it->AsLocalVariable()->Vreg()); + } + } + + if (expr->propagate_this_) { + arguments.push_back(etsg->GetThisReg()); + } + + etsg->InitLambdaObject(expr, ctor, arguments); + etsg->SetAccumulatorType(expr->resolved_lambda_->TsType()); } void ETSCompiler::Compile(const ir::AssignmentExpression *expr) const diff --git a/compiler/core/JSCompiler.cpp b/compiler/core/JSCompiler.cpp index a0b481589..5be37688d 100644 --- a/compiler/core/JSCompiler.cpp +++ b/compiler/core/JSCompiler.cpp @@ -15,18 +15,10 @@ #include "JSCompiler.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classDefinition.h" -#include "ir/base/classProperty.h" -#include "ir/base/classStaticBlock.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/functionExpression.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" #include "util/helpers.h" namespace panda::es2panda::compiler { @@ -523,39 +515,33 @@ void JSCompiler::Compile(const ir::ETSPackageDeclaration *expr) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSParameterExpression *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSParameterExpression *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSPrimitiveType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReference *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReference *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReferencePart *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReferencePart *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSWildcardType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *expr) const { - (void)expr; UNREACHABLE(); } @@ -568,8 +554,8 @@ void JSCompiler::Compile(const ir::ArrayExpression *expr) const void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName()); } void JSCompiler::Compile(const ir::AssignmentExpression *expr) const diff --git a/ir/ets/etsParameterExpression.cpp b/ir/ets/etsParameterExpression.cpp index c862ffe69..30fee78aa 100644 --- a/ir/ets/etsParameterExpression.cpp +++ b/ir/ets/etsParameterExpression.cpp @@ -15,9 +15,11 @@ #include "etsParameterExpression.h" -#include "compiler/core/pandagen.h" #include "checker/ETSchecker.h" #include "checker/ets/typeRelationContext.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" #include "ir/typeNode.h" #include "ir/expressions/identifier.h" @@ -140,43 +142,24 @@ void ETSParameterExpression::Dump(ir::AstDumper *const dumper) const } } -void ETSParameterExpression::Compile([[maybe_unused]] compiler::PandaGen *const pg) const +void ETSParameterExpression::Compile(compiler::PandaGen *const pg) const { - UNREACHABLE(); + pg->GetAstCompiler()->Compile(this); } -void ETSParameterExpression::Compile([[maybe_unused]] compiler::ETSGen *const etsg) const +void ETSParameterExpression::Compile(compiler::ETSGen *const etsg) const { - ident_->Identifier::Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSParameterExpression::Check([[maybe_unused]] checker::TSChecker *const checker) +checker::Type *ETSParameterExpression::Check(checker::TSChecker *const checker) { - UNREACHABLE(); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSParameterExpression::Check(checker::ETSChecker *const checker) { - if (TsType() == nullptr) { - checker::Type *param_type; - - if (ident_->TsType() != nullptr) { - param_type = ident_->TsType(); - } else { - param_type = !IsRestParameter() ? ident_->Check(checker) : spread_->Check(checker); - if (IsDefault()) { - [[maybe_unused]] auto *const init_type = initializer_->Check(checker); - // TODO(ttamas) : fix this aftet nullable fix - // const checker::AssignmentContext ctx(checker->Relation(), initializer_, init_type, name_type, - // initializer_->Start(), - // {"Initializers type is not assignable to the target type"}); - } - } - - SetTsType(param_type); - } - - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/ets/etsParameterExpression.h b/ir/ets/etsParameterExpression.h index d63a3dd88..1160289e8 100644 --- a/ir/ets/etsParameterExpression.h +++ b/ir/ets/etsParameterExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class ETSParameterExpression final : public Expression { public: @@ -29,6 +33,9 @@ public: explicit ETSParameterExpression(AnnotatedExpression *ident_or_spread, Expression *initializer); + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const Identifier *Ident() const noexcept; [[nodiscard]] Identifier *Ident() noexcept; diff --git a/ir/ets/etsPrimitiveType.cpp b/ir/ets/etsPrimitiveType.cpp index 820b6fb54..7e722d78c 100644 --- a/ir/ets/etsPrimitiveType.cpp +++ b/ir/ets/etsPrimitiveType.cpp @@ -15,10 +15,11 @@ #include "etsPrimitiveType.h" -#include "ir/astDump.h" #include "checker/TSchecker.h" #include "checker/ETSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" namespace panda::es2panda::ir { void ETSPrimitiveType::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -29,16 +30,19 @@ void ETSPrimitiveType::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ETSPrimitiveType"}}); } -void ETSPrimitiveType::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void ETSPrimitiveType::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} -void ETSPrimitiveType::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSPrimitiveType::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSPrimitiveType::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSPrimitiveType::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::TSChecker *checker) @@ -46,9 +50,9 @@ checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::TSChecker *ch return checker->GlobalAnyType(); } -checker::Type *ETSPrimitiveType::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ETSPrimitiveType::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSPrimitiveType::GetType([[maybe_unused]] checker::ETSChecker *checker) diff --git a/ir/ets/etsPrimitiveType.h b/ir/ets/etsPrimitiveType.h index c8ae085f4..c394b0019 100644 --- a/ir/ets/etsPrimitiveType.h +++ b/ir/ets/etsPrimitiveType.h @@ -33,11 +33,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsStructDeclaration.cpp b/ir/ets/etsStructDeclaration.cpp index 62f8b74cd..3b05f2717 100644 --- a/ir/ets/etsStructDeclaration.cpp +++ b/ir/ets/etsStructDeclaration.cpp @@ -15,6 +15,7 @@ #include "etsStructDeclaration.h" +#include "checker/TSchecker.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" @@ -48,24 +49,23 @@ void ETSStructDeclaration::Dump(ir::AstDumper *dumper) const {{"type", "ETSStructDeclaration"}, {"definition", def_}, {"decorators", AstDumper::Optional(decorators_)}}); } -void ETSStructDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ETSStructDeclaration::Compile(compiler::PandaGen *pg) const { - UNREACHABLE(); + pg->GetAstCompiler()->Compile(this); } -void ETSStructDeclaration::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSStructDeclaration::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSStructDeclaration::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSStructDeclaration::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSStructDeclaration::Check(checker::ETSChecker *checker) { - def_->Check(checker); - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/ets/etsStructDeclaration.h b/ir/ets/etsStructDeclaration.h index 7a52e9b80..10a2201ab 100644 --- a/ir/ets/etsStructDeclaration.h +++ b/ir/ets/etsStructDeclaration.h @@ -54,11 +54,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ClassDefinition *def_; diff --git a/ir/ets/etsTypeReference.cpp b/ir/ets/etsTypeReference.cpp index 849f52346..7ac21bd51 100644 --- a/ir/ets/etsTypeReference.cpp +++ b/ir/ets/etsTypeReference.cpp @@ -15,11 +15,13 @@ #include "etsTypeReference.h" +#include "checker/ETSchecker.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" #include "ir/ts/tsQualifiedName.h" #include "ir/ets/etsTypeReferencePart.h" -#include "checker/ETSchecker.h" -#include "compiler/core/ETSGen.h" namespace panda::es2panda::ir { void ETSTypeReference::TransformChildren(const NodeTransformer &cb) @@ -60,20 +62,23 @@ void ETSTypeReference::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ETSTypeReference"}, {"part", part_}}); } -void ETSTypeReference::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} -void ETSTypeReference::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSTypeReference::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} +void ETSTypeReference::Compile(compiler::ETSGen *etsg) const { - part_->Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSTypeReference::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSTypeReference::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReference::Check(checker::ETSChecker *checker) { - return GetType(checker); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReference::GetType(checker::ETSChecker *checker) diff --git a/ir/ets/etsTypeReference.h b/ir/ets/etsTypeReference.h index 17b8ef6ce..d771dcceb 100644 --- a/ir/ets/etsTypeReference.h +++ b/ir/ets/etsTypeReference.h @@ -41,10 +41,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsTypeReferencePart.cpp b/ir/ets/etsTypeReferencePart.cpp index 4d86e0905..79a2b4e97 100644 --- a/ir/ets/etsTypeReferencePart.cpp +++ b/ir/ets/etsTypeReferencePart.cpp @@ -15,13 +15,12 @@ #include "etsTypeReferencePart.h" -#include "ir/astDump.h" -#include "ir/expressions/identifier.h" -#include "ir/ts/tsTypeParameterInstantiation.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" #include "checker/ets/typeRelationContext.h" +#include "checker/TSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" namespace panda::es2panda::ir { void ETSTypeReferencePart::TransformChildren(const NodeTransformer &cb) @@ -58,20 +57,23 @@ void ETSTypeReferencePart::Dump(ir::AstDumper *dumper) const {"previous", AstDumper::Optional(prev_)}}); } -void ETSTypeReferencePart::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} -void ETSTypeReferencePart::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSTypeReferencePart::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} +void ETSTypeReferencePart::Compile(compiler::ETSGen *etsg) const { - name_->Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSTypeReferencePart::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSTypeReferencePart::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReferencePart::Check(checker::ETSChecker *checker) { - return GetType(checker); + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) diff --git a/ir/ets/etsTypeReferencePart.h b/ir/ets/etsTypeReferencePart.h index f8d82bcf2..f3e4b8aaf 100644 --- a/ir/ets/etsTypeReferencePart.h +++ b/ir/ets/etsTypeReferencePart.h @@ -58,10 +58,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/ets/etsWildcardType.cpp b/ir/ets/etsWildcardType.cpp index 07db87b9c..76c5ea1cc 100644 --- a/ir/ets/etsWildcardType.cpp +++ b/ir/ets/etsWildcardType.cpp @@ -15,11 +15,12 @@ #include "etsWildcardType.h" -#include "ir/astDump.h" -#include "ir/ets/etsTypeReference.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" +#include "checker/TSchecker.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" +#include "ir/astDump.h" +#include "ir/ets/etsTypeReference.h" namespace panda::es2panda::ir { void ETSWildcardType::TransformChildren(const NodeTransformer &cb) @@ -44,16 +45,19 @@ void ETSWildcardType::Dump(ir::AstDumper *dumper) const {"out", AstDumper::Optional(IsOut())}}); } -void ETSWildcardType::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void ETSWildcardType::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} -void ETSWildcardType::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ETSWildcardType::Compile(compiler::ETSGen *etsg) const { - etsg->Unimplemented(); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ETSWildcardType::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ETSWildcardType::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::TSChecker *checker) @@ -61,9 +65,9 @@ checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::TSChecker *che return nullptr; } -checker::Type *ETSWildcardType::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ETSWildcardType::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *ETSWildcardType::GetType([[maybe_unused]] checker::ETSChecker *checker) diff --git a/ir/ets/etsWildcardType.h b/ir/ets/etsWildcardType.h index 4ce8ce2ad..ca14342e0 100644 --- a/ir/ets/etsWildcardType.h +++ b/ir/ets/etsWildcardType.h @@ -36,11 +36,11 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; private: diff --git a/ir/expressions/arrowFunctionExpression.cpp b/ir/expressions/arrowFunctionExpression.cpp index a7ceaf07c..0cb1b3b65 100644 --- a/ir/expressions/arrowFunctionExpression.cpp +++ b/ir/expressions/arrowFunctionExpression.cpp @@ -44,124 +44,21 @@ void ArrowFunctionExpression::Dump(ir::AstDumper *dumper) const void ArrowFunctionExpression::Compile(compiler::PandaGen *pg) const { - pg->DefineFunction(func_, func_, func_->Scope()->InternalName()); + pg->GetAstCompiler()->Compile(this); } void ArrowFunctionExpression::Compile(compiler::ETSGen *etsg) const { - ASSERT(resolved_lambda_ != nullptr); - auto *ctor = resolved_lambda_->TsType()->AsETSObjectType()->ConstructSignatures()[0]; - std::vector arguments; - - for (auto *it : captured_vars_) { - if (it->HasFlag(binder::VariableFlags::LOCAL)) { - arguments.push_back(it->AsLocalVariable()->Vreg()); - } - } - - if (propagate_this_) { - arguments.push_back(etsg->GetThisReg()); - } - - etsg->InitLambdaObject(this, ctor, arguments); - etsg->SetAccumulatorType(resolved_lambda_->TsType()); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ArrowFunctionExpression::Check(checker::TSChecker *checker) { - binder::Variable *func_var = nullptr; - - if (func_->Parent()->Parent() != nullptr && func_->Parent()->Parent()->IsVariableDeclarator() && - func_->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) { - func_var = func_->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable(); - } - - checker::ScopeContext scope_ctx(checker, func_->Scope()); - - auto *signature_info = checker->Allocator()->New(checker->Allocator()); - checker->CheckFunctionParameterDeclarations(func_->Params(), signature_info); - - auto *signature = - checker->Allocator()->New(signature_info, checker->GlobalResolvingReturnType(), func_); - checker::Type *func_type = checker->CreateFunctionTypeWithSignature(signature); - - if (func_var != nullptr && func_var->TsType() == nullptr) { - func_var->SetTsType(func_type); - } - - signature->SetReturnType(checker->HandleFunctionReturn(func_)); - - if (!func_->Body()->IsExpression()) { - func_->Body()->Check(checker); - } - - return func_type; + return checker->GetAnalyzer()->Check(this); } checker::Type *ArrowFunctionExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - auto *func_type = checker->BuildFunctionSignature(func_, false); - - if (Function()->IsAsyncFunc()) { - auto *ret_type = static_cast(Function()->Signature()->ReturnType()); - if (ret_type->AssemblerName() != checker->GlobalBuiltinPromiseType()->AssemblerName()) { - checker->ThrowTypeError("Return type of async lambda must be 'Promise'", Function()->Start()); - } - } - - checker::ScopeContext scope_ctx(checker, func_->Scope()); - - if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) { - /* - example code: - ``` - class A { - prop:number - } - function A.method() { - let a = () => { - console.println(this.prop) - } - } - ``` - here the enclosing class of arrow function should be Class A - */ - checker->Context().SetContainingClass( - checker->Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType()); - } - - checker::SavedCheckerContext saved_context(checker, checker->Context().Status(), - checker->Context().ContainingClass()); - checker->AddStatus(checker::CheckerStatus::IN_LAMBDA); - checker->Context().SetContainingSignature(func_type->CallSignatures()[0]); - - auto *body_type = func_->Body()->Check(checker); - - if (func_->Body()->IsExpression()) { - if (func_->ReturnTypeAnnotation() == nullptr) { - func_type->CallSignatures()[0]->SetReturnType(body_type); - } - - checker::AssignmentContext( - checker->Relation(), func_->Body()->AsExpression(), body_type, func_type->CallSignatures()[0]->ReturnType(), - func_->Start(), - {"Return statements return type is not compatible with the containing functions return type"}, - checker::TypeRelationFlag::DIRECT_RETURN); - } - - checker->Context().SetContainingSignature(nullptr); - checker->CheckCapturedVariables(); - - for (auto [var, _] : checker->Context().CapturedVars()) { - (void)_; - captured_vars_.push_back(var); - } - - SetTsType(func_type); - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/expressions/arrowFunctionExpression.h b/ir/expressions/arrowFunctionExpression.h index 5bcefdb81..204ce84ac 100644 --- a/ir/expressions/arrowFunctionExpression.h +++ b/ir/expressions/arrowFunctionExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::compiler { +class ETSCompiler; +} // namespace panda::es2panda::compiler + namespace panda::es2panda::ir { class ScriptFunction; @@ -27,6 +31,8 @@ public: : Expression(AstNodeType::ARROW_FUNCTION_EXPRESSION), func_(func), captured_vars_(allocator->Adapter()) { } + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class compiler::ETSCompiler; const ScriptFunction *Function() const { @@ -71,10 +77,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ScriptFunction *func_; diff --git a/ir/statements/tryStatement.h b/ir/statements/tryStatement.h index 74b955c87..af69c1eb9 100644 --- a/ir/statements/tryStatement.h +++ b/ir/statements/tryStatement.h @@ -16,7 +16,7 @@ #ifndef ES2PANDA_IR_STATEMENT_TRY_STATEMENT_H #define ES2PANDA_IR_STATEMENT_TRY_STATEMENT_H -#include "compiler/core/ETSGen.h" +#include "compiler/core/labelPair.h" #include "ir/statement.h" namespace panda::es2panda::compiler { -- Gitee From 540c5e69d0ca199d7d4823d95d027f0c0be8ae45 Mon Sep 17 00:00:00 2001 From: Csaba Hurton Date: Fri, 27 Oct 2023 09:44:39 +0200 Subject: [PATCH 3/3] [eTS] (9) 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. AstNodes that are being moved: - nodes inside ir/statements: - EmptyStatement - ExpressionStatement - ForInStatement - ForOfStatement - ForUpdateStatement - FunctionDeclaration - IfStatement - LabelledStatement - SwitchCaseStatement - SwitchStatement Linked Internal Issue 13840 Signed-off-by: Csaba Hurton --- checker/ETSAnalyzer.cpp | 190 ++++++++++++++++++++++---- checker/TSAnalyzer.cpp | 108 ++++++++++++--- compiler/core/ETSCompiler.cpp | 182 ++++++++++++++++++++---- compiler/core/JSCompiler.cpp | 186 +++++++++++++++++++++---- ir/statements/emptyStatement.cpp | 22 ++- ir/statements/emptyStatement.h | 7 +- ir/statements/expressionStatement.cpp | 21 +-- ir/statements/expressionStatement.h | 8 +- ir/statements/forInStatement.cpp | 55 ++------ ir/statements/forInStatement.h | 7 +- ir/statements/forOfStatement.cpp | 170 +---------------------- ir/statements/forOfStatement.h | 8 +- ir/statements/forUpdateStatement.cpp | 119 ++-------------- ir/statements/forUpdateStatement.h | 8 +- ir/statements/functionDeclaration.cpp | 47 ++----- ir/statements/functionDeclaration.h | 8 +- ir/statements/ifStatement.cpp | 84 ++---------- ir/statements/ifStatement.h | 17 ++- ir/statements/labelledStatement.cpp | 30 ++-- ir/statements/labelledStatement.h | 15 +- ir/statements/switchCaseStatement.cpp | 23 +++- ir/statements/switchCaseStatement.h | 7 +- ir/statements/switchStatement.cpp | 135 ++---------------- ir/statements/switchStatement.h | 17 ++- 24 files changed, 746 insertions(+), 728 deletions(-) diff --git a/checker/ETSAnalyzer.cpp b/checker/ETSAnalyzer.cpp index 33a317b03..3d8cabf7b 100644 --- a/checker/ETSAnalyzer.cpp +++ b/checker/ETSAnalyzer.cpp @@ -19,14 +19,6 @@ #include "checker/ETSchecker.h" #include "checker/ets/castingContext.h" #include "checker/ets/typeRelationContext.h" -#include "ir/base/catchClause.h" -#include "ir/base/classProperty.h" -#include "ir/base/classStaticBlock.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/objectExpression.h" -#include "ir/expressions/arrayExpression.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" #include "util/helpers.h" namespace panda::es2panda::checker { @@ -637,52 +629,144 @@ checker::Type *ETSAnalyzer::Check(ir::DoWhileStatement *st) const UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::EmptyStatement *st) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::EmptyStatement *st) const { - (void)st; - UNREACHABLE(); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ExpressionStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + return st->GetExpression()->Check(checker); } -checker::Type *ETSAnalyzer::Check(ir::ForInStatement *st) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ForInStatement *st) const { - (void)st; UNREACHABLE(); } +// NOLINTBEGIN(modernize-avoid-c-arrays) +static constexpr char const INVALID_SOURCE_EXPR_TYPE[] = + "'For-of' statement source expression should be either a string or an array."; +static constexpr char const INVALID_CONST_ASSIGNMENT[] = "Cannot assign a value to a constant variable "; +static constexpr char const ITERATOR_TYPE_ABSENT[] = "Cannot obtain iterator type in 'for-of' statement."; +// NOLINTEND(modernize-avoid-c-arrays) + checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + checker::Type *const expr_type = st->Right()->Check(checker); + checker::Type *elem_type; + + if (expr_type == nullptr || (!expr_type->IsETSArrayType() && !expr_type->IsETSStringType())) { + checker->ThrowTypeError(INVALID_SOURCE_EXPR_TYPE, st->Right()->Start()); + } else if (expr_type->IsETSStringType()) { + elem_type = checker->GetGlobalTypesHolder()->GlobalCharType(); + } else { + elem_type = expr_type->AsETSArrayType()->ElementType()->Instantiate(checker->Allocator(), checker->Relation(), + checker->GetGlobalTypesHolder()); + elem_type->RemoveTypeFlag(checker::TypeFlag::CONSTANT); + } + + st->Left()->Check(checker); + checker::Type *iter_type = nullptr; + + if (st->Left()->IsIdentifier()) { + if (auto *const variable = st->Left()->AsIdentifier()->Variable(); variable != nullptr) { + if (variable->Declaration()->IsConstDecl()) { + checker->ThrowTypeError({INVALID_CONST_ASSIGNMENT, variable->Name()}, + variable->Declaration()->Node()->Start()); + } + } + iter_type = st->Left()->AsIdentifier()->TsType(); + } else if (st->Left()->IsVariableDeclaration()) { + if (auto const &declarators = st->Left()->AsVariableDeclaration()->Declarators(); !declarators.empty()) { + if (auto const &for_iterator = declarators.front(); for_iterator->TsType() == nullptr) { + if (auto *resolved = checker->FindVariableInFunctionScope(for_iterator->Id()->AsIdentifier()->Name()); + resolved != nullptr) { + resolved->SetTsType(elem_type); + iter_type = elem_type; + } + } else { + iter_type = for_iterator->TsType(); + } + } + } + + if (iter_type == nullptr) { + checker->ThrowTypeError(ITERATOR_TYPE_ABSENT, st->Left()->Start()); + } + + auto *const relation = checker->Relation(); + relation->SetFlags(checker::TypeRelationFlag::ASSIGNMENT_CONTEXT); + relation->SetNode(checker->AllocNode()); // Dummy node to avoid assertion! + + if (!relation->IsAssignableTo(elem_type, iter_type)) { + std::stringstream ss {}; + ss << "Source element type '"; + elem_type->ToString(ss); + ss << "' is not assignable to the loop iterator type '"; + iter_type->ToString(ss); + ss << "'."; + checker->ThrowTypeError(ss.str(), st->Start()); + } + + relation->SetNode(nullptr); + relation->SetFlags(checker::TypeRelationFlag::NONE); + + st->Body()->Check(checker); + + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ForUpdateStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + if (st->Init() != nullptr) { + st->Init()->Check(checker); + } + + if (st->Test() != nullptr) { + checker->CheckTruthinessOfType(st->Test()); + } + + if (st->Update() != nullptr) { + st->Update()->Check(checker); + } + + st->Body()->Check(checker); + + return nullptr; } -checker::Type *ETSAnalyzer::Check(ir::FunctionDeclaration *st) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::FunctionDeclaration *st) const { - (void)st; UNREACHABLE(); } checker::Type *ETSAnalyzer::Check(ir::IfStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + checker->CheckTruthinessOfType(st->test_); + + st->consequent_->Check(checker); + + if (st->Alternate() != nullptr) { + st->alternate_->Check(checker); + } + + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::LabelledStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + st->body_->Check(checker); + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ReturnStatement *st) const @@ -882,16 +966,64 @@ checker::Type *ETSAnalyzer::Check(ir::ReturnStatement *st) const return nullptr; } -checker::Type *ETSAnalyzer::Check(ir::SwitchCaseStatement *st) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::SwitchCaseStatement *st) const { - (void)st; UNREACHABLE(); } checker::Type *ETSAnalyzer::Check(ir::SwitchStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + checker::ScopeContext scope_ctx(checker, st->scope_); + st->discriminant_->Check(checker); + checker::SavedTypeRelationFlagsContext saved_type_relation_flag_ctx(checker->Relation(), + checker::TypeRelationFlag::NONE); + // TODO(user): check exhaustive Switch + checker->CheckSwitchDiscriminant(st->discriminant_); + auto *compared_expr_type = st->discriminant_->TsType(); + auto unboxed_disc_type = + (st->Discriminant()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U + ? checker->ETSBuiltinTypeAsPrimitiveType(compared_expr_type) + : compared_expr_type; + + bool valid_case_type; + + for (auto *it : st->Cases()) { + if (it->Test() != nullptr) { + auto *case_type = it->Test()->Check(checker); + valid_case_type = true; + if (case_type->HasTypeFlag(checker::TypeFlag::CHAR)) { + valid_case_type = compared_expr_type->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL); + } else if (case_type->IsETSEnumType() && st->Discriminant()->TsType()->IsETSEnumType()) { + valid_case_type = + st->Discriminant()->TsType()->AsETSEnumType()->IsSameEnumType(case_type->AsETSEnumType()); + } else if (case_type->IsETSStringEnumType() && st->Discriminant()->TsType()->IsETSStringEnumType()) { + valid_case_type = st->Discriminant()->TsType()->AsETSStringEnumType()->IsSameEnumType( + case_type->AsETSStringEnumType()); + } else { + checker::AssignmentContext( + checker->Relation(), st->discriminant_, case_type, unboxed_disc_type, it->Test()->Start(), + {"Switch case type ", case_type, " is not comparable to discriminant type ", compared_expr_type}, + (compared_expr_type->IsETSObjectType() ? checker::TypeRelationFlag::NO_WIDENING + : checker::TypeRelationFlag::NO_UNBOXING) | + checker::TypeRelationFlag::NO_BOXING); + } + + if (!valid_case_type) { + checker->ThrowTypeError( + {"Switch case type ", case_type, " is not comparable to discriminant type ", compared_expr_type}, + it->Test()->Start()); + } + } + + for (auto *case_stmt : it->Consequent()) { + case_stmt->Check(checker); + } + } + + checker->CheckForSameSwitchCases(&st->cases_); + + return nullptr; } checker::Type *ETSAnalyzer::Check(ir::ThrowStatement *st) const diff --git a/checker/TSAnalyzer.cpp b/checker/TSAnalyzer.cpp index 62f2f79a9..bb235a866 100644 --- a/checker/TSAnalyzer.cpp +++ b/checker/TSAnalyzer.cpp @@ -531,51 +531,90 @@ checker::Type *TSAnalyzer::Check(ir::DoWhileStatement *st) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::EmptyStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::EmptyStatement *st) const { - (void)st; - UNREACHABLE(); + return nullptr; } checker::Type *TSAnalyzer::Check(ir::ExpressionStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + return st->GetExpression()->Check(checker); } -checker::Type *TSAnalyzer::Check(ir::ForInStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ForInStatement *st) const { - (void)st; UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::ForOfStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ForOfStatement *st) const { - (void)st; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::ForUpdateStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + if (st->Init() != nullptr) { + st->Init()->Check(checker); + } + + if (st->Test() != nullptr) { + checker::Type *test_type = st->Test()->Check(checker); + checker->CheckTruthinessOfType(test_type, st->Start()); + } + + if (st->Update() != nullptr) { + st->Update()->Check(checker); + } + + st->Body()->Check(checker); + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::FunctionDeclaration *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + if (st->Function()->IsOverload()) { + return nullptr; + } + + const util::StringView &func_name = st->Function()->Id()->Name(); + auto result = checker->Scope()->Find(func_name); + ASSERT(result.variable); + + checker::ScopeContext scope_ctx(checker, st->Function()->Scope()); + + if (result.variable->TsType() == nullptr) { + checker->InferFunctionDeclarationType(result.variable->Declaration()->AsFunctionDecl(), result.variable); + } + + st->Function()->Body()->Check(checker); + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::IfStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *test_type = st->test_->Check(checker); + checker->CheckTruthinessOfType(test_type, st->Start()); + checker->CheckTestingKnownTruthyCallableOrAwaitableType(st->test_, test_type, st->consequent_); + + st->consequent_->Check(checker); + + if (st->Alternate() != nullptr) { + st->alternate_->Check(checker); + } + + return nullptr; } -checker::Type *TSAnalyzer::Check(ir::LabelledStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::LabelledStatement *st) const { - (void)st; UNREACHABLE(); } @@ -610,16 +649,43 @@ checker::Type *TSAnalyzer::Check(ir::ReturnStatement *st) const return nullptr; } -checker::Type *TSAnalyzer::Check(ir::SwitchCaseStatement *st) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SwitchCaseStatement *st) const { - (void)st; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::SwitchStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + checker::Type *expr_type = st->discriminant_->Check(checker); + bool expr_is_literal = checker::TSChecker::IsLiteralType(expr_type); + + for (auto *it : st->Cases()) { + if (it->Test() != nullptr) { + checker::Type *case_type = it->Test()->Check(checker); + bool case_is_literal = checker::TSChecker::IsLiteralType(case_type); + checker::Type *compared_expr_type = expr_type; + + if (!case_is_literal || !expr_is_literal) { + case_type = case_is_literal ? checker->GetBaseTypeOfLiteralType(case_type) : case_type; + compared_expr_type = checker->GetBaseTypeOfLiteralType(expr_type); + } + + if (!checker->IsTypeEqualityComparableTo(compared_expr_type, case_type) && + !checker->IsTypeComparableTo(case_type, compared_expr_type)) { + checker->ThrowTypeError({"Type ", case_type, " is not comparable to type ", compared_expr_type}, + it->Test()->Start()); + } + } + + for (auto *case_stmt : it->Consequent()) { + case_stmt->Check(checker); + } + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::ThrowStatement *st) const diff --git a/compiler/core/ETSCompiler.cpp b/compiler/core/ETSCompiler.cpp index ffff9fe9d..ad28f2648 100644 --- a/compiler/core/ETSCompiler.cpp +++ b/compiler/core/ETSCompiler.cpp @@ -19,6 +19,7 @@ #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" +#include "compiler/core/switchBuilder.h" #include "compiler/function/functionBuilder.h" namespace panda::es2panda::compiler { @@ -531,52 +532,156 @@ void ETSCompiler::Compile(const ir::DoWhileStatement *st) const UNREACHABLE(); } -void ETSCompiler::Compile(const ir::EmptyStatement *st) const -{ - (void)st; - UNREACHABLE(); -} +void ETSCompiler::Compile([[maybe_unused]] const ir::EmptyStatement *st) const {} void ETSCompiler::Compile(const ir::ExpressionStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + st->GetExpression()->Compile(etsg); } -void ETSCompiler::Compile(const ir::ForInStatement *st) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ForInStatement *st) const { - (void)st; UNREACHABLE(); } void ETSCompiler::Compile(const ir::ForOfStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + compiler::LocalRegScope decl_reg_scope(etsg, st->Scope()->DeclScope()->InitScope()); + + checker::Type const *const expr_type = st->Right()->TsType(); + ASSERT(expr_type->IsETSArrayType() || expr_type->IsETSStringType()); + + st->Right()->Compile(etsg); + compiler::VReg obj_reg = etsg->AllocReg(); + etsg->StoreAccumulator(st, obj_reg); + + if (expr_type->IsETSArrayType()) { + etsg->LoadArrayLength(st, obj_reg); + } else { + etsg->LoadStringLength(st); + } + + compiler::VReg size_reg = etsg->AllocReg(); + etsg->StoreAccumulator(st, size_reg); + + compiler::LabelTarget label_target(etsg); + auto label_ctx = compiler::LabelContext(etsg, label_target); + + etsg->BranchIfFalse(st, label_target.BreakTarget()); + + compiler::VReg count_reg = etsg->AllocReg(); + etsg->MoveImmediateToRegister(st, count_reg, checker::TypeFlag::INT, static_cast(0)); + etsg->LoadAccumulatorInt(st, static_cast(0)); + + auto *const start_label = etsg->AllocLabel(); + etsg->SetLabel(st, start_label); + + auto lref = compiler::ETSLReference::Create(etsg, st->Left(), false); + + if (st->Right()->TsType()->IsETSArrayType()) { + etsg->LoadArrayElement(st, obj_reg); + } else { + etsg->LoadStringChar(st, obj_reg, count_reg); + } + + lref.SetValue(); + st->Body()->Compile(etsg); + + etsg->SetLabel(st, label_target.ContinueTarget()); + + etsg->IncrementImmediateRegister(st, count_reg, checker::TypeFlag::INT, static_cast(1)); + etsg->LoadAccumulator(st, count_reg); + + etsg->JumpCompareRegister(st, size_reg, start_label); + etsg->SetLabel(st, label_target.BreakTarget()); } void ETSCompiler::Compile(const ir::ForUpdateStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + compiler::LocalRegScope decl_reg_scope(etsg, st->Scope()->DeclScope()->InitScope()); + + if (st->Init() != nullptr) { + ASSERT(st->Init()->IsVariableDeclaration() || st->Init()->IsExpression()); + st->Init()->Compile(etsg); + } + + auto *start_label = etsg->AllocLabel(); + compiler::LabelTarget label_target(etsg); + auto label_ctx = compiler::LabelContext(etsg, label_target); + etsg->SetLabel(st, start_label); + + { + compiler::LocalRegScope reg_scope(etsg, st->Scope()); + + if (st->Test() != nullptr) { + compiler::Condition::Compile(etsg, st->Test(), label_target.BreakTarget()); + } + + st->Body()->Compile(etsg); + etsg->SetLabel(st, label_target.ContinueTarget()); + } + + if (st->Update() != nullptr) { + st->Update()->Compile(etsg); + } + + etsg->Branch(st, start_label); + etsg->SetLabel(st, label_target.BreakTarget()); } -void ETSCompiler::Compile(const ir::FunctionDeclaration *st) const +void ETSCompiler::Compile([[maybe_unused]] const ir::FunctionDeclaration *st) const { - (void)st; UNREACHABLE(); } void ETSCompiler::Compile(const ir::IfStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto res = compiler::Condition::CheckConstantExpr(st->Test()); + + if (res == compiler::Condition::Result::CONST_TRUE) { + st->Consequent()->Compile(etsg); + return; + } + + if (res == compiler::Condition::Result::CONST_FALSE) { + if (st->Alternate() != nullptr) { + st->Alternate()->Compile(etsg); + } + return; + } + + auto *consequent_end = etsg->AllocLabel(); + compiler::Label *statement_end = consequent_end; + + compiler::Condition::Compile(etsg, st->Test(), consequent_end); + + st->Consequent()->Compile(etsg); + + if (st->Alternate() != nullptr) { + statement_end = etsg->AllocLabel(); + etsg->Branch(etsg->Insns().back()->Node(), statement_end); + + etsg->SetLabel(st, consequent_end); + st->Alternate()->Compile(etsg); + } + + etsg->SetLabel(st, statement_end); +} + +void CompileImpl(const ir::LabelledStatement *self, ETSGen *cg) +{ + compiler::LabelContext label_ctx(cg, self); + self->Body()->Compile(cg); } void ETSCompiler::Compile(const ir::LabelledStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + CompileImpl(st, etsg); } void ETSCompiler::Compile(const ir::ReturnStatement *st) const @@ -624,16 +729,47 @@ void ETSCompiler::Compile(const ir::ReturnStatement *st) const etsg->ReturnAcc(st); } -void ETSCompiler::Compile(const ir::SwitchCaseStatement *st) const +void ETSCompiler::Compile([[maybe_unused]] const ir::SwitchCaseStatement *st) const { - (void)st; UNREACHABLE(); } +static void CompileImpl(const ir::SwitchStatement *self, ETSGen *etsg) +{ + compiler::LocalRegScope lrs(etsg, self->Scope()); + compiler::SwitchBuilder builder(etsg, self); + compiler::VReg tag = etsg->AllocReg(); + + builder.CompileTagOfSwitch(tag); + uint32_t default_index = 0; + + for (size_t i = 0; i < self->Cases().size(); i++) { + const auto *clause = self->Cases()[i]; + + if (clause->Test() == nullptr) { + default_index = i; + continue; + } + + builder.JumpIfCase(tag, i); + } + + if (default_index > 0) { + builder.JumpToDefault(default_index); + } else { + builder.Break(); + } + + for (size_t i = 0; i < self->Cases().size(); i++) { + builder.SetCaseTarget(i); + builder.CompileCaseStatements(i); + } +} + void ETSCompiler::Compile(const ir::SwitchStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + CompileImpl(st, etsg); } void ETSCompiler::Compile(const ir::ThrowStatement *st) const diff --git a/compiler/core/JSCompiler.cpp b/compiler/core/JSCompiler.cpp index 5be37688d..e4b6d32fc 100644 --- a/compiler/core/JSCompiler.cpp +++ b/compiler/core/JSCompiler.cpp @@ -18,6 +18,7 @@ #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" +#include "compiler/core/switchBuilder.h" #include "compiler/function/functionBuilder.h" #include "util/helpers.h" @@ -834,52 +835,156 @@ void JSCompiler::Compile(const ir::DoWhileStatement *st) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::EmptyStatement *st) const -{ - (void)st; - UNREACHABLE(); -} +void JSCompiler::Compile([[maybe_unused]] const ir::EmptyStatement *st) const {} void JSCompiler::Compile(const ir::ExpressionStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + st->GetExpression()->Compile(pg); } void JSCompiler::Compile(const ir::ForInStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::LabelTarget label_target(pg); + + compiler::RegScope rs(pg); + compiler::VReg iter = pg->AllocReg(); + compiler::VReg prop_name = pg->AllocReg(); + + // create enumerator + st->Right()->Compile(pg); + pg->GetPropIterator(st); + pg->StoreAccumulator(st, iter); + + pg->SetLabel(st, label_target.ContinueTarget()); + + // get next prop of enumerator + pg->GetNextPropName(st, iter); + pg->StoreAccumulator(st, prop_name); + pg->BranchIfUndefined(st, label_target.BreakTarget()); + + compiler::LocalRegScope decl_reg_scope(pg, st->Scope()->DeclScope()->InitScope()); + auto lref = compiler::JSLReference::Create(pg, st->Left(), false); + pg->LoadAccumulator(st, prop_name); + lref.SetValue(); + + compiler::LoopEnvScope decl_env_scope(pg, st->Scope()->DeclScope()); + + { + compiler::LoopEnvScope env_scope(pg, st->Scope(), label_target); + st->Body()->Compile(pg); + } + + pg->Branch(st, label_target.ContinueTarget()); + pg->SetLabel(st, label_target.BreakTarget()); } void JSCompiler::Compile(const ir::ForOfStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::LocalRegScope decl_reg_scope(pg, st->Scope()->DeclScope()->InitScope()); + + st->Right()->Compile(pg); + + compiler::LabelTarget label_target(pg); + auto iterator_type = st->IsAwait() ? compiler::IteratorType::ASYNC : compiler::IteratorType::SYNC; + compiler::Iterator iterator(pg, st, iterator_type); + + pg->SetLabel(st, label_target.ContinueTarget()); + + iterator.Next(); + iterator.Complete(); + pg->BranchIfTrue(st, label_target.BreakTarget()); + + iterator.Value(); + pg->StoreAccumulator(st, iterator.NextResult()); + + auto lref = compiler::JSLReference::Create(pg, st->Left(), false); + + { + compiler::IteratorContext for_of_ctx(pg, iterator, label_target); + pg->LoadAccumulator(st, iterator.NextResult()); + lref.SetValue(); + + compiler::LoopEnvScope decl_env_scope(pg, st->Scope()->DeclScope()); + compiler::LoopEnvScope env_scope(pg, st->Scope(), {}); + st->Body()->Compile(pg); + } + + pg->Branch(st, label_target.ContinueTarget()); + pg->SetLabel(st, label_target.BreakTarget()); } void JSCompiler::Compile(const ir::ForUpdateStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::LocalRegScope decl_reg_scope(pg, st->Scope()->DeclScope()->InitScope()); + + if (st->Init() != nullptr) { + ASSERT(st->Init()->IsVariableDeclaration() || st->Init()->IsExpression()); + st->Init()->Compile(pg); + } + + auto *start_label = pg->AllocLabel(); + compiler::LabelTarget label_target(pg); + + compiler::LoopEnvScope decl_env_scope(pg, st->Scope()->DeclScope()); + compiler::LoopEnvScope env_scope(pg, label_target, st->Scope()); + pg->SetLabel(st, start_label); + + { + compiler::LocalRegScope reg_scope(pg, st->Scope()); + + if (st->Test() != nullptr) { + compiler::Condition::Compile(pg, st->Test(), label_target.BreakTarget()); + } + + st->Body()->Compile(pg); + pg->SetLabel(st, label_target.ContinueTarget()); + env_scope.CopyPetIterationCtx(); + } + + if (st->Update() != nullptr) { + st->Update()->Compile(pg); + } + + pg->Branch(st, start_label); + pg->SetLabel(st, label_target.BreakTarget()); } -void JSCompiler::Compile(const ir::FunctionDeclaration *st) const +void JSCompiler::Compile([[maybe_unused]] const ir::FunctionDeclaration *st) const {} + +void JSCompiler::Compile(const ir::IfStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + auto *consequent_end = pg->AllocLabel(); + compiler::Label *statement_end = consequent_end; + + compiler::Condition::Compile(pg, st->Test(), consequent_end); + st->Consequent()->Compile(pg); + + if (st->Alternate() != nullptr) { + statement_end = pg->AllocLabel(); + pg->Branch(pg->Insns().back()->Node(), statement_end); + + pg->SetLabel(st, consequent_end); + st->Alternate()->Compile(pg); + } + + pg->SetLabel(st, statement_end); } -void JSCompiler::Compile(const ir::IfStatement *st) const +void CompileImpl(const ir::LabelledStatement *self, PandaGen *cg) { - (void)st; - UNREACHABLE(); + compiler::LabelContext label_ctx(cg, self); + self->Body()->Compile(cg); } void JSCompiler::Compile(const ir::LabelledStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + CompileImpl(st, pg); } void JSCompiler::Compile(const ir::ReturnStatement *st) const @@ -908,16 +1013,47 @@ void JSCompiler::Compile(const ir::ReturnStatement *st) const } } -void JSCompiler::Compile(const ir::SwitchCaseStatement *st) const +void JSCompiler::Compile([[maybe_unused]] const ir::SwitchCaseStatement *st) const { - (void)st; UNREACHABLE(); } +static void CompileImpl(const ir::SwitchStatement *self, PandaGen *cg) +{ + compiler::LocalRegScope lrs(cg, self->Scope()); + compiler::SwitchBuilder builder(cg, self); + compiler::VReg tag = cg->AllocReg(); + + builder.CompileTagOfSwitch(tag); + uint32_t default_index = 0; + + for (size_t i = 0; i < self->Cases().size(); i++) { + const auto *clause = self->Cases()[i]; + + if (clause->Test() == nullptr) { + default_index = i; + continue; + } + + builder.JumpIfCase(tag, i); + } + + if (default_index > 0) { + builder.JumpToDefault(default_index); + } else { + builder.Break(); + } + + for (size_t i = 0; i < self->Cases().size(); i++) { + builder.SetCaseTarget(i); + builder.CompileCaseStatements(i); + } +} + void JSCompiler::Compile(const ir::SwitchStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + CompileImpl(st, pg); } void JSCompiler::Compile(const ir::ThrowStatement *st) const diff --git a/ir/statements/emptyStatement.cpp b/ir/statements/emptyStatement.cpp index 5da09fdd2..4c00f2a89 100644 --- a/ir/statements/emptyStatement.cpp +++ b/ir/statements/emptyStatement.cpp @@ -15,7 +15,9 @@ #include "emptyStatement.h" -#include "ir/astDump.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void EmptyStatement::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -26,15 +28,23 @@ void EmptyStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "EmptyStatement"}}); } -void EmptyStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void EmptyStatement::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} + +void EmptyStatement::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} -checker::Type *EmptyStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *EmptyStatement::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *EmptyStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *EmptyStatement::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/emptyStatement.h b/ir/statements/emptyStatement.h index 44c82b2fb..a815449fb 100644 --- a/ir/statements/emptyStatement.h +++ b/ir/statements/emptyStatement.h @@ -26,9 +26,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: }; diff --git a/ir/statements/expressionStatement.cpp b/ir/statements/expressionStatement.cpp index 61ebaca00..d4750832b 100644 --- a/ir/statements/expressionStatement.cpp +++ b/ir/statements/expressionStatement.cpp @@ -15,8 +15,9 @@ #include "expressionStatement.h" -#include "ir/astDump.h" -#include "ir/expression.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void ExpressionStatement::TransformChildren(const NodeTransformer &cb) @@ -34,23 +35,23 @@ void ExpressionStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ExpressionStatement"}, {"expression", expression_}}); } -void ExpressionStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ExpressionStatement::Compile(compiler::PandaGen *pg) const { - expression_->Compile(pg); + pg->GetAstCompiler()->Compile(this); } -void ExpressionStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ExpressionStatement::Compile(compiler::ETSGen *etsg) const { - expression_->Compile(etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ExpressionStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ExpressionStatement::Check(checker::TSChecker *checker) { - return expression_->Check(checker); + return checker->GetAnalyzer()->Check(this); } -checker::Type *ExpressionStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ExpressionStatement::Check(checker::ETSChecker *checker) { - return expression_->Check(checker); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/expressionStatement.h b/ir/statements/expressionStatement.h index 2f80d8722..56ea20363 100644 --- a/ir/statements/expressionStatement.h +++ b/ir/statements/expressionStatement.h @@ -38,10 +38,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *expression_; diff --git a/ir/statements/forInStatement.cpp b/ir/statements/forInStatement.cpp index 2a3a56cce..1bc536de5 100644 --- a/ir/statements/forInStatement.cpp +++ b/ir/statements/forInStatement.cpp @@ -15,14 +15,9 @@ #include "forInStatement.h" -#include "binder/scope.h" -#include "compiler/base/lreference.h" -#include "compiler/core/labelTarget.h" -#include "compiler/core/pandagen.h" #include "checker/TSchecker.h" - -#include "ir/astDump.h" -#include "ir/expression.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void ForInStatement::TransformChildren(const NodeTransformer &cb) @@ -44,49 +39,23 @@ void ForInStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ForInStatement"}, {"left", left_}, {"right", right_}, {"body", body_}}); } -void ForInStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ForInStatement::Compile(compiler::PandaGen *pg) const { - compiler::LabelTarget label_target(pg); - - compiler::RegScope rs(pg); - compiler::VReg iter = pg->AllocReg(); - compiler::VReg prop_name = pg->AllocReg(); - - // create enumerator - right_->Compile(pg); - pg->GetPropIterator(this); - pg->StoreAccumulator(this, iter); - - pg->SetLabel(this, label_target.ContinueTarget()); - - // get next prop of enumerator - pg->GetNextPropName(this, iter); - pg->StoreAccumulator(this, prop_name); - pg->BranchIfUndefined(this, label_target.BreakTarget()); - - compiler::LocalRegScope decl_reg_scope(pg, Scope()->DeclScope()->InitScope()); - auto lref = compiler::JSLReference::Create(pg, left_, false); - pg->LoadAccumulator(this, prop_name); - lref.SetValue(); - - compiler::LoopEnvScope decl_env_scope(pg, Scope()->DeclScope()); - - { - compiler::LoopEnvScope env_scope(pg, Scope(), label_target); - body_->Compile(pg); - } + pg->GetAstCompiler()->Compile(this); +} - pg->Branch(this, label_target.ContinueTarget()); - pg->SetLabel(this, label_target.BreakTarget()); +void ForInStatement::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ForInStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ForInStatement::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *ForInStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ForInStatement::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/forInStatement.h b/ir/statements/forInStatement.h index c8e99fb4f..9304d7264 100644 --- a/ir/statements/forInStatement.h +++ b/ir/statements/forInStatement.h @@ -72,9 +72,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: AstNode *left_; diff --git a/ir/statements/forOfStatement.cpp b/ir/statements/forOfStatement.cpp index 0e8fedbbc..a1bd259fa 100644 --- a/ir/statements/forOfStatement.cpp +++ b/ir/statements/forOfStatement.cpp @@ -15,18 +15,9 @@ #include "forOfStatement.h" -#include "binder/scope.h" -#include "compiler/base/iterators.h" -#include "compiler/base/lreference.h" -#include "compiler/core/labelTarget.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" -#include "ir/astDump.h" -#include "ir/expression.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/superExpression.h" -#include "ir/statements/variableDeclarator.h" -#include "ir/statements/variableDeclaration.h" namespace panda::es2panda::ir { void ForOfStatement::TransformChildren(const NodeTransformer &cb) @@ -49,170 +40,23 @@ void ForOfStatement::Dump(ir::AstDumper *dumper) const {{"type", "ForOfStatement"}, {"await", is_await_}, {"left", left_}, {"right", right_}, {"body", body_}}); } -void ForOfStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ForOfStatement::Compile(compiler::PandaGen *pg) const { - compiler::LocalRegScope decl_reg_scope(pg, Scope()->DeclScope()->InitScope()); - - right_->Compile(pg); - - compiler::LabelTarget label_target(pg); - auto iterator_type = is_await_ ? compiler::IteratorType::ASYNC : compiler::IteratorType::SYNC; - compiler::Iterator iterator(pg, this, iterator_type); - - pg->SetLabel(this, label_target.ContinueTarget()); - - iterator.Next(); - iterator.Complete(); - pg->BranchIfTrue(this, label_target.BreakTarget()); - - iterator.Value(); - pg->StoreAccumulator(this, iterator.NextResult()); - - auto lref = compiler::JSLReference::Create(pg, left_, false); - - { - compiler::IteratorContext for_of_ctx(pg, iterator, label_target); - pg->LoadAccumulator(this, iterator.NextResult()); - lref.SetValue(); - - compiler::LoopEnvScope decl_env_scope(pg, Scope()->DeclScope()); - compiler::LoopEnvScope env_scope(pg, Scope(), {}); - body_->Compile(pg); - } - - pg->Branch(this, label_target.ContinueTarget()); - pg->SetLabel(this, label_target.BreakTarget()); + pg->GetAstCompiler()->Compile(this); } void ForOfStatement::Compile(compiler::ETSGen *etsg) const { - compiler::LocalRegScope decl_reg_scope(etsg, Scope()->DeclScope()->InitScope()); - - checker::Type const *const expr_type = right_->TsType(); - ASSERT(expr_type->IsETSArrayType() || expr_type->IsETSStringType()); - - right_->Compile(etsg); - compiler::VReg obj_reg = etsg->AllocReg(); - etsg->StoreAccumulator(this, obj_reg); - - if (expr_type->IsETSArrayType()) { - etsg->LoadArrayLength(this, obj_reg); - } else { - etsg->LoadStringLength(this); - } - - compiler::VReg size_reg = etsg->AllocReg(); - etsg->StoreAccumulator(this, size_reg); - - compiler::LabelTarget label_target(etsg); - auto label_ctx = compiler::LabelContext(etsg, label_target); - - etsg->BranchIfFalse(this, label_target.BreakTarget()); - - compiler::VReg count_reg = etsg->AllocReg(); - etsg->MoveImmediateToRegister(this, count_reg, checker::TypeFlag::INT, static_cast(0)); - etsg->LoadAccumulatorInt(this, static_cast(0)); - - auto *const start_label = etsg->AllocLabel(); - etsg->SetLabel(this, start_label); - - auto lref = compiler::ETSLReference::Create(etsg, left_, false); - - if (right_->TsType()->IsETSArrayType()) { - etsg->LoadArrayElement(this, obj_reg); - } else { - etsg->LoadStringChar(this, obj_reg, count_reg); - } - - lref.SetValue(); - body_->Compile(etsg); - - etsg->SetLabel(this, label_target.ContinueTarget()); - - etsg->IncrementImmediateRegister(this, count_reg, checker::TypeFlag::INT, static_cast(1)); - etsg->LoadAccumulator(this, count_reg); - - etsg->JumpCompareRegister(this, size_reg, start_label); - etsg->SetLabel(this, label_target.BreakTarget()); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ForOfStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ForOfStatement::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } -// NOLINTBEGIN(modernize-avoid-c-arrays) -static constexpr char const INVALID_SOURCE_EXPR_TYPE[] = - "'For-of' statement source expression should be either a string or an array."; -static constexpr char const INVALID_CONST_ASSIGNMENT[] = "Cannot assign a value to a constant variable "; -static constexpr char const ITERATOR_TYPE_ABSENT[] = "Cannot obtain iterator type in 'for-of' statement."; -// NOLINTEND(modernize-avoid-c-arrays) - checker::Type *ForOfStatement::Check(checker::ETSChecker *checker) { - checker::ScopeContext scope_ctx(checker, Scope()); - - checker::Type *const expr_type = right_->Check(checker); - checker::Type *elem_type; - - if (expr_type == nullptr || (!expr_type->IsETSArrayType() && !expr_type->IsETSStringType())) { - checker->ThrowTypeError(INVALID_SOURCE_EXPR_TYPE, right_->Start()); - } else if (expr_type->IsETSStringType()) { - elem_type = checker->GetGlobalTypesHolder()->GlobalCharType(); - } else { - elem_type = expr_type->AsETSArrayType()->ElementType()->Instantiate(checker->Allocator(), checker->Relation(), - checker->GetGlobalTypesHolder()); - elem_type->RemoveTypeFlag(checker::TypeFlag::CONSTANT); - } - - left_->Check(checker); - checker::Type *iter_type = nullptr; - - if (left_->IsIdentifier()) { - if (auto *const variable = left_->AsIdentifier()->Variable(); variable != nullptr) { - if (variable->Declaration()->IsConstDecl()) { - checker->ThrowTypeError({INVALID_CONST_ASSIGNMENT, variable->Name()}, - variable->Declaration()->Node()->Start()); - } - } - iter_type = left_->AsIdentifier()->TsType(); - } else if (left_->IsVariableDeclaration()) { - if (auto const &declarators = left_->AsVariableDeclaration()->Declarators(); !declarators.empty()) { - if (auto const &for_iterator = declarators.front(); for_iterator->TsType() == nullptr) { - if (auto *resolved = checker->FindVariableInFunctionScope(for_iterator->Id()->AsIdentifier()->Name()); - resolved != nullptr) { - resolved->SetTsType(elem_type); - iter_type = elem_type; - } - } else { - iter_type = for_iterator->TsType(); - } - } - } - - if (iter_type == nullptr) { - checker->ThrowTypeError(ITERATOR_TYPE_ABSENT, left_->Start()); - } - - auto *const relation = checker->Relation(); - relation->SetFlags(checker::TypeRelationFlag::ASSIGNMENT_CONTEXT); - relation->SetNode(checker->AllocNode()); // Dummy node to avoid assertion! - - if (!relation->IsAssignableTo(elem_type, iter_type)) { - std::stringstream ss {}; - ss << "Source element type '"; - elem_type->ToString(ss); - ss << "' is not assignable to the loop iterator type '"; - iter_type->ToString(ss); - ss << "'."; - checker->ThrowTypeError(ss.str(), Start()); - } - - relation->SetNode(nullptr); - relation->SetFlags(checker::TypeRelationFlag::NONE); - - body_->Check(checker); - - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/forOfStatement.h b/ir/statements/forOfStatement.h index 9bb2ecf8d..ec35b6456 100644 --- a/ir/statements/forOfStatement.h +++ b/ir/statements/forOfStatement.h @@ -81,10 +81,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: AstNode *left_; diff --git a/ir/statements/forUpdateStatement.cpp b/ir/statements/forUpdateStatement.cpp index d297b0a52..6cb1f219d 100644 --- a/ir/statements/forUpdateStatement.cpp +++ b/ir/statements/forUpdateStatement.cpp @@ -15,16 +15,9 @@ #include "forUpdateStatement.h" -#include "binder/scope.h" -#include "compiler/base/condition.h" -#include "compiler/base/lreference.h" -#include "compiler/core/labelTarget.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" -#include "compiler/core/dynamicContext.h" #include "checker/TSchecker.h" -#include "ir/astDump.h" -#include "ir/expression.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void ForUpdateStatement::TransformChildren(const NodeTransformer &cb) @@ -68,115 +61,23 @@ void ForUpdateStatement::Dump(ir::AstDumper *dumper) const {"body", body_}}); } -void ForUpdateStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ForUpdateStatement::Compile(compiler::PandaGen *pg) const { - compiler::LocalRegScope decl_reg_scope(pg, Scope()->DeclScope()->InitScope()); - - if (init_ != nullptr) { - ASSERT(init_->IsVariableDeclaration() || init_->IsExpression()); - init_->Compile(pg); - } - - auto *start_label = pg->AllocLabel(); - compiler::LabelTarget label_target(pg); - - compiler::LoopEnvScope decl_env_scope(pg, Scope()->DeclScope()); - compiler::LoopEnvScope env_scope(pg, label_target, Scope()); - pg->SetLabel(this, start_label); - - { - compiler::LocalRegScope reg_scope(pg, Scope()); - - if (test_ != nullptr) { - compiler::Condition::Compile(pg, test_, label_target.BreakTarget()); - } - - body_->Compile(pg); - pg->SetLabel(this, label_target.ContinueTarget()); - env_scope.CopyPetIterationCtx(); - } - - if (update_ != nullptr) { - update_->Compile(pg); - } - - pg->Branch(this, start_label); - pg->SetLabel(this, label_target.BreakTarget()); + pg->GetAstCompiler()->Compile(this); } -void ForUpdateStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void ForUpdateStatement::Compile(compiler::ETSGen *etsg) const { - compiler::LocalRegScope decl_reg_scope(etsg, Scope()->DeclScope()->InitScope()); - - if (init_ != nullptr) { - ASSERT(init_->IsVariableDeclaration() || init_->IsExpression()); - init_->Compile(etsg); - } - - auto *start_label = etsg->AllocLabel(); - compiler::LabelTarget label_target(etsg); - auto label_ctx = compiler::LabelContext(etsg, label_target); - etsg->SetLabel(this, start_label); - - { - compiler::LocalRegScope reg_scope(etsg, Scope()); - - if (test_ != nullptr) { - compiler::Condition::Compile(etsg, test_, label_target.BreakTarget()); - } - - body_->Compile(etsg); - etsg->SetLabel(this, label_target.ContinueTarget()); - } - - if (update_ != nullptr) { - update_->Compile(etsg); - } - - etsg->Branch(this, start_label); - etsg->SetLabel(this, label_target.BreakTarget()); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ForUpdateStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *ForUpdateStatement::Check(checker::TSChecker *checker) { - checker::ScopeContext scope_ctx(checker, Scope()); - - if (init_ != nullptr) { - init_->Check(checker); - } - - if (test_ != nullptr) { - checker::Type *test_type = test_->Check(checker); - checker->CheckTruthinessOfType(test_type, Start()); - } - - if (update_ != nullptr) { - update_->Check(checker); - } - - body_->Check(checker); - - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *ForUpdateStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ForUpdateStatement::Check(checker::ETSChecker *checker) { - checker::ScopeContext scope_ctx(checker, Scope()); - - if (init_ != nullptr) { - init_->Check(checker); - } - - if (test_ != nullptr) { - checker->CheckTruthinessOfType(test_); - } - - if (update_ != nullptr) { - update_->Check(checker); - } - - body_->Check(checker); - - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/forUpdateStatement.h b/ir/statements/forUpdateStatement.h index d6a1c001a..1e213ff63 100644 --- a/ir/statements/forUpdateStatement.h +++ b/ir/statements/forUpdateStatement.h @@ -87,10 +87,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: AstNode *init_; diff --git a/ir/statements/functionDeclaration.cpp b/ir/statements/functionDeclaration.cpp index 5eb8b2749..1624bc1e2 100644 --- a/ir/statements/functionDeclaration.cpp +++ b/ir/statements/functionDeclaration.cpp @@ -15,18 +15,9 @@ #include "functionDeclaration.h" -#include "binder/variable.h" -#include "binder/scope.h" -#include "compiler/core/ETSGen.h" #include "checker/TSchecker.h" -#include "checker/ETSchecker.h" -#include "checker/types/ets/etsFunctionType.h" -#include "ir/astDump.h" -#include "ir/typeNode.h" -#include "ir/base/spreadElement.h" -#include "ir/base/decorator.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/identifier.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void FunctionDeclaration::TransformChildren(const NodeTransformer &cb) @@ -54,37 +45,23 @@ void FunctionDeclaration::Dump(ir::AstDumper *dumper) const {"function", func_}}); } -void FunctionDeclaration::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} - -void FunctionDeclaration::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void FunctionDeclaration::Compile(compiler::PandaGen *pg) const { - UNREACHABLE(); + pg->GetAstCompiler()->Compile(this); } -checker::Type *FunctionDeclaration::Check([[maybe_unused]] checker::TSChecker *checker) +void FunctionDeclaration::Compile(compiler::ETSGen *etsg) const { - if (func_->IsOverload()) { - return nullptr; - } - - const util::StringView &func_name = func_->Id()->Name(); - auto result = checker->Scope()->Find(func_name); - ASSERT(result.variable); - - checker::ScopeContext scope_ctx(checker, func_->Scope()); - - if (result.variable->TsType() == nullptr) { - checker->InferFunctionDeclarationType(result.variable->Declaration()->AsFunctionDecl(), result.variable); - } - - func_->Body()->Check(checker); + etsg->GetAstCompiler()->Compile(this); +} - return nullptr; +checker::Type *FunctionDeclaration::Check(checker::TSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); } -checker::Type *FunctionDeclaration::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *FunctionDeclaration::Check(checker::ETSChecker *checker) { - UNREACHABLE(); - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/functionDeclaration.h b/ir/statements/functionDeclaration.h index 0c00928b7..26bed64a5 100644 --- a/ir/statements/functionDeclaration.h +++ b/ir/statements/functionDeclaration.h @@ -51,10 +51,10 @@ public: void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ArenaVector decorators_; diff --git a/ir/statements/ifStatement.cpp b/ir/statements/ifStatement.cpp index b931fe18b..db7fe9795 100644 --- a/ir/statements/ifStatement.cpp +++ b/ir/statements/ifStatement.cpp @@ -15,12 +15,9 @@ #include "ifStatement.h" -#include "compiler/base/condition.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" #include "checker/TSchecker.h" -#include "ir/astDump.h" -#include "ir/expression.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void IfStatement::TransformChildren(const NodeTransformer &cb) @@ -51,84 +48,23 @@ void IfStatement::Dump(ir::AstDumper *dumper) const {"alternate", AstDumper::Nullable(alternate_)}}); } -void IfStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void IfStatement::Compile(compiler::PandaGen *pg) const { - auto *consequent_end = pg->AllocLabel(); - compiler::Label *statement_end = consequent_end; - - compiler::Condition::Compile(pg, test_, consequent_end); - consequent_->Compile(pg); - - if (alternate_ != nullptr) { - statement_end = pg->AllocLabel(); - pg->Branch(pg->Insns().back()->Node(), statement_end); - - pg->SetLabel(this, consequent_end); - alternate_->Compile(pg); - } - - pg->SetLabel(this, statement_end); + pg->GetAstCompiler()->Compile(this); } -void IfStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void IfStatement::Compile(compiler::ETSGen *etsg) const { - auto res = compiler::Condition::CheckConstantExpr(test_); - - if (res == compiler::Condition::Result::CONST_TRUE) { - consequent_->Compile(etsg); - return; - } - - if (res == compiler::Condition::Result::CONST_FALSE) { - if (alternate_ != nullptr) { - alternate_->Compile(etsg); - } - return; - } - - auto *consequent_end = etsg->AllocLabel(); - compiler::Label *statement_end = consequent_end; - - compiler::Condition::Compile(etsg, test_, consequent_end); - - consequent_->Compile(etsg); - - if (alternate_ != nullptr) { - statement_end = etsg->AllocLabel(); - etsg->Branch(etsg->Insns().back()->Node(), statement_end); - - etsg->SetLabel(this, consequent_end); - alternate_->Compile(etsg); - } - - etsg->SetLabel(this, statement_end); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *IfStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *IfStatement::Check(checker::TSChecker *checker) { - checker::Type *test_type = test_->Check(checker); - checker->CheckTruthinessOfType(test_type, Start()); - checker->CheckTestingKnownTruthyCallableOrAwaitableType(test_, test_type, consequent_); - - consequent_->Check(checker); - - if (alternate_ != nullptr) { - alternate_->Check(checker); - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *IfStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *IfStatement::Check(checker::ETSChecker *checker) { - checker->CheckTruthinessOfType(test_); - - consequent_->Check(checker); - - if (alternate_ != nullptr) { - alternate_->Check(checker); - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/ifStatement.h b/ir/statements/ifStatement.h index b2596d77b..b0fa50be8 100644 --- a/ir/statements/ifStatement.h +++ b/ir/statements/ifStatement.h @@ -18,6 +18,11 @@ #include "ir/statement.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class Expression; @@ -28,6 +33,10 @@ public: { } + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + friend class checker::TSAnalyzer; + const Expression *Test() const { return test_; @@ -56,10 +65,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *test_; diff --git a/ir/statements/labelledStatement.cpp b/ir/statements/labelledStatement.cpp index be77a04fd..c43d30ebf 100644 --- a/ir/statements/labelledStatement.cpp +++ b/ir/statements/labelledStatement.cpp @@ -15,11 +15,9 @@ #include "labelledStatement.h" -#include "compiler/core/pandagen.h" +#include "checker/TSchecker.h" #include "compiler/core/ETSGen.h" -#include "compiler/core/labelTarget.h" -#include "ir/astDump.h" -#include "ir/expressions/identifier.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void LabelledStatement::TransformChildren(const NodeTransformer &cb) @@ -39,13 +37,6 @@ void LabelledStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "LabelledStatement"}, {"label", ident_}, {"body", body_}}); } -template -void CompileImpl(const LabelledStatement *self, CodeGen *cg) -{ - compiler::LabelContext label_ctx(cg, self); - self->Body()->Compile(cg); -} - const ir::AstNode *LabelledStatement::GetReferencedStatement() const { const auto *iter = body_; @@ -67,24 +58,23 @@ const ir::AstNode *LabelledStatement::GetReferencedStatement() const } } -void LabelledStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void LabelledStatement::Compile(compiler::PandaGen *pg) const { - CompileImpl(this, pg); + pg->GetAstCompiler()->Compile(this); } -void LabelledStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void LabelledStatement::Compile(compiler::ETSGen *etsg) const { - CompileImpl(this, etsg); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *LabelledStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *LabelledStatement::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *LabelledStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *LabelledStatement::Check(checker::ETSChecker *checker) { - body_->Check(checker); - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/labelledStatement.h b/ir/statements/labelledStatement.h index 7a5779bd3..a0a2b52e1 100644 --- a/ir/statements/labelledStatement.h +++ b/ir/statements/labelledStatement.h @@ -19,6 +19,10 @@ #include "ir/statement.h" #include "util/ustring.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class Identifier; @@ -29,6 +33,9 @@ public: { } + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + const Statement *Body() const { return body_; @@ -51,10 +58,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Identifier *ident_; diff --git a/ir/statements/switchCaseStatement.cpp b/ir/statements/switchCaseStatement.cpp index d5135069d..6a73407e9 100644 --- a/ir/statements/switchCaseStatement.cpp +++ b/ir/statements/switchCaseStatement.cpp @@ -15,8 +15,9 @@ #include "switchCaseStatement.h" -#include "ir/astDump.h" -#include "ir/expression.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void SwitchCaseStatement::TransformChildren(const NodeTransformer &cb) @@ -46,15 +47,23 @@ void SwitchCaseStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "SwitchCase"}, {"test", AstDumper::Nullable(test_)}, {"consequent", consequent_}}); } -void SwitchCaseStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void SwitchCaseStatement::Compile(compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} + +void SwitchCaseStatement::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} -checker::Type *SwitchCaseStatement::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *SwitchCaseStatement::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } -checker::Type *SwitchCaseStatement::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *SwitchCaseStatement::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/statements/switchCaseStatement.h b/ir/statements/switchCaseStatement.h index 0ed32b85f..886520a6f 100644 --- a/ir/statements/switchCaseStatement.h +++ b/ir/statements/switchCaseStatement.h @@ -55,9 +55,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *test_; diff --git a/ir/statements/switchStatement.cpp b/ir/statements/switchStatement.cpp index 88b9b7151..e9c85161d 100644 --- a/ir/statements/switchStatement.cpp +++ b/ir/statements/switchStatement.cpp @@ -15,18 +15,9 @@ #include "switchStatement.h" -#include "binder/scope.h" -#include "compiler/core/labelTarget.h" -#include "compiler/core/switchBuilder.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" #include "checker/TSchecker.h" -#include "checker/ets/typeRelationContext.h" -#include "ir/astDump.h" -#include "ir/expression.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/memberExpression.h" -#include "ir/statements/switchCaseStatement.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" namespace panda::es2panda::ir { void SwitchStatement::TransformChildren(const NodeTransformer &cb) @@ -52,132 +43,24 @@ void SwitchStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "SwitchStatement"}, {"discriminant", discriminant_}, {"cases", cases_}}); } -template -void CompileImpl(const SwitchStatement *self, CodeGen *cg) +void SwitchStatement::Compile(compiler::PandaGen *pg) const { - compiler::LocalRegScope lrs(cg, self->Scope()); - compiler::SwitchBuilder builder(cg, self); - compiler::VReg tag = cg->AllocReg(); - - builder.CompileTagOfSwitch(tag); - uint32_t default_index = 0; - - for (size_t i = 0; i < self->Cases().size(); i++) { - const auto *clause = self->Cases()[i]; - - if (clause->Test() == nullptr) { - default_index = i; - continue; - } - - builder.JumpIfCase(tag, i); - } - - if (default_index > 0) { - builder.JumpToDefault(default_index); - } else { - builder.Break(); - } - - for (size_t i = 0; i < self->Cases().size(); i++) { - builder.SetCaseTarget(i); - builder.CompileCaseStatements(i); - } + pg->GetAstCompiler()->Compile(this); } -void SwitchStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void SwitchStatement::Compile(compiler::ETSGen *etsg) const { - CompileImpl(this, pg); + etsg->GetAstCompiler()->Compile(this); } -void SwitchStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +checker::Type *SwitchStatement::Check(checker::TSChecker *checker) { - CompileImpl(this, etsg); -} - -checker::Type *SwitchStatement::Check([[maybe_unused]] checker::TSChecker *checker) -{ - checker::ScopeContext scope_ctx(checker, scope_); - - checker::Type *expr_type = discriminant_->Check(checker); - bool expr_is_literal = checker::TSChecker::IsLiteralType(expr_type); - - for (auto *it : cases_) { - if (it->Test() != nullptr) { - checker::Type *case_type = it->Test()->Check(checker); - bool case_is_literal = checker::TSChecker::IsLiteralType(case_type); - checker::Type *compared_expr_type = expr_type; - - if (!case_is_literal || !expr_is_literal) { - case_type = case_is_literal ? checker->GetBaseTypeOfLiteralType(case_type) : case_type; - compared_expr_type = checker->GetBaseTypeOfLiteralType(expr_type); - } - - if (!checker->IsTypeEqualityComparableTo(compared_expr_type, case_type) && - !checker->IsTypeComparableTo(case_type, compared_expr_type)) { - checker->ThrowTypeError({"Type ", case_type, " is not comparable to type ", compared_expr_type}, - it->Test()->Start()); - } - } - - for (auto *case_stmt : it->Consequent()) { - case_stmt->Check(checker); - } - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *SwitchStatement::Check(checker::ETSChecker *const checker) { - checker::ScopeContext scope_ctx(checker, scope_); - discriminant_->Check(checker); - checker::SavedTypeRelationFlagsContext saved_type_relation_flag_ctx(checker->Relation(), - checker::TypeRelationFlag::NONE); - // TODO(user): check exhaustive Switch - checker->CheckSwitchDiscriminant(discriminant_); - auto *compared_expr_type = discriminant_->TsType(); - auto unboxed_disc_type = (Discriminant()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U - ? checker->ETSBuiltinTypeAsPrimitiveType(compared_expr_type) - : compared_expr_type; - - bool valid_case_type; - - for (auto *it : cases_) { - if (it->Test() != nullptr) { - auto *case_type = it->Test()->Check(checker); - valid_case_type = true; - if (case_type->HasTypeFlag(checker::TypeFlag::CHAR)) { - valid_case_type = compared_expr_type->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL); - } else if (case_type->IsETSEnumType() && discriminant_->TsType()->IsETSEnumType()) { - valid_case_type = discriminant_->TsType()->AsETSEnumType()->IsSameEnumType(case_type->AsETSEnumType()); - } else if (case_type->IsETSStringEnumType() && discriminant_->TsType()->IsETSStringEnumType()) { - valid_case_type = - discriminant_->TsType()->AsETSStringEnumType()->IsSameEnumType(case_type->AsETSStringEnumType()); - } else { - checker::AssignmentContext( - checker->Relation(), discriminant_, case_type, unboxed_disc_type, it->Test()->Start(), - {"Switch case type ", case_type, " is not comparable to discriminant type ", compared_expr_type}, - (compared_expr_type->IsETSObjectType() ? checker::TypeRelationFlag::NO_WIDENING - : checker::TypeRelationFlag::NO_UNBOXING) | - checker::TypeRelationFlag::NO_BOXING); - } - - if (!valid_case_type) { - checker->ThrowTypeError( - {"Switch case type ", case_type, " is not comparable to discriminant type ", compared_expr_type}, - it->Test()->Start()); - } - } - - for (auto *case_stmt : it->Consequent()) { - case_stmt->Check(checker); - } - } - - checker->CheckForSameSwitchCases(&cases_); - - return nullptr; + return checker->GetAnalyzer()->Check(this); } void SwitchStatement::SetReturnType(checker::ETSChecker *checker, checker::Type *type) diff --git a/ir/statements/switchStatement.h b/ir/statements/switchStatement.h index 847f426da..d574f6ec5 100644 --- a/ir/statements/switchStatement.h +++ b/ir/statements/switchStatement.h @@ -19,6 +19,11 @@ #include "binder/scope.h" #include "ir/statement.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class Expression; class SwitchCaseStatement; @@ -31,6 +36,10 @@ public: { } + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + friend class checker::TSAnalyzer; + const Expression *Discriminant() const { return discriminant_; @@ -56,10 +65,10 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; - void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: binder::LocalScope *scope_; -- Gitee