From 61cd9d6a42e39b06a660ba053b3333f16d78e60b Mon Sep 17 00:00:00 2001 From: daizihan Date: Wed, 4 Jun 2025 20:17:02 +0800 Subject: [PATCH] Support gradula type and any type in bc Description: 1. support gradual type, all the types except escompat & arkts1.2 type in the dynamic Declaration file are treated as gradual type. fe type checking is enabled. 2. add lowering to transfrom gradual type to any type, and emit any type related bc Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICE0RF?from=project-issue Signed-off-by: xuxinjie4 Signed-off-by: daizihan --- ets2panda/BUILD.gn | 3 + ets2panda/CMakeLists.txt | 3 + ets2panda/ast_verifier/helpers.cpp | 53 +-- .../invariants/importExportAccessValid.cpp | 7 - ets2panda/checker/ETSAnalyzer.cpp | 91 +++-- ets2panda/checker/ETSAnalyzerHelpers.cpp | 2 +- ets2panda/checker/ETSchecker.cpp | 15 +- ets2panda/checker/ETSchecker.h | 6 + ets2panda/checker/checker.cpp | 5 + ets2panda/checker/checker.h | 2 + ets2panda/checker/ets/assignAnalyzer.cpp | 4 + ets2panda/checker/ets/function.cpp | 25 +- ets2panda/checker/ets/function_helpers.h | 5 +- ets2panda/checker/ets/helpers.cpp | 48 ++- ets2panda/checker/ets/object.cpp | 33 +- ets2panda/checker/ets/typeCheckingHelpers.cpp | 42 ++- ets2panda/checker/ets/typeCreation.cpp | 51 ++- ets2panda/checker/ets/typeRelationContext.cpp | 1 - ets2panda/checker/ets/utilityTypeHandlers.cpp | 11 +- ets2panda/checker/types/ets/etsAnyType.cpp | 2 +- ets2panda/checker/types/ets/etsAnyType.h | 9 + ets2panda/checker/types/ets/etsObjectType.cpp | 4 +- .../types/ets/etsObjectTypeConstants.h | 1 + ets2panda/checker/types/ets/etsUnionType.cpp | 2 +- ets2panda/checker/types/globalTypesHolder.cpp | 6 + ets2panda/checker/types/globalTypesHolder.h | 2 + ets2panda/checker/types/gradualType.cpp | 101 ++++++ ets2panda/checker/types/gradualType.h | 89 +++++ ets2panda/checker/types/signature.cpp | 2 +- ets2panda/checker/types/type.cpp | 10 + ets2panda/checker/types/type.h | 5 + ets2panda/checker/types/typeFlag.h | 5 +- ets2panda/checker/types/typeMapping.h | 1 + ets2panda/compiler/base/lreference.cpp | 23 +- ets2panda/compiler/core/ETSCompiler.cpp | 106 +++++- ets2panda/compiler/core/ETSCompiler.h | 2 + .../compiler/core/ETSCompilerUnrechable.cpp | 5 - ets2panda/compiler/core/ETSGen.cpp | 69 ++++ ets2panda/compiler/core/ETSGen.h | 101 +++++- ets2panda/compiler/core/ETSemitter.cpp | 13 +- ets2panda/compiler/core/ETSfunction.cpp | 8 +- .../compiler/lowering/ets/dynamicImport.cpp | 251 ++++++++++++++ .../compiler/lowering/ets/dynamicImport.h | 35 ++ .../lowering/ets/gradualTypeNarrowing.cpp | 140 ++++++++ .../lowering/ets/gradualTypeNarrowing.h | 43 +++ .../ets/interfaceObjectLiteralLowering.cpp | 12 +- .../compiler/lowering/ets/lambdaLowering.cpp | 3 +- .../lowering/ets/localClassLowering.cpp | 3 + .../lowering/ets/objectIndexAccess.cpp | 24 +- .../lowering/ets/objectLiteralLowering.cpp | 6 +- .../ets/optionalArgumentsLowering.cpp | 7 - .../lowering/ets/restArgsLowering.cpp | 4 +- .../compiler/lowering/ets/unboxLowering.cpp | 34 +- ets2panda/compiler/lowering/phase.cpp | 4 + ets2panda/compiler/lowering/util.cpp | 9 +- ets2panda/compiler/scripts/signatures.yaml | 2 + ets2panda/declgen_ets2ts/declgenEts2Ts.cpp | 17 +- ets2panda/ir/base/classDefinition.h | 11 + ets2panda/ir/ets/etsTypeReference.cpp | 18 +- ets2panda/ir/ets/etsTypeReferencePart.cpp | 22 +- ets2panda/ir/ets/etsTypeReferencePart.h | 1 + ets2panda/ir/expressions/callExpression.cpp | 7 + ets2panda/ir/expressions/callExpression.h | 2 + ets2panda/ir/expressions/memberExpression.cpp | 31 ++ ets2panda/ir/expressions/memberExpression.h | 1 + ets2panda/ir/ts/tsTypeReference.cpp | 16 +- ets2panda/lsp/src/isolated_declaration.cpp | 2 +- ets2panda/parser/program/program.h | 5 + ets2panda/test/unit/CMakeLists.txt | 1 + .../test/unit/any_ins_test/CMakeLists.txt | 17 + .../test/unit/any_ins_test/any_ins_test.cpp | 312 ++++++++++++++++++ ets2panda/test/utils/asm_test.cpp | 2 +- ets2panda/test/utils/checker_test.h | 95 ++++++ ets2panda/util/diagnostic/semantic.yaml | 4 + ets2panda/util/importPathManager.cpp | 2 +- ets2panda/util/importPathManager.h | 2 + ets2panda/varbinder/variableFlags.h | 1 + 77 files changed, 1898 insertions(+), 221 deletions(-) create mode 100644 ets2panda/checker/types/gradualType.cpp create mode 100644 ets2panda/checker/types/gradualType.h create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.cpp create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.h create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h create mode 100644 ets2panda/test/unit/any_ins_test/CMakeLists.txt create mode 100644 ets2panda/test/unit/any_ins_test/any_ins_test.cpp diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 5a788f0982..92680ad14a 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -130,6 +130,7 @@ libes2panda_sources = [ "checker/types/ets/etsAnyType.cpp", "checker/types/ets/wildcardType.cpp", "checker/types/globalTypesHolder.cpp", + "checker/types/gradualType.cpp", "checker/types/signature.cpp", "checker/types/ts/anyType.cpp", "checker/types/ts/arrayType.cpp", @@ -224,11 +225,13 @@ libes2panda_sources = [ "compiler/lowering/ets/expressionLambdaLowering.cpp", "compiler/lowering/ets/extensionAccessorLowering.cpp", "compiler/lowering/ets/genericBridgesLowering.cpp", + "compiler/lowering/ets/gradualTypeNarrowing.cpp", "compiler/lowering/ets/insertOptionalParametersAnnotation.cpp", "compiler/lowering/ets/interfaceObjectLiteralLowering.cpp", "compiler/lowering/ets/interfacePropertyDeclarations.cpp", "compiler/lowering/ets/lateInitialization.cpp", "compiler/lowering/ets/lambdaLowering.cpp", + "compiler/lowering/ets/dynamicImport.cpp", "compiler/lowering/ets/localClassLowering.cpp", "compiler/lowering/ets/objectIndexAccess.cpp", "compiler/lowering/ets/objectIterator.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 2143314080..7719bd2968 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -276,6 +276,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/expressionLambdaLowering.cpp compiler/lowering/ets/extensionAccessorLowering.cpp compiler/lowering/ets/genericBridgesLowering.cpp + compiler/lowering/ets/gradualTypeNarrowing.cpp compiler/lowering/ets/arrayLiteralLowering.cpp compiler/lowering/ets/boxingForLocals.cpp compiler/lowering/ets/capturedVariables.cpp @@ -288,6 +289,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/exportAnonymousConst.cpp compiler/lowering/ets/lateInitialization.cpp compiler/lowering/ets/lambdaLowering.cpp + compiler/lowering/ets/dynamicImport.cpp compiler/lowering/ets/restTupleLowering.cpp compiler/lowering/ets/spreadLowering.cpp compiler/lowering/ets/localClassLowering.cpp @@ -574,6 +576,7 @@ set(ES2PANDA_LIB_SRC checker/types/type.cpp checker/types/typeRelation.cpp checker/types/globalTypesHolder.cpp + checker/types/gradualType.cpp checker/types/ets/byteType.cpp checker/types/ets/charType.cpp checker/types/ets/doubleType.cpp diff --git a/ets2panda/ast_verifier/helpers.cpp b/ets2panda/ast_verifier/helpers.cpp index d8a9998848..a2179fc411 100644 --- a/ets2panda/ast_verifier/helpers.cpp +++ b/ets2panda/ast_verifier/helpers.cpp @@ -19,6 +19,7 @@ #include "checker/types/type.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsUnionType.h" +#include "checker/types/gradualType.h" #include "ir/statements/blockStatement.h" #include "ir/ets/etsModule.h" #include "parser/program/program.h" @@ -50,17 +51,17 @@ bool IsBooleanType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); + return type->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || type->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); } bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) @@ -74,29 +75,29 @@ bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); if (IsBooleanType(ast)) { return isBitwise; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { return true; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && - !typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && + !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); + return type->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || + type->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || type->HasTypeFlag(checker::TypeFlag::BIGINT) || + type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); } bool IsStringType(const ir::AstNode *ast) @@ -110,17 +111,18 @@ bool IsStringType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE); + return type->HasTypeFlag(checker::TypeFlag::STRING_LIKE); } bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode) @@ -154,7 +156,8 @@ const checker::Type *GetClassDefinitionType(const ir::AstNode *ast) return nullptr; } auto *classDefinition = tmpNode->AsClassDefinition(); - return classDefinition->TsType(); + return classDefinition->TsType()->IsGradualType() ? classDefinition->TsType()->AsGradualType()->GetBaseType() + : classDefinition->TsType(); } const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) @@ -167,7 +170,9 @@ const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) return nullptr; } auto *tsInterfaceDeclaration = tmpNode->AsTSInterfaceDeclaration(); - return tsInterfaceDeclaration->TsType(); + return tsInterfaceDeclaration->TsType()->IsGradualType() + ? tsInterfaceDeclaration->TsType()->AsGradualType()->GetBaseType() + : tsInterfaceDeclaration->TsType(); } bool ValidateMethodAccessForClass(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode, @@ -323,8 +328,8 @@ bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir } if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && memberObjType->SuperType() != nullptr && - memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | - checker::ETSObjectFlags::GLOBAL)) { + memberObjType->SuperType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | + checker::ETSObjectFlags::GLOBAL)) { return true; } const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode(); diff --git a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index bf7351956a..6d6ff35487 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -78,13 +78,6 @@ bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_s const ir::AstNode *callExpr, util::StringView name) { auto *signature = callExpr->AsCallExpression()->Signature(); - if (signature == nullptr || signature->Owner() == nullptr) { - // NOTE(vpukhov): Add a synthetic owner for dynamic signatures - ES2PANDA_ASSERT( - callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)); - return true; - } - if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr && !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) && varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) { diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 5028e1770e..ca365e2a61 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -19,10 +19,14 @@ #include "generated/diagnostic.h" #include "checker/types/globalTypesHolder.h" #include "checker/types/ets/etsTupleType.h" +#include "checker/types/gradualType.h" #include "evaluate/scopedDebugInfoPlugin.h" #include "types/signature.h" #include "compiler/lowering/ets/setJumpTarget.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "types/ts/nullType.h" +#include "types/type.h" +#include "types/typeFlag.h" #include "util/es2pandaMacros.h" #include @@ -318,7 +322,8 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const } ETSChecker *checker = GetETSChecker(); - Type *exprType = expr->AsSpreadElement()->Argument()->Check(checker); + auto type = expr->AsSpreadElement()->Argument()->Check(checker); + Type *exprType = type->MaybeBaseTypeOfGradualType(); if (exprType->IsETSResizableArrayType()) { return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front()); @@ -332,7 +337,7 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const return checker->InvalidateType(expr); } - checker::Type *const elementType = exprType->IsETSTupleType() ? exprType : checker->GetElementTypeOfArray(exprType); + checker::Type *const elementType = exprType->IsETSTupleType() ? type : checker->GetElementTypeOfArray(exprType); return expr->SetTsType(elementType); } @@ -398,7 +403,7 @@ static bool CheckArrayElementType(ETSChecker *checker, T *newArrayInstanceExpr) ES2PANDA_ASSERT(checker != nullptr); ES2PANDA_ASSERT(newArrayInstanceExpr != nullptr); - checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker); + checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker)->MaybeBaseTypeOfGradualType(); if (elementType->IsETSPrimitiveType()) { return true; } @@ -467,7 +472,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewClassInstanceExpression *expr) { - checker::Type *calleeType = expr->GetTypeRef()->Check(checker); + checker::Type *res = expr->GetTypeRef()->Check(checker); + auto calleeType = res->MaybeBaseTypeOfGradualType(); if (calleeType->IsTypeError()) { return checker->InvalidateType(expr->GetTypeRef()); } @@ -498,7 +504,7 @@ static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewCl return checker->GlobalTypeError(); } - return calleeType; + return res; } checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const @@ -511,8 +517,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const if (calleeType->IsTypeError()) { return checker->InvalidateType(expr); } - auto *calleeObj = calleeType->AsETSObjectType(); - expr->SetTsType(calleeObj); + auto *calleeObj = calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + expr->SetTsType(calleeType); auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); @@ -618,11 +624,12 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNonNullishTypeNode *node) const return node->TsType(); } ETSChecker *checker = GetETSChecker(); - checker::Type *originalType = node->GetTypeNode()->Check(checker); + auto type = node->GetTypeNode()->Check(checker); + checker::Type *originalType = type->MaybeBaseTypeOfGradualType(); if (!originalType->IsETSTypeParameter()) { checker->LogError(diagnostic::ILLEGAL_NON_NULLISH_TYPE, {}, node->GetTypeNode()->Start()); } - return node->SetTsType(checker->GetNonNullishType(originalType)); + return node->SetTsType(checker->GetNonNullishType(type)); } checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const @@ -670,7 +677,7 @@ static void AddSpreadElementTypes(ETSChecker *checker, ir::SpreadElement *const return; } - Type *const spreadArgumentType = element->Argument()->TsType(); + Type *const spreadArgumentType = element->Argument()->TsType()->MaybeBaseTypeOfGradualType(); if (spreadArgumentType->IsETSTupleType()) { for (Type *type : spreadArgumentType->AsETSTupleType()->GetTupleTypesList()) { @@ -1111,7 +1118,10 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const CastPossibleTupleOnRHS(checker, expr); checker::Type *smartType = rightType; - if (!leftType->IsTypeError()) { + auto isLazyImportObject = + leftType->MaybeBaseTypeOfGradualType()->IsETSObjectType() && + leftType->MaybeBaseTypeOfGradualType()->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT); + if (!leftType->IsTypeError() && !isLazyImportObject) { if (const auto ctx = checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType, expr->Right()->Start(), {{diagnostic::INVALID_ASSIGNMNENT, {rightType, leftType}}}); @@ -1136,6 +1146,13 @@ static checker::Type *HandleSubstitution(ETSChecker *checker, ir::AssignmentExpr checker->TryInferTypeForLambdaTypeAlias(expr->Right()->AsArrowFunctionExpression(), preferredType->AsETSFunctionType()); } + } else if (expr->Right()->IsObjectExpression()) { + if (leftType->IsETSObjectType() && leftType->IsGradualType() && + (leftType->HasTypeFlag(TypeFlag::READONLY) || + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::REQUIRED))) { + checker->LogError(diagnostic::DYMANIC_INIT_WITH_OBJEXPR, {leftType}, expr->Right()->Start()); + } + expr->Right()->AsObjectExpression()->SetPreferredType(leftType); } else { expr->Right()->SetPreferredType(leftType); } @@ -1358,7 +1375,7 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE static ETSObjectType *GetCallExpressionCalleeObject(ETSChecker *checker, ir::CallExpression *expr, Type *calleeType) { if (expr->IsETSConstructorCall()) { - return calleeType->AsETSObjectType(); + return calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } auto callee = expr->Callee(); if (callee->IsMemberExpression()) { @@ -1421,10 +1438,15 @@ static void CheckAbstractCall(ETSChecker *checker, ir::CallExpression *expr) static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr) { checker->CheckNonNullish(expr->Callee()); - if (expr->Callee()->IsMemberExpression() && expr->Callee()->AsMemberExpression()->Object() != nullptr && - expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSObjectType() && - expr->Callee()->AsMemberExpression()->Object()->TsType()->AsETSObjectType()->HasObjectFlag( - ETSObjectFlags::READONLY)) { + if (!expr->Callee()->IsMemberExpression()) { + return; + } + auto memberExpr = expr->Callee()->AsMemberExpression(); + if (memberExpr->Object() == nullptr) { + return; + } + auto baseType = memberExpr->Object()->TsType()->MaybeBaseTypeOfGradualType(); + if (baseType->IsETSObjectType() && baseType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::READONLY)) { checker->LogError(diagnostic::READONLY_CALL, {}, expr->Start()); expr->SetTsType(checker->GlobalTypeError()); } @@ -1640,7 +1662,9 @@ checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression *const use, Type *type) { ES2PANDA_ASSERT(use->IsIdentifier() || use->IsMemberExpression()); - if (!type->IsETSMethodType()) { + if (!type->IsETSMethodType() || + (use->IsMemberExpression() && use->AsMemberExpression()->PropVar() != nullptr && + use->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return type; } auto const getUseSite = [use]() { @@ -1747,6 +1771,10 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke return checker->InvalidateType(expr); } + if (baseType->IsGradualType()) { + return ResolveMemberExpressionByBaseType(checker, baseType->AsGradualType()->GetBaseType(), expr); + } + if (baseType->IsETSArrayType()) { if (expr->Property()->AsIdentifier()->Name().Is("length")) { return expr->AdjustType(checker, checker->GlobalIntBuiltinType()); @@ -1785,8 +1813,7 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke "toFloat", "toDouble", }}; - auto method = expr->Property()->AsIdentifier()->Name().Utf8(); - auto res = std::find(castMethods.begin(), castMethods.end(), method); + auto res = std::find(castMethods.begin(), castMethods.end(), expr->Property()->AsIdentifier()->Name().Utf8()); if (res != castMethods.end()) { auto type = checker->MaybeBoxType(baseType); expr->SetAstNodeFlags(ir::AstNodeFlags::TMP_CONVERT_PRIMITIVE_CAST_METHOD_CALL); @@ -2171,8 +2198,9 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check std::vector candidateObjectTypes; // Phase 1: Gather all ETSObjectTypes from the union for (auto *constituentType : unionType->ConstituentTypes()) { - if (constituentType->IsETSObjectType()) { - candidateObjectTypes.push_back(constituentType->AsETSObjectType()); + auto type = constituentType->MaybeBaseTypeOfGradualType(); + if (type->IsETSObjectType()) { + candidateObjectTypes.push_back(type->AsETSObjectType()); } } @@ -3015,7 +3043,7 @@ checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point. auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt); - checker::Type *const exprType = st->Right()->Check(checker); + checker::Type *const exprType = st->Right()->Check(checker)->MaybeBaseTypeOfGradualType(); if (exprType == nullptr) { checker->LogError(diagnostic::FOROF_CANT_INFER_SOURCE, {}, st->Right()->Start()); return checker->GlobalTypeError(); @@ -3181,15 +3209,16 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S // Case when function's return type is defined explicitly: if (st->argument_ == nullptr) { - if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() && - !funcReturnType->IsETSAsyncFuncReturnType()) { + if (!funcReturnType->MaybeBaseTypeOfGradualType()->IsETSVoidType() && + funcReturnType != checker->GlobalVoidType() && + !funcReturnType->MaybeBaseTypeOfGradualType()->IsETSAsyncFuncReturnType()) { checker->LogError(diagnostic::RETURN_WITHOUT_VALUE, {}, st->Start()); return false; } funcReturnType = checker->GlobalVoidType(); } else { const auto name = containingFunc->Scope()->InternalName().Mutf8(); - if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) { + if (!CheckArgumentVoidType(funcReturnType->MaybeBaseTypeOfGradualType(), checker, name, st)) { return false; } @@ -3201,7 +3230,8 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S } checker::Type *argumentType = st->argument_->Check(checker); - return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc); + return CheckReturnType(checker, funcReturnType->MaybeBaseTypeOfGradualType(), argumentType, st->argument_, + containingFunc); } return true; } @@ -3554,13 +3584,14 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const return st->SetTsType(stmtType); } - auto *interfaceType = stmtType->AsETSObjectType(); + auto *interfaceType = stmtType->IsGradualType() ? stmtType->AsGradualType()->GetBaseType()->AsETSObjectType() + : stmtType->AsETSObjectType(); checker->CheckInterfaceAnnotations(st); interfaceType->SetSuperType(checker->GlobalETSObjectType()); checker->CheckInvokeMethodsLegitimacy(interfaceType); - st->SetTsType(interfaceType); + st->SetTsType(stmtType); checker::ScopeContext scopeCtx(checker, st->Scope()); auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType); @@ -3602,7 +3633,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSNonNullExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const { ETSChecker *checker = GetETSChecker(); - checker::Type *baseType = expr->Left()->Check(checker); + checker::Type *baseType = expr->Left()->Check(checker)->MaybeBaseTypeOfGradualType(); if (baseType->IsETSObjectType()) { // clang-format off auto searchName = expr->Right()->Name(); @@ -3612,7 +3643,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const searchName = expr->Right()->Name(); } varbinder::Variable *prop = - baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_DECL); + baseType->AsETSObjectType()->GetProperty(searchName, PropertySearchFlags::SEARCH_DECL); // NOTE(dslynko): in debugger evaluation mode must lazily generate module's properties here. if (prop == nullptr) { checker->LogError(diagnostic::NONEXISTENT_TYPE, {expr->Right()->Name()}, expr->Right()->Start()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index 2a5443ae23..7bf8a4136f 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -306,7 +306,7 @@ static bool HasIteratorInterface(ETSObjectType const *const objectType) void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc, const lexer::SourcePosition &position, const std::string &methodName) { - const auto *returnType = scriptFunc->Signature()->ReturnType(); + const auto *returnType = scriptFunc->Signature()->ReturnType()->MaybeBaseTypeOfGradualType(); if (returnType == nullptr) { checker->LogError(diagnostic::MISSING_RETURN_TYPE_2, {util::StringView(methodName)}, position); diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index 36830c6155..8d5541159a 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -25,6 +25,8 @@ #include "ir/expressions/callExpression.h" #include "ir/ts/tsInterfaceDeclaration.h" #include "ir/statements/blockStatement.h" +#include "types/type.h" +#include "utils/arena_containers.h" #include "varbinder/ETSBinder.h" #include "parser/program/program.h" #include "checker/ets/aliveAnalyzer.h" @@ -34,6 +36,7 @@ #include "ir/base/scriptFunction.h" #include "util/helpers.h" #include "evaluate/scopedDebugInfoPlugin.h" +#include "checker/types/ets/etsTupleType.h" namespace ark::es2panda::checker { @@ -418,16 +421,13 @@ bool ETSChecker::IsClassStaticMethod(checker::ETSObjectType *objType, checker::S [[nodiscard]] TypeFlag ETSChecker::TypeKind(const Type *const type) noexcept { - // These types were not present in the ETS_TYPE list. Some of them are omited intentionally, other are just bugs + // These types were not present in the ETS_TYPE list. Some of them are omitted intentionally, other are just bugs static constexpr auto TO_CLEAR = TypeFlag::CONSTANT | TypeFlag::GENERIC | TypeFlag::ETS_INT_ENUM | TypeFlag::ETS_STRING_ENUM | TypeFlag::READONLY | TypeFlag::BIGINT_LITERAL | TypeFlag::ETS_TYPE_ALIAS | TypeFlag::TYPE_ERROR; - // Bugs: these types do not appear as a valid TypeKind, as the TypeKind has more then one bit set - [[maybe_unused]] static constexpr auto NOT_A_TYPE_KIND = TypeFlag::ETS_DYNAMIC_FLAG; - auto res = static_cast(type->TypeFlags() & ~(TO_CLEAR)); - ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(NOT_A_TYPE_KIND)), + ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(TypeFlag::NONE)), ark::es2panda::GetPositionForDiagnostic()); return res; } @@ -538,6 +538,11 @@ Type *ETSChecker::GlobalETSAnyType() const return GetGlobalTypesHolder()->GlobalETSAnyType(); } +Type *ETSChecker::GlobalETSRelaxedAnyType() const +{ + return GetGlobalTypesHolder()->GlobalETSRelaxedAnyType(); +} + Type *ETSChecker::GlobalETSNeverType() const { return GetGlobalTypesHolder()->GlobalETSNeverType(); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 978b0b2bee..ec155ee9ae 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -26,6 +26,7 @@ #include "checker/types/ets/types.h" #include "checker/resolveResult.h" #include "ir/visitor/AstVisitor.h" +#include "types/type.h" #include "util/helpers.h" namespace ark::es2panda::varbinder { @@ -79,6 +80,7 @@ using ConstraintCheckRecord = std::tuple *, const Subs using MaybeDiagnosticInfo = std::optional>; using AstNodePtr = ir::AstNode *; +using TypePtr = Type *; class ETSChecker final : public Checker { public: @@ -129,6 +131,7 @@ public: Type *GlobalETSNullType() const; Type *GlobalETSUndefinedType() const; Type *GlobalETSAnyType() const; + Type *GlobalETSRelaxedAnyType() const; Type *GlobalETSNeverType() const; Type *GlobalETSStringLiteralType() const; Type *GlobalETSBigIntType() const; @@ -310,6 +313,7 @@ public: ETSResizableArrayType *CreateETSMultiDimResizableArrayType(Type *element, size_t dimSize); ETSResizableArrayType *CreateETSResizableArrayType(Type *element); ETSArrayType *CreateETSArrayType(Type *elementType, bool isCachePolluting = false); + Type *CreateGradualType(Type *baseType, Language lang = Language(Language::Id::JS)); Type *CreateETSUnionType(Span constituentTypes); template Type *CreateETSUnionType(Type *const (&arr)[N]) // NOLINT(modernize-avoid-c-arrays) @@ -560,6 +564,7 @@ public: const util::StringView &importPath); void SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath, ir::ETSImportDeclaration *importDecl = nullptr); + parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, const util::StringView &importPath); void SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType); Type *GetReferencedTypeFromBase(Type *baseType, ir::Expression *name); Type *GetReferencedTypeBase(ir::Expression *name); @@ -1008,6 +1013,7 @@ private: void SetUpTypeParameterConstraint(ir::TSTypeParameter *param); ETSObjectType *UpdateGlobalType(ETSObjectType *objType, util::StringView name); + void WrapTypeNode(ir::AstNode *node); void CheckProgram(parser::Program *program, bool runAnalysis = false); void CheckWarnings(parser::Program *program, const util::Options &options); diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index 74c90e7452..ab4e17a2e0 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -164,6 +164,11 @@ bool Checker::IsAnyError() return DiagnosticEngine().IsAnyError(); } +bool Checker::IsDeclForDynamicStaticInterop() const +{ + return Program()->IsDeclForDynamicStaticInterop(); +} + ScopeContext::ScopeContext(Checker *checker, varbinder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_), prevProgram_(checker_->Program()) { diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index 5b849a4980..e3ae34e684 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -239,6 +239,8 @@ public: return programAllocator_ == nullptr ? allocator_ : programAllocator_; } + bool IsDeclForDynamicStaticInterop() const; + protected: parser::Program *Program() const; void SetProgram(parser::Program *program); diff --git a/ets2panda/checker/ets/assignAnalyzer.cpp b/ets2panda/checker/ets/assignAnalyzer.cpp index bbc3f6aa28..d5d379b49b 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -57,6 +57,7 @@ #include "varbinder/scope.h" #include "varbinder/declaration.h" #include "checker/ETSchecker.h" +#include "checker/types/gradualType.h" #include "ir/base/catchClause.h" #include "parser/program/program.h" #include "checker/types/ts/objectType.h" @@ -1351,6 +1352,9 @@ static bool IsDefaultValueType(const Type *type, bool isNonReadonlyField) if (type == nullptr) { return false; } + if (type->IsGradualType()) { + return IsDefaultValueType(type->AsGradualType()->GetBaseType(), isNonReadonlyField); + } return (type->IsETSPrimitiveType() || (type->IsETSObjectType() && type->AsETSObjectType()->IsBoxedPrimitive()) || type->IsETSNeverType() || type->IsETSUndefinedType() || type->IsETSNullType() || (type->PossiblyETSUndefined() && (!type->HasTypeFlag(checker::TypeFlag::GENERIC) || diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 22f1c94672..c2c1ed6974 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -23,6 +23,7 @@ #include "checker/ets/typeRelationContext.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/base/catchClause.h" #include "ir/base/classDefinition.h" @@ -82,8 +83,8 @@ bool ETSChecker::IsCompatibleTypeArgument(ETSTypeParameter *typeParam, Type *typ bool ETSChecker::EnhanceSubstitutionForReadonly(const ArenaVector &typeParams, ETSReadonlyType *paramType, Type *argumentType, Substitution *substitution) { - return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying(), GetReadonlyType(argumentType), - substitution); + return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying()->MaybeBaseTypeOfGradualType(), + GetReadonlyType(argumentType), substitution); } /* A very rough and imprecise partial type inference */ @@ -115,6 +116,10 @@ bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector &typeParam if (paramType->IsETSReadonlyType()) { return EnhanceSubstitutionForReadonly(typeParams, paramType->AsETSReadonlyType(), argumentType, substitution); } + if (paramType->IsGradualType()) { + return EnhanceSubstitutionForType(typeParams, paramType->AsGradualType()->GetBaseType(), argumentType, + substitution); + } if (paramType->IsETSUnionType()) { return EnhanceSubstitutionForUnion(typeParams, paramType->AsETSUnionType(), argumentType, substitution); } @@ -152,7 +157,8 @@ bool ETSChecker::EnhanceSubstitutionForUnion(const ArenaVector &typePara if (!argumentType->IsETSUnionType()) { bool foundValid = false; for (Type *ctype : paramUn->ConstituentTypes()) { - foundValid |= ValidateTypeSubstitution(typeParams, ctype, argumentType, substitution); + foundValid |= + ValidateTypeSubstitution(typeParams, ctype->MaybeBaseTypeOfGradualType(), argumentType, substitution); } return foundValid; } @@ -433,7 +439,8 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, auto &argument = arguments[index]; // #22952: infer optional parameter heuristics - auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + auto const paramType = + GetNonNullishType(substitutedSig->Params()[index]->TsType())->MaybeBaseTypeOfGradualType(); if (argument->IsObjectExpression()) { if (!paramType->IsETSObjectType()) { return false; @@ -955,8 +962,9 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector static Type *GetParameterTypeOrRestAtIdx(checker::ETSChecker *checker, Signature *sig, const size_t idx) { - return idx < sig->ArgCount() ? sig->Params().at(idx)->TsType() - : checker->GetElementTypeOfArray(sig->RestVar()->TsType()); + return idx < sig->ArgCount() + ? sig->Params().at(idx)->TsType()->MaybeBaseTypeOfGradualType() + : checker->GetElementTypeOfArray(sig->RestVar()->TsType())->MaybeBaseTypeOfGradualType(); } static void InitMostSpecificType(TypeRelation *relation, const ArenaVector &signatures, @@ -1095,8 +1103,9 @@ void ETSChecker::CollectSuitableSignaturesForTypeInference( } for (auto *sig : signatures) { - if (paramIdx >= sig->Params().size() || !sig->Params().at(paramIdx)->TsType()->IsETSObjectType() || - !sig->Params().at(paramIdx)->TsType()->AsETSObjectType()->IsGlobalETSObjectType()) { + auto paramType = sig->Params().at(paramIdx)->TsType()->MaybeBaseTypeOfGradualType(); + if (paramIdx >= sig->Params().size() || !paramType->IsETSObjectType() || + !paramType->AsETSObjectType()->IsGlobalETSObjectType()) { bestSignaturesForParameter.insert({paramIdx, sig}); } } diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index c4803d3dd8..7c22e2f83b 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -169,14 +169,15 @@ static std::optional BuildExplicitSubstitutionForArguments(ETSChec } } for (size_t ix = instArgs.size(); ix < sigParams.size(); ++ix) { - auto *dflt = sigParams[ix]->AsETSTypeParameter()->GetDefaultType(); + auto typeParam = sigParams[ix]->AsETSTypeParameter(); + auto *dflt = typeParam->GetDefaultType(); if (dflt == nullptr) { break; } dflt = dflt->Substitute(checker->Relation(), &constraintsSubstitution); instArgs.push_back(dflt); - checker->EmplaceSubstituted(&constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(&constraintsSubstitution, typeParam, instArgs[ix]); } if (sigParams.size() != instArgs.size()) { if ((flags & TypeRelationFlag::NO_THROW) == static_cast>(0U)) { diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 8be037eadd..26ed9ad28c 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include "checker/ETSchecker.h" #include "checker/types/globalTypesHolder.h" @@ -1590,7 +1591,7 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); - auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); + auto *typeAliasType = typeAliasTypeName->Variable()->TsType()->MaybeBaseTypeOfGradualType(); if (!typeAliasType->IsETSTypeParameter()) { continue; } @@ -1688,8 +1689,8 @@ util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringVi } // Helps to prevent searching for the imported file among external sources if it is the entry program -static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, - const util::StringView &importPath) +parser::Program *ETSChecker::SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, + const util::StringView &importPath) { if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) { return etsBinder->GetGlobalRecordTable()->Program(); @@ -1710,21 +1711,37 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjT program->SetASTChecked(); program->Ast()->Check(this); } + if (program->IsDeclForDynamicStaticInterop()) { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } else { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } } void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType) @@ -1749,6 +1766,10 @@ Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) return name->Check(this); } + if (name->IsMemberExpression()) { + return name->Check(this); + } + ES2PANDA_ASSERT(name->IsIdentifier()); auto *const var = name->AsIdentifier()->Variable(); @@ -1794,6 +1815,7 @@ checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) if (type->IsTypeError()) { return GlobalTypeError(); } + if (type->IsETSArrayType()) { return type->AsETSArrayType()->ElementType(); } diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index c6a6dda41a..f3c52febcb 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -19,6 +19,7 @@ #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsTupleType.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "checker/types/gradualType.h" #include "compiler/lowering/phase.h" #include "ir/base/classDefinition.h" #include "ir/base/classElement.h" @@ -155,7 +156,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return false; } - Type *superType = classDef->Super()->AsTypeNode()->GetType(this); + auto *superType = classDef->Super()->AsTypeNode()->GetType(this); if (superType == nullptr) { return true; } @@ -165,7 +166,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return true; } - ETSObjectType *superObj = superType->AsETSObjectType(); + ETSObjectType *superObj = superType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); // struct node has class definition, too if (superObj->GetDeclNode()->Parent()->IsETSStructDeclaration()) { @@ -405,7 +406,6 @@ ETSTypeParameter *ETSChecker::SetUpParameterType(ir::TSTypeParameter *const para paramType->SetVariable(param->Variable()); // NOTE: #15026 recursive type parameter workaround paramType->SetConstraintType(GlobalETSAnyType()); - var->SetTsType(paramType); return paramType; } @@ -437,12 +437,15 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte } checker::ETSObjectType *interfaceType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { interfaceType = CreateETSObjectTypeOrBuiltin(interfaceDecl, checker::ETSObjectFlags::INTERFACE); interfaceType->SetVariable(var); - var->SetTsType(interfaceType); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType) : interfaceType; + var->SetTsType(type); } else { interfaceType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType) : interfaceType; } ConstraintCheckScope ctScope(this); @@ -462,7 +465,7 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte CheckInterfaceFunctions(interfaceType); } - return interfaceType; + return type; } Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) @@ -478,21 +481,24 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) } checker::ETSObjectType *classType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { classType = CreateETSObjectTypeOrBuiltin(classDef, checker::ETSObjectFlags::CLASS); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType) : classType; classType->SetVariable(var); - var->SetTsType(classType); + var->SetTsType(type); if (classDef->IsAbstract()) { classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); } } else if (var->TsType()->IsETSObjectType()) { classType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType) : classType; } else { ES2PANDA_ASSERT(IsAnyError()); return GlobalTypeError(); } - classDef->SetTsType(classType); + classDef->SetTsType(type); ConstraintCheckScope ctScope(this); if (classDef->TypeParams() != nullptr) { @@ -516,7 +522,7 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) GetInterfaces(classType); } ctScope.TryCheckConstraints(); - return classType; + return type; } ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType) @@ -620,6 +626,10 @@ static void ResolveDeclaredDeclsOfObject(ETSChecker *checker, const ETSObjectTyp void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) { + if (type->IsGradualType()) { + return ResolveDeclaredMembersOfObject(type->AsGradualType()->GetBaseType()); + } + if (!type->IsETSObjectType() || type->AsETSObjectType()->IsPropertiesInstantiated()) { return; } @@ -1188,7 +1198,7 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) return; } - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); if (classType->SuperType() != nullptr) { classType->SuperType()->GetDeclNode()->Check(this); } @@ -1521,7 +1531,7 @@ void ETSChecker::CheckInnerClassMembers(const ETSObjectType *classType) bool ETSChecker::ValidateArrayIndex(ir::Expression *const expr, bool relaxed) { - auto const expressionType = expr->Check(this); + auto const expressionType = expr->Check(this)->MaybeBaseTypeOfGradualType(); if (expressionType->IsTypeError()) { return false; } @@ -2185,6 +2195,9 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member } const auto *const targetRef = GetTargetRef(memberExpr); auto searchFlag = GetSearchFlags(memberExpr, targetRef); + if (target->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT)) { + searchFlag |= PropertySearchFlags::SEARCH_INSTANCE; + } auto searchName = target->GetReExportAliasValue(memberExpr->Property()->AsIdentifier()->Name()); auto *prop = target->GetProperty(searchName, searchFlag); varbinder::Variable *const globalFunctionVar = ResolveInstanceExtension(memberExpr); diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 515b663e38..3728f497e3 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -16,6 +16,7 @@ #include "checker/checker.h" #include "checker/ets/wideningConverter.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "ir/base/catchClause.h" @@ -87,6 +88,9 @@ bool ETSChecker::CheckNonNullish(ir::Expression const *expr) Type *ETSChecker::GetNonNullishType(Type *type) { + if (type->IsGradualType()) { + return GetNonNullishType(type->AsGradualType()->GetBaseType()); + } if (type->DefinitelyNotETSNullish()) { return type; } @@ -192,7 +196,8 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) ArenaVector nullishTypes(ProgramAllocator()->Adapter()); ArenaVector notNullishTypes(ProgramAllocator()->Adapter()); - for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) { + for (auto *ctype : type->AsETSUnionType()->ConstituentTypes()) { + auto constituentType = ctype->MaybeBaseTypeOfGradualType(); if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) { nullishTypes.push_back(constituentType); } else { @@ -214,6 +219,9 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) template static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv) { + if (type->IsGradualType()) { + return MatchConstituentOrConstraint(type->AsGradualType()->GetBaseType(), pred, trv); + } auto const traverse = [&pred, &trv](const Type *ttype) { return MatchConstituentOrConstraint(ttype, pred, trv); }; @@ -265,6 +273,30 @@ bool Type::PossiblyETSUndefined() const [](const Type *t) { return !t->IsETSNonNullishType(); }); } +static bool ObjectPossiblyInForeignDomain(ETSObjectType const *type) +{ + if (type->IsGlobalETSObjectType()) { + return true; + } + auto dnode = type->GetDeclNode(); + if (dnode == nullptr) { + return false; + } + auto lang = dnode->IsClassDefinition() ? dnode->AsClassDefinition()->Language() + : dnode->AsTSInterfaceDeclaration()->Language(); + return lang != Language::Id::ETS; +} + +bool Type::PossiblyInForeignDomain() const +{ + return MatchConstituentOrConstraint( + this, + [](const Type *t) { + return t->IsETSAnyType() || (t->IsETSObjectType() && ObjectPossiblyInForeignDomain(t->AsETSObjectType())); + }, + []([[maybe_unused]] const Type *t) { return true; }); +} + bool Type::PossiblyETSNullish() const { return MatchConstituentOrConstraint( @@ -335,7 +367,7 @@ bool Type::IsETSMethodType() const TypeFlag::ETS_TYPE_PARAMETER | TypeFlag::WILDCARD | TypeFlag::ETS_NONNULLISH | TypeFlag::ETS_REQUIRED_TYPE_PARAMETER | TypeFlag::ETS_ANY | TypeFlag::ETS_NEVER | TypeFlag::ETS_UNION | TypeFlag::ETS_ARRAY | TypeFlag::FUNCTION | TypeFlag::ETS_PARTIAL_TYPE_PARAMETER | TypeFlag::ETS_TUPLE | - TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY; + TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE; // Issues if (type->IsETSVoidType()) { // NOTE(vpukhov): #19701 void refactoring @@ -461,10 +493,10 @@ void ETSChecker::IterateInVariableContext(varbinder::Variable *const var) Type *containingClass {}; if (classDef->TsType() == nullptr) { - containingClass = BuildBasicClassProperties(classDef); - ResolveDeclaredMembersOfObject(containingClass->AsETSObjectType()); + containingClass = BuildBasicClassProperties(classDef)->MaybeBaseTypeOfGradualType(); + ResolveDeclaredMembersOfObject(containingClass); } else { - containingClass = classDef->TsType()->AsETSObjectType(); + containingClass = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } ES2PANDA_ASSERT(classDef->TsType()); diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 8b6d09ef64..a6d97d25ce 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -19,6 +19,7 @@ #include "checker/types/ets/etsEnumType.h" #include "checker/types/ets/etsResizableArrayType.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/type.h" #include "ir/statements/annotationDeclaration.h" @@ -124,6 +125,30 @@ ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePoll return arrayType; } +Type *ETSChecker::CreateGradualType(Type *type, Language const lang) +{ + if (type == nullptr) { + return type; + } + if (type->IsGradualType()) { + return type; + } + if (!type->PossiblyInForeignDomain()) { + return type; + } + if (type->IsETSAnyType()) { + return type; + } + if (type->IsETSUnionType()) { + ArenaVector copied(ProgramAllocator()->Adapter()); + for (auto const &t : type->AsETSUnionType()->ConstituentTypes()) { + copied.push_back(CreateGradualType(t, lang)); + } + return CreateETSUnionType(std::move(copied)); + } + return ProgramAllocator()->New(type); +} + Type *ETSChecker::CreateETSUnionType(Span constituentTypes) { if (constituentTypes.empty()) { @@ -332,22 +357,24 @@ ETSObjectType *ETSChecker::CreateETSObjectTypeOrBuiltin(ir::AstNode *declNode, E ETSObjectType *ETSChecker::CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags) { auto const [name, internalName] = GetObjectTypeDeclNames(declNode); - + ETSObjectType *objectType = nullptr; if (declNode->IsClassDefinition() && (declNode->AsClassDefinition()->IsEnumTransformed())) { if (declNode->AsClassDefinition()->IsIntEnumTransformed()) { - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, - Relation()); + objectType = + ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, Relation()); + } else { + ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); + objectType = ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, + Relation()); } - ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, declNode, Relation()); - } - - if (internalName == compiler::Signatures::BUILTIN_ARRAY) { - return ProgramAllocator()->New(ProgramAllocator(), name, - std::make_tuple(declNode, flags, Relation())); + } else if (internalName == compiler::Signatures::BUILTIN_ARRAY) { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, + std::make_tuple(declNode, flags, Relation())); + } else { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, internalName, + std::make_tuple(declNode, flags, Relation())); } - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, - std::make_tuple(declNode, flags, Relation())); + return objectType; } std::tuple ETSChecker::CreateBuiltinArraySignatureInfo(const ETSArrayType *arrayType, diff --git a/ets2panda/checker/ets/typeRelationContext.cpp b/ets2panda/checker/ets/typeRelationContext.cpp index bfcd8ef61a..80bfff4d4e 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -84,7 +84,6 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParame while (typeArgTypes.size() < type->TypeArguments().size()) { Type *defaultType = nullptr; - if (type->TypeArguments().at(typeArgTypes.size())->IsETSTypeParameter()) { defaultType = type->TypeArguments().at(typeArgTypes.size())->AsETSTypeParameter()->GetDefaultType(); } else { diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index 1bfc781d84..33339c96f0 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -23,6 +23,8 @@ #include "varbinder/ETSBinder.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include + namespace ark::es2panda::checker { std::optional ETSChecker::GetUtilityTypeTypeParamNode( @@ -123,6 +125,11 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) return typeToBePartial; } + if (typeToBePartial->IsGradualType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return CreatePartialType(typeToBePartial->AsGradualType()->GetBaseType()); + } + if (typeToBePartial->IsETSTypeParameter()) { return CreatePartialTypeParameter(typeToBePartial->AsETSTypeParameter()); } @@ -388,8 +395,8 @@ ir::TSTypeParameterInstantiation *ETSChecker::CreateNewSuperPartialRefTypeParams !originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->IsETSTypeParameter()) { continue; } - auto it = likeSubstitution->find( - originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->AsETSTypeParameter()->GetDeclNode()); + auto type = originRefParams[ix]->AsETSTypeReference()->Part()->TsType(); + auto it = likeSubstitution->find(type->AsETSTypeParameter()->GetDeclNode()); if (it != likeSubstitution->end()) { auto *typeParamRefPart = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) diff --git a/ets2panda/checker/types/ets/etsAnyType.cpp b/ets2panda/checker/types/ets/etsAnyType.cpp index d9b87e49a2..52793689c9 100644 --- a/ets2panda/checker/types/ets/etsAnyType.cpp +++ b/ets2panda/checker/types/ets/etsAnyType.cpp @@ -22,7 +22,7 @@ namespace ark::es2panda::checker { void ETSAnyType::Identical(TypeRelation *relation, Type *other) { - relation->Result(other->IsAnyType()); + relation->Result(other->IsETSAnyType()); } void ETSAnyType::AssignmentTarget(TypeRelation *relation, Type *source) diff --git a/ets2panda/checker/types/ets/etsAnyType.h b/ets2panda/checker/types/ets/etsAnyType.h index 1a315b36d6..7d12b6a004 100644 --- a/ets2panda/checker/types/ets/etsAnyType.h +++ b/ets2panda/checker/types/ets/etsAnyType.h @@ -22,6 +22,7 @@ namespace ark::es2panda::checker { class ETSAnyType : public Type { public: ETSAnyType() : Type(TypeFlag::ETS_ANY) {} + explicit ETSAnyType(bool isRelaxedAny) : Type(TypeFlag::ETS_ANY), isRelaxedAny_(isRelaxedAny) {} void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; @@ -38,6 +39,14 @@ public: TypeFacts GetTypeFacts() const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + bool IsRelaxedAny() const + { + return isRelaxedAny_; + } + +private: + bool isRelaxedAny_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index 6af463b16d..72c5694774 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -235,7 +235,6 @@ varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(ut for (auto &s : signatureSet) { funcType->AddCallSignature(s.second); } - res->SetTsType(funcType); funcType->SetVariable(res); @@ -1284,7 +1283,8 @@ ETSObjectType *ETSObjectType::SubstituteArguments(TypeRelation *relation, ArenaV ES2PANDA_ASSERT(typeArguments_.size() == arguments.size()); for (size_t ix = 0; ix < typeArguments_.size(); ix++) { - substitution.emplace(typeArguments_[ix]->AsETSTypeParameter(), checker->MaybeBoxType(arguments[ix])); + substitution.emplace(typeArguments_[ix]->AsETSTypeParameter(), + checker->MaybeBoxType(arguments[ix]->MaybeBaseTypeOfGradualType())); } return Substitute(relation, &substitution); diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index 6bd4357e59..a13f0d4685 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -60,6 +60,7 @@ enum class ETSObjectFlags : std::uint64_t { EXTENSION_FUNCTION = 1ULL << 35U, FUNCTIONAL_REFERENCE = 1ULL << 36U, + LAZY_IMPORT_OBJECT = 1ULL << 37U, ENUM_OBJECT = INT_ENUM_OBJECT | STRING_ENUM_OBJECT, diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index b5a4244e54..7b94a53112 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -359,7 +359,7 @@ bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::Type *sour bool rc = false; auto it = unionTypes.cbegin(); while (it != unionTypes.cend()) { - auto *constituentType = *it; + auto *constituentType = (*it)->MaybeBaseTypeOfGradualType(); // Because 'instanceof' expression does not check for type parameters, then for generic types we should // consider that expressions like 'SomeType' and 'SomeType' are identical for smart casting. // We also have to pass through all the union to process cases like 'C|A|B|C|undefined` diff --git a/ets2panda/checker/types/globalTypesHolder.cpp b/ets2panda/checker/types/globalTypesHolder.cpp index 8aa8a44cb7..effd56d037 100644 --- a/ets2panda/checker/types/globalTypesHolder.cpp +++ b/ets2panda/checker/types/globalTypesHolder.cpp @@ -150,6 +150,7 @@ void GlobalTypesHolder::AddEtsSpecificTypes(ArenaAllocator *allocator) globalTypes_[static_cast(GlobalTypeId::ETS_WILDCARD)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::TYPE_ERROR)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ETS_ANY)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::ETS_RELAXED_ANY)] = allocator->New(true); globalTypes_[static_cast(GlobalTypeId::ETS_NEVER)] = allocator->New(); } @@ -412,6 +413,11 @@ Type *GlobalTypesHolder::GlobalETSAnyType() return globalTypes_.at(static_cast(GlobalTypeId::ETS_ANY)); } +Type *GlobalTypesHolder::GlobalETSRelaxedAnyType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_RELAXED_ANY)); +} + Type *GlobalTypesHolder::GlobalETSNeverType() { return globalTypes_.at(static_cast(GlobalTypeId::ETS_NEVER)); diff --git a/ets2panda/checker/types/globalTypesHolder.h b/ets2panda/checker/types/globalTypesHolder.h index 9374659544..1818385b6c 100644 --- a/ets2panda/checker/types/globalTypesHolder.h +++ b/ets2panda/checker/types/globalTypesHolder.h @@ -59,6 +59,7 @@ enum class GlobalTypeId : std::size_t { ETS_UNDEFINED, ETS_UNION_UNDEFINED_NULL, ETS_ANY, + ETS_RELAXED_ANY, ETS_NEVER, ETS_UNION_UNDEFINED_NULL_OBJECT, ETS_WILDCARD, @@ -270,6 +271,7 @@ public: Type *GlobalETSNullType(); Type *GlobalETSUndefinedType(); Type *GlobalETSAnyType(); + Type *GlobalETSRelaxedAnyType(); Type *GlobalETSNeverType(); Type *GlobalETSUnionUndefinedNull(); Type *GlobalETSUnionUndefinedNullObject(); diff --git a/ets2panda/checker/types/gradualType.cpp b/ets2panda/checker/types/gradualType.cpp new file mode 100644 index 0000000000..cdb568ab47 --- /dev/null +++ b/ets2panda/checker/types/gradualType.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 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 "gradualType.h" + +#include "checker/ETSchecker.h" +#include "checker/ets/conversion.h" + +namespace ark::es2panda::checker { +void GradualType::Identical(TypeRelation *relation, Type *other) +{ + baseType_->Identical(relation, other); +} + +void GradualType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + baseType_->AssignmentTarget(relation, source); +} + +bool GradualType::AssignmentSource(TypeRelation *relation, Type *target) +{ + return baseType_->AssignmentSource(relation, target); +} + +void GradualType::Compare(TypeRelation *relation, Type *other) +{ + return baseType_->Compare(relation, other); +} + +void GradualType::Cast(TypeRelation *relation, Type *target) +{ + return baseType_->Cast(relation, target); +} + +void GradualType::CastTarget(TypeRelation *relation, Type *source) +{ + return baseType_->CastTarget(relation, source); +} + +void GradualType::IsSubtypeOf(TypeRelation *relation, Type *target) +{ + return baseType_->IsSubtypeOf(relation, target); +} + +void GradualType::IsSupertypeOf(TypeRelation *relation, Type *source) +{ + if (source->IsGradualType()) { + relation->IsSupertypeOf(baseType_, source->AsGradualType()->GetBaseType()); + } else { + baseType_->IsSupertypeOf(relation, source); + } +} + +void GradualType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const +{ + baseType_->ToString(ss); +} + +Type *GradualType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + return baseType_->Instantiate(allocator, relation, globalTypes); +} + +Type *GradualType::Substitute(TypeRelation *relation, const Substitution *substitution) +{ + return baseType_->Substitute(relation, substitution); +} + +void GradualType::ToAssemblerType(std::stringstream &ss) const +{ + baseType_->ToAssemblerType(ss); +} + +void GradualType::ToDebugInfoType(std::stringstream &ss) const +{ + baseType_->ToDebugInfoType(ss); +} + +void GradualType::ToAssemblerTypeWithRank(std::stringstream &ss) const +{ + baseType_->ToAssemblerTypeWithRank(ss); +} + +void GradualType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) +{ + // The type of array should be Invariant + relation->CheckVarianceRecursively(baseType_, varianceFlag); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/gradualType.h b/ets2panda/checker/types/gradualType.h new file mode 100644 index 0000000000..eb1978e8d6 --- /dev/null +++ b/ets2panda/checker/types/gradualType.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 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_CHECKER_TYPES_GRADUAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_GRADUAL_TYPE_H + +#include "checker/types/type.h" +#include "ir/astNode.h" + +namespace ark::es2panda::checker { +class GradualType : public Type { +public: + explicit GradualType(checker::Type *baseType) + : Type(TypeFlag::GRADUAL_TYPE), baseType_(baseType), lang_(es2panda::Language(Language::Id::ETS)) + { + } + + explicit GradualType(checker::Type *baseType, Language lang) + : Type(TypeFlag::GRADUAL_TYPE), baseType_(baseType), lang_(lang) + { + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource(TypeRelation *relation, Type *target) override; + void Compare(TypeRelation *relation, Type *other) override; + void Cast(TypeRelation *relation, Type *target) override; + void CastTarget(TypeRelation *relation, Type *source) override; + void IsSubtypeOf(TypeRelation *relation, Type *target) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; + void ToString(std::stringstream &ss, bool precise) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void ToDebugInfoType(std::stringstream &ss) const override; + void ToAssemblerTypeWithRank(std::stringstream &ss) const override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + Type *Substitute(TypeRelation *relation, const Substitution *substitution) override; + void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; + + const Type *GetBaseType() const + { + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; + } + + Type *GetBaseType() + { + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; + } + + Type *BaseType() + { + return baseType_; + } + + Type *BaseType() const + { + return baseType_; + } + + es2panda::Language Language() const + { + return lang_; + } + +private: + Type *baseType_; + es2panda::Language lang_; +}; +} // namespace ark::es2panda::checker + +#endif \ No newline at end of file diff --git a/ets2panda/checker/types/signature.cpp b/ets2panda/checker/types/signature.cpp index 5ced79c411..be45282d5f 100644 --- a/ets2panda/checker/types/signature.cpp +++ b/ets2panda/checker/types/signature.cpp @@ -112,7 +112,7 @@ Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, Gl SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); for (size_t idx = 0U; idx < signatureInfo_->params.size(); ++idx) { - auto *const paramType = signatureInfo_->params[idx]->TsType(); + auto *const paramType = signatureInfo_->params[idx]->TsType()->MaybeBaseTypeOfGradualType(); if (paramType->HasTypeFlag(TypeFlag::GENERIC) && paramType->IsETSObjectType()) { copiedInfo->params[idx]->SetTsType(paramType->Instantiate(allocator, relation, globalTypes)); auto originalTypeArgs = paramType->AsETSObjectType()->GetOriginalBaseType()->TypeArguments(); diff --git a/ets2panda/checker/types/type.cpp b/ets2panda/checker/types/type.cpp index 090981812f..61a6b8568e 100644 --- a/ets2panda/checker/types/type.cpp +++ b/ets2panda/checker/types/type.cpp @@ -18,6 +18,7 @@ #include "checker/types/typeFlag.h" #include "checker/types/typeRelation.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "checker/checker.h" namespace ark::es2panda::checker { @@ -161,4 +162,13 @@ bool IsTypeError(Type const *tp) return tp != nullptr && tp->IsTypeError(); } +Type *Type::MaybeBaseTypeOfGradualType() +{ + auto res = this; + while (res->IsGradualType()) { + res = res->AsGradualType()->GetBaseType(); + } + return res; +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/type.h b/ets2panda/checker/types/type.h index 7f0aa993d4..3840d6ab66 100644 --- a/ets2panda/checker/types/type.h +++ b/ets2panda/checker/types/type.h @@ -33,6 +33,7 @@ class ETSAsyncFuncReturnType; class ETSChecker; class ETSTypeParameter; class ETSEnumType; +class GradualType; // CC-OFFNXT(G.PRE.02) name part // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -113,6 +114,8 @@ public: bool PossiblyETSValueTyped() const; bool PossiblyETSValueTypedExceptNullish() const; + bool PossiblyInForeignDomain() const; + ETSStringType *AsETSStringType() { ES2PANDA_ASSERT(IsETSObjectType()); @@ -162,6 +165,8 @@ public: return HasTypeFlag(checker::TypeFlag::CONSTANT); } + Type *MaybeBaseTypeOfGradualType(); + TypeFlag TypeFlags() const { return typeFlags_; diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index feea58ce12..5f01ae8225 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -69,11 +69,10 @@ enum class TypeFlag : uint64_t { ETS_ARRAY = 1ULL << 41ULL, // ETS array type WILDCARD = 1ULL << 42ULL, // new A() ETS_TYPE_PARAMETER = 1ULL << 43ULL, // ETS type parameter - ETS_TYPE_REFERENCE = 1ULL << 44ULL, // ETS type reference GENERIC = 1ULL << 45ULL, // ETS Generic ETS_INT_ENUM = 1ULL << 46ULL, // ETS Enum ETS_STRING_ENUM = 1ULL << 47ULL, // ETS string-type Enumeration - ETS_DYNAMIC_FLAG = 1ULL << 48ULL, // ETS Dynamic flag + GRADUAL_TYPE = 1ULL << 48ULL, // gradual type GETTER = 1ULL << 49ULL, // ETS Getter SETTER = 1ULL << 50ULL, // ETS Setter ETS_EXTENSION_FUNC_HELPER = 1ULL << 51ULL, // ETS Extension Function Helper @@ -90,8 +89,6 @@ enum class TypeFlag : uint64_t { ETS_ANY = 1ULL << 22ULL, // ETS any, the value was *stolen* from the CONDITIONAL type kind ETS_NEVER = 1ULL << 62ULL, // ETS never ETS_METHOD = 1ULL << 63ULL, // ETS method (or function in module) (possibly overloaded) - ETS_DYNAMIC_TYPE = ETS_OBJECT | ETS_DYNAMIC_FLAG, - ETS_DYNAMIC_FUNCTION_TYPE = FUNCTION | ETS_DYNAMIC_FLAG, ETS_INTEGRAL_NUMERIC = BYTE | SHORT | INT | LONG, ETS_FLOATING_POINT = FLOAT | DOUBLE, ETS_NUMERIC = ETS_INTEGRAL_NUMERIC | ETS_FLOATING_POINT, diff --git a/ets2panda/checker/types/typeMapping.h b/ets2panda/checker/types/typeMapping.h index a856a49bdb..84e3ba9cfe 100644 --- a/ets2panda/checker/types/typeMapping.h +++ b/ets2panda/checker/types/typeMapping.h @@ -71,6 +71,7 @@ _(TypeFlag::ETS_TUPLE, ETSTupleType) \ _(TypeFlag::ETS_PARTIAL_TYPE_PARAMETER, ETSPartialTypeParameter) \ _(TypeFlag::TYPE_ERROR, TypeError) \ + _(TypeFlag::GRADUAL_TYPE, GradualType) \ _(TypeFlag::ETS_TYPE_ALIAS, ETSTypeAliasType) #define OBJECT_TYPE_MAPPING(_) \ diff --git a/ets2panda/compiler/base/lreference.cpp b/ets2panda/compiler/base/lreference.cpp index 96572db071..f83427ac2b 100644 --- a/ets2panda/compiler/base/lreference.cpp +++ b/ets2panda/compiler/base/lreference.cpp @@ -183,7 +183,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind const auto *memberExpr = Node()->AsMemberExpression(); staticObjRef_ = memberExpr->Object()->TsType(); - if (!memberExpr->IsComputed() && etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { + if (!memberExpr->IsComputed() && memberExpr->PropVar() != nullptr && + etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { return; } @@ -196,7 +197,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind TargetTypeContext pttctx(etsg_, memberExpr->Property()->TsType()); memberExpr->Property()->Compile(etsg_); etsg_->ApplyConversion(memberExpr->Property()); - ES2PANDA_ASSERT(etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); + ES2PANDA_ASSERT(memberExpr->Object()->TsType()->IsETSAnyType() || + etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); propReg_ = etsg_->AllocReg(); etsg_->StoreAccumulator(node, propReg_); } @@ -280,6 +282,15 @@ void ETSLReference::SetValueComputed(const ir::MemberExpression *memberExpr) con { const auto *const objectType = memberExpr->Object()->TsType(); + if (objectType->IsETSAnyType()) { + if (memberExpr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg_->StoreByIndexAny(memberExpr, baseReg_, propReg_); + } else { + etsg_->StoreByValueAny(memberExpr, baseReg_, propReg_); + } + return; + } + if (objectType->IsETSTupleType()) { ES2PANDA_ASSERT(memberExpr->GetTupleIndexValue().has_value()); @@ -325,6 +336,7 @@ void ETSLReference::SetValue() const const auto *const memberExpr = Node()->AsMemberExpression(); const auto *const memberExprTsType = memberExpr->TsType(); + auto const *objectType = memberExpr->Object()->TsType(); if (!memberExpr->IsIgnoreBox()) { etsg_->ApplyConversion(Node(), memberExprTsType); @@ -335,6 +347,11 @@ void ETSLReference::SetValue() const return; } + if (objectType->IsETSAnyType()) { + etsg_->StorePropertyByNameAny(memberExpr, baseReg_, memberExpr->Property()->AsIdentifier()->Name()); + return; + } + if (memberExpr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { SetValueGetterSetter(memberExpr); return; @@ -349,8 +366,6 @@ void ETSLReference::SetValue() const return; } - auto const *objectType = memberExpr->Object()->TsType(); - if (objectType->IsETSUnionType()) { etsg_->StorePropertyByName(Node(), baseReg_, checker::ETSChecker::FormNamedAccessMetadata(memberExpr->PropVar())); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 09d7df240a..a015993439 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -19,9 +19,15 @@ #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/switchBuilder.h" +#include "compiler/core/targetTypeContext.h" +#include "compiler/core/vReg.h" +#include "compiler/function/functionBuilder.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" #include "ETSGen-inl.h" +#include "generated/signatures.h" +#include "util/es2pandaMacros.h" +#include "varbinder/ETSBinder.h" namespace ark::es2panda::compiler { @@ -242,9 +248,17 @@ static void GetSizeInForOf(compiler::ETSGen *etsg, checker::Type const *const ex void ETSCompiler::Compile(const ir::ETSNewClassInstanceExpression *expr) const { ETSGen *etsg = GetETSGen(); - ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); - etsg->InitObject(expr, expr->signature_, expr->GetArguments()); + if (expr->TsType()->IsETSAnyType()) { + compiler::RegScope rs(etsg); + auto objReg = etsg->AllocReg(); + expr->GetTypeRef()->Compile(etsg); + etsg->StoreAccumulator(expr->GetTypeRef(), objReg); + etsg->CallAnyNew(expr, expr->GetArguments(), objReg); + } else { + ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr); + etsg->InitObject(expr, expr->signature_, expr->GetArguments()); + } etsg->SetAccumulatorType(expr->TsType()); } @@ -476,6 +490,17 @@ static void CompileLogical(compiler::ETSGen *etsg, const ir::BinaryExpression *e etsg->SetAccumulatorType(expr->TsType()); } +static void CompileAnyInstanceOf(compiler::ETSGen *etsg, const VReg lhs, const ir::Expression *expr) +{ + RegScope rs(etsg); + VReg objReg = etsg->AllocReg(); + expr->Compile(etsg); + etsg->StoreAccumulator(expr, objReg); + etsg->LoadAccumulator(expr, lhs); + etsg->EmitAnyIsInstance(expr, objReg); + etsg->SetAccumulatorType(etsg->Checker()->GlobalETSBooleanType()); +} + static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression *expr) { ES2PANDA_ASSERT(expr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF); @@ -487,7 +512,11 @@ static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression etsg->ApplyConversionAndStoreAccumulator(expr->Left(), lhs, expr->OperationType()); auto target = expr->Right()->TsType(); - etsg->IsInstance(expr, lhs, target); + if (target->IsETSAnyType() && target->AsETSAnyType()->IsRelaxedAny()) { + CompileAnyInstanceOf(etsg, lhs, expr->Right()); + } else { + etsg->IsInstance(expr, lhs, target); + } ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } @@ -662,6 +691,26 @@ bool IsCastCall(checker::Signature *signature) (signature->Params().size() == 1) && signature->Params()[0]->TsType()->IsETSPrimitiveType()); } +void ETSCompiler::CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, + compiler::VReg &calleeReg) const +{ + ETSGen *etsg = GetETSGen(); + auto memberExpr = callee->AsMemberExpression(); + memberExpr->Object()->Compile(etsg); + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + if (expr->Signature()->Function() != nullptr && expr->Signature()->Function()->IsStatic()) { + etsg->LoadPropertyByNameAny(memberExpr, objReg, memberExpr->Property()->AsIdentifier()->Name()); + etsg->StoreAccumulator(expr, calleeReg); + etsg->CallAny(callee->AsMemberExpression()->Object(), expr->Arguments(), calleeReg); + } else { + etsg->CallAnyThis(expr, memberExpr->Property()->AsIdentifier(), expr->Arguments(), objReg); + } + auto returnType = expr->Signature()->ReturnType(); + etsg->EmitAnyCheckCast(expr, returnType); +} + void ETSCompiler::EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const { @@ -713,7 +762,9 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr, signature); - if (callee->IsIdentifier()) { + if (expr->IsDynamicCall()) { + CompileAny(expr, callee, calleeReg); + } else if (callee->IsIdentifier()) { if (!isStatic) { etsg->LoadThis(expr); etsg->StoreAccumulator(expr, calleeReg); @@ -785,6 +836,17 @@ void ETSCompiler::Compile(const ir::Identifier *expr) const etsg->SetAccumulatorType(smartType); } +static void LoadETSAnyTypeFromMemberExpr(compiler::ETSGen *etsg, const ir::MemberExpression *expr, + compiler::VReg objReg) +{ + if (expr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg->LoadByIndexAny(expr, objReg); + } else { + etsg->LoadByValueAny(expr, objReg); + } + etsg->EmitAnyCheckCast(expr, expr->TsType()); +} + bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr) { if (!expr->IsComputed()) { @@ -810,6 +872,8 @@ bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpres auto indexValue = *expr->GetTupleIndexValue(); auto *tupleElementType = objectType->AsETSTupleType()->GetTypeAtIndex(indexValue); etsg->LoadTupleElement(expr, objReg, tupleElementType, indexValue); + } else if (objectType->IsETSAnyType()) { + LoadETSAnyTypeFromMemberExpr(etsg, expr, objReg); } else { ES2PANDA_ASSERT(objectType->IsETSArrayType()); etsg->LoadArrayElement(expr, objReg); @@ -822,6 +886,23 @@ bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpres return true; } +bool ETSCompiler::CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const +{ + if (!etsg->Checker()->GetApparentType(expr->Object()->TsType())->IsETSAnyType()) { + return false; + } + auto ottctx = compiler::TargetTypeContext(etsg, expr->Object()->TsType()); + etsg->CompileAndCheck(expr->Object()); + + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); + + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + etsg->LoadPropertyByNameAny(expr, objReg, expr->Property()->AsIdentifier()->Name()); + etsg->EmitAnyCheckCast(expr, expr->TsType()); + return true; +} + void ETSCompiler::Compile(const ir::MemberExpression *expr) const { ETSGen *etsg = GetETSGen(); @@ -832,6 +913,10 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const return; } + if (CompileAny(etsg, expr)) { + return; + } + if (HandleArrayTypeLengthProperty(expr, etsg)) { return; } @@ -1630,4 +1715,17 @@ void ETSCompiler::Compile(const ir::TSNonNullExpression *expr) const void ETSCompiler::Compile([[maybe_unused]] const ir::TSTypeAliasDeclaration *st) const {} +void ETSCompiler::Compile(const ir::TSQualifiedName *expr) const +{ + ES2PANDA_ASSERT(expr->Left()->IsMemberExpression()); + + ETSGen *etsg = GetETSGen(); + expr->Left()->Compile(etsg); + + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr->Left(), objReg); + etsg->LoadPropertyByNameAny(expr->Left(), objReg, expr->Right()->AsIdentifier()->Name()); + etsg->EmitAnyCheckCast(expr, expr->Right()->AsIdentifier()->Variable()->TsType()); +} + } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index d09df63fd6..868ce81e49 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -35,6 +35,7 @@ public: private: void GetDynamicNameParts(const ir::CallExpression *expr, ArenaVector &parts) const; + void CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, compiler::VReg &calleeReg) const; void CompileCastPrimitives(const ir::Expression *expr, checker::Type const *targetType) const; void CompileCast(const ir::TSAsExpression *expr, checker::Type const *targetType) const; void EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const; @@ -44,6 +45,7 @@ private: void CompileTupleCreation(const ir::ArrayExpression *tupleInitializer) const; static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr); + bool CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp index 44669b1241..4536b5cebf 100644 --- a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp +++ b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp @@ -419,11 +419,6 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::TSParenthesizedType *node) ES2PANDA_UNREACHABLE(); } -void ETSCompiler::Compile([[maybe_unused]] const ir::TSQualifiedName *expr) const -{ - ES2PANDA_UNREACHABLE(); -} - void ETSCompiler::Compile([[maybe_unused]] const ir::TSStringKeyword *node) const { ES2PANDA_UNREACHABLE(); diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 4abf10587d..4e62393a95 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -15,6 +15,8 @@ #include "ETSGen-inl.h" +#include "compiler/core/regScope.h" +#include "generated/isa.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" @@ -372,6 +374,22 @@ void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *p } } +void ETSGen::StorePropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + +void ETSGen::LoadPropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg, const util::StringView &fullName) { @@ -425,6 +443,48 @@ void ETSGen::LoadPropertyByName([[maybe_unused]] const ir::AstNode *const node, #endif // PANDA_WITH_ETS } +void ETSGen::StoreByIndexAny(const ir::MemberExpression *node, VReg objectReg, VReg index) +{ + RegScope rs(this); + + // Store property by index + Ra().Emit(node, objectReg, index); + SetAccumulatorType(Checker()->GlobalVoidType()); +} + +void ETSGen::LoadByIndexAny(const ir::MemberExpression *node, VReg objectReg) +{ + RegScope rs(this); + + VReg indexReg = AllocReg(); + StoreAccumulator(node, indexReg); + + // Get property by index + Ra().Emit(node, objectReg); + SetAccumulatorType(node->TsType()); +} + +void ETSGen::StoreByValueAny(const ir::MemberExpression *node, VReg objectReg, VReg value) +{ + RegScope rs(this); + + // Store property by value + Ra().Emit(node, objectReg, value); + SetAccumulatorType(Checker()->GlobalVoidType()); +} + +void ETSGen::LoadByValueAny(const ir::MemberExpression *node, VReg objectReg) +{ + RegScope rs(this); + + VReg valueReg = AllocReg(); + StoreAccumulator(node, valueReg); + + // Get property by value + Ra().Emit(node, objectReg, valueReg); + SetAccumulatorType(node->TsType()); +} + void ETSGen::CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg) { @@ -649,6 +709,15 @@ void ETSGen::InternalCheckCast(const ir::AstNode *node, const es2panda::checker: SetAccumulatorType(target); } +// Handle checkcast for interop if it is 1.2 type. +void ETSGen::EmitAnyCheckCast(const ir::AstNode *node, const checker::Type *target) +{ + if (!target->IsETSAnyType() && + (target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType())) { + InternalCheckCast(node, target); + } +} + // optimized specialization for object and [] targets void ETSGen::CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target) { diff --git a/ets2panda/compiler/core/ETSGen.h b/ets2panda/compiler/core/ETSGen.h index d875e54e04..084e2edfa2 100644 --- a/ets2panda/compiler/core/ETSGen.h +++ b/ets2panda/compiler/core/ETSGen.h @@ -21,6 +21,7 @@ #include "compiler/core/ETSfunction.h" #include "compiler/core/targetTypeContext.h" #include "checker/ETSchecker.h" +#include "ir/expressions/identifier.h" #include "util/helpers.h" #include @@ -69,6 +70,15 @@ public: void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &fullName); + void StoreByIndexAny(const ir::MemberExpression *node, VReg objectReg, VReg index); + void LoadByIndexAny(const ir::MemberExpression *node, VReg objectReg); + + void StoreByValueAny(const ir::MemberExpression *node, VReg objectReg, VReg value); + void LoadByValueAny(const ir::MemberExpression *node, VReg objectReg); + + void StorePropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName); + void LoadPropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName); + void StorePropertyByName(const ir::AstNode *node, VReg objReg, checker::ETSChecker::NamedAccessMeta const &fieldMeta); void LoadPropertyByName(const ir::AstNode *node, VReg objReg, @@ -268,6 +278,7 @@ public: void InternalIsInstance(const ir::AstNode *node, const checker::Type *target); void InternalCheckCast(const ir::AstNode *node, const checker::Type *target); + void EmitAnyCheckCast(const ir::AstNode *node, const checker::Type *target); void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target); void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target); @@ -310,6 +321,11 @@ public: #endif // PANDA_WITH_ETS } + void EmitAnyIsInstance(const ir::AstNode *node, VReg objReg) + { + Sa().Emit(node, objReg); + } + void CallExact(const ir::AstNode *node, checker::Signature *signature, const ArenaVector &arguments) { @@ -413,6 +429,22 @@ public: CallDynamicImpl(data, param3, signature, arguments); } + void CallAnyNew(const ir::AstNode *const node, const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, arguments, athis); + } + + void CallAnyThis(const ir::AstNode *const node, const ir::Identifier *ident, + const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, ident, arguments, athis); + } + + void CallAny(const ir::AstNode *const node, const ArenaVector &arguments, const VReg athis) + { + CallAnyImpl(node, arguments, athis); + } + // until a lowering for implicit super is available void CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg); @@ -554,7 +586,7 @@ private: template void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs); -// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty) +// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) #define COMPILE_ARG(idx) \ ES2PANDA_ASSERT((idx) < arguments.size()); \ ES2PANDA_ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr); \ @@ -733,7 +765,72 @@ private: } #undef COMPILE_ARG - // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty) + +#define COMPILE_ANY_ARG(idx) \ + ES2PANDA_ASSERT((idx) < arguments.size()); \ + auto *param##idx = arguments[idx]; \ + auto *paramType##idx = param##idx->TsType(); \ + auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ + arguments[idx]->Compile(this); \ + VReg arg##idx = AllocReg(); \ + ApplyConversion(arguments[idx], nullptr); \ + ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx) + + template + void CallAnyImpl(const ir::AstNode *node, const ir::Identifier *ident, + const ArenaVector &arguments, const VReg athis) + { + ES2PANDA_ASSERT(ident != nullptr); + RegScope rs(this); + + switch (arguments.size()) { + case 0U: { + Ra().Emit(node, ident->Name(), athis); + break; + } + case 1U: { + COMPILE_ANY_ARG(0); + Ra().Emit(node, ident->Name(), athis, arg0); + break; + } + default: { + VReg argStart = NextReg(); + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ANY_ARG(idx); + } + + Rra().Emit(node, argStart, arguments.size(), ident->Name(), athis, argStart, arguments.size()); + } + } + } + + template + void CallAnyImpl(const ir::AstNode *node, const ArenaVector &arguments, const VReg athis) + { + RegScope rs(this); + + switch (arguments.size()) { + case 0U: { + Ra().Emit(node, athis); + break; + } + case 1U: { + COMPILE_ANY_ARG(0); + Ra().Emit(node, athis, arg0); + break; + } + default: { + VReg argStart = NextReg(); + for (size_t idx = 0; idx < arguments.size(); idx++) { + COMPILE_ANY_ARG(idx); + } + + Rra().Emit(node, argStart, arguments.size(), athis, argStart, arguments.size()); + } + } + } +#undef COMPILE_ANY_ARG + // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty, modernize-loop-convert) void ToBinaryResult(const ir::AstNode *node, Label *ifFalse); diff --git a/ets2panda/compiler/core/ETSemitter.cpp b/ets2panda/compiler/core/ETSemitter.cpp index 81ff05cfe2..81a59f6310 100644 --- a/ets2panda/compiler/core/ETSemitter.cpp +++ b/ets2panda/compiler/core/ETSemitter.cpp @@ -36,6 +36,7 @@ #include "checker/types/signature.h" #include "checker/ETSchecker.h" #include "checker/types/type.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/types.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "public/public.h" @@ -94,6 +95,9 @@ static uint32_t TranslateModifierFlags(ir::ModifierFlags modifierFlags) static pandasm::Type PandasmTypeWithRank(checker::Type const *type, uint32_t rank = 0) { + if (type->IsGradualType()) { + return PandasmTypeWithRank(type->AsGradualType()->GetBaseType()); + } if (type->IsETSTypeParameter()) { return PandasmTypeWithRank(type->AsETSTypeParameter()->GetConstraintType()); } @@ -506,7 +510,9 @@ void ETSEmitter::GenGlobalArrayRecord(const checker::ETSArrayType *arrayType, ch void ETSEmitter::GenInterfaceRecord(const ir::TSInterfaceDeclaration *interfaceDecl, bool external) { - auto *baseType = interfaceDecl->TsType()->AsETSObjectType(); + auto *baseType = interfaceDecl->TsType()->IsGradualType() + ? interfaceDecl->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : interfaceDecl->TsType()->AsETSObjectType(); auto interfaceRecord = pandasm::Record(interfaceDecl->InternalName().Mutf8(), Program()->lang); uint32_t accessFlags = ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE; @@ -603,8 +609,9 @@ void ETSEmitter::GenClassRecord(const ir::ClassDefinition *classDef, bool extern uint32_t accessFlags = GetAccessFlags(classDef); classRecord.metadata->SetAccessFlags(accessFlags); classRecord.sourceFile = std::string {Context()->parserProgram->VarBinder()->Program()->RelativeFilePath()}; - - auto *baseType = classDef->TsType()->AsETSObjectType(); + auto *baseType = classDef->TsType()->IsGradualType() + ? classDef->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : classDef->TsType()->AsETSObjectType(); GenClassInheritedFields(baseType, classRecord); for (const auto *prop : classDef->Body()) { if (!prop->IsClassProperty()) { diff --git a/ets2panda/compiler/core/ETSfunction.cpp b/ets2panda/compiler/core/ETSfunction.cpp index 4edd9d31b2..672f29e5f6 100644 --- a/ets2panda/compiler/core/ETSfunction.cpp +++ b/ets2panda/compiler/core/ETSfunction.cpp @@ -33,18 +33,18 @@ #include "ir/ts/tsEnumDeclaration.h" #include "ir/ts/tsEnumMember.h" #include "checker/types/ets/types.h" - +#include "checker/types/gradualType.h" namespace ark::es2panda::compiler { // #22952: this should have been done in lowering void ETSFunction::CallImplicitCtor(ETSGen *etsg) { RegScope rs(etsg); - auto *superType = etsg->ContainingObjectType()->SuperType(); - + auto *type = etsg->ContainingObjectType()->SuperType(); + auto superType = + type->IsGradualType() ? type->AsGradualType()->GetBaseType()->AsETSObjectType() : type->AsETSObjectType(); if (superType == nullptr) { etsg->CallExact(etsg->RootNode(), Signatures::BUILTIN_OBJECT_CTOR, etsg->GetThisReg()); - return; } diff --git a/ets2panda/compiler/lowering/ets/dynamicImport.cpp b/ets2panda/compiler/lowering/ets/dynamicImport.cpp new file mode 100644 index 0000000000..08228d3af8 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.cpp @@ -0,0 +1,251 @@ +/** + * Copyright (c) 2025 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 "dynamicImport.h" +#include +#include +#include "checker/ETSchecker.h" +#include "ir/astNode.h" + +#include "compiler/lowering/util.h" +#include "compiler/lowering/scopesInit/scopesInitPhase.h" + +namespace ark::es2panda::compiler { + +using AstNodePtr = ir::AstNode *; +static constexpr std::string_view LAZY_IMPORT_OBJECT_SUFFIX = "%%lazyImportObject-"; +static constexpr std::string_view FIELD_NAME = "value"; + +ir::ClassDeclaration *GetOrCreateLazyImportObjectClass(ark::ArenaAllocator *allocator, + ir::ETSImportDeclaration *importDecl, parser::Program *program) +{ + auto checker = program->Checker()->AsETSChecker(); + auto globalClass = program->GlobalClass(); + auto varbinder = checker->VarBinder()->AsETSBinder(); + auto sourceProgram = checker->SelectEntryOrExternalProgram(varbinder, importDecl->DeclPath()); + + const std::string classNameStr = std::string(LAZY_IMPORT_OBJECT_SUFFIX) + sourceProgram->ModuleName().Mutf8(); + const util::UString className(classNameStr, allocator); + const auto nameView = className.View().Mutf8(); + + auto &classBody = globalClass->BodyForUpdate(); + auto classIt = std::find_if(classBody.begin(), classBody.end(), [&nameView](ir::AstNode *node) { + if (!node->IsClassDeclaration()) { + return false; + } + return node->AsClassDeclaration()->Definition()->Ident()->Name().Mutf8() == nameView; + }); + if (classIt != classBody.end()) { + return (*classIt)->AsClassDeclaration(); + } + + auto *ident = allocator->New(className.View(), allocator); + auto *classDef = util::NodeAllocator::ForceSetParent( + allocator, allocator, ident, ir::ClassDefinitionModifiers::CLASS_DECL, ir::ModifierFlags::ABSTRACT, + Language(Language::Id::ETS)); + + classDef->SetLazyImportObjectClass(); + return util::NodeAllocator::ForceSetParent(allocator, classDef, allocator); +} + +static void AddImportInitializationStatement(public_lib::Context *ctx, ir::ETSImportDeclaration *import, + ArenaVector *statements, util::StringView className) +{ + auto allocator = ctx->GetChecker()->AsETSChecker()->ProgramAllocator(); + const auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language()); + auto [builtinClassName, builtinMethodName] = util::Helpers::SplitSignature(builtin); + + auto *classId = allocator->New(builtinClassName, allocator); + auto *methodId = allocator->New(builtinMethodName, allocator); + auto *callee = util::NodeAllocator::ForceSetParent( + allocator, classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + util::StringView ohmUrl = util::UString(import->OhmUrl(), allocator).View(); + if (ohmUrl.Empty()) { + ohmUrl = import->ResolvedSource(); + if (ark::os::file::File::IsRegularFile(ohmUrl.Mutf8())) { + ohmUrl = util::UString(ark::os::RemoveExtension(ohmUrl.Mutf8()), allocator).View(); + } + } + + ArenaVector callParams(allocator->Adapter()); + callParams.push_back(allocator->New(ohmUrl)); + + auto *loadCall = util::NodeAllocator::ForceSetParent(allocator, callee, std::move(callParams), + nullptr, false); + auto *moduleClassId = allocator->New(className, allocator); + auto *fieldId = allocator->New(FIELD_NAME, allocator); + auto *property = util::NodeAllocator::ForceSetParent( + allocator, moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + moduleClassId->SetParent(property); + fieldId->SetParent(property); + + auto *initializer = util::NodeAllocator::ForceSetParent( + allocator, property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION); + + statements->push_back(util::NodeAllocator::ForceSetParent(allocator, initializer)); +} + +checker::Type *CreateModuleObjectType(public_lib::Context *ctx, ir::ETSImportDeclaration *importDecl) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varbinder = static_cast(checker->VarBinder()->AsETSBinder()); + auto allocator = checker->ProgramAllocator(); + + const auto importPath = importDecl->DeclPath(); + auto program = checker->SelectEntryOrExternalProgram(varbinder, importPath); + if (program == nullptr) { + return checker->GlobalTypeError(); + } + + const auto moduleName = program->ModuleName(); + const auto internalNameStr = std::string(moduleName.Mutf8()) + .append(compiler::Signatures::METHOD_SEPARATOR) + .append(compiler::Signatures::ETS_GLOBAL); + const util::UString internalName(internalNameStr, allocator); + + auto *moduleObjectType = allocator->New( + allocator, moduleName, internalName.View(), + std::make_tuple(program->GlobalClass(), checker::ETSObjectFlags::CLASS, checker->Relation())); + + auto *rootDecl = allocator->New(moduleName); + auto *rootVar = allocator->New(rootDecl, varbinder::VariableFlags::NONE); + rootVar->SetTsType(moduleObjectType); + checker->SetPropertiesForModuleObject(moduleObjectType, importPath, nullptr); + + return moduleObjectType; +} + +static void BuildLazyImportObject(public_lib::Context *ctx, ir::ETSImportDeclaration *importDecl, + parser::Program *program, + ArenaUnorderedMap &moduleMap, + ArenaUnorderedMap &varMap) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varBinder = checker->VarBinder()->AsETSBinder(); + auto allocator = checker->ProgramAllocator(); + + auto declProgram = checker->SelectEntryOrExternalProgram(varBinder, importDecl->DeclPath()); + if (!declProgram->IsDeclForDynamicStaticInterop()) { + return; + } + + auto *classDecl = GetOrCreateLazyImportObjectClass(allocator, importDecl, program); + for (auto specifier : importDecl->Specifiers()) { + auto var = specifier->AsImportSpecifier()->Imported()->Variable(); + var->AddFlag(varbinder::VariableFlags::DYNAMIC); + varMap.insert({var, classDecl->Definition()}); + } + + const auto className = classDecl->Definition()->Ident()->Name(); + if (declProgram->IsASTChecked()) { + checker->SetPropertiesForModuleObject(moduleMap.find(className)->second, importDecl->DeclPath(), nullptr); + return; + } + + auto *objType = CreateModuleObjectType(ctx, importDecl)->AsETSObjectType(); + moduleMap.insert({className, objType}); + + objType->AddObjectFlag(checker::ETSObjectFlags::LAZY_IMPORT_OBJECT); + auto moduleType = checker->CreateGradualType(objType, Language::Id::JS); + + auto parser = ctx->parser->AsETSParser(); + auto *typeAnnotation = allocator->New(moduleType, allocator); + auto *classProp = parser->CreateFormattedClassFieldDefinition(std::string {FIELD_NAME} + ": @@T1", typeAnnotation) + ->AsClassProperty(); + typeAnnotation->SetParent(classProp); + classProp->AddModifier(ir::ModifierFlags::CONST | ir::ModifierFlags::STATIC); + + classDecl->Definition()->EmplaceBody(classProp); + classProp->SetParent(classDecl->Definition()); + + auto initializer = checker->CreateClassStaticInitializer( + [ctx, importDecl, className](ArenaVector *statements, + [[maybe_unused]] ArenaVector *params) { + AddImportInitializationStatement(ctx, importDecl, statements, className); + }); + + classDecl->Definition()->EmplaceBody(initializer); + initializer->SetParent(classDecl->Definition()); + + for (auto specifier : importDecl->Specifiers()) { + varMap.insert({specifier->AsImportSpecifier()->Imported()->Variable(), classDecl->Definition()}); + } + + program->GlobalClass()->EmplaceBody(classDecl); + classDecl->SetParent(program->GlobalClass()); + + auto lexScope = varbinder::LexicalScope::Enter(varBinder, program->GlobalClassScope()); + InitScopesPhaseETS::RunExternalNode(classDecl, varBinder); + varBinder->ResolveReferencesForScopeWithContext(classDecl, varBinder->TopScope()); + classDecl->Check(checker); +} + +static AstNodePtr TransformIdentifier(ir::Identifier *ident, public_lib::Context *ctx, + const ArenaUnorderedMap &varMap) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varBinder = checker->VarBinder()->AsETSBinder(); + auto allocator = checker->ProgramAllocator(); + + const auto parent = ident->Parent(); + if (parent->IsImportSpecifier() || parent->IsScriptFunction() || parent->IsMethodDefinition()) { + return ident; + } + + auto varIt = varMap.find(ident->Variable()); + if (varIt == varMap.end()) { + return ident; + } + + auto *leftId = allocator->New(varIt->second->Ident()->Name(), allocator); + auto *rightId = allocator->New(FIELD_NAME, allocator); + + auto *expr = util::NodeAllocator::ForceSetParent( + allocator, leftId, rightId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + auto *memberExpr = util::NodeAllocator::ForceSetParent( + allocator, expr, ident, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + memberExpr->SetParent(parent); + CheckLoweredNode(varBinder, checker, memberExpr); + + return memberExpr; +} + +bool DynamicImport::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + auto allocator = ctx->Allocator(); + ArenaUnorderedMap varMap {allocator->Adapter()}; + ArenaUnorderedMap moduleMap {allocator->Adapter()}; + + auto dynamicImports = program->VarBinder()->AsETSBinder()->DynamicImports(); + for (auto *importDecl : dynamicImports) { + BuildLazyImportObject(ctx, importDecl, program, moduleMap, varMap); + } + + program->Ast()->TransformChildrenRecursively( + [ctx, &varMap](ir::AstNode *node) -> AstNodePtr { + if (node->IsIdentifier() && node->AsIdentifier()->Variable() != nullptr) { + return TransformIdentifier(node->AsIdentifier(), ctx, varMap); + } + return node; + }, + Name()); + + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/dynamicImport.h b/ets2panda/compiler/lowering/ets/dynamicImport.h new file mode 100644 index 0000000000..78af90c013 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025 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_LOWERING_LAZY_IMPORT_OBJECT_H +#define ES2PANDA_COMPILER_LOWERING_LAZY_IMPORT_OBJECT_H + +#include "compiler/lowering/phase.h" +#include "utils/arena_containers.h" + +namespace ark::es2panda::compiler { + +class DynamicImport : public PhaseForBodies { +public: + std::string_view Name() const override + { + return "DynamicImport"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; +}; +} // namespace ark::es2panda::compiler + +#endif // ES2PANDA_COMPILER_LOWERING_TYPE_FOR_LOWERING_H \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp new file mode 100644 index 0000000000..778b1a5949 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 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 "gradualTypeNarrowing.h" + +#include "checker/ETSchecker.h" +#include "checker/types/ets/etsTupleType.h" +#include "checker/types/gradualType.h" +#include "es2panda.h" +#include "ir/astNode.h" +#include "ir/opaqueTypeNode.h" +#include "ir/typed.h" +#include "util/language.h" + +namespace ark::es2panda::compiler { + +checker::Type *GradualTypeNarrowing::TransformType(checker::Type *type, + const std::function &func) +{ + if (type->IsETSFunctionType()) { + auto funcType = type->AsETSFunctionType(); + for (auto sig : funcType->CallSignaturesOfMethodOrArrow()) { + sig->SetReturnType(TransformType(sig->ReturnType(), func)); + for (auto var : sig->Params()) { + var->SetTsType(TransformType(var->TsType(), func)); + } + + if (sig->RestVar() != nullptr) { + sig->RestVar()->SetTsType(TransformType(sig->RestVar()->TsType(), func)); + } + } + } + + if (type->IsETSUnionType()) { + auto unionType = type->AsETSUnionType(); + ArenaVector types {checker_->ProgramAllocator()->Adapter()}; + for (auto ctype : unionType->ConstituentTypes()) { + types.push_back(TransformType(ctype, func)); + } + type = checker_->CreateETSUnionType(std::move(types)); + } + + if (type->IsETSArrayType()) { + auto arrayType = type->AsETSArrayType(); + arrayType->SetElementType(TransformType(arrayType->ElementType(), func)); + } + + if (type->IsETSResizableArrayType()) { + auto arrayType = type->AsETSResizableArrayType(); + arrayType->SetElementType(TransformType(arrayType->ElementType(), func)); + } + + if (type->IsETSTupleType()) { + auto tupleType = type->AsETSTupleType(); + ArenaVector types {checker_->ProgramAllocator()->Adapter()}; + for (auto ctype : tupleType->GetTupleTypesList()) { + types.push_back(TransformType(ctype, func)); + } + type = checker_->ProgramAllocator()->New(checker_, std::move(types)); + } + + return func(type); +} + +void GradualTypeNarrowing::NarrowGradualType(ir::AstNode *node) +{ + auto typedNode = node->AsTyped(); + auto typeTransformFunc = [this](checker::Type *type) -> checker::TypePtr { + if (type->IsGradualType()) { + return type->AsGradualType()->GetBaseType()->IsETSTypeParameter() + ? type->AsGradualType()->GetBaseType()->AsETSTypeParameter()->GetConstraintType() + : this->checker_->GlobalETSRelaxedAnyType(); + } + return type; + }; + + if (typedNode->TsType() != nullptr) { + typedNode->SetTsType(TransformType(typedNode->TsType(), typeTransformFunc)); + } + + auto var = node->Variable(); + if (var != nullptr && var->TsType() != nullptr) { + var->SetTsType(TransformType(var->TsType(), typeTransformFunc)); + } +} + +ir::AstNode *GradualTypeNarrowing::ProcessGradualTypeNode(ir::ETSTypeReference *node) +{ + auto type = node->GetType(checker_); + if (!type->IsGradualType()) { + return node; + } + + // Only narrow explicit type annotation of gradual to T or to Any + if (!node->Part()->Name()->IsIdentifier() || + !(node->Part()->Name()->AsIdentifier()->Name() == compiler::Signatures::GRADUAL_TYPE_NAME)) { + return node; + } + auto loweredNode = context_->AllocNode(checker_->GlobalETSAnyType(), context_->Allocator()); + loweredNode->SetRange(node->Range()); + loweredNode->SetParent(node->Parent()); + loweredNode->SetTsType(checker_->GlobalETSRelaxedAnyType()); + return loweredNode; +} + +bool GradualTypeNarrowing::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + context_ = ctx; + checker_ = ctx->GetChecker()->AsETSChecker(); + + program->Ast()->TransformChildrenRecursively( + // CC-OFFNXT(G.FMT.14-CPP) project code style + [this](ir::AstNode *ast) -> checker::AstNodePtr { + if (ast->IsETSTypeReference()) { + return ProcessGradualTypeNode(ast->AsETSTypeReference()); + } + return ast; + }, + Name()); + + program->Ast()->IterateRecursively([this](ir::AstNode *ast) { + if (ast->IsTyped()) { + NarrowGradualType(ast); + } + }); + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h new file mode 100644 index 0000000000..e4a09c4ab9 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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_GRADUAL_TYPE_NARROWING_H +#define ES2PANDA_GRADUAL_TYPE_NARROWING_H + +#include "compiler/lowering/phase.h" +#include "ir/typeNode.h" + +namespace ark::es2panda::compiler { + +class GradualTypeNarrowing : public PhaseForBodies { +public: + std::string_view Name() const override + { + return "GradualTypeNarrowing"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; + +private: + checker::Type *TransformType(checker::Type *type, const std::function &func); + void NarrowGradualType(ir::AstNode *node); + ir::AstNode *ProcessGradualTypeNode(ir::ETSTypeReference *node); + + public_lib::Context *context_ {nullptr}; + checker::ETSChecker *checker_ {nullptr}; +}; +} // namespace ark::es2panda::compiler + +#endif \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp index c88ab3572b..2833087baa 100644 --- a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp @@ -35,15 +35,13 @@ std::string_view InterfaceObjectLiteralLowering::Name() const static inline bool IsInterfaceType(const checker::Type *type) { return type != nullptr && type->IsETSObjectType() && - type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) && - !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) && !type->IsETSAnyType(); } static inline bool IsAbstractClassType(const checker::Type *type) { return type != nullptr && type->IsETSObjectType() && - type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && - !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && !type->IsETSAnyType(); } static ir::AstNode *CreateAnonClassImplCtor(public_lib::Context *ctx, ArenaVector &readonlyFields) @@ -232,7 +230,7 @@ static void GenerateAnonClassTypeFromInterface(public_lib::Context *ctx, ir::TSI auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder); RefineSourceRanges(classDecl); auto *classDef = classDecl->Definition(); - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); classDef->SetAnonymousModifier(); classDecl->SetRange(ifaceNode->Range()); @@ -287,7 +285,7 @@ static void GenerateAnonClassTypeFromAbstractClass(public_lib::Context *ctx, ir: auto *classDecl = checker->BuildClass(anonClassName.View(), classBodyBuilder); RefineSourceRanges(classDecl); auto *classDef = classDecl->Definition(); - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); classDecl->SetRange(abstractClassNode->Range()); classDef->SetAnonymousModifier(); @@ -389,7 +387,7 @@ static bool CheckInterfaceShouldGenerateAnonClass(ir::TSInterfaceDeclaration *in static bool CheckAbstractClassShouldGenerateAnonClass(ir::ClassDefinition *classDef) { - auto constructorSigs = classDef->TsType()->AsETSObjectType()->ConstructSignatures(); + auto constructorSigs = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()->ConstructSignatures(); if (auto res = std::find_if(constructorSigs.cbegin(), constructorSigs.cend(), [](checker::Signature *sig) -> bool { return sig->MinArgCount() == 0; }); res == constructorSigs.cend()) { diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 00485472a3..5e4836e445 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -1235,7 +1235,8 @@ static ir::AstNode *BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::Ast // We are running this lowering only for ETS files // so it is correct to pass ETS extension here to isReference() if (id->IsReference(ScriptExtension::ETS) && id->TsType() != nullptr && id->TsType()->IsETSFunctionType() && - !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) { + !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var) && + !id->Variable()->HasFlag(varbinder::VariableFlags::DYNAMIC)) { return ConvertFunctionReference(ctx, id); } } diff --git a/ets2panda/compiler/lowering/ets/localClassLowering.cpp b/ets2panda/compiler/lowering/ets/localClassLowering.cpp index 53e9ecdb71..439208e640 100644 --- a/ets2panda/compiler/lowering/ets/localClassLowering.cpp +++ b/ets2panda/compiler/lowering/ets/localClassLowering.cpp @@ -274,6 +274,9 @@ bool LocalClassConstructionPhase::PerformForModule(public_lib::Context *ctx, par if (ast->IsETSNewClassInstanceExpression()) { auto *newExpr = ast->AsETSNewClassInstanceExpression(); checker::Type *calleeType = newExpr->GetTypeRef()->Check(checker); + if (calleeType->IsETSAnyType()) { + return; + } ES2PANDA_ASSERT(calleeType->IsETSObjectType()); auto *calleeObj = calleeType->AsETSObjectType(); diff --git a/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp b/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp index 5e42c84c84..8382b1dd2c 100644 --- a/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp +++ b/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp @@ -24,6 +24,7 @@ #include "objectIndexAccess.h" #include "checker/ETSchecker.h" +#include "checker/types/typeFlag.h" #include "compiler/lowering/util.h" #include "parser/ETSparser.h" #include "util/options.h" @@ -110,10 +111,11 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro program->Ast()->TransformChildrenRecursively( // CC-OFFNXT(G.FMT.14-CPP) project code style [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { - if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression() && - ast->AsAssignmentExpression()->Left()->AsMemberExpression()->Kind() == - ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType() != nullptr) { + if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression()) { + auto memberExpr = ast->AsAssignmentExpression()->Left()->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && + memberExpr->AsMemberExpression()->ObjType() != nullptr && + !memberExpr->Object()->TsType()->IsETSAnyType()) { return ProcessIndexSetAccess(parser, checker, ast->AsAssignmentExpression()); } } @@ -124,9 +126,10 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro program->Ast()->TransformChildrenRecursively( // CC-OFFNXT(G.FMT.14-CPP) project code style [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { - if (ast->IsMemberExpression() && - ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsMemberExpression()->ObjType() != nullptr) { + if (ast->IsMemberExpression()) { + auto memberExpr = ast->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && + memberExpr->ObjType() != nullptr && !memberExpr->Object()->TsType()->IsETSAnyType()) { return ProcessIndexGetAccess(parser, checker, ast->AsMemberExpression()); } } @@ -141,9 +144,10 @@ bool ObjectIndexLowering::PostconditionForModule([[maybe_unused]] public_lib::Co const parser::Program *program) { return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) { - if (ast->IsMemberExpression() && - ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (auto const *const objectType = ast->AsMemberExpression()->ObjType(); objectType != nullptr) { + if (ast->IsMemberExpression()) { + auto memberExpr = ast->AsMemberExpression(); + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && memberExpr->ObjType() != nullptr && + !memberExpr->Object()->TsType()->IsETSAnyType()) { return true; } } diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index a01f89264f..8e0fbcfb05 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -301,8 +301,7 @@ bool ObjectLiteralLowering::PerformForModule(public_lib::Context *ctx, parser::P // Skip processing invalid and dynamic objects if (ast->IsObjectExpression()) { auto *exprType = ast->AsObjectExpression()->TsType(); - if (exprType != nullptr && exprType->IsETSObjectType() && - !exprType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { + if (exprType != nullptr && exprType->IsETSObjectType()) { return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression()); } } @@ -318,8 +317,7 @@ bool ObjectLiteralLowering::PostconditionForModule([[maybe_unused]] public_lib:: { // In all object literal contexts (except dynamic) a substitution should take place return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool { - return ast->IsObjectExpression() && ast->AsObjectExpression()->TsType()->IsETSObjectType() && - !ast->AsObjectExpression()->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC); + return ast->IsObjectExpression() && ast->AsObjectExpression()->TsType()->IsETSObjectType(); }); } diff --git a/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp b/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp index 94ce179427..9363c7e363 100644 --- a/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp @@ -43,13 +43,6 @@ static void TransformArguments(public_lib::Context *ctx, ir::Expression *callLik return; } ES2PANDA_ASSERT(signature->ArgCount() >= signature->MinArgCount()); - if (arguments.size() < signature->MinArgCount()) { // #22952: workaround for dynamic types - auto callee = callLike->IsCallExpression() ? callLike->AsCallExpression()->Callee() - : callLike->AsETSNewClassInstanceExpression()->GetTypeRef(); - if (callee->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)) { - return; - } - } ES2PANDA_ASSERT((callLike->IsCallExpression() && callLike->AsCallExpression()->IsTrailingCall()) || arguments.size() >= signature->MinArgCount()); diff --git a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp index f0435fb4c0..9fe4183e9b 100644 --- a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp @@ -200,7 +200,9 @@ ir::CallExpression *RestArgsLowering::TransformCallExpressionWithRestArgs(ir::Ca public_lib::Context *context) { checker::Type *calleeType = callExpr->Callee()->TsType(); - if (calleeType == nullptr || calleeType->IsETSArrowType()) { + if (calleeType == nullptr || calleeType->IsETSArrowType() || + (callExpr->Callee()->IsMemberExpression() && callExpr->Callee()->AsMemberExpression()->PropVar() != nullptr && + callExpr->Callee()->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return callExpr; } diff --git a/ets2panda/compiler/lowering/ets/unboxLowering.cpp b/ets2panda/compiler/lowering/ets/unboxLowering.cpp index 1ad03961fd..fc2f51d866 100644 --- a/ets2panda/compiler/lowering/ets/unboxLowering.cpp +++ b/ets2panda/compiler/lowering/ets/unboxLowering.cpp @@ -17,6 +17,7 @@ #include "ir/visitor/IterateAstVisitor.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsTupleType.h" +#include "checker/types/typeFlag.h" #include "checker/types/globalTypesHolder.h" #include "compiler/lowering/util.h" @@ -244,7 +245,7 @@ static void NormalizeAllTypes(UnboxContext *uctx, ir::AstNode *ast) return child; } auto typeNodeType = child->AsExpression()->AsTypeNode()->GetType(uctx->checker); - if (typeNodeType == nullptr) { + if (typeNodeType == nullptr || typeNodeType->IsETSAnyType()) { return child; } auto r = uctx->allocator->New(NormalizeType(uctx, typeNodeType), @@ -315,12 +316,20 @@ static void HandleScriptFunctionHeader(UnboxContext *uctx, ir::ScriptFunction *f uctx->varbinder->BuildFunctionName(func); } -static void HandleClassProperty(UnboxContext *uctx, ir::ClassProperty *prop) +static void HandleClassProperty(UnboxContext *uctx, ir::ClassProperty *prop, bool forceUnbox = false) { auto *propType = prop->TsType(); if (propType == nullptr) { propType = prop->Key()->Variable()->TsType(); } + // Primitive Types from JS should be Boxed, but in case of annotation, it should be unboxed. + ir::AstNode *node = prop; + while (node != nullptr && !node->IsETSModule()) { + node = node->Parent(); + } + if (node != nullptr && node->AsETSModule()->Program()->IsDeclForDynamicStaticInterop() && !forceUnbox) { + return; + } ES2PANDA_ASSERT(propType != nullptr); if (IsUnboxingApplicable(propType) && prop->Key()->IsIdentifier()) { auto *unboxedType = MaybeRecursivelyUnboxType(uctx, propType); @@ -819,9 +828,8 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP, G.FUD.05) solid logic void VisitCallExpression(ir::CallExpression *call) override { - auto *func = call->Signature()->Function(); - if (func == nullptr) { - // some lambda call, all arguments and return type need to be boxed + if (!call->Signature()->HasFunction() || call->Signature()->Function()->Language() == Language::Id::JS) { + // some lambda call and dynamic call to js, all arguments and return type need to be boxed // NOLINTNEXTLINE(modernize-loop-convert) for (size_t i = 0; i < call->Arguments().size(); i++) { auto *arg = call->Arguments()[i]; @@ -830,6 +838,8 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { return; } + auto *func = call->Signature()->Function(); + HandleDeclarationNode(uctx_, func); // NOLINTNEXTLINE(modernize-loop-convert) for (size_t i = 0; i < call->Arguments().size(); i++) { @@ -890,6 +900,16 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { void VisitETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *call) override { auto *func = call->GetSignature()->Function(); + if (func == nullptr || func->Language() == Language::Id::JS) { + // For dynamic call to js, all arguments and return type need to be boxed + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < call->GetArguments().size(); i++) { + auto *arg = call->GetArguments()[i]; + call->GetArguments()[i] = AdjustType(uctx_, arg, uctx_->checker->MaybeBoxType(arg->TsType())); + } + return; + } + HandleDeclarationNode(uctx_, func); for (size_t i = 0; i < call->GetArguments().size(); i++) { @@ -1131,7 +1151,7 @@ struct UnboxVisitor : public ir::visitor::EmptyAstVisitor { if (mexpr->Property()->Variable()->Declaration() != nullptr && mexpr->Property()->Variable()->Declaration()->Node() != nullptr && mexpr->Property()->Variable()->Declaration()->Node()->IsTyped() && - mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType() != nullptr) { + !mexpr->Object()->TsType()->IsETSAnyType()) { HandleDeclarationNode(uctx_, mexpr->Property()->Variable()->Declaration()->Node()); propType = mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType(); } else if (mexpr->Property()->Variable()->TsType() != nullptr) { @@ -1422,7 +1442,7 @@ static void VisitExternalPrograms(UnboxVisitor *visitor, parser::Program *progra auto annotationIterator = [visitor](auto *child) { if (child->IsClassProperty()) { auto prop = child->AsClassProperty(); - HandleClassProperty(visitor->uctx_, prop); + HandleClassProperty(visitor->uctx_, prop, true); if (prop->Value() != nullptr) { ES2PANDA_ASSERT(prop->Value()->IsLiteral() || prop->Value()->IsArrayExpression() || (prop->Value()->IsTyped() && prop->Value()->AsTyped()->TsType()->IsETSEnumType())); diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index c321ed36b8..be67b7ac50 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -39,10 +39,12 @@ #include "compiler/lowering/ets/expressionLambdaLowering.h" #include "compiler/lowering/ets/extensionAccessorLowering.h" #include "compiler/lowering/ets/genericBridgesLowering.h" +#include "compiler/lowering/ets/gradualTypeNarrowing.h" #include "compiler/lowering/ets/insertOptionalParametersAnnotation.h" #include "compiler/lowering/ets/interfaceObjectLiteralLowering.h" #include "compiler/lowering/ets/interfacePropertyDeclarations.h" #include "compiler/lowering/ets/lambdaLowering.h" +#include "compiler/lowering/ets/dynamicImport.h" #include "compiler/lowering/ets/localClassLowering.h" #include "compiler/lowering/ets/objectIndexAccess.h" #include "compiler/lowering/ets/objectIterator.h" @@ -124,6 +126,8 @@ std::vector GetETSPhaseList() // pluginsAfterCheck has to go right after checkerPhase, nothing should be between them new PluginPhase {g_pluginsAfterCheck, ES2PANDA_STATE_CHECKED, &util::Plugin::AfterCheck}, // new ConvertPrimitiveCastMethodCall, + new DynamicImport, + new GradualTypeNarrowing, new AnnotationCopyPostLowering, new AsyncMethodLowering, new DeclareOverloadLowering, diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index b81f32d845..a4c4e1336a 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -19,6 +19,7 @@ #include "ir/expressions/identifier.h" #include "checker/checker.h" #include "checker/ETSAnalyzer.h" +#include "checker/types/gradualType.h" namespace ark::es2panda::compiler { @@ -365,8 +366,12 @@ void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *chec if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) { newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK; } - auto checkerCtx = checker::SavedCheckerContext( - checker, newStatus, containingClass != nullptr ? containingClass->TsType()->AsETSObjectType() : nullptr); + + auto classType = containingClass == nullptr ? nullptr + : containingClass->TsType()->IsGradualType() + ? containingClass->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : containingClass->TsType()->AsETSObjectType(); + auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, classType); auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 7cfa309206..490ec79695 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -157,6 +157,8 @@ defines: ref: REQUIRED_TYPE_NAME - name: 'Array' ref: ARRAY + - name: 'gradual' + ref: GRADUAL_TYPE_NAME - name: '' ref: PROPERTY - name: 'Any' diff --git a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp index c1629690a4..bc6861126e 100644 --- a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp +++ b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp @@ -93,11 +93,10 @@ void TSDeclGen::ProcessTypeAliasDependencies(const ir::TSTypeAliasDeclaration *t if (!parent->IsExported() && !parent->IsDefaultExported()) { return; } - if (typeFlag == checker::TypeFlag::ETS_OBJECT || typeFlag == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (typeFlag == checker::TypeFlag::ETS_OBJECT) { auto objectType = aliasedType->AsETSObjectType(); if (objectType->IsETSStringType() || objectType->IsETSBigIntType() || objectType->IsETSUnboxableObject() || - objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL) || - objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { + objectType->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL)) { return; } auto typeName = objectType->Name(); @@ -210,7 +209,7 @@ void TSDeclGen::ProcessClassMethodDependencies(const ir::MethodDefinition *metho void TSDeclGen::AddSuperType(const ir::Expression *super) { const auto superType = checker::ETSChecker::ETSType(super->TsType()); - if (superType == checker::TypeFlag::ETS_OBJECT || superType == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (superType == checker::TypeFlag::ETS_OBJECT) { auto objectType = super->TsType()->AsETSObjectType(); AddObjectDependencies(objectType->Name()); } @@ -219,7 +218,7 @@ void TSDeclGen::AddSuperType(const ir::Expression *super) void TSDeclGen::AddSuperType(const checker::Type *tsType) { const auto superType = checker::ETSChecker::ETSType(tsType); - if (superType == checker::TypeFlag::ETS_OBJECT || superType == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + if (superType == checker::TypeFlag::ETS_OBJECT) { auto objectType = tsType->AsETSObjectType(); AddObjectDependencies(objectType->Name()); } @@ -230,8 +229,7 @@ void TSDeclGen::ProcessInterfacesDependencies(const ArenaVectorName()); } }, @@ -422,7 +420,6 @@ bool TSDeclGen::HandleETSSpecificTypes(const checker::Type *checkerType) return true; case checker::TypeFlag::ETS_OBJECT: - case checker::TypeFlag::ETS_DYNAMIC_TYPE: return HandleObjectType(checkerType); case checker::TypeFlag::ETS_ARRAY: @@ -784,10 +781,6 @@ bool TSDeclGen::HandleSpecificObjectTypes(const checker::ETSObjectType *objectTy GenType(invoke); return true; } - if (objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { - OutDts("any"); - return true; - } return false; } diff --git a/ets2panda/ir/base/classDefinition.h b/ets2panda/ir/base/classDefinition.h index fd71f32cd3..d1a6f415bb 100644 --- a/ets2panda/ir/base/classDefinition.h +++ b/ets2panda/ir/base/classDefinition.h @@ -59,6 +59,7 @@ enum class ClassDefinitionModifiers : uint32_t { INT_ENUM_TRANSFORMED = 1U << 15U, FROM_STRUCT = 1U << 16U, FUNCTIONAL_REFERENCE = 1U << 17U, + LAZY_IMPORT_OBJECT_CLASS = 1U << 18U, DECLARATION_ID_REQUIRED = DECLARATION | ID_REQUIRED, ETS_MODULE = NAMESPACE_TRANSFORMED | GLOBAL }; @@ -272,6 +273,11 @@ public: return (Modifiers() & ClassDefinitionModifiers::NAMESPACE_TRANSFORMED) != 0; } + [[nodiscard]] bool IsLazyImportObjectClass() const noexcept + { + return (Modifiers() & ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS) != 0; + } + [[nodiscard]] bool IsFromStruct() const noexcept { return (Modifiers() & ClassDefinitionModifiers::FROM_STRUCT) != 0; @@ -312,6 +318,11 @@ public: AddClassModifiers(ClassDefinitionModifiers::NAMESPACE_TRANSFORMED); } + void SetLazyImportObjectClass() noexcept + { + AddClassModifiers(ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS); + } + void SetFromStructModifier() noexcept { AddClassModifiers(ClassDefinitionModifiers::FROM_STRUCT); diff --git a/ets2panda/ir/ets/etsTypeReference.cpp b/ets2panda/ir/ets/etsTypeReference.cpp index 7986198c72..5ad9abc912 100644 --- a/ets2panda/ir/ets/etsTypeReference.cpp +++ b/ets2panda/ir/ets/etsTypeReference.cpp @@ -61,13 +61,23 @@ ir::Identifier *ETSTypeReference::BaseName() const return baseName->AsIdentifier(); } - ir::TSQualifiedName *nameIter = baseName->AsTSQualifiedName(); + if (baseName->IsIdentifier()) { + return baseName->AsIdentifier(); + } + if (baseName->IsTSQualifiedName()) { + ir::TSQualifiedName *iter = baseName->AsTSQualifiedName(); - while (nameIter->Left()->IsTSQualifiedName()) { - nameIter = nameIter->Left()->AsTSQualifiedName(); + while (iter->Left()->IsTSQualifiedName()) { + iter = iter->Left()->AsTSQualifiedName(); + } + return iter->Left()->AsIdentifier(); } + ir::MemberExpression *iter = baseName->AsMemberExpression(); - return nameIter->Left()->AsIdentifier(); + while (iter->Property()->IsMemberExpression()) { + iter = iter->Property()->AsMemberExpression(); + } + return iter->Property()->AsIdentifier(); } void ETSTypeReference::Dump(ir::AstDumper *dumper) const diff --git a/ets2panda/ir/ets/etsTypeReferencePart.cpp b/ets2panda/ir/ets/etsTypeReferencePart.cpp index 8ebd5258b6..8a79259d94 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.cpp +++ b/ets2panda/ir/ets/etsTypeReferencePart.cpp @@ -234,6 +234,20 @@ checker::Type *ETSTypeReferencePart::HandleInternalTypes(checker::ETSChecker *co return nullptr; } +checker::Type *ETSTypeReferencePart::HandlerResultType(checker::ETSChecker *checker, checker::Type *baseType) +{ + if (checker->IsDeclForDynamicStaticInterop() && baseType->IsETSTypeParameter()) { + return checker->CreateGradualType(baseType); + } + if (baseType->IsGradualType()) { + return checker->CreateGradualType(HandlerResultType(checker, baseType->MaybeBaseTypeOfGradualType())); + } + if (baseType->IsETSObjectType()) { + checker::InstantiationContext ctx(checker, baseType->AsETSObjectType(), TypeParams(), Start()); + return ctx.Result(); + } + return baseType; +} checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) { @@ -253,14 +267,8 @@ checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) if (TsType() == nullptr) { checker::Type *baseType = checker->GetReferencedTypeBase(name); - ES2PANDA_ASSERT(baseType != nullptr); - if (baseType->IsETSObjectType()) { - checker::InstantiationContext ctx(checker, baseType->AsETSObjectType(), TypeParams(), Start()); - SetTsType(ctx.Result()); - } else { - SetTsType(baseType); - } + SetTsType(HandlerResultType(checker, baseType)); } } else { checker::Type *baseType = Previous()->GetType(checker); diff --git a/ets2panda/ir/ets/etsTypeReferencePart.h b/ets2panda/ir/ets/etsTypeReferencePart.h index 9fe657e930..40af6ab2b6 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.h +++ b/ets2panda/ir/ets/etsTypeReferencePart.h @@ -95,6 +95,7 @@ public: void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + checker::Type *HandlerResultType(checker::ETSChecker *checker, checker::Type *baseType); checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; ir::Identifier *GetIdent(); diff --git a/ets2panda/ir/expressions/callExpression.cpp b/ets2panda/ir/expressions/callExpression.cpp index a7bfa507bb..4333ae3e9b 100644 --- a/ets2panda/ir/expressions/callExpression.cpp +++ b/ets2panda/ir/expressions/callExpression.cpp @@ -176,4 +176,11 @@ bool CallExpression::IsExtensionAccessorCall() return (Signature() != nullptr) && (Signature()->Function()->IsExtensionAccessor()); } +bool CallExpression::IsDynamicCall() const noexcept +{ + return Callee()->IsMemberExpression() && Callee()->AsMemberExpression()->PropVar() != nullptr && + (Callee()->AsMemberExpression()->Object()->TsType()->IsETSAnyType() || + Callee()->AsMemberExpression()->Object()->TsType()->IsGradualType()); +} + } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/expressions/callExpression.h b/ets2panda/ir/expressions/callExpression.h index 1dc57dca97..53e6a1a108 100644 --- a/ets2panda/ir/expressions/callExpression.h +++ b/ets2panda/ir/expressions/callExpression.h @@ -164,6 +164,8 @@ public: return callee_->IsThisExpression() || callee_->IsSuperExpression(); } + bool IsDynamicCall() const noexcept; + [[nodiscard]] CallExpression *Clone(ArenaAllocator *allocator, AstNode *parent) override; void TransformChildren(const NodeTransformer &cb, std::string_view transformationName) override; diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 8db555d704..4fde4e957d 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -21,6 +21,7 @@ #include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" #include "util/diagnostic.h" +#include "util/es2pandaMacros.h" namespace ark::es2panda::ir { MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other, @@ -449,8 +450,38 @@ static void CastTupleElementFromClassMemberType(checker::ETSChecker *checker, tupleElementAccessor->Start(), checker::TypeRelationFlag::NO_THROW}); } +checker::Type *MemberExpression::HandleComputedInGradualType(checker::ETSChecker *checker, checker::Type *baseType) +{ + property_->Check(checker); + if (baseType->IsETSObjectType()) { + util::StringView searchName; + if (property_->IsLiteral()) { + searchName = util::StringView {property_->AsLiteral()->ToString()}; + } + auto found = baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_ALL); + if (found == nullptr) { + // Try to find indexer method + checker::Type *indexType = CheckIndexAccessMethod(checker); + if (indexType != nullptr) { + return indexType; + } + checker->LogError(diagnostic::PROPERTY_NONEXISTENT, {searchName, baseType->AsETSObjectType()->Name()}, + property_->Start()); + return nullptr; + } + return found->TsType(); + } + ES2PANDA_UNREACHABLE(); + return nullptr; +} + checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType) { + if (baseType->IsGradualType()) { + auto objType = baseType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + SetObjectType(objType); + return HandleComputedInGradualType(checker, objType); + } if (baseType->IsETSArrayType()) { auto *dflt = baseType->AsETSArrayType()->ElementType(); if (!checker->ValidateArrayIndex(property_)) { diff --git a/ets2panda/ir/expressions/memberExpression.h b/ets2panda/ir/expressions/memberExpression.h index 4906dc35d2..4c72e652fe 100644 --- a/ets2panda/ir/expressions/memberExpression.h +++ b/ets2panda/ir/expressions/memberExpression.h @@ -251,6 +251,7 @@ private: checker::Type *TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType); bool CheckArrayIndexValue(checker::ETSChecker *checker) const; + checker::Type *HandleComputedInGradualType(checker::ETSChecker *checker, checker::Type *baseType); checker::Type *CheckIndexAccessMethod(checker::ETSChecker *checker); checker::Type *ResolveReturnTypeFromSignature(checker::ETSChecker *checker, bool isSetter, ArenaVector &arguments, diff --git a/ets2panda/ir/ts/tsTypeReference.cpp b/ets2panda/ir/ts/tsTypeReference.cpp index 57b8f7d2ae..e6748d33c6 100644 --- a/ets2panda/ir/ts/tsTypeReference.cpp +++ b/ets2panda/ir/ts/tsTypeReference.cpp @@ -91,14 +91,20 @@ ir::Identifier *TSTypeReference::BaseName() const if (typeName_->IsIdentifier()) { return typeName_->AsIdentifier(); } + if (typeName_->IsTSQualifiedName()) { + ir::TSQualifiedName *iter = typeName_->AsTSQualifiedName(); - ir::TSQualifiedName *iter = typeName_->AsTSQualifiedName(); - - while (iter->Left()->IsTSQualifiedName()) { - iter = iter->Left()->AsTSQualifiedName(); + while (iter->Left()->IsTSQualifiedName()) { + iter = iter->Left()->AsTSQualifiedName(); + } + return iter->Left()->AsIdentifier(); } + ir::MemberExpression *iter = typeName_->AsMemberExpression(); - return iter->Left()->AsIdentifier(); + while (iter->Property()->IsMemberExpression()) { + iter = iter->Property()->AsMemberExpression(); + } + return iter->Property()->AsIdentifier(); } checker::Type *TSTypeReference::Check([[maybe_unused]] checker::TSChecker *checker) diff --git a/ets2panda/lsp/src/isolated_declaration.cpp b/ets2panda/lsp/src/isolated_declaration.cpp index d4df74d45d..77f06d2da7 100644 --- a/ets2panda/lsp/src/isolated_declaration.cpp +++ b/ets2panda/lsp/src/isolated_declaration.cpp @@ -214,7 +214,7 @@ std::optional HandleETSSpecificTypes(const checker::Type *checkerTy return checkerType->ToString(); case checker::TypeFlag::ETS_OBJECT: - case checker::TypeFlag::ETS_DYNAMIC_TYPE: + case checker::TypeFlag::GRADUAL_TYPE: return HandleObjectType(checkerType, checker); case checker::TypeFlag::ETS_ARRAY: diff --git a/ets2panda/parser/program/program.h b/ets2panda/parser/program/program.h index c29543942c..13762717ed 100644 --- a/ets2panda/parser/program/program.h +++ b/ets2panda/parser/program/program.h @@ -276,6 +276,11 @@ public: return moduleInfo_.kind == util::ModuleKind::PACKAGE; } + bool IsDeclForDynamicStaticInterop() const + { + return moduleInfo_.isDeclForDynamicStaticInterop; + } + void SetFlag(ProgramFlags flag); bool GetFlag(ProgramFlags flag) const; void SetASTChecked(); diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index c84184cf5f..78467d1449 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(arktsconfig-parser) add_subdirectory(annotations) add_subdirectory(lsp) add_subdirectory(relative_path) +add_subdirectory(any_ins_test) ets2panda_add_gtest(es2panda_astdumper_tests CPP_SOURCES ast_dumper_test.cpp diff --git a/ets2panda/test/unit/any_ins_test/CMakeLists.txt b/ets2panda/test/unit/any_ins_test/CMakeLists.txt new file mode 100644 index 0000000000..b41cb7c83f --- /dev/null +++ b/ets2panda/test/unit/any_ins_test/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2025 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. + + +ets2panda_add_gtest(any_ins_test + CPP_SOURCES any_ins_test.cpp +) diff --git a/ets2panda/test/unit/any_ins_test/any_ins_test.cpp b/ets2panda/test/unit/any_ins_test/any_ins_test.cpp new file mode 100644 index 0000000000..e6bda882e2 --- /dev/null +++ b/ets2panda/test/unit/any_ins_test/any_ins_test.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2025 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 +#include +#include "assembly-function.h" +#include "assembly-ins.h" +#include "assembly-parser.h" +#include "assembly-program.h" +#include "test/utils/checker_test.h" + +namespace ark::es2panda::compiler::test { + +class AnyInsTest : public ::test::utils::CheckerTest { +public: + AnyInsTest() = default; + + ~AnyInsTest() override = default; + + void AssertInstructions(const pandasm::Function &func, const std::vector &expectedInstructions) + { + size_t index = 0; + size_t expectedInsIndex = 0; + while (index < func.ins.size()) { + if (expectedInsIndex >= expectedInstructions.size()) { + break; + } + if (func.ins.at(index).opcode == expectedInstructions[expectedInsIndex]) { + expectedInsIndex++; + } + index++; + } + ASSERT_EQ(expectedInsIndex, expectedInstructions.size()) + << "Expected instructions order do not match actual instructions in function: " << func.name; + } + + void AssertFunctionInstructions(pandasm::Program *program, const std::string &functionName, + const std::vector &expectedInstructions) + { + const auto &functionTable = program->functionStaticTable; + + auto found = functionTable.find(functionName); + ASSERT_NE(found, functionTable.end()) << "Function not found: " << functionName; + + AssertInstructions(found->second, expectedInstructions); + } + +private: + NO_COPY_SEMANTIC(AnyInsTest); + NO_MOVE_SEMANTIC(AnyInsTest); +}; + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_01) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + x: X + + constructor() + constructor(x: X) + constructor(p1: number, p2: string, p3: string) + } + + function foo() { + let x: X = new X() + x = new X(x) + x = new X(1, "", "") + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = { + pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_NEW_SHORT, pandasm::Opcode::ANY_CALL_NEW_RANGE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_02) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + x: X + + constructor() + } + + function foo() { + let x: X = new X() + x.x = new X() + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_NEW_0, + pandasm::Opcode::ANY_STBYNAME}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_03) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + constructor() + } + + function foo() { + let x: X = new X() + x instanceof X + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSRelaxedAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_ISINSTANCE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_04) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + constructor() + + foo(): void + foo(x: X): void + foo(p1: number, p2: string, p3: string): void + } + + function bar() { + let x : X = new X() + x.foo() + x.foo(x) + x.foo(1, "", "") + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "String") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + if (tstype != nullptr && tstype->IsETSFunctionType() && tstype->AsETSFunctionType()->Name() == "foo") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_CALL_NEW_0, pandasm::Opcode::ANY_CALL_THIS_0, + pandasm::Opcode::ANY_CALL_THIS_SHORT, + pandasm::Opcode::ANY_CALL_THIS_RANGE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.bar:void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_05) +{ + std::string_view text = R"( + // Assume this is dynamic class + export declare class X { + s: string + } + + class Y { + s: string = "" + } + + function foo(v: X | Y) { + v.s + if (v instanceof X) { + v as X == v + } + if (v instanceof Y) { + v as Y == v + } + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType() && tstype->AsETSObjectType()->Name() != "Y") { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSRelaxedAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_ISINSTANCE, pandasm::Opcode::ISINSTANCE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:std.core.Object;void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_06) +{ + std::string_view text = R"( + export declare class X { + } + + function foo() { + let x = new X() + x == x + x === x + x ? true : false + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType()) { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ETS_EQUALS, pandasm::Opcode::ETS_STRICTEQUALS}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +} // namespace ark::es2panda::compiler::test \ No newline at end of file diff --git a/ets2panda/test/utils/asm_test.cpp b/ets2panda/test/utils/asm_test.cpp index 53fb2a8dcf..1f8e1c6d59 100644 --- a/ets2panda/test/utils/asm_test.cpp +++ b/ets2panda/test/utils/asm_test.cpp @@ -298,7 +298,7 @@ void AsmTest::SetCurrentProgram(std::string_view src) std::unique_ptr AsmTest::GetCurrentProgram(std::string_view src) { - static constexpr std::string_view FILE_NAME = "annotation.ets"; + static constexpr std::string_view FILE_NAME = "dummy.ets"; std::array args = {"../../../../../bin/es2panda", "--ets-unnamed"}; // NOLINT(modernize-avoid-c-arrays) diff --git a/ets2panda/test/utils/checker_test.h b/ets2panda/test/utils/checker_test.h index 79259abbad..69f1fbdee0 100644 --- a/ets2panda/test/utils/checker_test.h +++ b/ets2panda/test/utils/checker_test.h @@ -20,7 +20,9 @@ #include "compiler/lowering/phase.h" #include "panda_executable_path_getter.h" #include "compiler/core/regSpiller.h" +#include "compiler/core/ETSCompiler.h" #include "compiler/core/ETSemitter.h" +#include "compiler/core/ETSGen.h" #include "checker/ETSAnalyzer.h" #include "ir/astNode.h" #include "util/options.h" @@ -84,6 +86,37 @@ public: compiler_alias::ETSEmitter>(&es2pandaPathPtr, fileName, src, &checker_, &program_); } + template + std::unique_ptr RunCheckerWithCustomFunc(std::string_view fileName, std::string_view src, + CustomFunc customFunc) + { + auto es2pandaPathPtr = es2pandaPath_.c_str(); + ASSERT(es2pandaPathPtr); + + return std::unique_ptr( + InitializeCheckerWithCustomFunc(&es2pandaPathPtr, fileName, src, + &checker_, &program_, customFunc)); + } + + template + static plib_alias::Context::CodeGenCb MakeCompileJob() + { + return [](plib_alias::Context *context, varbinder_alias::FunctionScope *scope, + compiler_alias::ProgramElement *programElement) -> void { + RegSpiller regSpiller; + ark::ArenaAllocator allocator(ark::SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + AstCompiler astcompiler; + compiler_alias::SetPhaseManager(context->phaseManager); + CodeGen cg(&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.Generate(); + }; + } + template void InitializeChecker(char const *const *argv, std::string_view fileName, std::string_view src, @@ -137,6 +170,68 @@ public: } } } + + template + ark::pandasm::Program *InitializeCheckerWithCustomFunc(char const *const *argv, std::string_view fileName, + std::string_view src, checker_alias::ETSChecker *checker, + parser_alias::Program *program, CustomFunc customFunc) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto options = std::make_unique(argv[0], diagnosticEngine_); + if (!options->Parse(ark::Span(argv, 1))) { + return nullptr; + } + + ark::Logger::ComponentMask mask {}; + mask.set(ark::Logger::Component::ES2PANDA); + ark::Logger::InitializeStdLogging(options->LogLevel(), mask); + + ark::es2panda::Compiler compiler(options->GetExtension(), options->GetThread()); + ark::es2panda::SourceFile input(fileName, src, options->IsModule()); + compiler_alias::CompilationUnit unit {input, *options, 0, options->GetExtension(), diagnosticEngine_}; + auto parser = Parser(program, unit.options, diagnosticEngine_, + static_cast(unit.rawParserStatus)); + auto analyzer = Analyzer(checker); + checker->SetAnalyzer(&analyzer); + + auto *varbinder = program->VarBinder(); + varbinder->SetProgram(program); + + varbinder->SetContext(publicContext_.get()); + + auto emitter = Emitter(publicContext_.get()); + auto phaseManager = compiler_alias::PhaseManager(publicContext_.get(), unit.ext, allocator_.get()); + + auto config = plib_alias::ConfigImpl {}; + publicContext_->config = &config; + publicContext_->config->options = &unit.options; + publicContext_->sourceFile = &unit.input; + publicContext_->allocator = allocator_.get(); + publicContext_->parser = &parser; + parser.SetContext(publicContext_.get()); + publicContext_->parserProgram = program; + publicContext_->PushChecker(checker); + publicContext_->PushAnalyzer(publicContext_->GetChecker()->GetAnalyzer()); + publicContext_->emitter = &emitter; + publicContext_->diagnosticEngine = &diagnosticEngine_; + publicContext_->phaseManager = &phaseManager; + publicContext_->GetChecker()->Initialize(varbinder); + parser.ParseScript(unit.input, + unit.options.GetCompilationMode() == ark::es2panda::CompilationMode::GEN_STD_LIB); + while (auto phase = publicContext_->phaseManager->NextPhase()) { + if (!phase->Apply(publicContext_.get(), program)) { + return nullptr; + } + } + + // Run custom logic in here to modify ast + Program()->Ast()->IterateRecursively(customFunc); + + publicContext_->codeGenCb = MakeCompileJob(); + ark::es2panda::compiler::CompilerImpl compilerImpl(options->GetThread(), {}); + return compilerImpl.Emit(publicContext_.get()); + } NO_COPY_SEMANTIC(CheckerTest); NO_MOVE_SEMANTIC(CheckerTest); diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 5fd093da74..4c4ae2e28e 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1510,3 +1510,7 @@ semantic: - name: CYCLIC_CALLEE id: 381 message: "Circular call function" + +- name: DYMANIC_INIT_WITH_OBJEXPR + id: 382 + message: "Dymanic Type {} cannot be initialize with an object expression" diff --git a/ets2panda/util/importPathManager.cpp b/ets2panda/util/importPathManager.cpp index ae0e0b61c6..66d4b5a8ff 100644 --- a/ets2panda/util/importPathManager.cpp +++ b/ets2panda/util/importPathManager.cpp @@ -66,7 +66,7 @@ ImportPathManager::ImportMetadata ImportPathManager::GatherImportMetadata(parser // NOTE(dkofanov): The code below expresses the idea of 'dynamicPaths' defining separated, virtual file system. // Probably, paths of common imports should be isolated from the host fs as well, being resolved by 'ModuleInfo' // instead of 'AbsoluteName'. - isDynamic_ = program->ModuleInfo().isDeclForDynamicStaticInterop; + isDynamic_ = program->IsDeclForDynamicStaticInterop(); auto curModulePath = isDynamic_ ? program->ModuleInfo().moduleName : program->AbsoluteName(); auto [resolvedImportPath, resolvedIsDynamic] = ResolvePath(curModulePath.Utf8(), importPath); if (resolvedImportPath.empty()) { diff --git a/ets2panda/util/importPathManager.h b/ets2panda/util/importPathManager.h index 169960dff2..537aa4817f 100644 --- a/ets2panda/util/importPathManager.h +++ b/ets2panda/util/importPathManager.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H #define ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H +#include "language.h" #if defined PANDA_TARGET_MOBILE #define USE_UNIX_SYSCALL #endif @@ -68,6 +69,7 @@ struct ModuleInfo { // 'Program::MaybeTransformToDeclarationModule'. bool isDeclForDynamicStaticInterop {}; // NOLINTEND(misc-non-private-member-variables-in-classes) + Language lang = Language(Language::Id::ETS); }; class ImportPathManager { diff --git a/ets2panda/varbinder/variableFlags.h b/ets2panda/varbinder/variableFlags.h index 1177e71a30..5ec081c2e6 100644 --- a/ets2panda/varbinder/variableFlags.h +++ b/ets2panda/varbinder/variableFlags.h @@ -165,6 +165,7 @@ enum class VariableFlags : uint64_t { ANNOTATIONUSAGE = 1ULL << 34ULL, NAMESPACE = 1ULL << 35ULL, INIT_IN_STATIC_BLOCK = 1ULL << 36ULL, + DYNAMIC = 1ULL << 37ULL, HOIST_VAR = HOIST | VAR, CLASS_OR_INTERFACE = CLASS | INTERFACE, -- Gitee