diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 5a788f0982c1c3b03192087c2c64767440d065a7..92680ad14a82fad06fc4bba53184fdc7218b897f 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 214331408045028a732113408788dc4ef3eb74c6..7719bd2968c65efd0e3e361d5736ab6c3349b28c 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 d8a9998848ce9a4f5d6124d41c6587f385abb866..a2179fc411ba0b24db631a82db400ba318fc1b32 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 bf7351956a7b3bae812a360c9c28f6a777633fae..6d6ff354876263cca9636e8da9367292bb816629 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 5028e1770eda1b56748edca36155deab4ec0f749..ca365e2a61a38520779e0572b1f9310578e60b12 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 2a5443ae232284fc548176857faf987d2efdb0ab..7bf8a4136f600ffc2d6bd37a35e60050817a0579 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 36830c6155627fd285cb71743531dbda1fa0590b..8d5541159a917653b48cfe9b84c0f1b00f41dc65 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 978b0b2beed1b92403a6815b076183561f709bea..ec155ee9ae943e62fede1007345ea4c72a11717a 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 74c90e74523571418b8f7cc915ef8b67ecacfd9b..ab4e17a2e079d1182cf78b6a0e32ecc6eedbaa1d 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 5b849a4980607c0f4c21ab8bd65e4be2dd5c4110..e3ae34e68457fba2aecfd8671fb7601dfa4fb0cc 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 bbc3f6aa283e574a2b643b0ac5410c48c018dcbf..d5d379b49b766e6bd00725e16be30705bda186f4 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 22f1c94672ee59da68944b5fef8c27d3f81db994..c2c1ed697434860dfacf6da15c243c2c25063481 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 c4803d3dd896e27239b8844a6bf467821b768e44..7c22e2f83b690b8c753b021b516bb43c0a0e93d7 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 8be037eadd49d0a7fd4015ab2fc46536df8a43ac..26ed9ad28c6c05e1dea6f3aea8ee9436bd46b806 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 c6a6dda41ab8fce55d95db4ad86bc5498e23dc09..f3c52febcb5f7a8b3148660ee9b89a3bea289fcf 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 515b663e3878f586e60cc62e7affae3d28cbe31c..3728f497e3d262910b489dbdcb6e79f4ad420224 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 8b6d09ef64068c587391e35af20fc75497529f6f..a6d97d25ce3a555349e3a2a4903637766c3bb2f7 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 bfcd8ef61af7936503c974f504988e0bee77c4ef..80bfff4d4ebc492025a92f10c9e7d15cba978d00 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 1bfc781d84df6fcc344b577375408683c3aee19f..33339c96f06dd83b7ce8f593538fc7a8d570e62f 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 d9b87e49a24d5bef65de3d0ed9572f21a58fd5ae..52793689c9b76084dfc5ac556f7a9a53ef7d1319 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 1a315b36d603d3e67d3e199af08e5407844a2eae..7d12b6a004e77c58e9e50f6f0b2b2a2691dd5668 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 6af463b16dff19a4d72aade46e1e77cde4e21972..72c5694774fb38770d225a42eac13d92f626eec4 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 6bd4357e5908ecb0bce67d13c9491a3daa3204d3..a13f0d46855cb24e9979b6690211803af8200293 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 b5a4244e54630cac84ef1aa5d1c4be03e601ed8b..7b94a5311209335cc121aade18da8c1fcaf2d8d4 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 8aa8a44cb742427b6de4fa69db8db6e22c2d5513..effd56d037a21930e60f7d6bdbf11975003d90a5 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 9374659544914794f727dfec9f3e5a07618f87f7..1818385b6ce347ae9e5e3432498a13d974a5a10b 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 0000000000000000000000000000000000000000..cdb568ab47c0aa5a9947f45ba4b76f2c15c0c50e --- /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 0000000000000000000000000000000000000000..eb1978e8d64d03011ffa14443c8b8607c8559850 --- /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 5ced79c41120e511b5f411031ffe5a045c8275b0..be45282d5f95b09d4a98d5cc6a18a04096a96d88 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 090981812fedbf7f10a5ff7e16016de06995cc1d..61a6b8568e82040ac0fe3ebbed737ee8846d6c40 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 7f0aa993d47d92cb2dc3f9a57eb15d8731320490..3840d6ab66b98a8570b957cad0c6b5cde073c655 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 feea58ce1294a994cceccb60c8811fb08999593c..5f01ae82252c6d2fcf311bbdfc2804c5f010a98c 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 a856a49bdbc8561f4e98e87e5be6a1b936804281..84e3ba9cfef6fdf49983f38bf38c92e203b2e5b3 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 96572db0715cf5f6e9e7c0f052a6897e2f8b3612..f83427ac2b331cc0dbf8f0d8a3bb7cd985a14623 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 09d7df240ab32e6551008de170ebbfdcd00e30b0..a0159934396bc21a3d0f4721db1ee221abf28276 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 d09df63fd6c3d2e54a7fcc92214db24f5469d466..868ce81e497e15dbcb51d9a6dd65191d2249dc58 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 44669b1241826c6c824fbe63ccac691226ab45b4..4536b5cebfb6b2f03acb5665834293cd501dc59e 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 4abf10587db1909ccb48079a5a68c991c1b90665..4e62393a954b62facfe7f9675d6a93d5568f3c26 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 d875e54e04ffee5455066e67db62005e041515da..084e2edfa2f48d1e6bf6ee935a29bd2add62d5c8 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 81ff05cfe2b47612f29a5eb7a8a7bb1523c9432a..81a59f631085f223531026484b2d4ea169cc0513 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 4edd9d31b25ace531738391744a03630fe79e6ea..672f29e5f6d859490a0ed258dfe6b640d5853390 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 0000000000000000000000000000000000000000..08228d3af846f11b3251e0f53bb301e26205bf32 --- /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 0000000000000000000000000000000000000000..78af90c0132bd0801131f58f9434f32a50d34512 --- /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 0000000000000000000000000000000000000000..778b1a59496177bdda94aba0e836661e07991446 --- /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 0000000000000000000000000000000000000000..e4a09c4ab960a0760129e82eb57b03cb9ff406df --- /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 c88ab3572b51a831e15f60d1bffe3e22bae19a02..2833087baaa0a04dd327492e707b1891cee15851 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 00485472a3b292b537c896dadaac8d16766808cf..5e4836e4453e634a260ee27a996f7162bed529f1 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 53e9ecdb7110e1f53535add40eb311e072006289..439208e64049e26c6c466099f051d5311a90f348 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 5e42c84c84564887e1c949a85bfee3f1d3ce2633..8382b1dd2c95f87c5440f3d1fe67ebcd48edc6d0 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 a01f89264fd5777d9b469dcdb8730f60b0302dbf..8e0fbcfb05cb2aaa7afda6abd1486a510a2583bb 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 94ce1794274081033d3b74167141e6e6fd066f7f..9363c7e363ffd13e48061ac61c15b175e8f737c8 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 f0435fb4c077f40af0e1a63b859c33c148c85619..9fe4183e9b9155b496bb2aed6acacf199cee2546 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 1ad03961fdd765a58aee4ca529b10ce68857a718..fc2f51d86609bb41feacf323a5cea9446ed49489 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 c321ed36b8b664e093983492e3613f3e40d3ed51..be67b7ac50692ef8348cf8accd934a44b17c7ed4 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 b81f32d845bb531fd67da6b7b361147546dab0aa..a4c4e1336a99c85f07a286b5c5d047885ba99b37 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 7cfa3092069efbe16315bc78b51cf61b007ef06d..490ec79695636c4e36be77fad20d79d7480d53ce 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 c1629690a48d440bd3dcebde659a7db488c63b1e..bc6861126e0108b755ea60d3fd72e9f7c5fb5c40 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 fd71f32cd3f1a783f038df8cb781551345b4e36d..d1a6f415bbd0019cdd85d0163919c7b11460fb3c 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 7986198c729d4d33902ce54239119b9df91e4c72..5ad9abc912b061c3f5aa14cd625e6de80473ed3e 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 8ebd5258b695592122eed98bf6dac3b2d4c308d6..8a79259d948059e8a1762015d351614329c5a4c7 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 9fe657e9302608ebbc02c8b032041c5127466597..40af6ab2b6e6690d97ee297a0d4452ca6e3f6c9b 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 a7bfa507bb79d431683688d41d9312edb523bc4b..4333ae3e9bde96ff6254f29138b6e41ba953a9e9 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 1dc57dca97b45e1b324e62b97ec9c816223d7600..53e6a1a108987d0cbcb20784c69b94fa134d887c 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 8db555d704f26bfd119cc8800cf7d485af7ece1a..4fde4e957d99a2fd32e7ad385d5f501982c68d51 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 4906dc35d2e626cb0f2167e051c71fd7247dcb8f..4c72e652feabf12e3d22dd1f36bbfe9ed09b86f7 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 57b8f7d2ae2cd662db0504c73723e943cb43585f..e6748d33c67ccf5ab0e02f0d8ce4ade8124d3495 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 d4df74d45d740538637ee4e4c4ff8bf89eeb1ca6..77f06d2da7ae35876a82c166d868c86a8942a97d 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 c29543942cadcf88358a2ac9db0e08e8f34a4169..13762717edb7c183dc3c82117ec2515734cc861f 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 c84184cf5fed4406a6915b5ecb8eca0d3f138e9f..78467d144993ce0601bc57922ef60949dc454cb1 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 0000000000000000000000000000000000000000..b41cb7c83f96c685476aeab6d72e7298d17fdc7e --- /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 0000000000000000000000000000000000000000..e6bda882e2286bbcbd81f62f1e4fee43d29004c5 --- /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 53fb2a8dcf1e4258b5c3b2826a3a882c931fe8df..1f8e1c6d59eeda4e7e5b64eae587cccaa79588d5 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 79259abbadaa30314635362e73d5b21792a83a71..69f1fbdee00b0144e41b12514131388ff45479b0 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 5fd093da74fe192bfe450b01ec78c363d13a63b4..4c4ae2e28ecd4f21ea96d41b0697c01a66857615 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 ae0e0b61c667f5aed3e51f57fa87adde4d1683e1..66d4b5a8ffee74f2a49213bf82b6f3030dc4cfda 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 169960dff2f4533702a06388d342749e11489e7b..537aa4817f70b623822f3f4b3a467d3078204c9d 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 1177e71a3099a354ea472b0b41beac43ee24fe73..5ec081c2e68cbf1a6b70d29f88dbd94a571e2f68 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,