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..5f47162218b298ebcd934847681e334c27cda513 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 @@ -287,49 +361,271 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + checker::Type *arg_type = expr->argument_->Check(checker); + // Check the argument type of await expression + if (!arg_type->IsETSObjectType() || + (arg_type->AsETSObjectType()->AssemblerName() != compiler::Signatures::BUILTIN_PROMISE)) { + checker->ThrowTypeError("'await' expressions require Promise object as argument.", expr->Argument()->Start()); + } + + expr->SetTsType(arg_type->AsETSObjectType()->TypeArguments().at(0)); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + checker::Type *new_ts_type {nullptr}; + std::tie(new_ts_type, expr->operation_type_) = + checker->CheckBinaryOperator(expr->Left(), expr->Right(), expr->OperatorType(), expr->Start()); + expr->SetTsType(new_ts_type); + return expr->TsType(); +} + +static checker::Type *InitAnonymousLambdaCallee(checker::ETSChecker *checker, ir::Expression *callee, + checker::Type *callee_type) +{ + auto *const arrow_func = callee->AsArrowFunctionExpression()->Function(); + auto orig_params = arrow_func->Params(); + auto *func_type = checker->Allocator()->New( + arrow_func->Scope()->AsFunctionScope()->ParamScope(), std::move(orig_params), nullptr, + arrow_func->ReturnTypeAnnotation(), ir::ScriptFunctionFlags::NONE); + auto *const func_iface = func_type->Check(checker); + checker->Relation()->SetNode(callee); + checker->Relation()->IsAssignableTo(callee_type, func_iface); + return func_iface; +} + +static checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *function_type, + checker::ETSChecker *checker, ir::CallExpression *expr) +{ + auto *member_expr = expr->Callee()->AsMemberExpression(); + expr->Arguments().insert(expr->Arguments().begin(), member_expr->Object()); + auto *signature = + checker->ResolveCallExpressionAndTrailingLambda(function_type->CallSignatures(), expr, expr->Start()); + if (!signature->Function()->IsExtensionMethod()) { + checker->ThrowTypeError({"Property '", member_expr->Property()->AsIdentifier()->Name(), + "' does not exist on type '", member_expr->ObjType()->Name(), "'"}, + member_expr->Property()->Start()); + } + expr->SetSignature(signature); + expr->SetCallee(member_expr->Property()); + member_expr->Property()->AsIdentifier()->SetParent(expr); + expr->Arguments()[0]->SetParent(expr); + checker->HandleUpdatedCallExpressionNode(expr); + // Set TsType for new Callee(original member expression's Object) + expr->Callee()->Check(checker); + return signature; +} + +static checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type, + checker::ETSChecker *checker, + ir::CallExpression *expr) +{ + checker::Signature *signature = checker->ResolveCallExpressionAndTrailingLambda( + type->ClassMethodType()->CallSignatures(), expr, expr->Start(), checker::TypeRelationFlag::NO_THROW); + + if (signature != nullptr) { + return signature; + } + + return ResolveCallExtensionFunction(type->ExtensionMethodType(), checker, expr); } checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + auto *old_callee = expr->Callee(); + checker::Type *callee_type = expr->Callee()->Check(checker); + if (expr->Callee() != old_callee) { + // If it is a static invoke, the callee will be transformed from an identifier to a member expression + // Type check the callee again for member expression + callee_type = expr->Callee()->Check(checker); + } + + checker::Type *return_type; + if (callee_type->IsETSDynamicType() && !callee_type->AsETSDynamicType()->HasDecl()) { + // Trailing lambda for js function call is not supported, check the correctness of `foo() {}` + checker->EnsureValidCurlyBrace(expr); + auto lang = callee_type->AsETSDynamicType()->Language(); + expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), expr->Arguments(), lang, false)); + return_type = expr->Signature()->ReturnType(); + } else { + bool constructor_call = expr->IsETSConstructorCall(); + bool functional_interface = + callee_type->IsETSObjectType() && + callee_type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE); + bool ets_extension_func_helper_type = callee_type->IsETSExtensionFuncHelperType(); + bool extension_function_type = + expr->Callee()->IsMemberExpression() && checker->ExtensionETSFunctionType(callee_type); + + if (expr->Callee()->IsArrowFunctionExpression()) { + callee_type = InitAnonymousLambdaCallee(checker, expr->Callee(), callee_type); + functional_interface = true; + } + + if (!functional_interface && !callee_type->IsETSFunctionType() && !constructor_call && + !ets_extension_func_helper_type) { + checker->ThrowTypeError("This expression is not callable.", expr->Start()); + } + + checker::Signature *signature = nullptr; + + if (ets_extension_func_helper_type) { + signature = + ResolveCallForETSExtensionFuncHelperType(callee_type->AsETSExtensionFuncHelperType(), checker, expr); + } else { + if (extension_function_type) { + signature = ResolveCallExtensionFunction(callee_type->AsETSFunctionType(), checker, expr); + } else { + auto &signatures = constructor_call ? callee_type->AsETSObjectType()->ConstructSignatures() + : functional_interface + ? callee_type->AsETSObjectType() + ->GetOwnProperty("invoke") + ->TsType() + ->AsETSFunctionType() + ->CallSignatures() + : callee_type->AsETSFunctionType()->CallSignatures(); + signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start()); + if (signature->Function()->IsExtensionMethod()) { + checker->ThrowTypeError({"No matching call signature"}, expr->Start()); + } + } + } + + checker->CheckObjectLiteralArguments(signature, expr->Arguments()); + + checker->AddNullParamsForDefaultParams(signature, expr->Arguments(), checker); + + if (!functional_interface) { + checker::ETSObjectType *callee_obj {}; + if (constructor_call) { + callee_obj = callee_type->AsETSObjectType(); + } else if (expr->Callee()->IsIdentifier()) { + callee_obj = checker->Context().ContainingClass(); + } else { + ASSERT(expr->Callee()->IsMemberExpression()); + callee_obj = expr->Callee()->AsMemberExpression()->ObjType(); + } + + checker->ValidateSignatureAccessibility(callee_obj, signature, expr->Start()); + } + + ASSERT(signature->Function() != nullptr); + + if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) { + checker->CheckThrowingStatements(expr); + } + + if (signature->Function()->IsDynamic()) { + ASSERT(signature->Function()->IsDynamic()); + auto lang = signature->Function()->Language(); + expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), signature->Params(), lang, false)); + } else { + ASSERT(!signature->Function()->IsDynamic()); + expr->SetSignature(signature); + } + + return_type = signature->ReturnType(); + } + + if (expr->Signature()->RestVar() != nullptr) { + auto *const element_type = expr->Signature()->RestVar()->TsType()->AsETSArrayType()->ElementType(); + auto *const array_type = checker->CreateETSArrayType(element_type)->AsETSArrayType(); + checker->CreateBuiltinArraySignature(array_type, array_type->Rank()); + } + + expr->SetTsType(return_type); + return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::ChainExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ChainExpression *expr) const { - (void)expr; UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::ClassExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ClassExpression *expr) const { - (void)expr; UNREACHABLE(); } checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + checker->CheckTruthinessOfType(expr->Test()); + + checker::Type *consequent_type = expr->consequent_->Check(checker); + checker::Type *alternate_type = expr->alternate_->Check(checker); + + auto *primitive_consequent_type = checker->ETSBuiltinTypeAsPrimitiveType(consequent_type); + auto *primitive_alter_type = checker->ETSBuiltinTypeAsPrimitiveType(alternate_type); + + if (primitive_consequent_type != nullptr && primitive_alter_type != nullptr) { + if (checker->IsTypeIdenticalTo(consequent_type, alternate_type)) { + expr->SetTsType(checker->GetNonConstantTypeFromPrimitiveType(consequent_type)); + } else if (checker->IsTypeIdenticalTo(primitive_consequent_type, primitive_alter_type)) { + checker->FlagExpressionWithUnboxing(expr->consequent_->TsType(), primitive_consequent_type, + expr->consequent_); + checker->FlagExpressionWithUnboxing(expr->alternate_->TsType(), primitive_alter_type, expr->alternate_); + + expr->SetTsType(primitive_consequent_type); + } else if (primitive_consequent_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC) && + primitive_alter_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + checker->FlagExpressionWithUnboxing(expr->consequent_->TsType(), primitive_consequent_type, + expr->consequent_); + checker->FlagExpressionWithUnboxing(expr->alternate_->TsType(), primitive_alter_type, expr->alternate_); + + expr->SetTsType( + checker->ApplyConditionalOperatorPromotion(checker, primitive_consequent_type, primitive_alter_type)); + } else { + checker->ThrowTypeError("Type error", expr->Range().start); + } + } else { + if (!(consequent_type->IsETSArrayType() || alternate_type->IsETSArrayType()) && + !(consequent_type->IsETSObjectType() && alternate_type->IsETSObjectType())) { + checker->ThrowTypeError("Type error", expr->Range().start); + } else { + checker->Relation()->SetNode(expr->consequent_); + auto builtin_conseq_type = checker->PrimitiveTypeAsETSBuiltinType(consequent_type); + auto builtin_alternate_type = checker->PrimitiveTypeAsETSBuiltinType(alternate_type); + + if (builtin_conseq_type == nullptr) { + builtin_conseq_type = consequent_type; + } + + if (builtin_alternate_type == nullptr) { + builtin_alternate_type = alternate_type; + } + + expr->SetTsType(checker->FindLeastUpperBound(builtin_conseq_type, builtin_alternate_type)); + } + } + + return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::DirectEvalExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::DirectEvalExpression *expr) const { - (void)expr; UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::FunctionExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::FunctionExpression *expr) const { - (void)expr; UNREACHABLE(); } 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..ae0c996df199652ec273a0b019f99b815d5eb78f 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 @@ -245,52 +261,157 @@ checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::AwaitExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::AwaitExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // TODO(aszilagyi) + return checker->GlobalAnyType(); } checker::Type *TSAnalyzer::Check(ir::BinaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + auto *left_type = expr->Left()->Check(checker); + auto *right_type = expr->Right()->Check(checker); + + switch (expr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: { + return checker->CheckBinaryOperator(left_type, right_type, expr->Left(), expr->Right(), expr, + expr->OperatorType()); + } + case lexer::TokenType::PUNCTUATOR_PLUS: { + return checker->CheckPlusOperator(left_type, right_type, expr->Left(), expr->Right(), expr, + expr->OperatorType()); + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + return checker->CheckCompareOperator(left_type, right_type, expr->Left(), expr->Right(), expr, + expr->OperatorType()); + } + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: + case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { + if (checker->IsTypeEqualityComparableTo(left_type, right_type) || + checker->IsTypeEqualityComparableTo(right_type, left_type)) { + return checker->GlobalBooleanType(); + } + + checker->ThrowBinaryLikeError(expr->OperatorType(), left_type, right_type, expr->Start()); + } + case lexer::TokenType::KEYW_INSTANCEOF: { + return checker->CheckInstanceofExpression(left_type, right_type, expr->Right(), expr); + } + case lexer::TokenType::KEYW_IN: { + return checker->CheckInExpression(left_type, right_type, expr->Left(), expr->Right(), expr); + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { + return checker->CheckAndOperator(left_type, right_type, expr->Left()); + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + return checker->CheckOrOperator(left_type, right_type, expr->Left()); + } + case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: { + // TODO(Csaba Repasi): Implement checker for nullish coalescing + return checker->GlobalAnyType(); + } + case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { + checker->CheckAssignmentOperator(expr->OperatorType(), expr->Left(), left_type, right_type); + return right_type; + } + default: { + UNREACHABLE(); + break; + } + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::CallExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *callee_type = expr->callee_->Check(checker); + + // TODO(aszilagyi): handle optional chain + if (callee_type->IsObjectType()) { + checker::ObjectType *callee_obj = callee_type->AsObjectType(); + return checker->ResolveCallOrNewExpression(callee_obj->CallSignatures(), expr->Arguments(), expr->Start()); + } + + checker->ThrowTypeError("This expression is not callable.", expr->Start()); + return nullptr; } checker::Type *TSAnalyzer::Check(ir::ChainExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + return expr->expression_->Check(checker); } -checker::Type *TSAnalyzer::Check(ir::ClassExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ClassExpression *expr) const { - (void)expr; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::ConditionalExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::Type *test_type = expr->Test()->Check(checker); + + checker->CheckTruthinessOfType(test_type, expr->Test()->Start()); + checker->CheckTestingKnownTruthyCallableOrAwaitableType(expr->Test(), test_type, expr->consequent_); + + checker::Type *consequent_type = expr->consequent_->Check(checker); + checker::Type *alternate_type = expr->alternate_->Check(checker); + + return checker->CreateUnionType({consequent_type, alternate_type}); } -checker::Type *TSAnalyzer::Check(ir::DirectEvalExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::DirectEvalExpression *expr) const { - (void)expr; UNREACHABLE(); } checker::Type *TSAnalyzer::Check(ir::FunctionExpression *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())); + + expr->Function()->Body()->Check(checker); + + return func_type; } checker::Type *TSAnalyzer::Check(ir::Identifier *expr) 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..966a128064f1791d12b7b68681905121c82323f2 100644 --- a/compiler/core/ETSCompiler.cpp +++ b/compiler/core/ETSCompiler.cpp @@ -15,13 +15,11 @@ #include "ETSCompiler.h" +#include "checker/types/ets/etsDynamicFunctionType.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classProperty.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" namespace panda::es2panda::compiler { @@ -198,38 +196,36 @@ void ETSCompiler::Compile(const ir::ETSPackageDeclaration *st) const void ETSCompiler::Compile(const ir::ETSParameterExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + expr->Ident()->Identifier::Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSPrimitiveType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *node) const { - (void)node; UNREACHABLE(); } -void ETSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } void ETSCompiler::Compile(const ir::ETSTypeReference *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Part()->Compile(etsg); } void ETSCompiler::Compile(const ir::ETSTypeReferencePart *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + node->Name()->Compile(etsg); } -void ETSCompiler::Compile(const ir::ETSWildcardType *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *node) const { - (void)node; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + etsg->Unimplemented(); } // compile methods for EXPRESSIONS in alphabetical order void ETSCompiler::Compile(const ir::ArrayExpression *expr) const @@ -240,8 +236,23 @@ void ETSCompiler::Compile(const ir::ArrayExpression *expr) const void ETSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + ASSERT(expr->ResolvedLambda() != nullptr); + auto *ctor = expr->ResolvedLambda()->TsType()->AsETSObjectType()->ConstructSignatures()[0]; + std::vector arguments; + + for (auto *it : expr->CapturedVars()) { + if (it->HasFlag(binder::VariableFlags::LOCAL)) { + arguments.push_back(it->AsLocalVariable()->Vreg()); + } + } + + if (expr->propagate_this_) { + arguments.push_back(etsg->GetThisReg()); + } + + etsg->InitLambdaObject(expr, ctor, arguments); + etsg->SetAccumulatorType(expr->resolved_lambda_->TsType()); } void ETSCompiler::Compile(const ir::AssignmentExpression *expr) const @@ -252,49 +263,299 @@ void ETSCompiler::Compile(const ir::AssignmentExpression *expr) const void ETSCompiler::Compile(const ir::AwaitExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + static constexpr bool IS_UNCHECKED_CAST = false; + compiler::RegScope rs(etsg); + compiler::VReg argument_reg = etsg->AllocReg(); + expr->Argument()->Compile(etsg); + etsg->StoreAccumulator(expr, argument_reg); + etsg->CallThisVirtual0(expr->Argument(), argument_reg, compiler::Signatures::BUILTIN_PROMISE_AWAIT_RESOLUTION); + etsg->CastToArrayOrObject(expr->Argument(), expr->TsType(), IS_UNCHECKED_CAST); + etsg->SetAccumulatorType(expr->TsType()); +} + +static void CompileLogical(compiler::ETSGen *etsg, const ir::BinaryExpression *expr) +{ + auto *end_label = etsg->AllocLabel(); + + expr->Left()->Compile(etsg); + etsg->ApplyConversion(expr->Left(), expr->OperationType()); + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { + etsg->BranchIfFalse(expr, end_label); + } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { + etsg->BranchIfTrue(expr, end_label); + } else { + ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING); + etsg->BranchIfNotNull(expr, end_label); + } + + expr->Right()->Compile(etsg); + etsg->ApplyConversion(expr->Right(), expr->OperationType()); + + etsg->SetLabel(expr, end_label); } void ETSCompiler::Compile(const ir::BinaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + if (etsg->TryLoadConstantExpression(expr)) { + return; + } + + auto ttctx = compiler::TargetTypeContext(etsg, expr->OperationType()); + + if (expr->IsLogical()) { + CompileLogical(etsg, expr); + etsg->ApplyConversion(expr, expr->OperationType()); + return; + } + + compiler::RegScope rs(etsg); + compiler::VReg lhs = etsg->AllocReg(); + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS && + (expr->Left()->TsType()->IsETSStringType() || expr->Right()->TsType()->IsETSStringType())) { + etsg->BuildString(expr); + return; + } + + expr->Left()->Compile(etsg); + etsg->ApplyConversionAndStoreAccumulator(expr->Left(), lhs, expr->OperationType()); + expr->Right()->Compile(etsg); + etsg->ApplyConversion(expr->Right(), expr->OperationType()); + if (expr->OperatorType() >= lexer::TokenType::PUNCTUATOR_LEFT_SHIFT && + expr->OperatorType() <= lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT) { + etsg->ApplyCast(expr->Right(), expr->OperationType()); + } + + etsg->Binary(expr, expr->OperatorType(), lhs); +} + +static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::CallExpression *expr) +{ + if (expr->Signature()->RestVar() != nullptr) { + std::size_t const argument_count = expr->Arguments().size(); + std::size_t const parameter_count = expr->Signature()->MinArgCount(); + ASSERT(argument_count >= parameter_count); + + auto &arguments = const_cast &>(expr->Arguments()); + std::size_t i = parameter_count; + + if (i < argument_count && expr->Arguments()[i]->IsSpreadElement()) { + arguments[i] = expr->Arguments()[i]->AsSpreadElement()->Argument(); + } else { + ArenaVector elements(checker->Allocator()->Adapter()); + for (; i < argument_count; ++i) { + elements.emplace_back(expr->Arguments()[i]); + } + auto *array_expression = checker->AllocNode(std::move(elements), checker->Allocator()); + array_expression->SetParent(const_cast(expr)); + array_expression->SetTsType(expr->Signature()->RestVar()->TsType()); + arguments.erase(expr->Arguments().begin() + parameter_count, expr->Arguments().end()); + arguments.emplace_back(array_expression); + } + } } void ETSCompiler::Compile(const ir::CallExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + compiler::RegScope rs(etsg); + compiler::VReg callee_reg = etsg->AllocReg(); + + const auto is_proxy = expr->Signature()->HasSignatureFlag(checker::SignatureFlags::PROXY); + + if (is_proxy && expr->Callee()->IsMemberExpression()) { + auto *const callee_object = expr->callee_->AsMemberExpression()->Object(); + + auto const *const enum_interface = [callee_type = + callee_object->TsType()]() -> checker::ETSEnumInterface const * { + if (callee_type->IsETSEnumType()) { + return callee_type->AsETSEnumType(); + } + if (callee_type->IsETSStringEnumType()) { + return callee_type->AsETSStringEnumType(); + } + return nullptr; + }(); + + if (enum_interface != nullptr) { + ArenaVector arguments(etsg->Allocator()->Adapter()); + + checker::Signature *const signature = [expr, callee_object, enum_interface, &arguments]() { + const auto &member_proxy_method_name = expr->signature_->InternalName(); + + if (member_proxy_method_name == checker::ETSEnumType::TO_STRING_METHOD_NAME) { + arguments.push_back(callee_object); + return enum_interface->ToStringMethod().global_signature; + } + if (member_proxy_method_name == checker::ETSEnumType::GET_VALUE_METHOD_NAME) { + arguments.push_back(callee_object); + return enum_interface->GetValueMethod().global_signature; + } + if (member_proxy_method_name == checker::ETSEnumType::GET_NAME_METHOD_NAME) { + arguments.push_back(callee_object); + return enum_interface->GetNameMethod().global_signature; + } + if (member_proxy_method_name == checker::ETSEnumType::VALUES_METHOD_NAME) { + return enum_interface->ValuesMethod().global_signature; + } + if (member_proxy_method_name == checker::ETSEnumType::VALUE_OF_METHOD_NAME) { + arguments.push_back(expr->Arguments().front()); + return enum_interface->ValueOfMethod().global_signature; + } + UNREACHABLE(); + }(); + + ASSERT(signature->ReturnType() == expr->Signature()->ReturnType()); + etsg->CallStatic(expr, signature, arguments); + etsg->SetAccumulatorType(expr->TsType()); + return; + } + } + + bool is_static = expr->Signature()->HasSignatureFlag(checker::SignatureFlags::STATIC); + bool is_reference = expr->Signature()->HasSignatureFlag(checker::SignatureFlags::TYPE); + bool is_dynamic = expr->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG); + + ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); + + compiler::VReg dyn_param2; + + // Helper function to avoid branching in non optional cases + auto emit_arguments = [expr, etsg, is_static, is_dynamic, &callee_reg, &dyn_param2]() { + if (is_dynamic) { + etsg->CallDynamic(expr, callee_reg, dyn_param2, expr->Signature(), expr->Arguments()); + } else if (is_static) { + etsg->CallStatic(expr, expr->Signature(), expr->Arguments()); + } else if (expr->Signature()->HasSignatureFlag(checker::SignatureFlags::PRIVATE) || + expr->IsETSConstructorCall() || + (expr->Callee()->IsMemberExpression() && + expr->Callee()->AsMemberExpression()->Object()->IsSuperExpression())) { + etsg->CallThisStatic(expr, callee_reg, expr->Signature(), expr->Arguments()); + } else { + etsg->CallThisVirtual(expr, callee_reg, expr->Signature(), expr->Arguments()); + } + + if (expr->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) { + etsg->ApplyConversion(expr, nullptr); + } else { + etsg->SetAccumulatorType(expr->Signature()->ReturnType()); + } + }; + + if (is_dynamic) { + dyn_param2 = etsg->AllocReg(); + + ir::Expression *obj = expr->callee_; + std::vector parts; + + while (obj->IsMemberExpression() && obj->AsMemberExpression()->ObjType()->IsETSDynamicType()) { + auto *mem_expr = obj->AsMemberExpression(); + obj = mem_expr->Object(); + parts.push_back(mem_expr->Property()->AsIdentifier()->Name()); + } + + if (!obj->IsMemberExpression() && obj->IsIdentifier()) { + auto *var = obj->AsIdentifier()->Variable(); + auto *data = etsg->Binder()->DynamicImportDataForVar(var); + if (data != nullptr) { + auto *import = data->import; + auto *specifier = data->specifier; + ASSERT(import->Language().IsDynamic()); + etsg->LoadAccumulatorDynamicModule(expr, import); + if (specifier->IsImportSpecifier()) { + parts.push_back(specifier->AsImportSpecifier()->Imported()->Name()); + } + } else { + obj->Compile(etsg); + } + } else { + obj->Compile(etsg); + } + + etsg->StoreAccumulator(expr, callee_reg); + + if (!parts.empty()) { + std::stringstream ss; + for_each(parts.rbegin(), parts.rend(), [&ss](util::StringView sv) { ss << "." << sv; }); + + etsg->LoadAccumulatorString(expr, util::UString(ss.str(), etsg->Allocator()).View()); + } else { + auto lang = expr->Callee()->TsType()->IsETSDynamicFunctionType() + ? expr->Callee()->TsType()->AsETSDynamicFunctionType()->Language() + : expr->Callee()->TsType()->AsETSDynamicType()->Language(); + + etsg->LoadUndefinedDynamic(expr, lang); + } + + etsg->StoreAccumulator(expr, dyn_param2); + + emit_arguments(); + + if (expr->Signature()->ReturnType() != expr->TsType()) { + etsg->ApplyConversion(expr, expr->TsType()); + } + } else if (!is_reference && expr->Callee()->IsIdentifier()) { + if (!is_static) { + etsg->LoadThis(expr); + etsg->StoreAccumulator(expr, callee_reg); + } + emit_arguments(); + } else if (!is_reference && expr->Callee()->IsMemberExpression()) { + if (!is_static) { + expr->Callee()->AsMemberExpression()->Object()->Compile(etsg); + etsg->StoreAccumulator(expr, callee_reg); + } + emit_arguments(); + } else { + expr->Callee()->Compile(etsg); + etsg->StoreAccumulator(expr, callee_reg); + if (expr->optional_) { + compiler::Label *end_label = etsg->AllocLabel(); + etsg->BranchIfNull(expr, end_label); + emit_arguments(); + etsg->SetLabel(expr, end_label); + } else { + emit_arguments(); + } + } } -void ETSCompiler::Compile(const ir::ChainExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ChainExpression *expr) const { - (void)expr; UNREACHABLE(); } -void ETSCompiler::Compile(const ir::ClassExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::ClassExpression *expr) const { - (void)expr; UNREACHABLE(); } void ETSCompiler::Compile(const ir::ConditionalExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto *false_label = etsg->AllocLabel(); + auto *end_label = etsg->AllocLabel(); + + compiler::Condition::Compile(etsg, expr->Test(), false_label); + expr->Consequent()->Compile(etsg); + etsg->ApplyConversion(expr->Consequent()); + etsg->Branch(expr, end_label); + etsg->SetLabel(expr, false_label); + expr->Alternate()->Compile(etsg); + etsg->ApplyConversion(expr->Alternate()); + etsg->SetLabel(expr, end_label); } -void ETSCompiler::Compile(const ir::DirectEvalExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::DirectEvalExpression *expr) const { - (void)expr; UNREACHABLE(); } -void ETSCompiler::Compile(const ir::FunctionExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::FunctionExpression *expr) const { - (void)expr; UNREACHABLE(); } diff --git a/compiler/core/JSCompiler.cpp b/compiler/core/JSCompiler.cpp index a0b4815892d893a6432ee139e5fcea744c7a4b0c..bcfa6690ed12b68334f8da128ce0647d5e8fb696 100644 --- a/compiler/core/JSCompiler.cpp +++ b/compiler/core/JSCompiler.cpp @@ -15,18 +15,10 @@ #include "JSCompiler.h" +#include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" -#include "ir/base/catchClause.h" -#include "ir/base/classDefinition.h" -#include "ir/base/classProperty.h" -#include "ir/base/classStaticBlock.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/functionExpression.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/returnStatement.h" +#include "compiler/function/functionBuilder.h" #include "util/helpers.h" namespace panda::es2panda::compiler { @@ -523,39 +515,33 @@ void JSCompiler::Compile(const ir::ETSPackageDeclaration *expr) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSParameterExpression *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSParameterExpression *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSPrimitiveType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSPrimitiveType *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSStructDeclaration *node) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSStructDeclaration *node) const { - (void)node; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReference *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReference *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSTypeReferencePart *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSTypeReferencePart *expr) const { - (void)expr; UNREACHABLE(); } -void JSCompiler::Compile(const ir::ETSWildcardType *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *expr) const { - (void)expr; UNREACHABLE(); } @@ -568,8 +554,8 @@ void JSCompiler::Compile(const ir::ArrayExpression *expr) const void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName()); } void JSCompiler::Compile(const ir::AssignmentExpression *expr) const @@ -580,50 +566,237 @@ void JSCompiler::Compile(const ir::AssignmentExpression *expr) const void JSCompiler::Compile(const ir::AwaitExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + + if (expr->Argument() != nullptr) { + expr->Argument()->Compile(pg); + } else { + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + } + + pg->EmitAwait(expr); +} + +static void CompileLogical(compiler::PandaGen *pg, const ir::BinaryExpression *expr) +{ + compiler::RegScope rs(pg); + compiler::VReg lhs = pg->AllocReg(); + + ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || + expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR || + expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING); + + auto *skip_right = pg->AllocLabel(); + auto *end_label = pg->AllocLabel(); + + // left -> acc -> lhs -> toboolean -> acc -> bool_lhs + expr->Left()->Compile(pg); + pg->StoreAccumulator(expr, lhs); + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { + pg->ToBoolean(expr); + pg->BranchIfFalse(expr, skip_right); + } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { + pg->ToBoolean(expr); + pg->BranchIfTrue(expr, skip_right); + } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING) { + pg->BranchIfCoercible(expr, skip_right); + } + + // left is true/false(and/or) then right -> acc + expr->Right()->Compile(pg); + pg->Branch(expr, end_label); + + // left is false/true(and/or) then lhs -> acc + pg->SetLabel(expr, skip_right); + pg->LoadAccumulator(expr, lhs); + pg->SetLabel(expr, end_label); } void JSCompiler::Compile(const ir::BinaryExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + if (expr->IsLogical()) { + CompileLogical(pg, expr); + return; + } + + if (expr->OperatorType() == lexer::TokenType::KEYW_IN && expr->Left()->IsIdentifier() && + expr->Left()->AsIdentifier()->IsPrivateIdent()) { + compiler::RegScope rs(pg); + compiler::VReg ctor = pg->AllocReg(); + const auto &name = expr->Left()->AsIdentifier()->Name(); + compiler::Function::LoadClassContexts(expr, pg, ctor, name); + expr->Right()->Compile(pg); + pg->ClassPrivateFieldIn(expr, ctor, name); + return; + } + + compiler::RegScope rs(pg); + compiler::VReg lhs = pg->AllocReg(); + + expr->Left()->Compile(pg); + pg->StoreAccumulator(expr, lhs); + expr->Right()->Compile(pg); + + pg->Binary(expr, expr->OperatorType(), lhs); +} + +static compiler::VReg CreateSpreadArguments(compiler::PandaGen *pg, const ir::CallExpression *expr) +{ + compiler::VReg args_obj = pg->AllocReg(); + pg->CreateArray(expr, expr->Arguments(), args_obj); + + return args_obj; } void JSCompiler::Compile(const ir::CallExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + bool contains_spread = util::Helpers::ContainSpreadElement(expr->Arguments()); + + if (expr->Callee()->IsSuperExpression()) { + if (contains_spread) { + compiler::RegScope param_scope(pg); + compiler::VReg args_obj = CreateSpreadArguments(pg, expr); + + pg->GetFunctionObject(expr); + pg->SuperCallSpread(expr, args_obj); + } else { + compiler::RegScope param_scope(pg); + compiler::VReg arg_start {}; + + if (expr->Arguments().empty()) { + arg_start = pg->AllocReg(); + pg->StoreConst(expr, arg_start, compiler::Constant::JS_UNDEFINED); + } else { + arg_start = pg->NextReg(); + } + + for (const auto *it : expr->Arguments()) { + compiler::VReg arg = pg->AllocReg(); + it->Compile(pg); + pg->StoreAccumulator(it, arg); + } + + pg->GetFunctionObject(expr); + pg->SuperCall(expr, arg_start, expr->Arguments().size()); + } + + compiler::VReg new_this = pg->AllocReg(); + pg->StoreAccumulator(expr, new_this); + + pg->GetThis(expr); + pg->ThrowIfSuperNotCorrectCall(expr, 1); + + pg->LoadAccumulator(expr, new_this); + pg->SetThis(expr); + + compiler::Function::CompileInstanceFields(pg, pg->RootNode()->AsScriptFunction()); + return; + } + + compiler::VReg callee = pg->AllocReg(); + compiler::VReg this_reg = compiler::VReg::Invalid(); + + if (expr->Callee()->IsMemberExpression()) { + this_reg = pg->AllocReg(); + + compiler::RegScope mrs(pg); + expr->Callee()->AsMemberExpression()->CompileToReg(pg, this_reg); + } else if (expr->Callee()->IsChainExpression()) { + this_reg = pg->AllocReg(); + + compiler::RegScope mrs(pg); + expr->Callee()->AsChainExpression()->CompileToReg(pg, this_reg); + } else { + expr->Callee()->Compile(pg); + } + + pg->StoreAccumulator(expr, callee); + pg->OptionalChainCheck(expr->optional_, callee); + + if (contains_spread || expr->Arguments().size() >= compiler::PandaGen::MAX_RANGE_CALL_ARG) { + if (this_reg.IsInvalid()) { + this_reg = pg->AllocReg(); + pg->StoreConst(expr, this_reg, compiler::Constant::JS_UNDEFINED); + } + + compiler::VReg args_obj = CreateSpreadArguments(pg, expr); + pg->CallSpread(expr, callee, this_reg, args_obj); + } else { + pg->Call(expr, callee, this_reg, expr->Arguments()); + } } void JSCompiler::Compile(const ir::ChainExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::OptionalChain chain(pg, expr); + expr->GetExpression()->Compile(pg); } void JSCompiler::Compile(const ir::ClassExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + expr->Definition()->Compile(pg); +} + +template +static void CompileImpl(const ir::ConditionalExpression *self, CodeGen *cg) +{ + auto *false_label = cg->AllocLabel(); + auto *end_label = cg->AllocLabel(); + + compiler::Condition::Compile(cg, self->Test(), false_label); + self->Consequent()->Compile(cg); + cg->Branch(self, end_label); + cg->SetLabel(self, false_label); + self->Alternate()->Compile(cg); + cg->SetLabel(self, end_label); } void JSCompiler::Compile(const ir::ConditionalExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + CompileImpl(expr, pg); } void JSCompiler::Compile(const ir::DirectEvalExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + if (expr->Arguments().empty()) { + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + return; + } + + compiler::RegScope rs(pg); + bool contains_spread = util::Helpers::ContainSpreadElement(expr->Arguments()); + if (contains_spread) { + [[maybe_unused]] compiler::VReg args_obj = CreateSpreadArguments(pg, expr); + pg->LoadObjByIndex(expr, 0); + } else { + compiler::VReg arg0 = pg->AllocReg(); + auto iter = expr->Arguments().cbegin(); + (*iter++)->Compile(pg); + pg->StoreAccumulator(expr, arg0); + + while (iter != expr->Arguments().cend()) { + (*iter++)->Compile(pg); + } + + pg->LoadAccumulator(expr, arg0); + } + + pg->DirectEval(expr, expr->parser_status_); } void JSCompiler::Compile(const ir::FunctionExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName()); } void JSCompiler::Compile(const ir::Identifier *expr) 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/expressions/awaitExpression.cpp b/ir/expressions/awaitExpression.cpp index 14c81a3a0aef7d202c6a71d1aa68797e731e4f1e..44b0e274d78266299b5e8a592db86e93232ba16a 100644 --- a/ir/expressions/awaitExpression.cpp +++ b/ir/expressions/awaitExpression.cpp @@ -15,15 +15,10 @@ #include "awaitExpression.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" -#include "compiler/core/regScope.h" -#include "checker/TSchecker.h" -#include "checker/ETSchecker.h" #include "ir/astDump.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/arrowFunctionExpression.h" namespace panda::es2panda::ir { void AwaitExpression::TransformChildren(const NodeTransformer &cb) @@ -47,50 +42,22 @@ void AwaitExpression::Dump(ir::AstDumper *dumper) const void AwaitExpression::Compile(compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - - if (argument_ != nullptr) { - argument_->Compile(pg); - } else { - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - } - - pg->EmitAwait(this); + pg->GetAstCompiler()->Compile(this); } void AwaitExpression::Compile(compiler::ETSGen *etsg) const { - static constexpr bool IS_UNCHECKED_CAST = false; - compiler::RegScope rs(etsg); - compiler::VReg argument_reg = etsg->AllocReg(); - argument_->Compile(etsg); - etsg->StoreAccumulator(this, argument_reg); - etsg->CallThisVirtual0(argument_, argument_reg, compiler::Signatures::BUILTIN_PROMISE_AWAIT_RESOLUTION); - etsg->CastToArrayOrObject(argument_, TsType(), IS_UNCHECKED_CAST); - etsg->SetAccumulatorType(TsType()); + etsg->GetAstCompiler()->Compile(this); } -checker::Type *AwaitExpression::Check([[maybe_unused]] checker::TSChecker *checker) +checker::Type *AwaitExpression::Check(checker::TSChecker *checker) { - // TODO(aszilagyi) - return checker->GlobalAnyType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *AwaitExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - checker::Type *arg_type = argument_->Check(checker); - // Check the argument type of await expression - if (!arg_type->IsETSObjectType() || - (arg_type->AsETSObjectType()->AssemblerName() != compiler::Signatures::BUILTIN_PROMISE)) { - checker->ThrowTypeError("'await' expressions require Promise object as argument.", argument_->Start()); - } - - SetTsType(arg_type->AsETSObjectType()->TypeArguments().at(0)); - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/expressions/awaitExpression.h b/ir/expressions/awaitExpression.h index 17240df98c3efcdf6ca7e22e01545aa4a293e8f9..0a84c6e7bf4f44dcf491b3c5ebc765216ced2385 100644 --- a/ir/expressions/awaitExpression.h +++ b/ir/expressions/awaitExpression.h @@ -18,6 +18,10 @@ #include "ir/expression.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class AwaitExpression : public Expression { public: @@ -29,6 +33,9 @@ public: explicit AwaitExpression(Expression *argument) : Expression(AstNodeType::AWAIT_EXPRESSION), argument_(argument) {} + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const Expression *Argument() const noexcept { return argument_; @@ -43,7 +50,7 @@ public: void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *argument_; diff --git a/ir/expressions/binaryExpression.cpp b/ir/expressions/binaryExpression.cpp index 5c041a13fff7fcb6623b34bd93b5e46d9b1f90bc..edfe9793073b2b18a6b95e59ba87804c57165aea 100644 --- a/ir/expressions/binaryExpression.cpp +++ b/ir/expressions/binaryExpression.cpp @@ -15,16 +15,10 @@ #include "binaryExpression.h" -#include "binder/variable.h" -#include "compiler/core/function.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" -#include "compiler/core/regScope.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/expressions/identifier.h" -#include "lexer/token/tokenType.h" namespace panda::es2panda::ir { void BinaryExpression::TransformChildren(const NodeTransformer &cb) @@ -47,201 +41,24 @@ void BinaryExpression::Dump(ir::AstDumper *dumper) const {"right", right_}}); } -void BinaryExpression::CompileLogical(compiler::PandaGen *pg) const -{ - compiler::RegScope rs(pg); - compiler::VReg lhs = pg->AllocReg(); - - ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || - operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR || - operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING); - - auto *skip_right = pg->AllocLabel(); - auto *end_label = pg->AllocLabel(); - - // left -> acc -> lhs -> toboolean -> acc -> bool_lhs - left_->Compile(pg); - pg->StoreAccumulator(this, lhs); - - if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { - pg->ToBoolean(this); - pg->BranchIfFalse(this, skip_right); - } else if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { - pg->ToBoolean(this); - pg->BranchIfTrue(this, skip_right); - } else if (operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING) { - pg->BranchIfCoercible(this, skip_right); - } - - // left is true/false(and/or) then right -> acc - right_->Compile(pg); - pg->Branch(this, end_label); - - // left is false/true(and/or) then lhs -> acc - pg->SetLabel(this, skip_right); - pg->LoadAccumulator(this, lhs); - pg->SetLabel(this, end_label); -} - void BinaryExpression::Compile(compiler::PandaGen *pg) const { - if (IsLogical()) { - CompileLogical(pg); - return; - } - - if (operator_ == lexer::TokenType::KEYW_IN && left_->IsIdentifier() && left_->AsIdentifier()->IsPrivateIdent()) { - compiler::RegScope rs(pg); - compiler::VReg ctor = pg->AllocReg(); - const auto &name = left_->AsIdentifier()->Name(); - compiler::Function::LoadClassContexts(this, pg, ctor, name); - right_->Compile(pg); - pg->ClassPrivateFieldIn(this, ctor, name); - return; - } - - compiler::RegScope rs(pg); - compiler::VReg lhs = pg->AllocReg(); - - left_->Compile(pg); - pg->StoreAccumulator(this, lhs); - right_->Compile(pg); - - pg->Binary(this, operator_, lhs); + pg->GetAstCompiler()->Compile(this); } void BinaryExpression::Compile(compiler::ETSGen *etsg) const { - if (etsg->TryLoadConstantExpression(this)) { - return; - } - - auto ttctx = compiler::TargetTypeContext(etsg, operation_type_); - - if (IsLogical()) { - CompileLogical(etsg); - etsg->ApplyConversion(this, operation_type_); - return; - } - - compiler::RegScope rs(etsg); - compiler::VReg lhs = etsg->AllocReg(); - - if (operator_ == lexer::TokenType::PUNCTUATOR_PLUS && - (left_->TsType()->IsETSStringType() || right_->TsType()->IsETSStringType())) { - etsg->BuildString(this); - return; - } - - left_->Compile(etsg); - etsg->ApplyConversionAndStoreAccumulator(left_, lhs, operation_type_); - right_->Compile(etsg); - etsg->ApplyConversion(right_, operation_type_); - if (operator_ >= lexer::TokenType::PUNCTUATOR_LEFT_SHIFT && - operator_ <= lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT) { - etsg->ApplyCast(right_, operation_type_); - } - - etsg->Binary(this, operator_, lhs); -} - -void BinaryExpression::CompileLogical(compiler::ETSGen *etsg) const -{ - auto *end_label = etsg->AllocLabel(); - - left_->Compile(etsg); - etsg->ApplyConversion(left_, operation_type_); - - if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { - etsg->BranchIfFalse(this, end_label); - } else if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { - etsg->BranchIfTrue(this, end_label); - } else { - ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING); - etsg->BranchIfNotNull(this, end_label); - } - - right_->Compile(etsg); - etsg->ApplyConversion(right_, operation_type_); - - etsg->SetLabel(this, end_label); + etsg->GetAstCompiler()->Compile(this); } checker::Type *BinaryExpression::Check(checker::TSChecker *checker) { - auto *left_type = left_->Check(checker); - auto *right_type = right_->Check(checker); - - switch (operator_) { - case lexer::TokenType::PUNCTUATOR_MULTIPLY: - case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: - case lexer::TokenType::PUNCTUATOR_DIVIDE: - case lexer::TokenType::PUNCTUATOR_MOD: - case lexer::TokenType::PUNCTUATOR_MINUS: - case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: - case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: - case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: - case lexer::TokenType::PUNCTUATOR_BITWISE_AND: - case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: - case lexer::TokenType::PUNCTUATOR_BITWISE_OR: { - return checker->CheckBinaryOperator(left_type, right_type, left_, right_, this, operator_); - } - case lexer::TokenType::PUNCTUATOR_PLUS: { - return checker->CheckPlusOperator(left_type, right_type, left_, right_, this, operator_); - } - case lexer::TokenType::PUNCTUATOR_LESS_THAN: - case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { - return checker->CheckCompareOperator(left_type, right_type, left_, right_, this, operator_); - } - case lexer::TokenType::PUNCTUATOR_EQUAL: - case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: - case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: - case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { - if (checker->IsTypeEqualityComparableTo(left_type, right_type) || - checker->IsTypeEqualityComparableTo(right_type, left_type)) { - return checker->GlobalBooleanType(); - } - - checker->ThrowBinaryLikeError(operator_, left_type, right_type, Start()); - } - case lexer::TokenType::KEYW_INSTANCEOF: { - return checker->CheckInstanceofExpression(left_type, right_type, right_, this); - } - case lexer::TokenType::KEYW_IN: { - return checker->CheckInExpression(left_type, right_type, left_, right_, this); - } - case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { - return checker->CheckAndOperator(left_type, right_type, left_); - } - case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { - return checker->CheckOrOperator(left_type, right_type, left_); - } - case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: { - // TODO(Csaba Repasi): Implement checker for nullish coalescing - return checker->GlobalAnyType(); - } - case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { - checker->CheckAssignmentOperator(operator_, left_, left_type, right_type); - return right_type; - } - default: { - UNREACHABLE(); - break; - } - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *BinaryExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - checker::Type *new_ts_type {nullptr}; - std::tie(new_ts_type, operation_type_) = checker->CheckBinaryOperator(left_, right_, operator_, Start()); - SetTsType(new_ts_type); - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/expressions/binaryExpression.h b/ir/expressions/binaryExpression.h index de4488f0c1571d467b2d6f819e940d131e981ef2..b05fcf1e48d72d6006a4c4be8353cf791e9fc717 100644 --- a/ir/expressions/binaryExpression.h +++ b/ir/expressions/binaryExpression.h @@ -19,6 +19,10 @@ #include "ir/expression.h" #include "lexer/token/tokenType.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class BinaryExpression : public Expression { public: @@ -33,6 +37,9 @@ public: { } + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const Expression *Left() const noexcept { return left_; @@ -98,10 +105,8 @@ public: void Dump(ir::AstDumper *dumper) const override; void Compile(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; - void CompileLogical(compiler::PandaGen *pg) const; - void CompileLogical(compiler::ETSGen *etsg) const; checker::Type *Check(checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: Expression *left_ = nullptr; diff --git a/ir/expressions/callExpression.cpp b/ir/expressions/callExpression.cpp index 715e0706569785690eee7665cbfe1f27d625e376..95f3d5059c5fe3aa7181c8f71d70fe85019cac00 100644 --- a/ir/expressions/callExpression.cpp +++ b/ir/expressions/callExpression.cpp @@ -15,16 +15,13 @@ #include "callExpression.h" -#include "util/helpers.h" -#include "compiler/core/function.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" -#include "compiler/core/regScope.h" -#include "checker/TSchecker.h" #include "checker/ETSchecker.h" +#include "checker/TSchecker.h" #include "checker/types/ets/etsDynamicFunctionType.h" #include "checker/types/ts/objectType.h" #include "checker/types/signature.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" #include "ir/base/scriptFunction.h" #include "ir/base/spreadElement.h" @@ -36,8 +33,6 @@ #include "ir/expressions/arrowFunctionExpression.h" #include "ir/expressions/literals/numberLiteral.h" #include "ir/statements/blockStatement.h" -#include "ir/ts/tsTypeParameterInstantiation.h" -#include "ir/ts/tsEnumMember.h" namespace panda::es2panda::ir { void CallExpression::TransformChildren(const NodeTransformer &cb) @@ -87,285 +82,19 @@ compiler::VReg CallExpression::CreateSpreadArguments(compiler::PandaGen *pg) con return args_obj; } -void CallExpression::ConvertRestArguments(checker::ETSChecker *const checker) const -{ - if (signature_->RestVar() != nullptr) { - std::size_t const argument_count = arguments_.size(); - std::size_t const parameter_count = signature_->MinArgCount(); - ASSERT(argument_count >= parameter_count); - - auto &arguments = const_cast &>(arguments_); - std::size_t i = parameter_count; - - if (i < argument_count && arguments_[i]->IsSpreadElement()) { - arguments[i] = arguments_[i]->AsSpreadElement()->Argument(); - } else { - ArenaVector elements(checker->Allocator()->Adapter()); - for (; i < argument_count; ++i) { - elements.emplace_back(arguments_[i]); - } - auto *array_expression = checker->AllocNode(std::move(elements), checker->Allocator()); - array_expression->SetParent(const_cast(this)); - array_expression->SetTsType(signature_->RestVar()->TsType()); - arguments.erase(arguments_.begin() + parameter_count, arguments_.end()); - arguments.emplace_back(array_expression); - } - } -} - void CallExpression::Compile(compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - bool contains_spread = util::Helpers::ContainSpreadElement(arguments_); - - if (callee_->IsSuperExpression()) { - if (contains_spread) { - compiler::RegScope param_scope(pg); - compiler::VReg args_obj = CreateSpreadArguments(pg); - - pg->GetFunctionObject(this); - pg->SuperCallSpread(this, args_obj); - } else { - compiler::RegScope param_scope(pg); - compiler::VReg arg_start {}; - - if (arguments_.empty()) { - arg_start = pg->AllocReg(); - pg->StoreConst(this, arg_start, compiler::Constant::JS_UNDEFINED); - } else { - arg_start = pg->NextReg(); - } - - for (const auto *it : arguments_) { - compiler::VReg arg = pg->AllocReg(); - it->Compile(pg); - pg->StoreAccumulator(it, arg); - } - - pg->GetFunctionObject(this); - pg->SuperCall(this, arg_start, arguments_.size()); - } - - compiler::VReg new_this = pg->AllocReg(); - pg->StoreAccumulator(this, new_this); - - pg->GetThis(this); - pg->ThrowIfSuperNotCorrectCall(this, 1); - - pg->LoadAccumulator(this, new_this); - pg->SetThis(this); - - compiler::Function::CompileInstanceFields(pg, pg->RootNode()->AsScriptFunction()); - return; - } - - compiler::VReg callee = pg->AllocReg(); - compiler::VReg this_reg = compiler::VReg::Invalid(); - - if (callee_->IsMemberExpression()) { - this_reg = pg->AllocReg(); - - compiler::RegScope mrs(pg); - callee_->AsMemberExpression()->CompileToReg(pg, this_reg); - } else if (callee_->IsChainExpression()) { - this_reg = pg->AllocReg(); - - compiler::RegScope mrs(pg); - callee_->AsChainExpression()->CompileToReg(pg, this_reg); - } else { - callee_->Compile(pg); - } - - pg->StoreAccumulator(this, callee); - pg->OptionalChainCheck(optional_, callee); - - if (contains_spread || arguments_.size() >= compiler::PandaGen::MAX_RANGE_CALL_ARG) { - if (this_reg.IsInvalid()) { - this_reg = pg->AllocReg(); - pg->StoreConst(this, this_reg, compiler::Constant::JS_UNDEFINED); - } - - compiler::VReg args_obj = CreateSpreadArguments(pg); - pg->CallSpread(this, callee, this_reg, args_obj); - } else { - pg->Call(this, callee, this_reg, arguments_); - } + pg->GetAstCompiler()->Compile(this); } void CallExpression::Compile(compiler::ETSGen *etsg) const { - compiler::RegScope rs(etsg); - compiler::VReg callee_reg = etsg->AllocReg(); - - const auto is_proxy = signature_->HasSignatureFlag(checker::SignatureFlags::PROXY); - - if (is_proxy && callee_->IsMemberExpression()) { - auto *const callee_object = callee_->AsMemberExpression()->Object(); - - auto const *const enum_interface = [callee_type = - callee_object->TsType()]() -> checker::ETSEnumInterface const * { - if (callee_type->IsETSEnumType()) { - return callee_type->AsETSEnumType(); - } - if (callee_type->IsETSStringEnumType()) { - return callee_type->AsETSStringEnumType(); - } - return nullptr; - }(); - - if (enum_interface != nullptr) { - ArenaVector arguments(etsg->Allocator()->Adapter()); - - checker::Signature *const signature = [this, callee_object, enum_interface, &arguments]() { - const auto &member_proxy_method_name = signature_->InternalName(); - - if (member_proxy_method_name == checker::ETSEnumType::TO_STRING_METHOD_NAME) { - arguments.push_back(callee_object); - return enum_interface->ToStringMethod().global_signature; - } - if (member_proxy_method_name == checker::ETSEnumType::GET_VALUE_METHOD_NAME) { - arguments.push_back(callee_object); - return enum_interface->GetValueMethod().global_signature; - } - if (member_proxy_method_name == checker::ETSEnumType::GET_NAME_METHOD_NAME) { - arguments.push_back(callee_object); - return enum_interface->GetNameMethod().global_signature; - } - if (member_proxy_method_name == checker::ETSEnumType::VALUES_METHOD_NAME) { - return enum_interface->ValuesMethod().global_signature; - } - if (member_proxy_method_name == checker::ETSEnumType::VALUE_OF_METHOD_NAME) { - arguments.push_back(arguments_.front()); - return enum_interface->ValueOfMethod().global_signature; - } - UNREACHABLE(); - }(); - - ASSERT(signature->ReturnType() == signature_->ReturnType()); - etsg->CallStatic(this, signature, arguments); - etsg->SetAccumulatorType(TsType()); - return; - } - } - - bool is_static = signature_->HasSignatureFlag(checker::SignatureFlags::STATIC); - bool is_reference = signature_->HasSignatureFlag(checker::SignatureFlags::TYPE); - bool is_dynamic = callee_->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG); - - ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker())); - - compiler::VReg dyn_param2; - - // Helper function to avoid branching in non optional cases - auto emit_arguments = [this, etsg, is_static, is_dynamic, &callee_reg, &dyn_param2]() { - if (is_dynamic) { - etsg->CallDynamic(this, callee_reg, dyn_param2, signature_, arguments_); - } else if (is_static) { - etsg->CallStatic(this, signature_, arguments_); - } else if (signature_->HasSignatureFlag(checker::SignatureFlags::PRIVATE) || IsETSConstructorCall() || - (callee_->IsMemberExpression() && callee_->AsMemberExpression()->Object()->IsSuperExpression())) { - etsg->CallThisStatic(this, callee_reg, signature_, arguments_); - } else { - etsg->CallThisVirtual(this, callee_reg, signature_, arguments_); - } - - if (GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) { - etsg->ApplyConversion(this, nullptr); - } else { - etsg->SetAccumulatorType(signature_->ReturnType()); - } - }; - - if (is_dynamic) { - dyn_param2 = etsg->AllocReg(); - - ir::Expression *obj = callee_; - std::vector parts; - - while (obj->IsMemberExpression() && obj->AsMemberExpression()->ObjType()->IsETSDynamicType()) { - auto *mem_expr = obj->AsMemberExpression(); - obj = mem_expr->Object(); - parts.push_back(mem_expr->Property()->AsIdentifier()->Name()); - } - - if (!obj->IsMemberExpression() && obj->IsIdentifier()) { - auto *var = obj->AsIdentifier()->Variable(); - auto *data = etsg->Binder()->DynamicImportDataForVar(var); - if (data != nullptr) { - auto *import = data->import; - auto *specifier = data->specifier; - ASSERT(import->Language().IsDynamic()); - etsg->LoadAccumulatorDynamicModule(this, import); - if (specifier->IsImportSpecifier()) { - parts.push_back(specifier->AsImportSpecifier()->Imported()->Name()); - } - } else { - obj->Compile(etsg); - } - } else { - obj->Compile(etsg); - } - - etsg->StoreAccumulator(this, callee_reg); - - if (!parts.empty()) { - std::stringstream ss; - for_each(parts.rbegin(), parts.rend(), [&ss](util::StringView sv) { ss << "." << sv; }); - - etsg->LoadAccumulatorString(this, util::UString(ss.str(), etsg->Allocator()).View()); - } else { - auto lang = callee_->TsType()->IsETSDynamicFunctionType() - ? callee_->TsType()->AsETSDynamicFunctionType()->Language() - : callee_->TsType()->AsETSDynamicType()->Language(); - - etsg->LoadUndefinedDynamic(this, lang); - } - - etsg->StoreAccumulator(this, dyn_param2); - - emit_arguments(); - - if (signature_->ReturnType() != TsType()) { - etsg->ApplyConversion(this, TsType()); - } - } else if (!is_reference && callee_->IsIdentifier()) { - if (!is_static) { - etsg->LoadThis(this); - etsg->StoreAccumulator(this, callee_reg); - } - emit_arguments(); - } else if (!is_reference && callee_->IsMemberExpression()) { - if (!is_static) { - callee_->AsMemberExpression()->Object()->Compile(etsg); - etsg->StoreAccumulator(this, callee_reg); - } - emit_arguments(); - } else { - callee_->Compile(etsg); - etsg->StoreAccumulator(this, callee_reg); - if (optional_) { - compiler::Label *end_label = etsg->AllocLabel(); - etsg->BranchIfNull(this, end_label); - emit_arguments(); - etsg->SetLabel(this, end_label); - } else { - emit_arguments(); - } - } + etsg->GetAstCompiler()->Compile(this); } checker::Type *CallExpression::Check(checker::TSChecker *checker) { - checker::Type *callee_type = callee_->Check(checker); - - // TODO(aszilagyi): handle optional chain - if (callee_type->IsObjectType()) { - checker::ObjectType *callee_obj = callee_type->AsObjectType(); - return checker->ResolveCallOrNewExpression(callee_obj->CallSignatures(), arguments_, Start()); - } - - checker->ThrowTypeError("This expression is not callable.", Start()); - return nullptr; + return checker->GetAnalyzer()->Check(this); } bool CallExpression::IsETSConstructorCall() const @@ -373,158 +102,8 @@ bool CallExpression::IsETSConstructorCall() const return callee_->IsThisExpression() || callee_->IsSuperExpression(); } -checker::Signature *CallExpression::ResolveCallExtensionFunction(checker::ETSFunctionType *function_type, - checker::ETSChecker *checker) -{ - auto *member_expr = callee_->AsMemberExpression(); - arguments_.insert(arguments_.begin(), member_expr->Object()); - auto *signature = checker->ResolveCallExpressionAndTrailingLambda(function_type->CallSignatures(), this, Start()); - if (!signature->Function()->IsExtensionMethod()) { - checker->ThrowTypeError({"Property '", member_expr->Property()->AsIdentifier()->Name(), - "' does not exist on type '", member_expr->ObjType()->Name(), "'"}, - member_expr->Property()->Start()); - } - this->SetSignature(signature); - this->SetCallee(member_expr->Property()); - member_expr->Property()->AsIdentifier()->SetParent(this); - this->Arguments()[0]->SetParent(this); - checker->HandleUpdatedCallExpressionNode(this); - // Set TsType for new Callee(original member expression's Object) - this->Callee()->Check(checker); - return signature; -} - -checker::Signature *CallExpression::ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type, - checker::ETSChecker *checker) -{ - checker::Signature *signature = checker->ResolveCallExpressionAndTrailingLambda( - type->ClassMethodType()->CallSignatures(), this, Start(), checker::TypeRelationFlag::NO_THROW); - - if (signature != nullptr) { - return signature; - } - - return ResolveCallExtensionFunction(type->ExtensionMethodType(), checker); -} - checker::Type *CallExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - auto *old_callee = callee_; - checker::Type *callee_type = callee_->Check(checker); - if (callee_ != old_callee) { - // If it is a static invoke, the callee will be transformed from an identifier to a member expression - // Type check the callee again for member expression - callee_type = callee_->Check(checker); - } - - checker::Type *return_type; - if (callee_type->IsETSDynamicType() && !callee_type->AsETSDynamicType()->HasDecl()) { - // Trailing lambda for js function call is not supported, check the correctness of `foo() {}` - checker->EnsureValidCurlyBrace(this); - auto lang = callee_type->AsETSDynamicType()->Language(); - signature_ = checker->ResolveDynamicCallExpression(callee_, arguments_, lang, false); - return_type = signature_->ReturnType(); - } else { - bool constructor_call = IsETSConstructorCall(); - bool functional_interface = - callee_type->IsETSObjectType() && - callee_type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE); - bool ets_extension_func_helper_type = callee_type->IsETSExtensionFuncHelperType(); - bool extension_function_type = callee_->IsMemberExpression() && checker->ExtensionETSFunctionType(callee_type); - - if (callee_->IsArrowFunctionExpression()) { - callee_type = InitAnonymousLambdaCallee(checker, callee_, callee_type); - functional_interface = true; - } - - if (!functional_interface && !callee_type->IsETSFunctionType() && !constructor_call && - !ets_extension_func_helper_type) { - checker->ThrowTypeError("This expression is not callable.", Start()); - } - - checker::Signature *signature = nullptr; - - if (ets_extension_func_helper_type) { - signature = ResolveCallForETSExtensionFuncHelperType(callee_type->AsETSExtensionFuncHelperType(), checker); - } else { - if (extension_function_type) { - signature = ResolveCallExtensionFunction(callee_type->AsETSFunctionType(), checker); - } else { - auto &signatures = constructor_call ? callee_type->AsETSObjectType()->ConstructSignatures() - : functional_interface - ? callee_type->AsETSObjectType() - ->GetOwnProperty("invoke") - ->TsType() - ->AsETSFunctionType() - ->CallSignatures() - : callee_type->AsETSFunctionType()->CallSignatures(); - signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, this, Start()); - if (signature->Function()->IsExtensionMethod()) { - checker->ThrowTypeError({"No matching call signature"}, Start()); - } - } - } - - checker->CheckObjectLiteralArguments(signature, arguments_); - - checker->AddNullParamsForDefaultParams(signature, arguments_, checker); - - if (!functional_interface) { - checker::ETSObjectType *callee_obj {}; - if (constructor_call) { - callee_obj = callee_type->AsETSObjectType(); - } else if (callee_->IsIdentifier()) { - callee_obj = checker->Context().ContainingClass(); - } else { - ASSERT(callee_->IsMemberExpression()); - callee_obj = callee_->AsMemberExpression()->ObjType(); - } - - checker->ValidateSignatureAccessibility(callee_obj, signature, Start()); - } - - ASSERT(signature->Function() != nullptr); - - if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) { - checker->CheckThrowingStatements(this); - } - - if (signature->Function()->IsDynamic()) { - ASSERT(signature->Function()->IsDynamic()); - auto lang = signature->Function()->Language(); - signature_ = checker->ResolveDynamicCallExpression(callee_, signature->Params(), lang, false); - } else { - ASSERT(!signature->Function()->IsDynamic()); - signature_ = signature; - } - - return_type = signature->ReturnType(); - } - - if (signature_->RestVar() != nullptr) { - auto *const element_type = signature_->RestVar()->TsType()->AsETSArrayType()->ElementType(); - auto *const array_type = checker->CreateETSArrayType(element_type)->AsETSArrayType(); - checker->CreateBuiltinArraySignature(array_type, array_type->Rank()); - } - - SetTsType(return_type); - return TsType(); -} - -checker::Type *CallExpression::InitAnonymousLambdaCallee(checker::ETSChecker *checker, Expression *callee, - checker::Type *callee_type) -{ - auto *const arrow_func = callee->AsArrowFunctionExpression()->Function(); - auto orig_params = arrow_func->Params(); - auto *func_type = checker->Allocator()->New( - arrow_func->Scope()->AsFunctionScope()->ParamScope(), std::move(orig_params), nullptr, - arrow_func->ReturnTypeAnnotation(), ir::ScriptFunctionFlags::NONE); - auto *const func_iface = func_type->Check(checker); - checker->Relation()->SetNode(callee); - checker->Relation()->IsAssignableTo(callee_type, func_iface); - return func_iface; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/expressions/callExpression.h b/ir/expressions/callExpression.h index a63399fe5ae9ce8a0192644ba33ea90f314864b1..ef82726da0a6d9846f858c7fc6f11f053385a309 100644 --- a/ir/expressions/callExpression.h +++ b/ir/expressions/callExpression.h @@ -21,9 +21,16 @@ #include "ir/expression.h" namespace panda::es2panda::checker { +class ETSAnalyzer; +class TSAnalyzer; class Signature; } // namespace panda::es2panda::checker +namespace panda::es2panda::compiler { +class JSCompiler; +class ETSCompiler; +} // namespace panda::es2panda::compiler + namespace panda::es2panda::ir { class TSTypeParameterInstantiation; @@ -40,6 +47,12 @@ public: { } + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class checker::TSAnalyzer; + friend class checker::ETSAnalyzer; + friend class compiler::JSCompiler; + friend class compiler::ETSCompiler; + const Expression *Callee() const { return callee_; @@ -123,14 +136,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; - checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *function_type, - checker::ETSChecker *checker); - checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type, - checker::ETSChecker *checker); + 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; protected: compiler::VReg CreateSpreadArguments(compiler::PandaGen *pg) const; @@ -151,7 +160,6 @@ private: bool IsETSConstructorCall() const; checker::Type *InitAnonymousLambdaCallee(checker::ETSChecker *checker, Expression *callee, checker::Type *callee_type); - void ConvertRestArguments(checker::ETSChecker *checker) const; }; } // namespace panda::es2panda::ir diff --git a/ir/expressions/chainExpression.cpp b/ir/expressions/chainExpression.cpp index ce71eac838816b664bcb6cbfc04018e05cfb1b23..c2987762eee531c3fea3dbb286fea0a153a21aa3 100644 --- a/ir/expressions/chainExpression.cpp +++ b/ir/expressions/chainExpression.cpp @@ -15,12 +15,11 @@ #include "chainExpression.h" -#include "ir/expressions/callExpression.h" -#include "ir/expressions/memberExpression.h" -#include "compiler/base/optionalChain.h" -#include "compiler/core/regScope.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" #include "ir/astDump.h" +#include "ir/expressions/memberExpression.h" namespace panda::es2panda::ir { void ChainExpression::TransformChildren(const NodeTransformer &cb) @@ -38,10 +37,9 @@ void ChainExpression::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ChainExpression"}, {"expression", expression_}}); } -void ChainExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ChainExpression::Compile(compiler::PandaGen *pg) const { - compiler::OptionalChain chain(pg, this); - expression_->Compile(pg); + pg->GetAstCompiler()->Compile(this); } void ChainExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg &obj_reg) const @@ -57,14 +55,19 @@ void ChainExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg &obj_r } } -checker::Type *ChainExpression::Check([[maybe_unused]] checker::TSChecker *checker) +void ChainExpression::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} + +checker::Type *ChainExpression::Check(checker::TSChecker *checker) { - return expression_->Check(checker); + return checker->GetAnalyzer()->Check(this); } -checker::Type *ChainExpression::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ChainExpression::Check(checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/expressions/chainExpression.h b/ir/expressions/chainExpression.h index 252ece9500b899c32839e359942a82ab5d196360..a54d632b09a4af969e1f47beb4339cfc12575226 100644 --- a/ir/expressions/chainExpression.h +++ b/ir/expressions/chainExpression.h @@ -19,6 +19,10 @@ #include "ir/expression.h" #include "ir/irnode.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class ChainExpression : public Expression { public: @@ -33,6 +37,9 @@ public: { } + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class checker::TSAnalyzer; + const Expression *GetExpression() const noexcept { return expression_; @@ -44,10 +51,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(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; void CompileToReg(compiler::PandaGen *pg, compiler::VReg &obj_reg) const; - 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: Expression *expression_; diff --git a/ir/expressions/classExpression.cpp b/ir/expressions/classExpression.cpp index ecefc3a8faa98819a7af97b4a76a6d32be7280c2..394625e52d007dc35002fa03fd2b6bf60bb6784a 100644 --- a/ir/expressions/classExpression.cpp +++ b/ir/expressions/classExpression.cpp @@ -15,8 +15,10 @@ #include "classExpression.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" -#include "ir/base/classDefinition.h" namespace panda::es2panda::ir { void ClassExpression::TransformChildren(const NodeTransformer &cb) @@ -34,18 +36,23 @@ void ClassExpression::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "ClassExpression"}, {"definition", def_}}); } -void ClassExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const +void ClassExpression::Compile(compiler::PandaGen *pg) const { - def_->Compile(pg); + pg->GetAstCompiler()->Compile(this); } -checker::Type *ClassExpression::Check([[maybe_unused]] checker::TSChecker *checker) +void ClassExpression::Compile(compiler::ETSGen *etsg) const { - return nullptr; + etsg->GetAstCompiler()->Compile(this); } -checker::Type *ClassExpression::Check([[maybe_unused]] checker::ETSChecker *checker) +checker::Type *ClassExpression::Check(checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); +} + +checker::Type *ClassExpression::Check(checker::ETSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/expressions/classExpression.h b/ir/expressions/classExpression.h index 1ab8e4a09cef41e69b7982dadee50bf1f4d269bb..168b3fac747cc97d0f71c742626e6c80299418d8 100644 --- a/ir/expressions/classExpression.h +++ b/ir/expressions/classExpression.h @@ -33,9 +33,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: ClassDefinition *def_; diff --git a/ir/expressions/conditionalExpression.cpp b/ir/expressions/conditionalExpression.cpp index a45322d47678e3062c0ba9d73ef7748ac793a442..4fbdda8aa2985b1e1b517af3607ae9e240daa449 100644 --- a/ir/expressions/conditionalExpression.cpp +++ b/ir/expressions/conditionalExpression.cpp @@ -15,10 +15,9 @@ #include "conditionalExpression.h" -#include "compiler/base/condition.h" +#include "checker/TSchecker.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" -#include "checker/TSchecker.h" #include "ir/astDump.h" namespace panda::es2panda::ir { @@ -42,107 +41,24 @@ void ConditionalExpression::Dump(ir::AstDumper *dumper) const {{"type", "ConditionalExpression"}, {"test", test_}, {"consequent", consequent_}, {"alternate", alternate_}}); } -template -void CompileImpl(const ConditionalExpression *self, CodeGen *cg) -{ - auto *false_label = cg->AllocLabel(); - auto *end_label = cg->AllocLabel(); - - compiler::Condition::Compile(cg, self->Test(), false_label); - self->Consequent()->Compile(cg); - cg->Branch(self, end_label); - cg->SetLabel(self, false_label); - self->Alternate()->Compile(cg); - cg->SetLabel(self, end_label); -} - void ConditionalExpression::Compile(compiler::PandaGen *pg) const { - CompileImpl(this, pg); + pg->GetAstCompiler()->Compile(this); } void ConditionalExpression::Compile(compiler::ETSGen *etsg) const { - auto *false_label = etsg->AllocLabel(); - auto *end_label = etsg->AllocLabel(); - - compiler::Condition::Compile(etsg, Test(), false_label); - Consequent()->Compile(etsg); - etsg->ApplyConversion(Consequent()); - etsg->Branch(this, end_label); - etsg->SetLabel(this, false_label); - Alternate()->Compile(etsg); - etsg->ApplyConversion(Alternate()); - etsg->SetLabel(this, end_label); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ConditionalExpression::Check(checker::TSChecker *checker) { - checker::Type *test_type = test_->Check(checker); - - checker->CheckTruthinessOfType(test_type, test_->Start()); - checker->CheckTestingKnownTruthyCallableOrAwaitableType(test_, test_type, consequent_); - - checker::Type *consequent_type = consequent_->Check(checker); - checker::Type *alternate_type = alternate_->Check(checker); - - return checker->CreateUnionType({consequent_type, alternate_type}); + return checker->GetAnalyzer()->Check(this); } checker::Type *ConditionalExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - checker->CheckTruthinessOfType(test_); - - checker::Type *consequent_type = consequent_->Check(checker); - checker::Type *alternate_type = alternate_->Check(checker); - - auto *primitive_consequent_type = checker->ETSBuiltinTypeAsPrimitiveType(consequent_type); - auto *primitive_alter_type = checker->ETSBuiltinTypeAsPrimitiveType(alternate_type); - - if (primitive_consequent_type != nullptr && primitive_alter_type != nullptr) { - if (checker->IsTypeIdenticalTo(consequent_type, alternate_type)) { - SetTsType(checker->GetNonConstantTypeFromPrimitiveType(consequent_type)); - } else if (checker->IsTypeIdenticalTo(primitive_consequent_type, primitive_alter_type)) { - checker->FlagExpressionWithUnboxing(consequent_->TsType(), primitive_consequent_type, consequent_); - checker->FlagExpressionWithUnboxing(alternate_->TsType(), primitive_alter_type, alternate_); - - SetTsType(primitive_consequent_type); - } else if (primitive_consequent_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC) && - primitive_alter_type->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { - checker->FlagExpressionWithUnboxing(consequent_->TsType(), primitive_consequent_type, consequent_); - checker->FlagExpressionWithUnboxing(alternate_->TsType(), primitive_alter_type, alternate_); - - SetTsType( - checker->ApplyConditionalOperatorPromotion(checker, primitive_consequent_type, primitive_alter_type)); - } else { - checker->ThrowTypeError("Type error", this->range_.start); - } - } else { - if (!(consequent_type->IsETSArrayType() || alternate_type->IsETSArrayType()) && - !(consequent_type->IsETSObjectType() && alternate_type->IsETSObjectType())) { - checker->ThrowTypeError("Type error", this->range_.start); - } else { - checker->Relation()->SetNode(consequent_); - auto builtin_conseq_type = checker->PrimitiveTypeAsETSBuiltinType(consequent_type); - auto builtin_alternate_type = checker->PrimitiveTypeAsETSBuiltinType(alternate_type); - - if (builtin_conseq_type == nullptr) { - builtin_conseq_type = consequent_type; - } - - if (builtin_alternate_type == nullptr) { - builtin_alternate_type = alternate_type; - } - - SetTsType(checker->FindLeastUpperBound(builtin_conseq_type, builtin_alternate_type)); - } - } - - return TsType(); + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ir/expressions/conditionalExpression.h b/ir/expressions/conditionalExpression.h index 8e0e7a10d8f05320189fb05223dedd18f2d73dea..77ca2e883e915e9f8854caef60ae4a70f26f47c7 100644 --- a/ir/expressions/conditionalExpression.h +++ b/ir/expressions/conditionalExpression.h @@ -18,6 +18,11 @@ #include "ir/expression.h" +namespace panda::es2panda::checker { +class TSAnalyzer; +class ETSAnalyzer; +} // namespace panda::es2panda::checker + namespace panda::es2panda::ir { class ConditionalExpression : public Expression { public: @@ -32,6 +37,10 @@ public: { } + // TODO (csabahurton): these friend relationships can be removed once there are getters for private fields + friend class checker::TSAnalyzer; + friend class checker::ETSAnalyzer; + [[nodiscard]] const Expression *Test() const noexcept { return test_; diff --git a/ir/expressions/directEvalExpression.cpp b/ir/expressions/directEvalExpression.cpp index 356301fb15468c40b02608ae59669c30bd649465..95cb09e36775f8723343bdc0e58c9e2184637a57 100644 --- a/ir/expressions/directEvalExpression.cpp +++ b/ir/expressions/directEvalExpression.cpp @@ -17,36 +17,29 @@ #include "directEvalExpression.h" -#include "util/helpers.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" -#include "compiler/core/regScope.h" namespace panda::es2panda::ir { void DirectEvalExpression::Compile(compiler::PandaGen *pg) const { - if (arguments_.empty()) { - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - return; - } - - compiler::RegScope rs(pg); - bool contains_spread = util::Helpers::ContainSpreadElement(arguments_); - if (contains_spread) { - [[maybe_unused]] compiler::VReg args_obj = CreateSpreadArguments(pg); - pg->LoadObjByIndex(this, 0); - } else { - compiler::VReg arg0 = pg->AllocReg(); - auto iter = arguments_.cbegin(); - (*iter++)->Compile(pg); - pg->StoreAccumulator(this, arg0); - - while (iter != arguments_.cend()) { - (*iter++)->Compile(pg); - } - - pg->LoadAccumulator(this, arg0); - } - - pg->DirectEval(this, parser_status_); + pg->GetAstCompiler()->Compile(this); } + +void DirectEvalExpression::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} + +checker::Type *DirectEvalExpression::Check(checker::TSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); +} + +checker::Type *DirectEvalExpression::Check(checker::ETSChecker *checker) +{ + return checker->GetAnalyzer()->Check(this); +} + } // namespace panda::es2panda::ir diff --git a/ir/expressions/directEvalExpression.h b/ir/expressions/directEvalExpression.h index 0e62ab53e8d1ec79b74e4b868823fd62b44997aa..956f0a9ff1a29acd0959a6cf4b5e3a2fa7658c93 100644 --- a/ir/expressions/directEvalExpression.h +++ b/ir/expressions/directEvalExpression.h @@ -16,6 +16,10 @@ #ifndef ES2PANDA_IR_EXPRESSION_DIRECT_EVAL_H #define ES2PANDA_IR_EXPRESSION_DIRECT_EVAL_H +namespace panda::es2panda::compiler { +class JSCompiler; +} // namespace panda::es2panda::compiler + #include "ir/expressions/callExpression.h" namespace panda::es2panda::ir { @@ -28,7 +32,13 @@ public: type_ = AstNodeType::DIRECT_EVAL; } - void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + // TODO (csabahurton): friend relationship can be removed once there are getters for private fields + friend class compiler::JSCompiler; + + 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: uint32_t parser_status_ {}; diff --git a/ir/expressions/functionExpression.cpp b/ir/expressions/functionExpression.cpp index 6800ae4017f7e7c745fa1d14e317beaa631063b3..01f1f4b68c92b1eaf2c57e34f6e3e9bce512f835 100644 --- a/ir/expressions/functionExpression.cpp +++ b/ir/expressions/functionExpression.cpp @@ -15,13 +15,10 @@ #include "functionExpression.h" -#include "compiler/core/pandagen.h" -#include "compiler/core/ETSGen.h" #include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" -#include "ir/base/scriptFunction.h" -#include "ir/expressions/identifier.h" -#include "ir/statements/variableDeclarator.h" namespace panda::es2panda::ir { void FunctionExpression::TransformChildren(const NodeTransformer &cb) @@ -41,45 +38,21 @@ void FunctionExpression::Dump(ir::AstDumper *dumper) const void FunctionExpression::Compile(compiler::PandaGen *pg) const { - pg->DefineFunction(func_, func_, func_->Scope()->InternalName()); + pg->GetAstCompiler()->Compile(this); } -void FunctionExpression::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +void FunctionExpression::Compile(compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } checker::Type *FunctionExpression::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_)); - - func_->Body()->Check(checker); - - return func_type; + return checker->GetAnalyzer()->Check(this); } checker::Type *FunctionExpression::Check([[maybe_unused]] checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ir/expressions/functionExpression.h b/ir/expressions/functionExpression.h index 4b2191857876d40e841c41400d5ce63f5f3de283..1e858ac12a74374f74dee572122095b8b04474d1 100644 --- a/ir/expressions/functionExpression.h +++ b/ir/expressions/functionExpression.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(compiler::PandaGen *pg) const override; void Compile(compiler::ETSGen *etsg) const override; - checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; - checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + checker::Type *Check(checker::TSChecker *checker) override; + checker::Type *Check(checker::ETSChecker *checker) override; private: ScriptFunction *func_; diff --git a/ir/statements/tryStatement.h b/ir/statements/tryStatement.h index 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); +}