From eba3471beb4533896682b12271dbe5f81cbc14e7 Mon Sep 17 00:00:00 2001 From: daizihan Date: Wed, 4 Jun 2025 20:17:02 +0800 Subject: [PATCH] Support gradula type and any type in bc Description: 1. support gradual type, all the types except escompat & arkts1.2 type in the dynamic Declaration file are treated as gradual type. fe type checking is enabled. 2. add lowering to transfrom gradual type to any type, and emit any type related bc Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICE0RF?from=project-issue Signed-off-by: xuxinjie4 Signed-off-by: daizihan --- ets2panda/BUILD.gn | 3 + ets2panda/CMakeLists.txt | 3 + .../invariants/importExportAccessValid.cpp | 7 - ets2panda/checker/ETSAnalyzer.cpp | 35 +- ets2panda/checker/ETSchecker.cpp | 42 ++- ets2panda/checker/ETSchecker.h | 12 + ets2panda/checker/ets/helpers.cpp | 98 +++++- ets2panda/checker/ets/object.cpp | 3 + ets2panda/checker/ets/typeCheckingHelpers.cpp | 2 +- ets2panda/checker/ets/typeCreation.cpp | 69 +++- ets2panda/checker/ets/utilityTypeHandlers.cpp | 2 +- ets2panda/checker/types/ets/etsAnyType.cpp | 2 +- ets2panda/checker/types/ets/etsAnyType.h | 12 + ets2panda/checker/types/ets/etsObjectType.cpp | 1 - .../types/ets/etsObjectTypeConstants.h | 1 + ets2panda/checker/types/gradualType.cpp | 91 +++++ ets2panda/checker/types/gradualType.h | 90 +++++ ets2panda/checker/types/type.h | 79 +++-- ets2panda/checker/types/typeFlag.h | 6 +- ets2panda/compiler/base/lreference.cpp | 23 +- ets2panda/compiler/core/ETSCompiler.cpp | 93 +++++- ets2panda/compiler/core/ETSCompiler.h | 2 + ets2panda/compiler/core/ETSGen.cpp | 69 ++++ ets2panda/compiler/core/ETSGen.h | 101 +++++- .../compiler/lowering/ets/dynamicImport.cpp | 252 ++++++++++++++ .../compiler/lowering/ets/dynamicImport.h | 35 ++ .../lowering/ets/gradualTypeNarrowing.cpp | 93 ++++++ .../lowering/ets/gradualTypeNarrowing.h | 43 +++ .../ets/interfaceObjectLiteralLowering.cpp | 4 +- .../compiler/lowering/ets/lambdaLowering.cpp | 9 +- .../lowering/ets/objectIndexAccess.cpp | 13 +- .../lowering/ets/objectLiteralLowering.cpp | 4 +- .../ets/optionalArgumentsLowering.cpp | 13 +- .../lowering/ets/restArgsLowering.cpp | 4 +- .../compiler/lowering/ets/unboxLowering.cpp | 35 +- ets2panda/compiler/lowering/phase.cpp | 4 + ets2panda/compiler/scripts/signatures.yaml | 2 + ets2panda/declgen_ets2ts/declgenEts2Ts.cpp | 16 +- ets2panda/ir/base/classDefinition.h | 11 + ets2panda/ir/ets/etsTypeReference.cpp | 21 +- ets2panda/ir/ets/etsTypeReferencePart.cpp | 26 +- ets2panda/ir/ets/etsTypeReferencePart.h | 1 + ets2panda/ir/expressions/memberExpression.cpp | 34 ++ ets2panda/ir/expressions/memberExpression.h | 1 + ets2panda/ir/ts/tsTypeReference.cpp | 17 +- ets2panda/lsp/src/isolated_declaration.cpp | 2 +- ets2panda/parser/ETSparser.cpp | 1 + ets2panda/parser/program/program.h | 20 ++ ets2panda/test/unit/CMakeLists.txt | 1 + .../test/unit/any_ins_test/CMakeLists.txt | 17 + .../test/unit/any_ins_test/any_ins_test.cpp | 312 ++++++++++++++++++ ets2panda/test/utils/asm_test.cpp | 2 +- ets2panda/test/utils/checker_test.h | 95 ++++++ ets2panda/util/diagnostic/semantic.yaml | 6 +- ets2panda/util/importPathManager.cpp | 2 +- ets2panda/util/importPathManager.h | 2 + ets2panda/varbinder/variableFlags.h | 1 + 57 files changed, 1803 insertions(+), 142 deletions(-) create mode 100644 ets2panda/checker/types/gradualType.cpp create mode 100644 ets2panda/checker/types/gradualType.h create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.cpp create mode 100644 ets2panda/compiler/lowering/ets/dynamicImport.h create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp create mode 100644 ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h create mode 100644 ets2panda/test/unit/any_ins_test/CMakeLists.txt create mode 100644 ets2panda/test/unit/any_ins_test/any_ins_test.cpp diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 7d999578ef..37577029f0 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -124,6 +124,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", @@ -218,11 +219,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 4b348db7b7..b1b2f0d5e0 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/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index bf7351956a..6d6ff35487 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -78,13 +78,6 @@ bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_s const ir::AstNode *callExpr, util::StringView name) { auto *signature = callExpr->AsCallExpression()->Signature(); - if (signature == nullptr || signature->Owner() == nullptr) { - // NOTE(vpukhov): Add a synthetic owner for dynamic signatures - ES2PANDA_ASSERT( - callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)); - return true; - } - if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr && !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) && varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) { diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 3aa4109c2e..ac11ae2a02 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -23,6 +23,7 @@ #include "types/signature.h" #include "compiler/lowering/ets/setJumpTarget.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "types/typeFlag.h" #include "util/es2pandaMacros.h" #include @@ -512,7 +513,11 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const return checker->InvalidateType(expr); } auto *calleeObj = calleeType->AsETSObjectType(); - expr->SetTsType(calleeObj); + if (calleeType->IsGradualType()) { + expr->SetTsType(calleeType); + } else { + expr->SetTsType(calleeObj); + } auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); @@ -1111,7 +1116,8 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const CastPossibleTupleOnRHS(checker, expr); checker::Type *smartType = rightType; - if (!leftType->IsTypeError()) { + if (!leftType->IsTypeError() && !(leftType->IsETSObjectType() && + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT))) { if (const auto ctx = checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType, expr->Right()->Start(), {{diagnostic::INVALID_ASSIGNMNENT, {rightType, leftType}}}); @@ -1136,6 +1142,14 @@ 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->GetUnderlyingType()}, + expr->Right()->Start()); + } + expr->Right()->AsObjectExpression()->SetPreferredType(leftType); } else { expr->Right()->SetPreferredType(leftType); } @@ -1635,7 +1649,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]() { @@ -1671,7 +1687,8 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression checker->LogError(diagnostic::OVERLOADED_METHOD_AS_VALUE, getUseSite()); return checker->GlobalTypeError(); } - return functionType->MethodToArrow(checker); + checker::Type *funcType = functionType->MethodToArrow(checker); + return funcType; } checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const @@ -1760,8 +1777,9 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke if (baseType->IsETSObjectType()) { checker->ETSObjectTypeDeclNode(checker, baseType->AsETSObjectType()); - return expr->SetTsType(TransformTypeForMethodReference( - checker, expr, expr->SetAndAdjustType(checker, baseType->AsETSObjectType()))); + auto res = TransformTypeForMethodReference(checker, expr, + expr->SetAndAdjustType(checker, baseType->AsETSObjectType())); + return expr->SetTsType(res); } if (baseType->IsETSUnionType()) { @@ -1780,8 +1798,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); @@ -3607,7 +3624,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/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index ce5dd58750..334730ba4d 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 { @@ -309,6 +312,12 @@ void ETSChecker::InitializeBuiltin(varbinder::Variable *var, const util::StringV GetGlobalTypesHolder()->InitializeBuiltin(name, type); } +void ETSChecker::InitialGradualTypes() +{ + ArenaUnorderedMap jsMap {ProgramAllocator()->Adapter()}; + gradualTypes_.insert({Language(Language::Id::JS), jsMap}); +} + bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Options &options) { if (options.IsParseOnly()) { @@ -317,6 +326,7 @@ bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Optio auto *etsBinder = varbinder->AsETSBinder(); InitializeBuiltins(etsBinder); + InitialGradualTypes(); bool isEvalMode = (debugInfoPlugin_ != nullptr); if (UNLIKELY(isEvalMode)) { @@ -361,6 +371,22 @@ void ETSChecker::SetDebugInfoPlugin(evaluate::ScopedDebugInfoPlugin *debugInfo) debugInfoPlugin_ = debugInfo; } +void ETSChecker::WrapTypeNode(ir::AstNode *nodeToWrap) +{ + auto tranformFunc = [this](Type *t) { return GetOrCreateGradualType(t, Program()->GetModuleLang(), false); }; + std::function doNode = ([this, &tranformFunc, &doNode](ir::AstNode *node) { + if (node->IsTyped() && node->AsTyped()->TsType() != nullptr) { + node->AsTyped()->SetTsType(TransformType(node->AsTyped()->TsType(), tranformFunc)); + } + if (node->Variable() != nullptr && node->Variable()->TsType() != nullptr) { + node->Variable()->SetTsType(TransformType(node->Variable()->TsType(), tranformFunc)); + } + node->Iterate(doNode); + }); + + doNode(nodeToWrap); +} + void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) { auto *savedProgram = Program(); @@ -383,6 +409,10 @@ void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) Program()->Ast()->Check(this); + if (program->IsDeclForDynamicStaticInterop()) { + WrapTypeNode(program->Ast()); + } + if (runAnalysis && !IsAnyError()) { AliveAnalyzer aliveAnalyzer(Program()->Ast(), this); AssignAnalyzer(this).Analyze(Program()->Ast()); @@ -418,16 +448,14 @@ 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; + TypeFlag::ETS_TYPE_ALIAS | TypeFlag::TYPE_ERROR | TypeFlag::GRADUAL_MARK; - auto res = static_cast(type->TypeFlags() & ~(TO_CLEAR)); - ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(NOT_A_TYPE_KIND)), + auto res = static_cast(type->GetUnderlyingType()->TypeFlags() & ~(TO_CLEAR)); + ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || + helpers::math::IsPowerOfTwo(static_cast>(res)), ark::es2panda::GetPositionForDiagnostic()); return res; } diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index b3ced7b7d3..96c48549ac 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: @@ -87,6 +89,7 @@ public: // NOLINTNEXTLINE(readability-redundant-member-init) : Checker(allocator, diagnosticEngine, programAllocator), arrayTypes_(Allocator()->Adapter()), + gradualTypes_(Allocator()->Adapter()), pendingConstraintCheckRecords_(Allocator()->Adapter()), objectInstantiationMap_(Allocator()->Adapter()), invokeToArrowSignatures_(Allocator()->Adapter()), @@ -184,6 +187,7 @@ public: void InitializeBuiltins(varbinder::ETSBinder *varbinder); void InitializeBuiltin(varbinder::Variable *var, const util::StringView &name); + void InitialGradualTypes(); bool StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const util::Options &options) override; Type *CheckTypeCached(ir::Expression *expr) override; void ResolveStructuredTypeMembers([[maybe_unused]] Type *type) override {}; @@ -311,6 +315,9 @@ public: ETSResizableArrayType *CreateETSMultiDimResizableArrayType(Type *element, size_t dimSize); ETSResizableArrayType *CreateETSResizableArrayType(Type *element); ETSArrayType *CreateETSArrayType(Type *elementType, bool isCachePolluting = false); + Type *GetGlobalGradualType(Type *baseType) const; + Type *GetOrCreateGradualType(Type *baseType, const Language lang = Language(Language::Id::JS), + bool recursive = true); Type *CreateETSUnionType(Span constituentTypes); template Type *CreateETSUnionType(Type *const (&arr)[N]) // NOLINT(modernize-avoid-c-arrays) @@ -328,6 +335,7 @@ public: ETSTypeAliasType *CreateETSTypeAliasType(util::StringView name, const ir::AstNode *declNode, bool isRecursive = false); ETSFunctionType *CreateETSArrowType(Signature *signature); + ETSAnyType *CreateETSAnyType(bool transformFromGradual); ETSFunctionType *CreateETSMethodType(util::StringView name, ArenaVector &&signatures); ETSExtensionFuncHelperType *CreateETSExtensionFuncHelperType(ETSFunctionType *classMethodType, ETSFunctionType *extensionFunctionType); @@ -562,6 +570,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); @@ -619,6 +628,7 @@ public: Type *RemoveNullType(Type *type); Type *RemoveUndefinedType(Type *type); std::pair RemoveNullishTypes(Type *type); + Type *TransformType(Type *type, const std::function &func); void ConcatConstantString(util::UString &target, Type *type); Type *HandleStringConcatenation(Type *leftType, Type *rightType); @@ -1010,6 +1020,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); @@ -1053,6 +1064,7 @@ private: std::unordered_set &typeAliases); ArrayMap arrayTypes_; + ArenaUnorderedMap> gradualTypes_; ArenaVector pendingConstraintCheckRecords_; ObjectInstantiationMap objectInstantiationMap_; FunctionSignatureMap invokeToArrowSignatures_; diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 553abd66e2..06e080484e 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1686,8 +1686,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(); @@ -1708,21 +1708,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()->InstanceDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } } void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType) @@ -1747,6 +1763,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(); @@ -3118,4 +3138,58 @@ checker::ETSFunctionType *ETSChecker::IntersectSignatureSets(const checker::ETSF return CreateETSMethodType(left->Name(), std::move(intersection)); } +Type *ETSChecker::TransformType(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 {ProgramAllocator()->Adapter()}; + for (auto ctype : unionType->ConstituentTypes()) { + types.push_back(TransformType(ctype, func)); + } + type = 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 {ProgramAllocator()->Adapter()}; + for (auto ctype : tupleType->GetTupleTypesList()) { + types.push_back(TransformType(ctype, func)); + } + type = ProgramAllocator()->New(this, std::move(types)); + } + + if (type->IsETSTypeParameter()) { + auto typeParam = type->AsETSTypeParameter(); + typeParam->SetConstraintType(TransformType(typeParam->GetConstraintType(), func)); + if (typeParam->GetDefaultType() != nullptr) { + typeParam->SetDefaultType(TransformType(typeParam->GetDefaultType(), func)); + } + } + return func(type); +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 1649138262..250ad8c932 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -2187,6 +2187,9 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member } const auto *const targetRef = GetTargetRef(memberExpr); auto searchFlag = GetSearchFlags(memberExpr, targetRef); + if (target->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT)) { + searchFlag |= PropertySearchFlags::SEARCH_INSTANCE; + } auto searchName = target->GetReExportAliasValue(memberExpr->Property()->AsIdentifier()->Name()); auto *prop = target->GetProperty(searchName, searchFlag); varbinder::Variable *const globalFunctionVar = ResolveInstanceExtension(memberExpr); diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 515b663e38..822fe9739a 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -73,7 +73,7 @@ void ETSChecker::CheckTruthinessOfType(ir::Expression *expr) bool ETSChecker::CheckNonNullish(ir::Expression const *expr) { - if (!expr->TsType()->PossiblyETSNullish()) { + if (!expr->TsType()->PossiblyETSNullish() || expr->TsType()->IsETSAnyType()) { return true; } diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 1c8c6c9a98..3044f28859 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,43 @@ ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePoll return arrayType; } +Type *ETSChecker::GetGlobalGradualType(Type *baseType) const +{ + auto gradualMap = gradualTypes_.find(Language::Id::JS)->second; + auto res = gradualMap.find(baseType); + if (res != gradualMap.end()) { + return res->second; + } + return gradualMap.find(GlobalTypeError())->second; +} + +Type *ETSChecker::GetOrCreateGradualType(Type *baseType, const Language lang, bool recursive) +{ + // For built-in 1.2 types and pure static 1.2 types, gradual will be simplified to T. + if (!(baseType->HasTypeFlag(TypeFlag::GRADUAL_MARK) && baseType->IsETSObjectType())) { + return baseType; + } + + if (!recursive && baseType->IsGradualType()) { + return baseType->AsGradualType(); + } + + auto &gradualMap = gradualTypes_.find(lang)->second; + + auto res = gradualMap.find(baseType); + if (res != gradualMap.end()) { + return res->second; + } + + auto *gradualType = ProgramAllocator()->New(baseType, lang); + + std::stringstream ss; + gradualType->ToAssemblerTypeWithRank(ss); + + gradualMap.insert({baseType, gradualType}); + return gradualType; +} + Type *ETSChecker::CreateETSUnionType(Span constituentTypes) { if (constituentTypes.empty()) { @@ -152,6 +190,11 @@ ETSFunctionType *ETSChecker::CreateETSArrowType(Signature *signature) return ProgramAllocator()->New(this, signature); } +ETSAnyType *ETSChecker::CreateETSAnyType(bool transformFromGradual) +{ + return ProgramAllocator()->New(transformFromGradual); +} + ETSFunctionType *ETSChecker::CreateETSMethodType(util::StringView name, ArenaVector &&signatures) { return ProgramAllocator()->New(this, name, std::move(signatures)); @@ -332,22 +375,28 @@ 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()); + } 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())); } - if (internalName == compiler::Signatures::BUILTIN_ARRAY) { - return ProgramAllocator()->New(ProgramAllocator(), name, - std::make_tuple(declNode, flags, Relation())); + if (Program()->IsDeclForDynamicStaticInterop()) { + objectType->AddTypeFlag(TypeFlag::GRADUAL_MARK); } - 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/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index c267defa3c..04c1bf62dd 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -134,7 +134,7 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return CreatePartialTypeClass(typeToBePartial->AsETSObjectType(), - typeToBePartial->Variable()->Declaration()->Node()); + typeToBePartial->GetUnderlyingType()->Variable()->Declaration()->Node()); } Type *ETSChecker::CreatePartialTypeParameter(ETSTypeParameter *typeToBePartial) diff --git a/ets2panda/checker/types/ets/etsAnyType.cpp b/ets2panda/checker/types/ets/etsAnyType.cpp index d9b87e49a2..52793689c9 100644 --- a/ets2panda/checker/types/ets/etsAnyType.cpp +++ b/ets2panda/checker/types/ets/etsAnyType.cpp @@ -22,7 +22,7 @@ namespace ark::es2panda::checker { void ETSAnyType::Identical(TypeRelation *relation, Type *other) { - relation->Result(other->IsAnyType()); + relation->Result(other->IsETSAnyType()); } void ETSAnyType::AssignmentTarget(TypeRelation *relation, Type *source) diff --git a/ets2panda/checker/types/ets/etsAnyType.h b/ets2panda/checker/types/ets/etsAnyType.h index 1a315b36d6..bab24a6004 100644 --- a/ets2panda/checker/types/ets/etsAnyType.h +++ b/ets2panda/checker/types/ets/etsAnyType.h @@ -22,6 +22,10 @@ namespace ark::es2panda::checker { class ETSAnyType : public Type { public: ETSAnyType() : Type(TypeFlag::ETS_ANY) {} + explicit ETSAnyType(bool isTransformedFromGradualType) + : Type(TypeFlag::ETS_ANY), isTransformedFromGradualType_(isTransformedFromGradualType) + { + } void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; @@ -38,6 +42,14 @@ public: TypeFacts GetTypeFacts() const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + bool IsTransformedFromGradualType() const + { + return isTransformedFromGradualType_; + } + +private: + bool isTransformedFromGradualType_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index ace95542ce..ca053561e0 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); diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index 1b27c6e7c7..651e28c1be 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -61,6 +61,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/gradualType.cpp b/ets2panda/checker/types/gradualType.cpp new file mode 100644 index 0000000000..08b8a3d6fa --- /dev/null +++ b/ets2panda/checker/types/gradualType.cpp @@ -0,0 +1,91 @@ +/* + * 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) +{ + return 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); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/gradualType.h b/ets2panda/checker/types/gradualType.h new file mode 100644 index 0000000000..fec9826a0d --- /dev/null +++ b/ets2panda/checker/types/gradualType.h @@ -0,0 +1,90 @@ +/* + * 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; + + const Type *GetUnderlyingType() const override + { + auto underlyingType = baseType_; + while (underlyingType->IsGradualType()) { + underlyingType = underlyingType->AsGradualType()->BaseType(); + } + return underlyingType; + } + + Type *GetUnderlyingType() override + { + auto underlyingType = baseType_; + while (underlyingType->IsGradualType()) { + underlyingType = underlyingType->AsGradualType()->BaseType(); + } + return underlyingType; + } + + 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/type.h b/ets2panda/checker/types/type.h index d3f83e9d37..7256bdfec3 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) @@ -60,31 +61,41 @@ public: virtual ~Type() = default; static std::mutex idLock_; + virtual const Type *GetUnderlyingType() const + { + return const_cast(this); + } + + virtual Type *GetUnderlyingType() + { + return this; + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TYPE_IS_CHECKS(typeFlag, typeName) \ bool Is##typeName() const noexcept \ { \ /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ - return HasTypeFlag(typeFlag); \ + return GetUnderlyingType()->HasTypeFlag(typeFlag); \ } TYPE_MAPPING(TYPE_IS_CHECKS) #undef DECLARE_IS_CHECKS /* CC-OFFNXT(G.PRE.06,G.PRE.02) solid logic, name part */ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define TYPE_AS_CASTS(typeFlag, typeName) \ - /* CC-OFFNXT(G.PRE.02) name part*/ \ - typeName *As##typeName() \ - { \ - ES2PANDA_ASSERT(Is##typeName()); \ - /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ - return reinterpret_cast(this); /* CC-OFF(G.PRE.02) name part*/ \ - } \ - const typeName *As##typeName() const \ - { \ - ES2PANDA_ASSERT(Is##typeName()); \ - /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ - return reinterpret_cast(this); \ +#define TYPE_AS_CASTS(typeFlag, typeName) \ + /* CC-OFFNXT(G.PRE.02) name part*/ \ + typeName *As##typeName() \ + { \ + ES2PANDA_ASSERT(Is##typeName()); \ + /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ + return reinterpret_cast(this->GetUnderlyingType()); /* CC-OFF(G.PRE.02) name part*/ \ + } \ + const typeName *As##typeName() const \ + { \ + ES2PANDA_ASSERT(Is##typeName()); \ + /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ + return reinterpret_cast(this->GetUnderlyingType()); \ } TYPE_MAPPING(TYPE_AS_CASTS) #undef TYPE_AS_CASTS @@ -111,6 +122,23 @@ public: bool PossiblyETSValueTyped() const; bool PossiblyETSValueTypedExceptNullish() const; + bool IsGradualType() const noexcept + { + return (typeFlags_ & TypeFlag::GRADUAL_TYPE) != 0; + } + + GradualType *AsGradualType() + { + ES2PANDA_ASSERT(IsGradualType()); + return reinterpret_cast(this); + } + + const GradualType *AsGradualType() const + { + ES2PANDA_ASSERT(IsGradualType()); + return reinterpret_cast(this); + } + ETSStringType *AsETSStringType() { ES2PANDA_ASSERT(IsETSObjectType()); @@ -146,38 +174,38 @@ public: ETSAsyncFuncReturnType *AsETSAsyncFuncReturnType() { ES2PANDA_ASSERT(IsETSAsyncFuncReturnType()); - return reinterpret_cast(this); + return reinterpret_cast(this->GetUnderlyingType()); } const ETSAsyncFuncReturnType *AsETSAsyncFuncReturnType() const { ES2PANDA_ASSERT(IsETSAsyncFuncReturnType()); - return reinterpret_cast(this); + return reinterpret_cast(this->GetUnderlyingType()); } bool IsConstantType() const { - return HasTypeFlag(checker::TypeFlag::CONSTANT); + return GetUnderlyingType()->HasTypeFlag(checker::TypeFlag::CONSTANT); } TypeFlag TypeFlags() const { - return typeFlags_; + return GetUnderlyingType()->typeFlags_; } bool HasTypeFlag(TypeFlag typeFlag) const { - return (typeFlags_ & typeFlag) != 0; + return (GetUnderlyingType()->typeFlags_ & typeFlag) != 0; } void AddTypeFlag(TypeFlag typeFlag) { - typeFlags_ |= typeFlag; + GetUnderlyingType()->typeFlags_ |= typeFlag; } void RemoveTypeFlag(TypeFlag typeFlag) { - typeFlags_ &= ~typeFlag; + GetUnderlyingType()->typeFlags_ &= ~typeFlag; } uint64_t Id() const @@ -187,16 +215,25 @@ public: void SetVariable(varbinder::Variable *variable) { + if (IsGradualType()) { + GetUnderlyingType()->SetVariable(variable); + } variable_ = variable; } varbinder::Variable *Variable() { + if (IsGradualType()) { + GetUnderlyingType()->Variable(); + } return variable_; } const varbinder::Variable *Variable() const { + if (IsGradualType()) { + GetUnderlyingType()->Variable(); + } return variable_; } diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index feea58ce12..3339251c4f 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -69,11 +69,11 @@ 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 + GRADUAL_MARK = 1ULL << 44ULL, // gradual mark 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 +90,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/compiler/base/lreference.cpp b/ets2panda/compiler/base/lreference.cpp index 96572db071..f83427ac2b 100644 --- a/ets2panda/compiler/base/lreference.cpp +++ b/ets2panda/compiler/base/lreference.cpp @@ -183,7 +183,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind const auto *memberExpr = Node()->AsMemberExpression(); staticObjRef_ = memberExpr->Object()->TsType(); - if (!memberExpr->IsComputed() && etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { + if (!memberExpr->IsComputed() && memberExpr->PropVar() != nullptr && + etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { return; } @@ -196,7 +197,8 @@ ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind TargetTypeContext pttctx(etsg_, memberExpr->Property()->TsType()); memberExpr->Property()->Compile(etsg_); etsg_->ApplyConversion(memberExpr->Property()); - ES2PANDA_ASSERT(etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); + ES2PANDA_ASSERT(memberExpr->Object()->TsType()->IsETSAnyType() || + etsg_->GetAccumulatorType()->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)); propReg_ = etsg_->AllocReg(); etsg_->StoreAccumulator(node, propReg_); } @@ -280,6 +282,15 @@ void ETSLReference::SetValueComputed(const ir::MemberExpression *memberExpr) con { const auto *const objectType = memberExpr->Object()->TsType(); + if (objectType->IsETSAnyType()) { + if (memberExpr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg_->StoreByIndexAny(memberExpr, baseReg_, propReg_); + } else { + etsg_->StoreByValueAny(memberExpr, baseReg_, propReg_); + } + return; + } + if (objectType->IsETSTupleType()) { ES2PANDA_ASSERT(memberExpr->GetTupleIndexValue().has_value()); @@ -325,6 +336,7 @@ void ETSLReference::SetValue() const const auto *const memberExpr = Node()->AsMemberExpression(); const auto *const memberExprTsType = memberExpr->TsType(); + auto const *objectType = memberExpr->Object()->TsType(); if (!memberExpr->IsIgnoreBox()) { etsg_->ApplyConversion(Node(), memberExprTsType); @@ -335,6 +347,11 @@ void ETSLReference::SetValue() const return; } + if (objectType->IsETSAnyType()) { + etsg_->StorePropertyByNameAny(memberExpr, baseReg_, memberExpr->Property()->AsIdentifier()->Name()); + return; + } + if (memberExpr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { SetValueGetterSetter(memberExpr); return; @@ -349,8 +366,6 @@ void ETSLReference::SetValue() const return; } - auto const *objectType = memberExpr->Object()->TsType(); - if (objectType->IsETSUnionType()) { etsg_->StorePropertyByName(Node(), baseReg_, checker::ETSChecker::FormNamedAccessMetadata(memberExpr->PropVar())); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 09d7df240a..e2a57c66ae 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()->IsTransformedFromGradualType()) { + 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 (callee->IsMemberExpression() && callee->AsMemberExpression()->Object()->TsType()->IsETSAnyType()) { + 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; } diff --git a/ets2panda/compiler/core/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index d09df63fd6..868ce81e49 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -35,6 +35,7 @@ public: private: void GetDynamicNameParts(const ir::CallExpression *expr, ArenaVector &parts) const; + void CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, compiler::VReg &calleeReg) const; void CompileCastPrimitives(const ir::Expression *expr, checker::Type const *targetType) const; void CompileCast(const ir::TSAsExpression *expr, checker::Type const *targetType) const; void EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const; @@ -44,6 +45,7 @@ private: void CompileTupleCreation(const ir::ArrayExpression *tupleInitializer) const; static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr); + bool CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 149c12d4d5..420e085a6a 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 6712e10b91..b10b95c999 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, @@ -269,6 +279,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); @@ -311,6 +322,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) { @@ -414,6 +430,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); @@ -555,7 +587,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); \ @@ -734,7 +766,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/lowering/ets/dynamicImport.cpp b/ets2panda/compiler/lowering/ets/dynamicImport.cpp new file mode 100644 index 0000000000..5312455df4 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.cpp @@ -0,0 +1,252 @@ +/** + * 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->AddTypeFlag(checker::TypeFlag::GRADUAL_MARK); + objType->AddObjectFlag(checker::ETSObjectFlags::LAZY_IMPORT_OBJECT); + auto moduleType = checker->GetOrCreateGradualType(objType); + + auto parser = ctx->parser->AsETSParser(); + auto *typeAnnotation = allocator->New(moduleType, allocator); + auto *classProp = parser->CreateFormattedClassFieldDefinition(std::string {FIELD_NAME} + ": @@T1", typeAnnotation) + ->AsClassProperty(); + typeAnnotation->SetParent(classProp); + classProp->AddModifier(ir::ModifierFlags::CONST | ir::ModifierFlags::STATIC); + + classDecl->Definition()->EmplaceBody(classProp); + classProp->SetParent(classDecl->Definition()); + + auto initializer = checker->CreateClassStaticInitializer( + [ctx, importDecl, className](ArenaVector *statements, + [[maybe_unused]] ArenaVector *params) { + AddImportInitializationStatement(ctx, importDecl, statements, className); + }); + + classDecl->Definition()->EmplaceBody(initializer); + initializer->SetParent(classDecl->Definition()); + + for (auto specifier : importDecl->Specifiers()) { + varMap.insert({specifier->AsImportSpecifier()->Imported()->Variable(), classDecl->Definition()}); + } + + program->GlobalClass()->EmplaceBody(classDecl); + classDecl->SetParent(program->GlobalClass()); + + auto lexScope = varbinder::LexicalScope::Enter(varBinder, program->GlobalClassScope()); + InitScopesPhaseETS::RunExternalNode(classDecl, varBinder); + varBinder->ResolveReferencesForScopeWithContext(classDecl, varBinder->TopScope()); + classDecl->Check(checker); +} + +static AstNodePtr TransformIdentifier(ir::Identifier *ident, public_lib::Context *ctx, + const ArenaUnorderedMap &varMap) +{ + auto checker = ctx->GetChecker()->AsETSChecker(); + auto varBinder = checker->VarBinder()->AsETSBinder(); + auto allocator = checker->ProgramAllocator(); + + const auto parent = ident->Parent(); + if (parent->IsImportSpecifier() || parent->IsScriptFunction() || parent->IsMethodDefinition()) { + return ident; + } + + auto varIt = varMap.find(ident->Variable()); + if (varIt == varMap.end()) { + return ident; + } + + auto *leftId = allocator->New(varIt->second->Ident()->Name(), allocator); + auto *rightId = allocator->New(FIELD_NAME, allocator); + + auto *expr = util::NodeAllocator::ForceSetParent( + allocator, leftId, rightId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + auto *memberExpr = util::NodeAllocator::ForceSetParent( + allocator, expr, ident, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + + memberExpr->SetParent(parent); + CheckLoweredNode(varBinder, checker, memberExpr); + + return memberExpr; +} + +bool DynamicImport::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + auto allocator = ctx->Allocator(); + ArenaUnorderedMap varMap {allocator->Adapter()}; + ArenaUnorderedMap moduleMap {allocator->Adapter()}; + + auto dynamicImports = program->VarBinder()->AsETSBinder()->DynamicImports(); + for (auto *importDecl : dynamicImports) { + BuildLazyImportObject(ctx, importDecl, program, moduleMap, varMap); + } + + program->Ast()->TransformChildrenRecursively( + [ctx, &varMap](ir::AstNode *node) -> AstNodePtr { + if (node->IsIdentifier() && node->AsIdentifier()->Variable() != nullptr) { + return TransformIdentifier(node->AsIdentifier(), ctx, varMap); + } + return node; + }, + Name()); + + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/dynamicImport.h b/ets2panda/compiler/lowering/ets/dynamicImport.h new file mode 100644 index 0000000000..78af90c013 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/dynamicImport.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_LOWERING_LAZY_IMPORT_OBJECT_H +#define ES2PANDA_COMPILER_LOWERING_LAZY_IMPORT_OBJECT_H + +#include "compiler/lowering/phase.h" +#include "utils/arena_containers.h" + +namespace ark::es2panda::compiler { + +class DynamicImport : public PhaseForBodies { +public: + std::string_view Name() const override + { + return "DynamicImport"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; +}; +} // namespace ark::es2panda::compiler + +#endif // ES2PANDA_COMPILER_LOWERING_TYPE_FOR_LOWERING_H \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp new file mode 100644 index 0000000000..c0b3165eeb --- /dev/null +++ b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.cpp @@ -0,0 +1,93 @@ +/* + * 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 "es2panda.h" +#include "ir/astNode.h" +#include "ir/opaqueTypeNode.h" +#include "ir/typed.h" +#include "util/language.h" + +namespace ark::es2panda::compiler { + +bool GradualTypeNarrowing::TypeIsGradualType(checker::Type *type) +{ + return type->IsGradualType() || (type->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK) && type->IsETSObjectType()); +} + +void GradualTypeNarrowing::NarrowGradualType(ir::AstNode *node) +{ + auto typedNode = node->AsTyped(); + auto typeTransformFunc = [this](checker::Type *type) -> checker::TypePtr { + if (TypeIsGradualType(type)) { + return this->checker_->CreateETSAnyType(true); + } + return type; + }; + + if (typedNode->TsType() != nullptr) { + typedNode->SetTsType(checker_->TransformType(typedNode->TsType(), typeTransformFunc)); + } + + auto var = node->Variable(); + if (var != nullptr && var->TsType() != nullptr) { + var->SetTsType(checker_->TransformType(var->TsType(), typeTransformFunc)); + } +} + +ir::AstNode *GradualTypeNarrowing::ProcessGradualTypeNode(ir::ETSTypeReference *node) +{ + auto type = node->GetType(checker_); + if (!TypeIsGradualType(type)) { + 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_->CreateETSAnyType(true)); + return loweredNode; +} + +bool GradualTypeNarrowing::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + context_ = ctx; + checker_ = ctx->GetChecker()->AsETSChecker(); + + program->Ast()->TransformChildrenRecursively( + // CC-OFFNXT(G.FMT.14-CPP) project code style + [this](ir::AstNode *ast) -> checker::AstNodePtr { + if (ast->IsETSTypeReference()) { + return ProcessGradualTypeNode(ast->AsETSTypeReference()); + } + return ast; + }, + Name()); + + program->Ast()->IterateRecursively([this](ir::AstNode *ast) { + if (ast->IsTyped()) { + NarrowGradualType(ast); + } + }); + return true; +} +} // namespace ark::es2panda::compiler \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h b/ets2panda/compiler/lowering/ets/gradualTypeNarrowing.h new file mode 100644 index 0000000000..a7f9a8c9e1 --- /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: + void NarrowGradualType(ir::AstNode *node); + bool TypeIsGradualType(checker::Type *type); + ir::AstNode *ProcessGradualTypeNode(ir::ETSTypeReference *node); + + public_lib::Context *context_ {nullptr}; + checker::ETSChecker *checker_ {nullptr}; +}; +} // namespace ark::es2panda::compiler + +#endif \ No newline at end of file diff --git a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp index c88ab3572b..00261382e9 100644 --- a/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/interfaceObjectLiteralLowering.cpp @@ -36,14 +36,14 @@ 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->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK); } 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->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK); } static ir::AstNode *CreateAnonClassImplCtor(public_lib::Context *ctx, ArenaVector &readonlyFields) diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index c62bcac5c1..4ee4dd4f42 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -1149,6 +1149,9 @@ static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpressio auto *oldCallee = call->Callee(); auto *oldType = checker->GetApparentType(oldCallee->TsType()); + if (oldType->IsGradualType()) { + return call; + } size_t arity = call->Arguments().size(); auto *ifaceType = oldType->IsETSObjectType() ? oldType->AsETSObjectType() @@ -1235,14 +1238,16 @@ 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)) { + !id->Variable()->HasFlag(varbinder::VariableFlags::DYNAMIC) && !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && + IsValidFunctionDeclVar(var)) { return ConvertFunctionReference(ctx, id); } } if (node->IsMemberExpression()) { auto *mexpr = node->AsMemberExpression(); if (mexpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && mexpr->TsType() != nullptr && - mexpr->TsType()->IsETSFunctionType() && mexpr->Object()->TsType()->IsETSObjectType()) { + !mexpr->TsType()->IsGradualType() && mexpr->TsType()->IsETSFunctionType() && + mexpr->Object()->TsType()->IsETSObjectType()) { ES2PANDA_ASSERT(mexpr->Property()->IsIdentifier()); auto *var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty( mexpr->Property()->AsIdentifier()->Name(), diff --git a/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp b/ets2panda/compiler/lowering/ets/objectIndexAccess.cpp index 5e42c84c84..9ee34b1e35 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" @@ -113,7 +114,9 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression() && ast->AsAssignmentExpression()->Left()->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType() != nullptr) { + auto objType = ast->AsAssignmentExpression()->Left()->AsMemberExpression()->ObjType(); + if (objType != nullptr && !objType->IsGradualType() && + !objType->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { return ProcessIndexSetAccess(parser, checker, ast->AsAssignmentExpression()); } } @@ -126,7 +129,9 @@ bool ObjectIndexLowering::PerformForModule(public_lib::Context *ctx, parser::Pro [this, parser, checker](ir::AstNode *const ast) -> ir::AstNode * { if (ast->IsMemberExpression() && ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { - if (ast->AsMemberExpression()->ObjType() != nullptr) { + auto objType = ast->AsMemberExpression()->ObjType(); + if (objType != nullptr && !objType->IsGradualType() && + !objType->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { return ProcessIndexGetAccess(parser, checker, ast->AsMemberExpression()); } } @@ -143,7 +148,9 @@ bool ObjectIndexLowering::PostconditionForModule([[maybe_unused]] public_lib::Co 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 (auto const *const objectType = ast->AsMemberExpression()->ObjType(); + objectType != nullptr && !objectType->IsGradualType() && + !objectType->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { return true; } } diff --git a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp index d0f415917d..116fca2580 100644 --- a/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp +++ b/ets2panda/compiler/lowering/ets/objectLiteralLowering.cpp @@ -298,7 +298,7 @@ bool ObjectLiteralLowering::PerformForModule(public_lib::Context *ctx, parser::P if (ast->IsObjectExpression()) { auto *exprType = ast->AsObjectExpression()->TsType(); if (exprType != nullptr && exprType->IsETSObjectType() && - !exprType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { + !exprType->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { return HandleObjectLiteralLowering(ctx, ast->AsObjectExpression()); } } @@ -315,7 +315,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); + !ast->AsObjectExpression()->TsType()->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK); }); } diff --git a/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp b/ets2panda/compiler/lowering/ets/optionalArgumentsLowering.cpp index 94ce179427..a9f597574a 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()); @@ -70,12 +63,14 @@ bool OptionalArgumentsLowering::PerformForModule(public_lib::Context *ctx, parse program->Ast()->TransformChildrenRecursivelyPreorder( // CC-OFFNXT(G.FMT.14-CPP) project code style [ctx](ir::AstNode *const node) -> ir::AstNode * { - if (node->IsCallExpression()) { + if (node->IsCallExpression() && !node->AsCallExpression()->Callee()->TsType()->IsGradualType()) { auto callExpr = node->AsCallExpression(); callExpr->IsTrailingCall() ? TransformArgumentsForTrailingLambda(ctx, callExpr->AsCallExpression(), callExpr->Signature()) : TransformArguments(ctx, callExpr, callExpr->Signature(), callExpr->Arguments()); - } else if (node->IsETSNewClassInstanceExpression()) { + } else if (node->IsETSNewClassInstanceExpression() && + node->AsETSNewClassInstanceExpression()->GetSignature()->Function()->Language() != + Language::Id::ETS) { auto newExpr = node->AsETSNewClassInstanceExpression(); TransformArguments(ctx, newExpr, newExpr->GetSignature(), newExpr->GetArguments()); } diff --git a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp index f0435fb4c0..9fe4183e9b 100644 --- a/ets2panda/compiler/lowering/ets/restArgsLowering.cpp +++ b/ets2panda/compiler/lowering/ets/restArgsLowering.cpp @@ -200,7 +200,9 @@ ir::CallExpression *RestArgsLowering::TransformCallExpressionWithRestArgs(ir::Ca public_lib::Context *context) { checker::Type *calleeType = callExpr->Callee()->TsType(); - if (calleeType == nullptr || calleeType->IsETSArrowType()) { + if (calleeType == nullptr || calleeType->IsETSArrowType() || + (callExpr->Callee()->IsMemberExpression() && callExpr->Callee()->AsMemberExpression()->PropVar() != nullptr && + callExpr->Callee()->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return callExpr; } diff --git a/ets2panda/compiler/lowering/ets/unboxLowering.cpp b/ets2panda/compiler/lowering/ets/unboxLowering.cpp index 2f60dd17e0..5a1ceb5c01 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" @@ -241,7 +242,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->IsGradualType()) { return child; } auto r = uctx->allocator->New(NormalizeType(uctx, typeNodeType), @@ -318,12 +319,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); @@ -860,9 +869,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]; @@ -871,6 +879,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++) { @@ -931,6 +941,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++) { @@ -1172,7 +1192,8 @@ 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->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType() != nullptr && + !mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType()->IsGradualType()) { HandleDeclarationNode(uctx_, mexpr->Property()->Variable()->Declaration()->Node()); propType = mexpr->Property()->Variable()->Declaration()->Node()->AsTyped()->TsType(); } else if (mexpr->Property()->Variable()->TsType() != nullptr) { @@ -1463,7 +1484,7 @@ static void VisitExternalPrograms(UnboxVisitor *visitor, parser::Program *progra auto annotationIterator = [visitor](auto *child) { if (child->IsClassProperty()) { auto prop = child->AsClassProperty(); - HandleClassProperty(visitor->uctx_, prop); + HandleClassProperty(visitor->uctx_, prop, true); if (prop->Value() != nullptr) { ES2PANDA_ASSERT(prop->Value()->IsLiteral() || prop->Value()->IsArrayExpression() || (prop->Value()->IsTyped() && prop->Value()->AsTyped()->TsType()->IsETSEnumType())); diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index c321ed36b8..72613d7ef0 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,7 @@ 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 AnnotationCopyPostLowering, new AsyncMethodLowering, new DeclareOverloadLowering, @@ -153,6 +156,7 @@ std::vector GetETSPhaseList() new TypeFromLowering, new PrimitiveConversionPhase, new UnboxPhase, + new GradualTypeNarrowing, // pluginsAfterLowerings has to come at the very end, nothing should go after it new PluginPhase{g_pluginsAfterLowering, ES2PANDA_STATE_LOWERED, &util::Plugin::AfterLowerings}, diff --git a/ets2panda/compiler/scripts/signatures.yaml b/ets2panda/compiler/scripts/signatures.yaml index 7cfa309206..490ec79695 100644 --- a/ets2panda/compiler/scripts/signatures.yaml +++ b/ets2panda/compiler/scripts/signatures.yaml @@ -157,6 +157,8 @@ defines: ref: REQUIRED_TYPE_NAME - name: 'Array' ref: ARRAY + - name: 'gradual' + ref: GRADUAL_TYPE_NAME - name: '' ref: PROPERTY - name: 'Any' diff --git a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp index 924f0807e2..ff25229ad1 100644 --- a/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp +++ b/ets2panda/declgen_ets2ts/declgenEts2Ts.cpp @@ -93,11 +93,11 @@ 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->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { return; } auto typeName = objectType->Name(); @@ -210,7 +210,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 +219,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 +230,7 @@ void TSDeclGen::ProcessInterfacesDependencies(const ArenaVectorName()); } }, @@ -420,7 +419,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: @@ -779,8 +777,8 @@ bool TSDeclGen::HandleSpecificObjectTypes(const checker::ETSObjectType *objectTy GenType(invoke); return true; } - if (objectType->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC)) { - OutDts("any"); + if (objectType->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK)) { + OutDts("Any"); return true; } return false; diff --git a/ets2panda/ir/base/classDefinition.h b/ets2panda/ir/base/classDefinition.h index fd71f32cd3..d1a6f415bb 100644 --- a/ets2panda/ir/base/classDefinition.h +++ b/ets2panda/ir/base/classDefinition.h @@ -59,6 +59,7 @@ enum class ClassDefinitionModifiers : uint32_t { INT_ENUM_TRANSFORMED = 1U << 15U, FROM_STRUCT = 1U << 16U, FUNCTIONAL_REFERENCE = 1U << 17U, + LAZY_IMPORT_OBJECT_CLASS = 1U << 18U, DECLARATION_ID_REQUIRED = DECLARATION | ID_REQUIRED, ETS_MODULE = NAMESPACE_TRANSFORMED | GLOBAL }; @@ -272,6 +273,11 @@ public: return (Modifiers() & ClassDefinitionModifiers::NAMESPACE_TRANSFORMED) != 0; } + [[nodiscard]] bool IsLazyImportObjectClass() const noexcept + { + return (Modifiers() & ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS) != 0; + } + [[nodiscard]] bool IsFromStruct() const noexcept { return (Modifiers() & ClassDefinitionModifiers::FROM_STRUCT) != 0; @@ -312,6 +318,11 @@ public: AddClassModifiers(ClassDefinitionModifiers::NAMESPACE_TRANSFORMED); } + void SetLazyImportObjectClass() noexcept + { + AddClassModifiers(ClassDefinitionModifiers::LAZY_IMPORT_OBJECT_CLASS); + } + void SetFromStructModifier() noexcept { AddClassModifiers(ClassDefinitionModifiers::FROM_STRUCT); diff --git a/ets2panda/ir/ets/etsTypeReference.cpp b/ets2panda/ir/ets/etsTypeReference.cpp index 7986198c72..5fa3e352f7 100644 --- a/ets2panda/ir/ets/etsTypeReference.cpp +++ b/ets2panda/ir/ets/etsTypeReference.cpp @@ -61,13 +61,24 @@ ir::Identifier *ETSTypeReference::BaseName() const return baseName->AsIdentifier(); } - ir::TSQualifiedName *nameIter = baseName->AsTSQualifiedName(); - - while (nameIter->Left()->IsTSQualifiedName()) { - nameIter = nameIter->Left()->AsTSQualifiedName(); + if (baseName->IsIdentifier()) { + return baseName->AsIdentifier(); } + if (baseName->IsTSQualifiedName()) { + ir::TSQualifiedName *iter = baseName->AsTSQualifiedName(); - return nameIter->Left()->AsIdentifier(); + while (iter->Left()->IsTSQualifiedName()) { + iter = iter->Left()->AsTSQualifiedName(); + } + return iter->Left()->AsIdentifier(); + } else { + ir::MemberExpression *iter = baseName->AsMemberExpression(); + + 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 9dbef072a2..9b8197a6e3 100644 --- a/ets2panda/ir/ets/etsTypeReferencePart.cpp +++ b/ets2panda/ir/ets/etsTypeReferencePart.cpp @@ -225,15 +225,7 @@ 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); - } + ResolveBaseTypeWhenNoPrevious(checker, name); } } else { checker::Type *baseType = Previous()->GetType(checker); @@ -242,6 +234,22 @@ checker::Type *ETSTypeReferencePart::GetType(checker::ETSChecker *checker) return TsType(); } +void ETSTypeReferencePart::ResolveBaseTypeWhenNoPrevious(checker::ETSChecker *checker, ir::Expression *name) +{ + checker::Type *baseType = checker->GetReferencedTypeBase(name); + ES2PANDA_ASSERT(baseType != nullptr); + if (baseType->IsETSObjectType()) { + checker::InstantiationContext ctx(checker, baseType->AsETSObjectType(), TypeParams(), Start()); + if (baseType->IsGradualType()) { + SetTsType(checker->GetGlobalGradualType(ctx.Result())); + } else { + SetTsType(ctx.Result()); + } + } else { + SetTsType(baseType); + } +} + ETSTypeReferencePart *ETSTypeReferencePart::Clone(ArenaAllocator *const allocator, AstNode *const parent) { auto *const nameClone = Name() != nullptr ? Name()->Clone(allocator, nullptr)->AsExpression() : nullptr; diff --git a/ets2panda/ir/ets/etsTypeReferencePart.h b/ets2panda/ir/ets/etsTypeReferencePart.h index 9fe657e930..0d71b9c2ce 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; + void ResolveBaseTypeWhenNoPrevious(checker::ETSChecker *checker, ir::Expression *name); checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; ir::Identifier *GetIdent(); diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 2ea5a0d847..5d204ba95c 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, @@ -283,6 +284,9 @@ checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checke objType->ToAssemblerName().str() == compiler::Signatures::BUILTIN_RECORD) { type = AdjustRecordReturnType(type, objType); } + if (type != nullptr && (type->IsGradualType() || type->HasTypeFlag(checker::TypeFlag::GRADUAL_MARK))) { + type = checker->GetOrCreateGradualType(type); + } if (PropVar() != nullptr) { // access erased property type uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar()); @@ -445,8 +449,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()) { + SetObjectType(baseType->AsETSObjectType()); + return HandleComputedInGradualType(checker, baseType); + } + if (baseType->IsETSArrayType()) { auto *dflt = baseType->AsETSArrayType()->ElementType(); if (!checker->ValidateArrayIndex(property_)) { diff --git a/ets2panda/ir/expressions/memberExpression.h b/ets2panda/ir/expressions/memberExpression.h index 4906dc35d2..4c72e652fe 100644 --- a/ets2panda/ir/expressions/memberExpression.h +++ b/ets2panda/ir/expressions/memberExpression.h @@ -251,6 +251,7 @@ private: checker::Type *TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType); bool CheckArrayIndexValue(checker::ETSChecker *checker) const; + checker::Type *HandleComputedInGradualType(checker::ETSChecker *checker, checker::Type *baseType); checker::Type *CheckIndexAccessMethod(checker::ETSChecker *checker); checker::Type *ResolveReturnTypeFromSignature(checker::ETSChecker *checker, bool isSetter, ArenaVector &arguments, diff --git a/ets2panda/ir/ts/tsTypeReference.cpp b/ets2panda/ir/ts/tsTypeReference.cpp index 57b8f7d2ae..d71e15963d 100644 --- a/ets2panda/ir/ts/tsTypeReference.cpp +++ b/ets2panda/ir/ts/tsTypeReference.cpp @@ -91,14 +91,21 @@ 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(); + } + return iter->Left()->AsIdentifier(); + } else { + ir::MemberExpression *iter = typeName_->AsMemberExpression(); - while (iter->Left()->IsTSQualifiedName()) { - iter = iter->Left()->AsTSQualifiedName(); + while (iter->Property()->IsMemberExpression()) { + iter = iter->Property()->AsMemberExpression(); + } + return iter->Property()->AsIdentifier(); } - - return iter->Left()->AsIdentifier(); } checker::Type *TSTypeReference::Check([[maybe_unused]] checker::TSChecker *checker) diff --git a/ets2panda/lsp/src/isolated_declaration.cpp b/ets2panda/lsp/src/isolated_declaration.cpp index d4df74d45d..3d4cdf3738 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_MARK: return HandleObjectType(checkerType, checker); case checker::TypeFlag::ETS_ARRAY: diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 2a7ea152f1..ed35b3c354 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -282,6 +282,7 @@ void ETSParser::ParseParseListElement(const util::ImportPathManager::ParseInfo & auto src = importData.HasSpecifiedDeclPath() ? importData.declPath : importData.resolvedSource; SourceFile sf {src, extSrc->View().Utf8(), importData.resolvedSource, false, importData.HasSpecifiedDeclPath()}; parser::Program *newProg = ParseSource(sf); + newProg->SetModuleLang(importData.lang); if (!importData.IsImplicitPackageImported() || newProg->IsPackage()) { AddDirectImportsToDirectExternalSources(directImportsFromMainSource, newProg); // don't insert the separate modules into the programs, when we collect implicit package imports diff --git a/ets2panda/parser/program/program.h b/ets2panda/parser/program/program.h index c29543942c..800b10165f 100644 --- a/ets2panda/parser/program/program.h +++ b/ets2panda/parser/program/program.h @@ -276,6 +276,26 @@ public: return moduleInfo_.kind == util::ModuleKind::PACKAGE; } + bool IsDeclForDynamicStaticInterop() const + { + return moduleInfo_.isDeclForDynamicStaticInterop; + } + + Language GetModuleLang() const + { + return moduleInfo_.lang; + } + + Language GetModuleLang() + { + return moduleInfo_.lang; + } + + void SetModuleLang(Language lang) + { + moduleInfo_.lang = lang; + } + void SetFlag(ProgramFlags flag); bool GetFlag(ProgramFlags flag) const; void SetASTChecked(); diff --git a/ets2panda/test/unit/CMakeLists.txt b/ets2panda/test/unit/CMakeLists.txt index c84184cf5f..78467d1449 100644 --- a/ets2panda/test/unit/CMakeLists.txt +++ b/ets2panda/test/unit/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(arktsconfig-parser) add_subdirectory(annotations) add_subdirectory(lsp) add_subdirectory(relative_path) +add_subdirectory(any_ins_test) ets2panda_add_gtest(es2panda_astdumper_tests CPP_SOURCES ast_dumper_test.cpp diff --git a/ets2panda/test/unit/any_ins_test/CMakeLists.txt b/ets2panda/test/unit/any_ins_test/CMakeLists.txt new file mode 100644 index 0000000000..b41cb7c83f --- /dev/null +++ b/ets2panda/test/unit/any_ins_test/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +ets2panda_add_gtest(any_ins_test + CPP_SOURCES any_ins_test.cpp +) diff --git a/ets2panda/test/unit/any_ins_test/any_ins_test.cpp b/ets2panda/test/unit/any_ins_test/any_ins_test.cpp new file mode 100644 index 0000000000..a84f106ba0 --- /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()->CreateETSAnyType(true)); + } + }); + } + }; + // 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()->CreateETSAnyType(true)); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ANY_ISINSTANCE, pandasm::Opcode::ISINSTANCE}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:std.core.Object;void;", expectedOps); +} + +// CC-OFFNXT(huge_depth) solid logic +TEST_F(AnyInsTest, any_ins_test_06) +{ + std::string_view text = R"( + export declare class X { + } + + function foo() { + let x = new X() + x == x + x === x + x ? true : false + })"; + + auto doNode = [this](ir::AstNode *ast) { + if (ast->IsClassDefinition() && ast->AsClassDefinition()->IsGlobal()) { + auto cls = ast->AsClassDefinition(); + cls->IterateRecursively([this](ir::AstNode *astInClass) { + if (!astInClass->IsTyped()) { + return; + } + auto tstype = astInClass->AsTyped()->TsType(); + if (tstype != nullptr && tstype->IsETSObjectType()) { + astInClass->AsTyped()->SetTsType(Checker()->GlobalETSAnyType()); + } + }); + } + }; + // Manually replace ETSObject type to ETSAnyType for testing purpose. + auto program = RunCheckerWithCustomFunc("dummy.ets", text, doNode); + ASSERT_NE(program, nullptr); + + const std::vector expectedOps = {pandasm::Opcode::ETS_EQUALS, pandasm::Opcode::ETS_STRICTEQUALS}; + AssertFunctionInstructions(program.get(), "dummy.ETSGLOBAL.foo:void;", expectedOps); +} + +} // namespace ark::es2panda::compiler::test \ No newline at end of file diff --git a/ets2panda/test/utils/asm_test.cpp b/ets2panda/test/utils/asm_test.cpp index 53fb2a8dcf..1f8e1c6d59 100644 --- a/ets2panda/test/utils/asm_test.cpp +++ b/ets2panda/test/utils/asm_test.cpp @@ -298,7 +298,7 @@ void AsmTest::SetCurrentProgram(std::string_view src) std::unique_ptr AsmTest::GetCurrentProgram(std::string_view src) { - static constexpr std::string_view FILE_NAME = "annotation.ets"; + static constexpr std::string_view FILE_NAME = "dummy.ets"; std::array args = {"../../../../../bin/es2panda", "--ets-unnamed"}; // NOLINT(modernize-avoid-c-arrays) diff --git a/ets2panda/test/utils/checker_test.h b/ets2panda/test/utils/checker_test.h index 79259abbad..69f1fbdee0 100644 --- a/ets2panda/test/utils/checker_test.h +++ b/ets2panda/test/utils/checker_test.h @@ -20,7 +20,9 @@ #include "compiler/lowering/phase.h" #include "panda_executable_path_getter.h" #include "compiler/core/regSpiller.h" +#include "compiler/core/ETSCompiler.h" #include "compiler/core/ETSemitter.h" +#include "compiler/core/ETSGen.h" #include "checker/ETSAnalyzer.h" #include "ir/astNode.h" #include "util/options.h" @@ -84,6 +86,37 @@ public: compiler_alias::ETSEmitter>(&es2pandaPathPtr, fileName, src, &checker_, &program_); } + template + std::unique_ptr RunCheckerWithCustomFunc(std::string_view fileName, std::string_view src, + CustomFunc customFunc) + { + auto es2pandaPathPtr = es2pandaPath_.c_str(); + ASSERT(es2pandaPathPtr); + + return std::unique_ptr( + InitializeCheckerWithCustomFunc(&es2pandaPathPtr, fileName, src, + &checker_, &program_, customFunc)); + } + + template + static plib_alias::Context::CodeGenCb MakeCompileJob() + { + return [](plib_alias::Context *context, varbinder_alias::FunctionScope *scope, + compiler_alias::ProgramElement *programElement) -> void { + RegSpiller regSpiller; + ark::ArenaAllocator allocator(ark::SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + AstCompiler astcompiler; + compiler_alias::SetPhaseManager(context->phaseManager); + CodeGen cg(&allocator, ®Spiller, context, std::make_tuple(scope, programElement, &astcompiler)); + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.Generate(); + }; + } + template void InitializeChecker(char const *const *argv, std::string_view fileName, std::string_view src, @@ -137,6 +170,68 @@ public: } } } + + template + ark::pandasm::Program *InitializeCheckerWithCustomFunc(char const *const *argv, std::string_view fileName, + std::string_view src, checker_alias::ETSChecker *checker, + parser_alias::Program *program, CustomFunc customFunc) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto options = std::make_unique(argv[0], diagnosticEngine_); + if (!options->Parse(ark::Span(argv, 1))) { + return nullptr; + } + + ark::Logger::ComponentMask mask {}; + mask.set(ark::Logger::Component::ES2PANDA); + ark::Logger::InitializeStdLogging(options->LogLevel(), mask); + + ark::es2panda::Compiler compiler(options->GetExtension(), options->GetThread()); + ark::es2panda::SourceFile input(fileName, src, options->IsModule()); + compiler_alias::CompilationUnit unit {input, *options, 0, options->GetExtension(), diagnosticEngine_}; + auto parser = Parser(program, unit.options, diagnosticEngine_, + static_cast(unit.rawParserStatus)); + auto analyzer = Analyzer(checker); + checker->SetAnalyzer(&analyzer); + + auto *varbinder = program->VarBinder(); + varbinder->SetProgram(program); + + varbinder->SetContext(publicContext_.get()); + + auto emitter = Emitter(publicContext_.get()); + auto phaseManager = compiler_alias::PhaseManager(publicContext_.get(), unit.ext, allocator_.get()); + + auto config = plib_alias::ConfigImpl {}; + publicContext_->config = &config; + publicContext_->config->options = &unit.options; + publicContext_->sourceFile = &unit.input; + publicContext_->allocator = allocator_.get(); + publicContext_->parser = &parser; + parser.SetContext(publicContext_.get()); + publicContext_->parserProgram = program; + publicContext_->PushChecker(checker); + publicContext_->PushAnalyzer(publicContext_->GetChecker()->GetAnalyzer()); + publicContext_->emitter = &emitter; + publicContext_->diagnosticEngine = &diagnosticEngine_; + publicContext_->phaseManager = &phaseManager; + publicContext_->GetChecker()->Initialize(varbinder); + parser.ParseScript(unit.input, + unit.options.GetCompilationMode() == ark::es2panda::CompilationMode::GEN_STD_LIB); + while (auto phase = publicContext_->phaseManager->NextPhase()) { + if (!phase->Apply(publicContext_.get(), program)) { + return nullptr; + } + } + + // Run custom logic in here to modify ast + Program()->Ast()->IterateRecursively(customFunc); + + publicContext_->codeGenCb = MakeCompileJob(); + ark::es2panda::compiler::CompilerImpl compilerImpl(options->GetThread(), {}); + return compilerImpl.Emit(publicContext_.get()); + } NO_COPY_SEMANTIC(CheckerTest); NO_MOVE_SEMANTIC(CheckerTest); diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 568a3b7312..a575f67773 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1496,5 +1496,9 @@ semantic: message: "Class field '{}' defined by the parent class is not accessible in the child class via super." - name: INTERFACE_EXTENDS_CLASS - id: 373 + id: 378 message: "Interfaces cannot extend classes, only other interfaces." + +- name: DYMANIC_INIT_WITH_OBJEXPR + id: 379 + message: "Dymanic Type {} cannot be initialize with an object expression" diff --git a/ets2panda/util/importPathManager.cpp b/ets2panda/util/importPathManager.cpp index 7cf0466927..f69485a96a 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 9e011a2159..0537c7a067 100644 --- a/ets2panda/util/importPathManager.h +++ b/ets2panda/util/importPathManager.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H #define ES2PANDA_UTIL_IMPORT_PATH_MANAGER_H +#include "language.h" #if defined PANDA_TARGET_MOBILE #define USE_UNIX_SYSCALL #endif @@ -68,6 +69,7 @@ struct ModuleInfo { // 'Program::MaybeTransformToDeclarationModule'. bool isDeclForDynamicStaticInterop {}; // NOLINTEND(misc-non-private-member-variables-in-classes) + Language lang = Language(Language::Id::ETS); }; class ImportPathManager { diff --git a/ets2panda/varbinder/variableFlags.h b/ets2panda/varbinder/variableFlags.h index 1177e71a30..5ec081c2e6 100644 --- a/ets2panda/varbinder/variableFlags.h +++ b/ets2panda/varbinder/variableFlags.h @@ -165,6 +165,7 @@ enum class VariableFlags : uint64_t { ANNOTATIONUSAGE = 1ULL << 34ULL, NAMESPACE = 1ULL << 35ULL, INIT_IN_STATIC_BLOCK = 1ULL << 36ULL, + DYNAMIC = 1ULL << 37ULL, HOIST_VAR = HOIST | VAR, CLASS_OR_INTERFACE = CLASS | INTERFACE, -- Gitee