diff --git a/BUILD.gn b/BUILD.gn index 99b4a26efe5d858895b44d50fc60166ab698188e..8663df87208714a800ee7d95c44ad9ca0cf1726b 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 e49880048b39a7862c1959de208a79d92c2719a7..b34882d04858cdb74b422b54727fbbe973c8c977 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/checker/ETSAnalyzer.cpp b/checker/ETSAnalyzer.cpp index 2e05e528f30d1b428c49aef664379507c47a488b..3d8cabf7b22e33ab852e74793e168c329fedaab2 100644 --- a/checker/ETSAnalyzer.cpp +++ b/checker/ETSAnalyzer.cpp @@ -16,15 +16,9 @@ #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" -#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 { @@ -233,37 +227,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 +287,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 @@ -555,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 @@ -800,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/SemanticAnalyzer.h b/checker/SemanticAnalyzer.h index 72cfe3735de6e7438ae231ba39f65cf98146e39e..8cc27451f53332c643d0c2cbf5c7139e84b51f76 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 f5bfa79b2f493c500b2a5eb57e720362351182ea..bb235a866efb952249de8c034f33922f4be04934 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 @@ -515,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(); } @@ -594,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/ASTCompiler.h b/compiler/core/ASTCompiler.h index b5bd6661794f22acfc3e000542ef9b1282941eda..03fdde033adf00ef433610758d7e7dbcbdccb6c0 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/ASTVerifier.cpp b/compiler/core/ASTVerifier.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f59438c301e16963727eee4a563f37305bd0c933 --- /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 0000000000000000000000000000000000000000..d9bdba1180982ab20a5050b1b263dfe8f9db2cda --- /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/core/ETSCompiler.cpp b/compiler/core/ETSCompiler.cpp index c7f91c0d7962a2e2b8797f073f71b64e0e9c642f..ad28f26481d4ff690053b8bdfe81d64fc295b8f6 100644 --- a/compiler/core/ETSCompiler.cpp +++ b/compiler/core/ETSCompiler.cpp @@ -15,13 +15,12 @@ #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/core/switchBuilder.h" +#include "compiler/function/functionBuilder.h" namespace panda::es2panda::compiler { @@ -198,38 +197,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 +237,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 @@ -520,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 @@ -613,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 a0b4815892d893a6432ee139e5fcea744c7a4b0c..e4b6d32fc37f8b47b44cfb5377d2fa29aabfe8e5 100644 --- a/compiler/core/JSCompiler.cpp +++ b/compiler/core/JSCompiler.cpp @@ -15,18 +15,11 @@ #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/core/switchBuilder.h" +#include "compiler/function/functionBuilder.h" #include "util/helpers.h" namespace panda::es2panda::compiler { @@ -523,39 +516,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 +555,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 @@ -848,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 @@ -922,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/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 5877c6e4907ef23c263a0290514724a6c3c3becb..0b7c300573d1bcf2bf72fea9e6ec70e3d668a095 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 b9558663a3a25fd3e987cc93a64866b1fa0e4b12..5accd1e728953732abeb88275b0252004feebff8 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/ir/ets/etsParameterExpression.cpp b/ir/ets/etsParameterExpression.cpp index c862ffe69700f0e0eb8df259431497a42cf888d3..30fee78aa4f556aff9461fd5fa689f1cc5183758 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 d63a3dd887cd921d18cacf3f9e98f1a327a0fdb3..1160289e8857c7061ed70be9d20f93637d05f9b3 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 820b6fb5448dc8d09b2a45b200d35fd229f549cb..7e722d78c6c25cb8134f8a32d09d8adbfe49b296 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 c8ae085f4585c41133ece704a672f1730826cc76..c394b0019617a7cee0117fe9a666514feda59b01 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 62f8b74cdf5b9d4c5b577779dc33efa932f0d523..3b05f2717a58e9f06fd49b95e29ce09bdb8838d4 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 7a52e9b804388b7722ac9993ddced626184610ec..10a2201ab66054c5439654f7c01363b52520d218 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 849f5234630df198d5d699fb7da1081ed8f802a4..7ac21bd519609eefdad4dd3b0d8ad0d0a1438b1f 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 17b8ef6ce1c30595a325a66579d06005fa28314c..d771dcceb0b8cdfb80668aa6d9861e7ec4d51f04 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 4d86e09056694abe5f068f41ab938457160d3c9e..79a2b4e97a74dcaa63f37e1151bd8c658e2331cd 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 f8d82bcf2934f5df7b119ff7dd3952b6b5eca2a2..f3e4b8aafa150d7615ff64c1cbaf4c5b84146efd 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 07db87b9c6c5e99b1846269df3f178c6b0c3ee33..76c5ea1cc3fa4cc74d2e5b7de0644e290332dc51 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 4ce8ce2adbed61728aa477c22a1d7fdec22f3a5c..ca14342e0d1e44b64cab6164cb81721b8ba5bfbf 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 a7ceaf07c98cedf1e0434fd224eafc22f089b02c..0cb1b3b65fda36d2382bd984c471d1ff3110e718 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 5bcefdb81bafc68f1657f7ef856caeb730c6276c..204ce84ac5bb4e47207b3e8e6ed0344e793d852b 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/emptyStatement.cpp b/ir/statements/emptyStatement.cpp index 5da09fdd2284bad2b67f115767bb55cd0e163517..4c00f2a89ee6f42aea577b5c9121229994a54b11 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 44c82b2fba26420907ff6388cb687a08da889b22..a815449fbadf23621f73dfd5ba1ba21070799c57 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 61ebaca008147e8c817413c5420f97726aaa5de2..d4750832be8cdad6688db5c3f34f4f80185336df 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 2f80d87221946a812a42877339724d51be7cad30..56ea20363273428ddb7a53d833ecd72818830d1f 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 2a3a56cce8a51b3a37eee70f2d705d0b77184cf0..1bc536de527ea437c84b73053c4a6e63912307b3 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 c8e99fb4fb670336cffa9225866a076bbfdd9f1f..9304d72644cad0f7c6e523fe1e03a79addeeaab3 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 0e8fedbbc02c44b26f974ec98bbf08d431a4bd94..a1bd259fab26711648dd01ee0e949904e84935cb 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 9bb2ecf8d51dd132908988e1d8230a0b3c7d32ab..ec35b6456954a4c5944bd61fb578662dceb64624 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 d297b0a52fbcc177e3e6302bccb81c3f16c598a6..6cb1f219dfb41302625526bb3f507b5a5ace5c2f 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 d6a1c001a7311e6754a6d13401c44fdc0d563b33..1e213ff63ff120852826a51267ec4f68fdafba70 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 5eb8b27490ede291508d662466151fe01679f93d..1624bc1e2155751914a3fd7c8bc31ab40e05af91 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 0c00928b77c77239586cc8f93347ddc1cd002ad2..26bed64a5df7b0c8b4a5d95c192dfe669bbd3c75 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 b931fe18b8e94cd6a346990c5a22145b11176b11..db7fe97958d4b87a8d4d422ec6ade462e7b02c2d 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 b2596d77bc4043995a8fca37e3c52555433dac39..b0fa50be838b3d2a064a0e569cfc2ec63df6f581 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 be77a04fdcb42c826352d2467bebf1c37e54b8bc..c43d30ebf809b61b750156899434574fa474ec94 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 7a5779bd3febc3c7c20a37847e7b76a2f3191d3b..a0a2b52e110d553499b35b3b4237f955866480bd 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 d5135069d4ecf8d229075d70e0095d2c6c4de3c2..6a73407e9e05c4fe112ed49ef3a58bc45fcdac0f 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 0ed32b85facd23ca10301372f9e6c2616dd9e74e..886520a6f719792a1a9835481d2750439f97e1ee 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 88b9b7151ca911fb29284b2b500c3481ce954a3d..e9c85161dd1822b32cd8f91766e52e6efeee3e70 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 847f426da372597214fb733e03ddabd5a05c4bac..d574f6ec593712732dec709d48e0cc281f6b872f 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_; diff --git a/ir/statements/tryStatement.h b/ir/statements/tryStatement.h index 74b955c87aca3f798585faf0c83457db28d74159..af69c1eb9d91f8f33a9ee79f4244c548b90e706b 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 { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f40aed24be528f99fc880954a9bb04380f30fce6..ff14df7c4ae66ff22c3618052179032ef05d69e4 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 0000000000000000000000000000000000000000..dd36a1937988cfe8ea1ae3622f6bfae857a57f81 --- /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); +}