diff --git a/BUILD.gn b/BUILD.gn index a12adf7eac1e181c3ddc8c61430e34a59f6eb496..8a47ac16131496261e2badabd49bc841f2b3323f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -11,7 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -if ((defined(ark_standalone_build) && ark_standalone_build) || (defined(is_arkui_x) && is_arkui_x)) { +if ((defined(ark_standalone_build) && ark_standalone_build) || + (defined(is_arkui_x) && is_arkui_x)) { import("//arkcompiler/runtime_core/ark_config.gni") } else { import("//arkcompiler/ets_frontend/ark_config.gni") diff --git a/ark_config.gni b/ark_config.gni index 581cf22b006bc8af0817db79bb28ec109f8709cf..67f6106897e10c8968a851e148d0e7a2c620a7b3 100644 --- a/ark_config.gni +++ b/ark_config.gni @@ -15,16 +15,16 @@ import("ark_root.gni") if (is_standard_system) { default_enabled_plugins = [] - platform_libs = [] - platform_ldflags = [ "-static-libstdc++" ] - platform_include_dirs = [] - use_pbqp = false + platform_libs = [] + platform_ldflags = [ "-static-libstdc++" ] + platform_include_dirs = [] + use_pbqp = false } else { - default_enabled_plugins = [] - platform_libs = [] - platform_ldflags = [] - platform_include_dirs = [] - use_pbqp = false + default_enabled_plugins = [] + platform_libs = [] + platform_ldflags = [] + platform_include_dirs = [] + use_pbqp = false } enabled_plugins = default_enabled_plugins @@ -56,4 +56,4 @@ is_mob = !ark_standalone_build && !is_standard_system && enable_hilog = !ark_standalone_build && is_standard_system && current_os == "ohos" && - (current_cpu == "arm64" || current_cpu == "arm") \ No newline at end of file + (current_cpu == "arm64" || current_cpu == "arm") diff --git a/ark_root.gni b/ark_root.gni index 1fe90831239e65507c2fab374b2a522e93b9e777..6fd5ea34756196a859996a98f53c561f75a9b5fc 100644 --- a/ark_root.gni +++ b/ark_root.gni @@ -28,4 +28,4 @@ ark_root = "//arkcompiler/runtime_core" is_build_sdk = false if (device_name == "sdk") { is_build_sdk = true -} \ No newline at end of file +} diff --git a/es2panda/es2abc_config.gni b/es2panda/es2abc_config.gni index 3ea8013994e7d0fb481763f01c9081a397dd0434..018782c50591b5bba970b90f78a9bc6d9c03f043 100644 --- a/es2panda/es2abc_config.gni +++ b/es2panda/es2abc_config.gni @@ -68,11 +68,11 @@ template("es2abc_gen_abc") { deps += es2abc_build_deps } else { if (host_toolchain == toolchain_mac) { - external_deps = ["ets_frontend:es2panda($toolchain_mac)"] + external_deps = [ "ets_frontend:es2panda($toolchain_mac)" ] } else if (host_toolchain == toolchain_win) { - external_deps = ["ets_frontend:es2panda(${host_toolchain})"] + external_deps = [ "ets_frontend:es2panda(${host_toolchain})" ] } else { - external_deps = ["ets_frontend:es2panda($toolchain_linux)"] + external_deps = [ "ets_frontend:es2panda($toolchain_linux)" ] } } @@ -138,11 +138,11 @@ template("es2abc_gen_newest_abc") { if (host_toolchain == toolchain_mac) { deps += es2abc_build_deps } else if (host_toolchain == toolchain_win) { - external_deps = ["ets_frontend:es2panda(${host_toolchain})"] + external_deps = [ "ets_frontend:es2panda(${host_toolchain})" ] } else if (ark_standalone_build) { deps += es2abc_build_deps } else { - external_deps = ["ets_frontend:es2panda($toolchain_linux)"] + external_deps = [ "ets_frontend:es2panda($toolchain_linux)" ] } args = [ diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 1b0b4102617ff12c35ddec0490bed3b34e5671f0..fc8adbcf0c9180036939111174c800ce1c91b4ec 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -41,6 +41,12 @@ config("libes2panda_public_config") { } } +action("check_build_system_consistency") { + script = "./scripts/check_build_system_consistency.py" + args = [ rebase_path(".", root_build_dir) ] + outputs = [ "$target_gen_dir/consistency_check.stamp" ] +} + libes2panda_sources = [ "ast_verifier/ASTVerifier.cpp", "ast_verifier/helpers.cpp", @@ -69,7 +75,6 @@ libes2panda_sources = [ "checker/ETSAnalyzerHelpers.cpp", "checker/ETSAnalyzerUnreachable.cpp", "checker/ETSchecker.cpp", - "checker/IsolatedDeclgenChecker.cpp", "checker/JSchecker.cpp", "checker/TSAnalyzer.cpp", "checker/TSAnalyzerUnreachable.cpp", @@ -84,12 +89,9 @@ libes2panda_sources = [ "checker/ets/castingContext.cpp", "checker/ets/conversion.cpp", "checker/ets/dynamic.cpp", - "checker/ets/dynamic/dynamicCall.cpp", "checker/ets/etsWarningAnalyzer.cpp", "checker/ets/function.cpp", "checker/ets/helpers.cpp", - "checker/ets/narrowingConverter.cpp", - "checker/ets/narrowingWideningConverter.cpp", "checker/ets/object.cpp", "checker/ets/typeCheckingHelpers.cpp", "checker/ets/typeConverter.cpp", @@ -116,7 +118,6 @@ libes2panda_sources = [ "checker/types/ets/etsAsyncFuncReturnType.cpp", "checker/types/ets/etsBigIntType.cpp", "checker/types/ets/etsBooleanType.cpp", - "checker/types/ets/etsDynamicType.cpp", "checker/types/ets/etsEnumType.cpp", "checker/types/ets/etsExtensionFuncHelperType.cpp", "checker/types/ets/etsFunctionType.cpp", @@ -139,6 +140,7 @@ libes2panda_sources = [ "checker/types/ets/shortType.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", @@ -214,26 +216,30 @@ libes2panda_sources = [ "compiler/function/generatorFunctionBuilder.cpp", "compiler/lowering/checkerPhase.cpp", "compiler/lowering/ets/ambientLowering.cpp", + "compiler/lowering/ets/annotationCopyLowering.cpp", + "compiler/lowering/ets/annotationCopyPostLowering.cpp", "compiler/lowering/ets/arrayLiteralLowering.cpp", "compiler/lowering/ets/asyncMethodLowering.cpp", "compiler/lowering/ets/bigintLowering.cpp", - "compiler/lowering/ets/boxedTypeLowering.cpp", "compiler/lowering/ets/boxingForLocals.cpp", "compiler/lowering/ets/capturedVariables.cpp", "compiler/lowering/ets/cfgBuilderPhase.cpp", "compiler/lowering/ets/constantExpressionLowering.cpp", "compiler/lowering/ets/convertPrimitiveCastMethodCall.cpp", + "compiler/lowering/ets/declGenPhase.cpp", "compiler/lowering/ets/declareOverloadLowering.cpp", "compiler/lowering/ets/defaultParametersInConstructorLowering.cpp", "compiler/lowering/ets/defaultParametersLowering.cpp", - "compiler/lowering/ets/dynamicImportLowering.cpp", + "compiler/lowering/ets/dynamicImport.cpp", "compiler/lowering/ets/enumLowering.cpp", "compiler/lowering/ets/enumPostCheckLowering.cpp", + "compiler/lowering/ets/enumPropertiesInAnnotationsLowering.cpp", "compiler/lowering/ets/expandBrackets.cpp", "compiler/lowering/ets/exportAnonymousConst.cpp", "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", @@ -245,14 +251,17 @@ libes2panda_sources = [ "compiler/lowering/ets/opAssignment.cpp", "compiler/lowering/ets/optionalArgumentsLowering.cpp", "compiler/lowering/ets/optionalLowering.cpp", + "compiler/lowering/ets/overloadMappingLowering.cpp", "compiler/lowering/ets/packageImplicitImport.cpp", "compiler/lowering/ets/partialExportClassGen.cpp", + "compiler/lowering/ets/primitiveConversionPhase.cpp", "compiler/lowering/ets/promiseVoid.cpp", "compiler/lowering/ets/recordLowering.cpp", "compiler/lowering/ets/resizableArrayLowering.cpp", "compiler/lowering/ets/restArgsLowering.cpp", "compiler/lowering/ets/restTupleLowering.cpp", "compiler/lowering/ets/setJumpTarget.cpp", + "compiler/lowering/ets/setterLowering.cpp", "compiler/lowering/ets/spreadLowering.cpp", "compiler/lowering/ets/stringComparison.cpp", "compiler/lowering/ets/stringConstantsLowering.cpp", @@ -262,6 +271,7 @@ libes2panda_sources = [ "compiler/lowering/ets/topLevelStmts/importExportDecls.cpp", "compiler/lowering/ets/topLevelStmts/topLevelStmts.cpp", "compiler/lowering/ets/typeFromLowering.cpp", + "compiler/lowering/ets/unboxLowering.cpp", "compiler/lowering/ets/unionLowering.cpp", "compiler/lowering/phase.cpp", "compiler/lowering/plugin_phase.cpp", @@ -295,6 +305,7 @@ libes2panda_sources = [ "ir/base/decorator.cpp", "ir/base/metaProperty.cpp", "ir/base/methodDefinition.cpp", + "ir/base/overloadDeclaration.cpp", "ir/base/property.cpp", "ir/base/scriptFunction.cpp", "ir/base/scriptFunctionSignature.cpp", @@ -307,6 +318,7 @@ libes2panda_sources = [ "ir/brokenTypeNode.cpp", "ir/ets/etsClassLiteral.cpp", "ir/ets/etsFunctionType.cpp", + "ir/ets/etsIntrinsicNode.cpp", "ir/ets/etsKeyofType.cpp", "ir/ets/etsModule.cpp", "ir/ets/etsNeverType.cpp", @@ -471,11 +483,11 @@ libes2panda_sources = [ "parser/ETSparserClasses.cpp", "parser/ETSparserEnums.cpp", "parser/ETSparserExpressions.cpp", - "parser/ETSparserJsDocInfo.cpp", "parser/ETSparserNamespaces.cpp", "parser/ETSparserStatements.cpp", "parser/ETSparserTypes.cpp", "parser/JSparser.cpp", + "parser/JsdocHelper.cpp", "parser/TSparser.cpp", "parser/ThrowingTypedParser.cpp", "parser/TypedParser.cpp", @@ -488,6 +500,7 @@ libes2panda_sources = [ "parser/program/program.cpp", "parser/statementParser.cpp", "parser/statementTSParser.cpp", + "public/public.cpp", "util/arktsconfig.cpp", "util/bitset.cpp", "util/diagnostic.cpp", @@ -497,7 +510,9 @@ libes2panda_sources = [ "util/es2pandaMacros.cpp", "util/helpers.cpp", "util/importPathManager.cpp", + "util/nameMangler.cpp", "util/path.cpp", + "util/perfMetrics.cpp", "util/plugin.cpp", "util/ustring.cpp", "varbinder/ASBinder.cpp", @@ -540,6 +555,7 @@ HEADERS_TO_BE_PARSED = [ "ir/statement.h", "ir/irnode.h", "checker/types/typeRelation.h", + "ir/ets/etsIntrinsicNode.h", "ir/visitor/AstVisitor.h", "ir/statements/classDeclaration.h", "ir/base/tsMethodSignature.h", @@ -548,7 +564,6 @@ HEADERS_TO_BE_PARSED = [ "checker/types/ts/nonPrimitiveType.h", "ir/ts/tsTypeParameterInstantiation.h", "ir/module/importDeclaration.h", - "checker/types/ets/etsDynamicType.h", "ir/statements/doWhileStatement.h", "ir/expressions/literals/bigIntLiteral.h", "ir/expressions/assignmentExpression.h", @@ -636,7 +651,6 @@ HEADERS_TO_BE_PARSED = [ "ir/ts/tsParenthesizedType.h", "ir/ts/tsModuleDeclaration.h", "ir/ets/etsPackageDeclaration.h", - "checker/types/ets/etsDynamicFunctionType.h", "ir/expressions/literals/regExpLiteral.h", "ir/ets/etsNewArrayInstanceExpression.h", "checker/types/ets/etsVoidType.h", @@ -726,6 +740,7 @@ HEADERS_TO_BE_PARSED = [ "ir/ts/tsImportEqualsDeclaration.h", "ir/validationInfo.h", "ir/base/methodDefinition.h", + "ir/base/overloadDeclaration.h", "ir/ts/tsIntersectionType.h", "checker/types/ts/nullType.h", "checker/types/ts/unknownType.h", @@ -762,7 +777,6 @@ HEADERS_TO_BE_PARSED = [ "ir/ets/etsParameterExpression.h", "ir/ts/tsTypeQuery.h", "ir/expressions/importExpression.h", - "ir/jsDocAllowed.h", "varbinder/variable.h", "varbinder/scope.h", "varbinder/varbinder.h", @@ -780,6 +794,9 @@ HEADERS_TO_BE_PARSED = [ "es2panda.h", "ast_verifier/ASTVerifier.h", "util/importPathManager.h", + "util/options.h", + "util/path.h", + "util/arktsconfig.h", ] ES2PANDA_API_GENERATED = [ @@ -789,7 +806,6 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/varbinder/variableFlags.yaml", "$LIBGEN_DIR/gen/headers/ir/typed.yaml", "$LIBGEN_DIR/gen/headers/ir/annotationAllowed.yaml", - "$LIBGEN_DIR/gen/headers/ir/jsDocAllowed.yaml", "$LIBGEN_DIR/gen/headers/es2panda.yaml", "$LIBGEN_DIR/gen/headers/ir/statements/labelledStatement.yaml", "$LIBGEN_DIR/gen/headers/checker/types/ts/unknownType.yaml", @@ -809,7 +825,6 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/ir/expressions/objectExpression.yaml", "$LIBGEN_DIR/gen/headers/ir/module/importSpecifier.yaml", "$LIBGEN_DIR/gen/headers/ir/expressions/conditionalExpression.yaml", - "$LIBGEN_DIR/gen/headers/checker/types/ets/etsDynamicFunctionType.yaml", "$LIBGEN_DIR/gen/headers/ir/expressions/callExpression.yaml", "$LIBGEN_DIR/gen/headers/ir/expressions/literals/bigIntLiteral.yaml", "$LIBGEN_DIR/gen/headers/ir/base/classElement.yaml", @@ -819,6 +834,7 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/ir/visitor/IterateAstVisitor.yaml", "$LIBGEN_DIR/gen/headers/ir/statements/functionDeclaration.yaml", "$LIBGEN_DIR/gen/headers/ir/ets/etsTypeReference.yaml", + "$LIBGEN_DIR/gen/headers/ir/ets/etsIntrinsicNode.yaml", "$LIBGEN_DIR/gen/headers/checker/types/ts/tupleType.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsTypeReference.yaml", "$LIBGEN_DIR/gen/headers/checker/types/ts/functionType.yaml", @@ -933,7 +949,6 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/ir/expressions/blockExpression.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsTypeLiteral.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsTypeParameter.yaml", - "$LIBGEN_DIR/gen/headers/checker/types/ets/etsDynamicType.yaml", "$LIBGEN_DIR/gen/headers/checker/types/ets/charType.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsBooleanKeyword.yaml", "$LIBGEN_DIR/gen/headers/ir/base/spreadElement.yaml", @@ -973,6 +988,7 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/ir/expressions/directEvalExpression.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsTypeParameterDeclaration.yaml", "$LIBGEN_DIR/gen/headers/ir/base/methodDefinition.yaml", + "$LIBGEN_DIR/gen/headers/ir/base/overloadDeclaration.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsNullKeyword.yaml", "$LIBGEN_DIR/gen/headers/ir/ts/tsInterfaceHeritage.yaml", "$LIBGEN_DIR/gen/headers/checker/types/ts/enumLiteralType.yaml", @@ -1047,6 +1063,9 @@ ES2PANDA_API_GENERATED = [ "$LIBGEN_DIR/gen/headers/parser/program/program.yaml", "$LIBGEN_DIR/gen/headers/ast_verifier/ASTVerifier.yaml", "$LIBGEN_DIR/gen/headers/util/importPathManager.yaml", + "$LIBGEN_DIR/gen/headers/util/path.yaml", + "$LIBGEN_DIR/gen/headers/util/arktsconfig.yaml", + "$LIBGEN_DIR/gen/headers/util/options.yaml", ] ES2PANDA_API = [ @@ -1064,6 +1083,7 @@ action("libes2panda_public_parse_headers") { script = "./public/headers_parser/main.py" deps = [ + ":check_build_system_consistency", ":es2panda_options_gen_options_h", ":gen_es2panda_lexer_tokenType_h", ] @@ -1106,6 +1126,7 @@ ark_gen("gen") { # libarkruntime, and conflict with JIT setup ensues libes2panda_public_sources = [ "declgen_ets2ts/declgenEts2Ts.cpp", + "declgen_ets2ts/isolatedDeclgenChecker.cpp", "public/${LIB_NAME}.cpp", "util/generateBin.cpp", "util/options.cpp", @@ -1134,6 +1155,7 @@ if (ark_standalone_build || ark_static_standalone_build) { "$ark_root/assembler:arkassembler_public_config", "$ark_root/libpandabase:arkbase_public_config", "$ark_root/libpandafile:arkfile_public_config", + "$ark_root/abc2program:arkts_abc2program_public_config", ] } @@ -1187,6 +1209,7 @@ ohos_source_set("libes2panda_frontend_static") { ":isa_gen_es2panda_isa_h", ] external_deps = [ + "runtime_core:libarktsabc2program_package", "runtime_core:libarktsassembler_package", "runtime_core:libarktsbase_package", "runtime_core:libarktscompiler_package", @@ -1202,6 +1225,7 @@ ohos_source_set("libes2panda_frontend_static") { external_deps += [ "icu:static_icui18n", "icu:static_icuuc", + "runtime_core:arkts_abc2program_public_headers", "runtime_core:assembler_headers", "runtime_core:libpandabase_headers", "runtime_core:libpandafile_headers", @@ -1238,7 +1262,7 @@ if (!(defined(ark_static_standalone_build) && ark_static_standalone_build)) { ":gen_${LIB_NAME}_idl", ":gen_${LIB_NAME}_impl_inc", ":gen_${LIB_NAME}_include_inc", - ":gen_${LIB_NAME}_list_inc" + ":gen_${LIB_NAME}_list_inc", ] part_name = "ets_frontend" subsystem_name = "arkcompiler" @@ -1267,12 +1291,20 @@ ohos_source_set("libes2panda_public_frontend_static") { } external_deps = [ + "runtime_core:libarktsabc2program_package", "runtime_core:libarktsbytecodeopt_package", sdk_libc_secshared_dep, ] if (ark_standalone_build || ark_static_standalone_build) { - deps += [ "$ark_root/bytecode_optimizer:libarktsbytecodeopt_package" ] + deps += [ + "$ark_root/abc2program:libarktsabc2program_package", + "$ark_root/assembler:libarktsassembler", + "$ark_root/bytecode_optimizer:libarktsbytecodeopt_package", + "$ark_root/compiler:libarktscompiler", + "$ark_root/libpandabase:libarktsbase", + "$ark_root/libpandafile:libarktsfile", + ] } else { external_deps += [ "runtime_core:assembler_headers", @@ -1313,9 +1345,9 @@ ark_gen("es2panda_diagnostic_gen") { "util/diagnostic/semantic.yaml", "util/diagnostic/warning.yaml", "util/diagnostic/fatal.yaml", - "util/diagnostic/isolated_declgen.yaml", "declgen_ets2ts/declgen_ets2ts_error.yaml", "declgen_ets2ts/declgen_ets2ts_warning.yaml", + "declgen_ets2ts/isolated_declgen.yaml", "util/diagnostic/arktsconfig_error.yaml", ] template_files = [ "diagnostic.h.erb" ] @@ -1358,3 +1390,41 @@ ark_gen("gen_es2panda_compiler") { destination = "$target_gen_dir/generated" api = [ "compiler/scripts/signatures.rb" ] } + +template("panda_code_fix_gen") { + code_fix_api = "$ark_es2panda_root/lsp/code_fix_register.rb" + api_list = [] + foreach(i, invoker.data) { + api_list += [ code_fix_api ] + } + + ark_gen("$target_name") { + data = invoker.data + template_files = invoker.template_files + sources = invoker.sources + destination = invoker.destination + api = api_list + if (defined(invoker.requires)) { + requires = invoker.requires + } + if (defined(invoker.extra_dependencies)) { + extra_dependencies = invoker.extra_dependencies + } + } +} + +panda_code_fix_gen("es2panda_lsp_code_fix_register_gen") { + data = [ + "util/diagnostic/syntax.yaml", + "util/diagnostic/semantic.yaml", + "util/diagnostic/warning.yaml", + "util/diagnostic/fatal.yaml", + "declgen_ets2ts/declgen_ets2ts_error.yaml", + "declgen_ets2ts/declgen_ets2ts_warning.yaml", + "declgen_ets2ts/isolated_declgen.yaml", + "util/diagnostic/arktsconfig_error.yaml", + ] + template_files = [ "code_fix_register.h.erb" ] + sources = "lsp" + destination = "$target_gen_dir/generated" +} diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 2ae469b045e143d4776a3c90c0dab8b4baf52b51..30cae66ddec7250e2afcbd3cc7d5fc6c099740ed 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -86,10 +86,10 @@ if(PANDA_WITH_ETS) " \"@arkts.collections\": [\"${STATIC_CORE}${DELIM}plugins${DELIM}ets${DELIM}sdk${DELIM}arkts${DELIM}@arkts.collections.ets\"],\n" " \"import_tests\": [\"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/import_tests\"]\n" " },\n" - " \"dynamicPaths\": {\n" + " \"dependencies\": {\n" " \"dynamic_import_tests\": {\"language\": \"js\", \"ohmUrl\": \"dynamic_import_tests\"},\n" - " \"dynamic_import_tests/modules/instanceof\": {\"language\": \"js\", \"declPath\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/instanceof.ets\", \"ohmUrl\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/instanceof.ets\"},\n" - " \"dynamic_import_tests/modules/module\": {\"language\": \"js\", \"declPath\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/module.ets\", \"ohmUrl\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/module.ets\"}\n" + " \"dynamic_import_tests/modules/instanceof\": {\"language\": \"js\", \"path\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/instanceof.ets\", \"ohmUrl\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/instanceof.ets\"},\n" + " \"dynamic_import_tests/modules/module\": {\"language\": \"js\", \"path\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/module.ets\", \"ohmUrl\": \"${CMAKE_CURRENT_SOURCE_DIR}/test/parser/ets/dynamic_import_tests/modules/module.ets\"}\n" " }\n" " }\n" "}\n" @@ -157,8 +157,8 @@ panda_gen( ${DIAGNOSTIC_DIR}/fatal.yaml ${CMAKE_CURRENT_SOURCE_DIR}/declgen_ets2ts/declgen_ets2ts_error.yaml ${CMAKE_CURRENT_SOURCE_DIR}/declgen_ets2ts/declgen_ets2ts_warning.yaml + ${CMAKE_CURRENT_SOURCE_DIR}/declgen_ets2ts/isolated_declgen.yaml ${DIAGNOSTIC_DIR}/arktsconfig_error.yaml - ${DIAGNOSTIC_DIR}/isolated_declgen.yaml TARGET_NAME es2panda_diagnostic_gen TEMPLATES diagnostic.h.erb SOURCE ${DIAGNOSTIC_DIR} @@ -289,20 +289,21 @@ 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/boxedTypeLowering.cpp compiler/lowering/ets/boxingForLocals.cpp compiler/lowering/ets/capturedVariables.cpp compiler/lowering/ets/cfgBuilderPhase.cpp compiler/lowering/ets/constantExpressionLowering.cpp compiler/lowering/ets/convertPrimitiveCastMethodCall.cpp compiler/lowering/ets/declareOverloadLowering.cpp + compiler/lowering/ets/declGenPhase.cpp compiler/lowering/ets/defaultParametersInConstructorLowering.cpp compiler/lowering/ets/defaultParametersLowering.cpp - compiler/lowering/ets/dynamicImportLowering.cpp 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/objectIndexAccess.cpp @@ -319,6 +320,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/unionLowering.cpp compiler/lowering/ets/optionalArgumentsLowering.cpp compiler/lowering/ets/optionalLowering.cpp + compiler/lowering/ets/overloadMappingLowering.cpp compiler/lowering/ets/expandBrackets.cpp compiler/lowering/ets/packageImplicitImport.cpp compiler/lowering/ets/partialExportClassGen.cpp @@ -331,7 +333,13 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/typeFromLowering.cpp compiler/lowering/ets/enumLowering.cpp compiler/lowering/ets/enumPostCheckLowering.cpp + compiler/lowering/ets/enumPropertiesInAnnotationsLowering.cpp compiler/lowering/ets/setJumpTarget.cpp + compiler/lowering/ets/setterLowering.cpp + compiler/lowering/ets/annotationCopyLowering.cpp + compiler/lowering/ets/annotationCopyPostLowering.cpp + compiler/lowering/ets/primitiveConversionPhase.cpp + compiler/lowering/ets/unboxLowering.cpp ir/astDump.cpp ir/srcDump.cpp ir/astNode.cpp @@ -348,6 +356,7 @@ set(ES2PANDA_LIB_SRC ir/base/decorator.cpp ir/base/metaProperty.cpp ir/base/methodDefinition.cpp + ir/base/overloadDeclaration.cpp ir/base/property.cpp ir/base/scriptFunction.cpp ir/base/scriptFunctionSignature.cpp @@ -435,6 +444,7 @@ set(ES2PANDA_LIB_SRC ir/as/namedType.cpp ir/as/prefixAssertionExpression.cpp ir/ets/etsClassLiteral.cpp + ir/ets/etsIntrinsicNode.cpp ir/ets/etsFunctionType.cpp ir/ets/etsKeyofType.cpp ir/ets/etsNewArrayInstanceExpression.cpp @@ -519,6 +529,7 @@ set(ES2PANDA_LIB_SRC parser/expressionTSParser.cpp parser/ASparser.cpp parser/JSparser.cpp + parser/JsdocHelper.cpp parser/parserImpl.cpp parser/ETSFormattedParser.cpp parser/ETSparser.cpp @@ -526,7 +537,6 @@ set(ES2PANDA_LIB_SRC parser/ETSparserClasses.cpp parser/ETSparserEnums.cpp parser/ETSparserExpressions.cpp - parser/ETSparserJsDocInfo.cpp parser/ETSparserNamespaces.cpp parser/ETSparserStatements.cpp parser/ETSparserTypes.cpp @@ -538,6 +548,7 @@ set(ES2PANDA_LIB_SRC parser/program/program.cpp parser/statementParser.cpp parser/statementTSParser.cpp + public/public.cpp checker/checker.cpp checker/checkerContext.cpp checker/ETSAnalyzer.cpp @@ -549,7 +560,6 @@ set(ES2PANDA_LIB_SRC checker/TSAnalyzer.cpp checker/TSAnalyzerUnreachable.cpp checker/JSchecker.cpp - checker/IsolatedDeclgenChecker.cpp checker/typeChecker/TypeChecker.cpp checker/ets/aliveAnalyzer.cpp checker/ets/etsWarningAnalyzer.cpp @@ -560,13 +570,10 @@ set(ES2PANDA_LIB_SRC checker/ets/castingContext.cpp checker/ets/conversion.cpp checker/ets/dynamic.cpp - checker/ets/dynamic/dynamicCall.cpp checker/ets/function.cpp checker/ets/validateHelpers.cpp checker/ets/typeCheckingHelpers.cpp checker/ets/helpers.cpp - checker/ets/narrowingConverter.cpp - checker/ets/narrowingWideningConverter.cpp checker/ets/object.cpp checker/ets/typeConverter.cpp checker/ets/typeCreation.cpp @@ -586,6 +593,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 @@ -596,7 +604,6 @@ set(ES2PANDA_LIB_SRC checker/types/ets/etsAnyType.cpp checker/types/ets/etsArrayType.cpp checker/types/ets/etsBooleanType.cpp - checker/types/ets/etsDynamicType.cpp checker/types/ets/etsEnumType.cpp checker/types/ets/etsExtensionFuncHelperType.cpp checker/types/ets/etsFunctionType.cpp @@ -654,9 +661,11 @@ set(ES2PANDA_LIB_SRC util/es2pandaMacros.cpp util/helpers.cpp util/importPathManager.cpp + util/nameMangler.cpp util/path.cpp + util/plugin.cpp + util/perfMetrics.cpp util/ustring.cpp - test/utils/panda_executable_path_getter.cpp evaluate/debugInfoDeserialization/debugInfoDeserializer.cpp evaluate/debugInfoDeserialization/inheritanceResolution.cpp evaluate/debugInfoDeserialization/methodBuilder.cpp @@ -695,9 +704,16 @@ panda_target_compile_options(es2panda-lib PRIVATE -fexceptions -Werror=shadow ) +if (EN_ISOLATED_DECLGEN) + message(STATUS "Isolated declgen enabled") + panda_target_compile_definitions(es2panda-lib + PRIVATE -DENABLE_ISOLATED_DECLGEN + ) +endif() + panda_target_link_libraries(es2panda-lib PUBLIC arkbase hmicuuc.z - PRIVATE arkassembler arkdisassembler arkfile + PRIVATE arkassembler arkdisassembler arkfile abc2program ) if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) OR diff --git a/ets2panda/REVIEWERS b/ets2panda/REVIEWERS index 663ccf744fea2522a4a47fcca8c457704cead6b7..3094eef833dd1bb2cdef7034e1eb6cce4a5f247a 100644 --- a/ets2panda/REVIEWERS +++ b/ets2panda/REVIEWERS @@ -40,7 +40,7 @@ /ets2panda/declgen_ets2ts/ @dreamdoomwalker ^trubachevilya ^ivagin ^igelhaus ^Prof1983 ^luobohua2025 @chenmudan /ets2panda/driver/ @trubachevilya @dreamdoomwalker ^igelhaus ^Prof1983 ^luobohua2025 @chenmudan /ets2panda/ir/ @ziziziiziziz ^gavin1012_hw @csabaosztrogonac ^igelhaus ^Prof1983 -/ets2panda/linter/ @ragnvald @rosinskiyigor @eokolnov1 @liwentao_uiw ^utkugursel +/ets2panda/linter/ @rosinskiyigor @eokolnov1 @yyytiancai ^utkugursel /ets2panda/parser/ @robertsipka @shimenkovmikhail ^igelhaus ^csabaosztrogonac ^Prof1983 ^chernykhsergey ^zelentsovdmitry /ets2panda/scripts/*-build.sh @titovatatiana ^igelhaus ^Prof1983 /ets2panda/scripts/arkui* @titovatatiana ^igelhaus ^Prof1983 diff --git a/ets2panda/aot/BUILD.gn b/ets2panda/aot/BUILD.gn index 0c56beff07d52d7d4d293575e1420f7bfbad8ab2..c650d73072d88a6d8705d37bf038f44a085c6db9 100644 --- a/ets2panda/aot/BUILD.gn +++ b/ets2panda/aot/BUILD.gn @@ -40,7 +40,7 @@ ohos_executable("ets2panda") { "//build/config/components/runtime_core/static_core:ark_common_config", ] } - + if (ark_standalone_build || ark_static_standalone_build) { configs += [ "$ark_root/assembler:arkassembler_public_config", @@ -52,9 +52,7 @@ ohos_executable("ets2panda") { ] } - configs += [ - "$ark_es2panda_root:libes2panda_public_config", - ] + configs += [ "$ark_es2panda_root:libes2panda_public_config" ] deps = [ "$ark_es2panda_root:libes2panda_frontend_static", @@ -70,6 +68,7 @@ ohos_executable("ets2panda") { } external_deps = [ + "runtime_core:libarktsabc2program_package", "runtime_core:libarktsassembler_package", "runtime_core:libarktsbase_package", "runtime_core:libarktsbytecodeopt_package", @@ -82,15 +81,19 @@ ohos_executable("ets2panda") { external_deps += [ "runtime_core:assembler_headers", "runtime_core:bytecode_optimizer_headers", + "runtime_core:compiler_headers", "runtime_core:libpandabase_headers", "runtime_core:libpandafile_headers", "runtime_core:runtime_gen_headers", "runtime_core:runtime_headers", - "runtime_core:compiler_headers", "runtime_core:verification_headers", ] } + if (defined(ohos_indep_compiler_enable) && ohos_indep_compiler_enable) { + external_deps += [ "icu:shared_icuuc" ] + } + libs = platform_libs ldflags = platform_ldflags if (is_linux) { diff --git a/ets2panda/aot/main.cpp b/ets2panda/aot/main.cpp index df7b35d0dcd6d0e3dffaec26fa50696024d6b4f3..4e2aeabb7c99377afd4dfdbdb164b884c214e080 100644 --- a/ets2panda/aot/main.cpp +++ b/ets2panda/aot/main.cpp @@ -109,8 +109,8 @@ static unsigned int ReleaseInputsAndReturn(std::vector &parserInp return returnCode; } -static unsigned int CompileFromConfig(es2panda::Compiler &compiler, util::Options *options, - util::DiagnosticEngine &diagnosticEngine) +static int CompileFromConfig(es2panda::Compiler &compiler, util::Options *options, + util::DiagnosticEngine &diagnosticEngine) { auto compilationList = FindProjectSources(options->ArkTSConfig()); if (compilationList.empty()) { @@ -215,6 +215,9 @@ static int Run(Span args) es2panda::SourceFile input(sourceFile, parserInput, options->IsModule(), options->GetOutput()); res = CompileFromSource(compiler, input, *options.get(), diagnosticEngine); } + if (options->IsDumpPerfMetrics()) { + util::DumpPerfMetrics(); + } diagnosticEngine.FlushDiagnostic(); return res; } diff --git a/ets2panda/ast_verifier/ASTVerifier.h b/ets2panda/ast_verifier/ASTVerifier.h index 217bd310ff32b259e30947657579e1a358f71e8a..edd854d180e676902b83c79a880c711717515d54 100644 --- a/ets2panda/ast_verifier/ASTVerifier.h +++ b/ets2panda/ast_verifier/ASTVerifier.h @@ -139,7 +139,8 @@ public: i <= VerifierInvariants::AFTER_CHECKER_PHASE_LAST; i++) { allowed_[i] = true; } - // NOTE(dkofanov): This should be called after "NumberLowering" phase: + } + if (occurredPhaseName == "Unbox") { Get()->SetNumberLoweringOccured(); } if (occurredPhaseName == "UnionLowering") { diff --git a/ets2panda/ast_verifier/helpers.cpp b/ets2panda/ast_verifier/helpers.cpp index 13d55e496cb3a1ff72fa56415462b0b8b08664a9..a2179fc411ba0b24db631a82db400ba318fc1b32 100644 --- a/ets2panda/ast_verifier/helpers.cpp +++ b/ets2panda/ast_verifier/helpers.cpp @@ -19,6 +19,7 @@ #include "checker/types/type.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsUnionType.h" +#include "checker/types/gradualType.h" #include "ir/statements/blockStatement.h" #include "ir/ets/etsModule.h" #include "parser/program/program.h" @@ -50,18 +51,17 @@ bool IsBooleanType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - ast->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); + return type->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || type->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); } bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) @@ -75,30 +75,29 @@ bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); if (IsBooleanType(ast)) { return isBitwise; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { return true; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - ast->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && - !typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && + !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); + return type->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || + type->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || type->HasTypeFlag(checker::TypeFlag::BIGINT) || + type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); } bool IsStringType(const ir::AstNode *ast) @@ -112,17 +111,18 @@ bool IsStringType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE); + return type->HasTypeFlag(checker::TypeFlag::STRING_LIKE); } bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode) @@ -156,7 +156,8 @@ const checker::Type *GetClassDefinitionType(const ir::AstNode *ast) return nullptr; } auto *classDefinition = tmpNode->AsClassDefinition(); - return classDefinition->TsType(); + return classDefinition->TsType()->IsGradualType() ? classDefinition->TsType()->AsGradualType()->GetBaseType() + : classDefinition->TsType(); } const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) @@ -169,7 +170,9 @@ const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) return nullptr; } auto *tsInterfaceDeclaration = tmpNode->AsTSInterfaceDeclaration(); - return tsInterfaceDeclaration->TsType(); + return tsInterfaceDeclaration->TsType()->IsGradualType() + ? tsInterfaceDeclaration->TsType()->AsGradualType()->GetBaseType() + : tsInterfaceDeclaration->TsType(); } bool ValidateMethodAccessForClass(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode, @@ -325,8 +328,8 @@ bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir } if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && memberObjType->SuperType() != nullptr && - memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | - checker::ETSObjectFlags::GLOBAL)) { + memberObjType->SuperType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | + checker::ETSObjectFlags::GLOBAL)) { return true; } const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode(); diff --git a/ets2panda/ast_verifier/invariants/everyChildHasValidParent.h b/ets2panda/ast_verifier/invariants/everyChildHasValidParent.h index d18220f781f94a2e7563cdc420aca729529d7714..a2a3070c25b20d307a04d462dc0904490e2fbdc2 100644 --- a/ets2panda/ast_verifier/invariants/everyChildHasValidParent.h +++ b/ets2panda/ast_verifier/invariants/everyChildHasValidParent.h @@ -16,7 +16,7 @@ #ifndef ES2PANDA_COMPILER_CORE_AST_VERIFIER_INVARIANTS_EVERYCHILDHASVALIDPARENT_H #define ES2PANDA_COMPILER_CORE_AST_VERIFIER_INVARIANTS_EVERYCHILDHASVALIDPARENT_H -#include "../invariantBase.h" +#include "ast_verifier/invariantBase.h" namespace ark::es2panda::compiler::ast_verifier { diff --git a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index e90b1ddaa86758dbbedc531ed0fbfef5e4baa382..4832d10fb7b4853f971cc7314a5d4132f76e1953 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -70,7 +70,7 @@ bool ImportExportAccessValid::ValidateExport(const varbinder::Variable *var) if (node == nullptr) { return false; } - return node->IsExported() || node->IsExportedType() || node->HasExportAlias(); + return node->IsExported() || node->HasExportAlias(); } bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_set &importedVariables, @@ -78,20 +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 || 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/ast_verifier/invariants/nodeHasType.cpp b/ets2panda/ast_verifier/invariants/nodeHasType.cpp index 4a57eda031014279d77fb35958bb4df766e93824..a269292f9f5a330eba313720803c4c29fe209b32 100644 --- a/ets2panda/ast_verifier/invariants/nodeHasType.cpp +++ b/ets2panda/ast_verifier/invariants/nodeHasType.cpp @@ -38,7 +38,8 @@ public: { return nulltypeNode_->IsIdentifier() || MatchFunctionExpression() || nulltypeNode_->IsTSClassImplements() || nulltypeNode_->IsSpreadElement() || nulltypeNode_->IsTSThisType() || nulltypeNode_->IsETSNullType() || - nulltypeNode_->IsStringLiteral() || AnyChildStringLiteral(); + nulltypeNode_->IsStringLiteral() || AnyChildStringLiteral() || nulltypeNode_->IsOverloadDeclaration() || + nulltypeNode_->IsProperty(); } auto ShouldSkipSubtree() const diff --git a/ets2panda/ast_verifier/invariants/nodeHasType.h b/ets2panda/ast_verifier/invariants/nodeHasType.h index 12e7eaf4e6b12063468326eba03f866100c717ab..c48d97ce8095479c72c9335c9ee02d918353aac1 100644 --- a/ets2panda/ast_verifier/invariants/nodeHasType.h +++ b/ets2panda/ast_verifier/invariants/nodeHasType.h @@ -49,7 +49,8 @@ public: if (type == nullptr) { return {CheckDecision::CORRECT, CheckAction::CONTINUE}; } - if (!numberLoweringOccurred_ && type->IsETSPrimitiveType()) { + // NOTE(dkofanov): Broken extension functions. + if (!numberLoweringOccurred_ && !type->IsETSExtensionFuncHelperType() && type->IsETSPrimitiveType()) { AddCheckMessage("PRIMITIVE_BEFORE_LOWERING", *ast); return {CheckDecision::INCORRECT, CheckAction::CONTINUE}; } diff --git a/ets2panda/bindings/.prettierignore b/ets2panda/bindings/.prettierignore index 2f3613c27765c7087fcd45acc3d317ba0611d214..518efe7433cc5e17dcbee89e3c68635376e48411 100644 --- a/ets2panda/bindings/.prettierignore +++ b/ets2panda/bindings/.prettierignore @@ -17,7 +17,9 @@ native/ dist/** dist-test/** node_modules/** -**.json +test/ets/** +tsconfig.json +**.json5 **.js **.md **.ets diff --git a/ets2panda/bindings/BUILD.gn b/ets2panda/bindings/BUILD.gn index 4a86840448e8c7c2d1dd5c9a1cda2371c871cd57..998e007efffad553254bb0c8b8686f9b039c458b 100644 --- a/ets2panda/bindings/BUILD.gn +++ b/ets2panda/bindings/BUILD.gn @@ -29,7 +29,6 @@ npm_path = "//prebuilts/build-tools/common/nodejs/current/bin/npm" shared_library("ts_bindings") { sources = [ - "./native/src/callback-resource.cpp", "./native/src/common-interop.cpp", "./native/src/convertors-napi.cpp", "./native/src/lsp.cpp", @@ -65,7 +64,8 @@ shared_library("ts_bindings") { if (ark_standalone_build) { deps += [ - "$ark_root/bytecode_optimizer:libarktsbytecodeopt_package", + "$ark_root/abc2program:arkts_abc2program_static", + "$ark_root/bytecode_optimizer:libarktsbytecodeopt_frontend_static", "$ark_third_party_root/bounds_checking_function:libsec_shared", ] include_dirs += [ "//third_party/node/src" ] @@ -198,7 +198,6 @@ shared_library("ts_bindings") { shared_library("public") { sources = [ "./native/src/bridges.cpp", - "./native/src/callback-resource.cpp", "./native/src/common-interop.cpp", "./native/src/common.cpp", "./native/src/convertors-napi.cpp", @@ -353,24 +352,33 @@ action("build_bindings") { ":ts_bindings", ] sources = [ - "./src/Es2pandaNativeModule.ts", - "./src/InteropNativeModule.ts", - "./src/InteropTypes.ts", - "./src/Platform.ts", - "./src/Wrapper.ts", - "./src/arrays.ts", - "./src/generated/Es2pandaEnums.ts", - "./src/generated/Es2pandaNativeModule.ts", - "./src/global.ts", - "./src/index.ts", - "./src/loadLibraries.ts", - "./src/lspNode.ts", - "./src/mainWrapper.ts", - "./src/private.ts", - "./src/strings.ts", - "./src/ts-reflection.ts", - "./src/types.ts", - "./src/utils.ts", + "src/common/Es2pandaNativeModule.ts", + "src/common/InteropNativeModule.ts", + "src/common/InteropTypes.ts", + "src/common/Platform.ts", + "src/common/Wrapper.ts", + "src/common/arkTSConfigGenerator.ts", + "src/common/arrays.ts", + "src/common/driver_helper.ts", + "src/common/global.ts", + "src/common/loadLibraries.ts", + "src/common/mainWrapper.ts", + "src/common/preDefine.ts", + "src/common/private.ts", + "src/common/strings.ts", + "src/common/ts-reflection.ts", + "src/common/types.ts", + "src/common/ui_plugins_driver.ts", + "src/common/utils.ts", + "src/generated/Es2pandaEnums.ts", + "src/generated/Es2pandaNativeModule.ts", + "src/index.ts", + "src/lsp/compile_thread_worker.ts", + "src/lsp/generateArkTSConfig.ts", + "src/lsp/generateBuildConfig.ts", + "src/lsp/index.ts", + "src/lsp/lspNode.ts", + "src/lsp/lsp_helper.ts", ] script = "build_bindings.py" diff --git a/ets2panda/bindings/native/src/bridges.cpp b/ets2panda/bindings/native/src/bridges.cpp index 1b4b6d38f6c08587c8f9affbd32854eb9a35fff4..d318f9816184aa75c2308a5e2c9b30744103363c 100644 --- a/ets2panda/bindings/native/src/bridges.cpp +++ b/ets2panda/bindings/native/src/bridges.cpp @@ -36,14 +36,23 @@ KNativePointer impl_CreateContextFromString(KNativePointer configPtr, KStringPtr } TS_INTEROP_3(CreateContextFromString, KNativePointer, KNativePointer, KStringPtr, KStringPtr) +KNativePointer impl_CreateContextFromStringWithHistory(KNativePointer configPtr, KStringPtr &sourcePtr, + KStringPtr &filenamePtr) +{ + auto config = reinterpret_cast(configPtr); + return GetPublicImpl()->CreateContextFromStringWithHistory(config, sourcePtr.data(), filenamePtr.data()); +} +TS_INTEROP_3(CreateContextFromStringWithHistory, KNativePointer, KNativePointer, KStringPtr, KStringPtr) + KInt impl_GenerateTsDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputDeclEts, KStringPtr &outputEts, - KBoolean exportAll, KStringPtr &recordFile) + KBoolean exportAll, KBoolean isolated, KStringPtr &recordFile) { auto context = reinterpret_cast(contextPtr); return static_cast(GetPublicImpl()->GenerateTsDeclarationsFromContext( - context, outputDeclEts.data(), outputEts.data(), exportAll != 0, recordFile.data())); + context, outputDeclEts.data(), outputEts.data(), exportAll != 0, isolated != 0, recordFile.data())); } -TS_INTEROP_5(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KStringPtr) +TS_INTEROP_6(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KBoolean, + KStringPtr) KNativePointer impl_CreateContextFromFile(KNativePointer configPtr, KStringPtr &filenamePtr) { diff --git a/ets2panda/bindings/native/src/common.cpp b/ets2panda/bindings/native/src/common.cpp index 78f051246e1ae3d1ab4313b6e2f5dc18a2581b35..7bc499ea450df7025d1792365cdbe0873ca271e2 100644 --- a/ets2panda/bindings/native/src/common.cpp +++ b/ets2panda/bindings/native/src/common.cpp @@ -131,4 +131,79 @@ KNativePointer impl_DestroyContext(KNativePointer contextPtr) } TS_INTEROP_1(DestroyContext, KNativePointer, KNativePointer) +void impl_MemInitialize(KStringPtr &pandaLibPath) +{ + g_pandaLibPath = GetStringView(pandaLibPath); + GetPublicImpl()->MemInitialize(); +} +TS_INTEROP_V1(MemInitialize, KStringPtr) + +void impl_MemFinalize() +{ + GetPublicImpl()->MemFinalize(); +} +TS_INTEROP_V0(MemFinalize) + +KNativePointer impl_CreateGlobalContext(KNativePointer configPtr, KStringArray externalFileListPtr, KInt fileNum) +{ + auto config = reinterpret_cast(configPtr); + + const std::size_t headerLen = 4; + if (fileNum <= 0) { + return nullptr; + } + const char **externalFileList = new const char *[fileNum]; + std::size_t position = headerLen; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(fileNum); ++i) { + strLen = UnpackUInt(externalFileListPtr + position); + position += headerLen; + externalFileList[i] = + strdup(std::string(reinterpret_cast(externalFileListPtr + position), strLen).c_str()); + position += strLen; + } + + return GetPublicImpl()->CreateGlobalContext(config, externalFileList, fileNum, true); +} +TS_INTEROP_3(CreateGlobalContext, KNativePointer, KNativePointer, KStringArray, KInt) + +void impl_DestroyGlobalContext(KNativePointer globalContextPtr) +{ + auto context = reinterpret_cast(globalContextPtr); + GetPublicImpl()->DestroyGlobalContext(context); +} +TS_INTEROP_V1(DestroyGlobalContext, KNativePointer) + +KNativePointer impl_CreateCacheContextFromString(KNativePointer configPtr, KStringPtr &sourcePtr, + KStringPtr &filenamePtr, KNativePointer globalContext, + KBoolean isExternal) +{ + auto config = reinterpret_cast(configPtr); + auto context = reinterpret_cast(globalContext); + return GetPublicImpl()->CreateCacheContextFromString(config, sourcePtr.data(), filenamePtr.data(), context, + isExternal); +} +TS_INTEROP_5(CreateCacheContextFromString, KNativePointer, KNativePointer, KStringPtr, KStringPtr, KNativePointer, + KBoolean) + +void impl_RemoveFileCache(KNativePointer globalContextPtr, KStringPtr &filenamePtr) +{ + auto context = reinterpret_cast(globalContextPtr); + return GetPublicImpl()->RemoveFileCache(context, filenamePtr.data()); +} +TS_INTEROP_V2(RemoveFileCache, KNativePointer, KStringPtr) + +void impl_AddFileCache(KNativePointer globalContextPtr, KStringPtr &filenamePtr) +{ + auto context = reinterpret_cast(globalContextPtr); + return GetPublicImpl()->AddFileCache(context, filenamePtr.data()); +} +TS_INTEROP_V2(AddFileCache, KNativePointer, KStringPtr) + +void impl_InvalidateFileCache(KNativePointer globalContextPtr, KStringPtr &filenamePtr) +{ + auto context = reinterpret_cast(globalContextPtr); + return GetPublicImpl()->InvalidateFileCache(context, filenamePtr.data()); +} +TS_INTEROP_V2(InvalidateFileCache, KNativePointer, KStringPtr) // NOLINTEND diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index 3e3c0245c2c48b8ece7fa3735736a241e066bf8d..58fef1622a8c3fb58608c99debca3585d42e1b37 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -15,15 +15,12 @@ #include "convertors-napi.h" #include "lsp/include/api.h" -#include "lsp/include/completions.h" #include "common.h" #include "panda_types.h" #include "public/es2panda_lib.h" #include "lsp/include/refactors/refactor_types.h" #include -#include #include -#include namespace { using ark::es2panda::lsp::ClassHierarchy; @@ -65,6 +62,198 @@ KNativePointer impl_getClassPropertyInfo(KNativePointer context, KInt position, } TS_INTEROP_3(getClassPropertyInfo, KNativePointer, KNativePointer, KInt, KBoolean) +KNativePointer impl_getRenameLocationFileName(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return new std::string(renameLocationRef->fileName); +} +TS_INTEROP_1(getRenameLocationFileName, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameLocationPrefixText(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return new std::string(renameLocationRef->prefixText); +} +TS_INTEROP_1(getRenameLocationPrefixText, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameLocationSuffixText(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return new std::string(renameLocationRef->suffixText); +} +TS_INTEROP_1(getRenameLocationSuffixText, KNativePointer, KNativePointer) + +KInt impl_getRenameLocationStart(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return renameLocationRef->start; +} +TS_INTEROP_1(getRenameLocationStart, KInt, KNativePointer) + +KInt impl_getRenameLocationEnd(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return renameLocationRef->end; +} +TS_INTEROP_1(getRenameLocationEnd, KInt, KNativePointer) + +KInt impl_getRenameLocationLine(KNativePointer renameLocationPtr) +{ + auto *renameLocationRef = reinterpret_cast(renameLocationPtr); + return renameLocationRef->line; +} +TS_INTEROP_1(getRenameLocationLine, KInt, KNativePointer) + +// NOLINTBEGIN +inline KUInt UnpackUInt(const KByte *bytes) +{ + return (bytes[0] | (bytes[1] << 8U) | (bytes[2U] << 16U) | (bytes[3U] << 24U)); +} +// NOLINTEND + +/* + * Parses an array of pointers from a KStringArray. + * format: + * | header(4 bytes) | strLen(4 bytes) | strData(strLen bytes) | strLen(4 bytes) | strData(strLen bytes) | ... + */ +static std::vector ParsePointerArray(KInt argc, KStringArray pointerArrayPtr) +{ + const std::size_t headerLen = 4; + auto bigintPtrs = std::vector(); + bigintPtrs.reserve(static_cast(argc)); + std::size_t offset = headerLen; + std::size_t strLen = 0; + + for (std::size_t i = 0; i < static_cast(argc); ++i) { + strLen = UnpackUInt(pointerArrayPtr + offset); + offset += headerLen; + std::string bigintStr(reinterpret_cast(pointerArrayPtr + offset), strLen); + offset += strLen; + + uintptr_t ptrValue = 0; + const size_t prefixLen = 2; + const size_t hex = 16; + const size_t decimal = 10; + if (bigintStr.substr(0, prefixLen) == "0x" || bigintStr.substr(0, prefixLen) == "0X") { + ptrValue = std::stoull(bigintStr, nullptr, hex); + } else { + ptrValue = std::stoull(bigintStr, nullptr, decimal); + } + bigintPtrs.push_back(reinterpret_cast(ptrValue)); + } + + return bigintPtrs; +} + +KNativePointer impl_findRenameLocations(KInt argc, KStringArray pointerArrayPtr, KNativePointer context, KInt position) +{ + auto pointerArray = ParsePointerArray(argc, pointerArrayPtr); + auto fileContexts = std::vector {}; + fileContexts.reserve(argc); + for (std::size_t i = 0; i < static_cast(argc); ++i) { + auto contextPtr = reinterpret_cast(pointerArray[i]); + if (contextPtr != nullptr) { + fileContexts.push_back(contextPtr); + } + } + LSPAPI const *ctx = GetImpl(); + auto result = ctx->findRenameLocations(fileContexts, reinterpret_cast(context), + static_cast(position)); + auto ptrs = std::make_unique>(); + ptrs->reserve(result.size()); + for (auto &el : result) { + ptrs->push_back(new ark::es2panda::lsp::RenameLocation(std::move(el))); + } + return ptrs.release(); +} +TS_INTEROP_4(findRenameLocations, KNativePointer, KInt, KStringArray, KNativePointer, KInt) + +KNativePointer impl_getRenameSuccessFileName(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new std::string(successInfo->GetFileToRename()); +} +TS_INTEROP_1(getRenameSuccessFileName, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameSuccessKind(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new std::string(successInfo->GetKind()); +} +TS_INTEROP_1(getRenameSuccessKind, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameSuccessDisplayName(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new std::string(successInfo->GetDisplayName()); +} +TS_INTEROP_1(getRenameSuccessDisplayName, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameSuccessFullDisplayName(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new std::string(successInfo->GetFullDisplayName()); +} +TS_INTEROP_1(getRenameSuccessFullDisplayName, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameSuccessKindModifiers(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new std::string(successInfo->GetKindModifiers()); +} +TS_INTEROP_1(getRenameSuccessKindModifiers, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameSuccessTriggerSpan(KNativePointer successPtr) +{ + auto successInfo = reinterpret_cast(successPtr); + return new TextSpan(successInfo->GetTriggerSpan()); +} +TS_INTEROP_1(getRenameSuccessTriggerSpan, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameFailureLocalizedErrorMessage(KNativePointer failurePtr) +{ + auto failureInfo = reinterpret_cast(failurePtr); + return new std::string(failureInfo->GetLocalizedErrorMessage()); +} +TS_INTEROP_1(getRenameFailureLocalizedErrorMessage, KNativePointer, KNativePointer) + +KBoolean impl_getRenameInfoIsSuccess(KNativePointer renameInfoPtr) +{ + auto renameInfo = reinterpret_cast *>(renameInfoPtr); + return std::get<0>(*renameInfo) ? 1 : 0; +} +TS_INTEROP_1(getRenameInfoIsSuccess, KBoolean, KNativePointer) + +KNativePointer impl_getRenameInfoSuccess(KNativePointer renameInfoPtr) +{ + auto renameInfo = reinterpret_cast *>(renameInfoPtr); + auto [flag, successInfo] = *renameInfo; + return flag ? successInfo : nullptr; +} +TS_INTEROP_1(getRenameInfoSuccess, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameInfoFailure(KNativePointer renameInfoPtr) +{ + auto renameInfo = reinterpret_cast *>(renameInfoPtr); + auto [flag, failureInfo] = *renameInfo; + return flag ? nullptr : failureInfo; +} +TS_INTEROP_1(getRenameInfoFailure, KNativePointer, KNativePointer) + +KNativePointer impl_getRenameInfo(KNativePointer context, KInt position, KStringPtr &pandaLibPath) +{ + LSPAPI const *ctx = GetImpl(); + auto result = ctx->getRenameInfo(reinterpret_cast(context), static_cast(position), + GetStringCopy(pandaLibPath)); + if (std::holds_alternative(result)) { + auto &successInfo = std::get(result); + return new std::tuple(true, new ark::es2panda::lsp::RenameInfoSuccess(std::move(successInfo))); + } + auto &failureInfo = std::get(result); + return new std::tuple(false, new ark::es2panda::lsp::RenameInfoFailure(std::move(failureInfo))); +} +TS_INTEROP_3(getRenameInfo, KNativePointer, KNativePointer, KInt, KStringPtr) + KNativePointer impl_getFieldsInfoFromPropertyInfo(KNativePointer infoPtr) { auto info = reinterpret_cast *>(infoPtr); @@ -119,19 +308,19 @@ KNativePointer impl_getDisplayNameFromPropertyInfo(KNativePointer infoPtr) } TS_INTEROP_1(getDisplayNameFromPropertyInfo, KNativePointer, KNativePointer) -KNativePointer impl_getStartFromPropertyInfo(KNativePointer infoPtr) +KInt impl_getStartFromPropertyInfo(KNativePointer infoPtr) { auto info = reinterpret_cast(infoPtr); - return new std::size_t(info->start); + return info->start; } -TS_INTEROP_1(getStartFromPropertyInfo, KNativePointer, KNativePointer) +TS_INTEROP_1(getStartFromPropertyInfo, KInt, KNativePointer) -KNativePointer impl_getEndFromPropertyInfo(KNativePointer infoPtr) +KInt impl_getEndFromPropertyInfo(KNativePointer infoPtr) { auto info = reinterpret_cast(infoPtr); - return new std::size_t(info->end); + return info->end; } -TS_INTEROP_1(getEndFromPropertyInfo, KNativePointer, KNativePointer) +TS_INTEROP_1(getEndFromPropertyInfo, KInt, KNativePointer) KNativePointer impl_getSyntacticDiagnostics(KNativePointer context) { @@ -256,7 +445,7 @@ TS_INTEROP_1(getDiagRelatedInfo, KNativePointer, KNativePointer) KNativePointer impl_getRelatedInfoMsg(KNativePointer relatedInfoPtr) { auto *relatedInfoRef = reinterpret_cast(relatedInfoPtr); - return &relatedInfoRef->message_; + return new std::string(relatedInfoRef->message_); } TS_INTEROP_1(getRelatedInfoMsg, KNativePointer, KNativePointer) @@ -270,7 +459,7 @@ TS_INTEROP_1(getRelatedInfoLoc, KNativePointer, KNativePointer) KNativePointer impl_getLocUri(KNativePointer locPtr) { auto *locRef = reinterpret_cast(locPtr); - return &locRef->uri_; + return new std::string(locRef->uri_); } TS_INTEROP_1(getLocUri, KNativePointer, KNativePointer) @@ -800,14 +989,28 @@ KInt impl_getAliasScriptElementKind(KNativePointer context, KInt position) } TS_INTEROP_2(getAliasScriptElementKind, KInt, KNativePointer, KInt) +KNativePointer impl_pushBackToNativeContextVector(KNativePointer context, KNativePointer contextList, KBoolean isNew) +{ + auto contextPtr = reinterpret_cast(context); + if (isNew != 0) { + auto *newVector = new std::vector(); + newVector->push_back(contextPtr); + return newVector; + } + auto contextVector = reinterpret_cast *>(contextList); + contextVector->push_back(contextPtr); + return contextVector; +} +TS_INTEROP_3(pushBackToNativeContextVector, KNativePointer, KNativePointer, KNativePointer, KBoolean) + KNativePointer impl_getClassHierarchies(KNativePointer context, KStringPtr &fileNamePtr, KInt pos) { LSPAPI const *ctx = GetImpl(); if (ctx == nullptr) { return nullptr; } - auto infos = - ctx->getClassHierarchiesImpl(reinterpret_cast(context), GetStringCopy(fileNamePtr), pos); + auto *contextlist = reinterpret_cast *>(context); + auto infos = ctx->getClassHierarchiesImpl(contextlist, GetStringCopy(fileNamePtr), pos); std::vector ptrs; ptrs.reserve(infos.size()); for (auto &info : infos) { @@ -881,10 +1084,10 @@ TS_INTEROP_1(getApplicableRefactorAction, KNativePointer, KNativePointer) KNativePointer impl_getClassHierarchyList(KNativePointer infosPtr) { - auto *infos = reinterpret_cast *>(infosPtr); + auto *infos = reinterpret_cast *>(infosPtr); std::vector infoPtrList; for (auto &info : *infos) { - infoPtrList.push_back(new ark::es2panda::lsp::ClassHierarchyItemInfo(info)); + infoPtrList.push_back(info); } return new std::vector(infoPtrList); } @@ -918,9 +1121,8 @@ KNativePointer impl_getOverriddenFromClassHierarchyItemInfo(KNativePointer infoP auto &overridden = info->overridden; std::vector overriddenPtrList; overriddenPtrList.reserve(overridden.size()); - size_t idx = 0; for (auto &details : overridden) { - overriddenPtrList[idx++] = new ark::es2panda::lsp::ClassRelationDetails(details); + overriddenPtrList.push_back(new ark::es2panda::lsp::ClassRelationDetails(details)); } return new std::vector(std::move(overriddenPtrList)); } @@ -932,9 +1134,8 @@ KNativePointer impl_getOverridingFromClassHierarchyItemInfo(KNativePointer infoP auto &overriding = info->overriding; std::vector overridingPtrList; overridingPtrList.reserve(overriding.size()); - size_t idx = 0; for (auto &details : overriding) { - overridingPtrList[idx++] = new ark::es2panda::lsp::ClassRelationDetails(details); + overridingPtrList.push_back(new ark::es2panda::lsp::ClassRelationDetails(details)); } return new std::vector(std::move(overridingPtrList)); } @@ -946,9 +1147,8 @@ KNativePointer impl_getImplementedFromClassHierarchyItemInfo(KNativePointer info auto implemented = info->implemented; std::vector implementedPtrList; implementedPtrList.reserve(implemented.size()); - size_t idx = 0; for (auto &details : implemented) { - implementedPtrList[idx++] = new ark::es2panda::lsp::ClassRelationDetails(details); + implementedPtrList.push_back(new ark::es2panda::lsp::ClassRelationDetails(details)); } return new std::vector(std::move(implementedPtrList)); } @@ -960,9 +1160,8 @@ KNativePointer impl_getImplementingFromClassHierarchyItemInfo(KNativePointer inf auto implementing = info->implementing; std::vector implementingPtrList; implementingPtrList.reserve(implementing.size()); - size_t idx = 0; for (auto &details : implementing) { - implementingPtrList[idx++] = new ark::es2panda::lsp::ClassRelationDetails(details); + implementingPtrList.push_back(new ark::es2panda::lsp::ClassRelationDetails(details)); } return new std::vector(std::move(implementingPtrList)); } @@ -1418,7 +1617,7 @@ TS_INTEROP_3(getSpanOfEnclosingComment, KNativePointer, KNativePointer, KInt, KB KNativePointer impl_getInlayHintText(KNativePointer hintPtr) { auto *hint = reinterpret_cast(hintPtr); - return &hint->text; + return new std::string(hint->text); } TS_INTEROP_1(getInlayHintText, KNativePointer, KNativePointer) @@ -1473,7 +1672,7 @@ TS_INTEROP_2(getInlayHintList, KNativePointer, KNativePointer, KNativePointer) KNativePointer impl_getSignatureHelpParameterName(KNativePointer parameterPtr) { auto *parameterRef = reinterpret_cast(parameterPtr); - return ¶meterRef->GetName(); + return new std::string(parameterRef->GetName()); } TS_INTEROP_1(getSignatureHelpParameterName, KNativePointer, KNativePointer) @@ -1600,4 +1799,12 @@ KNativePointer impl_getSignatureHelpItems(KNativePointer context, KInt position) new SignatureHelpItems(ctx->getSignatureHelpItems(reinterpret_cast(context), position)); return textSpan; } -TS_INTEROP_2(getSignatureHelpItems, KNativePointer, KNativePointer, KInt) \ No newline at end of file +TS_INTEROP_2(getSignatureHelpItems, KNativePointer, KNativePointer, KInt) + +KInt impl_getOffsetByColAndLine(KNativePointer contextPtr, KInt line, KInt column) +{ + auto context = reinterpret_cast(contextPtr); + LSPAPI const *impl = GetImpl(); + return impl->getOffsetByColAndLine(context, line, column); +} +TS_INTEROP_3(getOffsetByColAndLine, KInt, KNativePointer, KInt, KInt) \ No newline at end of file diff --git a/ets2panda/bindings/package.json b/ets2panda/bindings/package.json index 92d680af8d08a135101997e4752810d95b16fca5..586b823c5bad393cd9b8569a72fce9f948a3561c 100644 --- a/ets2panda/bindings/package.json +++ b/ets2panda/bindings/package.json @@ -8,7 +8,7 @@ "prettier": "latest", "rimraf": "^6.0.1", "typescript": "4.9.5", - "node-api-headers": "^1.4.0" + "jest-diff": "latest" }, "main": "dist/index.js", "scripts": { @@ -16,6 +16,7 @@ "test:compile": "tsc --build --verbose test/tsconfig.json", "test:build": "npm run test:clean && npm run test:compile", "test": "npm run test:build && node dist-test/test/run_tests.js ./test", + "test:update": "npm run test:build && node dist-test/test/run_tests.js ./test --update", "compile": "tsc --build --verbose tsconfig.json", "clean": "rimraf dist tsconfig.tsbuildinfo package-lock.json", "run": "npm run clean && npm run compile", diff --git a/ets2panda/bindings/src/arktsConfigGenerate.ts b/ets2panda/bindings/src/arktsConfigGenerate.ts deleted file mode 100644 index 56f00052edfbf3e3b8fa7090facefb977730077b..0000000000000000000000000000000000000000 --- a/ets2panda/bindings/src/arktsConfigGenerate.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ - -import { BuildMode } from './build/buildMode'; -import { BuildConfig } from './types'; -import { ModuleDescriptor, generateBuildConfigs } from './buildConfigGenerate'; -import { PANDA_SDK_PATH_FROM_SDK } from './preDefine'; - -import * as fs from 'fs'; -import * as path from 'path'; -import { PluginDriver } from './ui_plugins_driver'; - -function processBuildConfig(projectConfig: BuildConfig): BuildConfig { - let buildConfig: BuildConfig = { ...projectConfig }; - let buildSdkPath: string = buildConfig.buildSdkPath as string; - buildConfig.pandaSdkPath = buildConfig.pandaSdkPath ?? path.resolve(buildSdkPath, PANDA_SDK_PATH_FROM_SDK); - PluginDriver.getInstance().initPlugins(buildConfig); - return buildConfig; -} - -export function generateArkTsConfigByModules( - buildSdkPath: string, - projectRoot: string, - modules?: ModuleDescriptor[] -): void { - const allBuildConfig = generateBuildConfigs(buildSdkPath, projectRoot, modules); - let compileFileInfos: Record = {}; - const cacheDir = path.join(projectRoot, '.idea', '.deveco'); - const compileFileInfosPath = path.join(cacheDir, 'lsp_compileFileInfos.json'); - Object.keys(allBuildConfig).forEach((moduleName) => { - const moduleConfig = allBuildConfig[moduleName] as BuildConfig; - const processedConfig = processBuildConfig(moduleConfig); - - const buildMode = new BuildMode(processedConfig); - buildMode.generateArkTSConfig(compileFileInfos); - }); - try { - const jsonCompileFileInfos = JSON.stringify(compileFileInfos, null, 2); - if (!fs.existsSync(cacheDir)) { - fs.mkdirSync(cacheDir, { recursive: true }); - } - fs.writeFileSync(compileFileInfosPath, jsonCompileFileInfos, 'utf-8'); - } catch (err) { - console.error(`Failed to write compileFileInfos to ${compileFileInfosPath} with error: ${err}`); - } -} diff --git a/ets2panda/bindings/src/build/buildMode.ts b/ets2panda/bindings/src/build/buildMode.ts deleted file mode 100644 index d1be834b99fda47c40eb4787f8670bfa76be6268..0000000000000000000000000000000000000000 --- a/ets2panda/bindings/src/build/buildMode.ts +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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. - */ - -import * as path from 'path'; - -import { ABC_SUFFIX, ARKTSCONFIG_JSON_FILE, LANGUAGE_VERSION } from '../preDefine'; -import { changeFileExtension } from '../utils'; -import { BuildConfig, DependentModuleConfig, ModuleInfo, CompileFileInfo } from '../types'; -import { ArkTSConfigGenerator } from './generateArkTSConfig'; - -export class BuildMode { - buildConfig: BuildConfig; - entryFiles: Set; - compileFiles: Map; - cacheDir: string; - pandaSdkPath: string; - buildSdkPath: string; - packageName: string; - sourceRoots: string[]; - moduleRootPath: string; - moduleType: string; - dependentModuleList: DependentModuleConfig[]; - moduleInfos: Map; - declgenV1OutPath: string | undefined; - declgenBridgeCodePath: string | undefined; - hasMainModule: boolean; - - constructor(buildConfig: BuildConfig) { - this.buildConfig = buildConfig; - this.entryFiles = new Set(buildConfig.compileFiles as string[]); - this.cacheDir = buildConfig.cachePath as string; - this.pandaSdkPath = buildConfig.pandaSdkPath as string; - this.buildSdkPath = buildConfig.buildSdkPath as string; - this.packageName = buildConfig.packageName as string; - this.sourceRoots = buildConfig.sourceRoots as string[]; - this.moduleRootPath = buildConfig.moduleRootPath as string; - this.moduleType = buildConfig.moduleType as string; - this.dependentModuleList = buildConfig.dependentModuleList; - this.hasMainModule = buildConfig.hasMainModule; - - this.declgenV1OutPath = buildConfig.declgenV1OutPath as string | undefined; - this.declgenBridgeCodePath = buildConfig.declgenBridgeCodePath as string | undefined; - - this.moduleInfos = new Map(); - this.compileFiles = new Map(); - } - - private getDependentModules(moduleInfo: ModuleInfo): Map[] { - let dynamicDepModules: Map = new Map(); - let staticDepModules: Map = new Map(); - - if (moduleInfo.isMainModule) { - this.moduleInfos.forEach((module: ModuleInfo, packageName: string) => { - if (module.isMainModule) { - return; - } - module.language === LANGUAGE_VERSION.ARKTS_1_2 - ? staticDepModules.set(packageName, module) - : dynamicDepModules.set(packageName, module); - }); - return [dynamicDepModules, staticDepModules]; - } - - if (moduleInfo.dependencies) { - moduleInfo.dependencies.forEach((packageName: string) => { - let depModuleInfo: ModuleInfo | undefined = this.moduleInfos.get(packageName); - if (!depModuleInfo) { - console.error(`Module ${packageName} not found in moduleInfos`); - } else { - depModuleInfo.language === LANGUAGE_VERSION.ARKTS_1_2 - ? staticDepModules.set(packageName, depModuleInfo) - : dynamicDepModules.set(packageName, depModuleInfo); - } - }); - } - return [dynamicDepModules, staticDepModules]; - } - - private generateArkTSConfigForModules(compileFileInfos: Record): void { - let generator = ArkTSConfigGenerator.getGenerator(this.buildConfig, this.moduleInfos); - this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleRootPath: string) => { - for (const fileInfo of moduleInfo.compileFileInfos) { - compileFileInfos[fileInfo.filePath] = fileInfo.arktsConfigFile; - } - generator.writeArkTSConfigFile(moduleInfo); - }); - } - - private collectDepModuleInfos(): void { - this.moduleInfos.forEach((moduleInfo) => { - let [dynamicDepModules, staticDepModules] = this.getDependentModules(moduleInfo); - moduleInfo.dynamicDepModuleInfos = dynamicDepModules; - moduleInfo.staticDepModuleInfos = staticDepModules; - }); - } - - private collectModuleInfos(): void { - if (this.hasMainModule && (!this.packageName || !this.moduleRootPath || !this.sourceRoots)) { - console.error('Main module info from hvigor is not correct.'); - } - let mainModuleInfo: ModuleInfo = { - isMainModule: this.hasMainModule, - packageName: this.packageName, - moduleRootPath: this.moduleRootPath, - moduleType: this.moduleType, - sourceRoots: this.sourceRoots, - entryFile: '', - arktsConfigFile: path.resolve(this.cacheDir, this.packageName, ARKTSCONFIG_JSON_FILE), - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - compileFileInfos: [], - declgenV1OutPath: this.declgenV1OutPath, - declgenBridgeCodePath: this.declgenBridgeCodePath - }; - this.moduleInfos.set(this.packageName, mainModuleInfo); - this.dependentModuleList.forEach((module: DependentModuleConfig) => { - if (!module.packageName || !module.modulePath || !module.sourceRoots || !module.entryFile) { - console.error('Dependent module info from hvigor is not correct.'); - } - let moduleInfo: ModuleInfo = { - isMainModule: false, - packageName: module.packageName, - moduleRootPath: module.modulePath, - moduleType: module.moduleType, - sourceRoots: module.sourceRoots, - entryFile: module.entryFile, - arktsConfigFile: path.resolve(this.cacheDir, module.packageName, ARKTSCONFIG_JSON_FILE), - compileFileInfos: [], - dynamicDepModuleInfos: new Map(), - staticDepModuleInfos: new Map(), - declgenV1OutPath: undefined, - declgenBridgeCodePath: undefined, - language: module.language, - declFilesPath: module.declFilesPath, - dependencies: module.dependencies - }; - this.moduleInfos.set(module.packageName, moduleInfo); - }); - this.collectDepModuleInfos(); - } - - private collectCompileFiles(): void { - this.entryFiles.forEach((file: string) => { - for (const [packageName, moduleInfo] of this.moduleInfos) { - if (!file.startsWith(moduleInfo.moduleRootPath)) { - continue; - } - let filePathFromModuleRoot: string = path.relative(moduleInfo.moduleRootPath, file); - let filePathInCache: string = path.join(this.cacheDir, moduleInfo.packageName, filePathFromModuleRoot); - let abcFilePath: string = path.resolve(changeFileExtension(filePathInCache, ABC_SUFFIX)); - - let fileInfo: CompileFileInfo = { - filePath: file, - dependentFiles: [], - abcFilePath: abcFilePath, - arktsConfigFile: moduleInfo.arktsConfigFile, - packageName: moduleInfo.packageName - }; - moduleInfo.compileFileInfos.push(fileInfo); - this.compileFiles.set(file, fileInfo); - return; - } - console.error('File does not belong to any module in moduleInfos.'); - }); - } - - public generateModuleInfos(): void { - this.collectModuleInfos(); - this.collectCompileFiles(); - } - - public async generateArkTSConfig(compileFileInfos: Record): Promise { - this.generateModuleInfos(); - this.generateArkTSConfigForModules(compileFileInfos); - } -} diff --git a/ets2panda/bindings/src/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts similarity index 86% rename from ets2panda/bindings/src/Es2pandaNativeModule.ts rename to ets2panda/bindings/src/common/Es2pandaNativeModule.ts index 2c20b7ffd14ec86aa9efa6d0b1309bec96f559ec..8d04990823d8281b5a571c4713ccc417224e91ff 100644 --- a/ets2panda/bindings/src/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -14,8 +14,17 @@ import * as fs from 'fs'; import * as path from 'path'; -import { KNativePointer as KPtr, KInt, KBoolean, KNativePointer, KDouble, KUInt, KStringPtr, KInt32ArrayPtr } from './InteropTypes'; -import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from './generated/Es2pandaNativeModule'; +import { + KNativePointer as KPtr, + KInt, + KBoolean, + KNativePointer, + KDouble, + KUInt, + KStringPtr, + KInt32ArrayPtr +} from './InteropTypes'; +import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from '../generated/Es2pandaNativeModule'; import { loadNativeModuleLibrary, registerNativeModuleLibraryName } from './loadLibraries'; import { throwError } from './utils'; @@ -65,11 +74,15 @@ export class Es2pandaNativeModule { _CreateContextFromString(config: KPtr, source: String, filename: String): KPtr { throw new Error('Not implemented'); } + _CreateContextFromStringWithHistory(config: KPtr, source: String, filename: String): KPtr { + throw new Error('Not implemented'); + } _GenerateTsDeclarationsFromContext( config: KPtr, outputDeclEts: String, outputEts: String, exportAll: KBoolean, + isolated: KBoolean, recordFile: String ): KPtr { throw new Error('Not implemented'); @@ -398,6 +411,10 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _pushBackToNativeContextVector(context: KNativePointer, contextList: KNativePointer, isNew: KBoolean): KPtr { + throw new Error('Not implemented'); + } + _getClassHierarchyList(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } @@ -733,8 +750,13 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getCodeFixesAtPosition(context: KNativePointer, startPosition: KInt, endPosition: KInt, - errorCodesPtr: KInt32ArrayPtr, codeLength: KInt): KPtr { + _getCodeFixesAtPosition( + context: KNativePointer, + startPosition: KInt, + endPosition: KInt, + errorCodesPtr: KInt32ArrayPtr, + codeLength: KInt + ): KPtr { throw new Error('Not implemented'); } @@ -790,6 +812,78 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _getRenameLocationFileName(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameLocationStart(ptr: KPtr): KInt { + throw new Error('Not implemented'); + } + + _getRenameLocationEnd(ptr: KPtr): KInt { + throw new Error('Not implemented'); + } + + _getRenameLocationLine(ptr: KPtr): KInt { + throw new Error('Not implemented'); + } + + _getRenameLocationPrefixText(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameLocationSuffixText(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _findRenameLocations(argc: KInt, fileContexts: Uint8Array, context: KNativePointer, position: KInt): KPtr { + throw new Error('Not implemented'); + } + + _getRenameSuccessFileName(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameSuccessKind(ptr: KPtr): KInt { + throw new Error('Not implemented'); + } + + _getRenameSuccessDisplayName(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameSuccessFullDisplayName(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameSuccessKindModifiers(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameSuccessTriggerSpan(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameFailureLocalizedErrorMessage(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameInfoIsSuccess(ptr: KPtr): KBoolean { + throw new Error('Not implemented'); + } + + _getRenameInfoSuccess(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameInfoFailure(ptr: KPtr): KPtr { + throw new Error('Not implemented'); + } + + _getRenameInfo(context: KNativePointer, position: KInt, pandaLibPath: String): KPtr { + throw new Error('Not implemented'); + } + _createTextSpan(start: KInt, length: KInt): KPtr { throw new Error('Not implemented'); } @@ -850,12 +944,53 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _getOffsetByColAndLine(context: KPtr, line: KInt, column: KInt): KInt { + throw new Error('Not implemented'); + } + + _MemInitialize(pandaLibPath: KStringPtr): void { + throw new Error('Not implemented'); + } + + _MemFinalize(): void { + throw new Error('Not implemented'); + } + + _CreateGlobalContext(configPtr: KNativePointer, externalFileList: string[] | Uint8Array, fileNum: KInt): KPtr { + throw new Error('Not implemented'); + } + + _DestroyGlobalContext(contextPtr: KPtr): void { + throw new Error('Not implemented'); + } + + _CreateCacheContextFromString( + config: KPtr, + source: String, + filename: String, + globalContext: KPtr, + isExternal: boolean + ): KPtr { + throw new Error('Not implemented'); + } + + _RemoveFileCache(globalContextPtr: KPtr, filename: String): void { + throw new Error('Not implemented'); + } + + _AddFileCache(globalContextPtr: KPtr, filename: String): void { + throw new Error('Not implemented'); + } + + _InvalidateFileCache(globalContextPtr: KPtr, filename: String): void { + throw new Error('Not implemented'); + } } export function initEs2panda(): Es2pandaNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../ts_bindings.node'); + libPath = path.resolve(__dirname, '../../ts_bindings.node'); } else { libPath = path.join(libPath, 'ts_bindings.node'); } @@ -871,7 +1006,7 @@ export function initEs2panda(): Es2pandaNativeModule { export function initGeneratedEs2panda(): GeneratedEs2pandaNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../ts_bindings.node'); + libPath = path.resolve(__dirname, '../../ts_bindings.node'); } else { libPath = path.join(libPath, 'ts_bindings.node'); } @@ -887,7 +1022,7 @@ export function initGeneratedEs2panda(): GeneratedEs2pandaNativeModule { export function initPublicEs2panda(): Es2pandaNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../public.node'); + libPath = path.resolve(__dirname, '../../public.node'); } else { libPath = path.join(libPath, 'public.node'); } @@ -903,7 +1038,7 @@ export function initPublicEs2panda(): Es2pandaNativeModule { export function initPublicGeneratedEs2panda(): GeneratedEs2pandaNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../public.node'); + libPath = path.resolve(__dirname, '../../public.node'); } else { libPath = path.join(libPath, 'public.node'); } diff --git a/ets2panda/bindings/src/InteropNativeModule.ts b/ets2panda/bindings/src/common/InteropNativeModule.ts similarity index 95% rename from ets2panda/bindings/src/InteropNativeModule.ts rename to ets2panda/bindings/src/common/InteropNativeModule.ts index 35eac9b80641b233c9e1c5e4add9fb2f7d995224..fcc82a57ebd647d95e660ab6b75c63fa29074b72 100644 --- a/ets2panda/bindings/src/InteropNativeModule.ts +++ b/ets2panda/bindings/src/common/InteropNativeModule.ts @@ -60,7 +60,7 @@ export class InteropNativeModule { export function initInterop(): InteropNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../ts_bindings.node'); + libPath = path.resolve(__dirname, '../../ts_bindings.node'); } else { libPath = path.join(libPath, 'ts_bindings.node'); } @@ -76,7 +76,7 @@ export function initInterop(): InteropNativeModule { export function initPublicInterop(): InteropNativeModule { let libPath = process.env.BINDINGS_PATH; if (libPath === undefined) { - libPath = path.resolve(__dirname, '../public.node'); + libPath = path.resolve(__dirname, '../../public.node'); } else { libPath = path.join(libPath, 'public.node'); } diff --git a/ets2panda/bindings/src/InteropTypes.ts b/ets2panda/bindings/src/common/InteropTypes.ts similarity index 100% rename from ets2panda/bindings/src/InteropTypes.ts rename to ets2panda/bindings/src/common/InteropTypes.ts diff --git a/ets2panda/bindings/src/Platform.ts b/ets2panda/bindings/src/common/Platform.ts similarity index 100% rename from ets2panda/bindings/src/Platform.ts rename to ets2panda/bindings/src/common/Platform.ts diff --git a/ets2panda/bindings/src/Wrapper.ts b/ets2panda/bindings/src/common/Wrapper.ts similarity index 100% rename from ets2panda/bindings/src/Wrapper.ts rename to ets2panda/bindings/src/common/Wrapper.ts diff --git a/ets2panda/bindings/src/build/generateArkTSConfig.ts b/ets2panda/bindings/src/common/arkTSConfigGenerator.ts similarity index 62% rename from ets2panda/bindings/src/build/generateArkTSConfig.ts rename to ets2panda/bindings/src/common/arkTSConfigGenerator.ts index fe1dc108fc57d5604614ef0e4498e76d8f1b21c5..5b2a1ee5f9b999b9ebe4a6ba658caaf301ddb078 100644 --- a/ets2panda/bindings/src/build/generateArkTSConfig.ts +++ b/ets2panda/bindings/src/common/arkTSConfigGenerator.ts @@ -16,14 +16,14 @@ import * as path from 'path'; import * as fs from 'fs'; -import { changeFileExtension, ensurePathExists } from '../utils'; -import { BuildConfig, ModuleInfo } from '../types'; -import { LANGUAGE_VERSION, SYSTEM_SDK_PATH_FROM_SDK } from '../preDefine'; +import { changeFileExtension, ensurePathExists } from './utils'; +import { BuildConfig, ModuleInfo } from './types'; +import { LANGUAGE_VERSION, PANDA_SDK_PATH_FROM_SDK, SYSTEM_SDK_PATH_FROM_SDK } from './preDefine'; -interface DynamicPathItem { +interface DependencyItem { language: string; - declPath: string; - runtimeName: string; + path: string; + ohmUrl: string; } interface ArkTSConfigObject { @@ -31,9 +31,8 @@ interface ArkTSConfigObject { package: string; baseUrl: string; paths: Record; - dependencies: string[] | undefined; entry: string; - dynamicPaths: Record; + dependencies: Record; }; } @@ -42,22 +41,24 @@ export class ArkTSConfigGenerator { private stdlibStdPath: string; private stdlibEscompatPath: string; private systemSdkPath: string; + private externalApiPath: string; - private moduleInfos: Map; + private moduleInfos: Record; private pathSection: Record; - private constructor(buildConfig: BuildConfig, moduleInfos: Map) { - let pandaStdlibPath: string = - buildConfig.pandaStdlibPath ?? path.resolve(buildConfig.pandaSdkPath!!, 'lib', 'stdlib'); + private constructor(buildConfig: BuildConfig, moduleInfos: Record) { + let pandaSdkPath = path.resolve(buildConfig.buildSdkPath, PANDA_SDK_PATH_FROM_SDK); + let pandaStdlibPath: string = path.resolve(pandaSdkPath, 'lib', 'stdlib'); this.stdlibStdPath = path.resolve(pandaStdlibPath, 'std'); this.stdlibEscompatPath = path.resolve(pandaStdlibPath, 'escompat'); this.systemSdkPath = path.resolve(buildConfig.buildSdkPath, SYSTEM_SDK_PATH_FROM_SDK); + this.externalApiPath = buildConfig.externalApiPath !== undefined ? buildConfig.externalApiPath : ''; this.moduleInfos = moduleInfos; this.pathSection = {}; } - public static getInstance(buildConfig?: BuildConfig, moduleInfos?: Map): ArkTSConfigGenerator { + public static getInstance(buildConfig?: BuildConfig, moduleInfos?: Record): ArkTSConfigGenerator { if (!ArkTSConfigGenerator.instance) { if (!buildConfig || !moduleInfos) { throw new Error('buildConfig and moduleInfos is required for the first instantiation of ArkTSConfigGenerator.'); @@ -67,7 +68,7 @@ export class ArkTSConfigGenerator { return ArkTSConfigGenerator.instance; } - public static getGenerator(buildConfig: BuildConfig, moduleInfos: Map): ArkTSConfigGenerator { + public static getGenerator(buildConfig: BuildConfig, moduleInfos: Record): ArkTSConfigGenerator { return new ArkTSConfigGenerator(buildConfig, moduleInfos); } @@ -76,11 +77,20 @@ export class ArkTSConfigGenerator { } private generateSystemSdkPathSection(pathSection: Record): void { - function traverse(currentDir: string, relativePath: string = '', isExcludedDir: boolean = false): void { + function traverse( + currentDir: string, + relativePath: string = '', + isExcludedDir: boolean = false, + allowedExtensions: string[] = ['.d.ets'] + ): void { const items = fs.readdirSync(currentDir); for (const item of items) { const itemPath = path.join(currentDir, item); const stat = fs.statSync(itemPath); + const isAllowedFile = allowedExtensions.some((ext) => item.endsWith(ext)); + if (stat.isFile() && !isAllowedFile) { + continue; + } if (stat.isFile()) { const basename = path.basename(item, '.d.ets'); @@ -88,9 +98,9 @@ export class ArkTSConfigGenerator { pathSection[key] = [changeFileExtension(itemPath, '', '.d.ets')]; } if (stat.isDirectory()) { - // For non-arkui files under api dir, + // For files under api dir excluding arkui/runtime-api dir, // fill path section with `"pathFromApi.subdir.fileName" = [${absolute_path_to_file}]`; - // For arkui files under api dir, + // For @koalaui files under arkui/runtime-api dir, // fill path section with `"fileName" = [${absolute_path_to_file}]`. const isCurrentDirExcluded = path.basename(currentDir) === 'arkui' && item === 'runtime-api'; const newRelativePath = isCurrentDirExcluded ? '' : relativePath ? `${relativePath}.${item}` : item; @@ -99,17 +109,18 @@ export class ArkTSConfigGenerator { } } - let apiPath: string = path.resolve(this.systemSdkPath, 'api'); - fs.existsSync(apiPath) ? traverse(apiPath) : console.error(`sdk path ${apiPath} not exist.`); - - let arktsPath: string = path.resolve(this.systemSdkPath, 'arkts'); - fs.existsSync(arktsPath) ? traverse(arktsPath) : console.error(`sdk path ${arktsPath} not exist.`); - - let kitsPath: string = path.resolve(this.systemSdkPath, 'kits'); - fs.existsSync(kitsPath) ? traverse(kitsPath) : console.error(`sdk path ${kitsPath} not exist.`); + let directoryNames: string[] = ['api', 'arkts', 'kits']; + directoryNames.forEach((dir) => { + let systemSdkPath = path.resolve(this.systemSdkPath, dir); + let externalApiPath = path.resolve(this.externalApiPath, dir); + fs.existsSync(systemSdkPath) ? traverse(systemSdkPath) : console.warn(`sdk path ${systemSdkPath} not exist.`); + fs.existsSync(externalApiPath) + ? traverse(externalApiPath) + : console.warn(`sdk path ${externalApiPath} not exist.`); + }); } - private getPathSection(): Record { + private getPathSection(moduleInfo: ModuleInfo): Record { if (Object.keys(this.pathSection).length !== 0) { return this.pathSection; } @@ -119,32 +130,24 @@ export class ArkTSConfigGenerator { this.generateSystemSdkPathSection(this.pathSection); - this.moduleInfos.forEach((moduleInfo: ModuleInfo, packageName: string) => { - if (moduleInfo.language === LANGUAGE_VERSION.ARKTS_1_2) { - this.pathSection[moduleInfo.packageName] = [path.resolve(moduleInfo.moduleRootPath, moduleInfo.sourceRoots[0])]; - } + Object.values(moduleInfo.staticDepModuleInfos).forEach((depModuleName: string) => { + let depModuleInfo = this.moduleInfos[depModuleName]; + this.pathSection[depModuleInfo.packageName] = [path.resolve(depModuleInfo.moduleRootPath)]; }); return this.pathSection; } - private getDependenciesSection(moduleInfo: ModuleInfo, dependenciesSection: string[]): void { - let depModules: Map = moduleInfo.staticDepModuleInfos; - depModules.forEach((depModuleInfo: ModuleInfo) => { - dependenciesSection.push(depModuleInfo.arktsConfigFile); - }); - } - private getOhmurl(file: string, moduleInfo: ModuleInfo): string { let unixFilePath: string = file.replace(/\\/g, '/'); let ohmurl: string = moduleInfo.packageName + '/' + unixFilePath; return changeFileExtension(ohmurl, ''); } - private getDynamicPathSection(moduleInfo: ModuleInfo, dynamicPathSection: Record): void { - let depModules: Map = moduleInfo.dynamicDepModuleInfos; - - depModules.forEach((depModuleInfo: ModuleInfo) => { + private getDependenciesSection(moduleInfo: ModuleInfo, dependencySection: Record): void { + let depModules: string[] = moduleInfo.dynamicDepModuleInfos; + depModules.forEach((depModuleName: string) => { + let depModuleInfo = this.moduleInfos[depModuleName]; if (!depModuleInfo.declFilesPath || !fs.existsSync(depModuleInfo.declFilesPath)) { console.error(`Module ${moduleInfo.packageName} depends on dynamic module ${depModuleInfo.packageName}, but decl file not found on path ${depModuleInfo.declFilesPath}`); @@ -153,39 +156,34 @@ export class ArkTSConfigGenerator { let declFilesObject = JSON.parse(fs.readFileSync(depModuleInfo.declFilesPath, 'utf-8')); Object.keys(declFilesObject.files).forEach((file: string) => { let ohmurl: string = this.getOhmurl(file, depModuleInfo); - dynamicPathSection[ohmurl] = { + dependencySection[ohmurl] = { language: 'js', - declPath: declFilesObject.files[file].declPath, - runtimeName: declFilesObject.files[file].ohmUrl + path: declFilesObject.files[file].declPath, + ohmUrl: declFilesObject.files[file].ohmUrl }; let absFilePath: string = path.resolve(depModuleInfo.moduleRootPath, file); let entryFileWithoutExtension: string = changeFileExtension(depModuleInfo.entryFile, ''); if (absFilePath === entryFileWithoutExtension) { - dynamicPathSection[depModuleInfo.packageName] = dynamicPathSection[ohmurl]; + dependencySection[depModuleInfo.packageName] = dependencySection[ohmurl]; } }); }); } public writeArkTSConfigFile(moduleInfo: ModuleInfo): void { - if (!moduleInfo.sourceRoots || moduleInfo.sourceRoots.length === 0) { - console.error('SourceRoots not set from hvigor.'); - } - let pathSection = this.getPathSection(); - let dependenciesSection: string[] = []; - let dynamicPathSection: Record = {}; - this.getDynamicPathSection(moduleInfo, dynamicPathSection); + let pathSection = this.getPathSection(moduleInfo); + let dependencySection: Record = {}; + this.getDependenciesSection(moduleInfo, dependencySection); - let baseUrl: string = path.resolve(moduleInfo.moduleRootPath, moduleInfo.sourceRoots[0]); + let baseUrl: string = path.resolve(moduleInfo.moduleRootPath); let arktsConfig: ArkTSConfigObject = { compilerOptions: { package: moduleInfo.packageName, baseUrl: baseUrl, paths: pathSection, - dependencies: dependenciesSection.length === 0 ? undefined : dependenciesSection, entry: moduleInfo.entryFile, - dynamicPaths: dynamicPathSection + dependencies: dependencySection } }; diff --git a/ets2panda/bindings/src/arrays.ts b/ets2panda/bindings/src/common/arrays.ts similarity index 94% rename from ets2panda/bindings/src/arrays.ts rename to ets2panda/bindings/src/common/arrays.ts index d0fe7499458c408d5b0467407f3d97bfc65007ab..75483d12dbba15e807d3867b1afadc17fafa3002 100644 --- a/ets2panda/bindings/src/arrays.ts +++ b/ets2panda/bindings/src/common/arrays.ts @@ -42,6 +42,15 @@ export function withStringArray(strings: Array | undefined): Uint8Array return array; } +export function withBigingArray(bigints: Array | undefined): Uint8Array { + if (bigints === undefined || bigints.length === 0) { + throwError('Error in strings array'); + } + + let array = encoder.encodeBigintArray(bigints); + return array; +} + function withArray(data: C | undefined, exec: ExecWithLength): R { return exec(data ?? null, data?.length ?? 0); } diff --git a/ets2panda/bindings/src/driver_helper.ts b/ets2panda/bindings/src/common/driver_helper.ts similarity index 73% rename from ets2panda/bindings/src/driver_helper.ts rename to ets2panda/bindings/src/common/driver_helper.ts index 4283055d0e0e5bc276112360a9b0d0234e106d33..18809ce73691e1e4ce14c6125997ceeb3e2d2ec7 100644 --- a/ets2panda/bindings/src/driver_helper.ts +++ b/ets2panda/bindings/src/common/driver_helper.ts @@ -16,9 +16,10 @@ import { Context, Config } from './types'; import { global } from './global'; import { throwError } from './utils'; -import { Es2pandaContextState } from './generated/Es2pandaEnums'; +import { Es2pandaContextState } from '../generated/Es2pandaEnums'; import { withStringResult } from './Platform'; -import { KBoolean, KNativePointer, KPointer } from './InteropTypes'; +import { KBoolean, KInt, KNativePointer, KPointer } from './InteropTypes'; +import { passStringArray } from './private'; export class DriverHelper { constructor(filePath: string, cmd: string[]) { @@ -76,19 +77,46 @@ export class DriverHelper { global.destroyCfg(); } - public generateTsDecl(declOutPath: string, etsOutPath: string, exportAll: boolean, recordFile: string): void { + public generateTsDecl(declOutPath: string, etsOutPath: string, exportAll: boolean, isolated: boolean, recordFile: string): void { let exportAll_: KBoolean = exportAll ? 1 : 0; - global.es2panda._GenerateTsDeclarationsFromContext(this._cfg.peer, declOutPath, etsOutPath, exportAll_, recordFile); + let isolated_: KBoolean = isolated ? 1 : 0; + global.es2panda._GenerateTsDeclarationsFromContext(this._cfg.peer, declOutPath, etsOutPath, exportAll_, isolated_, recordFile); } } export class LspDriverHelper { - public createCfg(cmd: string[], filePath: string, pandaLibPath: string): Config { + public memInitialize(pandaLibPath: string): void { + global.es2pandaPublic._MemInitialize(pandaLibPath); + } + + public memFinalize(): void { + global.es2pandaPublic._MemFinalize(); + } + + public createGlobalContext(config: KNativePointer, externalFileList: string[], fileNum: KInt): KNativePointer { + return global.es2pandaPublic._CreateGlobalContext(config, passStringArray(externalFileList), fileNum); + } + + public destroyGlobalContext(context: KNativePointer): void { + global.es2pandaPublic._DestroyGlobalContext(context); + } + + public createCfg(cmd: string[], filePath: string, pandaLibPath: string = ''): Config { return Config.create(cmd, filePath, pandaLibPath, true); } - public createCtx(source: string, filePath: string, cfg: Config): KNativePointer { - return Context.lspCreateFromString(source, filePath, cfg); + public createCtx( + source: string, + filePath: string, + cfg: Config, + globalContextPtr?: KNativePointer, + isExternal: boolean = false + ): KNativePointer { + if (globalContextPtr) { + return Context.lspCreateCacheContextFromString(source, filePath, cfg, globalContextPtr, isExternal); + } else { + return Context.lspCreateFromString(source, filePath, cfg); + } } public proceedToState(ctx: KNativePointer, state: Es2pandaContextState): void { diff --git a/ets2panda/bindings/src/global.ts b/ets2panda/bindings/src/common/global.ts similarity index 97% rename from ets2panda/bindings/src/global.ts rename to ets2panda/bindings/src/common/global.ts index 2c99dce5caf8834048741b4c88d1f713df2cd8d1..b6841d5e5fb6dd6ecd41df67f522238b72bdc3ce 100644 --- a/ets2panda/bindings/src/global.ts +++ b/ets2panda/bindings/src/common/global.ts @@ -22,9 +22,8 @@ import { initPublicEs2panda, initPublicGeneratedEs2panda } from './Es2pandaNativeModule'; -import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from './generated/Es2pandaNativeModule'; +import { Es2pandaNativeModule as GeneratedEs2pandaNativeModule } from '../generated/Es2pandaNativeModule'; import { initInterop, InteropNativeModule, initPublicInterop } from './InteropNativeModule'; -import { Context } from './types'; // CC-OFFNXT(G.NAM.01) project code style export class global { diff --git a/ets2panda/bindings/src/loadLibraries.ts b/ets2panda/bindings/src/common/loadLibraries.ts similarity index 100% rename from ets2panda/bindings/src/loadLibraries.ts rename to ets2panda/bindings/src/common/loadLibraries.ts diff --git a/ets2panda/bindings/src/mainWrapper.ts b/ets2panda/bindings/src/common/mainWrapper.ts similarity index 100% rename from ets2panda/bindings/src/mainWrapper.ts rename to ets2panda/bindings/src/common/mainWrapper.ts diff --git a/ets2panda/bindings/src/preDefine.ts b/ets2panda/bindings/src/common/preDefine.ts similarity index 77% rename from ets2panda/bindings/src/preDefine.ts rename to ets2panda/bindings/src/common/preDefine.ts index b0dc4d7f85d7414a5494160cebb02e6b18b62b68..79f470bd73f3d0e57f550c3dbafd4d309fe08aba 100644 --- a/ets2panda/bindings/src/preDefine.ts +++ b/ets2panda/bindings/src/common/preDefine.ts @@ -15,13 +15,16 @@ export const ARKTSCONFIG_JSON_FILE: string = 'arktsconfig.json'; -export const ABC_SUFFIX: string = '.abc'; - export enum LANGUAGE_VERSION { ARKTS_1_2 = '1.2', ARKTS_1_1 = '1.1', ARKTS_HYBRID = 'hybrid' } +export const DECL_ETS_SUFFIX: string = '.d.ets'; export const PANDA_SDK_PATH_FROM_SDK: string = './build-tools/ets2panda'; export const SYSTEM_SDK_PATH_FROM_SDK: string = './'; +export const EXTERNAL_API_PATH_FROM_SDK: string = '../../../hms/ets/ets1.2'; +export const DEFAULT_CACHE_DIR: string = './.idea/.deveco'; +export const ETS_SUFFIX: string = '.ets'; +export const TS_SUFFIX: string = '.ts'; diff --git a/ets2panda/bindings/src/private.ts b/ets2panda/bindings/src/common/private.ts similarity index 87% rename from ets2panda/bindings/src/private.ts rename to ets2panda/bindings/src/common/private.ts index 777aad9066bcee2f3e76ede341d48ea2057b884e..3ad06d3be69edede57a0f02e5bcbaafbca66c6b8 100644 --- a/ets2panda/bindings/src/private.ts +++ b/ets2panda/bindings/src/common/private.ts @@ -15,9 +15,9 @@ import { throwError } from './utils'; import { KNativePointer, nullptr } from './InteropTypes'; -import { withString, withStringArray } from './arrays'; +import { withString, withStringArray, withBigingArray } from './arrays'; import { NativePtrDecoder, withStringResult } from './Platform'; -import { LspDiagsNode, LspNode } from './lspNode'; +import { LspDiagsNode, LspNode } from '../lsp/lspNode'; export function lspData(peer: KNativePointer): LspNode { return new LspDiagsNode(peer); @@ -63,3 +63,7 @@ export function passString(str: string | undefined): string { export function passStringArray(strings: string[]): Uint8Array { return withStringArray(strings); } + +export function passPointerArray(pointers: KNativePointer[]): Uint8Array { + return withBigingArray(pointers.map((pointer) => BigInt(Number(pointer)))); +} diff --git a/ets2panda/bindings/src/strings.ts b/ets2panda/bindings/src/common/strings.ts similarity index 86% rename from ets2panda/bindings/src/strings.ts rename to ets2panda/bindings/src/common/strings.ts index ff72c1626e279be2e4e2a098b25fac2aaa3d84f5..4e0a118ad1b41663ae8cdcfa330936b90eaf35d1 100644 --- a/ets2panda/bindings/src/strings.ts +++ b/ets2panda/bindings/src/common/strings.ts @@ -122,6 +122,37 @@ export class CustomTextEncoder { return array; } + encodedBigintLength(value: bigint): int32 { + let str = value.toString(); + return this.encodedLength(str); + } + + encodeBigintInto(value: bigint, result: Uint8Array, position: int32): Uint8Array { + let str = value.toString(); + return this.encodeInto(str, result, position); + } + + encodeBigintArray(bigints: Array): Uint8Array { + let totalBytes = CustomTextEncoder.HeaderLen; + let lengths = new Int32Array(bigints.length); + for (let i = 0; i < lengths.length; i++) { + let len = this.encodedBigintLength(bigints[i]); + lengths[i] = len; + totalBytes += len + CustomTextEncoder.HeaderLen; + } + let array = new Uint8Array(totalBytes); + let position = 0; + this.addLength(array, position, lengths.length); + position += CustomTextEncoder.HeaderLen; + for (let i = 0; i < lengths.length; i++) { + this.addLength(array, position, lengths[i]); + position += CustomTextEncoder.HeaderLen; + this.encodeBigintInto(bigints[i], array, position); + position += lengths[i]; + } + return array; + } + encodeInto(input: string, result: Uint8Array, position: int32): Uint8Array { if (this.encoder !== undefined) { this.encoder!.encodeInto(input, result.subarray(position, result.length)); diff --git a/ets2panda/bindings/src/ts-reflection.ts b/ets2panda/bindings/src/common/ts-reflection.ts similarity index 100% rename from ets2panda/bindings/src/ts-reflection.ts rename to ets2panda/bindings/src/common/ts-reflection.ts diff --git a/ets2panda/bindings/src/types.ts b/ets2panda/bindings/src/common/types.ts similarity index 69% rename from ets2panda/bindings/src/types.ts rename to ets2panda/bindings/src/common/types.ts index 2a34d968b7c5a540cb7d7cfa7f17e0c7622eb623..29edf9d82a22794cef0e38dd051c4cb8c4417350 100644 --- a/ets2panda/bindings/src/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -13,11 +13,12 @@ * limitations under the License. */ -import { KNativePointer as KPtr } from './InteropTypes'; +import { KNativePointer, KNativePointer as KPtr } from './InteropTypes'; import { global } from './global'; import { throwError } from './utils'; import { passString, passStringArray, unpackString } from './private'; import { isNullPtr } from './Wrapper'; +import { Worker as ThreadWorker } from 'worker_threads'; export const arrayOfNullptr = new BigUint64Array([BigInt(0)]); @@ -103,12 +104,40 @@ export class Context extends ArktsObject { ); } + static createFromStringWithHistory(source: string): Context { + if (!global.configIsInitialized()) { + throwError(`Config not initialized`); + } + return new Context( + global.es2panda._CreateContextFromStringWithHistory(global.config, passString(source), passString(global.filePath)) + ); + } + static lspCreateFromString(source: string, filePath: string, cfg: Config): KPtr { if (cfg === undefined) { throwError(`Config not initialized`); } return global.es2pandaPublic._CreateContextFromString(cfg.peer, passString(source), passString(filePath)); } + + static lspCreateCacheContextFromString( + source: string, + filePath: string, + cfg: Config, + globalContextPtr: KNativePointer, + isExternal: boolean + ): KPtr { + if (cfg === undefined) { + throwError(`Config not initialized`); + } + return global.es2pandaPublic._CreateCacheContextFromString( + cfg.peer, + passString(source), + passString(filePath), + globalContextPtr, + isExternal + ); + } } // ProjectConfig begins @@ -116,30 +145,22 @@ export interface PluginsConfig { [pluginName: string]: string; } -export interface BuildBaseConfig { - buildType: 'build' | 'preview' | 'hotreload' | 'coldreload'; - buildMode: 'Debug' | 'Release'; - hasMainModule: boolean; - arkts: object; - arktsGlobal: object; -} - export interface ModuleConfig { packageName: string; moduleType: string; moduleRootPath: string; - sourceRoots: string[]; + language: string; + declFilesPath?: string; + dependencies?: string[]; } export interface PathConfig { - loaderOutPath: string; - declgenDtsOutPath: string; - declgenTsOutPath: string; - cachePath: string; buildSdkPath: string; - pandaSdkPath?: string; // path to panda sdk lib/bin, for local test - pandaStdlibPath?: string; // path to panda sdk stdlib, for local test - abcLinkerPath?: string; + projectPath: string; + declgenOutDir: string; + cacheDir?: string; + externalApiPath?: string; + aceModuleJsonPath?: string; } export interface DeclgenConfig { @@ -148,51 +169,59 @@ export interface DeclgenConfig { declgenBridgeCodePath?: string; } -export interface LoggerConfig { - getHvigorConsoleLogger?: Function; -} - -export interface DependentModuleConfig { - packageName: string; - moduleName: string; - moduleType: string; - modulePath: string; - sourceRoots: string[]; - entryFile: string; - language: string; - declFilesPath?: string; - dependencies?: string[]; -} - -export interface BuildConfig extends BuildBaseConfig, DeclgenConfig, LoggerConfig, ModuleConfig, PathConfig { +export interface BuildConfig extends DeclgenConfig, ModuleConfig, PathConfig { plugins: PluginsConfig; compileFiles: string[]; - dependentModuleList: DependentModuleConfig[]; } // ProjectConfig ends -export interface CompileFileInfo { - filePath: string; - dependentFiles: string[]; - abcFilePath: string; - arktsConfigFile: string; - packageName: string; -} - export interface ModuleInfo { - isMainModule: boolean; packageName: string; moduleRootPath: string; moduleType: string; - sourceRoots: string[]; entryFile: string; arktsConfigFile: string; - compileFileInfos: CompileFileInfo[]; + compileFiles: string[]; declgenV1OutPath: string | undefined; declgenBridgeCodePath: string | undefined; + staticDepModuleInfos: string[]; + dynamicDepModuleInfos: string[]; + language: string; dependencies?: string[]; - staticDepModuleInfos: Map; - dynamicDepModuleInfos: Map; - language?: string; declFilesPath?: string; } + +export interface Job { + id: string; + isDeclFile: boolean; + isInCycle?: boolean; + fileList: string[]; + dependencies: string[]; + dependants: string[]; + isValid: boolean; +} + +export interface JobInfo { + id: string; + filePath: string; + arktsConfigFile: string; + globalContextPtr: KNativePointer; + buildConfig: BuildConfig; + isValid: boolean; +} + +export interface FileDepsInfo { + dependencies: Record; + dependants: Record; +} + +export interface WorkerInfo { + worker: ThreadWorker; + isIdle: boolean; +} +export interface TextDocumentChangeInfo { + newDoc: string; + rangeStart?: number; + rangeEnd?: number; + updateText?: string; +} diff --git a/ets2panda/bindings/src/ui_plugins_driver.ts b/ets2panda/bindings/src/common/ui_plugins_driver.ts similarity index 93% rename from ets2panda/bindings/src/ui_plugins_driver.ts rename to ets2panda/bindings/src/common/ui_plugins_driver.ts index 1d30fb980313a62dfab333d979f6b776ca207725..464cd6b396dc8ef61bd9036fdb0dd07084331c12 100644 --- a/ets2panda/bindings/src/ui_plugins_driver.ts +++ b/ets2panda/bindings/src/common/ui_plugins_driver.ts @@ -65,12 +65,14 @@ class PluginContext { private program: object | undefined; private projectConfig: object | undefined; private contextPtr: KNativePointer | undefined; + private codingFilePath: string | undefined; constructor() { this.ast = undefined; this.program = undefined; this.projectConfig = undefined; this.contextPtr = undefined; + this.codingFilePath = undefined; } public setArkTSAst(ast: object): void { @@ -104,6 +106,18 @@ class PluginContext { public getContextPtr(): KNativePointer | undefined { return this.contextPtr; } + + public setCodingFilePath(codingFilePath: string): void { + this.codingFilePath = codingFilePath; + } + + public getCodingFilePath(): string | undefined { + return this.codingFilePath; + } + + public isCoding(): boolean { + return this.codingFilePath !== undefined; + } } export class PluginDriver { diff --git a/ets2panda/bindings/src/utils.ts b/ets2panda/bindings/src/common/utils.ts similarity index 64% rename from ets2panda/bindings/src/utils.ts rename to ets2panda/bindings/src/common/utils.ts index 5e21866cc6fe8a7bfe6763bde7ce076f6abf8ea2..800f92b064490f638bf0e71299ed0809ab6bcf76 100644 --- a/ets2panda/bindings/src/utils.ts +++ b/ets2panda/bindings/src/common/utils.ts @@ -15,6 +15,8 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as os from 'os'; +import { DECL_ETS_SUFFIX } from './preDefine'; export function throwError(error: string): never { throw new Error(error); @@ -43,3 +45,25 @@ export function ensurePathExists(filePath: string): void { } } } + +export function isMac(): boolean { + return os.type() === 'Darwin'; +} + +export function changeDeclgenFileExtension(file: string, targetExt: string): string { + if (file.endsWith(DECL_ETS_SUFFIX)) { + return changeFileExtension(file, targetExt, DECL_ETS_SUFFIX); + } + return changeFileExtension(file, targetExt); +} + +export function getModuleNameAndPath(filePath: string, projectPath: string): [string, string] { + let moduleName: string = ''; + let moduleRootPath: string = ''; + if (filePath.indexOf(projectPath) >= 0) { + const relativePath = path.relative(projectPath, filePath); + moduleName = relativePath.split(path.sep)[0]; + moduleRootPath = path.join(projectPath, moduleName); + } + return [moduleName, moduleRootPath]; +} diff --git a/ets2panda/bindings/src/generated/Es2pandaNativeModule.ts b/ets2panda/bindings/src/generated/Es2pandaNativeModule.ts index 8bd49b74b2f0d7c05b5b93ba613015467b5282c1..bb53f1d512a49c81af2aaf9a797748ea9f96f4d7 100644 --- a/ets2panda/bindings/src/generated/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/generated/Es2pandaNativeModule.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -import { KBoolean, KInt, KNativePointer } from '../InteropTypes'; +import { KBoolean, KInt, KNativePointer } from '../common/InteropTypes'; export class Es2pandaNativeModule { _CreateMemberExpression( diff --git a/ets2panda/bindings/src/index.ts b/ets2panda/bindings/src/index.ts index 7f572287111d7951aa275ad6f477cfece9b7cc24..d508b948c88a327860440a69dc67f46dfc34172e 100644 --- a/ets2panda/bindings/src/index.ts +++ b/ets2panda/bindings/src/index.ts @@ -13,19 +13,4 @@ * limitations under the License. */ -export { Lsp } from './lsp_helper'; -export { DriverHelper } from './driver_helper'; -export { Es2pandaContextState } from './generated/Es2pandaEnums'; -export { - LspCompletionInfo, - LspCompletionEntryKind, - LspDefinitionData, - LspDiagsNode, - LspDiagnosticNode, - LspDiagSeverity, - LspQuickInfo, - LspSymbolDisplayPart -} from './lspNode'; -export { generateArkTsConfigByModules } from './arktsConfigGenerate'; -export type { ModuleDescriptor } from './buildConfigGenerate'; -export type { TextDocumentChangeInfo } from './lsp_helper'; +export * from './lsp/index'; diff --git a/ets2panda/bindings/src/lsp/compile_thread_worker.ts b/ets2panda/bindings/src/lsp/compile_thread_worker.ts new file mode 100644 index 0000000000000000000000000000000000000000..14cdbd600444e40a33e91fe7a298586216cdaa7f --- /dev/null +++ b/ets2panda/bindings/src/lsp/compile_thread_worker.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ + +import { parentPort, workerData } from 'worker_threads'; +import * as fs from 'fs'; +import { PluginDriver, PluginHook } from '../common/ui_plugins_driver'; +import { LspDriverHelper } from '../common/driver_helper'; +import { Es2pandaContextState } from '../generated/Es2pandaEnums'; +import { JobInfo } from '../common/types'; + +const { workerId } = workerData; + +function compileExternalProgram(jobInfo: JobInfo): void { + PluginDriver.getInstance().initPlugins(jobInfo.buildConfig); + let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', jobInfo.arktsConfigFile]; + let lspDriverHelper = new LspDriverHelper(); + let config = lspDriverHelper.createCfg(ets2pandaCmd, jobInfo.filePath); + if (!fs.existsSync(jobInfo.filePath)) { + return; + } + const source = fs.readFileSync(jobInfo.filePath, 'utf8').replace(/\r\n/g, '\n'); + let context = lspDriverHelper.createCtx(source, jobInfo.filePath, config, jobInfo.globalContextPtr, true); + PluginDriver.getInstance().getPluginContext().setContextPtr(context); + lspDriverHelper.proceedToState(context, Es2pandaContextState.ES2PANDA_STATE_PARSED); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + lspDriverHelper.proceedToState(context, Es2pandaContextState.ES2PANDA_STATE_LOWERED); +} + +parentPort?.on('message', (msg) => { + if (msg.type === 'ASSIGN_TASK') { + const job = msg.jobInfo; + if (!job.isValid) { + compileExternalProgram(job); + } + + parentPort?.postMessage({ + type: 'TASK_FINISH', + jobId: job.id, + workerId + }); + } else if (msg.type === 'EXIT') { + process.exit(0); + } +}); diff --git a/ets2panda/bindings/src/lsp/generateArkTSConfig.ts b/ets2panda/bindings/src/lsp/generateArkTSConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..fae5a16029d155265c52a0df422300ce6b748559 --- /dev/null +++ b/ets2panda/bindings/src/lsp/generateArkTSConfig.ts @@ -0,0 +1,84 @@ +/* + * 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. + */ + +import * as path from 'path'; + +import { ARKTSCONFIG_JSON_FILE, LANGUAGE_VERSION } from '../common/preDefine'; +import { BuildConfig, ModuleInfo } from '../common/types'; +import { ArkTSConfigGenerator } from '../common/arkTSConfigGenerator'; + +function collectDepModuleInfos(moduleInfo: ModuleInfo, allBuildConfig: Record): void { + let dynamicDepModules: string[] = []; + let staticDepModules: string[] = []; + + if (moduleInfo.dependencies) { + moduleInfo.dependencies.forEach((moduleName: string) => { + let depModule = allBuildConfig[moduleName]; + depModule.language === LANGUAGE_VERSION.ARKTS_1_2 + ? staticDepModules.push(depModule.packageName) + : dynamicDepModules.push(depModule.packageName); + }); + } + moduleInfo.dynamicDepModuleInfos = dynamicDepModules; + moduleInfo.staticDepModuleInfos = staticDepModules; +} + +function collectModuleInfos(allBuildConfig: Record): Record { + let moduleInfos: Record = {}; + Object.values(allBuildConfig).forEach((buildConfig) => { + let moduleInfo = generateModuleInfo(allBuildConfig, buildConfig); + moduleInfos[moduleInfo.packageName] = moduleInfo; + }); + return moduleInfos; +} + +export function generateModuleInfo(allBuildConfig: Record, buildConfig: BuildConfig): ModuleInfo { + if (!buildConfig.packageName || !buildConfig.moduleRootPath) { + console.error('Main buildConfig info from hvigor is not correct.'); + } + let moduleInfo: ModuleInfo = { + packageName: buildConfig.packageName, + moduleRootPath: buildConfig.moduleRootPath, + moduleType: buildConfig.moduleType, + entryFile: buildConfig.packageName !== 'entry' ? path.join(buildConfig.moduleRootPath, 'Index.ets') : '', + arktsConfigFile: path.resolve(buildConfig.cacheDir!, buildConfig.packageName, ARKTSCONFIG_JSON_FILE), + compileFiles: buildConfig.compileFiles, + declgenV1OutPath: buildConfig.declgenV1OutPath, + declgenBridgeCodePath: buildConfig.declgenBridgeCodePath, + staticDepModuleInfos: [], + dynamicDepModuleInfos: [], + language: buildConfig.language, + dependencies: buildConfig.dependencies, + declFilesPath: buildConfig.declFilesPath + }; + collectDepModuleInfos(moduleInfo, allBuildConfig); + return moduleInfo; +} + +export function generateArkTsConfigs(allBuildConfig: Record): Record { + let moduleInfos: Record = collectModuleInfos(allBuildConfig); + Object.keys(moduleInfos).forEach((filePath: string) => { + let packageName = moduleInfos[filePath].packageName; + let generator = ArkTSConfigGenerator.getGenerator(allBuildConfig[packageName], moduleInfos); + generator.writeArkTSConfigFile(moduleInfos[filePath]); + }); + let fileToModuleInfo: Record = {}; + Object.values(moduleInfos).forEach((moduleInfo: ModuleInfo) => { + moduleInfo.compileFiles.forEach((file: string) => { + fileToModuleInfo[file] = moduleInfo; + }); + }); + return fileToModuleInfo; +} diff --git a/ets2panda/bindings/src/buildConfigGenerate.ts b/ets2panda/bindings/src/lsp/generateBuildConfig.ts similarity index 69% rename from ets2panda/bindings/src/buildConfigGenerate.ts rename to ets2panda/bindings/src/lsp/generateBuildConfig.ts index 478196c6aaaa569ab9b798c8eca2cc9879a506f8..7eace71cb11d0a6bd3f9445e2a25b66f473516e2 100644 --- a/ets2panda/bindings/src/buildConfigGenerate.ts +++ b/ets2panda/bindings/src/lsp/generateBuildConfig.ts @@ -16,13 +16,15 @@ import * as fs from 'fs'; import * as path from 'path'; import * as JSON5 from 'json5'; -import { BuildConfig } from './types'; +import { BuildConfig, PathConfig } from '../common/types'; +import { DEFAULT_CACHE_DIR, EXTERNAL_API_PATH_FROM_SDK } from '../common/preDefine'; export interface ModuleDescriptor { arktsversion: string; name: string; moduleType: string; srcPath: string; + aceModuleJsonPath?: string; } interface Json5Object { @@ -92,9 +94,8 @@ function getEtsFiles(modulePath: string): string[] { const files: string[] = []; const shouldSkipDirectory = (relativePath: string): boolean => { - const testDir1 = `src${path.sep}test`; - const testDir2 = `src${path.sep}ohosTest`; - return relativePath.startsWith(testDir1) || relativePath.startsWith(testDir2); + const filterList = [`src${path.sep}test`, `src${path.sep}ohosTest`, `build${path.sep}`, `oh_modules${path.sep}`]; + return filterList.some((directoryPrefix: string) => relativePath.startsWith(directoryPrefix)); }; const processEntry = (dir: string, entry: fs.Dirent): void => { @@ -150,15 +151,8 @@ function getModuleDependencies(modulePath: string, visited = new Set()): } }; - const resolveNestedDependencies = (deps: string[]): string[] => { - return deps.flatMap((depPath) => - visited.has(depPath) ? [] : [depPath, ...getModuleDependencies(depPath, visited)] - ); - }; - const dependencies = extractDependencies(); - const nestedDependencies = resolveNestedDependencies(dependencies); - return Array.from(new Set([...dependencies, ...nestedDependencies])); + return Array.from(new Set([...dependencies])); } function createMapEntryForPlugin(buildSdkPath: string, pluginName: string): string { @@ -174,68 +168,79 @@ function createPluginMap(buildSdkPath: string): Record { return pluginMap; } +function addPluginPathConfigs(buildConfig: BuildConfig, module: ModuleDescriptor): void { + buildConfig.aceModuleJsonPath = module.aceModuleJsonPath; +} + export function generateBuildConfigs( - buildSdkPath: string, - projectRoot: string, + pathConfig: PathConfig, modules?: ModuleDescriptor[] ): Record { const allBuildConfigs: Record = {}; if (!modules) { - const buildProfilePath = path.join(projectRoot, 'build-profile.json5'); + const buildProfilePath = path.join(pathConfig.projectPath, 'build-profile.json5'); modules = getModulesFromBuildProfile(buildProfilePath); } const definedModules = modules; - const cacheDir = path.join(projectRoot, '.idea', '.deveco'); + + const enableDeclgen: Map = new Map(modules.map((module) => [module.name, false])); for (const module of definedModules) { const modulePath = module.srcPath; const compileFiles = new Set(getEtsFiles(modulePath)); - const pluginMap = createPluginMap(buildSdkPath); + const pluginMap = createPluginMap(pathConfig.buildSdkPath); // Get recursive dependencies const dependencies = getModuleDependencies(modulePath); for (const depPath of dependencies) { getEtsFiles(depPath).forEach((file) => compileFiles.add(file)); + const depModule = definedModules.find((m) => m.srcPath === depPath); + if (module.arktsversion === '1.1' && depModule?.arktsversion === '1.2') { + enableDeclgen.set(depModule.name, true); + } } allBuildConfigs[module.name] = { plugins: pluginMap, - arkts: {}, - arktsGlobal: {}, compileFiles: Array.from(compileFiles), packageName: module.name, moduleType: module.moduleType, - buildType: 'build', - buildMode: 'Debug', moduleRootPath: modulePath, - sourceRoots: ['./'], - hasMainModule: true, - loaderOutPath: path.join(modulePath, 'build', 'default', 'cache'), - cachePath: cacheDir, - buildSdkPath: buildSdkPath, + language: module.arktsversion, + buildSdkPath: pathConfig.buildSdkPath, + projectPath: pathConfig.projectPath, + declgenOutDir: pathConfig.declgenOutDir, + externalApiPath: pathConfig.externalApiPath + ? pathConfig.externalApiPath + : path.resolve(pathConfig.buildSdkPath, EXTERNAL_API_PATH_FROM_SDK), + cacheDir: + pathConfig.cacheDir !== undefined ? pathConfig.cacheDir : path.join(pathConfig.projectPath, DEFAULT_CACHE_DIR), enableDeclgenEts2Ts: false, - declgenDtsOutPath: path.join(modulePath, 'build', 'default', 'cache'), - declgenTsOutPath: path.join(modulePath, 'build', 'default', 'cache'), - dependentModuleList: dependencies.map((dep) => { + declFilesPath: + module.arktsversion === '1.1' + ? path.join(pathConfig.declgenOutDir, 'static', module.name, 'decl-fileInfo.json') + : undefined, + dependencies: dependencies.map((dep) => { const depModule = definedModules.find((m) => m.srcPath === dep); - return { - packageName: path.basename(dep), - moduleName: path.basename(dep), - moduleType: depModule ? depModule.moduleType : 'har', - modulePath: dep, - sourceRoots: ['./'], - entryFile: 'index.ets', - language: depModule ? depModule.arktsversion : '1.1' - }; + return depModule!.name; }) }; + addPluginPathConfigs(allBuildConfigs[module.name], module); } - const outputPath = path.join(cacheDir, 'lsp_build_config.json'); - if (!fs.existsSync(cacheDir)) { - fs.mkdirSync(cacheDir, { recursive: true }); - } - fs.writeFileSync(outputPath, JSON.stringify(allBuildConfigs, null, 4)); + Object.entries(allBuildConfigs).forEach(([key, config]) => { + if (enableDeclgen.get(key) === true) { + config.enableDeclgenEts2Ts = true; + config.declgenV1OutPath = path.join(pathConfig.declgenOutDir, 'dynamic', key, 'declgenV1'); + config.declgenBridgeCodePath = path.join(pathConfig.declgenOutDir, 'dynamic', key, 'declgenBridgeCode'); + if (!fs.existsSync(config.declgenV1OutPath)) { + fs.mkdirSync(config.declgenV1OutPath, { recursive: true }); + } + if (!fs.existsSync(config.declgenBridgeCodePath)) { + fs.mkdirSync(config.declgenBridgeCodePath, { recursive: true }); + } + } + }); return allBuildConfigs; } diff --git a/ets2panda/bindings/src/lsp/index.ts b/ets2panda/bindings/src/lsp/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..effe67f08670ee67f0bfb3e486fd3060c32c7470 --- /dev/null +++ b/ets2panda/bindings/src/lsp/index.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +export { Lsp } from './lsp_helper'; +export { Es2pandaContextState } from '../generated/Es2pandaEnums'; +export { + LspCompletionInfo, + LspCompletionEntryKind, + LspDefinitionData, + LspDiagsNode, + LspDiagnosticNode, + LspDiagSeverity, + LspQuickInfo, + LspSymbolDisplayPart +} from './lspNode'; +export type { ModuleDescriptor } from './generateBuildConfig'; +export type { PathConfig, TextDocumentChangeInfo } from '../common/types'; diff --git a/ets2panda/bindings/src/lspNode.ts b/ets2panda/bindings/src/lsp/lspNode.ts similarity index 92% rename from ets2panda/bindings/src/lspNode.ts rename to ets2panda/bindings/src/lsp/lspNode.ts index ff74913d6a976e3d2a0785319fbd989c8ffa8c65..492350d708ede2822d13b30c4aa46f007edb110b 100644 --- a/ets2panda/bindings/src/lspNode.ts +++ b/ets2panda/bindings/src/lsp/lspNode.ts @@ -13,14 +13,18 @@ * limitations under the License. */ -import { KBoolean, KInt, KNativePointer, KUInt } from './InteropTypes'; -import { unpackString, VariantTypes } from './private'; -import { throwError } from './utils'; -import { isNullPtr } from './Wrapper'; -import { global } from './global'; -import { NativePtrDecoder } from './Platform'; +import { KBoolean, KInt, KNativePointer, KUInt } from '../common/InteropTypes'; +import { unpackString, VariantTypes } from '../common/private'; +import { throwError } from '../common/utils'; +import { isNullPtr } from '../common/Wrapper'; +import { global } from '../common/global'; +import { NativePtrDecoder } from '../common/Platform'; -enum HierarchyType { OTHERS, INTERFACE, CLASS }; +enum HierarchyType { + OTHERS, + INTERFACE, + CLASS +} export enum SetterStyle { NONE = 0, @@ -34,7 +38,14 @@ export enum AccessModifierStyle { PRIVATE } -enum ClassRelationKind { UNKNOWN, INTERFACE, CLASS, FIELD, METHOD, PROPERTY }; +enum ClassRelationKind { + UNKNOWN, + INTERFACE, + CLASS, + FIELD, + METHOD, + PROPERTY +} export enum ClassDefinitionStyle { FIELD = 0, @@ -324,7 +335,7 @@ export class FieldListProperty extends LspNode { this.modifierKinds = new NativePtrDecoder() .decode(global.es2panda._getModifierKindsFromPropertyInfo(peer)) .map((elPeer: KNativePointer) => { - return new String(elPeer); + return new String(unpackString(elPeer)); }); this.displayName = unpackString(global.es2panda._getDisplayNameFromPropertyInfo(peer)); this.start = global.es2panda._getStartFromPropertyInfo(peer); @@ -929,3 +940,53 @@ export class LspSignatureHelpItems extends LspNode { readonly argumentIndex: number; readonly argumentCount: number; } + +export class LspRenameInfoSuccess extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.canRenameSuccess = true; + this.fileToRename = unpackString(global.es2panda._getRenameSuccessFileName(peer)); + this.kind = unpackString(global.es2panda._getRenameSuccessKind(peer)); + this.displayName = unpackString(global.es2panda._getRenameSuccessDisplayName(peer)); + this.fullDisplayName = unpackString(global.es2panda._getRenameSuccessFullDisplayName(peer)); + this.kindModifiers = unpackString(global.es2panda._getRenameSuccessKindModifiers(peer)); + this.triggerSpan = new LspTextSpan(global.es2panda._getRenameSuccessTriggerSpan(peer)); + } + readonly canRenameSuccess: boolean; + readonly fileToRename: string; + readonly kind: string; + readonly displayName: string; + readonly fullDisplayName: string; + readonly kindModifiers: string; + readonly triggerSpan: LspTextSpan; +} + +export class LspRenameInfoFailure extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.canRenameFailure = false; + this.localizedErrorMessage = unpackString(global.es2panda._getRenameFailureLocalizedErrorMessage(peer)); + } + readonly canRenameFailure: boolean; + readonly localizedErrorMessage: string; +} + +export type LspRenameInfoType = LspRenameInfoSuccess | LspRenameInfoFailure; + +export class LspRenameLocation extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.fileName = unpackString(global.es2panda._getRenameLocationFileName(peer)); + this.start = global.es2panda._getRenameLocationStart(peer); + this.end = global.es2panda._getRenameLocationEnd(peer); + this.line = global.es2panda._getRenameLocationLine(peer); + this.prefixText = unpackString(global.es2panda._getRenameLocationPrefixText(peer)); + this.suffixText = unpackString(global.es2panda._getRenameLocationSuffixText(peer)); + } + readonly fileName: string; + readonly start: number; + readonly end: number; + readonly line: number; + readonly prefixText: string; + readonly suffixText: string; +} diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8308d46a3494c7a5c1e7c0aee7c84ca560e2e93 --- /dev/null +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -0,0 +1,1316 @@ +/* + * 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. + */ + +import { LspDriverHelper } from '../common/driver_helper'; +import { global } from '../common/global'; +import { + LspDefinitionData, + LspDiagsNode, + LspReferences, + LspQuickInfo, + LspClassHierarchy, + LspCompletionEntryKind, + LspClassPropertyInfo, + LspClassHierarchies, + LspDocumentHighlightsReferences, + LspCompletionInfo, + LspReferenceLocationList, + LspLineAndCharacter, + LspReferenceData, + LspClassConstructorInfo, + ApplicableRefactorItemInfo, + LspApplicableRefactorInfo, + CompletionEntryDetails, + LspFileTextChanges, + LspSafeDeleteLocationInfo, + LspSafeDeleteLocation, + LspTypeHierarchiesInfo, + LspTextSpan, + LspInlayHint, + LspInlayHintList, + TextSpan, + LspSignatureHelpItems, + CodeFixActionInfo, + CodeFixActionInfoList, + LspRenameLocation, + LspRenameInfoType, + LspRenameInfoSuccess, + LspRenameInfoFailure +} from './lspNode'; +import { passStringArray, unpackString } from '../common/private'; +import { Es2pandaContextState } from '../generated/Es2pandaEnums'; +import { + BuildConfig, + Config, + FileDepsInfo, + Job, + JobInfo, + WorkerInfo, + ModuleInfo, + PathConfig, + TextDocumentChangeInfo +} from '../common/types'; +import { PluginDriver, PluginHook } from '../common/ui_plugins_driver'; +import { ModuleDescriptor, generateBuildConfigs } from './generateBuildConfig'; +import { generateArkTsConfigs, generateModuleInfo } from './generateArkTSConfig'; + +import * as fs from 'fs'; +import * as path from 'path'; +import { KInt, KNativePointer, KPointer } from '../common/InteropTypes'; +import { passPointerArray } from '../common/private'; +import { NativePtrDecoder } from '../common/Platform'; +import { Worker as ThreadWorker } from 'worker_threads'; +import { ensurePathExists } from '../common/utils'; +import * as child_process from 'child_process'; +import { DECL_ETS_SUFFIX, DEFAULT_CACHE_DIR, TS_SUFFIX } from '../common/preDefine'; +import * as crypto from 'crypto'; +import * as os from 'os'; +import { changeDeclgenFileExtension, getModuleNameAndPath } from '../common/utils'; + +const ets2pandaCmdPrefix = ['-', '--extension', 'ets', '--arktsconfig']; + +function initBuildEnv(): void { + const currentPath: string | undefined = process.env.PATH; + let pandaLibPath: string = process.env.PANDA_LIB_PATH + ? process.env.PANDA_LIB_PATH + : path.resolve(__dirname, '../../../ets2panda/lib'); + process.env.PATH = `${currentPath}${path.delimiter}${pandaLibPath}`; +} + +export class Lsp { + private pandaLibPath: string; + private pandaBinPath: string; + private getFileContent: (filePath: string) => string; + private filesMap: Map; // Map + private cacheDir: string; + private globalContextPtr?: KNativePointer; + private globalConfig?: Config; + private globalLspDriverHelper?: LspDriverHelper; + private entryArkTsConfig: string; + private fileDependencies: string; + private buildConfigs: Record; // Map + private moduleInfos: Record; // Map + private pathConfig: PathConfig; + private lspDriverHelper = new LspDriverHelper(); + + constructor(pathConfig: PathConfig, getContentCallback?: (filePath: string) => string, modules?: ModuleDescriptor[]) { + initBuildEnv(); + this.cacheDir = + pathConfig.cacheDir !== undefined ? pathConfig.cacheDir : path.join(pathConfig.projectPath, DEFAULT_CACHE_DIR); + this.fileDependencies = path.join(this.cacheDir, 'file_dependencies.json'); + this.entryArkTsConfig = path.join(this.cacheDir, 'entry', 'arktsconfig.json'); + this.pandaLibPath = process.env.PANDA_LIB_PATH + ? process.env.PANDA_LIB_PATH + : path.resolve(__dirname, '../../../ets2panda/lib'); + this.pandaBinPath = process.env.PANDA_BIN_PATH + ? process.env.PANDA_BIN_PATH + : path.resolve(__dirname, '../../../ets2panda/bin'); + this.filesMap = new Map(); + this.getFileContent = getContentCallback || ((path: string): string => fs.readFileSync(path, 'utf8')); + this.buildConfigs = generateBuildConfigs(pathConfig, modules); + this.moduleInfos = generateArkTsConfigs(this.buildConfigs); + this.pathConfig = pathConfig; + PluginDriver.getInstance().initPlugins(Object.values(this.buildConfigs)[0]); + this.generateDeclFile(); + } + + // Partially update for new file + updateModuleInfos(module: ModuleDescriptor, newFilePath: String): void { + let buildConfig = this.buildConfigs[module.name]; + buildConfig.compileFiles.push(newFilePath.valueOf()); + let moduleInfo = generateModuleInfo(this.buildConfigs, buildConfig); + this.moduleInfos[newFilePath.valueOf()] = moduleInfo; + } + + // Full update for `Sync Now` + update(modules: ModuleDescriptor[]): void { + this.buildConfigs = generateBuildConfigs(this.pathConfig, modules); + this.moduleInfos = generateArkTsConfigs(this.buildConfigs); + } + + modifyFilesMap(fileName: string, fileContent: TextDocumentChangeInfo): void { + this.filesMap.set(fileName, fileContent.newDoc); + } + + deleteFromFilesMap(fileName: string): void { + this.filesMap.delete(fileName); + } + + private getFileSource(filePath: string): string { + const getSource = this.filesMap.get(filePath) || this.getFileContent(filePath) || fs.readFileSync(filePath, 'utf8'); + if (getSource === undefined) { + throw new Error(`File content not found for path: ${filePath}`); + } + return getSource.replace(/\r\n/g, '\n'); + } + + private createContext(filename: String, processToCheck: boolean = true): [Config, KNativePointer] { + const filePath = path.resolve(filename.valueOf()); + const arktsconfig = this.moduleInfos[filePath]?.arktsConfigFile; + if (!arktsconfig) { + throw new Error(`Missing arktsconfig for ${filePath}`); + } + + const ets2pandaCmd = [...ets2pandaCmdPrefix, arktsconfig]; + const localCfg = this.lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); + const source = this.getFileSource(filePath); + + const localCtx = this.lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); + try { + const packageName = this.moduleInfos[filePath].packageName; + const buildConfig = this.buildConfigs[packageName]; + const pluginContext = PluginDriver.getInstance().getPluginContext(); + pluginContext.setCodingFilePath(filePath); + pluginContext.setProjectConfig(buildConfig); + pluginContext.setContextPtr(localCtx); + + this.lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + + if (processToCheck) { + this.lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); + } + return [localCfg, localCtx]; + } catch (error) { + this.lspDriverHelper.destroyContext(localCtx); + this.lspDriverHelper.destroyConfig(localCfg); + throw error; + } + } + + private destroyContext(config: Config, context: KNativePointer): void { + try { + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + } finally { + this.lspDriverHelper.destroyContext(context); + this.lspDriverHelper.destroyConfig(config); + } + } + + generateDeclFile(): void { + for (const [moduleName, buildConfig] of Object.entries(this.buildConfigs)) { + if (!buildConfig.enableDeclgenEts2Ts) { + continue; + } + if (!buildConfig.declgenOutDir || buildConfig.declgenOutDir === '') { + return; + } + buildConfig.compileFiles.forEach((compilefilePath: string) => { + if (!Object.prototype.hasOwnProperty.call(this.moduleInfos, compilefilePath)) { + return; + } + const [cfg, ctx] = this.createContext(compilefilePath); + try { + // declgen file + let moduleInfo = this.moduleInfos[compilefilePath]; + let modulePath: string = path.relative(buildConfig.moduleRootPath, compilefilePath); + let declOut: string = ''; + let declBridgeOut: string = ''; + if (!moduleInfo.declgenV1OutPath) { + declOut = path.join(buildConfig.declgenOutDir, moduleName); + } + if (!moduleInfo.declgenBridgeCodePath) { + declBridgeOut = path.join(buildConfig.declgenOutDir, moduleName); + } + let declEtsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenV1OutPath ?? declOut, modulePath), + DECL_ETS_SUFFIX + ); + let etsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenBridgeCodePath ?? declBridgeOut, modulePath), + TS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + ensurePathExists(etsOutputPath); + global.es2pandaPublic._GenerateTsDeclarationsFromContext(ctx, declEtsOutputPath, etsOutputPath, 1, 0, ''); + } finally { + this.destroyContext(cfg, ctx); + } + }); + } + } + + modifyDeclFile(modifyFilePath: string, arktsConfigFile?: string): void { + // source file + let sourceFilePath = path.resolve(modifyFilePath.valueOf()); + let moduleInfo: ModuleInfo; + if (Object.prototype.hasOwnProperty.call(this.moduleInfos, sourceFilePath)) { + moduleInfo = this.moduleInfos[sourceFilePath]; + } else { + const [newModuleName, newModuleRootPath] = getModuleNameAndPath(modifyFilePath, this.pathConfig.projectPath); + if (newModuleName && newModuleName !== '' && newModuleRootPath && newModuleRootPath !== '') { + moduleInfo = { + packageName: newModuleName, + moduleRootPath: newModuleRootPath, + moduleType: '', + entryFile: '', + arktsConfigFile: arktsConfigFile ?? '', + compileFiles: [], + declgenV1OutPath: '', + declgenBridgeCodePath: '', + staticDepModuleInfos: [], + dynamicDepModuleInfos: [], + language: '' + }; + } else { + return; + } + } + const moduleName = moduleInfo.packageName; + const moduleRootPath = moduleInfo.moduleRootPath; + if (!Object.prototype.hasOwnProperty.call(this.buildConfigs, moduleName)) { + return; + } + const buildConfig = this.buildConfigs[moduleName]; + if (!buildConfig.enableDeclgenEts2Ts) { + return; + } + const [cfg, ctx] = this.createContext(sourceFilePath); + try { + // declgen file + let declOut: string = ''; + let declBridgeOut: string = ''; + if (!moduleInfo.declgenV1OutPath) { + declOut = path.join(buildConfig.declgenOutDir, moduleName); + } + if (!moduleInfo.declgenBridgeCodePath) { + declBridgeOut = path.join(buildConfig.declgenOutDir, moduleName); + } + let filePathFromModuleRoot: string = path.relative(moduleRootPath, modifyFilePath); + let declEtsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenV1OutPath ?? declOut, filePathFromModuleRoot), + DECL_ETS_SUFFIX + ); + let etsOutputPath: string = changeDeclgenFileExtension( + path.join(moduleInfo.declgenBridgeCodePath ?? declBridgeOut, filePathFromModuleRoot), + TS_SUFFIX + ); + ensurePathExists(declEtsOutputPath); + ensurePathExists(etsOutputPath); + global.es2pandaPublic._GenerateTsDeclarationsFromContext(ctx, declEtsOutputPath, etsOutputPath, 1, 0, ''); + } finally { + this.destroyContext(cfg, ctx); + } + } + + getOffsetByColAndLine(filename: String, line: number, column: number): number { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getOffsetByColAndLine(ctx, line, column); + } finally { + this.destroyContext(cfg, ctx); + } + return ptr; + } + + getDefinitionAtPosition(filename: String, offset: number): LspDefinitionData { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getDefinitionAtPosition(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDefinitionData(ptr); + } + + getSemanticDiagnostics(filename: String): LspDiagsNode { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getSemanticDiagnostics(ctx); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDiagsNode(ptr); + } + + getCurrentTokenValue(filename: String, offset: number): string { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getCurrentTokenValue(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return unpackString(ptr); + } + + getImplementationAtPosition(filename: String, offset: number): LspDefinitionData { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getImplementationAtPosition(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDefinitionData(ptr); + } + + getFileReferences(filename: String): LspReferenceData[] { + let isPackageModule: boolean; + const [cfg, searchCtx] = this.createContext(filename); + try { + isPackageModule = global.es2panda._isPackageModule(searchCtx); + } finally { + this.destroyContext(cfg, searchCtx); + } + let result: LspReferenceData[] = []; + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(compileFiles[i]); + try { + ptr = global.es2panda._getFileReferences(path.resolve(filename.valueOf()), ctx, isPackageModule); + } finally { + this.destroyContext(cfg, ctx); + } + let refs = new LspReferences(ptr); + for (let j = 0; j < refs.referenceInfos.length; j++) { + if (refs.referenceInfos[j].fileName !== '') { + result.push(refs.referenceInfos[j]); + } + } + } + return result; + } + + getReferencesAtPosition(filename: String, offset: number): LspReferenceData[] { + let declInfo: KPointer; + const [cfg, searchCtx] = this.createContext(filename); + try { + declInfo = global.es2panda._getDeclInfo(searchCtx, offset); + } finally { + this.destroyContext(cfg, searchCtx); + } + let result: LspReferenceData[] = []; + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(compileFiles[i]); + try { + ptr = global.es2panda._getReferencesAtPosition(ctx, declInfo); + } finally { + this.destroyContext(cfg, ctx); + } + let refs = new LspReferences(ptr); + result.push(...refs.referenceInfos); + } + return Array.from(new Set(result)); + } + + getTypeHierarchies(filename: String, offset: number): LspTypeHierarchiesInfo | null { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + ptr = global.es2panda._getTypeHierarchies(ctx, ctx, offset); + let ref = new LspTypeHierarchiesInfo(ptr); + if (ref.fileName === '') { + this.destroyContext(cfg, ctx); + return null; + } + let result: LspTypeHierarchiesInfo[] = []; + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let searchPtr: KPointer; + const [cfg, searchCtx] = this.createContext(compileFiles[i]); + try { + searchPtr = global.es2panda._getTypeHierarchies(searchCtx, ctx, offset); + } finally { + this.destroyContext(cfg, searchCtx); + } + let refs = new LspTypeHierarchiesInfo(searchPtr); + if (i > 0) { + result[0].subHierarchies.subOrSuper = result[0].subHierarchies.subOrSuper.concat( + refs.subHierarchies.subOrSuper + ); + } else { + result.push(refs); + } + } + for (let j = 0; j < result[0].subHierarchies.subOrSuper.length; j++) { + let res = this.getTypeHierarchies( + result[0].subHierarchies.subOrSuper[j].fileName, + result[0].subHierarchies.subOrSuper[j].pos + ); + if (res !== null) { + let subOrSuperTmp = result[0].subHierarchies.subOrSuper[j].subOrSuper.concat(res.subHierarchies.subOrSuper); + result[0].subHierarchies.subOrSuper[j].subOrSuper = Array.from( + new Map( + subOrSuperTmp.map((item) => [`${item.fileName}-${item.type}-${item.pos}-${item.name}`, item]) + ).values() + ); + } + } + return result[0]; + } + + getClassHierarchyInfo(filename: String, offset: number): LspClassHierarchy { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getClassHierarchyInfo(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspClassHierarchy(ptr); + } + + getAliasScriptElementKind(filename: String, offset: number): LspCompletionEntryKind { + let kind: KInt; + const [cfg, ctx] = this.createContext(filename); + try { + kind = global.es2panda._getAliasScriptElementKind(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return kind; + } + + getClassHierarchies(filename: String, offset: number): LspClassHierarchies { + let contextList = []; + const [cfg, ctx] = this.createContext(filename); + contextList.push({ ctx: ctx, cfg: cfg }); + let nativeContextList = global.es2panda._pushBackToNativeContextVector(ctx, ctx, 1); + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + if (path.resolve(filename.valueOf()) === filePath) { + continue; + } + const [searchCfg, searchCtx] = this.createContext(filePath); + contextList.push({ ctx: searchCtx, cfg: searchCfg }); + global.es2panda._pushBackToNativeContextVector(searchCtx, nativeContextList, 0); + } + let ptr = global.es2panda._getClassHierarchies(nativeContextList, filename, offset); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + for (const { ctx, cfg } of contextList) { + this.destroyContext(cfg, ctx); + } + return new LspClassHierarchies(ptr); + } + + getClassPropertyInfo( + filename: String, + offset: number, + shouldCollectInherited: boolean = false + ): LspClassPropertyInfo { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getClassPropertyInfo(ctx, offset, shouldCollectInherited); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspClassPropertyInfo(ptr); + } + + getOrganizeImports(filename: String): LspFileTextChanges { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._organizeImports(ctx, filename); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspFileTextChanges(ptr); + } + + findSafeDeleteLocation(filename: String, offset: number): LspSafeDeleteLocationInfo[] { + let declInfo: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + declInfo = global.es2panda._getDeclInfo(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + let result: LspSafeDeleteLocationInfo[] = []; + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let ptr: KPointer; + const [searchCfg, searchCtx] = this.createContext(compileFiles[i]); + try { + ptr = global.es2panda._findSafeDeleteLocation(searchCtx, declInfo); + } finally { + this.destroyContext(searchCfg, searchCtx); + } + let refs = new LspSafeDeleteLocation(ptr); + result.push(...refs.safeDeleteLocationInfos); + } + return Array.from(new Set(result)); + } + + getCompletionEntryDetails(filename: String, offset: number, entryName: String): CompletionEntryDetails { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getCompletionEntryDetails(entryName, filename, ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new CompletionEntryDetails(ptr); + } + + getApplicableRefactors(filename: String, kind: String, offset: number): ApplicableRefactorItemInfo[] { + let ptr: KPointer; + let result: ApplicableRefactorItemInfo[] = []; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getApplicableRefactors(ctx, kind, offset); + } finally { + this.destroyContext(cfg, ctx); + } + let refs = new LspApplicableRefactorInfo(ptr); + result.push(...refs.applicableRefactorInfo); + return Array.from(new Set(result)); + } + + getClassConstructorInfo(filename: String, offset: number, properties: string[]): LspClassConstructorInfo { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getClassConstructorInfo(ctx, offset, passStringArray(properties)); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspClassConstructorInfo(ptr); + } + + getSyntacticDiagnostics(filename: String): LspDiagsNode { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename, false); + try { + ptr = global.es2panda._getSyntacticDiagnostics(ctx); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDiagsNode(ptr); + } + + getSuggestionDiagnostics(filename: String): LspDiagsNode { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getSuggestionDiagnostics(ctx); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDiagsNode(ptr); + } + + getQuickInfoAtPosition(filename: String, offset: number): LspQuickInfo { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getQuickInfoAtPosition(filename, ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspQuickInfo(ptr); + } + + getDocumentHighlights(filename: String, offset: number): LspDocumentHighlightsReferences { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getDocumentHighlights(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspDocumentHighlightsReferences(ptr); + } + + getCompletionAtPosition(filename: String, offset: number): LspCompletionInfo { + let lspDriverHelper = new LspDriverHelper(); + let filePath = path.resolve(filename.valueOf()); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; + let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); + let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); + let source = this.getFileSource(filePath); + // This is a temporary solution to support "obj." with wildcard for better solution in internal issue. + if (source[offset - 1] === '.') { + const wildcard = '_WILDCARD'; + if (offset < source.length + 1) { + source = source.slice(0, offset) + wildcard + source.slice(offset); + } else { + source += wildcard; + } + offset += wildcard.length; + } + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); + const packageName = this.moduleInfos[filePath].packageName; + const buildConfig = this.buildConfigs[packageName]; + PluginDriver.getInstance().getPluginContext().setCodingFilePath(filePath); + PluginDriver.getInstance().getPluginContext().setProjectConfig(buildConfig); + PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); + lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); + let ptr = global.es2panda._getCompletionAtPosition(localCtx, offset); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + lspDriverHelper.destroyContext(localCtx); + lspDriverHelper.destroyConfig(localCfg); + return new LspCompletionInfo(ptr); + } + + toLineColumnOffset(filename: String, offset: number): LspLineAndCharacter { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._toLineColumnOffset(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspLineAndCharacter(ptr); + } + + getSafeDeleteInfo(filename: String, position: number): boolean { + let result: boolean; + const [cfg, ctx] = this.createContext(filename); + try { + result = global.es2panda._getSafeDeleteInfo(ctx, position); + } finally { + this.destroyContext(cfg, ctx); + } + return result; + } + + findRenameLocations(filename: String, offset: number): LspRenameLocation[] { + let compileFiles = this.moduleInfos[path.resolve(filename.valueOf())].compileFiles; + const fileContexts: KPointer[] = []; + const fileConfigs: Config[] = []; + for (let i = 0; i < compileFiles.length; i++) { + const [compileFileCfg, compileFileCtx] = this.createContext(compileFiles[i]); + fileContexts.push(compileFileCtx); + fileConfigs.push(compileFileCfg); + } + const [cfg, ctx] = this.createContext(filename); + const ptr = global.es2panda._findRenameLocations(fileContexts.length, passPointerArray(fileContexts), ctx, offset); + const result: LspRenameLocation[] = new NativePtrDecoder().decode(ptr).map((elPeer: KPointer) => { + return new LspRenameLocation(elPeer); + }); + for (let i = 0; i < fileContexts.length; i++) { + this.destroyContext(fileConfigs[i], fileContexts[i]); + } + this.destroyContext(cfg, ctx); + return Array.from(new Set(result)); + } + + getRenameInfo(filename: String, offset: number): LspRenameInfoType { + let ptr: KPointer; + let res: LspRenameInfoType; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getRenameInfo(ctx, offset, this.pandaLibPath); + const success = global.es2panda._getRenameInfoIsSuccess(ptr); + if (success) { + res = new LspRenameInfoSuccess(global.es2panda._getRenameInfoSuccess(ptr)); + } else { + res = new LspRenameInfoFailure(global.es2panda._getRenameInfoFailure(ptr)); + } + } finally { + this.destroyContext(cfg, ctx); + } + return res; + } + + getSpanOfEnclosingComment(filename: String, offset: number, onlyMultiLine: boolean): LspTextSpan { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getSpanOfEnclosingComment(ctx, offset, onlyMultiLine); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspTextSpan(ptr); + } + + getCodeFixesAtPosition(filename: String, start: number, end: number, errorCodes: number[]): CodeFixActionInfo[] { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getCodeFixesAtPosition(ctx, start, end, new Int32Array(errorCodes), errorCodes.length); + } finally { + this.destroyContext(cfg, ctx); + } + const codeFixActionInfoList = new CodeFixActionInfoList(ptr); + const codeFixActionInfos: CodeFixActionInfo[] = []; + codeFixActionInfos.push(...codeFixActionInfoList.codeFixActionInfos); + return codeFixActionInfos; + } + + provideInlayHints(filename: String, span: TextSpan): LspInlayHint[] { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + const nativeSpan = global.es2panda._createTextSpan(span.start, span.length); + ptr = global.es2panda._getInlayHintList(ctx, nativeSpan); + } finally { + this.destroyContext(cfg, ctx); + } + const inlayHintList = new LspInlayHintList(ptr); + const inlayHints: LspInlayHint[] = []; + inlayHints.push(...inlayHintList.inlayHints); + return inlayHints; + } + + getSignatureHelpItems(filename: String, offset: number): LspSignatureHelpItems { + let ptr: KPointer; + const [cfg, ctx] = this.createContext(filename); + try { + ptr = global.es2panda._getSignatureHelpItems(ctx, offset); + } finally { + this.destroyContext(cfg, ctx); + } + return new LspSignatureHelpItems(ptr); + } + + // Use AST cache start + private getFileDependencies(inputs: string[], output: string): void { + let depInputContent = ''; + let outputFile: string = output; + let depAnalyzerPath: string = path.join(this.pandaBinPath, 'dependency_analyzer'); + let depInputFile = path.join(this.cacheDir, 'depInput.txt'); + inputs.forEach((file) => { + depInputContent += file + os.EOL; + }); + fs.writeFileSync(depInputFile, depInputContent); + ensurePathExists(outputFile); + const result = child_process.spawnSync( + depAnalyzerPath, + [`@${depInputFile}`, `--output=${output}`, `--arktsconfig=${this.entryArkTsConfig}`], + { + encoding: 'utf-8', + windowsHide: true + } + ); + if (result.error || result.status !== 0) { + console.error('getFileDependencies failed: ', result.stderr || result.error); + } + } + + // Collect circular dependencies, like: A→B→C→A + private findStronglyConnectedComponents(graph: FileDepsInfo): Map> { + const adjacencyList: Record = {}; + const reverseAdjacencyList: Record = {}; + const allNodes = new Set(); + + for (const node in graph.dependencies) { + allNodes.add(node); + graph.dependencies[node].forEach((dep) => allNodes.add(dep)); + } + for (const node in graph.dependants) { + allNodes.add(node); + graph.dependants[node].forEach((dep) => allNodes.add(dep)); + } + + Array.from(allNodes).forEach((node) => { + adjacencyList[node] = graph.dependencies[node] || []; + reverseAdjacencyList[node] = graph.dependants[node] || []; + }); + + const visited = new Set(); + const order: string[] = []; + + function dfs(node: string): void { + visited.add(node); + for (const neighbor of adjacencyList[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + order.push(node); + } + + Array.from(allNodes).forEach((node) => { + if (!visited.has(node)) { + dfs(node); + } + }); + + visited.clear(); + const components = new Map>(); + + function reverseDfs(node: string, component: Set): void { + visited.add(node); + component.add(node); + for (const neighbor of reverseAdjacencyList[node]) { + if (!visited.has(neighbor)) { + reverseDfs(neighbor, component); + } + } + } + + for (let i = order.length - 1; i >= 0; i--) { + const node = order[i]; + if (!visited.has(node)) { + const component = new Set(); + reverseDfs(node, component); + if (component.size > 1) { + const sortedFiles = Array.from(component).sort(); + const hashKey = createHash(sortedFiles.join('|')); + components.set(hashKey, component); + } + } + } + + return components; + } + + private getJobDependencies(fileDeps: string[], cycleFiles: Map): Set { + let depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!cycleFiles.has(file)) { + depJobList.add('0' + file); + } else { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } + }); + + return depJobList; + } + + private getJobDependants(fileDeps: string[], cycleFiles: Map): Set { + let depJobList: Set = new Set(); + fileDeps.forEach((file) => { + if (!file.endsWith(DECL_ETS_SUFFIX)) { + depJobList.add('1' + file); + } + if (cycleFiles.has(file)) { + cycleFiles.get(file)?.forEach((f) => { + depJobList.add(f); + }); + } else { + depJobList.add('0' + file); + } + }); + + return depJobList; + } + + private collectCompileJobs(jobs: Record, isValid: boolean = false): void { + let entryFileList: string[] = Object.keys(this.moduleInfos); + this.getFileDependencies(entryFileList, this.fileDependencies); + const data = fs.readFileSync(this.fileDependencies, 'utf-8'); + let fileDepsInfo: FileDepsInfo = JSON.parse(data) as FileDepsInfo; + + Object.keys(fileDepsInfo.dependants).forEach((file) => { + if (!(file in fileDepsInfo.dependencies)) { + fileDepsInfo.dependencies[file] = []; + } + }); + + let cycleGroups = this.findStronglyConnectedComponents(fileDepsInfo); + + let cycleFiles: Map = new Map(); + cycleGroups.forEach((value: Set, key: string) => { + value.forEach((file) => { + cycleFiles.set(file, [key]); + }); + }); + + Object.entries(fileDepsInfo.dependencies).forEach(([key, value]) => { + let dependencies = this.getJobDependencies(value, cycleFiles); + if (cycleFiles.has(key)) { + const externalProgramJobIds = cycleFiles.get(key)!; + externalProgramJobIds.forEach((id) => { + let fileList: string[] = Array.from(cycleGroups.get(id)!); + this.createExternalProgramJob(id, fileList, jobs, dependencies, isValid, true); + }); + } else { + const id = '0' + key; + let fileList: string[] = [key]; + this.createExternalProgramJob(id, fileList, jobs, dependencies, isValid); + } + }); + + Object.entries(fileDepsInfo.dependants).forEach(([key, value]) => { + const dependants = this.getJobDependants(value, cycleFiles); + const jobIds = cycleFiles.has(key) ? cycleFiles.get(key)! : ['0' + key]; + + jobIds.forEach((jobId) => { + const currentDependants = new Set(dependants); + jobs[jobId].dependants.forEach((dep) => currentDependants.add(dep)); + currentDependants.delete(jobId); + jobs[jobId].dependants = Array.from(currentDependants); + }); + }); + } + + private createExternalProgramJob( + id: string, + fileList: string[], + jobs: Record, + dependencies: Set, + isValid: boolean, + isInCycle?: boolean + ): void { + if (dependencies.has(id)) { + dependencies.delete(id); + } + if (jobs[id]) { + const existingJob = jobs[id]; + const mergedFileList = [...new Set([...existingJob.fileList, ...fileList])]; + const mergedDependencies = [...new Set([...existingJob.dependencies, ...Array.from(dependencies)])]; + const mergedIsInCycle = existingJob.isInCycle || isInCycle; + + existingJob.fileList = mergedFileList; + existingJob.dependencies = mergedDependencies; + existingJob.isInCycle = mergedIsInCycle; + } else { + jobs[id] = { + id, + fileList, + isDeclFile: true, + isInCycle, + dependencies: Array.from(dependencies), + dependants: [], + isValid + }; + } + } + + private addJobToQueues(job: Job, queues: Job[]): void { + if (queues.some((j) => j.id === job.id)) { + return; + } + queues.push(job); + } + + private initCompileQueues(jobs: Record, queues: Job[], dependantJobs?: Record): void { + Object.values(jobs).forEach((job) => { + if (job.dependencies.length === 0) { + if (dependantJobs && job.id in dependantJobs) { + job.isValid = false; + this.invalidateFileCache(job.fileList); + } + this.addJobToQueues(job, queues); + } + }); + } + + private checkAllTasksDone(queues: Job[], workerPool: WorkerInfo[]): boolean { + if (queues.length === 0) { + for (let i = 0; i < workerPool.length; i++) { + if (!workerPool[i].isIdle) { + return false; + } + } + return true; + } + return false; + } + + private initGlobalContext(jobs: Record): void { + let files: string[] = []; + Object.entries(jobs).forEach(([key, job]) => { + for (let i = 0; i < job.fileList.length; i++) { + files.push(job.fileList[i]); + } + }); + + let ets2pandaCmd: string[] = [ + '_', + '--extension', + 'ets', + '--arktsconfig', + this.entryArkTsConfig, + Object.keys(this.moduleInfos)[0] + ]; + + this.globalLspDriverHelper = new LspDriverHelper(); + this.globalLspDriverHelper.memInitialize(this.pandaLibPath); + this.globalConfig = this.globalLspDriverHelper.createCfg(ets2pandaCmd, files[0], this.pandaLibPath); + this.globalContextPtr = this.globalLspDriverHelper.createGlobalContext(this.globalConfig.peer, files, files.length); + } + + private updateQueues( + jobs: Record, + queues: Job[], + jobId: string, + dependantJobs?: Record + ): void { + const completedJob = jobs[jobId]; + completedJob.dependants.forEach((depJobId) => { + const depJob = jobs[depJobId]; + if (!depJob) { + return; + } + const depIndex = depJob.dependencies.indexOf(jobId); + if (depIndex === -1) { + return; + } + depJob.dependencies.splice(depIndex, 1); + if (depJob.dependencies.length > 0) { + return; + } + this.processCompletedDependencies(depJob, queues, dependantJobs); + }); + } + + private processCompletedDependencies(depJob: Job, queues: Job[], dependantJobs?: Record): void { + if (dependantJobs && depJob.id in dependantJobs) { + depJob.isValid = false; + this.invalidateFileCache(depJob.fileList); + } + this.addJobToQueues(depJob, queues); + } + + private invalidateFileCache(fileList: string[]): void { + fileList.forEach((file) => { + global.es2pandaPublic._InvalidateFileCache(this.globalContextPtr!, file); + }); + } + + private async invokeWorkers( + jobs: Record, + queues: Job[], + processingJobs: Set, + workers: ThreadWorker[], + numWorkers: number, + dependantJobs?: Record + ): Promise { + return new Promise((resolve) => { + const workerPool = this.createWorkerPool(numWorkers, workers); + + workerPool.forEach((workerInfo) => { + this.setupWorkerListeners(workerInfo.worker, workerPool, jobs, queues, processingJobs, resolve, dependantJobs); + this.assignTaskToIdleWorker(workerInfo, queues, processingJobs); + }); + }); + } + + private createWorkerPool(numWorkers: number, workers: ThreadWorker[]): WorkerInfo[] { + const workerPool: WorkerInfo[] = []; + + for (let i = 0; i < numWorkers; i++) { + const worker = new ThreadWorker(path.resolve(__dirname, 'compile_thread_worker.js'), { + workerData: { workerId: i } + }); + workers.push(worker); + workerPool.push({ worker, isIdle: true }); + } + + return workerPool; + } + + private setupWorkerListeners( + worker: ThreadWorker, + workerPool: WorkerInfo[], + jobs: Record, + queues: Job[], + processingJobs: Set, + resolve: () => void, + dependantJobs?: Record + ): void { + worker.on('message', (msg) => { + if (msg.type !== 'TASK_FINISH') { + return; + } + + this.handleTaskCompletion(msg.jobId, worker, workerPool, jobs, queues, processingJobs, dependantJobs); + + if (this.checkAllTasksDone(queues, workerPool)) { + this.terminateWorkers(workerPool); + resolve(); + } + }); + } + + private handleTaskCompletion( + jobId: string, + worker: ThreadWorker, + workerPool: WorkerInfo[], + jobs: Record, + queues: Job[], + processingJobs: Set, + dependantJobs?: Record + ): void { + const workerInfo = workerPool.find((w) => w.worker === worker); + if (workerInfo) { + workerInfo.isIdle = true; + } + processingJobs.delete(jobId); + this.updateQueues(jobs, queues, jobId, dependantJobs); + workerPool.forEach((workerInfo) => { + if (workerInfo.isIdle) { + this.assignTaskToIdleWorker(workerInfo, queues, processingJobs); + } + }); + } + + private terminateWorkers(workerPool: WorkerInfo[]): void { + workerPool.forEach(({ worker }) => { + worker.postMessage({ type: 'EXIT' }); + }); + } + + private assignTaskToIdleWorker(workerInfo: WorkerInfo, queues: Job[], processingJobs: Set): void { + let job: Job | undefined; + let jobInfo: JobInfo | undefined; + + if (queues.length > 0) { + job = queues.shift()!; + jobInfo = { + id: job.id, + filePath: job.fileList[0], + arktsConfigFile: this.entryArkTsConfig, + globalContextPtr: this.globalContextPtr!, + buildConfig: Object.values(this.buildConfigs)[0], + isValid: job.isValid + }; + } + + if (job) { + processingJobs.add(job.id); + workerInfo.worker.postMessage({ type: 'ASSIGN_TASK', jobInfo }); + workerInfo.isIdle = false; + } + } + + // AST caching is not enabled by default. + // Call `initAstCache` before invoking the language service interface to enable AST cache + public async initAstCache(numWorkers: number = 1): Promise { + const jobs: Record = {}; + const queues: Job[] = []; + this.collectCompileJobs(jobs); + this.initGlobalContext(jobs); + this.initCompileQueues(jobs, queues); + + const processingJobs = new Set(); + const workers: ThreadWorker[] = []; + await this.invokeWorkers(jobs, queues, processingJobs, workers, numWorkers); + } + + private compileExternalProgram(jobInfo: JobInfo): void { + PluginDriver.getInstance().initPlugins(jobInfo.buildConfig); + let ets2pandaCmd = ['-', '--extension', 'ets', '--arktsconfig', jobInfo.arktsConfigFile]; + let lspDriverHelper = new LspDriverHelper(); + let config = lspDriverHelper.createCfg(ets2pandaCmd, jobInfo.filePath); + const source = fs.readFileSync(jobInfo.filePath, 'utf8').replace(/\r\n/g, '\n'); + let context = lspDriverHelper.createCtx(source, jobInfo.filePath, config, jobInfo.globalContextPtr, true); + PluginDriver.getInstance().getPluginContext().setContextPtr(context); + lspDriverHelper.proceedToState(context, Es2pandaContextState.ES2PANDA_STATE_PARSED); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + lspDriverHelper.proceedToState(context, Es2pandaContextState.ES2PANDA_STATE_LOWERED); + } + + public addFileCache(filename: String): void { + global.es2pandaPublic._AddFileCache(this.globalContextPtr!, filename); + let jobInfo = { + id: filename.valueOf(), + filePath: filename.valueOf(), + arktsConfigFile: this.entryArkTsConfig, + globalContextPtr: this.globalContextPtr!, + buildConfig: Object.values(this.buildConfigs)[0], + isValid: true + }; + this.compileExternalProgram(jobInfo); + } + + public removeFileCache(filename: String): void { + global.es2pandaPublic._RemoveFileCache(this.globalContextPtr!, filename); + } + + public async updateFileCache(filename: String, numWorkers: number = 1): Promise { + const queues: Job[] = []; + const jobs: Record = {}; + this.collectCompileJobs(jobs, true); + const dependantJobs = this.findJobDependants(jobs, filename.valueOf()); + this.initCompileQueues(jobs, queues, dependantJobs); + const processingJobs = new Set(); + const workers: ThreadWorker[] = []; + await this.invokeWorkers(jobs, queues, processingJobs, workers, numWorkers, dependantJobs); + } + + private findJobDependants(jobs: Record, filePath: string): Record { + const targetJobs = this.findTargetJobs(jobs, filePath); + const { visited, dependantJobs } = this.collectDependantJobs(jobs, targetJobs); + + return this.mergeJobs(targetJobs, dependantJobs); + } + + private findTargetJobs(jobs: Record, filePath: string): Job[] { + return Object.values(jobs).filter( + (job) => job.fileList.includes(filePath) || (job.isInCycle && job.fileList.some((f) => f === filePath)) + ); + } + + private collectDependantJobs( + jobs: Record, + targetJobs: Job[] + ): { visited: Set; dependantJobs: Map } { + const visited = new Set(); + const dependantJobs = new Map(); + const queue: Job[] = []; + + targetJobs.forEach((job) => { + if (!visited.has(job.id)) { + visited.add(job.id); + queue.push(job); + } + + while (queue.length) { + const current = queue.shift()!; + this.processDependants(jobs, current, visited, queue, dependantJobs); + } + }); + + return { visited, dependantJobs }; + } + + private processDependants( + jobs: Record, + current: Job, + visited: Set, + queue: Job[], + dependantJobs: Map + ): void { + current.dependants.forEach((dependantId) => { + const dependantJob = jobs[dependantId]; + if (dependantJob && !visited.has(dependantId)) { + visited.add(dependantId); + queue.push(dependantJob); + dependantJobs.set(dependantId, dependantJob); + } + }); + } + + private mergeJobs(targetJobs: Job[], dependantJobs: Map): Record { + return [...targetJobs, ...dependantJobs.values()].reduce( + (acc, job) => { + acc[job.id] = job; + return acc; + }, + {} as Record + ); + } + + public dispose(): void { + this.globalLspDriverHelper!.destroyGlobalContext(this.globalContextPtr!); + this.globalLspDriverHelper!.destroyConfig(this.globalConfig!); + this.globalLspDriverHelper!.memFinalize(); + } +} + +function createHash(str: string): string { + const hash = crypto.createHash('sha256'); + hash.update(str); + return hash.digest('hex'); +} +// Use AST cache end diff --git a/ets2panda/bindings/src/lsp_helper.ts b/ets2panda/bindings/src/lsp_helper.ts deleted file mode 100644 index 1731499f55c2c8b98a3955a1195155943d4c5efc..0000000000000000000000000000000000000000 --- a/ets2panda/bindings/src/lsp_helper.ts +++ /dev/null @@ -1,768 +0,0 @@ -/* - * 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. - */ - -import { LspDriverHelper } from './driver_helper'; -import { global } from './global'; -import { - LspDefinitionData, - LspDiagsNode, - LspReferences, - LspQuickInfo, - LspClassHierarchy, - LspCompletionEntryKind, - LspClassPropertyInfo, - LspClassHierarchies, - LspDocumentHighlightsReferences, - LspCompletionInfo, - LspReferenceLocationList, - LspLineAndCharacter, - LspReferenceData, - LspClassConstructorInfo, - ApplicableRefactorItemInfo, - LspApplicableRefactorInfo, - CompletionEntryDetails, - LspFileTextChanges, - LspSafeDeleteLocationInfo, - LspSafeDeleteLocation, - LspTypeHierarchiesInfo, - LspTextSpan, - LspInlayHint, - LspInlayHintList, - TextSpan, - LspSignatureHelpItems, - CodeFixActionInfo, - CodeFixActionInfoList -} from './lspNode'; -import { passStringArray, unpackString } from './private'; -import { Es2pandaContextState } from './generated/Es2pandaEnums'; -import { BuildConfig } from './types'; -import { PluginDriver, PluginHook } from './ui_plugins_driver'; -import { ModuleDescriptor } from './buildConfigGenerate'; -import { generateArkTsConfigByModules } from './arktsConfigGenerate'; - -import * as fs from 'fs'; -import * as path from 'path'; - -const ets2pandaCmdPrefix = ['-', '--extension', 'ets', '--arktsconfig']; - -function initBuildEnv(): void { - const currentPath: string | undefined = process.env.PATH; - let pandaLibPath: string = path.resolve(__dirname, '../../ets2panda/lib'); - process.env.PATH = `${currentPath}${path.delimiter}${pandaLibPath}`; -} - -export interface TextDocumentChangeInfo { - newDoc: string; - rangeStart?: number; - rangeEnd?: number; - updateText?: string; -} - -export class Lsp { - private pandaLibPath: string; - private projectPath: string; - private fileNameToArktsconfig: Record; // Map - private moduleToBuildConfig: Record; // Map - private getFileContent: (filePath: string) => string; - private filesMap: Map; // Map - - constructor(projectPath: string, getContentCallback?: (filePath: string) => string) { - initBuildEnv(); - this.pandaLibPath = path.resolve(__dirname, '../../ets2panda/lib'); - this.projectPath = projectPath; - let compileFileInfoPath = path.join(projectPath, '.idea', '.deveco', 'lsp_compileFileInfos.json'); - this.fileNameToArktsconfig = JSON.parse(fs.readFileSync(compileFileInfoPath, 'utf-8')); - let buildConfigPath = path.join(projectPath, '.idea', '.deveco', 'lsp_build_config.json'); - this.moduleToBuildConfig = JSON.parse(fs.readFileSync(buildConfigPath, 'utf-8')); - this.filesMap = new Map(); - this.getFileContent = getContentCallback || ((path: string): string => fs.readFileSync(path, 'utf8')); - } - - modifyFilesMap(fileName: string, fileContent: TextDocumentChangeInfo): void { - this.filesMap.set(fileName, fileContent.newDoc); - } - - deleteFromFilesMap(fileName: string): void { - this.filesMap.delete(fileName); - } - - updateConfig(buildSdkPath: string, modules?: ModuleDescriptor[]): void { - generateArkTsConfigByModules(buildSdkPath, this.projectPath, modules); - let compileFileInfoPath = path.join(this.projectPath, '.idea', '.deveco', 'lsp_compileFileInfos.json'); - this.fileNameToArktsconfig = JSON.parse(fs.readFileSync(compileFileInfoPath, 'utf-8')); - let buildConfigPath = path.join(this.projectPath, '.idea', '.deveco', 'lsp_build_config.json'); - this.moduleToBuildConfig = JSON.parse(fs.readFileSync(buildConfigPath, 'utf-8')); - } - - private getFileSource(filePath: string): string { - const getSource = this.filesMap.get(filePath) || this.getFileContent(filePath); - if (!getSource) { - throw new Error(`File content not found for path: ${filePath}`); - } - return getSource.replace(/\r\n/g, '\n'); - } - - private getModuleNameFromFilename(filePath: string): string { - const projectRoot = this.projectPath; - if (!filePath.startsWith(projectRoot)) { - return ''; - } - const relativePath = path.relative(projectRoot, filePath); - const parts = relativePath.split(path.sep); - return parts[0] || ''; - } - - getDefinitionAtPosition(filename: String, offset: number): LspDefinitionData { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getDefinitionAtPosition(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDefinitionData(ptr); - } - - getSemanticDiagnostics(filename: String): LspDiagsNode { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let pandaLibPath: string = path.resolve(__dirname, '../../ets2panda/lib'); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSemanticDiagnostics(localCtx); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDiagsNode(ptr); - } - - getCurrentTokenValue(filename: String, offset: number): string { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getCurrentTokenValue(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return unpackString(ptr); - } - - getImplementationAtPosition(filename: String, offset: number): LspDefinitionData { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getImplementationAtPosition(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDefinitionData(ptr); - } - - getFileReferences(filename: String): LspReferenceData[] { - let lspDriverHelper = new LspDriverHelper(); - let searchFilePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[searchFilePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, searchFilePath, this.pandaLibPath); - const source = this.getFileSource(searchFilePath); - let localCtx = lspDriverHelper.createCtx(source, searchFilePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let isPackageModule = global.es2panda._isPackageModule(localCtx); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let result: LspReferenceData[] = []; - let moduleName = path.basename(path.dirname(arktsconfig)); - let buildConfig: BuildConfig = this.moduleToBuildConfig[moduleName]; - for (let i = 0; i < buildConfig.compileFiles.length; i++) { - let filePath = path.resolve(buildConfig.compileFiles[i]); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getFileReferences(searchFilePath, localCtx, isPackageModule); - let refs = new LspReferences(ptr); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - for (let j = 0; j < refs.referenceInfos.length; j++) { - if (refs.referenceInfos[j].fileName !== '') { - result.push(refs.referenceInfos[j]); - } - } - } - return result; - } - - getReferencesAtPosition(filename: String, offset: number): LspReferenceData[] { - let lspDriverHelper = new LspDriverHelper(); - let searchFilePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[searchFilePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, searchFilePath, this.pandaLibPath); - const source = this.getFileSource(searchFilePath); - let localCtx = lspDriverHelper.createCtx(source, searchFilePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let declInfo = global.es2panda._getDeclInfo(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let result: LspReferenceData[] = []; - let moduleName = path.basename(path.dirname(arktsconfig)); - let buildConfig: BuildConfig = this.moduleToBuildConfig[moduleName]; - for (let i = 0; i < buildConfig.compileFiles.length; i++) { - let filePath = path.resolve(buildConfig.compileFiles[i]); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getReferencesAtPosition(localCtx, declInfo); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let refs = new LspReferences(ptr); - result.push(...refs.referenceInfos); - } - return Array.from(new Set(result)); - } - - getTypeHierarchies(filename: String, offset: number): LspTypeHierarchiesInfo | null { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getTypeHierarchies(localCtx, localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - let ref = new LspTypeHierarchiesInfo(ptr); - if (ref.fileName === '') { - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return null; - } - let result: LspTypeHierarchiesInfo[] = []; - let moduleName = path.basename(path.dirname(arktsconfig)); - let buildConfig: BuildConfig = this.moduleToBuildConfig[moduleName]; - for (let i = 0; i < buildConfig.compileFiles.length; i++) { - let filePath = path.resolve(buildConfig.compileFiles[i]); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let searchCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let searchCtx = lspDriverHelper.createCtx(source, filePath, searchCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(searchCtx); - lspDriverHelper.proceedToState(searchCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(searchCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getTypeHierarchies(searchCtx, localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(searchCtx); - lspDriverHelper.destroyConfig(searchCfg); - let refs = new LspTypeHierarchiesInfo(ptr); - if (i > 0) { - result[0].subHierarchies.subOrSuper = result[0].subHierarchies.subOrSuper.concat(refs.subHierarchies.subOrSuper); - } else { - result.push(refs); - } - } - for (let j = 0; j < result[0].subHierarchies.subOrSuper.length; j++) { - let res = this.getTypeHierarchies(result[0].subHierarchies.subOrSuper[j].fileName, result[0].subHierarchies.subOrSuper[j].pos); - if (res !== null) { - let subOrSuperTmp = result[0].subHierarchies.subOrSuper[j].subOrSuper.concat(res.subHierarchies.subOrSuper); - result[0].subHierarchies.subOrSuper[j].subOrSuper = Array.from( - new Map(subOrSuperTmp.map(item => [`${item.fileName}-${item.type}-${item.pos}-${item.name}`, item])).values() - ); - } - } - return result[0]; - } - - getClassHierarchyInfo(filename: String, offset: number): LspClassHierarchy { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getClassHierarchyInfo(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspClassHierarchy(ptr); - } - - getAliasScriptElementKind(filename: String, offset: number): LspCompletionEntryKind { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let kind = global.es2panda._getAliasScriptElementKind(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return kind; - } - - getClassHierarchies(filename: String, offset: number): LspClassHierarchies { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getClassHierarchies(localCtx, filename, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspClassHierarchies(ptr); - } - - getClassPropertyInfo(filename: String, offset: number, shouldCollectInherited: boolean = false): LspClassPropertyInfo { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getClassPropertyInfo(localCtx, offset, shouldCollectInherited); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspClassPropertyInfo(ptr); - } - - getOrganizeImports(filename: String): LspFileTextChanges { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._organizeImports(localCtx, filename); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspFileTextChanges(ptr); - } - - findSafeDeleteLocation(filename: String, offset: number): LspSafeDeleteLocationInfo[] { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let declInfo = global.es2panda._getDeclInfo(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let result: LspSafeDeleteLocationInfo[] = []; - let moduleName = path.basename(path.dirname(arktsconfig)); - let buildConfig: BuildConfig = this.moduleToBuildConfig[moduleName]; - for (let i = 0; i < buildConfig.compileFiles.length; i++) { - let filePath = path.resolve(buildConfig.compileFiles[i]); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._findSafeDeleteLocation(localCtx, declInfo); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let refs = new LspSafeDeleteLocation(ptr); - result.push(...refs.safeDeleteLocationInfos); - } - return Array.from(new Set(result)); - } - - getCompletionEntryDetails(filename: String, offset: number, entryName: String): CompletionEntryDetails { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getCompletionEntryDetails(entryName, filename, localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new CompletionEntryDetails(ptr); - } - - getApplicableRefactors(filename: String, kind: String, offset: number): ApplicableRefactorItemInfo[] { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let result: ApplicableRefactorItemInfo[] = []; - let ptr = global.es2panda._getApplicableRefactors(localCtx, kind, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - let refs = new LspApplicableRefactorInfo(ptr); - result.push(...refs.applicableRefactorInfo); - return Array.from(new Set(result)); - } - - getClassConstructorInfo(filename: String, offset: number, properties: string[]): LspClassConstructorInfo { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getClassConstructorInfo(localCtx, offset, passStringArray(properties)); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspClassConstructorInfo(ptr); - } - - getSyntacticDiagnostics(filename: String): LspDiagsNode { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - const moduleName = this.getModuleNameFromFilename(filePath); - const buildConfig = this.moduleToBuildConfig[moduleName]; - PluginDriver.getInstance().getPluginContext().setProjectConfig(buildConfig); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSyntacticDiagnostics(localCtx); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDiagsNode(ptr); - } - - getSuggestionDiagnostics(filename: String): LspDiagsNode { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSuggestionDiagnostics(localCtx); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDiagsNode(ptr); - } - - getQuickInfoAtPosition(filename: String, offset: number): LspQuickInfo { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getQuickInfoAtPosition(filename, localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspQuickInfo(ptr); - } - - getDocumentHighlights(filename: String, offset: number): LspDocumentHighlightsReferences { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getDocumentHighlights(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspDocumentHighlightsReferences(ptr); - } - - getCompletionAtPosition(filename: String, offset: number): LspCompletionInfo { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - let source = this.getFileSource(filePath); - // This is a temporary solution to support "obj." with wildcard for better solution in internal issue. - if (source[offset - 1] === '.') { - const wildcard = '_WILDCARD'; - if (offset < source.length + 1) { - source = source.slice(0, offset) + wildcard + source.slice(offset); - } else { - source += wildcard; - } - offset += wildcard.length; - } - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getCompletionAtPosition(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspCompletionInfo(ptr); - } - - toLineColumnOffset(filename: String, offset: number): LspLineAndCharacter { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._toLineColumnOffset(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspLineAndCharacter(ptr); - } - - getSafeDeleteInfo(filename: String, position: number): boolean { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let result = global.es2panda._getSafeDeleteInfo(localCtx, position); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return result; - } - - getSpanOfEnclosingComment(filename: String, offset: number, onlyMultiLine: boolean): LspTextSpan { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSpanOfEnclosingComment(localCtx, offset, onlyMultiLine); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspTextSpan(ptr); - } - - getCodeFixesAtPosition(filename: String, start: number, end: number, errorCodes: number[]): CodeFixActionInfo[] { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getCodeFixesAtPosition(localCtx, start, end, new Int32Array(errorCodes), errorCodes.length); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - const codeFixActionInfoList = new CodeFixActionInfoList(ptr); - const codeFixActionInfos: CodeFixActionInfo[] = []; - codeFixActionInfos.push(...codeFixActionInfoList.codeFixActionInfos); - return codeFixActionInfos; - } - - provideInlayHints(filename: String, span: TextSpan): LspInlayHint[] { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - const nativeSpan = global.es2panda._createTextSpan(span.start, span.length); - let ptr = global.es2panda._getInlayHintList(localCtx, nativeSpan); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - const inlayHintList = new LspInlayHintList(ptr); - const inlayHints: LspInlayHint[] = []; - inlayHints.push(...inlayHintList.inlayHints); - return inlayHints; - } - - getSignatureHelpItems(filename: String, offset: number): LspSignatureHelpItems { - let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; - let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); - PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); - PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); - lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); - let ptr = global.es2panda._getSignatureHelpItems(localCtx, offset); - PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); - return new LspSignatureHelpItems(ptr); - } -} diff --git a/ets2panda/bindings/test/BUILD.gn b/ets2panda/bindings/test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..83a299be121b1c897c8351347ee6ac00f76f1524 --- /dev/null +++ b/ets2panda/bindings/test/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +import("//build/version.gni") + +action("bindings_test") { + script = "run_bindings.sh" + args = [ + rebase_path("./"), + rebase_path( + "//prebuilts/build-tools/common/nodejs/node-v16.20.2-linux-x64/bin"), + rebase_path("//prebuilts/ohos-sdk/linux/${api_version}/ets"), + ] + outputs = [ "$target_out_dir/$target_name.timestamp" ] +} diff --git a/ets2panda/bindings/test/README.md b/ets2panda/bindings/test/README.md index fc51773ca6c6a0be35ea3d1cbf17fc903672278b..bce307bd3a7c4b9e782c71c91208b2dd33a84999 100644 --- a/ets2panda/bindings/test/README.md +++ b/ets2panda/bindings/test/README.md @@ -1,26 +1,19 @@ ### How to run bindings test? +first, you need download a SDK package, then unzip ets component into `bindings/test/` directory. +Download SDK package from [here](http://ci.openharmony.cn/workbench/cicd/dailybuild/dailylist). + ```Bash +unzip /path/to/ets-xxx.zip -d /path/to/bindings/test/ cd /path/to/bindings -### check environment -# if you are using Linux shell -bash test/prepare.sh -# or if you are using Windows shell -powershell -f test/prepare.ps1 - ### run test npm install npm run test - -### restore the path to the original state -bash test/prepare.sh --restore -# or -powershell -f test/prepare.ps1 --restore ``` #### tips -1. If you want to update a lot of expected results, you can use `node dist-test/test/run_tests.js ./test --update` +1. If you want to update a lot of expected results, you can use `npm run test:update` to update all expected results. ### testcase directory structure . @@ -35,7 +28,6 @@ powershell -f test/prepare.ps1 --restore    │   │   └── arktsconfig.json    │   ├── lsp_build_config.json    │   └── lsp_compileFileInfos.json -    ├── cases.json    └── exampleFuncName       └── exampleFuncName1.ets @@ -60,7 +52,3 @@ case.ts: 2. add exampleFuncName2 field in `cases.ts` file 3. add exampleFuncName2.json in `expected` directory 4. add a new test case according to the above steps - -⚠️⚠️⚠️ -Before push your code, please make sure that the path formats in all JSON files under the testcases directory are correct. -Incorrect path formats will render the function of prepare.sh ineffective, and manually handling the paths can be quite annoying. \ No newline at end of file diff --git a/ets2panda/bindings/test/cases.ts b/ets2panda/bindings/test/cases.ts index e6d5f4da8d118151c4a2e3d66d46adff044cb969..15266da171f757ec0910f949ad9170fe8bef5483 100644 --- a/ets2panda/bindings/test/cases.ts +++ b/ets2panda/bindings/test/cases.ts @@ -14,7 +14,7 @@ */ import path from 'path'; -import { TextSpan } from '../src/lspNode'; +import { TextSpan } from '../src/lsp/lspNode'; export interface TestConfig { expectedFilePath: string; @@ -32,7 +32,7 @@ function resolveTestPath(relativePath: string): string { return path.join(PROJECT_ROOT, relativePath); } -export const testCases: TestCases = { +export const basicCases: TestCases = { getDefinitionAtPosition: { expectedFilePath: resolveTestPath('test/expected/getDefinitionAtPosition.json'), '1': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition2.ets'), 655], @@ -49,29 +49,38 @@ export const testCases: TestCases = { getSemanticDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSemanticDiagnostics.json'), '1': [resolveTestPath('test/testcases/getSemanticDiagnostics/getSemanticDiagnostics1.ets')], - '2': [resolveTestPath('test/testcases/getSemanticDiagnostics/getSemanticDiagnostics2.ets')] + '2': [resolveTestPath('test/testcases/getSemanticDiagnostics/getSemanticDiagnostics2.ets')], + '3': [resolveTestPath('test/testcases/getSemanticDiagnostics/getSemanticDiagnostics3.ets')] }, getCurrentTokenValue: { expectedFilePath: resolveTestPath('test/expected/getCurrentTokenValue.json'), '1': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue1.ets'), 611], '2': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue2.ets'), 612], '3': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue3.ets'), 612], - '4': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue4.ets'), 611] + '4': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue4.ets'), 611], + '5': [resolveTestPath('test/testcases/getCurrentTokenValue/getCurrentTokenValue5.ets'), 697] }, getFileReferences: { expectedFilePath: resolveTestPath('test/expected/getFileReferences.json'), '1': [resolveTestPath('test/testcases/getFileReferences/getFileReferences1_export.ets')] }, + getFileSource: { + expectedFilePath: resolveTestPath('test/expected/getFileSource.json'), + '1': [resolveTestPath('test/testcases/getFileSource/getFileSource1.ets')] + }, getReferencesAtPosition: { expectedFilePath: resolveTestPath('test/expected/getReferencesAtPosition.json'), '1': [resolveTestPath('test/testcases/getReferencesAtPosition/getReferencesAtPosition1.ets'), 613], '2': [resolveTestPath('test/testcases/getReferencesAtPosition/getReferencesAtPosition2.ets'), 635], - '3': [resolveTestPath('test/testcases/getReferencesAtPosition/getReferencesAtPosition4.ets'), 625] + '3': [resolveTestPath('test/testcases/getReferencesAtPosition/getReferencesAtPosition4.ets'), 625], + '4': [resolveTestPath('test/testcases/getReferencesAtPosition/getReferencesAtPosition6.ets'), 697] }, getSyntacticDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSyntacticDiagnostics.json'), '1': [resolveTestPath('test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics1.ets')], - '2': [resolveTestPath('test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics2.ets')] + '2': [resolveTestPath('test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics2.ets')], + '3': [resolveTestPath('test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics3.ets')], + '4': [resolveTestPath('test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics4.ets')] }, getSuggestionDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSuggestionDiagnostics.json'), @@ -81,7 +90,8 @@ export const testCases: TestCases = { expectedFilePath: resolveTestPath('test/expected/getQuickInfoAtPosition.json'), '1': [resolveTestPath('test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition1.ets'), 626], '2': [resolveTestPath('test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition2.ets'), 618], - '3': [resolveTestPath('test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition3.ets'), 663] + '3': [resolveTestPath('test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition3.ets'), 663], + '4': [resolveTestPath('test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition4.ets'), 697] }, getDocumentHighlights: { expectedFilePath: resolveTestPath('test/expected/getDocumentHighlights.json'), @@ -91,7 +101,8 @@ export const testCases: TestCases = { '4': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights4.ets'), 626], '5': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights5.ets'), 619], '6': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights6.ets'), 657], - '7': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights7.ets'), 733] + '7': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights7.ets'), 733], + '8': [resolveTestPath('test/testcases/getDocumentHighlights/getDocumentHighlights8.ets'), 677] }, getCompletionAtPosition: { expectedFilePath: resolveTestPath('test/expected/getCompletionAtPosition.json'), @@ -108,12 +119,14 @@ export const testCases: TestCases = { '11': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition11.ets'), 682], '12': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition12.ets'), 720], '13': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition13.ets'), 658], - '14': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition14.ets'), 659] + '14': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition14.ets'), 659], + '15': [resolveTestPath('test/testcases/getCompletionAtPosition/getCompletionsAtPosition15.ets'), 722] }, toLineColumnOffset: { expectedFilePath: resolveTestPath('test/expected/toLineColumnOffset.json'), '1': [resolveTestPath('test/testcases/toLineColumnOffset/toLineColumnOffset1.ets'), 0], - '2': [resolveTestPath('test/testcases/toLineColumnOffset/toLineColumnOffset1.ets'), 642] + '2': [resolveTestPath('test/testcases/toLineColumnOffset/toLineColumnOffset1.ets'), 642], + '3': [resolveTestPath('test/testcases/toLineColumnOffset/toLineColumnOffset2.ets'), 709] }, getSpanOfEnclosingComment: { expectedFilePath: resolveTestPath('test/expected/getSpanOfEnclosingComment.json'), @@ -126,14 +139,56 @@ export const testCases: TestCases = { '1': [ resolveTestPath('test/testcases/provideInlayHints/provideInlayHints1.ets'), { start: 712, length: 11 } as TextSpan + ], + '2': [ + resolveTestPath('test/testcases/provideInlayHints/provideInlayHints2.ets'), + { start: 683, length: 5 } as TextSpan ] }, + getCodeFixesAtPosition: { + expectedFilePath: resolveTestPath('test/expected/getCodeFixesAtPosition.json'), + '1': [resolveTestPath('test/testcases/getCodeFixesAtPosition/getCodeFixesAtPosition1.ets'), 994, 995, [4000]] + }, getSignatureHelpItems: { expectedFilePath: resolveTestPath('test/expected/getSignatureHelpItems.json'), '1': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 613], '2': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 620], - '3': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 678] + '3': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 678], + '4': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems2.ets'), 697], + '5': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems2.ets'), 773] + }, + findRenameLocations: { + expectedFilePath: resolveTestPath('test/expected/findRenameLocations.json'), + '1': [resolveTestPath('test/testcases/findRenameLocations/findRenameLocations2.ets'), 632], + '2': [resolveTestPath('test/testcases/findRenameLocations/findRenameLocations1.ets'), 627], + '3': [resolveTestPath('test/testcases/findRenameLocations/findRenameLocations1.ets'), 670], + '4': [resolveTestPath('test/testcases/findRenameLocations/findRenameLocations1.ets'), 721] + }, + getRenameInfo: { + expectedFilePath: resolveTestPath('test/expected/getRenameInfo.json'), + '1': [resolveTestPath('test/testcases/getRenameInfo/getRenameInfo1.ets'), 615], + '2': [resolveTestPath('test/testcases/getRenameInfo/getRenameInfo2.ets'), 626], + '3': [resolveTestPath('test/testcases/getRenameInfo/getRenameInfo3.ets'), 697] + }, + getOffsetByColAndLine: { + expectedFilePath: resolveTestPath('test/expected/getOffsetByColAndLine.json'), + '1': [resolveTestPath('test/testcases/getOffsetByColAndLine/getOffsetByColAndLine1.ets'), 51, 14] + }, + entry: { + expectedFilePath: '', + '1': [resolveTestPath('test/testcases/entry/Index.ets'), 615] + } +}; + +export const singleModuleCases: TestCases = { + generateDeclFile: { + expectedFilePath: resolveTestPath('test/expected/generateDeclFile.json'), + '1': [] + }, + modifyDeclFile: { + expectedFilePath: resolveTestPath('test/expected/modifyDeclFile.json'), + '1': [resolveTestPath('test/testcases/modifyDeclFile/entry/index.ets')] } }; -export const getSpanOfEnclosingCommentTests = testCases.getSpanOfEnclosingComment; +export const getSpanOfEnclosingCommentTests = basicCases.getSpanOfEnclosingComment; diff --git a/ets2panda/bindings/test/expected/findRenameLocations.json b/ets2panda/bindings/test/expected/findRenameLocations.json new file mode 100644 index 0000000000000000000000000000000000000000..53743ac1c7e1bd73e6bce645a0678629d9853c33 --- /dev/null +++ b/ets2panda/bindings/test/expected/findRenameLocations.json @@ -0,0 +1,136 @@ +{ + "1": [ + { + "fileName": "findRenameLocations1.ets", + "start": 708, + "end": 711, + "line": 21 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 630, + "end": 633, + "line": 15 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 738, + "end": 741, + "line": 23 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 781, + "end": 784, + "line": 24 + } + ], + "2": [ + { + "fileName": "findRenameLocations1.ets", + "start": 625, + "end": 628, + "line": 15 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1259, + "end": 1262, + "line": 49 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1267, + "end": 1270, + "line": 50 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1275, + "end": 1278, + "line": 51 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 625, + "end": 628, + "line": 15 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 694, + "end": 697, + "line": 19 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 702, + "end": 705, + "line": 20 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 711, + "end": 714, + "line": 21 + } + ], + "3": [ + { + "fileName": "findRenameLocations1.ets", + "start": 667, + "end": 672, + "line": 18 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1239, + "end": 1244, + "line": 47 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1249, + "end": 1254, + "line": 48 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 618, + "end": 623, + "line": 15 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 673, + "end": 678, + "line": 17 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 683, + "end": 688, + "line": 18 + } + ], + "4": [ + { + "fileName": "findRenameLocations1.ets", + "start": 718, + "end": 722, + "line": 22 + }, + { + "fileName": "findRenameLocations1.ets", + "start": 882, + "end": 886, + "line": 27 + }, + { + "fileName": "findRenameLocations2.ets", + "start": 866, + "end": 870, + "line": 28 + } + ] +} diff --git a/ets2panda/bindings/test/expected/generateDeclFile.json b/ets2panda/bindings/test/expected/generateDeclFile.json new file mode 100644 index 0000000000000000000000000000000000000000..2eef5ba8ef8e1b09685b53ab5874f0cb87ea5ac4 --- /dev/null +++ b/ets2panda/bindings/test/expected/generateDeclFile.json @@ -0,0 +1,13 @@ +{ + "1": [ + "generateDeclFile1.d.ets", + "generateDeclFile2.d.ets", + "generateDeclFile3.d.ets", + "generateDeclFile4.d.ets", + "generateDeclFile5.d.ets", + "generateDeclFile6.d.ets", + "generateDeclFile7.d.ets", + "generateDeclFile8.d.ets", + "generateDeclFile9.d.ets" + ] +} diff --git a/ets2panda/bindings/test/expected/getCodeFixesAtPosition.json b/ets2panda/bindings/test/expected/getCodeFixesAtPosition.json new file mode 100644 index 0000000000000000000000000000000000000000..198d10973c7959790ea9d207b926dec4b5acf505 --- /dev/null +++ b/ets2panda/bindings/test/expected/getCodeFixesAtPosition.json @@ -0,0 +1,36 @@ +{ + "1": [ + { + "changes": [ + { + "fileName": "getCodeFixesAtPosition1.ets", + "textChanges": [ + { + "span": { + "start": 990, + "length": 6 + }, + "newText": "" + } + ] + } + ], + "description": "Remove the duplicate 'Entry' annotation", + "fixName": "Fix", + "fixId_": "UI_PLUGIN_SUGGEST", + "fixAllDescription_": "Fix All Description" + }, + { + "changes": [ + { + "fileName": "getCodeFixesAtPosition1.ets", + "textChanges": [] + } + ], + "description": "Remove the duplicate 'Entry' annotation", + "fixName": "Fix", + "fixId_": "UI_PLUGIN_SUGGEST", + "fixAllDescription_": "Fix All Description" + } + ] +} diff --git a/ets2panda/bindings/test/expected/getCompletionAtPosition.json b/ets2panda/bindings/test/expected/getCompletionAtPosition.json index 043a9b5580a8653f7583d0d812e8b37f900d3ed4..9a1ba0a4ab5f585aa1a54ec984e3d09a56b0b826 100644 --- a/ets2panda/bindings/test/expected/getCompletionAtPosition.json +++ b/ets2panda/bindings/test/expected/getCompletionAtPosition.json @@ -186,5 +186,35 @@ "kind": 2, "data": null } + ], + "15": [ + { + "name": "isEmpty", + "sortText": "17", + "insertText": "", + "kind": 2, + "data": null + }, + { + "name": "peek", + "sortText": "17", + "insertText": "", + "kind": 2, + "data": null + }, + { + "name": "pop", + "sortText": "17", + "insertText": "", + "kind": 2, + "data": null + }, + { + "name": "push", + "sortText": "17", + "insertText": "", + "kind": 2, + "data": null + } ] -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getCurrentTokenValue.json b/ets2panda/bindings/test/expected/getCurrentTokenValue.json index 2efc1c7dcc7425280098742dee114aaf29f47d4e..7e8e9ca7cbaa75db8b4adad99797db4a7b4d162f 100644 --- a/ets2panda/bindings/test/expected/getCurrentTokenValue.json +++ b/ets2panda/bindings/test/expected/getCurrentTokenValue.json @@ -2,5 +2,6 @@ "1": "ab", "2": "ab", "3": "ab", - "4": "ab" -} \ No newline at end of file + "4": "ab", + "5": "pu" +} diff --git a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json index 73b133987606044ddbf101795a4e7b21a1110a15..0463704db90962380049cb3bc31643843cdd5015 100644 --- a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json +++ b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json @@ -54,4 +54,4 @@ "start": 686, "length": 4 } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getDocumentHighlights.json b/ets2panda/bindings/test/expected/getDocumentHighlights.json index 49190c9ce2c7e36b26677db053333613d4eb04fc..cb53f7cf9a11a73a903ae19164bb7c26cfa72379 100644 --- a/ets2panda/bindings/test/expected/getDocumentHighlights.json +++ b/ets2panda/bindings/test/expected/getDocumentHighlights.json @@ -313,5 +313,38 @@ ] } ] + }, + "8": { + "documentHighlights": [ + { + "fileName": "getDocumentHighlights8.ets", + "highlightSpans": [ + { + "fileName": "getDocumentHighlights8.ets", + "textSpan": { + "start": 628, + "length": 5 + }, + "contextSpan": { + "start": 0, + "length": 0 + }, + "kind": 3 + }, + { + "fileName": "getDocumentHighlights8.ets", + "textSpan": { + "start": 674, + "length": 5 + }, + "contextSpan": { + "start": 0, + "length": 0 + }, + "kind": 2 + } + ] + } + ] } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getFileReferences.json b/ets2panda/bindings/test/expected/getFileReferences.json index 270729c437bc25ccff94f07becd5a81f684b8ba2..dfa9cce006bd48d00b702448bd8ce1581c1186d8 100644 --- a/ets2panda/bindings/test/expected/getFileReferences.json +++ b/ets2panda/bindings/test/expected/getFileReferences.json @@ -23,4 +23,4 @@ "length": 29 } ] -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getFileSource.json b/ets2panda/bindings/test/expected/getFileSource.json new file mode 100644 index 0000000000000000000000000000000000000000..e78e8263809eb2e6790b12307f456fad30c3db94 --- /dev/null +++ b/ets2panda/bindings/test/expected/getFileSource.json @@ -0,0 +1,3 @@ +{ + "1": "/*\n * Copyright (c) 2025 Huawei Device Co., Ltd.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nlet a = 1;" +} diff --git a/ets2panda/bindings/test/expected/getOffsetByColAndLine.json b/ets2panda/bindings/test/expected/getOffsetByColAndLine.json new file mode 100644 index 0000000000000000000000000000000000000000..20feeefb6924fc3fe9e6de00ff27b882f8737c40 --- /dev/null +++ b/ets2panda/bindings/test/expected/getOffsetByColAndLine.json @@ -0,0 +1,3 @@ +{ + "1": 1373 +} diff --git a/ets2panda/bindings/test/expected/getQuickInfoAtPosition.json b/ets2panda/bindings/test/expected/getQuickInfoAtPosition.json index d8dd45871470028b82e634b66b599d7087f65e11..ab3f7ca7b7a3bd711a1e7f8b19e0ed3f7eb02356 100644 --- a/ets2panda/bindings/test/expected/getQuickInfoAtPosition.json +++ b/ets2panda/bindings/test/expected/getQuickInfoAtPosition.json @@ -1,6 +1,6 @@ { "1": { - "kind": "", + "kind": "enum member", "kindModifier": "static public readonly", "textSpan": { "start": 626, @@ -70,7 +70,7 @@ ] }, "3": { - "kind": "property", + "kind": "get", "kindModifier": "public abstract", "textSpan": { "start": 661, @@ -103,5 +103,64 @@ "kind": "returnType" } ] + }, + "4": { + "kind": "method", + "kindModifier": "public declare", + "textSpan": { + "start": 695, + "length": 4 + }, + "fileName": "getQuickInfoAtPosition4.ets", + "displayParts": [ + { + "text": "Stack", + "kind": "className" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "push", + "kind": "functionName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "item", + "kind": "functionParameter" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "T", + "kind": "typeParameter" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "T", + "kind": "returnType" + } + ] } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getReferencesAtPosition.json b/ets2panda/bindings/test/expected/getReferencesAtPosition.json index 526e1ac25619fc3bbc93bf40deaacb4d251492c5..e487e7ef028a1fda1c6abc9466affaed9014552b 100644 --- a/ets2panda/bindings/test/expected/getReferencesAtPosition.json +++ b/ets2panda/bindings/test/expected/getReferencesAtPosition.json @@ -49,5 +49,17 @@ "start": 655, "length": 1 } + ], + "4": [ + { + "fileName": "getReferencesAtPosition6.ets", + "start": 695, + "length": 4 + }, + { + "fileName": "getReferencesAtPosition6.ets", + "start": 708, + "length": 4 + } ] -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getRenameInfo.json b/ets2panda/bindings/test/expected/getRenameInfo.json new file mode 100644 index 0000000000000000000000000000000000000000..801d75bc296ca7ce95d18f148b599e4c8978a871 --- /dev/null +++ b/ets2panda/bindings/test/expected/getRenameInfo.json @@ -0,0 +1,22 @@ +{ + "1": { + "canRenameSuccess": true, + "fileToRename": "", + "kind": "property", + "displayName": "aaa", + "fullDisplayName": "aaa", + "kindModifiers": "", + "triggerSpan": { + "start": 613, + "length": 3 + } + }, + "2": { + "canRenameFailure": false, + "localizedErrorMessage": "You cannot rename this element" + }, + "3": { + "canRenameFailure": false, + "localizedErrorMessage": "You cannot rename this element" + } +} diff --git a/ets2panda/bindings/test/expected/getSemanticDiagnostics.json b/ets2panda/bindings/test/expected/getSemanticDiagnostics.json index 679a81c0565df7ffcaec412a54ad8a473b295304..23cb7c2138b869ba1a223b8234a8f5309f5aafde 100644 --- a/ets2panda/bindings/test/expected/getSemanticDiagnostics.json +++ b/ets2panda/bindings/test/expected/getSemanticDiagnostics.json @@ -5,21 +5,41 @@ "2": { "diagnostics": [ { - "message": "Type '\"1\"' is not compatible with type 'double' at index 1", - "source": "\"1\"", + "message": "Type '\"hello\"' cannot be assigned to type 'Double'", + "range": { + "start": { + "line": 16, + "character": 19 + }, + "end": { + "line": 16, + "character": 26 + } + }, + "tags": [], + "relatedInfo": [], + "code": 2318, + "data": 0, + "severity": 1, + "codeDescription": { + "href": "test code description" + } + }, + { + "message": "No matching call signature for add(\"1\", Int)", "range": { "start": { "line": 20, - "character": 5 + "character": 1 }, "end": { "line": 20, - "character": 8 + "character": 4 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 2127, "data": 0, "severity": 1, "codeDescription": { @@ -27,21 +47,45 @@ } }, { - "message": "No matching call signature for add(\"1\", int)", - "source": "add", + "message": "Type '\"1\"' is not compatible with type 'Double' at index 1", "range": { "start": { "line": 20, - "character": 1 + "character": 5 }, "end": { "line": 20, + "character": 8 + } + }, + "tags": [], + "relatedInfo": [], + "code": 2046, + "data": 0, + "severity": 1, + "codeDescription": { + "href": "test code description" + } + } + ] + }, + "3": { + "diagnostics": [ + { + "message": "No matching call signature for push(\"123\")", + "range": { + "start": { + "line": 19, + "character": 1 + }, + "end": { + "line": 19, "character": 4 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 2127, "data": 0, "severity": 1, "codeDescription": { @@ -49,21 +93,20 @@ } }, { - "message": "Type '\"hello\"' cannot be assigned to type 'double'", - "source": "\"hello\"", + "message": "Type '\"123\"' is not compatible with type 'Double' at index 1", "range": { "start": { - "line": 16, - "character": 19 + "line": 19, + "character": 10 }, "end": { - "line": 16, - "character": 26 + "line": 19, + "character": 15 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 2046, "data": 0, "severity": 1, "codeDescription": { @@ -72,4 +115,4 @@ } ] } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getSignatureHelpItems.json b/ets2panda/bindings/test/expected/getSignatureHelpItems.json index 51881ad082d114fead94544196db25c846c52d88..1298c568414cbe6d1d84f1a5c99d2fe54153159d 100644 --- a/ets2panda/bindings/test/expected/getSignatureHelpItems.json +++ b/ets2panda/bindings/test/expected/getSignatureHelpItems.json @@ -37,7 +37,7 @@ "kind": "punctuation" }, { - "text": "double", + "text": "Double", "kind": "typeName" }, { @@ -114,7 +114,7 @@ "kind": "punctuation" }, { - "text": "double", + "text": "Double", "kind": "typeName" }, { @@ -191,7 +191,7 @@ "kind": "punctuation" }, { - "text": "double", + "text": "Double", "kind": "typeName" }, { @@ -257,7 +257,7 @@ "kind": "punctuation" }, { - "text": "double", + "text": "Double", "kind": "typeName" }, { @@ -295,5 +295,159 @@ "selectedItemIndex": 0, "argumentIndex": 1, "argumentCount": 0 + }, + "4": { + "items": [ + { + "prefixDisplayParts": [ + { + "text": "(", + "kind": "punctuation" + } + ], + "suffixDisplayParts": [ + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "Double", + "kind": "typeName" + } + ], + "separatorDisplayParts": [], + "parameters": [ + { + "name": "item", + "documentation": [], + "displayParts": [ + { + "text": "item", + "kind": "paramName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "Double", + "kind": "typeName" + } + ] + } + ], + "documentation": [] + } + ], + "applicableSpan": { + "start": 691, + "length": 11 + }, + "selectedItemIndex": 0, + "argumentIndex": 0, + "argumentCount": 1 + }, + "5": { + "items": [ + { + "prefixDisplayParts": [ + { + "text": "(", + "kind": "punctuation" + } + ], + "suffixDisplayParts": [ + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "Double|undefined", + "kind": "typeName" + } + ], + "separatorDisplayParts": [], + "parameters": [ + { + "name": "key", + "documentation": [], + "displayParts": [ + { + "text": "key", + "kind": "paramName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "String", + "kind": "typeName" + } + ] + } + ], + "documentation": [] + }, + { + "prefixDisplayParts": [ + { + "text": "(", + "kind": "punctuation" + } + ], + "suffixDisplayParts": [ + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "Double|undefined", + "kind": "typeName" + } + ], + "separatorDisplayParts": [], + "parameters": [ + { + "name": "key", + "documentation": [], + "displayParts": [ + { + "text": "key", + "kind": "paramName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": "String", + "kind": "typeName" + } + ] + } + ], + "documentation": [] + } + ], + "applicableSpan": { + "start": 767, + "length": 12 + }, + "selectedItemIndex": 0, + "argumentIndex": 0, + "argumentCount": 1 } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getSpanOfEnclosingComment.json b/ets2panda/bindings/test/expected/getSpanOfEnclosingComment.json index 10e44761c4c7f610708c88334bab57ec50979c4f..ef15b8474c684d8babe957943722f9d9ee73c7b4 100644 --- a/ets2panda/bindings/test/expected/getSpanOfEnclosingComment.json +++ b/ets2panda/bindings/test/expected/getSpanOfEnclosingComment.json @@ -11,4 +11,4 @@ "start": 659, "length": 9 } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getSuggestionDiagnostics.json b/ets2panda/bindings/test/expected/getSuggestionDiagnostics.json index cd3d78acf8f1199d3918b328ceab6e0403ffaafe..1b00d3a830ea2505fb10aee10847fa7c6615ab57 100644 --- a/ets2panda/bindings/test/expected/getSuggestionDiagnostics.json +++ b/ets2panda/bindings/test/expected/getSuggestionDiagnostics.json @@ -3,323 +3,14 @@ "diagnostics": [ { "message": "This_may_be_converted_to_an_async_function", - "source": "", "range": { "start": { - "line": 15, - "character": 609 + "line": 16, + "character": 1 }, "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 609 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 609 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 609 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 15, - "character": 618 - }, - "end": { - "line": 17, - "character": 661 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 662 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 662 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { "line": 18, - "character": 662 - }, - "end": { - "line": 20, - "character": 717 + "character": 3 } }, "tags": [], @@ -333,37 +24,14 @@ }, { "message": "This_may_be_converted_to_an_async_function", - "source": "", "range": { "start": { - "line": 18, - "character": 662 + "line": 16, + "character": 10 }, "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 + "character": 3 } }, "tags": [], @@ -377,15 +45,14 @@ }, { "message": "This_may_be_converted_to_an_async_function", - "source": "", "range": { "start": { - "line": 18, - "character": 671 + "line": 19, + "character": 1 }, "end": { - "line": 20, - "character": 717 + "line": 21, + "character": 2 } }, "tags": [], @@ -399,125 +66,14 @@ }, { "message": "This_may_be_converted_to_an_async_function", - "source": "", "range": { "start": { - "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 671 - }, - "end": { - "line": 20, - "character": 717 - } - }, - "tags": [], - "relatedInfo": [], - "code": 0, - "data": 0, - "severity": 4, - "codeDescription": { - "href": "" - } - }, - { - "message": "This_may_be_converted_to_an_async_function", - "source": "", - "range": { - "start": { - "line": 18, - "character": 671 + "line": 19, + "character": 10 }, "end": { - "line": 20, - "character": 717 + "line": 21, + "character": 2 } }, "tags": [], @@ -531,4 +87,4 @@ } ] } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json b/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json index baa3c0c3ad58cc3a339fcb4b84e70d6fc018dd53..647feecc453822e604b7757f9a99a5cb47cf2f75 100644 --- a/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json +++ b/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json @@ -6,7 +6,6 @@ "diagnostics": [ { "message": "Unexpected token 'add'.", - "source": "add", "range": { "start": { "line": 16, @@ -19,7 +18,7 @@ }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -28,7 +27,6 @@ }, { "message": "Unexpected token, expected ',' or ')'.", - "source": "*ERROR_LITERAL*", "range": { "start": { "line": 16, @@ -41,7 +39,7 @@ }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1229, "data": 0, "severity": 1, "codeDescription": { @@ -50,7 +48,6 @@ }, { "message": "Unexpected token ':'.", - "source": "*ERROR_LITERAL*", "range": { "start": { "line": 16, @@ -63,7 +60,7 @@ }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -71,21 +68,20 @@ } }, { - "message": "Unexpected token ':'.", - "source": "*ERROR_LITERAL*", + "message": "Unexpected token 'number'.", "range": { "start": { "line": 16, - "character": 14 + "character": 16 }, "end": { "line": 16, - "character": 15 + "character": 22 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -93,21 +89,20 @@ } }, { - "message": "Unexpected token 'number'.", - "source": "number", + "message": "Unexpected token ','.", "range": { "start": { "line": 16, - "character": 16 + "character": 22 }, "end": { "line": 16, - "character": 22 + "character": 23 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -115,21 +110,20 @@ } }, { - "message": "Unexpected token ','.", - "source": "*ERROR_LITERAL*", + "message": "Unexpected token 'b'.", "range": { "start": { "line": 16, - "character": 22 + "character": 24 }, "end": { "line": 16, - "character": 23 + "character": 24 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -137,21 +131,20 @@ } }, { - "message": "Unexpected token ','.", - "source": "*ERROR_LITERAL*", + "message": "Label must be followed by a loop statement.", "range": { "start": { "line": 16, - "character": 22 + "character": 27 }, "end": { "line": 16, - "character": 23 + "character": 33 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1038, "data": 0, "severity": 1, "codeDescription": { @@ -159,21 +152,20 @@ } }, { - "message": "Unexpected token 'b'.", - "source": "", + "message": "Unexpected token ')'.", "range": { "start": { "line": 16, - "character": 24 + "character": 33 }, "end": { "line": 16, - "character": 24 + "character": 34 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -181,21 +173,20 @@ } }, { - "message": "Label must be followed by a loop statement.", - "source": "", + "message": "Unexpected token '{'.", "range": { "start": { "line": 16, - "character": 27 + "character": 35 }, "end": { - "line": 16, - "character": 33 + "line": 18, + "character": 2 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1227, "data": 0, "severity": 1, "codeDescription": { @@ -203,21 +194,70 @@ } }, { - "message": "Unexpected token ')'.", - "source": "*ERROR_LITERAL*", + "message": "return keyword should be used in function body.", + "range": { + "start": { + "line": 17, + "character": 5 + }, + "end": { + "line": 17, + "character": 18 + } + }, + "tags": [], + "relatedInfo": [], + "code": 1163, + "data": 0, + "severity": 1, + "codeDescription": { + "href": "test code description" + } + } + ] + }, + "3": { + "diagnostics": [ + { + "message": "Unexpected token, expected 'from'.", "range": { "start": { "line": 16, - "character": 33 + "character": 1 }, "end": { "line": 16, - "character": 34 + "character": 52 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 1228, + "data": 0, + "severity": 1, + "codeDescription": { + "href": "test code description" + } + } + ] + }, + "4": { + "diagnostics": [ + { + "message": "A function can only be decorated by the 'Builder'.", + "range": { + "start": { + "line": 22, + "character": 2 + }, + "end": { + "line": 22, + "character": 7 + } + }, + "tags": [], + "relatedInfo": [], + "code": 4000, "data": 0, "severity": 1, "codeDescription": { @@ -225,21 +265,20 @@ } }, { - "message": "Unexpected token ')'.", - "source": "*ERROR_LITERAL*", + "message": "The '@Track' annotation can decorate only member variables of a class.", "range": { "start": { - "line": 16, - "character": 33 + "line": 19, + "character": 2 }, "end": { - "line": 16, - "character": 34 + "line": 19, + "character": 7 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 4000, "data": 0, "severity": 1, "codeDescription": { @@ -247,21 +286,41 @@ } }, { - "message": "Unexpected token '{'.", - "source": "{\n return ((a) + (b));\n}", + "message": "The '@Track' annotation can decorate only member variables of a class.", "range": { "start": { - "line": 16, - "character": 35 + "line": 22, + "character": 2 }, "end": { - "line": 18, + "line": 22, + "character": 7 + } + }, + "tags": [], + "relatedInfo": [], + "code": 4000, + "data": 0, + "severity": 1, + "codeDescription": { + "href": "test code description" + } + }, + { + "message": "The '@Track' annotation can decorate only member variables of a class.", + "range": { + "start": { + "line": 27, "character": 2 + }, + "end": { + "line": 27, + "character": 7 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 4000, "data": 0, "severity": 1, "codeDescription": { @@ -269,21 +328,20 @@ } }, { - "message": "return keyword should be used in function body.", - "source": "return ((a) + (b));", + "message": "The '@Track' annotation can decorate only member variables of a class.", "range": { "start": { - "line": 17, - "character": 5 + "line": 36, + "character": 6 }, "end": { - "line": 17, - "character": 18 + "line": 36, + "character": 11 } }, "tags": [], "relatedInfo": [], - "code": 1, + "code": 4000, "data": 0, "severity": 1, "codeDescription": { @@ -292,4 +350,4 @@ } ] } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/modifyDeclFile.json b/ets2panda/bindings/test/expected/modifyDeclFile.json new file mode 100644 index 0000000000000000000000000000000000000000..5718f834601f0bb0db309af84706591e48e639ff --- /dev/null +++ b/ets2panda/bindings/test/expected/modifyDeclFile.json @@ -0,0 +1,3 @@ +{ + "1": ["modifyDeclFile1.d.ets", "modifyDeclFile2.d.ets", "modifyDeclFile3.d.ets"] +} diff --git a/ets2panda/bindings/test/expected/provideInlayHints.json b/ets2panda/bindings/test/expected/provideInlayHints.json index 2fcb8921af2f206fc19a95241126550f0e14bd1a..8e47e660c665f5c68d3cf5daeceda5a0f4d85c14 100644 --- a/ets2panda/bindings/test/expected/provideInlayHints.json +++ b/ets2panda/bindings/test/expected/provideInlayHints.json @@ -14,5 +14,30 @@ "whitespaceBefore": false, "whitespaceAfter": true } + ], + "2": [ + { + "text": "item", + "number": 687, + "kind": 1, + "whitespaceBefore": false, + "whitespaceAfter": true + } + ], + "3": [ + { + "text": "key", + "number": 750, + "kind": 1, + "whitespaceBefore": false, + "whitespaceAfter": true + }, + { + "text": "val", + "number": 755, + "kind": 1, + "whitespaceBefore": false, + "whitespaceAfter": true + } ] -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/expected/toLineColumnOffset.json b/ets2panda/bindings/test/expected/toLineColumnOffset.json index df135e00843c85304cca7f3dea794ca01f3d70c5..0ba3e4e454c623140ae8b67aea249ab99d6448c9 100644 --- a/ets2panda/bindings/test/expected/toLineColumnOffset.json +++ b/ets2panda/bindings/test/expected/toLineColumnOffset.json @@ -6,5 +6,9 @@ "2": { "line": 17, "character": 642 + }, + "3": { + "line": 18, + "character": 708 } -} \ No newline at end of file +} diff --git a/ets2panda/bindings/test/monitor_node.js b/ets2panda/bindings/test/monitor_node.js new file mode 100644 index 0000000000000000000000000000000000000000..aade7588cfa828f60cbb067cb4f692eaf18b54e8 --- /dev/null +++ b/ets2panda/bindings/test/monitor_node.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +/* + * 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. + */ + +const { spawn } = require('child_process'); + +const child = spawn(process.argv[2], process.argv.slice(3), { + stdio: 'inherit', + detached: true, + windowsHide: true +}); + +const timeout = setTimeout(() => { + console.error('process timeout'); + child.kill('SIGKILL'); + process.exit(124); +}, 900000); + +child.on('exit', (code, signal) => { + clearTimeout(timeout); + + if (signal === 'SIGSEGV' || signal === 'SIGABRT') { + console.error(`process crashe: ${signal}`); + process.exit(128 + signal); + } else { + process.exit(code ?? 0); + } +}); + +child.on('error', (err) => { + clearTimeout(timeout); + console.error(`Promoter process failure: ${err.message}`); + process.exit(127); +}); + +child.unref(); \ No newline at end of file diff --git a/ets2panda/bindings/test/prepare.ps1 b/ets2panda/bindings/test/prepare.ps1 index 1be4b3a8d33506d25133b1af11be40c7ede1491c..ac679d55c1f18c078c27a0b1b28069f20234cdc6 100644 --- a/ets2panda/bindings/test/prepare.ps1 +++ b/ets2panda/bindings/test/prepare.ps1 @@ -29,56 +29,11 @@ if ($args.Count -gt 0 -and $args[0] -eq "--restore") { $RestoreMode = 1 } -$OldPath = "path/to/bindings/test" - -$TestcasesDir = "$ScriptDir\testcases" - -if ($RestoreMode -eq 1) { - Write-Host "Restoring path '$ScriptDir' back to '$OldPath' in files..." -} -else { - Write-Host "Replacing path '$OldPath' with '$ScriptDir' in files..." -} - -function Process-Directory { - param ( - [string] $directory - ) - - if (-not (Test-Path -Path $directory -PathType Container)) { - Write-Host "Directory $directory does not exist. Skipping." - return - } - - Write-Host "Processing directory: $directory" - - $jsonFiles = Get-ChildItem -Path $directory -Filter "*.json" -File -Recurse - - foreach ($file in $jsonFiles) { - Write-Host "Processing file: $($file.FullName)" - $content = Get-Content -Path $file.FullName -Raw - - $scriptDirJson = $ScriptDir -replace '\\', '/' - - if ($RestoreMode -eq 1) { - $newContent = $content -replace [regex]::Escape($scriptDirJson), $OldPath - } - else { - $newContent = $content -replace [regex]::Escape($OldPath), $scriptDirJson - } - - Set-Content -Path $file.FullName -Value $newContent -NoNewline - } -} - -Process-Directory -directory $TestcasesDir - if ($RestoreMode -eq 1) { if (Test-Path -Path "$ScriptDir\..\ets2panda") { Remove-Item -Path "$ScriptDir\..\ets2panda" -Recurse -Force Write-Host "Removed '$ScriptDir\..\ets2panda' directory." } - Write-Host "Path restoration completed." } else { $sourceDir = "$ScriptDir\ets\ets1.2\build-tools\ets2panda" @@ -99,7 +54,6 @@ else { try { Copy-Item -Path $sourceDir -Destination $destinationDir -Recurse -Force Write-Host "Directory copied successfully from '$sourceDir' to '$destinationDir'." - Write-Host "Path replacement completed." } catch { Write-Error "Failed to copy directory." diff --git a/ets2panda/bindings/test/run_bindings.sh b/ets2panda/bindings/test/run_bindings.sh new file mode 100755 index 0000000000000000000000000000000000000000..7fef1f2f16c23a3fbecf887dbaab90835f1141e3 --- /dev/null +++ b/ets2panda/bindings/test/run_bindings.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# 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. + +set -e + +readonly TEST_DIR="$1" +readonly NODE_DIR="$2" +readonly SDK_DIR="$3" +readonly CWD="${TEST_DIR}/../" +readonly CURRENT_NPM="${NODE_DIR}/npm" +readonly CURRENT_NODE="${NODE_DIR}/node" + +cp -rfp -- "$SDK_DIR" "$TEST_DIR" +cd "$CWD" && "$CURRENT_NPM" run test:build +if [ $? -eq 0 ]; then + echo "bindings test build successfully" +else + echo "bindings test build failed" + exit 1 +fi + +"$CURRENT_NODE" test/monitor_node.js "$CURRENT_NODE" --unhandled-rejections=strict dist-test/test/run_tests.js ./test +exit_code=$? +if [ $exit_code -eq 0 ]; then + echo "test execution successfully" +else + echo "test execution failed" + exit $exit_code +fi diff --git a/ets2panda/bindings/test/run_tests.ts b/ets2panda/bindings/test/run_tests.ts index 7d6edb92a1faabc95eaec38081f2301c8df40378..08df9960ce0ca96927202c9c2cc6f5d472505b1b 100644 --- a/ets2panda/bindings/test/run_tests.ts +++ b/ets2panda/bindings/test/run_tests.ts @@ -15,16 +15,15 @@ import path from 'path'; import fs from 'fs'; -import { - Lsp, - LspDefinitionData, - LspCompletionInfo, - LspDiagsNode, - ModuleDescriptor, - generateArkTsConfigByModules -} from '../src/index'; -import { testCases } from './cases'; -import { LspCompletionEntry } from '../src/lspNode'; +import { Lsp, LspDefinitionData, LspCompletionInfo, LspDiagsNode, ModuleDescriptor, PathConfig } from '../src/index'; +import { TestCases, basicCases, singleModuleCases } from './cases'; +import { LspCompletionEntry } from '../src/lsp/lspNode'; +import { diff } from 'jest-diff'; + +interface NormalizeOptions { + fieldsToDelete?: string[]; // try to delete these fields in the expected result, just focus on the important fields + normalizeFileName?: boolean; +} interface ComparisonOptions { subMatch?: boolean; @@ -38,8 +37,8 @@ interface ComparisonOutcome { let updateMode = false; -function getModules(projectRoot: string): ModuleDescriptor[] { - return Object.keys(testCases).map((name) => { +function getModules(projectRoot: string, cases: TestCases): ModuleDescriptor[] { + return Object.keys(cases).map((name) => { const modulePath = path.join(projectRoot, name); return { arktsversion: '1.2', @@ -60,6 +59,16 @@ function getExpectedResult(filePath: string): any { } } +// CC-OFFNXT(no_explicit_any) project code style +function getFilesByDir(dirPath: string): string[] { + try { + return fs.readdirSync(dirPath).filter((file) => fs.statSync(path.join(dirPath, file)).isFile()); + } catch (err) { + console.error(`Failed to load files from ${dirPath}: ${err}`); + return []; + } +} + function sortCompletions(completionResult: LspCompletionInfo): LspCompletionInfo { if (!completionResult || !completionResult.entries || !Array.isArray(completionResult.entries)) { return completionResult; @@ -111,19 +120,27 @@ function sortActualResult(testName: string, res: any): any { } // CC-OFFNXT(no_explicit_any) project code style -function normalizeData(obj: any): any { +function normalizeData(obj: any, options: NormalizeOptions = {}): any { + const { fieldsToDelete = [], normalizeFileName = true } = options; if (Array.isArray(obj)) { - return obj.map(normalizeData); + return obj.map((item) => normalizeData(item, options)); } else if (obj && typeof obj === 'object') { const newObj = { ...obj }; + // always remove 'peer' field if ('peer' in newObj) { - delete newObj.peer; // do not compare peer + delete newObj.peer; } - if (newObj.fileName) { + // remove specified fields + fieldsToDelete.forEach((field) => { + if (field in newObj) { + delete newObj[field]; + } + }); + if (normalizeFileName && newObj.fileName) { newObj.fileName = path.basename(newObj.fileName); } for (const key of Object.keys(newObj)) { - newObj[key] = normalizeData(newObj[key]); + newObj[key] = normalizeData(newObj[key], options); } return newObj; } @@ -166,7 +183,7 @@ function performComparison( expected: unknown, options: ComparisonOptions = {} ): ComparisonOutcome { - const { subMatch = false } = options; + const { subMatch: subMatch = false } = options; if (subMatch) { if (isSubObject(normalizedActual, expected)) { return { passed: true }; @@ -206,12 +223,14 @@ function compareResultsHelper( } console.log(`[${testName}] ❌ Failed`); - console.log(`Expected: ${comparison.expectedJSON}`); - console.log(`Actual: ${comparison.actualJSON}`); + const diffResult = diff(comparison.expectedJSON, comparison.actualJSON); + if (diffResult) { + console.log(diffResult); + } return false; } -function compareGetCompletionResult(testName: string, actual: unknown, expected: unknown): boolean { +function compareGetCompletionResult(testName: string, actual: unknown, expected: unknown): [boolean, unknown] { const completionResult = actual as LspCompletionInfo; const actualEntries = completionResult.entries as LspCompletionEntry[]; const expectedEntries = expected as { @@ -222,9 +241,26 @@ function compareGetCompletionResult(testName: string, actual: unknown, expected: data: null; }[]; - return compareResultsHelper(testName, normalizeData(actualEntries), expectedEntries, { - subMatch: true - } as ComparisonOptions); + const actualData = normalizeData(actualEntries); + return [ + compareResultsHelper(testName, actualData, expectedEntries, { + subMatch: true + } as ComparisonOptions), + actualData + ]; +} + +function compareDeclFileResult(testName: string, declgenOutDir: string, expected: unknown): [boolean, unknown] { + let fileList: string[] = getFilesByDir(declgenOutDir); + const actualEntries = fileList.filter((file) => file.endsWith('.d.ets')); + const expectedEntries = expected as string[]; + const actualData = normalizeData(actualEntries); + return [ + compareResultsHelper(testName, actualData, expectedEntries, { + subMatch: true + } as ComparisonOptions), + actualData + ]; } function findTextDefinitionPosition(sourceCode: string): number { @@ -256,58 +292,153 @@ function findTaskDefinitionPosition(sourceCode: string): number { throw new Error('Could not find Task definition in source code'); } -function compareGetDefinitionResult(testName: string, actual: any, expected: Record): boolean { +function compareGetDefinitionResult( + testName: string, + actual: unknown, + expected: Record +): [boolean, unknown] { + let expectedResult = expected; + const actualDef = actual as LspDefinitionData; + const fileName = actualDef.fileName as string; + const fileContent = fs.readFileSync(fileName, 'utf8'); // This is the definition info for the UI component. // File in the SDK might changed, so the offset needs to be checked dynamically. if (expected['fileName'] === 'text.d.ets') { - const actualDef = actual as LspDefinitionData; - const fileName = actualDef.fileName as string; - const fileContent = fs.readFileSync(fileName, 'utf8'); const expectedStart = findTextDefinitionPosition(fileContent); - const expectedResult = { + expectedResult = { ...expected, start: expectedStart }; - return compareResultsHelper(testName, normalizeData(actual), expectedResult); } // This is the definition info for the class in std library. // File in the SDK might changed, so the offset needs to be checked dynamically. if (expected['fileName'] === 'taskpool.ets') { - const actualDef = actual as LspDefinitionData; - const fileName = actualDef.fileName as string; - const fileContent = fs.readFileSync(fileName, 'utf8'); const expectedStart = findTaskDefinitionPosition(fileContent); - const expectedResult = { + expectedResult = { ...expected, start: expectedStart }; - return compareResultsHelper(testName, normalizeData(actual), expectedResult); } - return compareResultsHelper(testName, normalizeData(actual), expected); + const actualData = normalizeData(actual); + return [compareResultsHelper(testName, actualData, expectedResult), actualData]; } -// CC-OFFNXT(no_explicit_any) project code style -function compareResults(testName: string, index: string, actual: unknown, expected: unknown): boolean { - const name = `${testName}:${index}`; +function compareResults( + caseName: string, + actual: unknown, + expected: unknown, + declgenOutDir: string = '' +): [boolean, unknown] { + const testName = caseName.substring(0, caseName.indexOf(':')); if (testName === 'getDefinitionAtPosition') { - return compareGetDefinitionResult(name, actual, expected as Record); + return compareGetDefinitionResult(caseName, actual, expected as Record); } if (testName === 'getCompletionAtPosition') { - return compareGetCompletionResult(name, actual, expected); + return compareGetCompletionResult(caseName, actual, expected); + } + if (testName === 'generateDeclFile' || testName === 'modifyDeclFile') { + const declOutPath = path.join(declgenOutDir, 'dynamic', 'dep', 'declgenV1'); + return compareDeclFileResult(caseName, declOutPath, expected); + } + if ( + testName === 'getSemanticDiagnostics' || + testName === 'getSyntacticDiagnostics' || + testName === 'getSuggestionDiagnostics' + ) { + const normalizeOption: NormalizeOptions = { + fieldsToDelete: ['source'] + }; + const actualData = normalizeData(actual, normalizeOption); + return [compareResultsHelper(caseName, actualData, expected), actualData]; + } + if (testName === 'findRenameLocations') { + const normalizeOption: NormalizeOptions = { + fieldsToDelete: ['prefixText', 'suffixText'] + }; + const actualData = normalizeData(actual, normalizeOption); + return [compareResultsHelper(caseName, actualData, expected), actualData]; } - return compareResultsHelper(name, normalizeData(actual), expected); + const actualData = normalizeData(actual); + return [compareResultsHelper(caseName, actualData, expected), actualData]; } -function runTests(testDir: string, lsp: Lsp) { +function runTests(lsp: Lsp, cases: TestCases, failedList: string[]): string[] { console.log('Running tests...'); - if (!testCases) { - console.error('Failed to load test cases'); - return; + if (!cases) { + return []; } - let failedList: string[] = []; - for (const [testName, testConfig] of Object.entries(testCases)) { + for (const [testName, testConfig] of Object.entries(cases)) { + const { expectedFilePath, ...testCaseVariants } = testConfig; + const expectedResult = getExpectedResult(expectedFilePath); + if (expectedResult === null) { + console.error(`[${testName}] Skipped (expected result not found)`); + continue; + } + // CC-OFFNXT(no_explicit_any) project code style + if (typeof (lsp as any)[testName] !== 'function') { + console.error(`[${testName}] ❌ Error: Method "${testName}" not found on Lsp object`); + continue; + } + + for (const [index, params] of Object.entries(testCaseVariants)) { + let pass = false; + let actualData = undefined; + let actualResult = null; + try { + // CC-OFFNXT(no_explicit_any) project code style + actualResult = (lsp as any)[testName](...params); + actualResult = sortActualResult(testName, actualResult); + [pass, actualData] = compareResults(`${testName}:${index}`, actualResult, expectedResult[index]); + } catch (error) { + console.error(`[${testName}:${index}] ❌ Error: ${error}`); + } + if (!pass) { + failedList.push(`${testName}:${index}`); + } + if (!pass && updateMode) { + console.log(`Updating expected result for ${testName}:${index}`); + expectedResult[index] = actualData; + } + } + if (updateMode) { + fs.writeFileSync(expectedFilePath, JSON.stringify(expectedResult, null, 2)); + } + console.log(`Finished test: ${testName}`); + console.log('-----------------------------------'); + } + return failedList; +} + +function runSingleTests(testDir: string, failedList: string[]): string[] { + console.log('Running single tests...'); + if (!singleModuleCases) { + return []; + } + const testSrcPath = path.join(testDir, 'testcases'); + for (const [testName, testConfig] of Object.entries(singleModuleCases)) { + const testBuildPath = path.join(testSrcPath, '.idea', '.deveco', testName); + let pathConfig: PathConfig = { + buildSdkPath: path.join(testDir, 'ets', 'ets1.2'), + projectPath: testBuildPath, + declgenOutDir: testBuildPath + }; + const moduleList: ModuleDescriptor[] = [ + { + arktsversion: '1.1', + name: 'entry', + moduleType: 'har', + srcPath: path.join(testSrcPath, testName, 'entry') + }, + { + arktsversion: '1.2', + name: 'dep', + moduleType: 'har', + srcPath: path.join(testSrcPath, testName, 'dep') + } + ] as ModuleDescriptor[]; + const lsp = new Lsp(pathConfig, undefined, moduleList); const { expectedFilePath, ...testCaseVariants } = testConfig; const expectedResult = getExpectedResult(expectedFilePath); if (expectedResult === null) { @@ -322,12 +453,18 @@ function runTests(testDir: string, lsp: Lsp) { for (const [index, params] of Object.entries(testCaseVariants)) { let pass = false; + let actualData = undefined; let actualResult = null; try { // CC-OFFNXT(no_explicit_any) project code style actualResult = (lsp as any)[testName](...params); actualResult = sortActualResult(testName, actualResult); - pass = compareResults(testName, index, actualResult, expectedResult[index]); + [pass, actualData] = compareResults( + `${testName}:${index}`, + actualResult, + expectedResult[index], + pathConfig.declgenOutDir + ); } catch (error) { console.error(`[${testName}:${index}] ❌ Error: ${error}`); } @@ -336,7 +473,7 @@ function runTests(testDir: string, lsp: Lsp) { } if (!pass && updateMode) { console.log(`Updating expected result for ${testName}:${index}`); - expectedResult[index] = normalizeData(actualResult); + expectedResult[index] = actualData; } } if (updateMode) { @@ -345,13 +482,60 @@ function runTests(testDir: string, lsp: Lsp) { console.log(`Finished test: ${testName}`); console.log('-----------------------------------'); } + return failedList; +} + +function run(testDir: string, pathConfig: PathConfig): void { + let failedList: string[] = []; + + const basicModules = getModules(pathConfig.projectPath, basicCases); + const basicLsp = new Lsp(pathConfig, undefined, basicModules); + failedList = runTests(basicLsp, basicCases, failedList); + + failedList = runSingleTests(testDir, failedList); + + console.log('Tests completed.'); + if (failedList.length > 0) { + console.log('❌ Failed tests:'); + failedList.forEach((failedCase: string) => { + console.log(`- ${failedCase}`); + }); + + console.error('Tests failed without AST cache'); + process.exit(1); + } + console.log('Finished test without ast cache'); +} + +async function runWithAstCache(testDir: string, pathConfig: PathConfig): Promise { + let failedList: string[] = []; + // for generate ast cache + const entry_module = [ + { + arktsversion: '1.2', + name: 'entry', + moduleType: 'har', + srcPath: path.join(pathConfig.projectPath, 'entry') + } + ]; + + const basicModules = getModules(pathConfig.projectPath, basicCases); + const basicLsp = new Lsp(pathConfig, undefined, entry_module); + await basicLsp.initAstCache(); + basicLsp.update(basicModules); + failedList = runTests(basicLsp, basicCases, failedList); + console.log('Tests completed.'); if (failedList.length > 0) { console.log('❌ Failed tests:'); - failedList.forEach((failedCase) => { + failedList.forEach((failedCase: string) => { console.log(`- ${failedCase}`); }); + + console.error('Tests failed with AST cache'); + process.exit(1); } + console.log('Finished test with ast cache'); } if (require.main === module) { @@ -363,14 +547,17 @@ if (require.main === module) { if (process.argv[3] && process.argv[3] === '--update') { updateMode = true; } - const testDir = path.resolve(process.argv[2]); - const buildSdkPath = path.join(testDir, 'ets', 'ets1.2'); - const projectRoot = path.join(testDir, 'testcases'); - const modules = getModules(projectRoot); - generateArkTsConfigByModules(buildSdkPath, projectRoot, modules); - const lsp = new Lsp(projectRoot); + const testDir = path.resolve(process.argv[2]); + const pathConfig: PathConfig = { + buildSdkPath: path.join(testDir, 'ets', 'ets1.2'), + projectPath: path.join(testDir, 'testcases'), + declgenOutDir: '' + }; - process.env.BINDINGS_PATH = path.join(buildSdkPath, 'build-tools', 'bindings'); - runTests(testDir, lsp); + process.env.BINDINGS_PATH = path.join(pathConfig.buildSdkPath, 'build-tools', 'bindings'); + process.env.PANDA_LIB_PATH = path.join(pathConfig.buildSdkPath, 'build-tools', 'ets2panda', 'lib'); + process.env.PANDA_BIN_PATH = path.join(pathConfig.buildSdkPath, 'build-tools', 'ets2panda', 'bin'); + run(testDir, pathConfig); + runWithAstCache(testDir, pathConfig).then(() => {}); } diff --git a/ets2panda/bindings/test/testcases/entry/Index.ets b/ets2panda/bindings/test/testcases/entry/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..a665975076ca05f9e8111b218136fdbff21f123c --- /dev/null +++ b/ets2panda/bindings/test/testcases/entry/Index.ets @@ -0,0 +1,74 @@ +/* + * 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. + */ + +import { Text, Column, Component, Entry, Button, ClickEvent } from "@ohos.arkui.component" +import { State, Link, Prop } from "@ohos.arkui.stateManagement" +import hilog from '@ohos.hilog' + +@Entry +@Component +struct MyStateSample { + @State stateVar: string = "state var"; + message: string = `click to change state variable, add **`; + changeValue() { + this.stateVar+="**" + } + build() { + Column() { + Button("clean variable").onClick((e: ClickEvent) => { this.stateVar = "state var" }) + Text("Hello World").fontSize(20) + Button(this.message).backgroundColor("#FFFF00FF") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue() + }) + Text(this.stateVar).fontSize(20) + Child({linkVar: this.stateVar, propVar: this.stateVar}) + }.margin(10) + } +} + +@Component +struct Child { + @Link linkVar: string = ""; // TODO: remove this + @Prop propVar: string = "Prop"; + + changeValue1() { + this.linkVar+="!!" + } + + changeValue2() { + this.propVar+="~~" + } + + build() { + Column() { + Button(`click to change Link variable, add symbol !!`) + .backgroundColor("#4169E1") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue1() + }) + Button(`click to change Prop variable, add symbol ~~`) + .backgroundColor("#3CB371") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + this.changeValue2() + }) + Text(`Link variable in child: ${this.linkVar}`).fontSize(30) + Text(`Prop variable in child: ${this.propVar}`).fontSize(30) + } + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations1.ets b/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations1.ets new file mode 100644 index 0000000000000000000000000000000000000000..8fe412caf8c78831989ab3ffafb3794ee73091af --- /dev/null +++ b/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations1.ets @@ -0,0 +1,52 @@ +/* + * 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. + */ + +export function abc(x: number): void { +} + +export function dummy(x: number): void { +} + +export class Foo { + name: string = "unassigned"; + x: number = 1; + y: number = 2; + z: number = 3; + constructor(name: string, x: number, y: number, z: number) { + this.name = name; + this.x = x; + this.y = y; + this.z = z; + } +}; + +export class Oranges { + name: string = "unassigned"; + x: number = 1; + y: number = 2; + z: number = 3; + constructor(name: string, x: number, y: number, z: number) { + this.name = name; + this.x = x; + this.y = y; + this.z = z; + } +}; + +dummy(0); +dummy(1); +abc(2); +abc(3); +abc(4); diff --git a/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations2.ets b/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations2.ets new file mode 100644 index 0000000000000000000000000000000000000000..e37a0e5fc26bcbe61b345858655df093f17a14ed --- /dev/null +++ b/ets2panda/bindings/test/testcases/findRenameLocations/findRenameLocations2.ets @@ -0,0 +1,29 @@ +/* + * 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. + */ + +import { dummy, abc, Foo } from "./findRenameLocations1.ets"; + +dummy(4); +dummy(44); +abc(5); +abc(55); +abc(555); + +let myfoo = new Foo("apples", 1, 2, 3); +let otherfoo = new Foo("oranges", 4, 5, 6); + +console.log(myfoo) +console.log(otherfoo) +console.log(myfoo.name) diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile1.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile1.ets new file mode 100644 index 0000000000000000000000000000000000000000..7f033b2c0310beab1cecebeb2efb412fd53521a1 --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile1.ets @@ -0,0 +1,54 @@ +/** + * 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. + */ + +let currentValue: string = 'currentValue'; + +let resultValue: number = -1; + +let mathRandom: number = 14; + +function getCurrentValue(): number { + let firstValue = getFirstValue(); + let secondValue = getSecondValue(); + let thridValue = getThridValue(); + resultValue = currentValue.length; + return currentValue.length; +} + +function getFirstValue(): number { + let c = 15; + let value = c * mathRandom; + mathRandom++; + EtsStepArkTest() + return value; +} + +function getSecondValue(): number { + let d = 11; + let value1 = getFirstValue(); + return value1 * d; +} + +function getThridValue(): number { + let value1 = getFirstValue(); + let value2 = getSecondValue(); + return value1 * value2; +} + +function getValue(): string { + return getCurrentValue() + currentValue; +} + +getValue(); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile2.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile2.ets new file mode 100644 index 0000000000000000000000000000000000000000..d86e832dddad78e9c7c27228d4c1c1635ffe34fa --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile2.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export class Foo { + Foo(a:number, b:number): number { + return a + b; + } +} diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile3.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile3.ets new file mode 100644 index 0000000000000000000000000000000000000000..74e5fc562609d051b80763d02a9004b538ef7c5d --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile3.ets @@ -0,0 +1,23 @@ +/* + * 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. + */ + +function A(a:number, b:number): number { + return a + b; +} +A(1, 2); +function A(a:number): number { + return a; +} +A(1); diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile4.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile4.ets new file mode 100644 index 0000000000000000000000000000000000000000..7b762ba46101e5106cf2329ceb9fb4250b9e4439 --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile4.ets @@ -0,0 +1,31 @@ +/* + * 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. + */ + +export class A { + Foo(a:number, b:number): number { + return a + b; + } + readTest1(c:number, d:number): number { + return a - b; + } + + readTest2(e:number, f:number): number { + return a * b; + } + + readTest3(e:number, f:number): number { + return a / b; + } +}; \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile5.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile5.ets new file mode 100644 index 0000000000000000000000000000000000000000..50e29bb12fe3e8702274b4d541f47003f79e7de9 --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile5.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export class A { + Foo(a:number, b:number): number { + return a + b; + } +} diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile6.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile6.ets new file mode 100644 index 0000000000000000000000000000000000000000..c54a0d1535beb24b839528d630e01201cd4010fa --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile6.ets @@ -0,0 +1,47 @@ +/* + * 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. + */ + +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement"; +import { Text, Column, Component, Button, ClickEvent } from "@ohos.arkui.component"; + +import hilog from '@ohos.hilog'; + +@Component +export struct MyStateSample { + message: string = "Click"; + + build() { + Column(undefined) { + Text("Hello World") + .fontSize(20) + Button(this.message) + .backgroundColor("#FFFF00FF") + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + }) + Child() + } + } +} + +@Component +export struct Child { + stateVar: string = "Child"; + + build() { + Text(this.stateVar) + .fontSize(50) + } +} diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile7.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile7.ets new file mode 100644 index 0000000000000000000000000000000000000000..3f8d64d3fa9e7cc595245dbc303453427804d928 --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile7.ets @@ -0,0 +1,99 @@ +/** + * 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. + */ + +interface DataValue { + a: number; + b: string; + c: string; + d: number; +} + +let currentValue: DataValue = { + a: 12, + b: 'da', + c: 'vk', + d: 1111 +}; + +let result: number = 0; + +let resultStr: number = 0; + +let resultValue: number = -1; + +let mathRandom: number = 14; + +function getPath(c: number, d: number): number { + return c * d; +} + +function getPathDate(c: number): number { + return c * 2 + 6; +} + +function getCurrentValue(): DataValue { + let currentValue: DataValue = { + a: 12, + b: 'da', + c: 'vk', + d: 1111 + }; + currentValue.a = getFirstValue(); + currentValue.d = getSecondValue(); + currentValue.c = getThridValue() + getSecondValue() + getFirstValue(); + currentValue.b = getThridValue() + getSecondValue() + getFirstValue() + resultValue; + EtsStepArkTest() + while(currentValue.a < 12) { + if (resultValue % 2 === 0) { + currentValue.a = currentValue.a++; + } else { + currentValue.d = currentValue.d--; + } + } + return currentValue; +} + +function getFirstValue(): number { + let currentFirstValue: DataValue = { + a: 23, + b: '124f', + c: 'gfr', + d: 767 + }; + EtsStepArkTest() + return getPath(currentFirstValue.a, currentFirstValue.d); +} + +function getSecondValue(): number { + for(let index = 0; index < 2; index++) { + result = getPathDate(index); + } + return result; +} + +function getThridValue(): string { + let dd: string = ''; + for(let index = 0; index< 2; index++) { + dd = resultStr + getPathDate(index) + 'abc'; + } + EtsStepArkTest() + return dd; +} + +function getValueStr(): string { + return getCurrentValue().b + currentValue; +} + +getValueStr(); diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile8.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile8.ets new file mode 100644 index 0000000000000000000000000000000000000000..10859e929cf149305acef118db4e74f319c4d69c --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile8.ets @@ -0,0 +1,51 @@ +/** + * 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. + */ + +function func06(): void { + EtsStepArkTest(); +} + +function func05(): void { + func06(); + EtsStepArkTest(); +} + +function func04(): void { + func05(); + EtsStepArkTest(); +} + +function func03(): void { + let id = 0; + for (let i = 0; i < 10; ++i) { + id++ + } + func04(); + EtsStepArkTest(); +} + +function func02(): void { + func03(); + EtsStepArkTest(); +} + +function func01(): void { + func02(); + EtsStepArkTest(); +} + +function main(argc:string[]): void { + func01(); +} diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile9.ets b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile9.ets new file mode 100644 index 0000000000000000000000000000000000000000..a38038cd9c441a688778cffb05f72e5692d9ac9f --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/dep/generateDeclFile9.ets @@ -0,0 +1,74 @@ +/** + * 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. + */ + +interface FileDataValue { + fileName: string; + filePath: string; + data: DataValue; +} + +interface DataValue { + dataLength: number; + dataOffset: number; + dataStr: string; +} + +class OperationFactory { + private currentType: string = 'add'; + + createOperation(type: 'add' | 'subtract' | 'multiply' | 'divide', a: number, b: number): number { + this.currentType = type; + switch (type) { + case 'add': + return a + b; + case 'subtract': + return a - b; + case 'multiply': + return a * b; + case 'divide': + if (b === 0) { + throw new Error("Division by zero is not allowed."); + } + return a / b; + default: + throw new Error("Unknown operation type"); + } + } +} + +function getFileDate(): FileDataValue[] { + let FileDataValues: FileDataValue[] = [{ + fileName: 'OperationFactoryFile', + filePath: 'source/url/OperationFactoryFile', + data: { + dataLength: 10, + dataOffset: 2, + dataStr: 'This is a test data!' + } + }]; + EtsStepArkTest(); + return FileDataValues; +} + +function getOperationFactory(): void { + getFileDate(); + const factory = new OperationFactory(); + factory.createOperation('add', 2, 3); + factory.createOperation('subtract', 5, 5); + factory.createOperation('multiply', 3, 6); + factory.createOperation('divide', 2, 2); +} + +getOperationFactory(); diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/entry/index.ets b/ets2panda/bindings/test/testcases/generateDeclFile/entry/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..6fc34b5b10bc306f7613a62e1b441e9669e4b02e --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/entry/index.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + + let indexStr: string = "generateDeclFile"; \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/generateDeclFile/entry/oh-package.json5 b/ets2panda/bindings/test/testcases/generateDeclFile/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d361cd9947339c0cee25886c42ec923c723f70f8 --- /dev/null +++ b/ets2panda/bindings/test/testcases/generateDeclFile/entry/oh-package.json5 @@ -0,0 +1,23 @@ +/** + * 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. + */ + +{ + modelVersion: '5.0.1', + description: 'Please describe the basic information.', + dependencies: { + myDep: 'file:../dep' + }, + devDependencies: {} +} diff --git a/ets2panda/bindings/test/testcases/getCodeFixesAtPosition/getCodeFixesAtPosition1.ets b/ets2panda/bindings/test/testcases/getCodeFixesAtPosition/getCodeFixesAtPosition1.ets new file mode 100644 index 0000000000000000000000000000000000000000..8ac051ca62dbfb06be443f68172bf3ed900b0101 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getCodeFixesAtPosition/getCodeFixesAtPosition1.ets @@ -0,0 +1,57 @@ +/* + * 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. + */ + +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement' +import { + Entry + Text, + TextAttributes, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView +} from '@ohos.arkui.component' +import { State, MutableSate, stateOf, observableProxy } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .onClick(() => { + console.info("hello, :") + console.log(result.toString()) + }) + .width('100%') + } + .height('100%') + } +} + +@Entry +@Component +struct Index { + build() {} +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getCompletionAtPosition/getCompletionsAtPosition15.ets b/ets2panda/bindings/test/testcases/getCompletionAtPosition/getCompletionsAtPosition15.ets new file mode 100644 index 0000000000000000000000000000000000000000..d00b745ae4d0f96ac9e707f0ed4c4fed03127067 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getCompletionAtPosition/getCompletionsAtPosition15.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); +stk.p diff --git a/ets2panda/bindings/test/testcases/getCurrentTokenValue/getCurrentTokenValue5.ets b/ets2panda/bindings/test/testcases/getCurrentTokenValue/getCurrentTokenValue5.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c76d7275e73210dc679cc43e75c2195a1e1f27 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getCurrentTokenValue/getCurrentTokenValue5.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); diff --git a/ets2panda/bindings/test/testcases/getDocumentHighlights/getDocumentHighlights8.ets b/ets2panda/bindings/test/testcases/getDocumentHighlights/getDocumentHighlights8.ets new file mode 100644 index 0000000000000000000000000000000000000000..6d43a149b8b0b52c12d8ee4d06e19b7eac4f5d63 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDocumentHighlights/getDocumentHighlights8.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getFileSource/getFileSource1.ets b/ets2panda/bindings/test/testcases/getFileSource/getFileSource1.ets new file mode 100644 index 0000000000000000000000000000000000000000..119751e901726748feeeb271b61fd5633db5b46b --- /dev/null +++ b/ets2panda/bindings/test/testcases/getFileSource/getFileSource1.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + +let a = 1; \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getOffsetByColAndLine/getOffsetByColAndLine1.ets b/ets2panda/bindings/test/testcases/getOffsetByColAndLine/getOffsetByColAndLine1.ets new file mode 100644 index 0000000000000000000000000000000000000000..0d9c5f8afa5efa49d6dba9accca728964f091ba3 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getOffsetByColAndLine/getOffsetByColAndLine1.ets @@ -0,0 +1,53 @@ +/* + * 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. + */ + +// comment of line + +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement' +import { + Entry + Text, + TextAttributes, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView +} from '@ohos.arkui.component' +import { State, MutableSate, stateOf, observableProxy } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .onClick(() => { + console.info("hello, :") + console.log(result.toString()) + }) + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition4.ets b/ets2panda/bindings/test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition4.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c76d7275e73210dc679cc43e75c2195a1e1f27 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getQuickInfoAtPosition/getQuickInfoAtPosition4.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); diff --git a/ets2panda/bindings/test/testcases/getReferencesAtPosition/getReferencesAtPosition6.ets b/ets2panda/bindings/test/testcases/getReferencesAtPosition/getReferencesAtPosition6.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c76d7275e73210dc679cc43e75c2195a1e1f27 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getReferencesAtPosition/getReferencesAtPosition6.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); diff --git a/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo1.ets b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo1.ets new file mode 100644 index 0000000000000000000000000000000000000000..1986e1b330712c668afd67acde59f9da86f426be --- /dev/null +++ b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo1.ets @@ -0,0 +1,18 @@ +/* + * 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. + */ + +let aaa = 123; +let bbb = aaa + 123; +console.log(aaa + ""); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo2.ets b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo2.ets new file mode 100644 index 0000000000000000000000000000000000000000..ab20d0867bc0ad0761ce8eb8dfa34a85a4ad73ed --- /dev/null +++ b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo2.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + +let map1 = new Map(); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo3.ets b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo3.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c76d7275e73210dc679cc43e75c2195a1e1f27 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getRenameInfo/getRenameInfo3.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); diff --git a/ets2panda/bindings/test/testcases/getSemanticDiagnostics/getSemanticDiagnostics3.ets b/ets2panda/bindings/test/testcases/getSemanticDiagnostics/getSemanticDiagnostics3.ets new file mode 100644 index 0000000000000000000000000000000000000000..705da5d5f5037e3b790f07e04d9b78129b7115ef --- /dev/null +++ b/ets2panda/bindings/test/testcases/getSemanticDiagnostics/getSemanticDiagnostics3.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push("123"); diff --git a/ets2panda/bindings/test/testcases/getSignatureHelpItems/getSignatureHelpItems2.ets b/ets2panda/bindings/test/testcases/getSignatureHelpItems/getSignatureHelpItems2.ets new file mode 100644 index 0000000000000000000000000000000000000000..551e7e896be9a72e4fdcc481fc997aec5c2eac84 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getSignatureHelpItems/getSignatureHelpItems2.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); + +let map = new Map(); +map.set("a", 1); +let a = map.get("a"); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics3.ets b/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics3.ets new file mode 100644 index 0000000000000000000000000000000000000000..116e75a6113e06b17c637343d390e283cd69ddc5 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics3.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + +import {default as stack} frrom '@ohos.util.Stack'; \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics4.ets b/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics4.ets new file mode 100644 index 0000000000000000000000000000000000000000..e40cd7d11b0d277d686bd07d1645c6468b56eaba --- /dev/null +++ b/ets2panda/bindings/test/testcases/getSyntacticDiagnostics/getSyntacticDiagnostics4.ets @@ -0,0 +1,46 @@ +/* + * 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. + */ + +import { Entry, Component, Row, Column, Text } from '@ohos.arkui.component'; +import { Track } from '@ohos.arkui.stateManagement'; + +@Track +let A: number = 0; + +@Track +function B() { + +} + +@Track +interface IUser { + name: string; + age: number; +} + +@Entry +@Component +struct Index { + @Track + funA() { + + } + + build() { + Row() { + Text('Hello World') + } + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile1.ets b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile1.ets new file mode 100644 index 0000000000000000000000000000000000000000..6056b864f5a8aabc2d32242444578acb24417502 --- /dev/null +++ b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile1.ets @@ -0,0 +1,45 @@ +/* + * 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. + */ + +interface Employee { + name: string; + salary: number; + hasBonus: boolean; +} + +function calculateTotalSalary(employees: Employee[]): number { + const bonusAmount = 500; + + let totalSalary = 0; + EtsStepArkTest(); + + for (const employee of employees) { + let adjustedSalary = employee.salary; + if (employee.hasBonus) { + adjustedSalary += bonusAmount; + } + totalSalary += adjustedSalary; + } + return totalSalary; +} + +const employees: Employee[] = [ + { name: "Alice", salary: 3000, hasBonus: true }, + { name: "Bob", salary: 2800, hasBonus: false }, + { name: "Charlie", salary: 3200, hasBonus: true }, + { name: "David", salary: 2900, hasBonus: false } +]; + +const totalSalary = calculateTotalSalary(employees); diff --git a/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile2.ets b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile2.ets new file mode 100644 index 0000000000000000000000000000000000000000..fd7b2efb5ffc195a9655e062398f0124caa5c5b7 --- /dev/null +++ b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile2.ets @@ -0,0 +1,57 @@ +/* + * 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. + */ + +class ChainCalculator { + private result: number; + + constructor(initialValue: number) { + this.result = initialValue; + } + + add(value: number): ChainCalculator { + this.result += value; + return this; + } + + subtract(value: number): ChainCalculator { + this.result -= value; + return this; + } + + multiply(value: number): ChainCalculator { + this.result *= value; + return this; + } + + divide(value: number): ChainCalculator { + EtsStepArkTest(); + if (value === 0) { + throw new Error("Division by zero is not allowed."); + } + this.result /= value; + return this; + } + + getResult(): number { + return this.result; + } + + geFinalResult(addValue: number, subtractValue: number, multiplyValue: number, divideValue: number): number { + return chainCalc.add(addValue).subtract(subtractValue).multiply(multiplyValue).divide(divideValue).getResult(); + } +} + +const chainCalc = new ChainCalculator(10); +chainCalc.geFinalResult(2, 4, 6, 3); diff --git a/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile3.ets b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile3.ets new file mode 100644 index 0000000000000000000000000000000000000000..901ff0e5a05e58b5e6f4bce30d02198555876b88 --- /dev/null +++ b/ets2panda/bindings/test/testcases/modifyDeclFile/dep/modifyDeclFile3.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +function getC(a:number, b:number): number { + return a + b; +} + +getC(1, 120); diff --git a/ets2panda/bindings/test/testcases/modifyDeclFile/entry/index.ets b/ets2panda/bindings/test/testcases/modifyDeclFile/entry/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..b342f515ec93e1b84cf24499e987b66f03f7c43b --- /dev/null +++ b/ets2panda/bindings/test/testcases/modifyDeclFile/entry/index.ets @@ -0,0 +1,16 @@ +/* + * 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. + */ + + let indexStr: string = "modifyDeclFile"; \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/modifyDeclFile/entry/oh-package.json5 b/ets2panda/bindings/test/testcases/modifyDeclFile/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d361cd9947339c0cee25886c42ec923c723f70f8 --- /dev/null +++ b/ets2panda/bindings/test/testcases/modifyDeclFile/entry/oh-package.json5 @@ -0,0 +1,23 @@ +/** + * 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. + */ + +{ + modelVersion: '5.0.1', + description: 'Please describe the basic information.', + dependencies: { + myDep: 'file:../dep' + }, + devDependencies: {} +} diff --git a/ets2panda/bindings/test/testcases/provideInlayHints/provideInlayHints2.ets b/ets2panda/bindings/test/testcases/provideInlayHints/provideInlayHints2.ets new file mode 100644 index 0000000000000000000000000000000000000000..24211305cddf28d55724603478ad565a05d9e8cd --- /dev/null +++ b/ets2panda/bindings/test/testcases/provideInlayHints/provideInlayHints2.ets @@ -0,0 +1,23 @@ +/* + * 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. + */ + +import Stack from '@ohos.util.Stack'; +let stk = new Stack(); +stk.push(1); +stk.push(2); + +let map = new Map(); +map.set("a", 1); +let a = map.get("a"); \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/toLineColumnOffset/toLineColumnOffset2.ets b/ets2panda/bindings/test/testcases/toLineColumnOffset/toLineColumnOffset2.ets new file mode 100644 index 0000000000000000000000000000000000000000..98c76d7275e73210dc679cc43e75c2195a1e1f27 --- /dev/null +++ b/ets2panda/bindings/test/testcases/toLineColumnOffset/toLineColumnOffset2.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import {default as stack} from '@ohos.util.Stack'; +let stk = new stack(); +stk.push(1); +stk.push(2); diff --git a/ets2panda/checker/ASchecker.cpp b/ets2panda/checker/ASchecker.cpp index 3a3cb8e6919aafd41329ac9b4fa2d6880ee55fff..ee3cd234f8fcdb5e5d20957c1ff19f7d17086b66 100644 --- a/ets2panda/checker/ASchecker.cpp +++ b/ets2panda/checker/ASchecker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -21,8 +21,6 @@ namespace ark::es2panda::checker { bool ASChecker::StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const util::Options &options) { - Initialize(varbinder); - if (options.IsDumpAst()) { std::cout << Program()->Dump() << std::endl; } diff --git a/ets2panda/checker/ASchecker.h b/ets2panda/checker/ASchecker.h index 69a80488abb56b310d61bc7464e0480245f93cb7..84ba809e5b0d673bdfc99f006a9ea7a649a2b281 100644 --- a/ets2panda/checker/ASchecker.h +++ b/ets2panda/checker/ASchecker.h @@ -23,8 +23,9 @@ namespace ark::es2panda::checker { class ASChecker : public Checker { public: // NOLINTNEXTLINE(readability-redundant-member-init) - explicit ASChecker(util::DiagnosticEngine &diagnosticEngine, [[maybe_unused]] ArenaAllocator *allocator) - : Checker(diagnosticEngine) + explicit ASChecker([[maybe_unused]] ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + [[maybe_unused]] ArenaAllocator *programAllocator) + : Checker(allocator, diagnosticEngine) { } diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 1974f525ef8f53cb2a499789783464e0beb6c932..ba26ddb6eead4c8f315ebbe4442b1f9fa043f856 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -20,16 +20,22 @@ #include "generated/diagnostic.h" #include "checker/types/globalTypesHolder.h" #include "checker/types/ets/etsTupleType.h" +#include "checker/types/gradualType.h" #include "evaluate/scopedDebugInfoPlugin.h" #include "types/signature.h" #include "compiler/lowering/ets/setJumpTarget.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "types/ts/nullType.h" +#include "types/type.h" +#include "checker/types/typeError.h" #include "util/es2pandaMacros.h" #include namespace ark::es2panda::checker { +static Type *GetAppropriatePreferredType(Type *originalType, std::function const &predicate); + ETSChecker *ETSAnalyzer::GetETSChecker() const { return static_cast(GetChecker()); @@ -101,6 +107,11 @@ checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const ETSChecker *checker = GetETSChecker(); if (st->Id()->Variable() == nullptr) { + // Now invalid or dummy nodes obtaining after parsing don't have associated variables at all, that leads to + // incorrect AST and multiple reported errors in AST verifier. Need to create and bind [special]? variables for + // them with default TypeError set[?]. Why can't we directly check the 'Id'? During the process of + // resolveIdentifier, we might obtain the wrong variable, which breaks the consistency between the variable and + // its tsType. see wrong_variable_binding.ets for more details. auto ident = st->Id(); auto [decl, var] = checker->VarBinder()->NewVarDecl( ident->Start(), compiler::GenName(checker->ProgramAllocator()).View()); @@ -155,6 +166,11 @@ checker::Type *ETSAnalyzer::Check(ir::ClassStaticBlock *st) const } else { st->SetTsType(checker->BuildMethodType(func)); } + + if (!func->HasBody() || (func->IsExternal() && !func->IsExternalOverload())) { + return st->TsType(); + } + checker::ScopeContext scopeCtx(checker, func->Scope()); checker::SavedCheckerContext savedContext(checker, checker->Context().Status(), checker->Context().ContainingClass()); @@ -168,15 +184,10 @@ static void HandleNativeAndAsyncMethods(ETSChecker *checker, ir::MethodDefinitio { auto *scriptFunc = node->Function(); ES2PANDA_ASSERT(scriptFunc != nullptr); - if (node->IsNative() && !node->IsConstructor() && !scriptFunc->IsSetter()) { - if (scriptFunc->ReturnTypeAnnotation() == nullptr) { - checker->LogError(diagnostic::NATIVE_WITHOUT_RETURN, {}, scriptFunc->Start()); - node->SetTsType(checker->GlobalTypeError()); - } - } if (util::Helpers::IsAsyncMethod(node)) { - if (scriptFunc->ReturnTypeAnnotation() != nullptr) { + if (scriptFunc->ReturnTypeAnnotation() != nullptr && !scriptFunc->ReturnTypeAnnotation()->IsOpaqueTypeNode() && + scriptFunc->Signature() != nullptr) { auto *asyncFuncReturnType = scriptFunc->Signature()->ReturnType(); if (!asyncFuncReturnType->IsETSObjectType() || @@ -222,10 +233,6 @@ checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const return node->TsType(); }; - if (scriptFunc == nullptr) { - checker->LogError(diagnostic::FUNC_EXPR_INVALID, {}, node->Start()); - return returnErrorType(); - } checker->CheckAnnotations(scriptFunc->Annotations()); checker->CheckFunctionSignatureAnnotations(scriptFunc->Params(), scriptFunc->TypeParams(), scriptFunc->ReturnTypeAnnotation()); @@ -283,12 +290,6 @@ void ETSAnalyzer::CheckMethodModifiers(ir::MethodDefinition *node) const return; } - if (node->Function() == nullptr) { - checker->LogError(diagnostic::FUNC_EXPR_INVALID, {}, node->Start()); - node->SetTsType(checker->GlobalTypeError()); - return; - } - if ((node->IsAbstract() || (!node->Function()->HasBody() && !node->IsNative() && !node->IsDeclare())) && !(checker->HasStatus(checker::CheckerStatus::IN_ABSTRACT) || checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) { @@ -311,6 +312,92 @@ void ETSAnalyzer::CheckMethodModifiers(ir::MethodDefinition *node) const } } +static void CheckDuplicationInOverloadDeclaration(ETSChecker *const checker, ir::OverloadDeclaration *const node) +{ + auto overloadedNameSet = ArenaSet(checker->ProgramAllocator()->Adapter()); + for (ir::Expression *const overloadedName : node->OverloadedList()) { + bool isQualifiedName = true; + std::function getFullOverloadedName = + [&isQualifiedName, &getFullOverloadedName](ir::Expression *const expr) -> std::string { + if (!isQualifiedName) { + return ""; + } + if (expr->IsIdentifier()) { + return expr->AsIdentifier()->Name().Mutf8(); + } + if (expr->IsMemberExpression()) { + return getFullOverloadedName(expr->AsMemberExpression()->Object()) + "." + + getFullOverloadedName(expr->AsMemberExpression()->Property()); + } + isQualifiedName = false; + return ""; + }; + std::string fullOverloadedName = getFullOverloadedName(overloadedName); + if (!isQualifiedName) { + continue; + } + if (overloadedNameSet.find(fullOverloadedName) != overloadedNameSet.end()) { + checker->LogError(diagnostic::DUPLICATE_OVERLOADED_NAME, overloadedName->Start()); + continue; + } + overloadedNameSet.insert(fullOverloadedName); + } +} + +static void CheckOverloadSameNameMethod(ETSChecker *const checker, ir::OverloadDeclaration *const overloadDecl) +{ + Type *objectType = overloadDecl->Parent()->IsClassDefinition() + ? overloadDecl->Parent()->AsClassDefinition()->Check(checker) + : overloadDecl->Parent()->Parent()->AsTSInterfaceDeclaration()->Check(checker); + ES2PANDA_ASSERT(objectType->IsETSObjectType()); + + PropertySearchFlags searchFlags = PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION | + (overloadDecl->IsStatic() ? PropertySearchFlags::SEARCH_STATIC_METHOD + : PropertySearchFlags::SEARCH_INSTANCE_METHOD); + auto *sameNameMethod = objectType->AsETSObjectType()->GetProperty(overloadDecl->Id()->Name(), searchFlags); + if (sameNameMethod == nullptr) { + return; + } + + auto serachName = overloadDecl->Id()->Name().Mutf8(); + auto hasSameNameMethod = + std::find_if(overloadDecl->OverloadedList().begin(), overloadDecl->OverloadedList().end(), + [serachName](ir::Expression *overloadId) { + return overloadId->IsIdentifier() && overloadId->AsIdentifier()->Name().Is(serachName); + }); + if (hasSameNameMethod == overloadDecl->OverloadedList().end()) { + checker->LogError(diagnostic::OVERLOAD_SAME_NAME_METHOD, {serachName}, overloadDecl->Start()); + } +} + +checker::Type *ETSAnalyzer::Check(ir::OverloadDeclaration *node) const +{ + ETSChecker *checker = GetETSChecker(); + ES2PANDA_ASSERT(node != nullptr); + ES2PANDA_ASSERT(node->Key()); + + CheckDuplicationInOverloadDeclaration(checker, node); + CheckOverloadSameNameMethod(checker, node); + + if (node->IsConstructorOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->IsClassDefinition()); + checker->CheckConstructorOverloadDeclaration(checker, node); + } else if (node->IsFunctionOverloadDeclaration()) { + ES2PANDA_ASSERT( + node->Parent()->IsClassDefinition() && + (compiler::HasGlobalClassParent(node) || node->Parent()->AsClassDefinition()->IsNamespaceTransformed())); + checker->CheckFunctionOverloadDeclaration(checker, node); + } else if (node->IsClassMethodOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->IsClassDefinition()); + checker->CheckClassMethodOverloadDeclaration(checker, node); + } else if (node->IsInterfaceMethodOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->Parent()->IsTSInterfaceDeclaration()); + checker->CheckInterfaceMethodOverloadDeclaration(checker, node); + } + + return checker->CreateSyntheticTypeFromOverload(node->Id()->Variable()); +} + checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::Property *expr) const { ETSChecker *checker = GetETSChecker(); @@ -324,7 +411,11 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const } ETSChecker *checker = GetETSChecker(); - Type *exprType = expr->Argument()->Check(checker); + if (expr->PreferredType() != nullptr) { + expr->Argument()->SetPreferredType(expr->PreferredType()); + } + auto type = expr->Argument()->Check(checker); + Type *exprType = type->MaybeBaseTypeOfGradualType(); if (exprType->IsETSResizableArrayType()) { return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front()); @@ -338,7 +429,7 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const return checker->InvalidateType(expr); } - checker::Type *const elementType = exprType->IsETSTupleType() ? exprType : checker->GetElementTypeOfArray(exprType); + checker::Type *const elementType = exprType->IsETSTupleType() ? type : checker->GetElementTypeOfArray(exprType); return expr->SetTsType(elementType); } @@ -385,6 +476,10 @@ checker::Type *ETSAnalyzer::Check(ir::ETSFunctionType *node) const checker->CheckFunctionSignatureAnnotations(node->Params(), node->TypeParams(), node->ReturnType()); auto *signatureInfo = checker->ComposeSignatureInfo(node->TypeParams(), node->Params()); + if (signatureInfo == nullptr) { + ES2PANDA_ASSERT(GetChecker()->IsAnyError()); + return node->SetTsType(checker->GlobalTypeError()); + } auto *returnType = node->IsExtensionFunction() && node->ReturnType()->IsTSThisType() ? signatureInfo->params.front()->TsType() : checker->ComposeReturnType(node->ReturnType(), node->IsAsync()); @@ -407,7 +502,7 @@ static bool CheckArrayElementType(ETSChecker *checker, T *newArrayInstanceExpr) ES2PANDA_ASSERT(checker != nullptr); ES2PANDA_ASSERT(newArrayInstanceExpr != nullptr); - checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker); + checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker)->MaybeBaseTypeOfGradualType(); ES2PANDA_ASSERT(elementType != nullptr); if (elementType->IsETSPrimitiveType()) { return true; @@ -449,17 +544,20 @@ static bool NeedCreateETSResizableArrayType(ETSChecker *checker, Type *type) checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const { + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + ETSChecker *checker = GetETSChecker(); auto *elementType = expr->TypeReference()->GetType(checker); checker->ValidateArrayIndex(expr->Dimension(), true); CheckArrayElementType(checker, expr); - GetUnionPreferredType(expr, expr->GetPreferredType()); - - auto *preferredType = expr->GetPreferredType(); + auto *preferredType = GetAppropriatePreferredType( + expr->PreferredType(), [](Type *tp) -> bool { return tp->IsETSArrayType() || tp->IsETSResizableArrayType(); }); - if (NeedCreateETSResizableArrayType(checker, expr->GetPreferredType()) || + if (NeedCreateETSResizableArrayType(checker, expr->PreferredType()) || preferredType == nullptr || preferredType->IsETSResizableArrayType()) { expr->SetTsType(checker->CreateETSResizableArrayType(elementType)); } else { @@ -474,10 +572,13 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewClassInstanceExpression *expr) { - checker::Type *calleeType = expr->GetTypeRef()->Check(checker); + checker::Type *res = expr->GetTypeRef()->Check(checker); + auto calleeType = res->MaybeBaseTypeOfGradualType(); if (calleeType->IsTypeError()) { - return checker->InvalidateType(expr->GetTypeRef()); + checker->LogError(diagnostic::INVALID_TYPE_REF, {}, expr->GetTypeRef()->Start()); + return calleeType; } + if (calleeType->IsETSUnionType()) { return checker->TypeError(expr->GetTypeRef(), diagnostic::UNION_NONCONSTRUCTIBLE, expr->Start()); } @@ -505,7 +606,7 @@ static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewCl return checker->GlobalTypeError(); } - return calleeType; + return res; } checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const @@ -513,43 +614,34 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const if (expr->TsType() != nullptr) { return expr->TsType(); } + ETSChecker *checker = GetETSChecker(); auto *calleeType = CheckInstantiatedNewType(checker, expr); - if (calleeType->IsTypeError()) { - return checker->InvalidateType(expr); - } - auto *calleeObj = calleeType->AsETSObjectType(); - expr->SetTsType(calleeObj); + FORWARD_TYPE_ERROR(checker, calleeType, expr); - if (calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl()) { - auto lang = calleeType->AsETSDynamicType()->Language(); - expr->SetSignature(checker->ResolveDynamicCallExpression(expr->GetTypeRef(), expr->GetArguments(), lang, true)); - } else { - auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); + auto *calleeObj = calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + expr->SetTsType(calleeType); - if (signature == nullptr) { - return checker->InvalidateType(expr); - } + auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); - checker->CheckObjectLiteralArguments(signature, expr->GetArguments()); + if (signature == nullptr) { + return checker->InvalidateType(expr); + } - checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start()); + checker->CheckObjectLiteralArguments(signature, expr->GetArguments()); - if (calleeType->IsETSDynamicType()) { - ES2PANDA_ASSERT(signature->Function()->IsDynamic()); - auto lang = calleeType->AsETSDynamicType()->Language(); - expr->SetSignature( - checker->ResolveDynamicCallExpression(expr->GetTypeRef(), signature->Params(), lang, true)); - } else { - expr->SetSignature(signature); - } - } + checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start()); + + expr->SetSignature(signature); return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::ETSNewMultiDimArrayInstanceExpression *expr) const { + if (expr->TsType() != nullptr) { + return expr->TsType(); + } ETSChecker *checker = GetETSChecker(); CheckArrayElementType(checker, expr); @@ -560,11 +652,10 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewMultiDimArrayInstanceExpression *exp checker->ValidateArrayIndex(dim, true); fixedArrayType = checker->CreateETSArrayType(fixedArrayType); } - GetUnionPreferredType(expr, expr->GetPreferredType()); + auto *preferredType = GetAppropriatePreferredType( + expr->PreferredType(), [](Type *tp) -> bool { return tp->IsETSArrayType() || tp->IsETSResizableArrayType(); }); - auto *preferredType = expr->GetPreferredType(); - if (NeedCreateETSResizableArrayType(checker, expr->GetPreferredType()) || - preferredType->IsETSResizableArrayType()) { + if (NeedCreateETSResizableArrayType(checker, preferredType) || preferredType->IsETSResizableArrayType()) { expr->SetTsType(checker->CreateETSMultiDimResizableArrayType(elementType, expr->Dimensions().size())); } else { expr->SetTsType(fixedArrayType); @@ -635,11 +726,7 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNonNullishTypeNode *node) const return node->TsType(); } ETSChecker *checker = GetETSChecker(); - checker::Type *originalType = node->GetTypeNode()->Check(checker); - if (!originalType->IsETSTypeParameter()) { - checker->LogError(diagnostic::ILLEGAL_NON_NULLISH_TYPE, {}, node->GetTypeNode()->Start()); - } - return node->SetTsType(checker->GetNonNullishType(originalType)); + return node->SetTsType(checker->GetNonNullishType(node->GetTypeNode()->Check(checker))); } checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const @@ -677,11 +764,6 @@ checker::Type *ETSAnalyzer::Check(ir::ETSKeyofType *node) const // compile methods for EXPRESSIONS in alphabetical order -checker::Type *ETSAnalyzer::GetPreferredType(ir::ArrayExpression *expr) const -{ - return expr->preferredType_; -} - static void AddSpreadElementTypes(ETSChecker *checker, ir::SpreadElement *const element, ArenaVector> &elementTypes) { @@ -692,7 +774,7 @@ static void AddSpreadElementTypes(ETSChecker *checker, ir::SpreadElement *const return; } - Type *const spreadArgumentType = element->Argument()->TsType(); + Type *const spreadArgumentType = element->Argument()->TsType()->MaybeBaseTypeOfGradualType(); if (spreadArgumentType->IsETSTupleType()) { for (Type *type : spreadArgumentType->AsETSTupleType()->GetTupleTypesList()) { @@ -721,31 +803,30 @@ static ArenaVector> GetElementTypes(ETSCheck { ArenaVector> elementTypes(checker->ProgramAllocator()->Adapter()); - for (std::size_t idx = 0; idx < expr->Elements().size(); ++idx) { + auto *const exprPreferredType = expr->PreferredType(); + auto *const exprTupleType = exprPreferredType->IsETSTupleType() ? exprPreferredType->AsETSTupleType() : nullptr; + checker::Type *elemPreferredType = + exprPreferredType->IsETSTupleType() ? nullptr : checker->GetElementTypeOfArray(exprPreferredType); + + for (std::size_t idx = 0U; idx < expr->Elements().size(); ++idx) { ir::Expression *const element = expr->Elements()[idx]; if (element->IsSpreadElement()) { + element->SetPreferredType(exprPreferredType); AddSpreadElementTypes(checker, element->AsSpreadElement(), elementTypes); continue; } - auto *const exprPreferredType = expr->GetPreferredType(); - - if (expr->GetPreferredType()->IsETSTupleType() && - idx < expr->GetPreferredType()->AsETSTupleType()->GetTupleSize() && - !ValidArrayExprSizeForTupleSize(checker, exprPreferredType->AsETSTupleType()->GetTypeAtIndex(idx), - element)) { - elementTypes.emplace_back(checker->GlobalTypeError(), element); - continue; - } - - if (element->IsArrayExpression() || element->IsObjectExpression()) { - auto *const targetPreferredType = exprPreferredType->IsETSTupleType() - ? exprPreferredType->AsETSTupleType()->GetTypeAtIndex(idx) - : checker->GetElementTypeOfArray(exprPreferredType); - ETSChecker::SetPreferredTypeIfPossible(element, targetPreferredType); + if (exprTupleType != nullptr && exprPreferredType->IsETSTupleType()) { + if (idx >= exprTupleType->GetTupleSize() || + !ValidArrayExprSizeForTupleSize(checker, exprTupleType->GetTypeAtIndex(idx), element)) { + elementTypes.emplace_back(element->SetTsType(checker->GlobalTypeError()), element); + continue; + } + elemPreferredType = exprTupleType->GetTypeAtIndex(idx); } + element->SetPreferredType(elemPreferredType); elementTypes.emplace_back(element->Check(checker), element); } @@ -855,41 +936,43 @@ static bool CheckArrayExpressionElements(ETSChecker *checker, ir::ArrayExpressio [](auto &pair) { return pair.first->IsTypeError(); }); for (std::size_t idx = 0; idx < arrayExprElementTypes.size(); ++idx) { - allElementsAssignable &= CheckElement(checker, arrayExpr->GetPreferredType(), arrayExprElementTypes, idx); + allElementsAssignable &= CheckElement(checker, arrayExpr->PreferredType(), arrayExprElementTypes, idx); } return allElementsAssignable; } -static bool IsPossibleArrayExpressionType(Type const *type) +static Type *GetAppropriatePreferredType(Type *originalType, std::function const &predicate) { - return type->IsETSArrayType() || type->IsETSTupleType() || type->IsETSResizableArrayType(); -} + if (originalType == nullptr) { + return nullptr; + } -void ETSAnalyzer::GetUnionPreferredType(ir::Expression *expr, Type *originalType) const -{ - if (originalType == nullptr || !originalType->IsETSUnionType()) { - return; + while (originalType->IsETSTypeAliasType()) { + if (predicate(originalType)) { + return originalType; + } + originalType = originalType->AsETSTypeAliasType()->GetTargetType(); + } + + if (predicate(originalType)) { + return originalType; } - checker::Type *preferredType = nullptr; + + if (!originalType->IsETSUnionType()) { + return nullptr; + } + + Type *preferredType = nullptr; for (auto &type : originalType->AsETSUnionType()->ConstituentTypes()) { - if (IsPossibleArrayExpressionType(type)) { + if (predicate(type)) { if (preferredType != nullptr) { - preferredType = nullptr; - break; + return nullptr; // ambiguity } preferredType = type; } } - if (expr->IsArrayExpression()) { - expr->AsArrayExpression()->SetPreferredType(preferredType); - } else if (expr->IsETSNewArrayInstanceExpression()) { - expr->AsETSNewArrayInstanceExpression()->SetPreferredType(preferredType); - } else if (expr->IsETSNewMultiDimArrayInstanceExpression()) { - expr->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(preferredType); - } else { - ES2PANDA_UNREACHABLE(); - } + return preferredType; } checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const @@ -899,44 +982,39 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const return expr->TsType(); } - if (expr->GetPreferredType() != nullptr) { - if (expr->GetPreferredType()->IsETSTypeAliasType()) { - expr->SetPreferredType(expr->GetPreferredType()->AsETSTypeAliasType()->GetTargetType()); - } - - if (expr->GetPreferredType()->IsETSUnionType()) { - GetUnionPreferredType(expr, expr->GetPreferredType()); - } + auto *preferredType = GetAppropriatePreferredType(expr->PreferredType(), &Type::IsAnyETSArrayOrTupleType); - if (expr->GetPreferredType() != nullptr && !IsPossibleArrayExpressionType(expr->GetPreferredType())) { - expr->SetPreferredType(nullptr); - } + if (preferredType != nullptr && preferredType->IsETSReadonlyArrayType()) { + const auto elementType = preferredType->AsETSObjectType()->TypeArguments().front(); + preferredType = checker->CreateETSResizableArrayType(elementType); } - if (!IsArrayExpressionValidInitializerForType(checker, expr->GetPreferredType())) { - checker->LogError(diagnostic::UNEXPECTED_ARRAY, {expr->GetPreferredType()}, expr->Start()); + if (!IsArrayExpressionValidInitializerForType(checker, preferredType)) { + checker->LogError(diagnostic::UNEXPECTED_ARRAY, {expr->PreferredType()}, expr->Start()); return checker->InvalidateType(expr); } if (!expr->Elements().empty()) { - if (expr->GetPreferredType() == nullptr || expr->GetPreferredType() == checker->GlobalETSObjectType()) { - expr->SetPreferredType(InferPreferredTypeFromElements(checker, expr)); + if (preferredType == nullptr || preferredType == checker->GlobalETSObjectType()) { + preferredType = InferPreferredTypeFromElements(checker, expr); } - if (!ValidArrayExprSizeForTupleSize(checker, expr->GetPreferredType(), expr) || - !CheckArrayExpressionElements(checker, expr)) { - return checker->InvalidateType(expr); - } + expr->SetPreferredType(preferredType); } - if (expr->GetPreferredType() == nullptr) { + if (preferredType == nullptr) { return checker->TypeError(expr, diagnostic::UNRESOLVABLE_ARRAY, expr->Start()); } - expr->SetTsType(expr->GetPreferredType()); - if (!expr->GetPreferredType()->IsETSResizableArrayType() && !expr->TsType()->IsETSTupleType()) { - ES2PANDA_ASSERT(expr->TsType()->IsETSArrayType()); - const auto *const arrayType = expr->TsType()->AsETSArrayType(); + if (!ValidArrayExprSizeForTupleSize(checker, preferredType, expr) || + (!expr->Elements().empty() && !CheckArrayExpressionElements(checker, expr))) { + return checker->InvalidateType(expr); + } + + expr->SetTsType(preferredType); + if (!preferredType->IsETSResizableArrayType() && !preferredType->IsETSTupleType()) { + ES2PANDA_ASSERT(preferredType->IsETSArrayType()); + const auto *const arrayType = preferredType->AsETSArrayType(); checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank()); } return expr->TsType(); @@ -1023,19 +1101,23 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const checker->Context().SetContainingSignature(signature); expr->Function()->Body()->Check(checker); + if (expr->Function()->ReturnTypeAnnotation() == nullptr) { + if (expr->Function()->IsAsyncFunc()) { + auto *retType = signature->ReturnType(); + if (!retType->IsETSObjectType() || + retType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) { + auto returnType = checker->CreateETSAsyncFuncReturnTypeFromBaseType(signature->ReturnType()); + ES2PANDA_ASSERT(returnType != nullptr); + expr->Function()->Signature()->SetReturnType(returnType->PromiseType()); + for (auto &returnStatement : expr->Function()->ReturnStatements()) { + returnStatement->SetReturnType(checker, returnType); + } + } + } + } auto *funcType = checker->CreateETSArrowType(signature); checker->Context().SetContainingSignature(nullptr); - - if (expr->Function()->IsAsyncFunc()) { - auto *retType = signature->ReturnType(); - if (!retType->IsETSObjectType() || - retType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) { - checker->LogError(diagnostic::ASYNC_DOESNT_PROMISE, {}, expr->Function()->Start()); - expr->SetTsType(checker->GlobalTypeError()); - return expr->TsType(); - } - } expr->SetTsType(funcType); return expr->TsType(); } @@ -1073,7 +1155,10 @@ checker::Type *ETSAnalyzer::GetSmartType(ir::AssignmentExpression *expr, checker if (expr->Left()->IsIdentifier() && expr->Target() != nullptr) { // Now try to define the actual type of Identifier so that smart cast can be used in further checker // processing - smartType = checker->ResolveSmartType(rightType, leftType); + auto const value = expr->Right()->IsNumberLiteral() + ? std::make_optional(expr->Right()->AsNumberLiteral()->Number().GetDouble()) + : std::nullopt; + smartType = checker->ResolveSmartType(rightType, leftType, value); auto const *const variable = expr->Target(); // Add/Remove/Modify smart cast for identifier @@ -1094,25 +1179,55 @@ checker::Type *ETSAnalyzer::GetSmartType(ir::AssignmentExpression *expr, checker return smartType; } -checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const +static bool IsMethodDefinition(const ir::Expression *const expression) { - if (expr->TsType() != nullptr) { - return expr->TsType(); + return expression->IsMemberExpression() && expression->AsMemberExpression()->Property() != nullptr && + expression->AsMemberExpression()->Property()->Variable() != nullptr && + expression->AsMemberExpression()->Property()->Variable()->Declaration() != nullptr && + expression->AsMemberExpression()->Property()->Variable()->Declaration()->Node()->IsMethodDefinition(); +} + +static bool IsInvalidMethodAssignment(const ir::AssignmentExpression *const expr, ETSChecker *checker) +{ + auto left = expr->Left(); + if (IsMethodDefinition(left)) { + { + auto methodDefinition = + left->AsMemberExpression()->Property()->Variable()->Declaration()->Node()->AsMethodDefinition(); + if (!methodDefinition->IsSetter() && + std::none_of(methodDefinition->Overloads().cbegin(), methodDefinition->Overloads().cend(), + [](const auto *overload) { return overload->IsSetter(); })) { + checker->LogError(diagnostic::METHOD_ASSIGNMENT, expr->Left()->Start()); + return true; + } + } } - ETSChecker *checker = GetETSChecker(); + return false; +} - if (checker->HasStatus(CheckerStatus::IN_SETTER) && expr->Left()->IsMemberExpression()) { - checker->WarnForEndlessLoopInGetterSetter(expr->Left()->AsMemberExpression()); +// In assignment expression or object literal, we need the type of the setter instead of the type of the getter +static checker::Type *GetSetterType(varbinder::Variable *const var, ETSChecker *checker) +{ + if (var == nullptr || !checker->IsVariableGetterSetter(var)) { + return nullptr; } - const auto leftType = expr->Left()->Check(checker); - - if (IsInvalidArrayMemberAssignment(expr, checker)) { - expr->SetTsType(checker->GlobalTypeError()); - return expr->TsType(); + if (var->TsType()->IsETSFunctionType()) { + auto *funcType = var->TsType()->AsETSFunctionType(); + if (funcType->HasTypeFlag(checker::TypeFlag::SETTER)) { + auto *setter = funcType->FindSetter(); + ES2PANDA_ASSERT(setter != nullptr && setter->Params().size() == 1); + return setter->Params()[0]->TsType(); + } } + return nullptr; +} + +// Helper to set the target of assignment expression +bool ETSAnalyzer::SetAssignmentExpressionTarget(ir::AssignmentExpression *const expr, ETSChecker *checker) const +{ if (expr->Left()->IsIdentifier()) { expr->target_ = expr->Left()->AsIdentifier()->Variable(); } else if (expr->Left()->IsMemberExpression()) { @@ -1122,13 +1237,45 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const } expr->target_ = expr->Left()->AsMemberExpression()->PropVar(); } else { + return false; + } + return true; +} + +checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const +{ + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + ETSChecker *checker = GetETSChecker(); + + if (checker->HasStatus(CheckerStatus::IN_SETTER) && expr->Left()->IsMemberExpression()) { + checker->WarnForEndlessLoopInGetterSetter(expr->Left()->AsMemberExpression()); + } + + checker::Type *leftType = expr->Left()->Check(checker); + + if (IsInvalidArrayMemberAssignment(expr, checker) || IsInvalidMethodAssignment(expr, checker)) { + expr->SetTsType(checker->GlobalTypeError()); + return expr->TsType(); + } + + if (!SetAssignmentExpressionTarget(expr, checker)) { checker->LogError(diagnostic::ASSIGNMENT_INVALID_LHS, {}, expr->Left()->Start()); expr->SetTsType(checker->GlobalTypeError()); return expr->TsType(); } if (expr->target_ != nullptr && !expr->IsIgnoreConstAssign()) { - checker->ValidateUnaryOperatorOperand(expr->target_); + checker->ValidateUnaryOperatorOperand(expr->target_, expr); + } + + checker->InferLambdaInAssignmentExpression(expr); + + if (auto setterType = GetSetterType(expr->target_, checker); setterType != nullptr) { + leftType = setterType; + expr->Left()->SetTsType(leftType); } auto [rightType, relationNode] = CheckAssignmentExprOperatorType(expr, leftType); @@ -1139,7 +1286,10 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const CastPossibleTupleOnRHS(checker, expr); checker::Type *smartType = rightType; - if (!leftType->IsTypeError()) { + auto isLazyImportObject = + leftType->MaybeBaseTypeOfGradualType()->IsETSObjectType() && + leftType->MaybeBaseTypeOfGradualType()->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT); + if (!leftType->IsTypeError() && !isLazyImportObject) { if (const auto ctx = checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType, expr->Right()->Start(), {{diagnostic::INVALID_ASSIGNMNENT, {rightType, leftType}}}); @@ -1157,22 +1307,22 @@ static checker::Type *HandleSubstitution(ETSChecker *checker, ir::AssignmentExpr leftType->IsETSTupleType() || leftType->IsETSUnionType(); if (expr->Right()->IsArrayExpression() && possibleInferredTypeOfArray) { checker->ModifyPreferredType(expr->Right()->AsArrayExpression(), leftType); - } - - if (expr->Right()->IsETSNewArrayInstanceExpression()) { - expr->Right()->AsETSNewArrayInstanceExpression()->SetPreferredType(leftType); - } - - if (expr->Right()->IsETSNewMultiDimArrayInstanceExpression()) { - expr->Right()->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(leftType); - } - - if (expr->Right()->IsObjectExpression()) { + } else if (expr->Right()->IsArrowFunctionExpression() && + (leftType->IsETSArrowType() || leftType->IsETSUnionType())) { + if (auto *preferredType = GetAppropriatePreferredType(leftType, [](Type *tp) { return tp->IsETSArrowType(); }); + preferredType != nullptr) { + checker->TryInferTypeForLambdaTypeAlias(expr->Right()->AsArrowFunctionExpression(), + preferredType->AsETSFunctionType()); + } + } else if (expr->Right()->IsObjectExpression()) { + if (leftType->IsETSObjectType() && leftType->IsGradualType() && + (leftType->HasTypeFlag(TypeFlag::READONLY) || + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::REQUIRED))) { + checker->LogError(diagnostic::DYMANIC_INIT_WITH_OBJEXPR, {leftType}, expr->Right()->Start()); + } expr->Right()->AsObjectExpression()->SetPreferredType(leftType); - } - - if (expr->Right()->IsArrowFunctionExpression() && (leftType->IsETSArrowType() || leftType->IsETSUnionType())) { - expr->Right()->AsArrowFunctionExpression()->SetPreferredType(leftType); + } else { + expr->Right()->SetPreferredType(leftType); } return expr->Right()->Check(checker); @@ -1199,10 +1349,7 @@ std::tuple ETSAnalyzer::CheckAssignmentExprOperatorTyp case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { std::tie(std::ignore, expr->operationType_) = checker->CheckBinaryOperator( expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start(), true); - - auto unboxedLeft = checker->MaybeUnboxInRelation(leftType); - sourceType = unboxedLeft == nullptr ? leftType : unboxedLeft; - + sourceType = leftType; relationNode = expr; break; } @@ -1233,7 +1380,6 @@ checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const } checker::Type *argType = checker->GetApparentType(expr->argument_->Check(checker)); - ES2PANDA_ASSERT(argType != nullptr); ArenaVector awaitedTypes(checker->ProgramAllocator()->Adapter()); if (argType->IsETSUnionType()) { @@ -1314,10 +1460,10 @@ checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const } } - checker::Type *newTsType {nullptr}; - std::tie(newTsType, expr->operationType_) = + auto [newTsType, operationType] = checker->CheckBinaryOperator(expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start()); - expr->SetTsType(newTsType); + expr->SetTsType(checker->MaybeBoxType(newTsType)); + expr->SetOperationType(checker->MaybeBoxType(operationType)); checker->Context().CheckBinarySmartCastCondition(expr); @@ -1357,6 +1503,18 @@ static bool LambdaIsField(ir::CallExpression *expr) return me->PropVar() != nullptr; } +static bool OverloadDeclaration(ir::Expression *expr) +{ + while (expr->IsMemberExpression()) { + expr = expr->AsMemberExpression()->Property(); + } + + if (expr->IsIdentifier() && expr->AsIdentifier()->Variable() != nullptr) { + return expr->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::OVERLOAD); + } + return false; +} + checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallExpression *expr, checker::Type *calleeType) const { @@ -1370,6 +1528,10 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE return checker->ResolveCallExpressionAndTrailingLambda(checker->GetOverloadSigContainer(), expr, expr->Start()); } + if (calleeType->IsETSFunctionType() && OverloadDeclaration(expr->Callee())) { + return checker->FirstMatchSignatures(expr, calleeType); + } + if (calleeType->IsETSExtensionFuncHelperType()) { auto *signature = ResolveCallForETSExtensionFuncHelperType(calleeType->AsETSExtensionFuncHelperType(), checker, expr); @@ -1397,7 +1559,7 @@ checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallE static ETSObjectType *GetCallExpressionCalleeObject(ETSChecker *checker, ir::CallExpression *expr, Type *calleeType) { if (expr->IsETSConstructorCall()) { - return calleeType->AsETSObjectType(); + return calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } auto callee = expr->Callee(); if (callee->IsMemberExpression()) { @@ -1422,6 +1584,7 @@ Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, Type *calleeType) con } Signature *const signature = ResolveSignature(checker, expr, calleeType); + if (signature == nullptr) { return checker->GlobalTypeError(); } @@ -1433,13 +1596,7 @@ Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, Type *calleeType) con checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start()); } - if (calleeType->IsETSMethodType() && signature->Function()->IsDynamic()) { - ES2PANDA_ASSERT(signature->Function()->IsDynamic()); - auto lang = signature->Function()->Language(); - expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), signature->Params(), lang, false)); - } else { - expr->SetSignature(signature); - } + expr->SetSignature(signature); // #22951: this type should not be encoded as a signature flag if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) { @@ -1466,10 +1623,15 @@ static void CheckAbstractCall(ETSChecker *checker, ir::CallExpression *expr) static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr) { checker->CheckNonNullish(expr->Callee()); - if (expr->Callee()->IsMemberExpression() && expr->Callee()->AsMemberExpression()->Object() != nullptr && - expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSObjectType() && - expr->Callee()->AsMemberExpression()->Object()->TsType()->AsETSObjectType()->HasObjectFlag( - ETSObjectFlags::READONLY)) { + if (!expr->Callee()->IsMemberExpression()) { + return; + } + auto memberExpr = expr->Callee()->AsMemberExpression(); + if (memberExpr->Object() == nullptr) { + return; + } + auto baseType = memberExpr->Object()->TsType()->MaybeBaseTypeOfGradualType(); + if (baseType->IsETSObjectType() && baseType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::READONLY)) { checker->LogError(diagnostic::READONLY_CALL, {}, expr->Start()); expr->SetTsType(checker->GlobalTypeError()); } @@ -1496,16 +1658,7 @@ static checker::SavedCheckerContext ReconstructOwnerClassContext(ETSChecker *che checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr, checker::Type *calleeType) const { ETSChecker *checker = GetETSChecker(); - checker::Type *returnType = nullptr; - if (UNLIKELY(calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl())) { - // Trailing lambda for js function call is not supported, check the correctness of `foo() {}` - checker->EnsureValidCurlyBrace(expr); - auto lang = calleeType->AsETSDynamicType()->Language(); - expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), expr->Arguments(), lang, false)); - returnType = expr->Signature()->ReturnType(); - } else { - returnType = GetReturnType(expr, calleeType); - } + checker::Type *returnType = GetReturnType(expr, calleeType); if (returnType->IsTypeError()) { return checker->GlobalTypeError(); @@ -1552,6 +1705,25 @@ checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr // NOTE(vpukhov): #14902 substituted signature is not updated } +static void CheckOverloadCall(ETSChecker *checker, ir::CallExpression *expr) +{ + if (!expr->Callee()->IsMemberExpression() || !OverloadDeclaration(expr->Callee())) { + return; + } + + auto *sig = expr->Signature(); + auto *functionNode = sig->OwnerVar()->Declaration()->Node(); + ir::AstNode *parent = functionNode->Parent(); + + bool isExported = functionNode->IsExported() || functionNode->IsDefaultExported(); + if (parent != nullptr && parent->IsClassDefinition() && parent->AsClassDefinition()->IsNamespaceTransformed() && + !parent->AsClassDefinition()->IsDeclare() && !isExported) { + checker->LogError(diagnostic::NOT_EXPORTED, + {sig->OwnerVar()->Declaration()->Name(), parent->AsClassDefinition()->Ident()->Name()}, + expr->Start()); + } +} + checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -1575,16 +1747,14 @@ checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const CheckCallee(checker, expr); checker::TypeStackElement tse(checker, expr, {{diagnostic::CYCLIC_CALLEE, {}}}, expr->Start()); - if (tse.HasTypeError()) { - expr->SetTsType(checker->GlobalTypeError()); - return checker->GlobalTypeError(); - } + ERROR_SANITY_CHECK(checker, !tse.HasTypeError(), return expr->SetTsType(checker->GlobalTypeError())); checker::Type *const returnType = GetCallExpressionReturnType(expr, calleeType); expr->SetTsType(returnType); if (returnType->IsTypeError()) { return returnType; } + if (calleeType->IsETSArrowType()) { expr->SetUncheckedType(checker->GuaranteedTypeForUncheckedCast( checker->GlobalETSAnyType(), checker->MaybeBoxType(expr->Signature()->ReturnType()))); @@ -1597,16 +1767,51 @@ checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const checker->ComputeApparentType(returnType); } - if (returnType->IsTypeError()) { - expr->SetTsType(returnType); - return expr->TsType(); - } - + CheckOverloadCall(checker, expr); CheckVoidTypeExpression(checker, expr); CheckAbstractCall(checker, expr); return expr->TsType(); } +static bool IsNumericType(ETSChecker *checker, Type *type) +{ + return checker->Relation()->IsSupertypeOf(checker->GetGlobalTypesHolder()->GlobalNumericBuiltinType(), type); +} + +static Type *BiggerNumericType(ETSChecker *checker, Type *t1, Type *t2) +{ + ES2PANDA_ASSERT(IsNumericType(checker, t1)); + ES2PANDA_ASSERT(IsNumericType(checker, t2)); + + auto *rel = checker->Relation(); + + if (rel->IsSupertypeOf(checker->GlobalDoubleBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalDoubleBuiltinType(), t2)) { + return checker->GlobalDoubleBuiltinType(); + } + if (rel->IsSupertypeOf(checker->GlobalFloatBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalFloatBuiltinType(), t2)) { + return checker->GlobalFloatBuiltinType(); + } + if (rel->IsSupertypeOf(checker->GlobalLongBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalLongBuiltinType(), t2)) { + return checker->GlobalLongBuiltinType(); + } + if (rel->IsSupertypeOf(checker->GlobalIntBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalIntBuiltinType(), t2)) { + return checker->GlobalIntBuiltinType(); + } + if (rel->IsSupertypeOf(checker->GlobalShortBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalShortBuiltinType(), t2)) { + return checker->GlobalShortBuiltinType(); + } + if (rel->IsSupertypeOf(checker->GlobalByteBuiltinType(), t1) || + rel->IsSupertypeOf(checker->GlobalByteBuiltinType(), t2)) { + return checker->GlobalByteBuiltinType(); + } + ES2PANDA_UNREACHABLE(); +} + checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const { if (expr->TsType() != nullptr) { @@ -1644,21 +1849,10 @@ checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const if (checker->IsTypeIdenticalTo(consequentType, alternateType)) { expr->SetTsType(consequentType); + } else if (IsNumericType(GetETSChecker(), consequentType) && IsNumericType(GetETSChecker(), alternateType)) { + expr->SetTsType(BiggerNumericType(GetETSChecker(), consequentType, alternateType)); } else { - // If possible and required update number literal type to the proper value (identical to left-side type) - if (alternate->IsNumberLiteral() && - checker->AdjustNumberLiteralType(alternate->AsNumberLiteral(), alternateType, consequentType)) { - expr->SetTsType(consequentType); - } else if (consequent->IsNumberLiteral() && - checker->AdjustNumberLiteralType(consequent->AsNumberLiteral(), consequentType, alternateType)) { - expr->SetTsType(alternateType); - } else { - expr->SetTsType(checker->CreateETSUnionType({consequentType, alternateType})); - if (expr->TsType()->IsETSReferenceType()) { - checker->MaybeBoxExpression(expr->Consequent()); - checker->MaybeBoxExpression(expr->Alternate()); - } - } + expr->SetTsType(checker->CreateETSUnionType({consequentType, alternateType})); } // Restore smart casts to initial state. @@ -1672,6 +1866,9 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression { ES2PANDA_ASSERT(use->IsIdentifier() || use->IsMemberExpression()); if (!type->IsETSMethodType()) { + if (use->Parent()->IsCallExpression() && type->IsETSObjectType() && use->IsMemberExpression()) { + checker->ValidateCallExpressionIdentifier(use->AsMemberExpression()->Property()->AsIdentifier(), type); + } return type; } auto const getUseSite = [use]() { @@ -1685,12 +1882,19 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression if (expr->Parent()->IsCallExpression() && expr->Parent()->AsCallExpression()->Callee() == expr) { return type; // type is actually used as method } + if (expr->Parent()->IsOverloadDeclaration()) { + return type; // Don't trans overloaded name to arrow type. + } auto *const functionType = type->AsETSFunctionType(); auto &signatures = functionType->CallSignatures(); if (signatures.at(0)->HasSignatureFlag(SignatureFlags::PRIVATE)) { - checker->LogError(diagnostic::PRIVATE_METHOD_AS_VALUE, getUseSite()); + checker->LogError(diagnostic::PRIVATE_OR_PROTECTED_METHOD_AS_VALUE, {"Private"}, getUseSite()); + return checker->GlobalTypeError(); + } + if (signatures.at(0)->HasSignatureFlag(SignatureFlags::PROTECTED)) { + checker->LogError(diagnostic::PRIVATE_OR_PROTECTED_METHOD_AS_VALUE, {"Protected"}, getUseSite()); return checker->GlobalTypeError(); } @@ -1708,7 +1912,8 @@ static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression checker->LogError(diagnostic::OVERLOADED_METHOD_AS_VALUE, getUseSite()); return checker->GlobalTypeError(); } - return type->AsETSFunctionType()->MethodToArrow(checker); + auto *otherFuncType = functionType->MethodToArrow(checker); + return otherFuncType == nullptr ? checker->GlobalTypeError() : otherFuncType; } checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const @@ -1735,6 +1940,7 @@ checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const ES2PANDA_ASSERT(identType != nullptr); expr->SetTsType(identType); + ES2PANDA_ASSERT(identType != nullptr); if (!identType->IsTypeError()) { checker->Context().CheckIdentifierSmartCastCondition(expr); } @@ -1742,7 +1948,7 @@ checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const } std::pair SearchReExportsType(ETSObjectType *baseType, ir::MemberExpression *expr, - util::StringView &aliasName, ETSChecker *checker) + util::StringView const &aliasName, ETSChecker *checker) { std::pair ret {}; @@ -1783,9 +1989,13 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke return checker->InvalidateType(expr); } + if (baseType->IsGradualType()) { + return ResolveMemberExpressionByBaseType(checker, baseType->AsGradualType()->GetBaseType(), expr); + } + if (baseType->IsETSArrayType()) { if (expr->Property()->AsIdentifier()->Name().Is("length")) { - return expr->AdjustType(checker, checker->GlobalIntType()); + return expr->AdjustType(checker, checker->GlobalIntBuiltinType()); } return expr->SetAndAdjustType(checker, checker->GlobalETSObjectType()); @@ -1821,8 +2031,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); @@ -1858,7 +2067,7 @@ checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const expr->Property()->AsIdentifier()->Name(), checker); reExportType.first != nullptr) { baseType = reExportType.first; - expr->object_->AsIdentifier()->SetTsType(baseType); + expr->object_->SetTsType(baseType); expr->property_->AsIdentifier()->SetName(reExportType.second); } } @@ -1876,11 +2085,6 @@ checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const return ResolveMemberExpressionByBaseType(checker, baseType, expr); } -checker::Type *ETSAnalyzer::PreferredType(ir::ObjectExpression *expr) const -{ - return expr->preferredType_; -} - checker::Type *ETSAnalyzer::CheckDynamic(ir::ObjectExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -1898,7 +2102,7 @@ checker::Type *ETSAnalyzer::CheckDynamic(ir::ObjectExpression *expr) const static bool ValidatePreferredType(ETSChecker *checker, ir::ObjectExpression *expr) { - auto preferredType = expr->PreferredType(); + auto preferredType = expr->PreferredType()->MaybeBaseTypeOfGradualType(); if (preferredType == nullptr) { checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start()); return false; @@ -1930,11 +2134,14 @@ static void SetTypeforRecordProperties(const ir::ObjectExpression *expr, checker recordPropertyExpr = recordProperty->AsProperty()->Value(); } else if (recordProperty->IsSpreadElement()) { recordPropertyExpr = recordProperty->AsSpreadElement()->Argument(); + } else if (recordProperty->IsIdentifier() && recordProperty->AsIdentifier()->IsErrorPlaceHolder()) { + ES2PANDA_ASSERT(checker->IsAnyError()); + continue; } else { ES2PANDA_UNREACHABLE(); } - ETSChecker::SetPreferredTypeIfPossible(recordPropertyExpr, valueType); + recordPropertyExpr->SetPreferredType(valueType); recordPropertyExpr->Check(checker); } } @@ -2119,12 +2326,12 @@ static bool AreAllRequiredInterfacePropertiesSatisfied(ir::ObjectExpression *exp auto allProperties = interfaceType->GetAllProperties(); // Create a set of property names provided in the object literal - std::unordered_set literalProperties; + std::unordered_set literalProperties; for (ir::Expression *propExpr : expr->Properties()) { if (propExpr->IsProperty()) { ir::Expression *key = propExpr->AsProperty()->Key(); if (auto optPname = GetPropertyNameFromKey(key); optPname.has_value()) { - literalProperties.insert(std::string(optPname.value().Utf8())); + literalProperties.insert(optPname.value().Utf8()); } } } @@ -2145,7 +2352,6 @@ static bool AreAllRequiredInterfacePropertiesSatisfied(ir::ObjectExpression *exp // Check that all required interface properties are satisfied for (auto *property : allProperties) { - std::string propName(property->Name().Utf8()); auto *propertyType = checker->GetTypeOfVariable(property); // Skip method types that aren't getters/setters (they make interface incompatible anyway) @@ -2156,7 +2362,7 @@ static bool AreAllRequiredInterfacePropertiesSatisfied(ir::ObjectExpression *exp } } // Check if this property is provided in the literal - bool isInLiteral = literalProperties.find(propName) != literalProperties.end(); + bool isInLiteral = literalProperties.find(property->Name().Utf8()) != literalProperties.end(); if (!isInLiteral) { // Property not in literal - check if it's optional or has default value bool isOptional = IsPropertyOptional(property, propertyType); @@ -2221,17 +2427,12 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check std::vector candidateObjectTypes; // Phase 1: Gather all ETSObjectTypes from the union for (auto *constituentType : unionType->ConstituentTypes()) { - if (constituentType->IsETSObjectType()) { - candidateObjectTypes.push_back(constituentType->AsETSObjectType()); + auto type = constituentType->MaybeBaseTypeOfGradualType(); + if (type->IsETSObjectType()) { + candidateObjectTypes.push_back(type->AsETSObjectType()); } } - if (candidateObjectTypes.empty()) { - // No ETSObjectTypes in the union at all - checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start()); - return nullptr; - } - std::vector matchingObjectTypes; // Phase 2: Filter candidates using the helper function for (auto *potentialObjType : candidateObjectTypes) { @@ -2257,7 +2458,11 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check static checker::ETSObjectType *ResolveObjectTypeFromPreferredType(ETSChecker *checker, ir::ObjectExpression *expr) { // Assume not null, checked by caller in Check() - checker::Type *preferredType = expr->PreferredType(); + checker::Type *preferredType = expr->PreferredType()->MaybeBaseTypeOfGradualType(); + + if (preferredType->IsETSAsyncFuncReturnType()) { + preferredType = preferredType->AsETSAsyncFuncReturnType()->GetPromiseTypeArg(); + } if (preferredType->IsETSAsyncFuncReturnType()) { preferredType = preferredType->AsETSAsyncFuncReturnType()->GetPromiseTypeArg(); @@ -2309,16 +2514,11 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const return expr->TsType(); } - if (!expr->PreferredType()->IsETSUnionType() && !expr->PreferredType()->IsETSDynamicType() && - !ValidatePreferredType(checker, expr)) { + if (!expr->PreferredType()->IsETSUnionType() && !ValidatePreferredType(checker, expr)) { expr->SetTsType(checker->GlobalTypeError()); return expr->TsType(); } - if (expr->PreferredType()->IsETSDynamicType() && !expr->PreferredType()->AsETSDynamicType()->HasDecl()) { - return CheckDynamic(expr); - } - checker::ETSObjectType *objType = ResolveObjectTypeFromPreferredType(checker, expr); if (objType == nullptr) { @@ -2396,7 +2596,11 @@ void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, return; } - ETSChecker::SetPreferredTypeIfPossible(value, propType); + if (auto setterType = GetSetterType(lv, checker); setterType != nullptr) { + propType = setterType; + } + + value->SetPreferredType(propType); propExpr->SetTsType(propType); key->SetTsType(propType); value->SetTsType(value->Check(checker)); @@ -2558,6 +2762,7 @@ static checker::Type *ComputeTypeOfType(ETSChecker *checker, checker::Type *argT { checker::Type *ret = nullptr; ArenaVector types(checker->ProgramAllocator()->Adapter()); + ES2PANDA_ASSERT(argType != nullptr); if (argType->IsETSUnionType()) { for (auto *it : argType->AsETSUnionType()->ConstituentTypes()) { checker::Type *elType = ComputeTypeOfType(checker, it); @@ -2592,16 +2797,14 @@ checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const auto argType = expr->argument_->Check(checker); const auto isCondExpr = expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK; - checker::Type *operandType = checker->ApplyUnaryOperatorPromotion(argType, true, true, isCondExpr); - auto unboxedOperandType = - isCondExpr ? checker->MaybeUnboxConditionalInRelation(argType) : checker->MaybeUnboxInRelation(argType); - + checker::Type *operandType = checker->ApplyUnaryOperatorPromotion(expr->argument_, argType, isCondExpr); if (argType != nullptr && argType->IsETSBigIntType() && argType->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL)) { switch (expr->OperatorType()) { case lexer::TokenType::PUNCTUATOR_MINUS: { checker::Type *type = checker->CreateETSBigIntLiteralType(argType->AsETSBigIntType()->GetValue()); ES2PANDA_ASSERT(type != nullptr); // We do not need this const anymore as we are negating the bigint object in runtime + ES2PANDA_ASSERT(type != nullptr); type->RemoveTypeFlag(checker::TypeFlag::CONSTANT); expr->argument_->SetTsType(type); expr->SetTsType(type); @@ -2627,11 +2830,6 @@ checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const } } - if ((argType != nullptr) && argType->IsETSObjectType() && (unboxedOperandType != nullptr) && - unboxedOperandType->IsETSPrimitiveType()) { - expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedOperandType)); - } - SetTsTypeForUnaryExpression(checker, expr, operandType); checker->Context().CheckUnarySmartCastCondition(expr); @@ -2647,25 +2845,23 @@ checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const } checker::Type *operandType = expr->argument_->Check(checker); - if (operandType->IsTypeError()) { - return checker->InvalidateType(expr); - } + FORWARD_TYPE_ERROR(checker, operandType, expr); if (expr->Argument()->IsIdentifier()) { - checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable()); + checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable(), expr); } else if (expr->Argument()->IsTSAsExpression()) { if (auto *const asExprVar = expr->Argument()->AsTSAsExpression()->Variable(); asExprVar != nullptr) { - checker->ValidateUnaryOperatorOperand(asExprVar); + checker->ValidateUnaryOperatorOperand(asExprVar, expr); } } else if (expr->Argument()->IsTSNonNullExpression()) { if (auto *const nonNullExprVar = expr->Argument()->AsTSNonNullExpression()->Variable(); nonNullExprVar != nullptr) { - checker->ValidateUnaryOperatorOperand(nonNullExprVar); + checker->ValidateUnaryOperatorOperand(nonNullExprVar, expr); } } else if (expr->Argument()->IsMemberExpression()) { varbinder::LocalVariable *propVar = expr->argument_->AsMemberExpression()->PropVar(); if (propVar != nullptr) { - checker->ValidateUnaryOperatorOperand(propVar); + checker->ValidateUnaryOperatorOperand(propVar, expr); } } else { ES2PANDA_ASSERT(checker->IsAnyError()); @@ -2683,11 +2879,6 @@ checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const return expr->SetTsType(checker->GlobalTypeError()); } - if (operandType->IsETSObjectType()) { - expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedType) | - checker->GetBoxingFlag(unboxedType)); - } - return expr->SetTsType(operandType); } @@ -2703,7 +2894,9 @@ checker::Type *ETSAnalyzer::Check(ir::BooleanLiteral *expr) const { ETSChecker *checker = GetETSChecker(); if (expr->TsType() == nullptr) { - expr->SetTsType(checker->CreateETSBooleanType(expr->Value())); + auto type = checker->GlobalETSBooleanBuiltinType()->Clone(GetChecker()); + type->AddTypeFlag(TypeFlag::CONSTANT); + expr->SetTsType(type); } return expr->TsType(); } @@ -2712,7 +2905,9 @@ checker::Type *ETSAnalyzer::Check(ir::CharLiteral *expr) const { ETSChecker *checker = GetETSChecker(); if (expr->TsType() == nullptr) { - expr->SetTsType(checker->ProgramAllocator()->New(expr->Char())); + auto type = checker->GlobalCharBuiltinType()->Clone(GetChecker()); + type->AddTypeFlag(TypeFlag::CONSTANT); + expr->SetTsType(type); } return expr->TsType(); } @@ -2726,26 +2921,74 @@ checker::Type *ETSAnalyzer::Check(ir::NullLiteral *expr) const return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::NumberLiteral *expr) const +static bool CheckIfLiteralValueIsAppropriate(ETSChecker *checker, Type *type, ir::NumberLiteral *expr) { - ETSChecker *checker = GetETSChecker(); - if (expr->Number().IsInt()) { - expr->SetTsType(checker->CreateIntType(expr->Number().GetInt())); - return expr->TsType(); + auto number = expr->Number(); + auto relation = checker->Relation(); + if (relation->IsSupertypeOf(checker->GetGlobalTypesHolder()->GlobalIntegralBuiltinType(), type)) { + if (number.IsReal()) { + return false; + } + auto val = number.GetValueAndCastTo(); + if (relation->IsIdenticalTo(type, checker->GlobalByteBuiltinType())) { + return val >= std::numeric_limits::min() && val <= std::numeric_limits::max(); + } + if (relation->IsIdenticalTo(type, checker->GlobalShortBuiltinType())) { + return val >= std::numeric_limits::min() && val <= std::numeric_limits::max(); + } + if (relation->IsIdenticalTo(type, checker->GlobalIntBuiltinType())) { + return val >= std::numeric_limits::min() && val <= std::numeric_limits::max(); + } + } else if (relation->IsIdenticalTo(type, checker->GlobalCharBuiltinType())) { + auto val = number.GetValueAndCastTo(); + return !number.IsReal() && val >= std::numeric_limits::min() && + val <= std::numeric_limits::max(); + } else if (number.IsDouble()) { + if (relation->IsIdenticalTo(type, checker->GlobalFloatBuiltinType())) { + auto doubleVal = number.GetDouble(); + if (doubleVal < std::numeric_limits::min() || doubleVal > std::numeric_limits::max()) { + return false; + } + auto floatVal = static_cast(doubleVal); + return static_cast(floatVal) == doubleVal; + } + return relation->IsIdenticalTo(checker->GlobalDoubleBuiltinType(), type); } + return true; +} - if (expr->Number().IsLong()) { - expr->SetTsType(checker->CreateLongType(expr->Number().GetLong())); +checker::Type *ETSAnalyzer::Check(ir::NumberLiteral *expr) const +{ + if (expr->TsType() != nullptr) { return expr->TsType(); } - if (expr->Number().IsFloat()) { - expr->SetTsType(checker->CreateFloatType(expr->Number().GetFloat())); - return expr->TsType(); + ETSChecker *checker = GetETSChecker(); + Type *type; + + if (auto *preferredType = + GetAppropriatePreferredType(expr->PreferredType(), [&](Type *tp) { return checker->CheckIfNumeric(tp); }); + preferredType != nullptr && !expr->IsFolded() && + CheckIfLiteralValueIsAppropriate(checker, preferredType, expr)) { + type = preferredType->Clone(checker); + } else if (expr->Number().IsInt()) { + type = checker->GlobalIntBuiltinType()->Clone(checker); + } else if (expr->Number().IsLong()) { + type = checker->GlobalLongBuiltinType()->Clone(checker); + } else if (expr->Number().IsFloat()) { + type = checker->GlobalFloatBuiltinType()->Clone(checker); + } else if (expr->Number().IsDouble()) { + type = checker->GlobalDoubleBuiltinType()->Clone(checker); + } else if (expr->Number().IsShort()) { + type = checker->GlobalShortBuiltinType()->Clone(checker); + } else if (expr->Number().IsByte()) { + type = checker->GlobalByteBuiltinType()->Clone(checker); + } else { + return checker->GlobalTypeError(); } - expr->SetTsType(checker->CreateDoubleType(expr->Number().GetDouble())); - return expr->TsType(); + type->AddTypeFlag(TypeFlag::CONSTANT); + return expr->SetTsType(type); } checker::Type *ETSAnalyzer::Check(ir::StringLiteral *expr) const @@ -2757,6 +3000,25 @@ checker::Type *ETSAnalyzer::Check(ir::StringLiteral *expr) const return expr->TsType(); } +checker::Type *ETSAnalyzer::Check(ir::ETSIntrinsicNode *node) const +{ + ETSChecker *checker = GetETSChecker(); + for (auto *arg : node->Arguments()) { + arg->Check(checker); + } + // Note (daizihan): #27074, make it more scalable when IntrinsicNodeType is extended. + if (node->Type() == ir::IntrinsicNodeType::TYPE_REFERENCE) { + auto type = checker->GlobalBuiltinClassType()->Clone(checker); + // Since std.core.Class initialize() is instance method, need to remove the variable flag. + auto newVar = type->Variable()->AsLocalVariable()->Copy(checker->Allocator(), type->Variable()->Declaration()); + newVar->RemoveFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE); + type->SetVariable(newVar); + return node->SetTsType(type); + } + ES2PANDA_UNREACHABLE(); + return checker->GlobalTypeError(); +} + checker::Type *ETSAnalyzer::Check(ir::ImportDeclaration *st) const { ETSChecker *checker = GetETSChecker(); @@ -2791,7 +3053,7 @@ checker::Type *ETSAnalyzer::Check(ir::ImportNamespaceSpecifier *st) const } if (importDecl->IsPureDynamic()) { - auto *type = checker->GlobalBuiltinDynamicType(importDecl->Language()); + auto *type = checker->GetImportSpecifierObjectType(importDecl, st->Local()->AsIdentifier())->AsETSObjectType(); checker->SetrModuleObjectTsType(st->Local(), type); return type; } @@ -2818,10 +3080,9 @@ checker::Type *ETSAnalyzer::Check(ir::BlockStatement *st) const stmt->Check(checker); // NOTE! Processing of trailing blocks was moved here so that smart casts could be applied correctly - if (auto const tb = st->trailingBlocks_.find(stmt); tb != st->trailingBlocks_.end()) { - auto *const trailingBlock = tb->second; + if (auto *const trailingBlock = st->SearchStatementInTrailingBlock(stmt); trailingBlock != nullptr) { trailingBlock->Check(checker); - st->Statements().emplace(std::next(st->Statements().begin() + idx), trailingBlock); + st->AddStatement(idx, trailingBlock); ++idx; } } @@ -2904,17 +3165,30 @@ checker::Type *ETSAnalyzer::Check(ir::AnnotationDeclaration *st) const auto baseName = st->GetBaseName(); if (!baseName->IsErrorPlaceHolder()) { - if (baseName->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) { - auto *annoDecl = baseName->Variable()->Declaration()->Node()->AsAnnotationDeclaration(); - if (annoDecl != st && annoDecl->IsDeclare()) { - checker->CheckAmbientAnnotation(st, annoDecl); - } + auto *annoDecl = baseName->Variable()->Declaration()->Node()->AsAnnotationDeclaration(); + if (annoDecl != st && annoDecl->IsDeclare()) { + checker->CheckAmbientAnnotation(st, annoDecl); } } return ReturnTypeForStatement(st); } +static void ProcessRequiredFields(ArenaUnorderedMap &fieldMap, + ir::AnnotationUsage *st, ETSChecker *checker) +{ + for (const auto &entry : fieldMap) { + if (entry.second->Value() == nullptr) { + checker->LogError(diagnostic::ANNOT_FIELD_NO_VAL, {entry.first}, st->Start()); + continue; + } + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + auto *clone = entry.second->Clone(checker->Allocator(), st); + st->AddProperty(clone); + clone->Check(checker); + } +} + checker::Type *ETSAnalyzer::Check(ir::AnnotationUsage *st) const { if (st->Expr()->TsType() != nullptr) { @@ -2923,22 +3197,24 @@ checker::Type *ETSAnalyzer::Check(ir::AnnotationUsage *st) const ETSChecker *checker = GetETSChecker(); st->Expr()->Check(checker); - if (st->GetBaseName()->Variable() == nullptr || - !st->GetBaseName()->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) { - checker->LogError(diagnostic::NOT_AN_ANNOTATION, {st->GetBaseName()->Name()}, st->GetBaseName()->Start()); + auto *baseName = st->GetBaseName(); + if (baseName->Variable() == nullptr || !baseName->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) { + if (!baseName->IsErrorPlaceHolder()) { + checker->LogError(diagnostic::NOT_AN_ANNOTATION, {baseName->Name()}, baseName->Start()); + } + + ES2PANDA_ASSERT(checker->IsAnyError()); return ReturnTypeForStatement(st); } - auto *annoDecl = st->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration(); + auto *annoDecl = baseName->Variable()->Declaration()->Node()->AsAnnotationDeclaration(); annoDecl->Check(checker); ArenaUnorderedMap fieldMap {checker->ProgramAllocator()->Adapter()}; for (auto *it : annoDecl->Properties()) { auto *field = it->AsClassProperty(); - ES2PANDA_ASSERT(field != nullptr); - auto *id = field->Id(); - ES2PANDA_ASSERT(id != nullptr); - fieldMap.insert(std::make_pair(id->Name(), field)); + ES2PANDA_ASSERT(field->Id() != nullptr); + fieldMap.insert(std::make_pair(field->Id()->Name(), field)); } if (annoDecl->Properties().size() < st->Properties().size()) { @@ -2951,10 +3227,10 @@ checker::Type *ETSAnalyzer::Check(ir::AnnotationUsage *st) const checker->CheckSinglePropertyAnnotation(st, annoDecl); fieldMap.clear(); } else { - checker->CheckMultiplePropertiesAnnotation(st, st->GetBaseName()->Name(), fieldMap); + checker->CheckMultiplePropertiesAnnotation(st, baseName->Name(), fieldMap); } - checker->ProcessRequiredFields(fieldMap, st, checker); + ProcessRequiredFields(fieldMap, st, checker); return ReturnTypeForStatement(st); } @@ -2971,6 +3247,19 @@ checker::Type *ETSAnalyzer::Check(ir::ContinueStatement *st) const return checker->GlobalTypeError(); } + // CTE if target is outside the function + auto getEnclosingMethod = [](const ir::AstNode *node) { + const ir::AstNode *enclosingMethod = node->Parent(); + while (enclosingMethod != nullptr && !enclosingMethod->IsMethodDefinition() && + !enclosingMethod->IsArrowFunctionExpression()) { + enclosingMethod = enclosingMethod->Parent(); + } + return enclosingMethod; + }; + if (getEnclosingMethod(st) != getEnclosingMethod(st->Target())) { + checker->LogError(diagnostic::CONTINUE_TARGET_OUTSIDE_FUNCTION, {}, st->Start()); + } + checker->AddStatus(CheckerStatus::MEET_CONTINUE); return ReturnTypeForStatement(st); } @@ -3016,7 +3305,7 @@ static bool ValidateAndProcessIteratorType(ETSChecker *checker, Type *elemType, relation->SetNode(ident); if (auto ctx = checker::AssignmentContext(checker->Relation(), ident, elemType, iterType, ident->Start(), std::nullopt, TypeRelationFlag::NO_THROW); - !ctx.IsAssignable()) { + !ctx.IsAssignable() && !relation->IsLegalBoxedPrimitiveConversion(iterType, elemType)) { checker->LogError(diagnostic::ITERATOR_ELEMENT_TYPE_MISMATCH, {elemType, iterType}, st->Start()); return false; } @@ -3042,12 +3331,7 @@ checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point. auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt); - checker::Type *const exprType = st->Right()->Check(checker); - if (exprType == nullptr) { - checker->LogError(diagnostic::FOROF_CANT_INFER_SOURCE, {}, st->Right()->Start()); - return checker->GlobalTypeError(); - } - + checker::Type *const exprType = st->Right()->Check(checker)->MaybeBaseTypeOfGradualType(); checker::Type *elemType = checker->GlobalTypeError(); if (exprType->IsETSStringType()) { @@ -3165,7 +3449,8 @@ static bool CheckIsValidReturnTypeAnnotation(ir::ReturnStatement *st, ir::Script ir::TypeNode *returnTypeAnnotation, ETSChecker *checker) { // check valid `this` type as return type - if (containingFunc->GetPreferredReturnType() != nullptr || !returnTypeAnnotation->IsTSThisType()) { + if (containingFunc->GetPreferredReturnType() != nullptr || + (returnTypeAnnotation != nullptr && !returnTypeAnnotation->IsTSThisType())) { return true; } @@ -3210,40 +3495,29 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S // Case when function's return type is defined explicitly: if (st->argument_ == nullptr) { ES2PANDA_ASSERT(funcReturnType != nullptr); - if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() && - !funcReturnType->IsETSAsyncFuncReturnType()) { + if (!funcReturnType->MaybeBaseTypeOfGradualType()->IsETSVoidType() && + funcReturnType != checker->GlobalVoidType() && + !funcReturnType->MaybeBaseTypeOfGradualType()->IsETSAsyncFuncReturnType()) { checker->LogError(diagnostic::RETURN_WITHOUT_VALUE, {}, st->Start()); return false; } funcReturnType = checker->GlobalVoidType(); } else { const auto name = containingFunc->Scope()->InternalName().Mutf8(); - if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) { + if (!CheckArgumentVoidType(funcReturnType->MaybeBaseTypeOfGradualType(), checker, name, st)) { return false; } - if (st->argument_->IsObjectExpression()) { - st->argument_->AsObjectExpression()->SetPreferredType(funcReturnType); - } if (st->argument_->IsMemberExpression()) { checker->SetArrayPreferredTypeForNestedMemberExpressions(st->argument_->AsMemberExpression(), funcReturnType); - } - - if (st->argument_->IsArrayExpression()) { - st->argument_->AsArrayExpression()->SetPreferredType(funcReturnType); - } - - if (st->argument_->IsETSNewArrayInstanceExpression()) { - st->argument_->AsETSNewArrayInstanceExpression()->SetPreferredType(funcReturnType); - } - - if (st->argument_->IsETSNewMultiDimArrayInstanceExpression()) { - st->argument_->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(funcReturnType); + } else { + st->argument_->SetPreferredType(funcReturnType); } checker::Type *argumentType = st->argument_->Check(checker); - return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc); + return CheckReturnType(checker, funcReturnType->MaybeBaseTypeOfGradualType(), argumentType, st->argument_, + containingFunc); } return true; } @@ -3265,13 +3539,14 @@ checker::Type *ETSAnalyzer::GetFunctionReturnType(ir::ReturnStatement *st, ir::S } else { // Case when function's return type should be inferred from return statement(s): if (containingFunc->Signature()->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) { - InferReturnType(checker, containingFunc, funcReturnType, - st->argument_); // This removes the NEED_RETURN_TYPE flag, so only the first return - // statement going to land here... + funcReturnType = InferReturnType(checker, containingFunc, + st->argument_); // This removes the NEED_RETURN_TYPE flag, so only the + // first return statement going to land here... } else { // All subsequent return statements: - ProcessReturnStatements(checker, containingFunc, funcReturnType, st, - st->argument_); // and the remaining return statements will get processed here. + funcReturnType = + ProcessReturnStatements(checker, containingFunc, st, + st->argument_); // and the remaining return statements will get processed here. } } @@ -3326,9 +3601,8 @@ checker::Type *ETSAnalyzer::Check(ir::SwitchStatement *st) const checker::TypeRelationFlag::NONE); auto *comparedExprType = checker->CheckSwitchDiscriminant(st->Discriminant()); - auto unboxedDiscType = (st->Discriminant()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U - ? checker->MaybeUnboxInRelation(comparedExprType) - : comparedExprType; + // may have no meaning to unbox comparedExprType + auto unboxedDiscType = checker->MaybeUnboxType(comparedExprType); SmartCastArray smartCasts = checker->Context().CloneSmartCasts(); bool hasDefaultCase = false; @@ -3461,7 +3735,11 @@ checker::Type *ETSAnalyzer::Check(ir::VariableDeclarator *st) const // NOTE: T_S and K_o_t_l_i_n don't act in such way, but we can try - why not? :) auto *smartType = variableType; if (auto *const initType = st->Init() != nullptr ? st->Init()->TsType() : nullptr; initType != nullptr) { - smartType = checker->ResolveSmartType(initType, variableType); + auto const value = st->Init()->IsNumberLiteral() + ? std::make_optional(st->Init()->AsNumberLiteral()->Number().GetDouble()) + : std::nullopt; + + smartType = checker->ResolveSmartType(initType, variableType, value); // Set smart type for identifier if it differs from annotated type // Top-level and captured variables are not processed here! if (!checker->Relation()->IsIdenticalTo(variableType, smartType)) { @@ -3534,40 +3812,45 @@ checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const checker->CheckAnnotations(expr->TypeAnnotation()->Annotations()); auto *const targetType = expr->TypeAnnotation()->AsTypeNode()->GetType(checker); - ES2PANDA_ASSERT(targetType != nullptr); - if (targetType->IsTypeError()) { - return checker->InvalidateType(expr); - } + FORWARD_TYPE_ERROR(checker, targetType, expr); - ETSChecker::SetPreferredTypeIfPossible(expr->Expr(), targetType); + expr->Expr()->SetPreferredType(targetType); auto const sourceType = expr->Expr()->Check(checker); - if (sourceType->IsTypeError()) { - return checker->InvalidateType(expr); + FORWARD_TYPE_ERROR(checker, sourceType, expr); + + if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) { + return expr->SetTsType(checker->TypeError(expr, diagnostic::NULLISH_CAST_TO_NONNULLISH, expr->Start())); } - // NOTE(vpukhov): #20510 lowering - if (targetType->IsETSPrimitiveType() && sourceType->IsETSReferenceType()) { - auto *const boxedTargetType = checker->MaybeBoxInRelation(targetType); - if (!checker->Relation()->IsIdenticalTo(sourceType, boxedTargetType)) { - expr->Expr()->AddAstNodeFlags(ir::AstNodeFlags::CHECKCAST); - } + if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSTypeParameter()) { + checker->LogError(diagnostic::INVALID_CAST, {sourceType->ToString(), targetType->ToString()}, + expr->Expr()->Start()); + return checker->InvalidateType(expr); } - if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) { - return checker->TypeError(expr, diagnostic::NULLISH_CAST_TO_NONNULLISH, expr->Start()); + if (expr->Expr()->IsLiteral() && sourceType->IsBuiltinNumeric() && targetType->IsETSUnionType()) { + bool allAreTypeParams = true; + for (auto *sub : targetType->AsETSUnionType()->ConstituentTypes()) { + if (!sub->IsETSTypeParameter()) { + allAreTypeParams = false; + } + } + if (allAreTypeParams) { + checker->LogError(diagnostic::INVALID_CAST, {sourceType->ToString(), targetType->ToString()}, + expr->Expr()->Start()); + return checker->InvalidateType(expr); + } } const checker::CastingContext ctx( - checker->Relation(), diagnostic::INVALID_CAST, {sourceType, targetType}, + checker->Relation(), + sourceType->IsBuiltinNumeric() && targetType->IsBuiltinNumeric() ? diagnostic::IMPROPER_NUMERIC_CAST + : diagnostic::INVALID_CAST, + // CC-OFFNXT(G.FMT.03-CPP) project code style + {sourceType, targetType}, checker::CastingContext::ConstructorData {expr->Expr(), sourceType, targetType, expr->Expr()->Start()}); - if (sourceType->IsETSDynamicType() && targetType->IsLambdaObject()) { - // NOTE: itrubachev. change targetType to created lambdaobject type. - // Now targetType is not changed, only construct signature is added to it - checker->BuildLambdaObjectClass(targetType->AsETSObjectType(), - expr->TypeAnnotation()->AsETSFunctionType()->ReturnType()); - } expr->isUncheckedCast_ = ctx.UncheckedCast(); // Make sure the array type symbol gets created for the assembler to be able to emit checkcast. @@ -3578,12 +3861,10 @@ checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const } if (targetType == checker->GetGlobalTypesHolder()->GlobalETSNeverType()) { - return checker->TypeError(expr, diagnostic::CAST_TO_NEVER, expr->Start()); + return expr->SetTsType(checker->TypeError(expr, diagnostic::CAST_TO_NEVER, expr->Start())); } - checker->ComputeApparentType(targetType); - expr->SetTsType(targetType); - return expr->TsType(); + return expr->SetTsType(targetType); } checker::Type *ETSAnalyzer::Check(ir::TSEnumDeclaration *st) const @@ -3602,18 +3883,17 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const auto *stmtType = checker->BuildBasicInterfaceProperties(st); ES2PANDA_ASSERT(stmtType != nullptr); - if (stmtType->IsTypeError()) { - return st->SetTsType(stmtType); - } + FORWARD_TYPE_ERROR(checker, stmtType, st); - auto *interfaceType = stmtType->AsETSObjectType(); + auto *interfaceType = stmtType->IsGradualType() ? stmtType->AsGradualType()->GetBaseType()->AsETSObjectType() + : stmtType->AsETSObjectType(); checker->CheckInterfaceAnnotations(st); interfaceType->SetSuperType(checker->GlobalETSObjectType()); checker->CheckInvokeMethodsLegitimacy(interfaceType); - st->SetTsType(interfaceType); - + st->SetTsType(stmtType); + checker->CheckDynamicInheritanceAndImplement(interfaceType->AsETSObjectType()); checker::ScopeContext scopeCtx(checker, st->Scope()); auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType); @@ -3651,10 +3931,62 @@ checker::Type *ETSAnalyzer::Check(ir::TSNonNullExpression *expr) const return expr->TsType(); } +static varbinder::Variable *FindNameForImportNamespace(ETSChecker *checker, util::StringView &searchName, + ETSObjectType *baseType) +{ + /* This function try to find name1.name2, name1.A in file file1.ets, + * ./file1.ets: + * import * as name1 from "./file2" + * + * ./file2.ets: + * import * as name2 from "./file3" + * import {A} from "./file3" + * export {name2} + * export {A} + * + * ./file3.ets + * export class A{} + * + * 1. Find in file2->program->ast->scope first + * 2. Find in varbinder->selectiveExportAliasMultimap second + * if both found, return variable + */ + auto declNode = baseType->GetDeclNode(); + if (!declNode->IsIdentifier()) { + return nullptr; + } + if (declNode->Parent() == nullptr || declNode->Parent()->Parent() == nullptr) { + return nullptr; + } + auto importDeclNode = declNode->Parent()->Parent(); + if (!importDeclNode->IsETSImportDeclaration()) { + return nullptr; + } + + auto importDecl = importDeclNode->AsETSImportDeclaration(); + + parser::Program *program = checker->SelectEntryOrExternalProgram( + static_cast(checker->VarBinder()), importDecl->ImportMetadata().resolvedSource); + + auto &bindings = program->Ast()->Scope()->Bindings(); + + if (auto result = bindings.find(searchName); result != bindings.end()) { + auto &sMap = checker->VarBinder() + ->AsETSBinder() + ->GetSelectiveExportAliasMultimap() + .find(importDecl->ImportMetadata().resolvedSource) + ->second; + if (auto it = sMap.find(searchName); it != sMap.end()) { + return result->second; + } + } + return nullptr; +} + checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const { ETSChecker *checker = GetETSChecker(); - checker::Type *baseType = expr->Left()->Check(checker); + checker::Type *baseType = expr->Left()->Check(checker)->MaybeBaseTypeOfGradualType(); if (baseType->IsETSObjectType()) { // clang-format off auto searchName = expr->Right()->Name(); @@ -3664,7 +3996,11 @@ 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); + + if (prop == nullptr) { + prop = FindNameForImportNamespace(GetETSChecker(), searchName, baseType->AsETSObjectType()); + } // 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/ETSAnalyzer.h b/ets2panda/checker/ETSAnalyzer.h index c9a398b319b807b28c697498308e787f9103af69..37bb84167219a16f40287c4d9aadc9222bfc44c3 100644 --- a/ets2panda/checker/ETSAnalyzer.h +++ b/ets2panda/checker/ETSAnalyzer.h @@ -36,7 +36,6 @@ public: virtual checker::Type *Check(ir::nodeType *node) const override; // CC-OFF(G.PRE.02,G.PRE.09) name part AST_NODE_REINTERPRET_MAPPING(DECLARE_ETSANALYZER_CHECK_METHOD) #undef DECLARE_ETSANALYZER_CHECK_METHOD - checker::Type *PreferredType(ir::ObjectExpression *expr) const; checker::Type *CheckDynamic(ir::ObjectExpression *expr) const; checker::Type *GetPreferredType(ir::ArrayExpression *expr) const; void GetUnionPreferredType(ir::Expression *expr, Type *originalType) const; @@ -58,6 +57,7 @@ private: checker::Type *UnwrapPromiseType(checker::Type *type) const; checker::Type *GetSmartType(ir::AssignmentExpression *expr, checker::Type *leftType, checker::Type *rightType) const; + bool SetAssignmentExpressionTarget(ir::AssignmentExpression *const expr, ETSChecker *checker) const; bool CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, ir::TypeNode *returnTypeAnnotation, ETSChecker *checker) const; diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index d6d7255b0aba4a661c40c27a1c4b1c2e65192268..2075563dcdcb51dd0e0c1ee36fc41d6a775046e7 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -15,6 +15,7 @@ #include "ETSAnalyzerHelpers.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "checker/types/typeError.h" namespace ark::es2panda::checker { @@ -55,6 +56,9 @@ void CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *chec return; } + if (methodVariable->TsType()->IsTypeError()) { + return; + } const auto *const funcType = methodVariable->TsType()->AsETSFunctionType(); for (auto *funcSignature : funcType->CallSignatures()) { ES2PANDA_ASSERT(signature != nullptr); @@ -247,7 +251,7 @@ void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scr auto *objectId = checker->ProgramAllocNode(compiler::Signatures::BUILTIN_OBJECT_CLASS, checker->ProgramAllocator()); - checker->VarBinder()->AsETSBinder()->LookupTypeReference(objectId, false); + checker->VarBinder()->AsETSBinder()->LookupTypeReference(objectId); auto *returnType = checker->ProgramAllocNode( checker->ProgramAllocNode(objectId, nullptr, nullptr, checker->ProgramAllocator()), checker->ProgramAllocator()); @@ -308,14 +312,10 @@ static bool HasIteratorInterface(ETSObjectType const *const objectType) } void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc, - const lexer::SourcePosition &position, const std::string &methodName) + const lexer::SourcePosition &position, + [[maybe_unused]] const std::string &methodName) { - const auto *returnType = scriptFunc->Signature()->ReturnType(); - - if (returnType == nullptr) { - checker->LogError(diagnostic::MISSING_RETURN_TYPE_2, {util::StringView(methodName)}, position); - return; - } + const auto *returnType = scriptFunc->Signature()->ReturnType()->MaybeBaseTypeOfGradualType(); if (returnType->IsETSTypeParameter()) { returnType = checker->GetApparentType(returnType->AsETSTypeParameter()->GetConstraintType()); @@ -372,12 +372,6 @@ checker::Signature *ResolveCallExtensionFunction(checker::Type *functionType, ch expr->Arguments().erase(expr->Arguments().begin()); return nullptr; } - if (!signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION)) { - checker->LogError(diagnostic::PROPERTY_NONEXISTENT, - {memberExpr->Property()->AsIdentifier()->Name(), memberExpr->ObjType()->Name()}, - memberExpr->Property()->Start()); - return nullptr; - } SwitchMethodCallToFunctionCall(checker, expr, signature); return signature; @@ -425,7 +419,7 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E methodCallSig->GetSignatureInfo()->minArgCount++; auto ¶msVar = methodCallSig->Params(); paramsVar.insert(paramsVar.begin(), dummyReceiverVar); - auto ¶ms = methodCallSig->Function()->Params(); + auto ¶ms = methodCallSig->Function()->ParamsForUpdate(); params.insert(params.begin(), dummyReceiver); if (typeParamsNeeded) { auto &typeParams = methodCallSig->TypeParams(); @@ -441,7 +435,7 @@ checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::E methodCallSig->GetSignatureInfo()->minArgCount--; auto ¶msVar = methodCallSig->Params(); paramsVar.erase(paramsVar.begin()); - auto ¶ms = methodCallSig->Function()->Params(); + auto ¶ms = methodCallSig->Function()->ParamsForUpdate(); params.erase(params.begin()); if (typeParamsNeeded) { auto &typeParams = methodCallSig->TypeParams(); @@ -514,27 +508,9 @@ ArenaVector GetUnionTypeSignatures(ETSChecker *checker, ch void ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType) { - if (checker->IsNullLikeOrVoidExpression(expr->Argument())) { - auto tsType = checker->CreateETSBooleanType(true); - CHECK_NOT_NULL(tsType); - tsType->AddTypeFlag(checker::TypeFlag::CONSTANT); - expr->SetTsType(tsType); - return; - } + FORWARD_VALUE_ON_TYPE_ERROR(checker, operandType, expr, EMPTY_VALUE); - if (operandType == nullptr || operandType->IsTypeError()) { - expr->SetTsType(checker->GlobalTypeError()); - return; - } - - auto exprRes = operandType->ResolveConditionExpr(); - if (std::get<0>(exprRes)) { - auto tsType = checker->CreateETSBooleanType(!std::get<1>(exprRes)); - tsType->AddTypeFlag(checker::TypeFlag::CONSTANT); - expr->SetTsType(tsType); - return; - } - expr->SetTsType(checker->GlobalETSBooleanType()); + expr->SetTsType(checker->GlobalETSBooleanBuiltinType()); } void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType) @@ -542,29 +518,27 @@ void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, switch (expr->OperatorType()) { case lexer::TokenType::PUNCTUATOR_MINUS: case lexer::TokenType::PUNCTUATOR_PLUS: { - if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { + if (operandType == nullptr || !operandType->IsETSObjectType() || + !operandType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CONVERTIBLE_TO_NUMERIC)) { checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start()); expr->SetTsType(checker->GlobalTypeError()); break; } - if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT) && - expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) { - expr->SetTsType(checker->NegateNumericType(operandType, expr)); - break; - } - expr->SetTsType(operandType); break; } case lexer::TokenType::PUNCTUATOR_TILDE: { - if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { + if (operandType == nullptr || !operandType->IsETSObjectType() || + !operandType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CONVERTIBLE_TO_NUMERIC)) { checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start()); expr->SetTsType(checker->GlobalTypeError()); break; } - - expr->Argument()->SetTsType(expr->SetTsType(checker->SelectGlobalIntegerTypeForNumeric(operandType))); + auto exprType = expr->SetTsType(checker->SelectGlobalIntegerTypeForNumeric(operandType)); + if (!expr->Argument()->TsType()->IsETSIntEnumType()) { + expr->Argument()->SetTsType(exprType); + } break; } case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { @@ -619,11 +593,12 @@ checker::Type *GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir: return checker->GetNonConstantType(iterType); } -bool CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name, +bool CheckArgumentVoidType(checker::Type *funcReturnType, ETSChecker *checker, const std::string &name, ir::ReturnStatement *st) { if (name.find(compiler::Signatures::ETS_MAIN_WITH_MANGLE_BEGIN) != std::string::npos) { - if (!funcReturnType->IsETSVoidType() && !funcReturnType->IsIntType()) { + if (!funcReturnType->IsETSVoidType() && + !checker->Relation()->IsSupertypeOf(checker->GlobalIntBuiltinType(), funcReturnType)) { checker->LogError(diagnostic::MAIN_BAD_RETURN, {}, st->Start()); } } @@ -638,14 +613,6 @@ bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker checker->LogError(diagnostic::UNEXPECTED_VALUE_RETURN, {}, stArgument->Start()); return false; } - if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType, - stArgument->Start(), std::nullopt, - checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW) - // CC-OFFNXT(G.FMT.02) project code style - .IsAssignable()) { - checker->LogError(diagnostic::RETURN_TYPE_MISMATCH, {}, stArgument->Start()); - return false; - } return true; } @@ -671,16 +638,15 @@ bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker return true; } -void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, - ir::Expression *stArgument) +checker::Type *InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, ir::Expression *stArgument) { // First (or single) return statement in the function: - funcReturnType = + auto *funcReturnType = stArgument == nullptr ? checker->GlobalVoidType() : checker->GetNonConstantType(stArgument->Check(checker)); ES2PANDA_ASSERT(funcReturnType != nullptr); if (funcReturnType->IsTypeError()) { containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE); - return; + return funcReturnType; } /* @@ -690,40 +656,23 @@ void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, ch return () => {} ``` */ - if (stArgument != nullptr && stArgument->IsArrowFunctionExpression()) { - auto arrowFunc = stArgument->AsArrowFunctionExpression(); - auto typeAnnotation = arrowFunc->CreateTypeAnnotation(checker); - - auto *argumentType = arrowFunc->TsType(); - ES2PANDA_ASSERT(typeAnnotation != nullptr); - funcReturnType = typeAnnotation->GetType(checker); - if (!checker::AssignmentContext(checker->Relation(), arrowFunc, argumentType, funcReturnType, - stArgument->Start(), std::nullopt, - checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW) - // CC-OFFNXT(G.FMT.02) project code style - .IsAssignable()) { - checker->LogError(diagnostic::ARROW_TYPE_MISMATCH, {argumentType, funcReturnType}, stArgument->Start()); - funcReturnType = checker->GlobalTypeError(); - return; - } - } containingFunc->Signature()->SetReturnType(funcReturnType); containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE); containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE); checker->VarBinder()->AsETSBinder()->BuildFunctionName(containingFunc); - if (stArgument != nullptr && stArgument->IsObjectExpression()) { - stArgument->AsObjectExpression()->SetPreferredType(funcReturnType); + if (stArgument != nullptr) { + stArgument->SetPreferredType(funcReturnType); } + + return funcReturnType; } bool IsArrayExpressionValidInitializerForType(ETSChecker *checker, const Type *const arrayExprPreferredType) { const auto validForTarget = arrayExprPreferredType == nullptr // preferred type will be inferred from elements - || arrayExprPreferredType->IsETSArrayType() // valid for fixed array type - || arrayExprPreferredType->IsETSResizableArrayType() // valid for resizable array type - || arrayExprPreferredType->IsETSTupleType() // valid for tuple type + || arrayExprPreferredType->IsAnyETSArrayOrTupleType() // valid for array or tuple types || checker->Relation()->IsSupertypeOf(arrayExprPreferredType, // valid for 'Object' checker->GlobalETSObjectType()); @@ -744,16 +693,16 @@ void CastPossibleTupleOnRHS(ETSChecker *checker, ir::AssignmentExpression *expr) } } -void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, - ir::ReturnStatement *st, ir::Expression *stArgument) +checker::Type *ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, ir::ReturnStatement *st, + ir::Expression *stArgument) { - funcReturnType = containingFunc->Signature()->ReturnType(); + auto *funcReturnType = containingFunc->Signature()->ReturnType(); if (stArgument == nullptr) { // previous return statement(s) have value if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType()) { checker->LogError(diagnostic::MIXED_VOID_NONVOID, {}, st->Start()); - return; + return funcReturnType; } } else { if (stArgument->IsObjectExpression()) { @@ -770,12 +719,12 @@ void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containing ES2PANDA_ASSERT(argumentType != nullptr); if (funcReturnType->IsETSVoidType() && !argumentType->IsETSVoidType()) { checker->LogError(diagnostic::MIXED_VOID_NONVOID, {}, stArgument->Start()); - return; + return funcReturnType; } const auto name = containingFunc->Scope()->InternalName().Mutf8(); if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) { - return; + return funcReturnType; } auto *const relation = checker->Relation(); @@ -788,6 +737,7 @@ void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containing relation->SetNode(nullptr); relation->SetFlags(checker::TypeRelationFlag::NONE); } + return funcReturnType; } bool CheckReturnTypeNecessity(ir::MethodDefinition *node) @@ -816,11 +766,40 @@ void CheckAllConstPropertyInitialized(checker::ETSChecker *checker, ir::ETSModul continue; } - if (prop->AsClassProperty()->Key()->Variable()->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK) && - !prop->AsClassProperty()->Key()->Variable()->HasFlag(varbinder::VariableFlags::INITIALIZED)) { + auto *classProp = prop->AsClassProperty(); + if (classProp->Key()->Variable() == nullptr) { + continue; + } + + if (classProp->Key()->Variable()->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK) && + !classProp->Key()->Variable()->HasFlag(varbinder::VariableFlags::INITIALIZED)) { checker->LogError(diagnostic::MISSING_INIT_FOR_CONST_PACKAGE_PROP, {}, prop->Start()); } } } +// NOLINTBEGIN(readability-else-after-return) +std::tuple IsConstantTestValue(ir::Expression const *expr) +{ + if (expr->IsNullLiteral() || expr->IsUndefinedLiteral()) { + return {true, false}; + } else if (expr->IsBooleanLiteral()) { + return {true, expr->AsBooleanLiteral()->Value()}; + } else if (expr->IsStringLiteral()) { + return {true, expr->AsStringLiteral()->Str().Length() != 0}; + } else if (expr->IsCharLiteral()) { + return {true, expr->AsCharLiteral()->Char() != 0}; + } else if (expr->IsBigIntLiteral()) { + return {true, expr->AsBigIntLiteral()->Str() != "0"}; + } else if (expr->IsNumberLiteral()) { + auto num = expr->AsNumberLiteral()->Number(); + return {true, !num.IsZero()}; + } else if (expr->TsType()->IsETSEnumType() && expr->TsType()->IsConstantType()) { + // NOTE(gogabr): Should handle enum constants + return {false, false}; + } + return {false, false}; +} +// NOLINTEND(readability-else-after-return) + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ETSAnalyzerHelpers.h b/ets2panda/checker/ETSAnalyzerHelpers.h index 979d6413ac63d58a4c73e16aabef6a38c793068f..37ac4ce4f2c8fa2188c44b75e7a5ad116a72b27a 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.h +++ b/ets2panda/checker/ETSAnalyzerHelpers.h @@ -52,18 +52,20 @@ ArenaVector GetUnionTypeSignatures(ETSChecker *checker, ch void ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType); void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType); checker::Type *GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir::AstNode *left); -bool CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name, +bool CheckArgumentVoidType(checker::Type *funcReturnType, ETSChecker *checker, const std::string &name, ir::ReturnStatement *st); bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker::Type *argumentType, ir::Expression *stArgument, ir::ScriptFunction *containingFunc); -void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, - ir::Expression *stArgument); +checker::Type *InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, ir::Expression *stArgument); bool IsArrayExpressionValidInitializerForType(ETSChecker *checker, const Type *arrayExprPreferredType); void CastPossibleTupleOnRHS(ETSChecker *checker, ir::AssignmentExpression *expr); -void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, - ir::ReturnStatement *st, ir::Expression *stArgument); +checker::Type *ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, ir::ReturnStatement *st, + ir::Expression *stArgument); bool CheckReturnTypeNecessity(ir::MethodDefinition *node); + void CheckAllConstPropertyInitialized(checker::ETSChecker *checker, ir::ETSModule *pkg); + +std::tuple IsConstantTestValue(ir::Expression const *expr); } // namespace ark::es2panda::checker #endif // ES2PANDA_CHECKER_ETSANALYZERHELPERS_H diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index f9e052f39f937fd28397b1efeaa18d1da1f25fa1..88a71002f8b233850bcbf3444e179df12e41f060 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -13,6 +13,10 @@ * limitations under the License. */ +#include +#include +#include + #include "ETSchecker.h" #include "es2panda.h" @@ -21,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" @@ -30,43 +36,61 @@ #include "ir/base/scriptFunction.h" #include "util/helpers.h" #include "evaluate/scopedDebugInfoPlugin.h" +#include "checker/types/ets/etsTupleType.h" namespace ark::es2panda::checker { -ETSChecker::ETSChecker(util::DiagnosticEngine &diagnosticEngine) - // NOLINTNEXTLINE(readability-redundant-member-init) - : Checker(diagnosticEngine), - arrayTypes_(Allocator()->Adapter()), - pendingConstraintCheckRecords_(Allocator()->Adapter()), - globalArraySignatures_(Allocator()->Adapter()), - dynamicIntrinsics_ {DynamicCallIntrinsicsMap {Allocator()->Adapter()}, - DynamicCallIntrinsicsMap {Allocator()->Adapter()}}, - dynamicClasses_ {DynamicClassIntrinsicsMap(Allocator()->Adapter()), - DynamicClassIntrinsicsMap(Allocator()->Adapter())}, - dynamicLambdaSignatureCache_(Allocator()->Adapter()), - functionalInterfaceCache_(Allocator()->Adapter()), - apparentTypes_(Allocator()->Adapter()), - dynamicCallNames_ {{DynamicCallNamesMap(Allocator()->Adapter()), DynamicCallNamesMap(Allocator()->Adapter())}}, - overloadSigContainer_(Allocator()->Adapter()) -{ -} - -ETSChecker::ETSChecker(util::DiagnosticEngine &diagnosticEngine, ArenaAllocator *programAllocator) - // NOLINTNEXTLINE(readability-redundant-member-init) - : Checker(diagnosticEngine, programAllocator), - arrayTypes_(Allocator()->Adapter()), - pendingConstraintCheckRecords_(Allocator()->Adapter()), - globalArraySignatures_(Allocator()->Adapter()), - dynamicIntrinsics_ {DynamicCallIntrinsicsMap {Allocator()->Adapter()}, - DynamicCallIntrinsicsMap {Allocator()->Adapter()}}, - dynamicClasses_ {DynamicClassIntrinsicsMap(Allocator()->Adapter()), - DynamicClassIntrinsicsMap(Allocator()->Adapter())}, - dynamicLambdaSignatureCache_(Allocator()->Adapter()), - functionalInterfaceCache_(Allocator()->Adapter()), - apparentTypes_(Allocator()->Adapter()), - dynamicCallNames_ {{DynamicCallNamesMap(Allocator()->Adapter()), DynamicCallNamesMap(Allocator()->Adapter())}}, - overloadSigContainer_(Allocator()->Adapter()) +void ETSChecker::ReputCheckerData() { + readdedChecker_.insert(this); + for (auto &[_, extPrograms] : Program()->ExternalSources()) { + (void)_; + auto *extProg = extPrograms.front(); + if (!extProg->IsASTLowered()) { + continue; + } + auto eChecker = extProg->Checker()->AsETSChecker(); + + if (!HasStatus(CheckerStatus::BUILTINS_INITIALIZED)) { + SetGlobalTypesHolder(eChecker->GetGlobalTypesHolder()); + AddStatus(CheckerStatus::BUILTINS_INITIALIZED); + } + + if (auto it = readdedChecker_.find(eChecker); it != readdedChecker_.end()) { + continue; + } + readdedChecker_.insert(eChecker->readdedChecker_.begin(), eChecker->readdedChecker_.end()); + auto computedAbstractMapToCopy = eChecker->GetCachedComputedAbstracts(); + for (auto &[key, value] : *computedAbstractMapToCopy) { + if (GetCachedComputedAbstracts()->find(key) != GetCachedComputedAbstracts()->end()) { + continue; + } + auto &[v1, v2] = value; + ArenaVector newV1(Allocator()->Adapter()); + ArenaUnorderedSet newV2(Allocator()->Adapter()); + newV1.assign(v1.cbegin(), v1.cend()); + newV2.insert(v2.cbegin(), v2.cend()); + GetCachedComputedAbstracts()->try_emplace(key, newV1, newV2); + } + + auto &globalArraySigs = eChecker->globalArraySignatures_; + globalArraySignatures_.insert(globalArraySigs.cbegin(), globalArraySigs.cend()); + + auto &apparentTypes = eChecker->apparentTypes_; + apparentTypes_.insert(apparentTypes.cbegin(), apparentTypes.cend()); + + auto &objectInstantiationMap = eChecker->objectInstantiationMap_; + for (auto &[key, value] : objectInstantiationMap) { + if (objectInstantiationMap_.find(key) == objectInstantiationMap_.end()) { + objectInstantiationMap_.insert(objectInstantiationMap.cbegin(), objectInstantiationMap.cend()); + } + } + + auto &invokeToArrowSignatures = eChecker->invokeToArrowSignatures_; + invokeToArrowSignatures_.insert(invokeToArrowSignatures.cbegin(), invokeToArrowSignatures.cend()); + auto &arrowToFuncInterfaces = eChecker->arrowToFuncInterfaces_; + arrowToFuncInterfaces_.insert(arrowToFuncInterfaces.cbegin(), arrowToFuncInterfaces.cend()); + } } static util::StringView InitBuiltin(ETSChecker *checker, std::string_view signature) @@ -90,8 +114,7 @@ static util::StringView InitBuiltin(ETSChecker *checker, std::string_view signat void ETSChecker::CheckObjectLiteralKeys(const ArenaVector &properties) { - static std::set names; - names.clear(); + std::set names; for (auto property : properties) { if (!property->IsProperty()) { @@ -235,7 +258,7 @@ static void IntializeFunctionInterfaces(GlobalTypesHolder *typeHolder) return typeHolder->GlobalFunctionBuiltinType(arity, hasRest)->AsETSObjectType(); }; - for (size_t arity = 0; arity < typeHolder->VariadicFunctionTypeThreshold(); arity++) { + for (size_t arity = 0; arity <= typeHolder->VariadicFunctionTypeThreshold(); arity++) { getItf(arity, false)->AddObjectFlag(ETSObjectFlags::FUNCTIONAL); getItf(arity, true)->AddObjectFlag(ETSObjectFlags::FUNCTIONAL); } @@ -248,6 +271,9 @@ void ETSChecker::InitializeBuiltins(varbinder::ETSBinder *varbinder) } const auto varMap = varbinder->TopScope()->Bindings(); + if (varMap.find(compiler::Signatures::BUILTIN_OBJECT_CLASS) == varMap.end()) { + return; + } auto const objectName = InitBuiltin(this, compiler::Signatures::BUILTIN_OBJECT_CLASS); @@ -293,8 +319,6 @@ void ETSChecker::InitializeBuiltin(varbinder::Variable *var, const util::StringV bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Options &options) { - Initialize(varbinder); - if (options.IsParseOnly()) { return false; } @@ -302,13 +326,6 @@ bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const util::Optio auto *etsBinder = varbinder->AsETSBinder(); InitializeBuiltins(etsBinder); - for (auto &entry : etsBinder->DynamicImportVars()) { - auto &data = entry.second; - if (data.import->IsPureDynamic()) { - data.variable->SetTsType(GlobalBuiltinDynamicType(data.import->Language())); - } - } - bool isEvalMode = (debugInfoPlugin_ != nullptr); if (UNLIKELY(isEvalMode)) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -354,29 +371,24 @@ void ETSChecker::SetDebugInfoPlugin(evaluate::ScopedDebugInfoPlugin *debugInfo) void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) { - if (program->IsASTChecked()) { - return; - } - auto *savedProgram = Program(); SetProgram(program); for (auto &[_, extPrograms] : program->ExternalSources()) { (void)_; for (auto *extProg : extPrograms) { - if (extProg->IsASTChecked()) { - continue; + if (!extProg->IsASTLowered()) { + extProg->PushChecker(this); + auto *savedProgram2 = VarBinder()->AsETSBinder()->Program(); + varbinder::RecordTableContext recordTableCtx(VarBinder()->AsETSBinder(), extProg); + VarBinder()->AsETSBinder()->SetProgram(extProg); + VarBinder()->AsETSBinder()->ResetTopScope(extProg->GlobalScope()); + checker::SavedCheckerContext savedContext(this, Context().Status(), Context().ContainingClass()); + AddStatus(checker::CheckerStatus::IN_EXTERNAL); + CheckProgram(extProg, VarBinder()->IsGenStdLib() || extProg->IsGenAbcForExternal()); + VarBinder()->AsETSBinder()->SetProgram(savedProgram2); + VarBinder()->AsETSBinder()->ResetTopScope(savedProgram2->GlobalScope()); } - - auto *savedProgram2 = VarBinder()->AsETSBinder()->Program(); - VarBinder()->AsETSBinder()->SetProgram(extProg); - VarBinder()->AsETSBinder()->ResetTopScope(extProg->GlobalScope()); - checker::SavedCheckerContext savedContext(this, Context().Status(), Context().ContainingClass()); - AddStatus(checker::CheckerStatus::IN_EXTERNAL); - CheckProgram(extProg, VarBinder()->IsGenStdLib()); - extProg->SetFlag(parser::ProgramFlags::AST_CHECK_PROCESSED); - VarBinder()->AsETSBinder()->SetProgram(savedProgram2); - VarBinder()->AsETSBinder()->ResetTopScope(savedProgram2->GlobalScope()); } } @@ -419,16 +431,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; CHECK_NOT_NULL(type); auto res = static_cast(type->TypeFlags() & ~(TO_CLEAR)); - ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(NOT_A_TYPE_KIND)), + ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(TypeFlag::NONE)), ark::es2panda::GetPositionForDiagnostic()); return res; } @@ -539,6 +549,11 @@ Type *ETSChecker::GlobalETSAnyType() const return GetGlobalTypesHolder()->GlobalETSAnyType(); } +Type *ETSChecker::GlobalETSRelaxedAnyType() const +{ + return GetGlobalTypesHolder()->GlobalETSRelaxedAnyType(); +} + Type *ETSChecker::GlobalETSNeverType() const { return GetGlobalTypesHolder()->GlobalETSNeverType(); @@ -576,6 +591,11 @@ ETSUnionType *ETSChecker::GlobalETSUnionUndefinedNullObject() const return ret != nullptr ? ret->AsETSUnionType() : nullptr; } +ETSObjectType *ETSChecker::GlobalBuiltinClassType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalClassBuiltinType); +} + ETSObjectType *ETSChecker::GlobalBuiltinETSResizableArrayType() const { return AsETSObjectType(&GlobalTypesHolder::GlobalArrayBuiltinType); @@ -661,29 +681,34 @@ ETSObjectType *ETSChecker::GlobalBuiltinDynamicType(Language lang) const ETSObjectType *ETSChecker::GlobalBuiltinBoxType(Type *contents) { - switch (TypeKind(contents)) { - case TypeFlag::ETS_BOOLEAN: + ES2PANDA_ASSERT(contents->IsETSReferenceType()); + if (!contents->IsETSUnboxableObject()) { + auto *base = AsETSObjectType(&GlobalTypesHolder::GlobalBoxBuiltinType); + auto substitution = Substitution {}; + ES2PANDA_ASSERT(base != nullptr); + substitution.emplace(base->TypeArguments()[0]->AsETSTypeParameter(), contents); + return base->Substitute(Relation(), &substitution); + } + + switch (contents->AsETSObjectType()->UnboxableKind()) { + case ETSObjectFlags::BUILTIN_BOOLEAN: return AsETSObjectType(&GlobalTypesHolder::GlobalBooleanBoxBuiltinType); - case TypeFlag::BYTE: + case ETSObjectFlags::BUILTIN_BYTE: return AsETSObjectType(&GlobalTypesHolder::GlobalByteBoxBuiltinType); - case TypeFlag::CHAR: - return AsETSObjectType(&GlobalTypesHolder::GlobalCharBoxBuiltinType); - case TypeFlag::SHORT: + case ETSObjectFlags::BUILTIN_SHORT: return AsETSObjectType(&GlobalTypesHolder::GlobalShortBoxBuiltinType); - case TypeFlag::INT: + case ETSObjectFlags::BUILTIN_CHAR: + return AsETSObjectType(&GlobalTypesHolder::GlobalCharBoxBuiltinType); + case ETSObjectFlags::BUILTIN_INT: return AsETSObjectType(&GlobalTypesHolder::GlobalIntBoxBuiltinType); - case TypeFlag::LONG: + case ETSObjectFlags::BUILTIN_LONG: return AsETSObjectType(&GlobalTypesHolder::GlobalLongBoxBuiltinType); - case TypeFlag::FLOAT: + case ETSObjectFlags::BUILTIN_FLOAT: return AsETSObjectType(&GlobalTypesHolder::GlobalFloatBoxBuiltinType); - case TypeFlag::DOUBLE: + case ETSObjectFlags::BUILTIN_DOUBLE: return AsETSObjectType(&GlobalTypesHolder::GlobalDoubleBoxBuiltinType); default: { - auto *base = AsETSObjectType(&GlobalTypesHolder::GlobalBoxBuiltinType); - auto *substitution = NewSubstitution(); - ES2PANDA_ASSERT(base != nullptr); - substitution->emplace(base->TypeArguments()[0]->AsETSTypeParameter(), contents); - return base->Substitute(Relation(), substitution); + ES2PANDA_UNREACHABLE(); } } } @@ -698,6 +723,16 @@ const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const return globalArraySignatures_; } +const ArenaSet &ETSChecker::UnionAssemblerTypes() const +{ + return unionAssemblerTypes_; +} + +ArenaSet &ETSChecker::UnionAssemblerTypes() +{ + return unionAssemblerTypes_; +} + Type *ETSChecker::GlobalTypeError() const { return GetGlobalTypesHolder()->GlobalTypeError(); @@ -741,19 +776,20 @@ void ETSChecker::HandleUpdatedCallExpressionNode(ir::CallExpression *callExpr) VarBinder()->AsETSBinder()->HandleCustomNodes(callExpr); } -Type *ETSChecker::SelectGlobalIntegerTypeForNumeric(Type *type) +Type *ETSChecker::SelectGlobalIntegerTypeForNumeric(Type *type) const { - switch (ETSType(type)) { - case checker::TypeFlag::FLOAT: { - return GlobalIntType(); - } - case checker::TypeFlag::DOUBLE: { - return GlobalLongType(); + if (type->IsETSObjectType()) { + auto const *objectType = type->AsETSObjectType(); + + if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_FLOAT)) { + return GlobalIntBuiltinType(); } - default: { - return type; + + if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE)) { + return GlobalLongBuiltinType(); } } + return type; } Signature *ETSChecker::FindExtensionSetterInMap(util::StringView name, ETSObjectType *type) @@ -775,4 +811,20 @@ void ETSChecker::InsertExtensionGetterToMap(util::StringView name, ETSObjectType { GetGlobalTypesHolder()->InsertExtensionGetterToMap(name, type, sig); } + +bool ETSChecker::TypeHasDefaultValue(Type *tp) const +{ + return tp->IsBuiltinNumeric() || tp->IsETSBooleanType() || tp->IsETSCharType() || + Relation()->IsSupertypeOf(GlobalETSUndefinedType(), tp); +} + +/* Invoke method name in functional interfaces */ +std::string ETSChecker::FunctionalInterfaceInvokeName(size_t arity, bool hasRest) +{ + if (arity < GlobalBuiltinFunctionTypeVariadicThreshold()) { + return (hasRest ? "invokeR" : "invoke") + std::to_string(arity); + } + return "unsafeCall"; +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 009726683cf233dd7df9e195e0c550a6071b7f8e..bf0ebfb7ab9d7cff63e12abe94aa3857c454af82 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -25,8 +25,8 @@ #include "checker/types/ets/etsResizableArrayType.h" #include "checker/types/ets/types.h" #include "checker/resolveResult.h" -#include "ir/ts/tsInterfaceDeclaration.h" #include "ir/visitor/AstVisitor.h" +#include "types/type.h" #include "util/helpers.h" namespace ark::es2panda::varbinder { @@ -65,23 +65,48 @@ struct PairHash { using ComputedAbstracts = ArenaUnorderedMap, ArenaUnorderedSet>>; using ArrayMap = ArenaUnorderedMap, ETSArrayType *, PairHash>; +using ObjectInstantiationMap = ArenaUnorderedMap>; using GlobalArraySignatureMap = ArenaUnorderedMap; using DynamicCallIntrinsicsMap = ArenaUnorderedMap>; +using FunctionSignatureMap = ArenaUnorderedMap; +using FunctionInterfaceMap = ArenaUnorderedMap; using DynamicClassIntrinsicsMap = ArenaUnorderedMap; using DynamicLambdaObjectSignatureMap = ArenaUnorderedMap; using FunctionalInterfaceMap = ArenaUnorderedMap; using TypeMapping = ArenaUnorderedMap; using DynamicCallNamesMap = ArenaMap, uint32_t>; -using ConstraintCheckRecord = std::tuple *, const Substitution *, lexer::SourcePosition>; +using ConstraintCheckRecord = std::tuple *, const Substitution, lexer::SourcePosition>; // can't use util::DiagnosticWithParams because std::optional can't contain references using MaybeDiagnosticInfo = std::optional>; using AstNodePtr = ir::AstNode *; +using TypePtr = Type *; class ETSChecker final : public Checker { public: - explicit ETSChecker(util::DiagnosticEngine &diagnosticEngine); - explicit ETSChecker(util::DiagnosticEngine &diagnosticEngine, ArenaAllocator *programAllocator); + explicit ETSChecker(ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + ThreadSafeArenaAllocator *programAllocator = nullptr) + // NOLINTNEXTLINE(readability-redundant-member-init) + : Checker(allocator, diagnosticEngine, programAllocator), + arrayTypes_(Allocator()->Adapter()), + objectInstantiationMap_(Allocator()->Adapter()), + invokeToArrowSignatures_(Allocator()->Adapter()), + arrowToFuncInterfaces_(Allocator()->Adapter()), + globalArraySignatures_(Allocator()->Adapter()), + unionAssemblerTypes_(Allocator()->Adapter()), + dynamicIntrinsics_ {DynamicCallIntrinsicsMap {Allocator()->Adapter()}, + DynamicCallIntrinsicsMap {Allocator()->Adapter()}}, + dynamicClasses_ {DynamicClassIntrinsicsMap(Allocator()->Adapter()), + DynamicClassIntrinsicsMap(Allocator()->Adapter())}, + dynamicLambdaSignatureCache_(Allocator()->Adapter()), + functionalInterfaceCache_(Allocator()->Adapter()), + apparentTypes_(Allocator()->Adapter()), + dynamicCallNames_ { + {DynamicCallNamesMap(Allocator()->Adapter()), DynamicCallNamesMap(Allocator()->Adapter())}}, + overloadSigContainer_(Allocator()->Adapter()), + readdedChecker_(Allocator()->Adapter()) + { + } ~ETSChecker() override = default; @@ -107,6 +132,7 @@ public: Type *GlobalETSNullType() const; Type *GlobalETSUndefinedType() const; Type *GlobalETSAnyType() const; + Type *GlobalETSRelaxedAnyType() const; Type *GlobalETSNeverType() const; Type *GlobalETSStringLiteralType() const; Type *GlobalETSBigIntType() const; @@ -124,6 +150,7 @@ public: ETSObjectType *GlobalETSObjectType() const; ETSUnionType *GlobalETSUnionUndefinedNull() const; ETSUnionType *GlobalETSUnionUndefinedNullObject() const; + ETSObjectType *GlobalBuiltinClassType() const; ETSObjectType *GlobalBuiltinETSResizableArrayType() const; ETSObjectType *GlobalBuiltinETSStringType() const; ETSObjectType *GlobalBuiltinETSBigIntType() const; @@ -148,6 +175,9 @@ public: GlobalArraySignatureMap &GlobalArrayTypes(); const GlobalArraySignatureMap &GlobalArrayTypes() const; + const ArenaSet &UnionAssemblerTypes() const; + ArenaSet &UnionAssemblerTypes(); + Type *GlobalTypeError() const; [[nodiscard]] Type *InvalidateType(ir::Typed *node); [[nodiscard]] Type *TypeError(ir::Typed *node, const diagnostic::DiagnosticKind &diagKind, @@ -170,6 +200,7 @@ public: Type *GuaranteedTypeForUncheckedCallReturn(Signature *sig); Type *GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable *prop); Type *GuaranteedTypeForUnionFieldAccess(ir::MemberExpression *memberExpression, ETSUnionType *etsUnionType); + void ReputCheckerData(); [[nodiscard]] bool IsETSChecker() const noexcept override { @@ -180,6 +211,7 @@ public: void CheckObjectLiteralKeys(const ArenaVector &properties); Type *BuildBasicClassProperties(ir::ClassDefinition *classDef); ETSObjectType *BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType); + Type *MaybeGradualType(ir::AstNode *node, ETSObjectType *type); Type *BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *interfaceDecl); ETSObjectType *GetSuperType(ETSObjectType *type); ArenaVector GetInterfaces(ETSObjectType *type); @@ -188,8 +220,7 @@ public: void ValidateImplementedInterface(ETSObjectType *type, Type *interface, std::unordered_set *extendsSet, const lexer::SourcePosition &pos); void ResolveDeclaredMembersOfObject(const Type *type); - lexer::Number ExtractNumericValue(Type const *const indexType); - std::optional GetTupleElementAccessValue(const Type *type); + std::optional GetTupleElementAccessValue(const ir::Expression *expr); bool ValidateArrayIndex(ir::Expression *expr, bool relaxed = false); bool ValidateTupleIndex(const ETSTupleType *tuple, ir::MemberExpression *expr, bool reportError = true); bool ValidateTupleIndexFromEtsObject(const ETSTupleType *const tuple, ir::MemberExpression *expr); @@ -198,6 +229,7 @@ public: ETSTypeParameter *SetUpParameterType(ir::TSTypeParameter *param); void GetInterfacesOfClass(ETSObjectType *type, ArenaVector &interfaces); void CheckIfOverrideIsValidInInterface(ETSObjectType *classType, Signature *sig, Signature *sigFunc); + void CheckDynamicInheritanceAndImplement(ETSObjectType *const interfaceOrClassType); void CheckFunctionRedeclarationInInterface(ETSObjectType *classType, ArenaVector &similarSignatures, Signature *sigFunc); void ValidateAbstractMethodsToBeImplemented(ArenaVector &abstractsToBeImplemented, @@ -242,8 +274,15 @@ public: void CreateFunctionTypesFromAbstracts(const std::vector &abstracts, ArenaVector *target); void CheckCyclicConstructorCall(Signature *signature); + void CheckAnnotationReference(const ir::MemberExpression *memberExpr, const varbinder::LocalVariable *prop); + std::vector HandlePropertyResolution(varbinder::LocalVariable *const prop, + ir::MemberExpression *const memberExpr, + varbinder::Variable *const globalFunctionVar, + PropertySearchFlags searchFlag); std::vector ResolveMemberReference(const ir::MemberExpression *memberExpr, const ETSObjectType *target); + varbinder::LocalVariable *ResolveOverloadReference(const ir::Identifier *ident, ETSObjectType *objType, + PropertySearchFlags searchFlags); void WarnForEndlessLoopInGetterSetter(const ir::MemberExpression *const memberExpr); varbinder::Variable *GetExtensionFuncVarInGlobalFunction(const ir::MemberExpression *const memberExpr); varbinder::Variable *GetExtensionFuncVarInGlobalField(const ir::MemberExpression *const memberExpr); @@ -272,6 +311,8 @@ public: void VariableTypeFromInitializer(varbinder::Variable *variable, Type *annotationType, Type *initType); + bool TypeHasDefaultValue(Type *tp) const; + // Type creation ByteType *CreateByteType(int8_t value); ETSBooleanType *CreateETSBooleanType(bool value); @@ -286,6 +327,7 @@ public: ETSResizableArrayType *CreateETSMultiDimResizableArrayType(Type *element, size_t dimSize); ETSResizableArrayType *CreateETSResizableArrayType(Type *element); ETSArrayType *CreateETSArrayType(Type *elementType, bool isCachePolluting = false); + Type *CreateGradualType(Type *baseType, Language lang = Language(Language::Id::JS)); Type *CreateETSUnionType(Span constituentTypes); template Type *CreateETSUnionType(Type *const (&arr)[N]) // NOLINT(modernize-avoid-c-arrays) @@ -304,9 +346,6 @@ public: bool isRecursive = false); ETSFunctionType *CreateETSArrowType(Signature *signature); ETSFunctionType *CreateETSMethodType(util::StringView name, ArenaVector &&signatures); - ETSFunctionType *CreateETSDynamicArrowType(Signature *signature, Language lang); - ETSFunctionType *CreateETSDynamicMethodType(util::StringView name, ArenaVector &&signatures, - Language lang); ETSExtensionFuncHelperType *CreateETSExtensionFuncHelperType(ETSFunctionType *classMethodType, ETSFunctionType *extensionFunctionType); void AddThisReturnTypeFlagForInterfaceInvoke(ETSObjectType *interface); @@ -316,8 +355,6 @@ public: std::tuple CreateBuiltinArraySignatureInfo(const ETSArrayType *arrayType, size_t dim); Signature *CreateBuiltinArraySignature(const ETSArrayType *arrayType, size_t dim); - IntType *CreateIntTypeFromType(Type *type); - std::tuple CheckForDynamicLang(ir::AstNode *declNode, util::StringView assemblerName); ETSObjectType *CreatePromiseOf(Type *type); Signature *CreateSignature(SignatureInfo *info, Type *returnType, ir::ScriptFunction *func); @@ -325,7 +362,6 @@ public: SignatureInfo *CreateSignatureInfo(); // Arithmetic - Type *NegateNumericType(Type *type, ir::Expression *node); bool CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::TokenType op); [[nodiscard]] bool CheckBinaryPlusMultDivOperandsForUnionType(const Type *leftType, const Type *rightType, const ir::Expression *left, @@ -341,12 +377,9 @@ public: checker::Type *CheckBinaryOperatorMulDivMod( std::tuple op, bool isEqualOp, std::tuple types); - checker::Type *CheckBinaryOperatorForIntEnums(const checker::Type *const leftType, - const checker::Type *const rightType); + checker::Type *CheckBinaryBitwiseOperatorForIntEnums(const checker::Type *const leftType, const checker::Type *const rightType); - checker::Type *CheckBinaryOperatorPlusForEnums(const checker::Type *const leftType, - const checker::Type *const rightType); checker::Type *CheckBinaryOperatorPlus( std::tuple op, bool isEqualOp, std::tuple types); @@ -357,9 +390,8 @@ public: std::tuple op, bool isEqualOp, std::tuple types); // CC-OFFNXT(G.FUN.01-CPP) solid logic - checker::Type *CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, ir::BinaryExpression *expr, - checker::Type *leftType, checker::Type *rightType, Type *unboxedL, - Type *unboxedR); + checker::Type *CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, checker::Type *leftType, + checker::Type *rightType, Type *unboxedL, Type *unboxedR); std::tuple CheckBinaryOperatorStrictEqual(ir::Expression *left, lexer::TokenType operationType, lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType); @@ -372,15 +404,13 @@ public: checker::Type *rightType); checker::Type *CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right, lexer::SourcePosition pos); + bool CheckIfNumeric(Type *type); + bool CheckIfFloatingPoint(Type *type); bool AdjustNumberLiteralType(ir::NumberLiteral *literal, Type *literalType, Type *otherType); Type *HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); - Type *HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); - void FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression); - template - Type *PerformArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); - - Type *HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); + void SetGenerateValueOfFlags(std::tuple types, + std::tuple nodes); template Type *PerformRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); @@ -404,21 +434,20 @@ public: bool CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda); bool IsCompatibleTypeArgument(ETSTypeParameter *typeParam, Type *typeArgument, const Substitution *substitution); - Substitution *NewSubstitution() + ArenaSubstitution *NewArenaSubstitution() { - return ProgramAllocator()->New(ProgramAllocator()->Adapter()); + return ProgramAllocator()->New(ProgramAllocator()->Adapter()); } - Substitution *CopySubstitution(const Substitution *src) - { - return ProgramAllocator()->New(*src); - } bool ValidateTypeSubstitution(const ArenaVector &typeParams, Type *ctype, Type *argumentType, Substitution *substitution); bool ProcessUntypedParameter(ir::AstNode *declNode, size_t paramIndex, Signature *paramSig, Signature *argSig, Substitution *substitution); + static Substitution ArenaSubstitutionToSubstitution(const ArenaSubstitution *orig); void EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg); + void EmplaceSubstituted(ArenaSubstitution *substitution, ETSTypeParameter *tparam, Type *typeArg); + [[nodiscard]] bool EnhanceSubstitutionForType(const ArenaVector &typeParams, Type *paramType, Type *argumentType, Substitution *substitution); [[nodiscard]] bool EnhanceSubstitutionForReadonly(const ArenaVector &typeParams, ETSReadonlyType *paramType, @@ -450,13 +479,38 @@ public: bool ValidateSignatureInvocationContext(Signature *substitutedSig, ir::Expression *argument, std::size_t index, TypeRelationFlag flags); bool CheckOptionalLambdaFunction(ir::Expression *argument, Signature *substitutedSig, std::size_t index); - bool ValidateArgumentAsIdentifier(const ir::Identifier *identifier); bool IsValidRestArgument(ir::Expression *argument, Signature *substitutedSig, TypeRelationFlag flags, std::size_t index); + bool SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr, Signature *substitutedSig); bool ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, bool reportError, bool unique); void ThrowSignatureMismatch(ArenaVector &signatures, const ArenaVector &arguments, const lexer::SourcePosition &pos, std::string_view signatureKind); + Signature *FirstMatchSignatures(ir::CallExpression *expr, checker::Type *calleeType); + Signature *MatchOrderSignatures(ArenaVector &signatures, + const ir::TSTypeParameterInstantiation *typeArguments, + const ArenaVector &arguments, const lexer::SourcePosition &pos, + TypeRelationFlag resolveFlags); + void CleanArgumentsInformation(const ArenaVector &arguments); + Signature *ValidateOrderSignature( + std::tuple info, + const ArenaVector &arguments, const lexer::SourcePosition &pos, + const std::vector &argTypeInferenceRequired, const bool unique); + bool ValidateOrderSignatureRequiredParams(Signature *substitutedSig, const ArenaVector &arguments, + TypeRelationFlag flags, + const std::vector &argTypeInferenceRequired); + bool ValidateOrderSignatureInvocationContext(Signature *substitutedSig, ir::Expression *argument, std::size_t index, + TypeRelationFlag flags); + void ThrowOverloadMismatch(util::StringView callName, const ArenaVector &arguments, + const lexer::SourcePosition &pos, std::string_view signatureKind); + Signature *ResolveTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, + const lexer::SourcePosition &pos, + TypeRelationFlag reportFlag = TypeRelationFlag::NONE); + Signature *ResolvePotentialTrailingLambda(ir::CallExpression *callExpr, ArenaVector const &signatures, + ArenaVector &arguments); + bool SetPreferredTypeBeforeValidate(Signature *substitutedSig, ir::Expression *argument, size_t index, + TypeRelationFlag flags, const std::vector &argTypeInferenceRequired); + // CC-OFFNXT(G.FUN.01-CPP) solid logic Signature *ValidateSignatures(ArenaVector &signatures, const ir::TSTypeParameterInstantiation *typeArguments, @@ -469,6 +523,8 @@ public: void SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature *&prevSig, std::tuple info, bool lookForClassType); + void CheckAmbiguousCall(Type *&mostSpecificType, Type *sigType, Signature *prevSig, Signature *sig, + const lexer::SourcePosition &pos); void CollectSuitableSignaturesForTypeInference(size_t paramIdx, ArenaVector &signatures, ArenaMultiMap &bestSignaturesForParameter, const ArenaVector &arguments); @@ -482,6 +538,7 @@ public: Signature *ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpression *callExpr, ArenaVector const &signatures, ArenaVector &arguments); + Signature *MakeSignatureInvocable(Signature *sig, ir::CallExpression *callExpr); Signature *ResolveCallExpressionAndTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, const lexer::SourcePosition &pos, TypeRelationFlag reportFlag = TypeRelationFlag::NONE); @@ -502,8 +559,8 @@ public: Signature *CheckEveryAbstractSignatureIsOverridden(ETSFunctionType *target, ETSFunctionType *source); static Signature *GetSignatureFromMethodDefinition(const ir::MethodDefinition *methodDef); bool CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType *overload, - const ir::MethodDefinition *currentFunc, bool omitSameAsm = false); - static bool CmpAssemblerTypesWithRank(Signature const *const sig1, Signature const *const sig2) noexcept; + const ir::MethodDefinition *currentFunc, bool omitSameAsm = false, + TypeRelationFlag relationFlags = TypeRelationFlag::NO_RETURN_TYPE_CHECK); static bool HasSameAssemblySignature(Signature const *const sig1, Signature const *const sig2) noexcept; static bool HasSameAssemblySignatures(ETSFunctionType const *const func1, ETSFunctionType const *const func2) noexcept; @@ -535,9 +592,9 @@ public: void ReplaceScope(ir::AstNode *root, ir::AstNode *oldNode, varbinder::Scope *newScope); // Helpers + std::string FunctionalInterfaceInvokeName(size_t arity, bool hasRest); static std::string GetAsyncImplName(const util::StringView &name); static std::string GetAsyncImplName(ir::MethodDefinition *asyncMethod); - static bool IsAsyncImplMethod(ir::MethodDefinition const *method); std::vector GetNameForSynteticObjectType(const util::StringView &source); template void BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType, ir::ETSImportDeclaration *importDecl, @@ -547,6 +604,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); @@ -564,14 +622,11 @@ public: const ir::TSTypeParameterInstantiation *typeParams, size_t idx); Type *GetTypeFromTypeParameterReference(varbinder::LocalVariable *var, const lexer::SourcePosition &pos); Type *GetNonConstantType(Type *type); - checker::Type *GetElementTypeOfArray(checker::Type *type); + checker::Type *GetElementTypeOfArray(checker::Type *type) const; const checker::Type *GetElementTypeOfArray(const checker::Type *type) const; bool IsNullLikeOrVoidExpression(const ir::Expression *expr) const; bool IsConstantExpression(ir::Expression *expr, Type *type); - void ValidateUnaryOperatorOperand(varbinder::Variable *variable); - bool ValidateAnnotationPropertyType(checker::Type *tsType); - void ProcessRequiredFields(ArenaUnorderedMap &fieldMap, - ir::AnnotationUsage *st, ETSChecker *checker) const; + void ValidateUnaryOperatorOperand(varbinder::Variable *variable, ir::Expression *expr); void CheckFunctionSignatureAnnotations(const ArenaVector ¶ms, ir::TSTypeParameterDeclaration *typeParams, ir::TypeNode *returnTypeAnnotation); @@ -589,18 +644,15 @@ public: void CheckSinglePropertyAnnotation(ir::AnnotationUsage *st, ir::AnnotationDeclaration *annoDecl); void CheckMultiplePropertiesAnnotation(ir::AnnotationUsage *st, util::StringView const &baseName, ArenaUnorderedMap &fieldMap); + void InferLambdaInAssignmentExpression(ir::AssignmentExpression *const expr); void InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init); checker::Type *ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL, checker::Type *unboxedR); - Type *ApplyUnaryOperatorPromotion(Type *type, bool createConst = true, bool doPromotion = true, - bool isCondExpr = false); + Type *ApplyUnaryOperatorPromotion(ir::Expression *expr, Type *type, bool isCondExpr = false); + Type *GetUnaryOperatorPromotedType(Type *type, const bool doPromotion = true); Type *HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType); - bool HandleLogicalPotentialResult(ir::Expression *left, ir::Expression *right, ir::BinaryExpression *expr, - checker::Type *leftType); - - checker::Type *FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags, - ir::Expression *init); + checker::Type *FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags); void CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName); checker::Type *CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init, ir::ModifierFlags flags); @@ -632,11 +684,7 @@ public: Type *MaybeUnboxInRelation(Type *objectType); Type *MaybeUnboxConditionalInRelation(Type *objectType); Type *MaybeBoxInRelation(Type *objectType); - void AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType); - ir::BoxingUnboxingFlags GetBoxingFlag(Type *boxingType); - ir::BoxingUnboxingFlags GetUnboxingFlag(Type const *unboxingType) const; Type *MaybeBoxExpression(ir::Expression *expr); - Type *MaybeUnboxExpression(ir::Expression *expr); Type *MaybeBoxType(Type *type) const; Type *MaybeUnboxType(Type *type) const; Type const *MaybeBoxType(Type const *type) const; @@ -646,21 +694,22 @@ public: bool CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue); void CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase, const lexer::SourcePosition &pos); - std::string GetStringFromLiteral(ir::Expression *caseTest) const; - varbinder::Variable *FindVariableInFunctionScope(util::StringView name); + varbinder::Variable *FindVariableInFunctionScope( + util::StringView name, const varbinder::ResolveBindingOptions options = varbinder::ResolveBindingOptions::ALL); std::pair FindVariableInClassOrEnclosing( util::StringView name, const ETSObjectType *classType); - varbinder::Variable *FindVariableInGlobal(const ir::Identifier *identifier); + varbinder::Variable *FindVariableInGlobal( + const ir::Identifier *identifier, + const varbinder::ResolveBindingOptions options = varbinder::ResolveBindingOptions::ALL); varbinder::Variable *ExtraCheckForResolvedError(ir::Identifier *ident); void ValidateResolvedIdentifier(ir::Identifier *ident); static bool IsVariableStatic(const varbinder::Variable *var); static bool IsVariableGetterSetter(const varbinder::Variable *var); static bool IsVariableExtensionAccessor(const varbinder::Variable *var); + static bool IsVariableOverloadDeclaration(const varbinder::Variable *var); bool IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare); void SaveCapturedVariable(varbinder::Variable *var, ir::Identifier *ident); bool SaveCapturedVariableInLocalClass(varbinder::Variable *var, ir::Identifier *ident); - void MaybeAddBoxingFlagInRelation(TypeRelation *relation, Type *target); - void MaybeAddUnboxingFlagInRelation(TypeRelation *relation, Type *source, Type *self); void CheckUnboxedTypeWidenable(TypeRelation *relation, Type *target, Type *self); void CheckUnboxedTypesAssignable(TypeRelation *relation, Type *source, Type *target); void CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *source, Type *target); @@ -675,6 +724,12 @@ public: ir::BlockStatement *FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p); void CheckExceptionClauseType(const std::vector &exceptions, ir::CatchClause *catchClause, checker::Type *clauseType); + + void CheckConstructorOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; + void CheckFunctionOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; + void CheckClassMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; + void CheckInterfaceMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const; + ETSObjectType *GetRelevantArgumentedTypeFromChild(ETSObjectType *child, ETSObjectType *target); util::StringView GetHashFromTypeArguments(const ArenaVector &typeArgTypes); util::StringView GetHashFromSubstitution(const Substitution *substitution, const bool isExtensionFuncFlag); @@ -685,7 +740,7 @@ public: bool IsExtensionAccessorFunctionType(const checker::Type *type); bool IsArrayExprSizeValidForTuple(const ir::ArrayExpression *arrayExpr, const ETSTupleType *tuple); void ModifyPreferredType(ir::ArrayExpression *arrayExpr, Type *newPreferredType); - Type *SelectGlobalIntegerTypeForNumeric(Type *type); + Type *SelectGlobalIntegerTypeForNumeric(Type *type) const; ir::ClassProperty *ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope); ir::Expression *GenerateImplicitInstantiateArg(const std::string &className); @@ -705,7 +760,6 @@ public: void ETSObjectTypeDeclNode(ETSChecker *checker, ETSObjectType *const objectType); ir::CallExpression *CreateExtensionAccessorCall(ETSChecker *checker, ir::MemberExpression *expr, ArenaVector &&args); - static void SetPreferredTypeIfPossible(ir::Expression *expr, Type *targetType); Signature *FindRelativeExtensionGetter(ir::MemberExpression *const expr, ETSFunctionType *funcType); Signature *FindRelativeExtensionSetter(ir::MemberExpression *const expr, ETSFunctionType *funcType); Type *GetExtensionAccessorReturnType(ir::MemberExpression *expr); @@ -759,15 +813,17 @@ public: void MakePropertyNonNullish(ETSObjectType *classType, varbinder::LocalVariable *prop); void ValidateObjectLiteralForRequiredType(const ETSObjectType *requiredType, const ir::ObjectExpression *initObjExpr); + bool IsStaticInvoke(ir::MemberExpression *const expr); + void ValidateCallExpressionIdentifier(ir::Identifier *const ident, Type *const type); using NamedAccessMeta = std::tuple; static NamedAccessMeta FormNamedAccessMetadata(varbinder::Variable const *prop); // Smart cast support - [[nodiscard]] checker::Type *ResolveSmartType(checker::Type *sourceType, checker::Type *targetType); + [[nodiscard]] checker::Type *ResolveSmartType(checker::Type *sourceType, checker::Type *targetType, + std::optional value = std::nullopt); [[nodiscard]] std::pair CheckTestNullishCondition(Type *testedType, Type *actualType, bool strict); - [[nodiscard]] std::pair CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType, - bool strict); + [[nodiscard]] std::pair CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType); [[nodiscard]] std::pair CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType); void ApplySmartCast(varbinder::Variable const *variable, checker::Type *smartType) noexcept; @@ -779,15 +835,6 @@ public: static Type *TryToInstantiate(Type *type, ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes); - // Dynamic interop - template - Signature *ResolveDynamicCallExpression(ir::Expression *callee, const ArenaVector &arguments, Language lang, - bool isConstruct); - ir::ClassProperty *CreateStaticReadonlyField(const char *name); - void BuildClassBodyFromDynamicImports(const ArenaVector &dynamicImports, - ArenaVector *classBody); - void BuildDynamicImportClass(); - void BuildLambdaObjectClass(ETSObjectType *functionalInterface, ir::TypeNode *retTypeAnnotation); // Trailing lambda void EnsureValidCurlyBrace(ir::CallExpression *callExpr); @@ -838,7 +885,7 @@ public: return util::NodeAllocator::ForceSetParent(ProgramAllocator(), std::forward(args)...); } - ArenaVector &PendingConstraintCheckRecords(); + std::vector &PendingConstraintCheckRecords(); size_t &ConstraintCheckScopesCount(); ETSObjectType *GetCachedFunctionalInterface(ir::ETSFunctionType *type); @@ -867,6 +914,7 @@ public: void LogUnresolvedReferenceError(ir::Identifier *ident); void WrongContextErrorClassifyByType(ir::Identifier *ident); + Type *CreateSyntheticTypeFromOverload(varbinder::Variable *const var); void CreateOverloadSigContainer(Signature *overloadHelperSig) { @@ -883,6 +931,26 @@ public: return overloadSigContainer_; } + ObjectInstantiationMap &GetObjectInstantiationMap() + { + return objectInstantiationMap_; + } + + FunctionSignatureMap &GetInvokeToArrowSignatures() + { + return invokeToArrowSignatures_; + } + + FunctionInterfaceMap &GetArrowToFuncInterfaces() + { + return arrowToFuncInterfaces_; + } + + void ClearApparentTypes() noexcept + { + apparentTypes_.clear(); + } + void CleanUp() override { Checker::CleanUp(); @@ -890,6 +958,7 @@ public: pendingConstraintCheckRecords_.clear(); constraintCheckScopesCount_ = 0; globalArraySignatures_.clear(); + unionAssemblerTypes_.clear(); GetCachedComputedAbstracts()->clear(); for (auto &dynamicCallIntrinsicsMap : dynamicIntrinsics_) { dynamicCallIntrinsicsMap.clear(); @@ -939,7 +1008,6 @@ private: std::pair GetTargetIdentifierAndType(ir::Identifier *ident); void NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar, const ETSObjectType *classType); - void ValidateCallExpressionIdentifier(ir::Identifier *const ident, Type *const type); void ValidateNewClassInstanceIdentifier(ir::Identifier *const ident); void ValidateMemberIdentifier(ir::Identifier *const ident); void ValidateAssignmentIdentifier(ir::Identifier *const ident, Type *const type); @@ -983,7 +1051,6 @@ private: ir::MethodDefinition *CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature, ir::TypeNode *retTypeAnnotation); - void ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector *statements); void EmitDynamicModuleClassInitCall(); DynamicCallIntrinsicsMap *DynamicCallIntrinsics(bool isConstruct) { @@ -999,6 +1066,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); @@ -1006,15 +1074,6 @@ private: bool ComputeSuperType(ETSObjectType *type); - template - UType HandleModulo(UType leftValue, UType rightValue); - - template - Type *HandleBitWiseArithmetic(Type *leftValue, Type *rightValue, lexer::TokenType operationType); - - template - typename TargetType::UType GetOperand(Type *type); - template ETSObjectType *AsETSObjectType(Type *(GlobalTypesHolder::*typeFunctor)(Args...), Args... args) const; Signature *GetMostSpecificSignature(ArenaVector &compatibleSignatures, @@ -1026,14 +1085,15 @@ private: const lexer::SourcePosition &pos, TypeRelationFlag resolveFlags); // Trailing lambda void MoveTrailingBlockToEnclosingBlockStatement(ir::CallExpression *callExpr); + ir::ScriptFunction *CreateLambdaFunction(ir::BlockStatement *trailingBlock, Signature *sig); void TransformTraillingLambda(ir::CallExpression *callExpr, Signature *sig); ArenaVector ExtendArgumentsWithFakeLamda(ir::CallExpression *callExpr); // Static invoke bool SetStaticInvokeValues(ir::Identifier *const ident, ir::Identifier *classId, ir::Identifier *methodId, varbinder::LocalVariable *instantiateMethod); - void CreateTransformedCallee(ir::Identifier *classId, ir::Identifier *methodId, ir::Identifier *const ident, - varbinder::LocalVariable *instantiateMethod); + void CreateTransformedCallee(ir::Identifier *ident, ir::Identifier *classId, ir::Identifier *methodId, + ir::CallExpression *callExpr); bool TryTransformingToStaticInvoke(ir::Identifier *ident, const Type *resolvedType); // Partial @@ -1050,9 +1110,13 @@ private: std::unordered_set &typeAliases); ArrayMap arrayTypes_; - ArenaVector pendingConstraintCheckRecords_; + std::vector pendingConstraintCheckRecords_ {}; + ObjectInstantiationMap objectInstantiationMap_; + FunctionSignatureMap invokeToArrowSignatures_; + FunctionInterfaceMap arrowToFuncInterfaces_; size_t constraintCheckScopesCount_ {0}; GlobalArraySignatureMap globalArraySignatures_; + ArenaSet unionAssemblerTypes_; ComputedAbstracts *cachedComputedAbstracts_ {nullptr}; // NOTE(aleksisch): Extract dynamic from checker to separate class std::array dynamicIntrinsics_; @@ -1065,6 +1129,7 @@ private: evaluate::ScopedDebugInfoPlugin *debugInfoPlugin_ {nullptr}; std::unordered_set elementStack_; ArenaVector overloadSigContainer_; + ArenaSet readdedChecker_; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/JSchecker.cpp b/ets2panda/checker/JSchecker.cpp index 3efe6f5b8d7bbc0d6d74297acdd42fcab796c441..62c02cc2064b2540fbdcc5551816e2681d53fa0f 100644 --- a/ets2panda/checker/JSchecker.cpp +++ b/ets2panda/checker/JSchecker.cpp @@ -23,7 +23,6 @@ namespace ark::es2panda::checker { bool JSChecker::StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const util::Options &options) { - Initialize(varbinder); varbinder->IdentifierAnalysis(); if (options.IsDumpAst()) { diff --git a/ets2panda/checker/JSchecker.h b/ets2panda/checker/JSchecker.h index 0d0a6052089ea1bd959d2a88ca3b4774b31a2d69..3e261069aa8196ca0803ad84e0d67a53e8d9112d 100644 --- a/ets2panda/checker/JSchecker.h +++ b/ets2panda/checker/JSchecker.h @@ -23,9 +23,9 @@ namespace ark::es2panda::checker { class JSChecker : public Checker { public: // NOLINTNEXTLINE(readability-redundant-member-init) - explicit JSChecker(util::DiagnosticEngine &diagnosticEngine, - [[maybe_unused]] ArenaAllocator *programAllocator = nullptr) - : Checker(diagnosticEngine) + explicit JSChecker([[maybe_unused]] ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + [[maybe_unused]] ThreadSafeArenaAllocator *programAllocator = nullptr) + : Checker(allocator, diagnosticEngine, programAllocator) { } diff --git a/ets2panda/checker/SemanticAnalyzer.h b/ets2panda/checker/SemanticAnalyzer.h index b0db804c0861695bd2c965bb20e8f232feabd08c..1126ba93c03b19140c9f8c2a5b66671fcfd2a0d1 100644 --- a/ets2panda/checker/SemanticAnalyzer.h +++ b/ets2panda/checker/SemanticAnalyzer.h @@ -27,6 +27,7 @@ #include "ir/base/decorator.h" #include "ir/base/metaProperty.h" #include "ir/base/methodDefinition.h" +#include "ir/base/overloadDeclaration.h" #include "ir/base/property.h" #include "ir/base/scriptFunction.h" #include "ir/base/spreadElement.h" @@ -37,6 +38,7 @@ #include "ir/base/tsSignatureDeclaration.h" #include "ir/ets/etsClassLiteral.h" #include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsIntrinsicNode.h" #include "ir/ets/etsImportDeclaration.h" #include "ir/ets/etsKeyofType.h" #include "ir/ets/etsNewArrayInstanceExpression.h" diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index 0e66ad6f5368717e40284ecd8038014bc4e093f9..655c154d5f62250d0c9fb34c1864a88a80a7bb09 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -1740,6 +1740,7 @@ static void AddEnumValueDeclaration(checker::TSChecker *checker, double number, enumScope->AddDecl(checker->Allocator(), decl, ScriptExtension::TS); res = enumScope->FindLocal(memberStr, varbinder::ResolveBindingOptions::BINDINGS); ES2PANDA_ASSERT(res && res->IsEnumVariable()); + ES2PANDA_ASSERT(enumVar != nullptr); enumVar = res->AsEnumVariable(); enumVar->AsEnumVariable()->SetBackReference(); enumVar->SetTsType(checker->GlobalStringType()); diff --git a/ets2panda/checker/TSAnalyzerUnreachable.cpp b/ets2panda/checker/TSAnalyzerUnreachable.cpp index e5c22b6907deb5347cbd8553504c68ee31ae8b77..43a987d1cbef1363bed34d02767b82d326bec5b8 100644 --- a/ets2panda/checker/TSAnalyzerUnreachable.cpp +++ b/ets2panda/checker/TSAnalyzerUnreachable.cpp @@ -48,6 +48,11 @@ checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::MethodDefinition *node) co ES2PANDA_UNREACHABLE(); } +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::OverloadDeclaration *node) const +{ + ES2PANDA_UNREACHABLE(); +} + checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::Property *expr) const { ES2PANDA_UNREACHABLE(); @@ -84,6 +89,11 @@ checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSFunctionType *node) con ES2PANDA_UNREACHABLE(); } +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSIntrinsicNode *node) const +{ + ES2PANDA_UNREACHABLE(); +} + checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSImportDeclaration *node) const { ES2PANDA_UNREACHABLE(); diff --git a/ets2panda/checker/TSchecker.cpp b/ets2panda/checker/TSchecker.cpp index 9e820dfc4d067f98c1dfe1c36f675edcd0e9ed44..4fe4acde5426a0b21f5bd43b23d7fb79f17e0d44 100644 --- a/ets2panda/checker/TSchecker.cpp +++ b/ets2panda/checker/TSchecker.cpp @@ -23,7 +23,6 @@ namespace ark::es2panda::checker { bool TSChecker::StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const util::Options &options) { - Initialize(varbinder); varbinder->IdentifierAnalysis(); if (options.IsDumpAst()) { diff --git a/ets2panda/checker/TSchecker.h b/ets2panda/checker/TSchecker.h index 83ecaa50ca0e3d5ecf6df95e1d76e898c693a1cb..77756cb4891aa666dcdf36041f559260a12ff917 100644 --- a/ets2panda/checker/TSchecker.h +++ b/ets2panda/checker/TSchecker.h @@ -101,7 +101,7 @@ class TSMethodSignature; class ChainExpression; class VariableDeclarator; -enum class AstNodeType; +enum class AstNodeType : uint8_t; } // namespace ark::es2panda::ir namespace ark::es2panda::checker { @@ -121,8 +121,9 @@ struct TupleTypeInfo { class TSChecker : public Checker { public: // NOLINTNEXTLINE(readability-redundant-member-init) - explicit TSChecker(util::DiagnosticEngine &diagnosticEngine, [[maybe_unused]] ArenaAllocator *programAllocator) - : Checker(diagnosticEngine) + explicit TSChecker([[maybe_unused]] ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + [[maybe_unused]] ThreadSafeArenaAllocator *programAllocator) + : Checker(allocator, diagnosticEngine, programAllocator) { } diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index d4adeb07cc20d29c1f6cd650981628aeb2c0df7f..ab4e17a2e079d1182cf78b6a0e32ecc6eedbaa1d 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -20,8 +20,9 @@ #include "checker/types/ts/unionType.h" namespace ark::es2panda::checker { -Checker::Checker(util::DiagnosticEngine &diagnosticEngine, ArenaAllocator *programAllocator) - : allocator_(SpaceType::SPACE_TYPE_COMPILER, nullptr, true), +Checker::Checker(ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + ThreadSafeArenaAllocator *programAllocator) + : allocator_(allocator), programAllocator_(programAllocator), context_(this, CheckerStatus::NO_OPTS), diagnosticEngine_(diagnosticEngine) @@ -163,6 +164,11 @@ bool Checker::IsAnyError() return DiagnosticEngine().IsAnyError(); } +bool Checker::IsDeclForDynamicStaticInterop() const +{ + return Program()->IsDeclForDynamicStaticInterop(); +} + ScopeContext::ScopeContext(Checker *checker, varbinder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_), prevProgram_(checker_->Program()) { @@ -177,14 +183,16 @@ ScopeContext::ScopeContext(Checker *checker, varbinder::Scope *newScope) void Checker::CleanUp() { + if (!program_->IsASTLowered()) { + globalTypes_ = allocator_->New(allocator_); + } context_ = CheckerContext(this, CheckerStatus::NO_OPTS); - globalTypes_ = allocator_.New(&allocator_); - relation_ = allocator_.New(this); - identicalResults_.cached.clear(); - assignableResults_.cached.clear(); - comparableResults_.cached.clear(); - uncheckedCastableResults_.cached.clear(); - supertypeResults_.cached.clear(); + relation_ = allocator_->New(this); + identicalResults_.Clear(); + assignableResults_.Clear(); + comparableResults_.Clear(); + uncheckedCastableResults_.Clear(); + supertypeResults_.Clear(); typeStack_.clear(); namedTypeStack_.clear(); } diff --git a/ets2panda/checker/checker.h b/ets2panda/checker/checker.h index 8c0e0dc583d66fc04ca0e3b7c8175699eead9846..2cfda48b919cd4f30bf8403045b982324158b07f 100644 --- a/ets2panda/checker/checker.h +++ b/ets2panda/checker/checker.h @@ -33,7 +33,7 @@ namespace ark::es2panda::ir { class AstNode; class Expression; class BlockStatement; -enum class AstNodeType; +enum class AstNodeType : uint8_t; } // namespace ark::es2panda::ir namespace ark::es2panda::varbinder { @@ -50,6 +50,7 @@ namespace ark::es2panda::checker { class ETSChecker; class InterfaceType; class GlobalTypesHolder; +class SemanticAnalyzer; using StringLiteralPool = std::unordered_map; using NumberLiteralPool = std::unordered_map; @@ -63,15 +64,16 @@ using ArgRange = std::pair; class Checker { public: - explicit Checker(util::DiagnosticEngine &diagnosticEngine, ArenaAllocator *programAllocator = nullptr); + explicit Checker(ThreadSafeArenaAllocator *allocator, util::DiagnosticEngine &diagnosticEngine, + ThreadSafeArenaAllocator *programAllocator = nullptr); virtual ~Checker() = default; NO_COPY_SEMANTIC(Checker); NO_MOVE_SEMANTIC(Checker); - [[nodiscard]] ArenaAllocator *Allocator() noexcept + [[nodiscard]] ThreadSafeArenaAllocator *Allocator() noexcept { - return &allocator_; + return allocator_; } [[nodiscard]] varbinder::Scope *Scope() const noexcept @@ -114,7 +116,7 @@ public: return globalTypes_; } - void SetGlobalTypes(GlobalTypesHolder *globalTypes) noexcept + void SetGlobalTypesHolder(GlobalTypesHolder *globalTypes) { globalTypes_ = globalTypes; } @@ -232,18 +234,20 @@ public: virtual void CleanUp(); - [[nodiscard]] ArenaAllocator *ProgramAllocator() + [[nodiscard]] ThreadSafeArenaAllocator *ProgramAllocator() { - return programAllocator_ == nullptr ? &allocator_ : programAllocator_; + return programAllocator_ == nullptr ? allocator_ : programAllocator_; } + bool IsDeclForDynamicStaticInterop() const; + protected: parser::Program *Program() const; void SetProgram(parser::Program *program); private: - ArenaAllocator allocator_; - ArenaAllocator *programAllocator_ {nullptr}; + ThreadSafeArenaAllocator *allocator_; + ThreadSafeArenaAllocator *programAllocator_ {nullptr}; CheckerContext context_; GlobalTypesHolder *globalTypes_ {nullptr}; TypeRelation *relation_; @@ -253,11 +257,11 @@ private: varbinder::Scope *scope_ {}; util::DiagnosticEngine &diagnosticEngine_; - RelationHolder identicalResults_ {{}, RelationType::IDENTICAL}; - RelationHolder assignableResults_ {{}, RelationType::ASSIGNABLE}; - RelationHolder comparableResults_ {{}, RelationType::COMPARABLE}; - RelationHolder uncheckedCastableResults_ {{}, RelationType::UNCHECKED_CASTABLE}; - RelationHolder supertypeResults_ {{}, RelationType::SUPERTYPE}; + RelationHolder identicalResults_ {Allocator()}; + RelationHolder assignableResults_ {Allocator()}; + RelationHolder comparableResults_ {Allocator()}; + RelationHolder uncheckedCastableResults_ {Allocator()}; + RelationHolder supertypeResults_ {Allocator()}; std::unordered_map typeStack_; std::unordered_set namedTypeStack_; @@ -284,8 +288,9 @@ private: class TypeStackElement { public: - explicit TypeStackElement(Checker *checker, void *element, const std::optional &diag, - const lexer::SourcePosition &pos, bool isRecursive = false) + explicit TypeStackElement(Checker *checker, const void *element, + const std::optional &diag, const lexer::SourcePosition &pos, + bool isRecursive = false) : checker_(checker), element_(element), isRecursive_(isRecursive) { if (!checker->typeStack_.insert({element, nullptr}).second) { @@ -331,7 +336,7 @@ public: private: Checker *checker_; - void *element_; + const void *element_; bool hasErrorChecker_ {false}; bool isRecursive_; bool cleanup_ {true}; @@ -479,6 +484,42 @@ private: Type *type_ {}; }; +class SignatureMatchContext { +public: + explicit SignatureMatchContext(Checker *checker, util::DiagnosticType diagnosticKind, bool isLogError = true) + : diagnosticEngine_(checker->DiagnosticEngine()), + diagnosticCheckpoint_(), + diagnosticKind_(diagnosticKind), + isLogError_(isLogError) + { + diagnosticCheckpoint_ = diagnosticEngine_.Save(); + } + + bool ValidSignatureMatchStatus() + { + std::array diagnosticCheckpoint = diagnosticEngine_.Save(); + return diagnosticCheckpoint_[diagnosticKind_] == diagnosticCheckpoint[diagnosticKind_]; + } + + ~SignatureMatchContext() + { + if (isLogError_) { + return; + } + + diagnosticEngine_.Rollback(diagnosticCheckpoint_); + } + + NO_COPY_SEMANTIC(SignatureMatchContext); + NO_MOVE_SEMANTIC(SignatureMatchContext); + +private: + util::DiagnosticEngine &diagnosticEngine_; + std::array diagnosticCheckpoint_; + util::DiagnosticType diagnosticKind_; + bool isLogError_; +}; + } // namespace ark::es2panda::checker #endif /* CHECKER_H */ diff --git a/ets2panda/checker/checkerContext.cpp b/ets2panda/checker/checkerContext.cpp index b8da863c9b340d571fb4daa2bd1bab4c91eeea8f..fa10d82fac4f1bb494e247289620832e0fec818f 100644 --- a/ets2panda/checker/checkerContext.cpp +++ b/ets2panda/checker/checkerContext.cpp @@ -473,10 +473,6 @@ void CheckerContext::OnBreakStatement(ir::BreakStatement const *breakStatement) status_ |= CheckerStatus::MEET_BREAK; - if (smartCasts_.empty()) { - return; - } - SmartCastArray smartCasts {}; smartCasts.reserve(smartCasts_.size()); @@ -486,9 +482,7 @@ void CheckerContext::OnBreakStatement(ir::BreakStatement const *breakStatement) } } - if (!smartCasts.empty()) { - AddBreakSmartCasts(targetStatement, std::move(smartCasts)); - } + AddBreakSmartCasts(targetStatement, std::move(smartCasts)); ClearSmartCasts(); } @@ -500,7 +494,8 @@ void CheckerContext::AddBreakSmartCasts(ir::Statement const *targetStatement, Sm void CheckerContext::CombineBreakSmartCasts(ir::Statement const *targetStatement) { - ES2PANDA_ASSERT(smartCasts_.empty()); + ES2PANDA_ASSERT((targetStatement->IsSwitchStatement() && targetStatement->AsSwitchStatement()->Cases().empty()) || + smartCasts_.empty()); if (!breakSmartCasts_.empty()) { bool firstCase = true; diff --git a/ets2panda/checker/ets/aliveAnalyzer.cpp b/ets2panda/checker/ets/aliveAnalyzer.cpp index a41a6ac510d4a29fec567a1af729dedc8da6021b..e4edb04a7a1eadcccc752a94d25838480119604d 100644 --- a/ets2panda/checker/ets/aliveAnalyzer.cpp +++ b/ets2panda/checker/ets/aliveAnalyzer.cpp @@ -44,6 +44,7 @@ #include "ir/ets/etsNewClassInstanceExpression.h" #include "ir/ets/etsStructDeclaration.h" #include "ir/ts/tsInterfaceDeclaration.h" +#include "checker/ETSAnalyzerHelpers.h" #include "checker/types/globalTypesHolder.h" #include "varbinder/variable.h" #include "varbinder/declaration.h" @@ -257,9 +258,8 @@ void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef) } if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid) { - auto *fuction = methodDef->Function(); - ES2PANDA_ASSERT(fuction != nullptr); - if (!fuction->HasReturnStatement()) { + ES2PANDA_ASSERT(methodDef->Function() != nullptr); + if (!methodDef->Function()->HasReturnStatement()) { if (!util::Helpers::IsAsyncMethod(methodDef)) { checker_->LogError(diagnostic::MISSING_RETURN_STMT, {}, func->Start()); ClearPendingExits(); @@ -291,7 +291,7 @@ void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile) status_ = Or(status_, ResolveContinues(doWhile)); AnalyzeNode(doWhile->Test()); ES2PANDA_ASSERT(doWhile->Test()->TsType()); - const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr(); + const auto exprRes = IsConstantTestValue(doWhile->Test()); status_ = And(status_, static_cast(!std::get<0>(exprRes) || !std::get<1>(exprRes))); status_ = Or(status_, ResolveBreaks(doWhile)); } @@ -301,7 +301,7 @@ void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt) SetOldPendingExits(PendingExits()); AnalyzeNode(whileStmt->Test()); ES2PANDA_ASSERT(whileStmt->Test()->TsType()); - const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr(); + const auto exprRes = IsConstantTestValue(whileStmt->Test()); status_ = And(status_, static_cast(!std::get<0>(exprRes) || std::get<1>(exprRes))); AnalyzeStat(whileStmt->Body()); status_ = Or(status_, ResolveContinues(whileStmt)); @@ -320,7 +320,7 @@ void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt) AnalyzeNode(forStmt->Test()); ES2PANDA_ASSERT(forStmt->Test()->TsType()); condType = forStmt->Test()->TsType(); - std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr(); + std::tie(resolveType, res) = IsConstantTestValue(forStmt->Test()); status_ = From(!resolveType || res); } else { status_ = LivenessStatus::ALIVE; diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index 646b20787de9d39a414c9af02e51ce519f91da5f..0415b8a2ed995975f24f4a9b63ce0343b3302612 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -15,7 +15,8 @@ #include "arithmetic.h" -#include "checker/types/ts/nullType.h" +#include "checker/types/globalTypesHolder.h" +#include "checker/types/typeError.h" #include "lexer/token/token.h" namespace ark::es2panda::checker { @@ -28,7 +29,7 @@ struct BinaryArithmOperands { checker::Type *reducedR; }; -static inline BinaryArithmOperands GetBinaryOperands(ETSChecker *checker, ir::BinaryExpression *expr) +static BinaryArithmOperands GetBinaryOperands(ETSChecker *checker, ir::BinaryExpression *expr) { auto typeL = expr->Left()->Check(checker); auto typeR = expr->Right()->Check(checker); @@ -48,20 +49,6 @@ static void LogOperatorCannotBeApplied(ETSChecker *checker, BinaryArithmOperands LogOperatorCannotBeApplied(checker, ops.expr->OperatorType(), ops.typeL, ops.typeR, ops.expr->Start()); } -static inline void UnboxOperands(ETSChecker *checker, BinaryArithmOperands const &ops) -{ - auto const unbox = [checker](ir::Expression *expr, Type *type, Type *reducedType) { - if (type != reducedType) { - expr->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(reducedType)); - } - if (reducedType->IsETSEnumType()) { - expr->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - } - }; - unbox(ops.expr->Left(), ops.typeL, ops.reducedL); - unbox(ops.expr->Right(), ops.typeR, ops.reducedR); -} - static inline void RepairTypeErrorsInOperands(Type **left, Type **right) { if (IsTypeError(*left)) { @@ -87,11 +74,40 @@ static inline void RepairTypeErrorWithDefault(Type **type, Type *dflt) } } -static Type *EffectiveTypeOfNumericOp(ETSChecker *checker, Type *left, Type *right) +static bool CheckOpArgsTypeEq(ETSChecker *checker, Type *left, Type *right, Type *type) +{ + return ((left != nullptr) && (right != nullptr) && checker->IsTypeIdenticalTo(left, type) && + checker->IsTypeIdenticalTo(right, type)); +} + +static bool FindOpArgsType(ETSChecker *checker, Type *left, Type *right, Type *target) +{ + return (checker->Relation()->IsSupertypeOf(target, left) || checker->Relation()->IsSupertypeOf(target, right)); +} + +bool ETSChecker::CheckIfNumeric(Type *type) +{ + if (type == nullptr) { + return false; + } + if (type->IsETSPrimitiveType()) { + return type->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); + } + auto *unboxed = MaybeUnboxInRelation(type); + return (unboxed != nullptr) && unboxed->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); +} + +bool ETSChecker::CheckIfFloatingPoint(Type *type) { - ES2PANDA_ASSERT(left->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - right->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); + if (type == nullptr) { + return false; + } + auto *unboxed = MaybeUnboxInRelation(type); + return (unboxed != nullptr) && (unboxed->IsFloatType() || unboxed->IsDoubleType()); +} +static Type *EffectivePrimitiveTypeOfNumericOp(ETSChecker *checker, Type *left, Type *right) +{ if (left->IsDoubleType() || right->IsDoubleType()) { return checker->GlobalDoubleType(); } @@ -101,7 +117,10 @@ static Type *EffectiveTypeOfNumericOp(ETSChecker *checker, Type *left, Type *rig if (left->IsLongType() || right->IsLongType()) { return checker->GlobalLongType(); } - return checker->GlobalIntType(); + if (left->IsCharType() && right->IsCharType()) { + return checker->GlobalCharType(); + } + return checker->GlobalIntType(); // return int in case of primitive types by default } static Type *TryConvertToPrimitiveType(ETSChecker *checker, Type *type) @@ -111,99 +130,80 @@ static Type *TryConvertToPrimitiveType(ETSChecker *checker, Type *type) } if (type->IsETSIntEnumType()) { + // Pull out the type argument to BaseEnum + if (type->AsETSObjectType()->SuperType() != nullptr && + !type->AsETSObjectType()->SuperType()->TypeArguments().empty()) { + auto *baseEnumArg = type->AsETSObjectType()->SuperType()->TypeArguments()[0]; + return checker->MaybeUnboxInRelation(baseEnumArg); + } return checker->GlobalIntType(); } + if (type->IsETSStringEnumType()) { return checker->GlobalETSStringLiteralType(); } return checker->MaybeUnboxInRelation(type); } -static std::pair BinaryCoerceToPrimitives(ETSChecker *checker, Type *left, Type *right, - bool const promote) +static Type *EffectiveTypeOfNumericOp(ETSChecker *checker, Type *left, Type *right) { - Type *const unboxedL = TryConvertToPrimitiveType(checker, left); - Type *const unboxedR = TryConvertToPrimitiveType(checker, right); - if (unboxedL == nullptr || unboxedR == nullptr) { - return {nullptr, false}; - } + ES2PANDA_ASSERT(checker->CheckIfNumeric(left) && checker->CheckIfNumeric(right)); - bool const bothConst = unboxedL->IsConstantType() && unboxedR->IsConstantType(); - - if (!promote) { - return {unboxedR, bothConst}; + auto bothBoxed = left->IsETSUnboxableObject() && right->IsETSUnboxableObject(); + if (!bothBoxed) { + return EffectivePrimitiveTypeOfNumericOp(checker, left, right); } - if (unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - return {EffectiveTypeOfNumericOp(checker, unboxedL, unboxedR), bothConst}; + auto globalTypesHolder = checker->GetGlobalTypesHolder(); + if (FindOpArgsType(checker, left, right, globalTypesHolder->GlobalDoubleBuiltinType())) { + return globalTypesHolder->GlobalDoubleBuiltinType(); } - return {unboxedR, bothConst}; + if (FindOpArgsType(checker, left, right, globalTypesHolder->GlobalFloatBuiltinType())) { + return globalTypesHolder->GlobalFloatBuiltinType(); + } + if (FindOpArgsType(checker, left, right, globalTypesHolder->GlobalLongBuiltinType())) { + return globalTypesHolder->GlobalLongBuiltinType(); + } + return globalTypesHolder->GlobalIntegerBuiltinType(); // return Int for Byte, Short, Int } -Type *ETSChecker::NegateNumericType(Type *type, ir::Expression *node) +static Type *BinaryGetPromotedType(ETSChecker *checker, Type *left, Type *right, bool const promote) { - ES2PANDA_ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); - - TypeFlag typeKind = ETSType(type); - Type *result = nullptr; - - switch (typeKind) { - case TypeFlag::BYTE: { - result = CreateByteType(-(type->AsByteType()->GetValue())); - break; - } - case TypeFlag::CHAR: { - result = CreateCharType(-(type->AsCharType()->GetValue())); - break; - } - case TypeFlag::SHORT: { - result = CreateShortType(-(type->AsShortType()->GetValue())); - break; - } - case TypeFlag::INT: { - result = CreateIntType(-(type->AsIntType()->GetValue())); - break; - } - case TypeFlag::LONG: { - result = CreateLongType(-(type->AsLongType()->GetValue())); - break; - } - case TypeFlag::FLOAT: { - result = CreateFloatType(-(type->AsFloatType()->GetValue())); - break; - } - case TypeFlag::DOUBLE: { - result = CreateDoubleType(-(type->AsDoubleType()->GetValue())); - break; - } - default: { - ES2PANDA_UNREACHABLE(); - } + Type *const unboxedL = TryConvertToPrimitiveType(checker, left); + Type *const unboxedR = TryConvertToPrimitiveType(checker, right); + if (unboxedL == nullptr || unboxedR == nullptr) { + return nullptr; } - node->SetTsType(result); - return result; -} + Type *typeL = left; + Type *typeR = right; -Type *ETSChecker::HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) -{ - ES2PANDA_ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); + bool const bothBoxed = !typeL->IsETSPrimitiveType() && !typeR->IsETSPrimitiveType(); - if (left->IsDoubleType() || right->IsDoubleType()) { - return PerformRelationOperationOnTypes(left, right, operationType); + if (!promote) { + return typeR; } - if (left->IsFloatType() || right->IsFloatType()) { - return PerformRelationOperationOnTypes(left, right, operationType); + if (!bothBoxed) { + if (unboxedL->IsETSEnumType() || unboxedR->IsETSEnumType()) { + return nullptr; + } + if (!typeL->IsETSPrimitiveType()) { + typeL = checker->MaybeUnboxType(typeL); + } + if (!typeR->IsETSPrimitiveType()) { + typeR = checker->MaybeUnboxType(typeR); + } } - if (left->IsLongType() || right->IsLongType()) { - return PerformRelationOperationOnTypes(left, right, operationType); + if (checker->CheckIfNumeric(typeL) && checker->CheckIfNumeric(typeR)) { + return EffectiveTypeOfNumericOp(checker, typeL, typeR); + } + if (checker->CheckIfNumeric(typeR)) { + return typeR; } - return PerformRelationOperationOnTypes(left, right, operationType); + return typeL; } bool ETSChecker::CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::TokenType op) @@ -212,11 +212,26 @@ bool ETSChecker::CheckBinaryOperatorForBigInt(Type *left, Type *right, lexer::To return false; } - if (!left->IsETSBigIntType()) { - return false; + // Allow operations between BigInt and numeric types - number will be converted to BigInt + bool leftIsBigInt = left->IsETSBigIntType(); + bool rightIsBigInt = right->IsETSBigIntType(); + // Allow if either operand is BigInt. + // The non-BigInt operand will be converted to BigInt during lowering. + if ((leftIsBigInt && CheckIfNumeric(right)) || (rightIsBigInt && CheckIfNumeric(left))) { + switch (op) { + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: + case lexer::TokenType::PUNCTUATOR_LESS_THAN: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: + return true; + default: + break; + } } - if (!right->IsETSBigIntType()) { + if (!leftIsBigInt || !rightIsBigInt) { return false; } @@ -251,6 +266,53 @@ bool ETSChecker::CheckBinaryPlusMultDivOperandsForUnionType(const Type *leftType return true; } +void ETSChecker::SetGenerateValueOfFlags(std::tuple types, + std::tuple nodes) +{ + auto [leftType, rightType, _, __] = types; + auto [left, right] = nodes; + if (leftType->IsETSEnumType()) { + left->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); + } + if (rightType->IsETSEnumType()) { + right->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); + } +} + +static bool TypeIsAppropriateForArithmetic(const checker::Type *type, ETSChecker *checker) +{ + return type->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || + (type->IsETSObjectType() && + checker->Relation()->IsSupertypeOf(checker->GetGlobalTypesHolder()->GlobalNumericBuiltinType(), type)); +} + +static checker::Type *CheckBinaryOperatorForIntEnums(ETSChecker *checker, checker::Type *const leftType, + checker::Type *const rightType) +{ + if (!leftType->IsETSEnumType() && !rightType->IsETSEnumType()) { + return nullptr; + } + if (TypeIsAppropriateForArithmetic(leftType, checker) && TypeIsAppropriateForArithmetic(rightType, checker)) { + Type *leftNumeric; + if (leftType->IsETSIntEnumType()) { + leftNumeric = checker->MaybeBoxInRelation(TryConvertToPrimitiveType(checker, leftType)); + } else { + leftNumeric = leftType; + } + + Type *rightNumeric; + if (rightType->IsETSIntEnumType()) { + rightNumeric = checker->MaybeBoxInRelation(TryConvertToPrimitiveType(checker, rightType)); + } else { + rightNumeric = rightType; + } + + return EffectiveTypeOfNumericOp(checker, leftNumeric, rightNumeric); + } + + return nullptr; +} + checker::Type *ETSChecker::CheckBinaryOperatorMulDivMod( std::tuple op, bool isEqualOp, std::tuple types) @@ -261,22 +323,15 @@ checker::Type *ETSChecker::CheckBinaryOperatorMulDivMod( // Try to handle errors on a lower level RepairTypeErrorsInOperands(&leftType, &rightType); RepairTypeErrorsInOperands(&unboxedL, &unboxedR); - if (leftType->IsTypeError()) { // both are errors - return GlobalTypeError(); - } - - checker::Type *tsType {}; - auto const [promotedType, bothConst] = BinaryCoerceToPrimitives(this, unboxedL, unboxedR, !isEqualOp); - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); + ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); + auto const promotedType = BinaryGetPromotedType(this, leftType, rightType, !isEqualOp); if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) { return GlobalTypeError(); } - if (promotedType == nullptr || !unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - !unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - auto type = CheckBinaryOperatorForIntEnums(leftType, rightType); + if (promotedType == nullptr || !CheckIfNumeric(leftType) || !CheckIfNumeric(rightType)) { + auto type = CheckBinaryOperatorForIntEnums(this, leftType, rightType); if (type != nullptr) { return type; } @@ -284,66 +339,42 @@ checker::Type *ETSChecker::CheckBinaryOperatorMulDivMod( return GlobalTypeError(); } - if (bothConst) { - tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType); - } - - return (tsType != nullptr) ? tsType : promotedType; -} - -checker::Type *ETSChecker::CheckBinaryOperatorForIntEnums(const checker::Type *const leftType, - const checker::Type *const rightType) -{ - if (leftType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - rightType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) { - return GlobalIntType(); - } - if (leftType->IsFloatType() || rightType->IsFloatType()) { - return GlobalFloatType(); - } - if (leftType->IsDoubleType() || rightType->IsDoubleType()) { - return GlobalDoubleType(); - } - if (leftType->IsLongType() || rightType->IsLongType()) { - return GlobalLongType(); - } - return GlobalIntType(); - } - return nullptr; + return promotedType; } checker::Type *ETSChecker::CheckBinaryBitwiseOperatorForIntEnums(const checker::Type *const leftType, const checker::Type *const rightType) { - if (leftType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - rightType->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { + if (!leftType->IsETSEnumType() && !rightType->IsETSEnumType()) { + return nullptr; + } + if (TypeIsAppropriateForArithmetic(leftType, this) && TypeIsAppropriateForArithmetic(rightType, this)) { if (leftType->IsETSIntEnumType() && rightType->IsETSIntEnumType()) { - return GlobalIntType(); + return GlobalIntBuiltinType(); } if (leftType->IsFloatType() || rightType->IsFloatType()) { - return GlobalIntType(); + return GlobalIntBuiltinType(); } if (leftType->IsDoubleType() || rightType->IsDoubleType()) { - return GlobalLongType(); + return GlobalLongBuiltinType(); } if (leftType->IsLongType() || rightType->IsLongType()) { - return GlobalLongType(); + return GlobalLongBuiltinType(); } - return GlobalIntType(); + return GlobalIntBuiltinType(); } return nullptr; } -checker::Type *ETSChecker::CheckBinaryOperatorPlusForEnums(const checker::Type *const leftType, - const checker::Type *const rightType) +static checker::Type *CheckBinaryOperatorPlusForEnums(ETSChecker *checker, checker::Type *const leftType, + checker::Type *const rightType) { - if (auto numericType = CheckBinaryOperatorForIntEnums(leftType, rightType); numericType != nullptr) { + if (auto numericType = CheckBinaryOperatorForIntEnums(checker, leftType, rightType); numericType != nullptr) { return numericType; } if ((leftType->IsETSStringEnumType() && (rightType->IsETSStringType() || rightType->IsETSStringEnumType())) || (rightType->IsETSStringEnumType() && (leftType->IsETSStringType() || leftType->IsETSStringEnumType()))) { - return GlobalETSStringLiteralType(); + return checker->GlobalETSStringLiteralType(); } return nullptr; } @@ -358,9 +389,7 @@ checker::Type *ETSChecker::CheckBinaryOperatorPlus( // Try to handle errors on a lower level RepairTypeErrorsInOperands(&leftType, &rightType); RepairTypeErrorsInOperands(&unboxedL, &unboxedR); - if (leftType->IsTypeError()) { // both are errors - return GlobalTypeError(); - } + ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); if (leftType->IsETSStringType() || rightType->IsETSStringType()) { if (operationType == lexer::TokenType::PUNCTUATOR_MINUS || @@ -375,29 +404,19 @@ checker::Type *ETSChecker::CheckBinaryOperatorPlus( if (!CheckBinaryPlusMultDivOperandsForUnionType(leftType, rightType, left, right)) { return GlobalTypeError(); } - - auto const [promotedType, bothConst] = BinaryCoerceToPrimitives(this, unboxedL, unboxedR, !isEqualOp); - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - - if (promotedType == nullptr || !unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - !unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - auto type = CheckBinaryOperatorPlusForEnums(leftType, rightType); + auto const promotedType = BinaryGetPromotedType(this, leftType, rightType, !isEqualOp); + if (promotedType == nullptr || !CheckIfNumeric(rightType) || !CheckIfNumeric(leftType)) { + auto type = CheckBinaryOperatorPlusForEnums(this, leftType, rightType); if (type != nullptr) { return type; } LogError(diagnostic::BINOP_NONARITHMETIC_TYPE, {}, pos); - return GlobalTypeError(); - } - - if (bothConst) { - return HandleArithmeticOperationOnTypes(leftType, rightType, operationType); } return promotedType; } -static checker::Type *GetBitwiseCompatibleType(ETSChecker *checker, Type *const type) +[[maybe_unused]] static checker::Type *GetBitwiseCompatibleType(ETSChecker *checker, Type *const type) { switch (checker->ETSType(type)) { case TypeFlag::BYTE: { @@ -431,8 +450,8 @@ checker::Type *ETSChecker::CheckBinaryOperatorShift( auto [left, right, operationType, pos] = op; auto [leftType, rightType, unboxedL, unboxedR] = types; - RepairTypeErrorWithDefault(&leftType, GlobalIntType()); - RepairTypeErrorWithDefault(&rightType, GlobalIntType()); + RepairTypeErrorWithDefault(&leftType, GlobalIntBuiltinType()); + RepairTypeErrorWithDefault(&rightType, GlobalIntBuiltinType()); RepairTypeErrorWithDefault(&unboxedL, GlobalIntType()); RepairTypeErrorWithDefault(&unboxedR, GlobalIntType()); @@ -441,15 +460,10 @@ checker::Type *ETSChecker::CheckBinaryOperatorShift( return GlobalTypeError(); } - auto promotedLeftType = ApplyUnaryOperatorPromotion(unboxedL, false, !isEqualOp); - auto promotedRightType = ApplyUnaryOperatorPromotion(unboxedR, false, !isEqualOp); - - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - - if (promotedLeftType == nullptr || !promotedLeftType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - promotedRightType == nullptr || - !promotedRightType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { + auto promotedLeftType = GetUnaryOperatorPromotedType(leftType, !isEqualOp); + auto promotedRightType = GetUnaryOperatorPromotedType(rightType, !isEqualOp); + if (promotedLeftType == nullptr || promotedRightType == nullptr || !CheckIfNumeric(promotedLeftType) || + !CheckIfNumeric(promotedRightType)) { auto type = CheckBinaryBitwiseOperatorForIntEnums(leftType, rightType); if (type != nullptr) { return type; @@ -458,10 +472,22 @@ checker::Type *ETSChecker::CheckBinaryOperatorShift( return GlobalTypeError(); } - if (promotedLeftType->HasTypeFlag(TypeFlag::CONSTANT) && promotedRightType->HasTypeFlag(TypeFlag::CONSTANT)) { - return HandleBitwiseOperationOnTypes(promotedLeftType, promotedRightType, operationType); + auto isPrim = promotedLeftType->IsETSPrimitiveType(); + auto unboxedProm = MaybeUnboxType(promotedLeftType); + if (unboxedProm->IsFloatType() || unboxedProm->IsIntType()) { + return isPrim ? GlobalIntType() : GetGlobalTypesHolder()->GlobalIntegerBuiltinType(); + } + + if (unboxedProm->IsLongType() || unboxedProm->IsDoubleType()) { + return isPrim ? GlobalLongType() : GetGlobalTypesHolder()->GlobalLongBuiltinType(); + } + + if (unboxedProm->IsByteType() || unboxedProm->IsShortType() || unboxedProm->IsCharType()) { + return promotedLeftType; } - return GetBitwiseCompatibleType(this, promotedLeftType); + + ES2PANDA_UNREACHABLE(); + return nullptr; } checker::Type *ETSChecker::CheckBinaryOperatorBitwise( @@ -473,28 +499,19 @@ checker::Type *ETSChecker::CheckBinaryOperatorBitwise( RepairTypeErrorsInOperands(&leftType, &rightType); RepairTypeErrorsInOperands(&unboxedL, &unboxedR); - if (leftType->IsTypeError()) { // both are errors - return GlobalTypeError(); - } + ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) { LogError(diagnostic::BINOP_UNION, {}, pos); return GlobalTypeError(); } - if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr && - unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - return HandleBooleanLogicalOperators(unboxedL, unboxedR, operationType); + if (CheckOpArgsTypeEq(this, unboxedL, unboxedR, GlobalETSBooleanType())) { + return GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType(); } - auto const [promotedType, bothConst] = BinaryCoerceToPrimitives(this, unboxedL, unboxedR, !isEqualOp); - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - - if (promotedType == nullptr || !unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - !unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { + auto const promotedType = BinaryGetPromotedType(this, leftType, rightType, !isEqualOp); + if (promotedType == nullptr || !CheckIfNumeric(rightType) || !CheckIfNumeric(leftType)) { auto type = CheckBinaryBitwiseOperatorForIntEnums(leftType, rightType); if (type != nullptr) { return type; @@ -502,65 +519,77 @@ checker::Type *ETSChecker::CheckBinaryOperatorBitwise( LogError(diagnostic::OP_NONNUMERIC, {}, pos); return GlobalTypeError(); } + SetGenerateValueOfFlags(types, {left, right}); + + auto isPrim = promotedType->IsETSPrimitiveType(); + auto unboxedProm = MaybeUnboxType(promotedType); + if (unboxedProm->IsFloatType() || unboxedProm->IsIntType()) { + return isPrim ? GlobalIntType() : GetGlobalTypesHolder()->GlobalIntegerBuiltinType(); + } - if (bothConst) { - return HandleBitwiseOperationOnTypes(leftType, rightType, operationType); + if (unboxedProm->IsLongType() || unboxedProm->IsDoubleType()) { + return isPrim ? GlobalLongType() : GetGlobalTypesHolder()->GlobalLongBuiltinType(); } - return SelectGlobalIntegerTypeForNumeric(promotedType); + if (unboxedProm->IsByteType() || unboxedProm->IsShortType() || unboxedProm->IsCharType()) { + return promotedType; + } + return nullptr; } checker::Type *ETSChecker::CheckBinaryOperatorLogical(ir::Expression *left, ir::Expression *right, - ir::BinaryExpression *expr, checker::Type *leftType, - checker::Type *rightType, Type *unboxedL, Type *unboxedR) + checker::Type *leftType, checker::Type *rightType, Type *unboxedL, + Type *unboxedR) { RepairTypeErrorsInOperands(&leftType, &rightType); RepairTypeErrorsInOperands(&unboxedL, &unboxedR); - if (leftType->IsTypeError()) { // both are errors - return GlobalTypeError(); - } + ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); + // Don't do any boxing for primitive type when another operand is Enum. Enum will become primitive type later. if (leftType->IsETSEnumType() || rightType->IsETSEnumType()) { left->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); right->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); return CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)}); } - if (right->IsNumberLiteral() && AdjustNumberLiteralType(right->AsNumberLiteral(), rightType, leftType)) { + + if (right->IsNumberLiteral() && !left->IsNumberLiteral() && leftType->IsBuiltinNumeric()) { return leftType; } - if (left->IsNumberLiteral() && AdjustNumberLiteralType(left->AsNumberLiteral(), leftType, rightType)) { + if (left->IsNumberLiteral() && !right->IsNumberLiteral() && rightType->IsBuiltinNumeric()) { return rightType; } - if (HandleLogicalPotentialResult(left, right, expr, leftType)) { - ES2PANDA_ASSERT(expr->Result()->TsType() != nullptr); - return expr->Result()->TsType(); - } - if (IsTypeIdenticalTo(leftType, rightType)) { return GetNonConstantType(leftType); } - if (IsTypeIdenticalTo(unboxedL, unboxedR)) { - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - return GetNonConstantType(unboxedL); - } + return CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)}); +} - if (unboxedL != nullptr && unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && unboxedR != nullptr && - unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); - return EffectiveTypeOfNumericOp(this, unboxedL, unboxedR); +static bool ContainsNumbers(ETSChecker *checker, Type *tp) +{ + auto isSubtypeOfNumeric = [checker](Type *tp2) { + return checker->Relation()->IsSupertypeOf(checker->GetGlobalTypesHolder()->GlobalNumericBuiltinType(), tp2); + }; + if (isSubtypeOfNumeric(tp)) { + return true; + } + if (tp->IsETSUnionType()) { + for (auto *constituent : tp->AsETSUnionType()->ConstituentTypes()) { + if (isSubtypeOfNumeric(constituent)) { + return true; + } + } } - return CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)}); + return false; } +// CC-OFFNXT(huge_cyclomatic_complexity, huge_cca_cyclomatic_complexity[C++]) solid logic // NOTE: code inside this function follows the broken logic bool ETSChecker::CheckValidEqualReferenceType(checker::Type *const leftType, checker::Type *const rightType) { - auto isRelaxedType {[](checker::Type *const type) -> bool { + auto isRelaxedType {[&](checker::Type *const type) -> bool { return (type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType()) || type->IsETSAnyType() || type->IsETSNullType() || type->IsETSUndefinedType(); }}; @@ -570,6 +599,18 @@ bool ETSChecker::CheckValidEqualReferenceType(checker::Type *const leftType, che return true; } + // Any two types that can be numeric are comparable + if (ContainsNumbers(this, leftType) && ContainsNumbers(this, rightType)) { + return true; + } + + // Boolean and any type that can be numeric or char are not comparable + if ((FindOpArgsType(this, leftType, rightType, GetGlobalTypesHolder()->GlobalNumericBuiltinType()) || + FindOpArgsType(this, leftType, rightType, GetGlobalTypesHolder()->GlobalCharBuiltinType())) && + FindOpArgsType(this, leftType, rightType, GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType())) { + return false; + } + // NOTE (mxlgv): Skip for unions. Required implementation of the specification section: // 7.25.6 Reference Equality Based on Actual Type (Union Equality Operators) if (leftType->IsETSUnionType()) { @@ -596,6 +637,11 @@ bool ETSChecker::CheckValidEqualReferenceType(checker::Type *const leftType, che } } + if (FindOpArgsType(this, leftType, rightType, GetGlobalTypesHolder()->GlobalNumericBuiltinType()) && + (leftType->IsETSEnumType() || rightType->IsETSEnumType())) { + return true; + } + // 7.24.5 Enumeration Relational Operators return leftType->IsETSEnumType() == rightType->IsETSEnumType(); } @@ -606,12 +652,9 @@ std::tuple ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expres checker::Type *leftType, checker::Type *rightType) { RepairTypeErrorsInOperands(&leftType, &rightType); - if (leftType->IsTypeError()) { // both are errors - // We still know that operation result should be boolean, so recover. - return {GlobalETSBooleanType(), GlobalETSObjectType()}; - } + // We still know that operation result should be boolean, so recover. + ERROR_TYPE_CHECK(this, leftType, return std::make_tuple(GlobalETSBooleanBuiltinType(), GlobalETSObjectType())); - checker::Type *tsType {}; if (!IsReferenceType(leftType) || !IsReferenceType(rightType)) { LogError(diagnostic::BINOP_NOT_REFERENCE, {}, pos); return {GlobalETSBooleanType(), GlobalETSObjectType()}; @@ -620,43 +663,11 @@ std::tuple ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expres Relation()->SetNode(left); if (!CheckValidEqualReferenceType(leftType, rightType)) { LogOperatorCannotBeApplied(this, operationType, leftType, rightType, pos); - return {GlobalETSBooleanType(), GlobalETSObjectType()}; - } - - if (!Relation()->IsCastableTo(leftType, rightType) && !Relation()->IsCastableTo(rightType, leftType)) { + } else if (!Relation()->IsCastableTo(leftType, rightType) && !Relation()->IsCastableTo(rightType, leftType)) { LogOperatorCannotBeApplied(this, operationType, leftType, rightType, pos); - return {GlobalETSBooleanType(), GlobalETSObjectType()}; - } - - tsType = GlobalETSBooleanType(); - if (rightType->IsETSDynamicType() && leftType->IsETSDynamicType()) { - return {tsType, GlobalBuiltinJSValueType()}; } - return {tsType, GlobalETSObjectType()}; -} - -static Type *CheckOperatorEqualDynamic(ETSChecker *checker, BinaryArithmOperands const &ops) -{ - auto left = ops.expr->Left(); - auto right = ops.expr->Right(); - // canonicalize - auto *const dynExp = left->TsType()->IsETSDynamicType() ? left : right; - auto *const otherExp = dynExp == left ? right : left; - - if (otherExp->TsType()->IsETSDynamicType()) { - return checker->GlobalBuiltinJSValueType(); - } - if (dynExp->TsType()->AsETSDynamicType()->IsConvertible(otherExp->TsType())) { - // NOTE: vpukhov. boxing flags are not set in dynamic values - return otherExp->TsType(); - } - if (otherExp->TsType()->IsETSReferenceType()) { - // have to prevent casting dyn_exp via ApplyCast without nullish flag - return checker->GlobalETSAnyType(); - } - checker->LogError(diagnostic::BINOP_DYN_UNIMPLEMENTED, {}, ops.expr->Start()); - return checker->GlobalETSAnyType(); + return {GlobalETSBooleanType(), GlobalETSObjectType()}; } static Type *HandelReferenceBinaryEquality(ETSChecker *checker, BinaryArithmOperands const &ops) @@ -667,13 +678,6 @@ static Type *HandelReferenceBinaryEquality(ETSChecker *checker, BinaryArithmOper return checker->CreateETSUnionType({typeL, typeR}); } - if (typeL->IsETSEnumType() && typeR->IsETSEnumType()) { - if (checker->Relation()->IsIdenticalTo(typeL, typeR)) { - return typeL; - } - return nullptr; - } - if (typeL->IsETSReferenceType() && typeR->IsETSReferenceType()) { checker->Relation()->SetNode(expr->Left()); if (!checker->CheckValidEqualReferenceType(typeL, typeR)) { @@ -699,24 +703,17 @@ static Type *CheckBinaryOperatorEqual(ETSChecker *checker, BinaryArithmOperands { [[maybe_unused]] auto const [expr, typeL, typeR, reducedL, reducedR] = ops; - expr->Left()->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - expr->Right()->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - if (typeL->IsTypeError()) { // both are errors - return checker->GlobalTypeError(); - } - - if (typeL->IsETSDynamicType() || typeR->IsETSDynamicType()) { - return CheckOperatorEqualDynamic(checker, ops); - } + ERROR_TYPE_CHECK(checker, typeL, return checker->GlobalTypeError()); if (reducedL->IsETSBooleanType() && reducedR->IsETSBooleanType()) { if (reducedL->IsConstantType() && reducedR->IsConstantType()) { - bool res = reducedL->AsETSBooleanType()->GetValue() == reducedR->AsETSBooleanType()->GetValue(); - res = ((expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EQUAL) == res); - return checker->CreateETSBooleanType(res); + return checker->GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType(); + } + if (checker->CheckIfNumeric(typeL) && checker->CheckIfNumeric(typeR) && typeL->IsETSUnboxableObject() && + typeR->IsETSUnboxableObject()) { + return typeL; } - UnboxOperands(checker, ops); - return checker->GlobalETSBooleanType(); + return reducedL; } return HandelReferenceBinaryEquality(checker, ops); @@ -752,9 +749,7 @@ std::tuple ETSChecker::CheckBinaryOperatorLessGreater(ir::Expres { RepairTypeErrorsInOperands(&leftType, &rightType); RepairTypeErrorsInOperands(&unboxedL, &unboxedR); - if (leftType->IsTypeError()) { // both are errors - return {GlobalETSBooleanType(), GlobalTypeError()}; - } + ERROR_TYPE_CHECK(this, leftType, return std::make_tuple(GlobalETSBooleanBuiltinType(), GlobalTypeError())); if ((leftType->IsETSUnionType() || rightType->IsETSUnionType()) && operationType != lexer::TokenType::PUNCTUATOR_EQUAL && @@ -762,98 +757,164 @@ std::tuple ETSChecker::CheckBinaryOperatorLessGreater(ir::Expres operationType != lexer::TokenType::PUNCTUATOR_STRICT_EQUAL && operationType != lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL) { LogError(diagnostic::BINOP_UNION, {}, pos); - return {GlobalETSBooleanType(), leftType}; + return {GlobalETSBooleanBuiltinType(), leftType}; } - auto *const promotedType = std::get<0>(BinaryCoerceToPrimitives(this, unboxedL, unboxedR, !isEqualOp)); - FlagExpressionWithUnboxing(leftType, unboxedL, left); - FlagExpressionWithUnboxing(rightType, unboxedR, right); + auto const promotedType = BinaryGetPromotedType(this, leftType, rightType, !isEqualOp); if (leftType->IsETSUnionType() || rightType->IsETSUnionType()) { - return {GlobalETSBooleanType(), CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)})}; + return {GlobalETSBooleanBuiltinType(), + CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)})}; } - if (promotedType != nullptr && (unboxedL->IsETSBooleanType() != unboxedR->IsETSBooleanType())) { + if (promotedType != nullptr && unboxedL != nullptr && unboxedR != nullptr && + unboxedL->IsETSBooleanType() != unboxedR->IsETSBooleanType()) { LogOperatorCannotBeApplied(this, operationType, leftType, rightType, pos); - return {GlobalETSBooleanType(), leftType}; + return {GlobalETSBooleanBuiltinType(), leftType}; } if (promotedType == nullptr) { if (!NonNumericTypesAreAppropriateForComparison(this, leftType, rightType)) { LogError(diagnostic::BINOP_INCOMPARABLE, {}, pos); } - return {GlobalETSBooleanType(), GlobalETSBooleanType()}; + return {GlobalETSBooleanBuiltinType(), GlobalETSBooleanBuiltinType()}; } - return {GlobalETSBooleanType(), promotedType}; + return {GlobalETSBooleanBuiltinType(), promotedType}; } std::tuple ETSChecker::CheckBinaryOperatorInstanceOf(lexer::SourcePosition pos, checker::Type *leftType, checker::Type *rightType) { RepairTypeErrorsInOperands(&leftType, &rightType); - if (leftType->IsTypeError()) { // both are errors - return {GlobalETSBooleanType(), GlobalTypeError()}; - } + ERROR_TYPE_CHECK(this, leftType, return std::make_tuple(GlobalETSBooleanBuiltinType(), GlobalTypeError())); - if (!IsReferenceType(leftType) || !IsReferenceType(rightType)) { + if (leftType->IsETSPrimitiveType() || rightType->IsETSPrimitiveType()) { LogError(diagnostic::BINOP_NOT_SAME, {}, pos); - return {GlobalETSBooleanType(), leftType}; - } - - if (rightType->IsETSDynamicType() && !rightType->AsETSDynamicType()->HasDecl()) { - LogError(diagnostic::INSTANCEOF_NOT_TYPE, {}, pos); - return {GlobalETSBooleanType(), leftType}; + return {GlobalETSBooleanBuiltinType(), leftType}; } - checker::Type *opType = rightType->IsETSDynamicType() ? GlobalBuiltinJSValueType() : GlobalETSObjectType(); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ComputeApparentType(rightType); + checker::Type *opType = GlobalETSObjectType(); RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT); - return {GlobalETSBooleanType(), opType}; + return {GlobalETSBooleanBuiltinType(), opType}; } -bool ETSChecker::AdjustNumberLiteralType(ir::NumberLiteral *const literal, Type *literalType, Type *const otherType) +template +static void ConvertNumberLiteralTo(ir::NumberLiteral *lit, Type *toType) { - if (otherType->IsETSObjectType() && !otherType->IsETSEnumType()) { - auto *const objectType = otherType->AsETSObjectType(); - if (objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) && !objectType->IsETSStringType()) { - literal->RemoveBoxingUnboxingFlags(GetBoxingFlag(literalType)); - literalType = MaybeUnboxInRelation(objectType); - if (literalType == nullptr) { - return false; - } - literal->SetTsType(literalType); - literal->AddBoxingUnboxingFlags(GetBoxingFlag(literalType)); - return true; + auto &number = lit->Number(); + number.SetValue(number.GetValueAndCastTo()); + toType->AddTypeFlag(TypeFlag::CONSTANT); + lit->SetTsType(toType); +} + +template +static bool CheckNumberLiteralValue(ETSChecker *checker, ir::NumberLiteral const *const lit) +{ + auto const maxTo = static_cast(std::numeric_limits::max()); + auto const minTo = static_cast(std::numeric_limits::min()); + auto const val = lit->Number().GetValue(); + if (val < minTo || val > maxTo) { + checker->LogError(diagnostic::CONSTANT_VALUE_OUT_OF_RANGE, {}, lit->Start()); + return false; + } + return true; +} + +// Just to reduce the size of 'ConvertNumberLiteral' +static void ConvertIntegerNumberLiteral(ETSChecker *checker, ir::NumberLiteral *lit, ETSObjectType *fromType, + ETSObjectType *toType) +{ + if (toType->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG)) { + ConvertNumberLiteralTo(lit, checker->GlobalLongBuiltinType()->Clone(checker)); + } else if (toType->HasObjectFlag(ETSObjectFlags::BUILTIN_INT)) { + if (!fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG) || + CheckNumberLiteralValue(checker, lit)) { + ConvertNumberLiteralTo(lit, checker->GlobalIntBuiltinType()->Clone(checker)); + } + } else if (toType->HasObjectFlag(ETSObjectFlags::BUILTIN_SHORT)) { + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG) && + !CheckNumberLiteralValue(checker, lit)) { + return; + } + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_INT) && + !CheckNumberLiteralValue(checker, lit)) { + return; + } + ConvertNumberLiteralTo(lit, checker->GlobalShortBuiltinType()->Clone(checker)); + } else if (toType->HasObjectFlag(ETSObjectFlags::BUILTIN_BYTE)) { + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG) && + !CheckNumberLiteralValue(checker, lit)) { + return; + } + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_INT) && + !CheckNumberLiteralValue(checker, lit)) { + return; + } + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_SHORT) && + !CheckNumberLiteralValue(checker, lit)) { + return; + } + ConvertNumberLiteralTo(lit, checker->GlobalByteBuiltinType()->Clone(checker)); + } +} + +static void ConvertNumberLiteral(ETSChecker *checker, ir::NumberLiteral *lit, ETSObjectType *toType) +{ + ES2PANDA_ASSERT(toType->IsBuiltinNumeric() && lit->TsType()->IsBuiltinNumeric()); + + if (auto *fromType = lit->TsType()->AsETSObjectType(); !checker->Relation()->IsIdenticalTo(fromType, toType)) { + switch (static_cast(toType->ObjectFlags() & ETSObjectFlags::BUILTIN_NUMERIC)) { + case ETSObjectFlags::BUILTIN_DOUBLE: + ConvertNumberLiteralTo(lit, checker->GlobalDoubleBuiltinType()->Clone(checker)); + break; + + case ETSObjectFlags::BUILTIN_FLOAT: + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE)) { + checker->LogError(diagnostic::INVALID_ASSIGNMNENT, {fromType, toType}, lit->Start()); + } else { + ConvertNumberLiteralTo(lit, checker->GlobalFloatBuiltinType()->Clone(checker)); + } + break; + + case ETSObjectFlags::BUILTIN_LONG: + case ETSObjectFlags::BUILTIN_INT: + case ETSObjectFlags::BUILTIN_SHORT: + case ETSObjectFlags::BUILTIN_BYTE: + if (fromType->HasObjectFlag(ETSObjectFlags::BUILTIN_FLOATING_POINT)) { + checker->LogError(diagnostic::INVALID_ASSIGNMNENT, {fromType, toType}, lit->Start()); + } else { + ConvertIntegerNumberLiteral(checker, lit, fromType, toType); + } + break; + + default: + ES2PANDA_UNREACHABLE(); } } - return false; } Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir::Expression *right, lexer::SourcePosition pos) { auto *leftType = left->TsType(); - if (!IsReferenceType(leftType)) { - LogError(diagnostic::COALESCE_NOT_REF, {}, pos); - return leftType; - } leftType = GetNonNullishType(leftType); - ES2PANDA_ASSERT(leftType != nullptr); - if (leftType->IsTypeError()) { - ES2PANDA_ASSERT(IsAnyError()); - return GlobalTypeError(); + + ERROR_TYPE_CHECK(this, leftType, return GlobalTypeError()); + + if (leftType->IsETSPrimitiveType()) { + LogError(diagnostic::COALESCE_NOT_REF, {}, pos); } - auto *rightType = MaybeBoxExpression(right); + auto *rightType = MaybeBoxType(right->TsType()); if (IsTypeIdenticalTo(leftType, rightType)) { return leftType; } // If possible and required update number literal type to the proper value (identical to left-side type) - if (right->IsNumberLiteral() && AdjustNumberLiteralType(right->AsNumberLiteral(), rightType, leftType)) { + if (right->IsNumberLiteral() && leftType->IsBuiltinNumeric()) { + ConvertNumberLiteral(this, right->AsNumberLiteral(), leftType->AsETSObjectType()); return leftType; } @@ -913,6 +974,7 @@ struct TypeParams { Type *unboxedR; }; +// CC-OFFNXT(G.FUN.01, huge_method) solid logic static std::tuple CheckBinaryOperatorHelper(ETSChecker *checker, const BinaryOperatorParams &binaryParams, const TypeParams &typeParams) @@ -923,22 +985,23 @@ static std::tuple CheckBinaryOperatorHelper(ETSChecker *checker, checker::Type *const leftType = typeParams.leftType; checker::Type *const rightType = typeParams.rightType; checker::Type *tsType {}; + BinaryArithmOperands ops = GetBinaryOperands(checker, binaryParams.expr->AsBinaryExpression()); BinaryArithmOperands opsRepaired = RepairTypeErrorsInOperands(ops); switch (binaryParams.operationType) { case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { - tsType = checker->CheckBinaryOperatorLogical(left, right, binaryParams.expr->AsBinaryExpression(), leftType, - rightType, typeParams.unboxedL, typeParams.unboxedR); + tsType = checker->CheckBinaryOperatorLogical(left, right, leftType, rightType, typeParams.unboxedL, + typeParams.unboxedR); break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: case lexer::TokenType::PUNCTUATOR_EQUAL: case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { - if (Type *res = CheckBinaryOperatorEqual(checker, opsRepaired); res != nullptr) { - return {checker->GlobalETSBooleanType(), res}; + if (auto res = CheckBinaryOperatorEqual(checker, opsRepaired); res != nullptr) { + return {checker->GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType(), res}; } [[fallthrough]]; } @@ -966,123 +1029,70 @@ static std::tuple CheckBinaryOperatorHelper(ETSChecker *checker, return {tsType, tsType}; } -namespace { -bool IsStringEnum(const ir::Expression *expr) +static void TryAddValueOfFlagToStringEnumOperand(ir::Expression *op, const ir::Expression *otherOp) { - if (expr == nullptr) { - return false; - } - - auto type = expr->TsType(); - if (type == nullptr) { - return false; + auto type = op->TsType(); + auto otherType = otherOp->TsType(); + if (type->IsETSStringEnumType() && (otherType->IsETSStringType() || otherType->IsETSStringEnumType())) { + op->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); } - - return type->IsETSStringEnumType(); } -bool IsIntEnum(const ir::Expression *expr) +static void TryAddValueOfFlagToIntEnumOperand(ir::Expression *op, const ir::Expression *otherOp) { - if (expr == nullptr) { - return false; + auto type = op->TsType(); + auto otherType = otherOp->TsType(); + if (type->IsETSIntEnumType() && + ((otherType->IsETSObjectType() && otherType->AsETSObjectType()->IsBoxedPrimitive()) || + otherType->IsETSIntEnumType())) { + op->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); } - - auto type = expr->TsType(); - if (type == nullptr) { - return false; - } - - return type->IsETSIntEnumType(); } -bool CheckNumericOperatorContext(ir::Expression *expression, lexer::TokenType op) +static void CheckEnumInOperatorContext(ir::Expression *expression, lexer::TokenType opType, ir::Expression *left, + ir::Expression *right, ETSChecker *checker) { - const bool isMultiplicative = op == lexer::TokenType::PUNCTUATOR_MULTIPLY || - op == lexer::TokenType::PUNCTUATOR_DIVIDE || op == lexer::TokenType::PUNCTUATOR_MOD; - const bool isAdditive = op == lexer::TokenType::PUNCTUATOR_PLUS || op == lexer::TokenType::PUNCTUATOR_MINUS; - const bool isShift = op == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT || - op == lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT || - op == lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT; - const bool isRelational = - op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL || - op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL; - const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL; - const bool isBitwise = op == lexer::TokenType::PUNCTUATOR_BITWISE_AND || - op == lexer::TokenType::PUNCTUATOR_BITWISE_OR || - op == lexer::TokenType::PUNCTUATOR_BITWISE_XOR; - const bool isConditionalAndOr = - op == lexer::TokenType::PUNCTUATOR_LOGICAL_AND || op == lexer::TokenType::PUNCTUATOR_LOGICAL_OR; - - if (IsIntEnum(expression)) { - if (isMultiplicative || isAdditive || isShift || isRelational || isEquality || isBitwise || - isConditionalAndOr) { - expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - } - return true; - } - return false; -} + auto [lType, rType] = std::tuple {left->TsType(), right->TsType()}; -void CheckStringOperatorContext(ir::Expression *expression, checker::Type *otherType, lexer::TokenType op) -{ - const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL; - const bool isRelational = - op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL || - op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL; - if (IsStringEnum(expression) && (otherType->IsETSStringType() || otherType->IsETSStringEnumType())) { - if (op == lexer::TokenType::PUNCTUATOR_PLUS || isRelational || isEquality) { - expression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); + switch (opType) { + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_LESS_THAN: + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + if (lType->IsETSEnumType() && rType->IsETSEnumType() && !checker->Relation()->IsIdenticalTo(lType, rType)) { + checker->LogError(diagnostic::BINOP_INCOMPARABLE, {}, expression->Start()); + return; + } + [[fallthrough]]; } - } -} - -bool CheckRelationalOperatorsBetweenEnums(ir::Expression *left, ir::Expression *right, lexer::TokenType op) -{ - const bool isRelational = - op == lexer::TokenType::PUNCTUATOR_GREATER_THAN || op == lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL || - op == lexer::TokenType::PUNCTUATOR_LESS_THAN || op == lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL; - const bool isEquality = op == lexer::TokenType::PUNCTUATOR_EQUAL || op == lexer::TokenType::PUNCTUATOR_NOT_EQUAL; - - if (((IsStringEnum(left) && IsStringEnum(right)) || - (IsIntEnum(left) && IsIntEnum(right)))) { // NOTE(psiket) In case of int enums it has been already checked in - // the CheckNumericOperatorContext function - if (isRelational || isEquality) { - left->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - right->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - return true; + case lexer::TokenType::PUNCTUATOR_PLUS: { + TryAddValueOfFlagToStringEnumOperand(left, right); + TryAddValueOfFlagToStringEnumOperand(right, left); + [[fallthrough]]; } - } - return false; -} - -void CheckNeedToGenerateGetValueForBinaryExpression(ir::Expression *expression) -{ - if (!expression->IsBinaryExpression()) { - return; - } - - auto binaryExpression = expression->AsBinaryExpression(); - auto op = binaryExpression->OperatorType(); - auto leftExp = binaryExpression->Left(); - auto rightExp = binaryExpression->Right(); - - // Numeric Operator Context - auto leftIsIntEnum = CheckNumericOperatorContext(leftExp, op); - auto rightIsIntEnum = CheckNumericOperatorContext(rightExp, op); - if (leftIsIntEnum || rightIsIntEnum) { - return; - } - - // String Operator Context - CheckStringOperatorContext(leftExp, rightExp->TsType(), op); - CheckStringOperatorContext(rightExp, leftExp->TsType(), op); - - // Relational operators if both are enumeration Types - if (CheckRelationalOperatorsBetweenEnums(leftExp, rightExp, op)) { - return; + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + TryAddValueOfFlagToIntEnumOperand(left, right); + TryAddValueOfFlagToIntEnumOperand(right, left); + break; + } + default: + // NOTE(dkofanov): What about the '+=' operation? + break; } } -} // namespace std::tuple ETSChecker::CheckArithmeticOperations( ir::Expression *expr, std::tuple op, @@ -1098,16 +1108,28 @@ std::tuple ETSChecker::CheckArithmeticOperations( if (rightType->IsETSUnionType()) { rightType = GetNonConstantType(rightType); } + CheckEnumInOperatorContext(expr, operationType, left, right, this); auto checkMap = GetCheckMap(); if (checkMap.find(operationType) != checkMap.end()) { auto check = checkMap[operationType]; auto tsType = check(this, std::make_tuple(left, right, operationType, pos), isEqualOp, std::make_tuple(leftType, rightType, unboxedL, unboxedR)); + if (tsType == nullptr) { + return {leftType, rightType}; + } + if (tsType->IsETSPrimitiveType()) { + tsType = MaybeBoxType(tsType); + } + if (left->TsType()->IsTypeError()) { + left->SetTsType(tsType); + } + if (right->TsType()->IsTypeError()) { + right->SetTsType(tsType); + } return {tsType, tsType}; } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return CheckBinaryOperatorHelper(this, {left, right, expr, operationType, pos, isEqualOp}, {leftType, rightType, unboxedL, unboxedR}); } @@ -1120,6 +1142,8 @@ static std::tuple ResolveCheckBinaryOperatorForBigInt(ETSChecker case lexer::TokenType::PUNCTUATOR_LESS_THAN: case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: return {checker->GlobalETSBooleanType(), checker->GlobalETSBooleanType()}; default: return {leftType, rightType}; @@ -1156,8 +1180,6 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, return {leftType, leftType}; } - CheckNeedToGenerateGetValueForBinaryExpression(expr); - const bool isLogicalExtendedOperator = (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) || (operationType == lexer::TokenType::PUNCTUATOR_LOGICAL_OR); Type *unboxedL = @@ -1177,55 +1199,4 @@ std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, std::make_tuple(leftType, rightType, unboxedL, unboxedR)); } -Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) -{ - ES2PANDA_ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); - - if (left->IsDoubleType() || right->IsDoubleType()) { - return PerformArithmeticOperationOnTypes(left, right, operationType); - } - - if (left->IsFloatType() || right->IsFloatType()) { - return PerformArithmeticOperationOnTypes(left, right, operationType); - } - - if (left->IsLongType() || right->IsLongType()) { - return PerformArithmeticOperationOnTypes(left, right, operationType); - } - - return PerformArithmeticOperationOnTypes(left, right, operationType); -} - -Type *ETSChecker::HandleBitwiseOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) -{ - ES2PANDA_ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && - right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); - - if (left->IsDoubleType() || right->IsDoubleType()) { - return HandleBitWiseArithmetic(left, right, operationType); - } - - if (left->IsFloatType() || right->IsFloatType()) { - return HandleBitWiseArithmetic(left, right, operationType); - } - - if (left->IsLongType() || right->IsLongType()) { - return HandleBitWiseArithmetic(left, right, operationType); - } - - return HandleBitWiseArithmetic(left, right, operationType); -} - -void ETSChecker::FlagExpressionWithUnboxing(Type *type, Type *unboxedType, ir::Expression *typeExpression) -{ - if (type->IsETSEnumType()) { - typeExpression->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); - return; - } - if (type->IsETSObjectType() && (unboxedType != nullptr)) { - typeExpression->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedType)); - } -} - } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/arithmetic.h b/ets2panda/checker/ets/arithmetic.h index 64c9e8ffe5ab027488dde629517ce7e93b21938f..6ca03978bb6127cf14ad560447bfb15a78af169d 100644 --- a/ets2panda/checker/ets/arithmetic.h +++ b/ets2panda/checker/ets/arithmetic.h @@ -18,176 +18,9 @@ #include "checker/ETSchecker.h" #include "checker/ETSAnalyzer.h" +#include "checker/types/globalTypesHolder.h" namespace ark::es2panda::checker { - -template -typename TargetType::UType ETSChecker::GetOperand(Type *type) -{ - switch (ETSType(type)) { - case TypeFlag::BYTE: { - return type->AsByteType()->GetValue(); - } - case TypeFlag::CHAR: { - return type->AsCharType()->GetValue(); - } - case TypeFlag::SHORT: { - return type->AsShortType()->GetValue(); - } - case TypeFlag::INT: { - return type->AsIntType()->GetValue(); - } - case TypeFlag::LONG: { - return type->AsLongType()->GetValue(); - } - case TypeFlag::FLOAT: { - return type->AsFloatType()->GetValue(); - } - case TypeFlag::DOUBLE: { - return type->AsDoubleType()->GetValue(); - } - case TypeFlag::ETS_BOOLEAN: { - return type->AsETSBooleanType()->GetValue(); - } - default: { - ES2PANDA_UNREACHABLE(); - } - } -} - -template -Type *ETSChecker::PerformRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) -{ - using UType = typename TargetType::UType; - - UType leftValue = GetOperand(left); - UType rightValue = GetOperand(right); - - bool result {}; - switch (operationType) { - case lexer::TokenType::PUNCTUATOR_LESS_THAN: { - result = leftValue < rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { - result = leftValue <= rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { - result = leftValue > rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { - result = leftValue >= rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: - case lexer::TokenType::PUNCTUATOR_EQUAL: { - result = leftValue == rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: - case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { - result = leftValue != rightValue; - break; - } - default: { - ES2PANDA_UNREACHABLE(); - } - } - - return CreateETSBooleanType(result); -} - -template -Type *ETSChecker::PerformArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) -{ - using UType = typename TargetType::UType; - - UType leftValue = GetOperand(left); - UType rightValue = GetOperand(right); - auto result = leftValue; - auto isForbiddenZeroDivision = [&rightValue]() { return std::is_integral::value && rightValue == 0; }; - auto isIntegralDivideResOverflow = [&rightValue, &leftValue]() { - // Note: Handle corner cases - return std::is_integral_v && leftValue == std::numeric_limits::min() && rightValue == -1; - }; - - switch (operationType) { - case lexer::TokenType::PUNCTUATOR_PLUS: - case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { - result = leftValue + rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_MINUS: - case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { - result = leftValue - rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_DIVIDE: - case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { - if (isForbiddenZeroDivision()) { - return nullptr; - } - - if (isIntegralDivideResOverflow()) { - return ProgramAllocator()->New(std::numeric_limits::min()); - } - - result = leftValue / rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_MULTIPLY: - case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { - result = leftValue * rightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_MOD: - case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { - if (isForbiddenZeroDivision()) { - return nullptr; - } - result = HandleModulo(leftValue, rightValue); - break; - } - default: { - ES2PANDA_UNREACHABLE(); - } - } - - return ProgramAllocator()->New(result); -} - -template <> -inline IntType::UType ark::es2panda::checker::ETSChecker::HandleModulo(IntType::UType leftValue, - IntType::UType rightValue) -{ - ES2PANDA_ASSERT(rightValue != 0); - return leftValue % rightValue; -} - -template <> -inline LongType::UType ark::es2panda::checker::ETSChecker::HandleModulo(LongType::UType leftValue, - LongType::UType rightValue) -{ - ES2PANDA_ASSERT(rightValue != 0); - return leftValue % rightValue; -} - -template <> -inline FloatType::UType ark::es2panda::checker::ETSChecker::HandleModulo(FloatType::UType leftValue, - FloatType::UType rightValue) -{ - return std::fmod(leftValue, rightValue); -} - -template <> -inline DoubleType::UType ark::es2panda::checker::ETSChecker::HandleModulo( - DoubleType::UType leftValue, DoubleType::UType rightValue) -{ - return std::fmod(leftValue, rightValue); -} - template inline IntegerUType CastIfFloat(FloatOrIntegerUType num) { @@ -197,61 +30,6 @@ inline IntegerUType CastIfFloat(FloatOrIntegerUType num) return num; } } - -template -Type *ETSChecker::HandleBitWiseArithmetic(Type *left, Type *right, lexer::TokenType operationType) -{ - using IntegerUType = typename IntegerType::UType; - using UnsignedUType = std::make_unsigned_t; - - UnsignedUType result = 0; - UnsignedUType unsignedLeftValue = CastIfFloat(GetOperand(left)); - UnsignedUType unsignedRightValue = CastIfFloat(GetOperand(right)); - - auto mask = std::numeric_limits::digits - 1U; - auto shift = unsignedRightValue & mask; - - switch (operationType) { - case lexer::TokenType::PUNCTUATOR_BITWISE_AND: - case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { - result = unsignedLeftValue & unsignedRightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_BITWISE_OR: - case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { - result = unsignedLeftValue | unsignedRightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: - case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { - result = unsignedLeftValue ^ unsignedRightValue; - break; - } - case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: - case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { - static_assert(sizeof(UnsignedUType) == 4 || sizeof(UnsignedUType) == 8); - result = unsignedLeftValue << shift; - break; - } - case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: - case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { - static_assert(sizeof(IntegerUType) == 4 || sizeof(IntegerUType) == 8); - result = static_cast(unsignedLeftValue) >> shift; // NOLINT(hicpp-signed-bitwise) - break; - } - case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: - case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { - static_assert(sizeof(UnsignedUType) == 4 || sizeof(UnsignedUType) == 8); - result = unsignedLeftValue >> shift; - break; - } - default: { - ES2PANDA_UNREACHABLE(); - } - } - - return ProgramAllocator()->New(result); -} } // namespace ark::es2panda::checker #endif diff --git a/ets2panda/checker/ets/assignAnalyzer.cpp b/ets2panda/checker/ets/assignAnalyzer.cpp index 1966a95bfb61d0eee8b7bfa01be6538068434655..a411f9d429bf07f01adc90396a3a08a1261112b7 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -14,7 +14,6 @@ */ #include "assignAnalyzer.h" -#include #include "ir/base/classDefinition.h" #include "ir/base/classProperty.h" @@ -58,6 +57,7 @@ #include "varbinder/scope.h" #include "varbinder/declaration.h" #include "checker/ETSchecker.h" +#include "checker/types/gradualType.h" #include "ir/base/catchClause.h" #include "parser/program/program.h" #include "checker/types/ts/objectType.h" @@ -1219,7 +1219,7 @@ util::StringView AssignAnalyzer::GetVariableName(const ir::AstNode *node) const } } -const lexer::SourcePosition &AssignAnalyzer::GetVariablePosition(const ir::AstNode *node) const +lexer::SourcePosition AssignAnalyzer::GetVariablePosition(const ir::AstNode *node) const { switch (node->Type()) { case ir::AstNodeType::CLASS_PROPERTY: @@ -1360,8 +1360,11 @@ static bool IsDefaultValueType(const Type *type, bool isNonReadonlyField) if (type == nullptr) { return false; } - return (type->IsETSPrimitiveType() || type->IsETSNeverType() || type->IsETSUndefinedType() || - type->IsETSNullType() || + if (type->IsGradualType()) { + return IsDefaultValueType(type->AsGradualType()->GetBaseType(), isNonReadonlyField); + } + return (type->IsETSPrimitiveType() || (type->IsETSObjectType() && type->AsETSObjectType()->IsBoxedPrimitive()) || + type->IsETSNeverType() || type->IsETSUndefinedType() || type->IsETSNullType() || (type->PossiblyETSUndefined() && (!type->HasTypeFlag(checker::TypeFlag::GENERIC) || (isNonReadonlyField && !CHECK_GENERIC_NON_READONLY_PROPERTIES)))); } @@ -1403,7 +1406,7 @@ void AssignAnalyzer::LetInit(const ir::AstNode *node) // check reassignment of readonly properties util::StringView type = GetVariableType(declNode); util::StringView name = GetVariableName(declNode); - const lexer::SourcePosition &pos = GetVariablePosition(node); + const lexer::SourcePosition pos = GetVariablePosition(node); auto uninit = [this](NodeId a) { uninits_.Excl(a); @@ -1451,13 +1454,17 @@ void AssignAnalyzer::CheckInit(const ir::AstNode *node) } if (declNode->Parent() != classDef_) { - // property of an other class + // property of another class return; } if (node->IsDefinite()) { return; } + + if (declNode->AsClassProperty()->IsImmediateInit()) { + return; + } } if (classDef_ == globalClass_ || (adr < classFirstAdr_ || adr >= firstAdr_)) { @@ -1468,7 +1475,7 @@ void AssignAnalyzer::CheckInit(const ir::AstNode *node) util::StringView type = GetVariableType(declNode); util::StringView name = GetVariableName(declNode); - const lexer::SourcePosition &pos = GetVariablePosition(node); + const lexer::SourcePosition pos = GetVariablePosition(node); std::stringstream ss; if (node->IsClassProperty()) { diff --git a/ets2panda/checker/ets/assignAnalyzer.h b/ets2panda/checker/ets/assignAnalyzer.h index 621a24ba9e18239450879b044fd7fa70b0bf1823..c5004e5fae3e216dadfdda8282fd03dfb64ad9d6 100644 --- a/ets2panda/checker/ets/assignAnalyzer.h +++ b/ets2panda/checker/ets/assignAnalyzer.h @@ -150,7 +150,7 @@ private: NodeId GetNodeId(const ir::AstNode *node) const; util::StringView GetVariableType(const ir::AstNode *node) const; util::StringView GetVariableName(const ir::AstNode *node) const; - const lexer::SourcePosition &GetVariablePosition(const ir::AstNode *node) const; + lexer::SourcePosition GetVariablePosition(const ir::AstNode *node) const; const ir::AstNode *GetDeclaringNode(const ir::AstNode *node); varbinder::Variable *GetBoundVariable(const ir::AstNode *node); bool VariableHasDefaultValue(const ir::AstNode *node); diff --git a/ets2panda/checker/ets/baseAnalyzer.h b/ets2panda/checker/ets/baseAnalyzer.h index 8599c5d00a4cf3d9ed0b8b8b11b719a40f323683..93ed62e4836982c61eb7fce7ea3387956c0408d1 100644 --- a/ets2panda/checker/ets/baseAnalyzer.h +++ b/ets2panda/checker/ets/baseAnalyzer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -21,7 +21,7 @@ namespace ark::es2panda::ir { class AstNode; -enum class AstNodeType; +enum class AstNodeType : uint8_t; } // namespace ark::es2panda::ir namespace ark::es2panda::checker { diff --git a/ets2panda/checker/ets/castingContext.cpp b/ets2panda/checker/ets/castingContext.cpp index 0f1c377aaa3d61389ec083ad314ea99ca5704da2..d7a3e5f069d60d2a94c69c03debb0e95d5e3db20 100644 --- a/ets2panda/checker/ets/castingContext.cpp +++ b/ets2panda/checker/ets/castingContext.cpp @@ -26,9 +26,8 @@ CastingContext::CastingContext(TypeRelation *relation, const diagnostic::Diagnos const SavedTypeRelationFlagsContext savedTypeRelationFlags(relation, flags_); relation->SetNode(data.node); - const bool isLegalBoxedPrimitiveConversion = relation->IsLegalBoxedPrimitiveConversion(data.target, data.source); relation->Result(false); - if (!relation->IsSupertypeOf(data.target, data.source) && !isLegalBoxedPrimitiveConversion) { + if (!relation->IsSupertypeOf(data.target, data.source)) { relation->IsCastableTo(data.source, data.target); // #22954 string comparison if (!relation->IsTrue() && data.source->ToString() == data.target->ToString()) { @@ -39,13 +38,6 @@ CastingContext::CastingContext(TypeRelation *relation, const diagnostic::Diagnos } } - if (isLegalBoxedPrimitiveConversion && !relation->IsTrue()) { - auto *const checker = relation->GetChecker()->AsETSChecker(); - Type *sourceUnboxedType = checker->MaybeUnboxType(data.source); - relation->GetNode()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(sourceUnboxedType)); - relation->GetNode()->AddBoxingUnboxingFlags(checker->GetBoxingFlag(data.target)); - } - uncheckedCast_ = relation->UncheckedCast(); relation->SetNode(nullptr); } diff --git a/ets2panda/checker/ets/conversion.cpp b/ets2panda/checker/ets/conversion.cpp index 345a1a4f672ee164be2b91985212a77493fc625f..90c5d2f5be896c9e5a163c814e21236480cb8378 100644 --- a/ets2panda/checker/ets/conversion.cpp +++ b/ets2panda/checker/ets/conversion.cpp @@ -16,7 +16,6 @@ #include "conversion.h" #include "checker/ets/boxingConverter.h" -#include "checker/ets/narrowingConverter.h" #include "checker/ets/unboxingConverter.h" #include "checker/ets/wideningConverter.h" #include "checker/types/ets/etsTupleType.h" @@ -35,23 +34,6 @@ void WideningPrimitive(TypeRelation *const relation, Type *const source, Type *c WideningConverter(relation->GetChecker()->AsETSChecker(), relation, target, source); } -void NarrowingPrimitive(TypeRelation *const relation, Type *const source, Type *const target) -{ - ES2PANDA_ASSERT(source->IsETSPrimitiveType() && target->IsETSPrimitiveType()); - - NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, target, source); -} - -void WideningNarrowingPrimitive(TypeRelation *const relation, ByteType *const source, CharType *const target) -{ - auto *const tempInt = relation->GetChecker()->AsETSChecker()->GetGlobalTypesHolder()->GlobalIntType(); - WideningPrimitive(relation, source, tempInt); - if (!relation->IsTrue()) { - return; - } - NarrowingPrimitive(relation, tempInt, target); -} - void WideningReference(TypeRelation *const relation, ETSObjectType *const source, ETSObjectType *const target) { relation->IsSupertypeOf(target, source); @@ -253,13 +235,6 @@ void NarrowingReference(TypeRelation *const relation, ETSObjectType *const sourc NarrowingReferenceImpl(relation, source, target); } -static inline void RollbackBoxingIfFailed(TypeRelation *const relation) -{ - if (!relation->IsTrue()) { - relation->GetNode()->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE); - } -} - ETSObjectType *Boxing(TypeRelation *const relation, Type *const source) { auto *const etsChecker = relation->GetChecker()->AsETSChecker(); @@ -268,7 +243,6 @@ ETSObjectType *Boxing(TypeRelation *const relation, Type *const source) return nullptr; } auto *const boxedType = boxed.Result()->AsETSObjectType(); - relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(boxedType)); return boxedType; } @@ -280,7 +254,6 @@ Type *Unboxing(TypeRelation *const relation, ETSObjectType *const source) return nullptr; } auto *const unboxedType = unboxed.Result(); - relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(unboxedType)); return unboxedType; } @@ -292,27 +265,6 @@ void UnboxingWideningPrimitive(TypeRelation *const relation, ETSObjectType *cons } ES2PANDA_ASSERT(unboxedSource != nullptr); WideningPrimitive(relation, target, unboxedSource); - RollbackBoxingIfFailed(relation); -} - -void UnboxingNarrowingPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target) -{ - auto *const unboxedSource = Unboxing(relation, source); - if (!relation->IsTrue()) { - return; - } - ES2PANDA_ASSERT(unboxedSource != nullptr); - NarrowingPrimitive(relation, target, unboxedSource); -} - -void UnboxingWideningNarrowingPrimitive(TypeRelation *const relation, ETSObjectType *const source, Type *const target) -{ - auto *const unboxedSource = Unboxing(relation, source); - if (!relation->IsTrue()) { - return; - } - ES2PANDA_ASSERT(unboxedSource != nullptr); - WideningNarrowingPrimitive(relation, unboxedSource->AsByteType(), target->AsCharType()); } void NarrowingReferenceUnboxing(TypeRelation *const relation, ETSObjectType *const source, Type *const target) @@ -337,7 +289,6 @@ void BoxingWideningReference(TypeRelation *const relation, Type *const source, E } ES2PANDA_ASSERT(boxedSource != nullptr); WideningReference(relation, boxedSource, target); - RollbackBoxingIfFailed(relation); } void String(TypeRelation *const relation, Type *const source) diff --git a/ets2panda/checker/ets/conversion.h b/ets2panda/checker/ets/conversion.h index cf75be4906f1534d32e161d4088702fb7825aebc..e78b65c087e67a85ff304efbdb430ed4e6ab4620 100644 --- a/ets2panda/checker/ets/conversion.h +++ b/ets2panda/checker/ets/conversion.h @@ -23,8 +23,6 @@ namespace ark::es2panda::checker::conversion { void Identity(TypeRelation *relation, Type *source, Type *target); void WideningPrimitive(TypeRelation *relation, Type *source, Type *target); -void NarrowingPrimitive(TypeRelation *relation, Type *source, Type *target); -void WideningNarrowingPrimitive(TypeRelation *relation, ByteType *source, CharType *target); void WideningReference(TypeRelation *relation, ETSObjectType *source, ETSObjectType *target); void WideningReference(TypeRelation *relation, ETSArrayType *source, ETSObjectType *target); @@ -38,8 +36,6 @@ void NarrowingReference(TypeRelation *relation, ETSObjectType *source, ETSTupleT ETSObjectType *Boxing(TypeRelation *relation, Type *source); Type *Unboxing(TypeRelation *relation, ETSObjectType *source); -void UnboxingWideningNarrowingPrimitive(TypeRelation *relation, ETSObjectType *source, Type *target); -void UnboxingNarrowingPrimitive(TypeRelation *relation, ETSObjectType *source, Type *target); void UnboxingWideningPrimitive(TypeRelation *relation, ETSObjectType *source, Type *target); void NarrowingReferenceUnboxing(TypeRelation *relation, ETSObjectType *source, Type *target); void BoxingWideningReference(TypeRelation *relation, Type *source, ETSObjectType *target); diff --git a/ets2panda/checker/ets/dynamic.cpp b/ets2panda/checker/ets/dynamic.cpp index 93a5d32808d0c1f71fe9969cac04804fca1f713b..2a67acd88d5d2d5a73df5b16812ca6e63de7b950 100644 --- a/ets2panda/checker/ets/dynamic.cpp +++ b/ets2panda/checker/ets/dynamic.cpp @@ -20,8 +20,6 @@ #include "varbinder/declaration.h" #include "varbinder/varbinder.h" #include "varbinder/ETSBinder.h" -#include "checker/types/ets/etsDynamicFunctionType.h" -#include "checker/ets/dynamic/dynamicCall.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/base/classProperty.h" #include "ir/base/classStaticBlock.h" @@ -97,147 +95,6 @@ ir::ETSParameterExpression *ETSChecker::AddParam(util::StringView name, ir::Type return ProgramAllocNode(paramIdent, false, ProgramAllocator()); } -template -ir::MethodDefinition *ETSChecker::CreateDynamicCallIntrinsic(ir::Expression *callee, const ArenaVector &arguments, - Language lang) -{ - ArenaVector params(ProgramAllocator()->Adapter()); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto dynamicTypeNode = ProgramAllocNode(GlobalBuiltinDynamicType(lang), ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto intTypeNode = ProgramAllocNode(ir::PrimitiveType::INT, ProgramAllocator()); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *objParam = AddParam("obj", dynamicTypeNode); - params.push_back(objParam); - - ir::ETSParameterExpression *param2; - if (!DynamicCall::IsByValue(VarBinder()->AsETSBinder(), callee)) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - param2 = AddParam("qname_start", intTypeNode); - params.push_back(param2); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - param2 = AddParam("qname_len", intTypeNode->Clone(ProgramAllocator(), nullptr)); - } else { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - param2 = AddParam("this", dynamicTypeNode->Clone(ProgramAllocator(), nullptr)); - } - - params.push_back(param2); - - for (size_t i = 0; i < arguments.size(); i++) { - util::UString paramName("p" + std::to_string(i), ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto paramType = arguments[i]->TsType()->IsLambdaObject() - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ? dynamicTypeNode->Clone(ProgramAllocator(), nullptr) - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - : ProgramAllocNode(arguments[i]->TsType(), ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - params.emplace_back(AddParam(paramName.View(), paramType)); - } - - auto funcSignature = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ir::FunctionSignature(nullptr, std::move(params), dynamicTypeNode->Clone(ProgramAllocator(), nullptr)); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *func = ProgramAllocNode( - ProgramAllocator(), - ir::ScriptFunction::ScriptFunctionData {nullptr, std::move(funcSignature), ir::ScriptFunctionFlags::METHOD, - ir::ModifierFlags::NONE}); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *name = ProgramAllocNode(compiler::Signatures::STATIC_INVOKE_METHOD, ProgramAllocator()); - func->SetIdent(name); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *funcExpr = ProgramAllocNode(func); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *method = ProgramAllocNode( - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ir::MethodDefinitionKind::METHOD, func->Id()->Clone(ProgramAllocator(), nullptr), funcExpr, - ir::ModifierFlags::PUBLIC | ir::ModifierFlags::NATIVE | ir::ModifierFlags::STATIC, ProgramAllocator(), false); - return method; -} - -static void ToString(ETSChecker *checker, const ArenaVector &arguments, std::stringstream &ss) -{ - for (auto *arg : arguments) { - auto type = arg->Check(checker); - ss << "-"; - type->ToString(ss); - } -} - -static void ToString([[maybe_unused]] ETSChecker *checker, const ArenaVector &arguments, - std::stringstream &ss) -{ - for (auto *arg : arguments) { - auto *type = arg->TsType(); - ss << "-"; - type->ToString(ss); - } -} - -template -Signature *ETSChecker::ResolveDynamicCallExpression(ir::Expression *callee, const ArenaVector &arguments, - Language lang, bool isConstruct) -{ - auto &dynamicIntrinsics = *DynamicCallIntrinsics(isConstruct); - - auto mapIt = dynamicIntrinsics.find(lang); - if (mapIt == dynamicIntrinsics.cend()) { - std::tie(mapIt, std::ignore) = dynamicIntrinsics.emplace(lang, ProgramAllocator()->Adapter()); - } - - auto &map = mapIt->second; - - std::stringstream ss; - ss << "dyncall"; - if (DynamicCall::IsByValue(VarBinder()->AsETSBinder(), callee)) { - ss << "-byvalue"; - } else { - const auto callNames = DynamicCall::ResolveCall(VarBinder()->AsETSBinder(), callee); - DynamicCallNames(isConstruct)->try_emplace(callNames.name, 0); - } - - ToString(this, arguments, ss); - - auto key = ss.str(); - auto it = map.find(util::StringView(key)); - if (it == map.end()) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto klass = GetDynamicClass(lang, isConstruct); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *method = CreateDynamicCallIntrinsic(callee, arguments, lang); - auto props = ArenaVector(ProgramAllocator()->Adapter()); - props.emplace_back(method); - klass->Definition()->AddProperties(std::move(props)); - - { - auto prevClass = VarBinder()->AsETSBinder()->GetGlobalRecordTable()->ClassDefinition(); - VarBinder()->AsETSBinder()->GetGlobalRecordTable()->SetClassDefinition(klass->Definition()); - ProcessScopesNode(this, method); - ProcessCheckerNode(this, method); - VarBinder()->AsETSBinder()->GetGlobalRecordTable()->SetClassDefinition(prevClass); - } - method->Function()->Signature()->SetReturnType(GlobalBuiltinDynamicType(lang)); - - map.emplace(util::UString(key, ProgramAllocator()).View(), method->Function()); - return method->Function()->Signature(); - } - - return it->second->Signature(); -} - -template Signature *ETSChecker::ResolveDynamicCallExpression( - ir::Expression *callee, const ArenaVector &arguments, Language lang, bool isConstruct); - -template Signature *ETSChecker::ResolveDynamicCallExpression( - ir::Expression *callee, const ArenaVector &arguments, Language lang, bool isConstruct); - std::pair ETSChecker::CreateStaticScriptFunction( ClassInitializerBuilder const &builder) { @@ -331,33 +188,6 @@ ir::MethodDefinition *ETSChecker::CreateClassInstanceInitializer(const ClassInit return ctor; } -ir::ClassStaticBlock *ETSChecker::CreateDynamicCallClassInitializer(Language lang, bool isConstruct) -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateClassStaticInitializer( - [this, lang, isConstruct](ArenaVector *statements, - [[maybe_unused]] ArenaVector *params) { - auto [builtin_class_name, builtin_method_name] = - util::Helpers::SplitSignature(isConstruct ? compiler::Signatures::Dynamic::InitNewClassBuiltin(lang) - : compiler::Signatures::Dynamic::InitCallClassBuiltin(lang)); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *classId = ProgramAllocNode(builtin_class_name, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *methodId = ProgramAllocNode(builtin_method_name, ProgramAllocator()); - auto *callee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, - false, false); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *initCall = ProgramAllocNode( - callee, ArenaVector(ProgramAllocator()->Adapter()), nullptr, false); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - statements->push_back(ProgramAllocNode(initCall)); - }); -} - ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassBuilder &builder) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -371,7 +201,7 @@ ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassB // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *classDecl = ProgramAllocNode(classDef, ProgramAllocator()); - VarBinder()->Program()->Ast()->Statements().push_back(classDecl); + VarBinder()->Program()->Ast()->AddStatement(classDecl); classDecl->SetParent(VarBinder()->Program()->Ast()); auto varBinder = VarBinder()->AsETSBinder(); @@ -391,94 +221,6 @@ ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassB return classDecl; } -ir::ClassProperty *ETSChecker::CreateStaticReadonlyField(const char *name) -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *fieldIdent = ProgramAllocNode(name, ProgramAllocator()); - auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY; - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *field = ProgramAllocNode( - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - fieldIdent, nullptr, ProgramAllocNode(ir::PrimitiveType::INT, ProgramAllocator()), flags, - ProgramAllocator(), false); - return field; -} - -ir::ClassDeclaration *ETSChecker::GetDynamicClass(Language lang, bool isConstruct) -{ - auto &klasses = dynamicClasses_[static_cast(isConstruct)]; - if (klasses.count(lang) != 0U) { - return klasses[lang]; - } - auto className = - isConstruct ? compiler::Signatures::Dynamic::NewClass(lang) : compiler::Signatures::Dynamic::CallClass(lang); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto klass = BuildClass(className, [this, lang, isConstruct](ArenaVector *classBody) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateStaticReadonlyField("qname_start_from")); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateDynamicCallClassInitializer(lang, isConstruct)); - }); - klasses.emplace(lang, klass); - return klass; -} - -void ETSChecker::ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector *statements) -{ - auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language()); - auto [builtin_class_name, builtin_method_name] = util::Helpers::SplitSignature(builtin); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *classId = ProgramAllocNode(builtin_class_name, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *methodId = ProgramAllocNode(builtin_method_name, ProgramAllocator()); - auto *callee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, - false); - - // NOTE: #23698. Make 'ohmUrl' mandatory in 'dynamicPaths'. - util::StringView ohmUrl = util::UString(import->OhmUrl(), ProgramAllocator()).View(); - if (ohmUrl.Empty()) { - ohmUrl = import->ResolvedSource(); - if (ark::os::file::File::IsRegularFile(ohmUrl.Mutf8())) { - ohmUrl = util::UString(ark::os::RemoveExtension(ohmUrl.Mutf8()), ProgramAllocator()).View(); - } - } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ArenaVector callParams({ProgramAllocNode(ohmUrl)}, - ProgramAllocator()->Adapter()); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *loadCall = ProgramAllocNode(callee, std::move(callParams), nullptr, false); - auto *moduleClassId = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(compiler::Signatures::DYNAMIC_MODULE_CLASS, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *fieldId = ProgramAllocNode(import->AssemblerName(), ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *property = ProgramAllocNode(moduleClassId, fieldId, - ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); - - auto *initializer = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - statements->push_back(ProgramAllocNode(initializer)); -} - -ir::ClassStaticBlock *ETSChecker::CreateDynamicModuleClassInitializer( - const std::vector &imports) -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateClassStaticInitializer([this, imports](ArenaVector *statements, - [[maybe_unused]] ArenaVector *params) { - for (auto *import : imports) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ClassInitializerFromImport(import, statements); - } - }); -} - ir::MethodDefinition *ETSChecker::CreateClassMethod(const std::string_view name, ir::ScriptFunctionFlags funcFlags, ir::ModifierFlags modifierFlags, const MethodBuilder &builder) { @@ -516,228 +258,4 @@ ir::MethodDefinition *ETSChecker::CreateClassMethod(const std::string_view name, return method; } -ir::MethodDefinition *ETSChecker::CreateDynamicModuleClassInitMethod() -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateClassMethod(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, ir::ScriptFunctionFlags::METHOD, - ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC, - [this]([[maybe_unused]] ArenaVector *statements, - [[maybe_unused]] ArenaVector *params, - Type **returnType) { *returnType = GlobalVoidType(); }); -} - -ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature, - ir::TypeNode *retTypeAnnotation) -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateClassMethod( - compiler::Signatures::LAMBDA_OBJECT_INVOKE, ir::ScriptFunctionFlags::METHOD, ir::ModifierFlags::PUBLIC, - [this, invokeSignature, retTypeAnnotation](ArenaVector *statements, - ArenaVector *params, Type **returnType) { - util::UString thisParamName(std::string("this"), ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ir::ETSParameterExpression *thisParam = AddParam(thisParamName.View(), nullptr); - params->push_back(thisParam); - - ArenaVector callParams(ProgramAllocator()->Adapter()); - for (auto *invokeParam : invokeSignature->Params()) { - auto paramName = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - util::UString(std::string("p") + std::to_string(callParams.size()), ProgramAllocator()).View(); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *param = AddParam(paramName, - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(invokeParam->TsType(), ProgramAllocator())); - params->push_back(param); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - callParams.push_back(param->Clone(ProgramAllocator(), nullptr)); - } - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *properyId = ProgramAllocNode("jsvalue_lambda", ProgramAllocator()); - auto *callee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(thisParam->Clone(ProgramAllocator(), nullptr), properyId, - ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *callLambda = ProgramAllocNode(callee, std::move(callParams), nullptr, false); - - auto *castToRetTypeExpr = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(callLambda, retTypeAnnotation->Clone(ProgramAllocator(), nullptr), - false); - castToRetTypeExpr->SetTsType(invokeSignature->ReturnType()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *retStatement = ProgramAllocNode(castToRetTypeExpr); - statements->push_back(retStatement); - - *returnType = invokeSignature->ReturnType(); - }); -} - -void ETSChecker::EmitDynamicModuleClassInitCall() -{ - auto *globalClass = VarBinder()->Program()->GlobalClass(); - auto &body = globalClass->Body(); - auto it = std::find_if(body.begin(), body.end(), [](ir::AstNode *node) { return node->IsClassStaticBlock(); }); - - ES2PANDA_ASSERT(it != body.end()); - - auto *staticBlock = (*it)->AsClassStaticBlock(); - auto *cctorBody = staticBlock->Function()->Body()->AsBlockStatement(); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *classId = ProgramAllocNode(compiler::Signatures::DYNAMIC_MODULE_CLASS, ProgramAllocator()); - auto *methodId = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, ProgramAllocator()); - auto *callee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, - false); - - ArenaVector callParams(ProgramAllocator()->Adapter()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *initCall = ProgramAllocNode(callee, std::move(callParams), nullptr, false); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *const node = ProgramAllocNode(initCall); - node->SetParent(cctorBody); - cctorBody->Statements().push_back(node); - - ProcessScopesNode(this, node); - ProcessCheckerNode(this, node); -} - -void ETSChecker::BuildClassBodyFromDynamicImports(const ArenaVector &dynamicImports, - ArenaVector *classBody) -{ - std::unordered_set fields; - std::vector imports; - - for (auto *import : dynamicImports) { - auto source = import->Source()->Str(); - if (fields.find(source) != fields.cend()) { - continue; - } - - auto assemblyName = std::string(source); - std::replace_if( - assemblyName.begin(), assemblyName.end(), [](char c) { return std::isalnum(c) == 0; }, '_'); - assemblyName.append(std::to_string(fields.size())); - - import->AssemblerName() = util::UString(assemblyName, ProgramAllocator()).View(); - fields.insert(import->AssemblerName()); - imports.push_back(import); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *fieldIdent = ProgramAllocNode(import->AssemblerName(), ProgramAllocator()); - auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC | ir::ModifierFlags::READONLY; - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *field = ProgramAllocNode( - fieldIdent, nullptr, - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(GlobalBuiltinDynamicType(import->Language()), ProgramAllocator()), - flags, ProgramAllocator(), false); - - classBody->push_back(field); - } - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateDynamicModuleClassInitializer(imports)); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateDynamicModuleClassInitMethod()); -} - -void ETSChecker::BuildDynamicImportClass() -{ - const auto &dynamicImports = VarBinder()->AsETSBinder()->DynamicImports(); - if (dynamicImports.empty()) { - return; - } - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - BuildClass(compiler::Signatures::DYNAMIC_MODULE_CLASS, - [this, dynamicImports](ArenaVector *classBody) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - BuildClassBodyFromDynamicImports(dynamicImports, classBody); - }); - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - EmitDynamicModuleClassInitCall(); -} - -ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInitializer(ETSObjectType *functionalInterface) -{ - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateClassInstanceInitializer( - [this](ArenaVector *statements, ArenaVector *params) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ir::ETSParameterExpression *thisParam = AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr); - params->push_back(thisParam); - - util::UString jsvalueParamName(std::string("jsvalue_param"), ProgramAllocator()); - ir::ETSParameterExpression *jsvalueParam = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - AddParam(jsvalueParamName.View(), - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(GlobalBuiltinJSValueType(), ProgramAllocator())); - params->push_back(jsvalueParam); - auto *moduleClassId = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(varbinder::VarBinder::MANDATORY_PARAM_THIS, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *fieldId = ProgramAllocNode("jsvalue_lambda", ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *property = ProgramAllocNode( - moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *initializer = ProgramAllocNode( - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - property, jsvalueParam->Clone(ProgramAllocator(), nullptr), lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - statements->push_back(ProgramAllocNode(initializer)); - }, - functionalInterface); -} - -void ETSChecker::BuildLambdaObjectClass(ETSObjectType *functionalInterface, ir::TypeNode *retTypeAnnotation) -{ - auto *invokeMethod = functionalInterface->GetOwnProperty( - compiler::Signatures::STATIC_INVOKE_METHOD); - ES2PANDA_ASSERT(invokeMethod != nullptr); - auto *invokeSignature = invokeMethod->TsType()->AsETSFunctionType()->CallSignatures()[0]; - ES2PANDA_ASSERT(invokeSignature != nullptr); - std::stringstream ss; - ss << compiler::Signatures::LAMBDA_OBJECT; - ToString(this, invokeSignature->Params(), ss); - auto syntheticLambdaObjName = ss.str(); - if (dynamicLambdaSignatureCache_.count(syntheticLambdaObjName) != 0) { - functionalInterface->AddConstructSignature(dynamicLambdaSignatureCache_[syntheticLambdaObjName]); - return; - } - - auto buildBody = [this, invokeSignature, retTypeAnnotation, - functionalInterface](ArenaVector *classBody) { - auto assemblyName = "jsvalue_lambda"; - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *fieldIdent = ProgramAllocNode(assemblyName, ProgramAllocator()); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *field = ProgramAllocNode( - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - fieldIdent, nullptr, ProgramAllocNode(GlobalBuiltinJSValueType(), ProgramAllocator()), - ir::ModifierFlags::PRIVATE, ProgramAllocator(), false); - classBody->push_back(field); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateLambdaObjectClassInitializer(functionalInterface)); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - classBody->push_back(CreateLambdaObjectClassInvokeMethod(invokeSignature, retTypeAnnotation)); - }; - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - BuildClass(util::StringView(syntheticLambdaObjName), buildBody); - - dynamicLambdaSignatureCache_[syntheticLambdaObjName] = functionalInterface->ConstructSignatures()[0]; -} - } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/dynamic/dynamicCall.cpp b/ets2panda/checker/ets/dynamic/dynamicCall.cpp deleted file mode 100644 index a80dd1fd2d37652086e92691e3388feb4912ecd3..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/dynamic/dynamicCall.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2021-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 "checker/ets/dynamic/dynamicCall.h" - -#include "ir/ets/etsImportDeclaration.h" -#include "ir/ets/etsTypeReference.h" -#include "ir/ets/etsTypeReferencePart.h" -#include "ir/module/importSpecifier.h" -#include "ir/ts/tsQualifiedName.h" -#include "ir/expressions/memberExpression.h" - -namespace ark::es2panda::checker { - -DynamicCall::Result DynamicCall::ResolveCall(const varbinder::ETSBinder *varbinder, const ir::Expression *callee) -{ - auto calleeName = NameHolder(varbinder->Allocator()->Adapter()); - - if (callee->IsETSTypeReference()) { - // new A.B.C() => call js.new(A, ".B.C") - callee = callee->AsETSTypeReference()->Part()->Name(); - while (callee->IsTSQualifiedName()) { - auto *qname = callee->AsTSQualifiedName(); - callee = qname->Left(); - calleeName.emplace_back(qname->Right()->AsIdentifier()->Name()); - } - ES2PANDA_ASSERT(callee->IsIdentifier()); - } else if (callee->IsMemberExpression()) { - const auto memberExpr = callee->AsMemberExpression(); - callee = SqueezeExpr(memberExpr, calleeName); - } - if (callee->IsIdentifier()) { - // kinda optimization in case: - // `import X from Y` to use (load Y, call "X"), instead of (load Y, load X, call) - const auto var = callee->AsIdentifier()->Variable(); - const auto *data = varbinder->DynamicImportDataForVar(var); - if (data != nullptr && data->specifier != nullptr && data->specifier->IsImportSpecifier()) { - calleeName.emplace_back(data->specifier->AsImportSpecifier()->Imported()->Name()); - std::reverse(calleeName.begin(), calleeName.end()); - return {data->import, calleeName}; - } - } - std::reverse(calleeName.begin(), calleeName.end()); - return {callee, calleeName}; -} - -DynamicCall::Result DynamicCall::SqueezeExpr(ArenaAllocator *allocator, const ir::MemberExpression *expr) -{ - NameHolder name(allocator->Adapter()); - auto obj = SqueezeExpr(expr, name); - std::reverse(name.begin(), name.end()); - return {obj, name}; -} - -const ir::Expression *DynamicCall::SqueezeExpr(const ir::MemberExpression *memberExpr, NameHolder &name) -{ - if (!memberExpr->Object()->TsType()->IsETSDynamicType() || memberExpr->IsComputed()) { - return memberExpr; - } - ES2PANDA_ASSERT(memberExpr->Property()->IsIdentifier()); - name.emplace_back(memberExpr->Property()->AsIdentifier()->Name()); - if (memberExpr->Object()->IsMemberExpression()) { - return SqueezeExpr(memberExpr->Object()->AsMemberExpression(), name); - } - return memberExpr->Object(); -} - -} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/dynamic/dynamicCall.h b/ets2panda/checker/ets/dynamic/dynamicCall.h deleted file mode 100644 index b4de6cfd0a0b7bcab4e9cdace2a8997c5eff1db9..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/dynamic/dynamicCall.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2021 - 2024 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 ARK_DYNAMICCALLINFO_H -#define ARK_DYNAMICCALLINFO_H - -#include - -#include "varbinder/ETSBinder.h" -#include "ir/expression.h" - -namespace ark::es2panda::checker { - -class DynamicCall { - using NameHolder = ArenaVector; - -public: - struct Result { - const ir::AstNode *obj; - const NameHolder name; // NOLINT(readability-identifier-naming) - }; - - /** - * Resolve callee - * @param varbinder - * @param callee expression used to call method - * @return callee and name from which should be used to produce call - */ - static Result ResolveCall(const varbinder::ETSBinder *varbinder, const ir::Expression *callee); - static bool IsByValue(const varbinder::ETSBinder *varbinder, const ir::Expression *callee) - { - return ResolveCall(varbinder, callee).name.empty(); - } - - /** - * Example: A[0].C.D => return: A[0], name: ".C.D" - * @param expr member expression - * @param name to store result - * @return object with remaining member expression - */ - static Result SqueezeExpr(ArenaAllocator *allocator, const ir::MemberExpression *expr); - -private: - static const ir::Expression *SqueezeExpr(const ir::MemberExpression *expr, NameHolder &name); -}; - -} // namespace ark::es2panda::checker -#endif // ARK_DYNAMICCALLINFO_H diff --git a/ets2panda/checker/ets/etsWarningAnalyzer.cpp b/ets2panda/checker/ets/etsWarningAnalyzer.cpp index 4376b07ba802f57f2f1a131f291d017ea6cc907f..f207387c3f3d1c864cf618001ad783eeda5b3e71 100644 --- a/ets2panda/checker/ets/etsWarningAnalyzer.cpp +++ b/ets2panda/checker/ets/etsWarningAnalyzer.cpp @@ -34,8 +34,6 @@ #include "ir/base/classDefinition.h" #include "ir/statements/forOfStatement.h" #include "ir/statements/variableDeclarator.h" -#include "ir/statements/variableDeclaration.h" -#include "ir/expressions/updateExpression.h" namespace ark::es2panda::checker { @@ -273,157 +271,6 @@ void ETSWarningAnalyzer::ETSWarningRemoveLambda(const ir::AstNode *node) node->Iterate([&](auto *childNode) { ETSWarningRemoveLambda(childNode); }); } -void ETSWarningAnalyzer::CheckTypeOfBoxing(const ir::AstNode *node) -{ - ES2PANDA_ASSERT(node != nullptr); - const auto flags = node->GetBoxingUnboxingFlags(); - if ((flags & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0) { - std::string diagnosticParam; - switch (static_cast(flags & ir::BoxingUnboxingFlags::BOXING_FLAG)) { - case ir::BoxingUnboxingFlags::BOX_TO_INT: - diagnosticParam = "Int"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN: - diagnosticParam = "Boolean"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_BYTE: - diagnosticParam = "Byte"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_CHAR: - diagnosticParam = "Char"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE: - diagnosticParam = "Double"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_FLOAT: - diagnosticParam = "Float"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_LONG: - diagnosticParam = "Long"; - break; - case ir::BoxingUnboxingFlags::BOX_TO_SHORT: - diagnosticParam = "Short"; - break; - default: - break; - } - - if (!diagnosticParam.empty()) { - util::DiagnosticMessageParams diagnosticParams = {diagnosticParam, GetBoxingUnboxingType(node)}; - LogWarning(diagnostic::IMPLICIT_BOXING_TO, diagnosticParams, node->Start()); - } - } -} - -void ETSWarningAnalyzer::CheckTypeOfUnboxing(const ir::AstNode *node) -{ - ES2PANDA_ASSERT(node != nullptr); - const auto flags = node->GetBoxingUnboxingFlags(); - if ((flags & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0) { - std::string diagnosticParam; - switch (static_cast(flags & ir::BoxingUnboxingFlags::UNBOXING_FLAG)) { - case ir::BoxingUnboxingFlags::UNBOX_TO_INT: - diagnosticParam = "Int"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN: - diagnosticParam = "Boolean"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_BYTE: - diagnosticParam = "Byte"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_CHAR: - diagnosticParam = "Char"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE: - diagnosticParam = "Double"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT: - diagnosticParam = "Float"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_LONG: - diagnosticParam = "Long"; - break; - case ir::BoxingUnboxingFlags::UNBOX_TO_SHORT: - diagnosticParam = "Short"; - break; - default: - break; - } - - if (!diagnosticParam.empty()) { - util::DiagnosticMessageParams diagnosticParams = {diagnosticParam, GetBoxingUnboxingType(node)}; - LogWarning(diagnostic::IMPLICIT_BOXING_TO, diagnosticParams, node->Start()); - } - } -} - -void ETSWarningAnalyzer::CheckTypeOfBoxingUnboxing(const ir::AstNode *node) -{ - ES2PANDA_ASSERT(node != nullptr); - - CheckTypeOfBoxing(node); - CheckTypeOfUnboxing(node); -} - -std::string ETSWarningAnalyzer::GetBoxingUnboxingType(const ir::AstNode *node) -{ - ES2PANDA_ASSERT(node->Parent() != nullptr); - switch (node->Parent()->Type()) { - case ir::AstNodeType::VARIABLE_DECLARATOR: { - return " in Variable Declaration"; - } - case ir::AstNodeType::CALL_EXPRESSION: { - return " in Call Method/Function"; - } - case ir::AstNodeType::SWITCH_STATEMENT: { - return " in Switch-case Statement"; - } - case ir::AstNodeType::ASSIGNMENT_EXPRESSION: { - return " in Assignment Expression"; - } - case ir::AstNodeType::BINARY_EXPRESSION: { - return " in Binary Expression"; - } - case ir::AstNodeType::UNARY_EXPRESSION: { - return " in Unary Expression"; - } - case ir::AstNodeType::UPDATE_EXPRESSION: { - return " in Update Expression"; - } - case ir::AstNodeType::MEMBER_EXPRESSION: { - return " in Member Expression"; - } - default: - return ""; - } -} - -void ETSWarningAnalyzer::ETSWarningImplicitBoxingUnboxing(const ir::AstNode *node) -{ - ES2PANDA_ASSERT(node != nullptr); - - switch (node->Type()) { - case ir::AstNodeType::VARIABLE_DECLARATOR: - case ir::AstNodeType::SWITCH_STATEMENT: - case ir::AstNodeType::CALL_EXPRESSION: - case ir::AstNodeType::BINARY_EXPRESSION: - case ir::AstNodeType::ASSIGNMENT_EXPRESSION: - case ir::AstNodeType::UNARY_EXPRESSION: - case ir::AstNodeType::UPDATE_EXPRESSION: - case ir::AstNodeType::MEMBER_EXPRESSION: { - if (!program_->NodeContainsETSNolint(node, ETSWarnings::ETS_IMPLICIT_BOXING_UNBOXING)) { - node->Iterate([this](auto *childNode) { CheckTypeOfBoxingUnboxing(childNode); }); - } - break; - } - default: { - break; - } - } - - node->Iterate([&](auto *childNode) { ETSWarningImplicitBoxingUnboxing(childNode); }); -} - void ETSWarningAnalyzer::LogWarning(const diagnostic::DiagnosticKind &diagnostic, const lexer::SourcePosition &position) const { diff --git a/ets2panda/checker/ets/etsWarningAnalyzer.h b/ets2panda/checker/ets/etsWarningAnalyzer.h index 6a41550de33ac2945b21cec67b04b6e18a7fa880..8bfecef62796f19c4c764671e35cbc7b11fdb5fd 100644 --- a/ets2panda/checker/ets/etsWarningAnalyzer.h +++ b/ets2panda/checker/ets/etsWarningAnalyzer.h @@ -48,9 +48,6 @@ public: case ETSWarnings::ETS_REMOVE_LAMBDA: ETSWarningRemoveLambda(node); break; - case ETSWarnings::ETS_IMPLICIT_BOXING_UNBOXING: - ETSWarningImplicitBoxingUnboxing(node); - break; default: break; } @@ -63,12 +60,8 @@ private: void AnalyzeClassDefForFinalModifier(const ir::ClassDefinition *classDef); void AnalyzeClassMethodForFinalModifier(const ir::MethodDefinition *methodDef, const ir::ClassDefinition *classDef); - void CheckTypeOfBoxing(const ir::AstNode *node); - void CheckTypeOfUnboxing(const ir::AstNode *node); void CheckTopLevelExpressions(const ir::Expression *expression); void CheckProhibitedTopLevelStatements(const ir::Statement *statement); - std::string GetBoxingUnboxingType(const ir::AstNode *node); - void CheckTypeOfBoxingUnboxing(const ir::AstNode *node); void ETSWarningAnnotationUnusedGenericAliasWarn(const ir::AstNode *node); void ETSWarningSuggestFinal(const ir::AstNode *node); @@ -76,7 +69,6 @@ private: void ETSWarningBoostEqualityStatement(const ir::AstNode *node); void ETSWarningRemoveAsync(const ir::AstNode *node); void ETSWarningRemoveLambda(const ir::AstNode *node); - void ETSWarningImplicitBoxingUnboxing(const ir::AstNode *node); parser::Program *program_; util::DiagnosticEngine &diagnosticEngine_; diff --git a/ets2panda/checker/ets/function.cpp b/ets2panda/checker/ets/function.cpp index 6556eadc3faf4cc4012a55b81a31baf93000ef15..6f65e600f6800813cb3465f7ffeedcdc21b01117 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -16,12 +16,15 @@ #include "checker/types/ets/etsResizableArrayType.h" #include "checker/types/ets/etsTupleType.h" #include "generated/signatures.h" +#include "checker/ets/wideningConverter.h" #include "varbinder/ETSBinder.h" #include "checker/ETSchecker.h" #include "checker/ets/function_helpers.h" #include "checker/ets/typeRelationContext.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" +#include "checker/types/typeError.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/base/catchClause.h" #include "ir/base/classDefinition.h" @@ -54,6 +57,7 @@ #include "ir/ts/tsTypeParameterInstantiation.h" #include "parser/program/program.h" #include "util/helpers.h" +#include "util/nameMangler.h" #include @@ -81,8 +85,8 @@ bool ETSChecker::IsCompatibleTypeArgument(ETSTypeParameter *typeParam, Type *typ bool ETSChecker::EnhanceSubstitutionForReadonly(const ArenaVector &typeParams, ETSReadonlyType *paramType, Type *argumentType, Substitution *substitution) { - return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying(), GetReadonlyType(argumentType), - substitution); + return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying()->MaybeBaseTypeOfGradualType(), + GetReadonlyType(argumentType), substitution); } /* A very rough and imprecise partial type inference */ @@ -115,6 +119,10 @@ bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector &typeParam if (paramType->IsETSReadonlyType()) { return EnhanceSubstitutionForReadonly(typeParams, paramType->AsETSReadonlyType(), argumentType, substitution); } + if (paramType->IsGradualType()) { + return EnhanceSubstitutionForType(typeParams, paramType->AsGradualType()->GetBaseType(), argumentType, + substitution); + } if (paramType->IsETSUnionType()) { return EnhanceSubstitutionForUnion(typeParams, paramType->AsETSUnionType(), argumentType, substitution); } @@ -152,7 +160,8 @@ bool ETSChecker::EnhanceSubstitutionForUnion(const ArenaVector &typePara if (!argumentType->IsETSUnionType()) { bool foundValid = false; for (Type *ctype : paramUn->ConstituentTypes()) { - foundValid |= ValidateTypeSubstitution(typeParams, ctype, argumentType, substitution); + foundValid |= + ValidateTypeSubstitution(typeParams, ctype->MaybeBaseTypeOfGradualType(), argumentType, substitution); } return foundValid; } @@ -230,7 +239,7 @@ static void ResetInferredNode(ETSChecker *checker) auto resetFuncState = [](ir::ArrowFunctionExpression *expr) { auto *func = expr->Function(); func->SetSignature(nullptr); - func->ReturnStatements().clear(); + func->ClearReturnStatements(); expr->SetTsType(nullptr); }; @@ -371,7 +380,7 @@ bool ETSChecker::CheckOptionalLambdaFunction(ir::Expression *argument, Signature auto *const arrowFuncExpr = argument->AsArrowFunctionExpression(); if (ir::ScriptFunction *const lambda = arrowFuncExpr->Function(); - CheckLambdaAssignable(substitutedSig->Function()->Params()[index], lambda)) { + CheckLambdaAssignable(substitutedSig->Params()[index]->Declaration()->Node()->AsExpression(), lambda)) { return true; } } @@ -379,10 +388,11 @@ bool ETSChecker::CheckOptionalLambdaFunction(ir::Expression *argument, Signature return false; } -bool ETSChecker::ValidateArgumentAsIdentifier(const ir::Identifier *identifier) +static bool IsInvalidArgumentAsIdentifier(varbinder::Scope *scope, const ir::Identifier *identifier) { - auto result = Scope()->Find(identifier->Name()); - return result.variable != nullptr && (result.variable->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)); + auto result = scope->Find(identifier->Name()); + return result.variable != nullptr && (result.variable->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE | + varbinder::VariableFlags::TYPE_ALIAS)); } static void ClearPreferredTypeForArray(checker::ETSChecker *checker, ir::Expression *argument, Type *paramType, @@ -390,13 +400,13 @@ static void ClearPreferredTypeForArray(checker::ETSChecker *checker, ir::Express { if (argument->IsArrayExpression()) { // fixed array and resizeable array will cause problem here, so clear it. - argument->AsArrayExpression()->ClearPreferredType(); + argument->AsArrayExpression()->CleanCheckInformation(); argument->AsArrayExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else if (argument->IsETSNewArrayInstanceExpression()) { - argument->AsETSNewArrayInstanceExpression()->ClearPreferredType(); + argument->AsETSNewArrayInstanceExpression()->CleanCheckInformation(); argument->AsETSNewArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else if (argument->IsETSNewMultiDimArrayInstanceExpression()) { - argument->AsETSNewMultiDimArrayInstanceExpression()->ClearPreferredType(); + argument->AsETSNewMultiDimArrayInstanceExpression()->CleanCheckInformation(); argument->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(checker, paramType, flags); } else { @@ -407,13 +417,34 @@ static void ClearPreferredTypeForArray(checker::ETSChecker *checker, ir::Express } } +static bool CheckArrowFunctionParamIfNeeded(ETSChecker *checker, Signature *substitutedSig, + const ArenaVector &arguments, TypeRelationFlag flags) +{ + if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && arguments.back()->IsArrowFunctionExpression()) { + ir::ScriptFunction *const lambda = arguments.back()->AsArrowFunctionExpression()->Function(); + auto targetParm = substitutedSig->GetSignatureInfo()->params.back()->Declaration()->Node(); + if (!checker->CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda)) { + return false; + } + } + return true; +} + +// Note: (Issue27688) if lambda is trailing lambda transferred, it must be in recheck. +// in signature matching, foo(()=>void) should be the same with foo() {} +static bool HasTransferredTrailingLambda(const ArenaVector &arguments) +{ + return !arguments.empty() && arguments.back()->IsArrowFunctionExpression() && + arguments.back()->AsArrowFunctionExpression()->Function()->IsTrailingLambda(); +} + // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP, G.FUD.05) solid logic bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, const ArenaVector &arguments, TypeRelationFlag flags, const std::vector &argTypeInferenceRequired, bool reportError) { auto commonArity = std::min(arguments.size(), substitutedSig->ArgCount()); - if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) { + if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 || HasTransferredTrailingLambda(arguments)) { if (commonArity == 0) { ES2PANDA_ASSERT(substitutedSig->GetSignatureInfo()->params.empty()); return true; @@ -424,14 +455,17 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, auto &argument = arguments[index]; // #22952: infer optional parameter heuristics - auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + auto const paramType = + GetNonNullishType(substitutedSig->Params()[index]->TsType())->MaybeBaseTypeOfGradualType(); if (argument->IsObjectExpression()) { ES2PANDA_ASSERT(paramType != nullptr); - if (paramType->IsETSObjectType()) { - // No chance to check the argument at this point - continue; + if (!paramType->IsETSObjectType()) { + return false; } - return false; + if (paramType->AsETSObjectType()->IsBoxedPrimitive()) { + return false; + } + argument->SetPreferredType(paramType); } if (argument->IsMemberExpression()) { @@ -441,6 +475,9 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, LogError(diagnostic::SPREAD_ONTO_SINGLE_PARAM, {}, argument->Start()); } return false; + } else if (argument->IsNumberLiteral()) { + argument->SetTsType(nullptr); + argument->SetPreferredType(paramType); } if (argTypeInferenceRequired[index]) { @@ -448,6 +485,7 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, // Note: If the signatures are from lambdas, then they have no `Function`. ir::ScriptFunction *const lambda = argument->AsArrowFunctionExpression()->Function(); auto targetParm = substitutedSig->GetSignatureInfo()->params[index]->Declaration()->Node(); + ERROR_SANITY_CHECK(this, targetParm->IsETSParameterExpression(), return false); if (CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda)) { continue; } @@ -456,7 +494,7 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, ClearPreferredTypeForArray(this, argument, paramType, flags, false); - if (argument->IsIdentifier() && ValidateArgumentAsIdentifier(argument->AsIdentifier())) { + if (argument->IsIdentifier() && IsInvalidArgumentAsIdentifier(Scope(), argument->AsIdentifier())) { LogError(diagnostic::ARG_IS_CLASS_ID, {}, argument->Start()); return false; } @@ -467,14 +505,7 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, } } - if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && arguments.back()->IsArrowFunctionExpression()) { - ir::ScriptFunction *const lambda = arguments.back()->AsArrowFunctionExpression()->Function(); - auto targetParm = substitutedSig->GetSignatureInfo()->params.back()->Declaration()->Node(); - if (!CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda)) { - return false; - } - } - return true; + return CheckArrowFunctionParamIfNeeded(this, substitutedSig, arguments, flags); } bool ETSChecker::ValidateSignatureInvocationContext(Signature *substitutedSig, ir::Expression *argument, @@ -484,7 +515,7 @@ bool ETSChecker::ValidateSignatureInvocationContext(Signature *substitutedSig, i // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *argumentType = argument->Check(this); - flags |= TypeRelationFlag::ONLY_CHECK_WIDENING; + flags |= (TypeRelationFlag::ONLY_CHECK_WIDENING); auto const invocationCtx = checker::InvocationContext(Relation(), argument, argumentType, targetType, argument->Start(), @@ -496,20 +527,29 @@ bool ETSChecker::ValidateSignatureInvocationContext(Signature *substitutedSig, i bool ETSChecker::IsValidRestArgument(ir::Expression *const argument, Signature *const substitutedSig, const TypeRelationFlag flags, const std::size_t index) { + auto *restParamType = substitutedSig->RestVar()->TsType(); + if (restParamType->IsETSTupleType()) { + return false; + } if (argument->IsObjectExpression()) { + argument->SetPreferredType(GetElementTypeOfArray(restParamType)); // Object literals should be checked separately afterwards after call resolution return true; } - const auto argumentType = argument->Check(this); - auto *restParam = substitutedSig->RestVar()->TsType(); - if (restParam->IsETSTupleType()) { - return false; + + // Set preferred type for array expressions before checking, similar to spread elements + if (argument->IsArrayExpression()) { + if (!SetPreferredTypeForArrayArgument(argument->AsArrayExpression(), substitutedSig)) { + return false; + } } + + const auto argumentType = argument->Check(this); if (argument->HasAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST)) { return true; } - auto targetType = GetElementTypeOfArray(restParam); + auto targetType = GetElementTypeOfArray(restParamType); if (substitutedSig->OwnerVar() == nullptr) { targetType = MaybeBoxType(targetType); } @@ -517,7 +557,35 @@ bool ETSChecker::IsValidRestArgument(ir::Expression *const argument, Signature * Relation(), argument, argumentType, targetType, argument->Start(), {{diagnostic::REST_PARAM_INCOMPAT_AT, {argumentType, targetType, index + 1}}}, flags); - return invocationCtx.IsInvocable(); + bool result = invocationCtx.IsInvocable(); + // Clear preferred type if invocation fails, similar to spread elements + if (!result && argument->IsArrayExpression()) { + ModifyPreferredType(argument->AsArrayExpression(), nullptr); + } + + return result; +} + +bool ETSChecker::SetPreferredTypeForArrayArgument(ir::ArrayExpression *arrayExpr, Signature *substitutedSig) +{ + auto *const restVarType = substitutedSig->RestVar()->TsType(); + if (!restVarType->IsETSArrayType() && !restVarType->IsETSResizableArrayType()) { + return true; + } + auto targetType = GetElementTypeOfArray(restVarType); + if (substitutedSig->OwnerVar() == nullptr) { + targetType = MaybeBoxType(targetType); + } + // Validate tuple size before setting preferred type + if (targetType->IsETSTupleType()) { + auto *tupleType = targetType->AsETSTupleType(); + if (tupleType->GetTupleSize() != arrayExpr->Elements().size()) { + // Size mismatch - don't set preferred type, this will cause a type error + return false; + } + } + arrayExpr->SetPreferredType(targetType); + return true; } bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const ArenaVector &arguments, @@ -552,9 +620,7 @@ bool ETSChecker::ValidateSignatureRestParams(Signature *substitutedSig, const Ar Type *targetType = substitutedSig->RestVar()->TsType(); // backing out of check that results in a signature mismatch would be difficult // so only attempt it if there is only one candidate signature - if (restArgument->IsArrayExpression()) { - restArgument->AsArrayExpression()->SetPreferredType(targetType); - } + restArgument->SetPreferredType(targetType); auto const argumentType = restArgument->Check(this); auto const invocationCtx = checker::InvocationContext( @@ -582,7 +648,9 @@ Signature *ETSChecker::ValidateSignature( // setting the boxing/unboxing flag for the arguments if needed. // So handle substitution arguments only in the case of unique function or collecting signature phase. Signature *const signature = - MaybeSubstituteTypeParameters(this, baseSignature, typeArguments, arguments, pos, flags); + ((flags & TypeRelationFlag::NO_SUBSTITUTION_NEEDED) != 0U) + ? baseSignature + : MaybeSubstituteTypeParameters(this, baseSignature, typeArguments, arguments, pos, flags); if (signature == nullptr) { return nullptr; } @@ -679,6 +747,7 @@ std::array GetFlagVariants() }; } +// CC-OFFNXT(huge_method) solid logic ArenaVector ETSChecker::CollectSignatures(ArenaVector &signatures, const ir::TSTypeParameterInstantiation *typeArguments, const ArenaVector &arguments, @@ -688,12 +757,20 @@ ArenaVector ETSChecker::CollectSignatures(ArenaVector std::vector argTypeInferenceRequired = FindTypeInferenceArguments(arguments); Signature *notVisibleSignature = nullptr; + if (signatures.size() > 1) { + resolveFlags |= TypeRelationFlag::OVERLOADING_CONTEXT; + } + auto collectSignatures = [&](TypeRelationFlag relationFlags) { for (auto *sig : signatures) { if (notVisibleSignature != nullptr && !IsSignatureAccessible(sig, Context().ContainingClass(), Relation())) { continue; } + if (sig->HasSignatureFlag(SignatureFlags::BRIDGE)) { + // Bridges are never invoked direcly + continue; + } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *concreteSig = ValidateSignature(std::make_tuple(sig, typeArguments, relationFlags), arguments, pos, argTypeInferenceRequired, signatures.size() == 1); @@ -731,20 +808,6 @@ ArenaVector ETSChecker::CollectSignatures(ArenaVector return compatibleSignatures; } -static void ClearUnboxingFlags(TypeRelation *relation, Signature *sig, ir::Expression *argument, size_t index) -{ - auto identical = relation->IsIdenticalTo(sig->Params()[index]->TsType(), argument->TsType()); - // NOTE(gaborarontakacs): The unboxing flag, which was added due to overloading, needs to be removed when it's - // unnecessary for the most specific signature. - // Do not remove the flag for tuples, e.g., `let a = [21 as Number] as [number]`, - // because unboxing will be executed later during the function call in this case. - // This condition may be removed after refactoring primitive types. - if (identical && argument->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG) && - !(argument->IsMemberExpression() && argument->AsMemberExpression()->Object()->TsType()->IsETSTupleType())) { - argument->RemoveBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG); - } -} - static void UpdateArrayArgsAndUnboxingFlags(ETSChecker *checker, Signature *sig, const ArenaVector &arguments) { @@ -755,7 +818,6 @@ static void UpdateArrayArgsAndUnboxingFlags(ETSChecker *checker, Signature *sig, auto flags = TypeRelationFlag::NO_THROW | TypeRelationFlag::BOXING | TypeRelationFlag::UNBOXING | TypeRelationFlag::WIDENING; ClearPreferredTypeForArray(checker, argument, paramType, flags, true); - ClearUnboxingFlags(checker->Relation(), sig, argument, index); } } @@ -834,7 +896,18 @@ Signature *ETSChecker::ValidateSignatures(ArenaVector &signatures, auto compatibleSignatures = CollectSignatures(signatures, typeArguments, arguments, pos, resolveFlags); if (!compatibleSignatures.empty()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return GetMostSpecificSignature(compatibleSignatures, arguments, pos, resolveFlags); + auto *sig = GetMostSpecificSignature(compatibleSignatures, arguments, pos, resolveFlags); + // NOTE (DZ): skip Promise constructor/then/catch check - + // temporary solution, need to be removed after fixing OHOS code! + if (sig == nullptr || !sig->HasFunction() || + !(sig->Function()->IsConstructor() || sig->Function()->Id()->Name().Is("then") || + sig->Function()->Id()->Name().Is("catch")) || + !sig->Owner()->Name().Is("Promise")) { + // May need to re-check the arguments now that we know the particular signature to call. + ValidateSignature({sig, nullptr, TypeRelationFlag::WIDENING | TypeRelationFlag::NO_SUBSTITUTION_NEEDED}, + arguments, pos, FindTypeInferenceArguments(arguments), true); + } + return sig; } if ((resolveFlags & TypeRelationFlag::NO_THROW) == 0) { @@ -858,6 +931,8 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector return true; }; + auto isGeneric = [](const Signature *sig) { return sig->TypeParams().empty(); }; + Signature *result = nullptr; size_t currentMinLength = SIZE_MAX; @@ -885,8 +960,15 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector } else if (candidateLength < currentLength) { result = candidate; // Shorter parameter count wins currentMinLength = result->Function()->Params().size(); - } else if (candidateLength == currentLength) { - // Ambiguous resolution for same-length params + } else if (candidateLength >= currentLength) { + continue; + // NOTE (smartin): all other cases below are unreachable code + } else if (!isGeneric(candidate) && isGeneric(result)) { + result = candidate; + } else if (isGeneric(candidate) && !isGeneric(result)) { + continue; + } else { + // Ambiguous resolution for same-length params, same genericity if (result->Owner() == candidate->Owner()) { result = nullptr; } @@ -896,45 +978,121 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector return result; } -static Type *GetParatmeterTypeOrRestAtIdx(checker::ETSChecker *checker, Signature *sig, const size_t idx) +static Type *GetParameterTypeOrRestAtIdx(checker::ETSChecker *checker, Signature *sig, const size_t idx) { - return idx < sig->ArgCount() ? sig->Params().at(idx)->TsType() - : checker->GetElementTypeOfArray(sig->RestVar()->TsType()); + return idx < sig->ArgCount() + ? sig->Params().at(idx)->TsType()->MaybeBaseTypeOfGradualType() + : checker->GetElementTypeOfArray(sig->RestVar()->TsType())->MaybeBaseTypeOfGradualType(); } -static void InitMostSpecificType(checker::ETSChecker *checker, const ArenaVector &signatures, - [[maybe_unused]] Type *&mostSpecificType, [[maybe_unused]] Signature *&prevSig, - const size_t idx) +static void InitMostSpecificType(TypeRelation *relation, const ArenaVector &signatures, + Type *&mostSpecificType, Signature *&prevSig, const size_t idx) { + // Attempt to choose the widest type of available ones + SavedTypeRelationFlagsContext ctx {relation, TypeRelationFlag::WIDENING | TypeRelationFlag::ONLY_CHECK_WIDENING}; + auto checker = relation->GetChecker()->AsETSChecker(); for (auto *sig : signatures) { - if (Type *sigType = GetParatmeterTypeOrRestAtIdx(checker, sig, idx); - sigType->IsETSObjectType() && !sigType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + Type *sigType = GetParameterTypeOrRestAtIdx(checker, sig, idx); + relation->Result(false); + + if (sigType->IsETSObjectType()) { + if (sigType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + continue; + } + if (!sigType->AsETSObjectType()->IsBoxedPrimitive()) { + // Found "non-primitive" ref type + mostSpecificType = sigType; + prevSig = sig; + return; + } + relation->SetNode(prevSig->Function()->Params()[idx]->AsETSParameterExpression()); + if (relation->IsLegalBoxedPrimitiveConversion(sigType, mostSpecificType)) { + mostSpecificType = sigType; + prevSig = sig; + continue; + } + } + if (sigType->IsETSFunctionType() && relation->IsSupertypeOf(sigType, mostSpecificType)) { mostSpecificType = sigType; prevSig = sig; - return; + continue; + } + relation->Result(false); + WideningConverter(checker, relation, sigType, mostSpecificType); + if (relation->IsTrue()) { + mostSpecificType = sigType; + prevSig = sig; + continue; } } } +void ETSChecker::CheckAmbiguousCall(Type *&mostSpecificType, Type *sigType, Signature *prevSig, Signature *sig, + const lexer::SourcePosition &pos) +{ + if (((sigType->IsETSObjectType() && mostSpecificType->IsETSObjectType()) || + (sigType->IsETSUnionType() && mostSpecificType->IsETSUnionType() && + ((Relation()->IsSupertypeOf(sigType->AsETSUnionType(), GlobalETSNullType()) && + Relation()->IsSupertypeOf(mostSpecificType->AsETSUnionType(), GlobalETSNullType())) || + // CC-OFFNXT(G.FMT.02-CPP) project code style + (Relation()->IsSupertypeOf(sigType->AsETSUnionType(), GlobalETSUndefinedType()) && + Relation()->IsSupertypeOf(mostSpecificType->AsETSUnionType(), GlobalETSUndefinedType()))))) && + !Relation()->IsAssignableTo(mostSpecificType, sigType) && + !Relation()->IsLegalBoxedPrimitiveConversion(sigType, mostSpecificType)) { + auto funcName = sig->Function()->Id()->Name(); + LogError(diagnostic::AMBIGUOUS_CALL, {funcName, funcName, funcName, prevSig, funcName, sig}, pos); + } +} + void ETSChecker::SearchAmongMostSpecificTypes(Type *&mostSpecificType, Signature *&prevSig, std::tuple info, bool lookForClassType) { auto [pos, idx, sig] = info; - Type *sigType = GetParatmeterTypeOrRestAtIdx(this, sig, idx); + Type *sigType = GetParameterTypeOrRestAtIdx(this, sig, idx); + if (prevSig->Function()->Params()[idx]->IsETSParameterExpression()) { + Relation()->SetNode(prevSig->Function()->Params()[idx]->AsETSParameterExpression()); + } const bool isClassType = sigType->IsETSObjectType() && !sigType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE); if (isClassType == lookForClassType) { if (Relation()->IsIdenticalTo(sigType, mostSpecificType)) { + Relation()->SetNode(nullptr); + return; + } + + if (idx >= prevSig->MinArgCount() && idx < sig->MinArgCount()) { + // NOTE (smartin): prefer non-optional parameters over optional ones + Relation()->Result(true); + mostSpecificType = sigType; + prevSig = sig; return; } + + if (isClassType && sigType->AsETSObjectType()->IsBoxedPrimitive() && mostSpecificType->IsETSObjectType() && + mostSpecificType->AsETSObjectType()->IsBoxedPrimitive()) { + // NOTE (smartin): when a param with type int is available, make it more specific than other primitive + // types. The making of correct rules for this is still in progress in spec, so this is a temp solution. + if (mostSpecificType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_INT)) { + return; + } + + TypeRelationFlag flags = TypeRelationFlag::NO_THROW | TypeRelationFlag::UNBOXING | + TypeRelationFlag::BOXING | TypeRelationFlag::WIDENING; + Relation()->SetFlags(flags); + if (sigType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_INT) || + Relation()->IsLegalBoxedPrimitiveConversion(mostSpecificType, sigType)) { + Relation()->Result(true); + mostSpecificType = sigType; + prevSig = sig; + return; + } + } if (Relation()->IsAssignableTo(sigType, mostSpecificType)) { mostSpecificType = sigType; prevSig = sig; - } else if (sigType->IsETSObjectType() && mostSpecificType->IsETSObjectType() && - !Relation()->IsAssignableTo(mostSpecificType, sigType)) { - auto funcName = sig->Function()->Id()->Name(); - LogError(diagnostic::AMBIGUOUS_CALL, {funcName, funcName, funcName, prevSig, funcName, sig}, pos); + } else { + CheckAmbiguousCall(mostSpecificType, sigType, prevSig, sig, pos); } } } @@ -979,8 +1137,9 @@ void ETSChecker::CollectSuitableSignaturesForTypeInference( } for (auto *sig : signatures) { - if (paramIdx >= sig->Params().size() || !sig->Params().at(paramIdx)->TsType()->IsETSObjectType() || - !sig->Params().at(paramIdx)->TsType()->AsETSObjectType()->IsGlobalETSObjectType()) { + auto paramType = sig->Params().at(paramIdx)->TsType()->MaybeBaseTypeOfGradualType(); + if (paramIdx >= sig->Params().size() || !paramType->IsETSObjectType() || + !paramType->AsETSObjectType()->IsGlobalETSObjectType()) { bestSignaturesForParameter.insert({paramIdx, sig}); } } @@ -1011,7 +1170,9 @@ ArenaMultiMap ETSChecker::GetSuitableSignaturesForParameter Type *mostSpecificType = signatures.front()->Params().at(i)->TsType(); Signature *prevSig = signatures.front(); - InitMostSpecificType(this, signatures, mostSpecificType, prevSig, i); + // NOTE: first we choose the some signature with possibly widest argumetns' types + // Then we search for the most specific signature + InitMostSpecificType(Relation(), signatures, mostSpecificType, prevSig, i); for (auto *sig : signatures) { SearchAmongMostSpecificTypes(mostSpecificType, prevSig, std::make_tuple(pos, i, sig), true); } @@ -1020,8 +1181,9 @@ ArenaMultiMap ETSChecker::GetSuitableSignaturesForParameter } for (auto *sig : signatures) { - Type *sigType = GetParatmeterTypeOrRestAtIdx(this, sig, i); - if (Relation()->IsIdenticalTo(sigType, mostSpecificType)) { + Type *sigType = GetParameterTypeOrRestAtIdx(this, sig, i); + if (Relation()->IsIdenticalTo(sigType, mostSpecificType) || + (sigType->IsETSFunctionType() && Relation()->IsSupertypeOf(sigType, mostSpecificType))) { bestSignaturesForParameter.insert({i, sig}); } } @@ -1111,7 +1273,7 @@ Signature *ETSChecker::ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpres auto *candidateFunctionType = sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType(); auto *currentReceiver = candidateFunctionType->Params()[0]; - trailingLambda->Function()->Params().emplace_back(currentReceiver); + trailingLambda->Function()->EmplaceParams(currentReceiver); sigContainLambdaWithReceiverAsParam.emplace_back(sig); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) signature = ValidateSignatures(sigContainLambdaWithReceiverAsParam, callExpr->TypeParams(), arguments, @@ -1121,7 +1283,7 @@ Signature *ETSChecker::ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpres return signature; } sigContainLambdaWithReceiverAsParam.clear(); - trailingLambda->Function()->Params().clear(); + trailingLambda->Function()->ClearParams(); } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return ValidateSignatures(normalSig, callExpr->TypeParams(), arguments, callExpr->Start(), "call", @@ -1161,9 +1323,36 @@ void ETSChecker::UpdateDeclarationFromSignature(ir::CallExpression *expr, checke auto newDecl = allocator->New(allocator, sigName, declNode); auto newVar = allocator->New(newDecl, varbinder::VariableFlags::METHOD | varbinder::VariableFlags::SYNTHETIC); + newVar->SetTsType(declNode->AsMethodDefinition()->TsType()->Clone(this)); callIdentifier->SetVariable(newVar); } +Signature *ETSChecker::MakeSignatureInvocable(Signature *sig, ir::CallExpression *callExpr) +{ + if (sig == nullptr) { + return nullptr; + } + std::size_t const argumentCount = callExpr->Arguments().size(); + std::size_t const parameterCount = sig->Params().size(); + auto count = std::min(parameterCount, argumentCount); + for (std::size_t idx = 0; idx < count; ++idx) { + // Kludge to make promise code compile + if (callExpr->Arguments().at(idx)->IsArrowFunctionExpression()) { + continue; + } + + auto ctx = checker::AssignmentContext( + Relation(), callExpr->Arguments().at(idx), callExpr->Arguments().at(idx)->TsType(), + sig->Params().at(idx)->TsType(), callExpr->Arguments().at(idx)->Start(), + {{diagnostic::INVALID_ASSIGNMNENT, + {callExpr->Arguments().at(idx)->TsType(), sig->Params().at(idx)->TsType()}}}); + if (!ctx.IsAssignable()) { + return nullptr; + } + } + return sig; +} + Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, const lexer::SourcePosition &pos, @@ -1173,6 +1362,7 @@ Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVectorTypeParams(), callExpr->Arguments(), pos, "call", reportFlag); + sig = MakeSignatureInvocable(sig, callExpr); UpdateDeclarationFromSignature(callExpr, sig); return sig; } @@ -1193,6 +1383,7 @@ Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVectorTypeParams(), callExpr->Arguments(), pos, "call", reportFlag); + sig = MakeSignatureInvocable(sig, callExpr); if (sig != nullptr) { EnsureValidCurlyBrace(callExpr); } @@ -1204,6 +1395,15 @@ Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVector &arguments, const lexer::SourcePosition &pos) { + auto *var = type->GetProperty(compiler::Signatures::CONSTRUCTOR_NAME, PropertySearchFlags::SEARCH_STATIC_METHOD); + if (var != nullptr && var->TsType()->IsETSFunctionType()) { + auto sig = MatchOrderSignatures(var->TsType()->AsETSFunctionType()->CallSignatures(), nullptr, arguments, pos, + TypeRelationFlag::NONE); + if (sig == nullptr) { + ThrowOverloadMismatch(type->Name(), arguments, pos, "construct"); + } + return sig; + } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return ValidateSignatures(type->ConstructSignatures(), nullptr, arguments, pos, "construct"); } @@ -1229,7 +1429,7 @@ void ETSChecker::CheckObjectLiteralArguments(Signature *signature, ArenaVectorParams()[index]->TsType()); } - arguments[index]->AsObjectExpression()->SetPreferredType(tp); + arguments[index]->SetPreferredType(tp); arguments[index]->Check(this); } } @@ -1237,11 +1437,15 @@ void ETSChecker::CheckObjectLiteralArguments(Signature *signature, ArenaVectorGetOverloadInfo(); + ir::OverloadInfo &ldInfo = method->GetOverloadInfoForUpdate(); ArenaVector overloads(checker->ProgramAllocator()->Adapter()); for (ir::MethodDefinition *const currentFunc : method->Overloads()) { - ldInfo.isDeclare &= currentFunc->IsDeclare(); + if (currentFunc->IsDeclare() != ldInfo.isDeclare) { + checker->LogError(diagnostic::AMBIGUOUS_AMBIENT, {currentFunc->Id()->Name()}, currentFunc->Start()); + method->Id()->Variable()->SetTsType(checker->GlobalTypeError()); + return false; + } ES2PANDA_ASSERT(currentFunc->Function() != nullptr); ES2PANDA_ASSERT(currentFunc->Id() != nullptr); currentFunc->Function()->Id()->SetVariable(currentFunc->Id()->Variable()); @@ -1252,11 +1456,16 @@ static bool CollectOverload(checker::ETSChecker *checker, ir::MethodDefinition * methodId->Variable()->SetTsType(checker->GlobalTypeError()); return false; } - auto *const overloadType = checker->BuildMethodType(currentFunc->Function()); + + auto *const overloadType = currentFunc->TsType() != nullptr ? currentFunc->TsType()->AsETSFunctionType() + : checker->BuildMethodType(currentFunc->Function()); ldInfo.needHelperOverload |= checker->CheckIdenticalOverloads(funcType, overloadType, currentFunc, ldInfo.isDeclare); - currentFunc->SetTsType(overloadType); + if (currentFunc->TsType() == nullptr) { + currentFunc->SetTsType(overloadType); + } + auto overloadSig = currentFunc->Function()->Signature(); funcType->AddCallSignature(overloadSig); if (overloadSig->IsExtensionAccessor()) { @@ -1292,6 +1501,9 @@ checker::Type *ETSChecker::BuildMethodSignature(ir::MethodDefinition *method) auto *methodId = method->Id(); ES2PANDA_ASSERT(methodId != nullptr); ES2PANDA_ASSERT(method->Function() != nullptr); + if (methodId->AsIdentifier()->IsErrorPlaceHolder()) { + return methodId->Variable()->SetTsType(GlobalTypeError()); + } method->Function()->Id()->SetVariable(methodId->Variable()); BuildFunctionSignature(method->Function(), method->IsConstructor()); if (method->Function()->Signature() == nullptr) { @@ -1302,7 +1514,7 @@ checker::Type *ETSChecker::BuildMethodSignature(ir::MethodDefinition *method) if (!CollectOverload(this, method, funcType)) { return GlobalTypeError(); } - ir::OverloadInfo &ldInfo = method->GetOverloadInfo(); + ir::OverloadInfo &ldInfo = method->GetOverloadInfoForUpdate(); ldInfo.needHelperOverload &= ldInfo.isDeclare; if (ldInfo.needHelperOverload) { @@ -1313,7 +1525,8 @@ checker::Type *ETSChecker::BuildMethodSignature(ir::MethodDefinition *method) } bool ETSChecker::CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType *overload, - const ir::MethodDefinition *const currentFunc, bool omitSameAsm) + const ir::MethodDefinition *const currentFunc, bool omitSameAsm, + TypeRelationFlag relationFlags) { // Don't necessary to check overload for invalid functions if (func->Name().Is(ERROR_LITERAL)) { @@ -1321,7 +1534,7 @@ bool ETSChecker::CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType return false; } - SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK); + SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), relationFlags); Relation()->SignatureIsIdenticalTo(func->CallSignatures()[0], overload->CallSignatures()[0]); if (Relation()->IsTrue() && func->CallSignatures()[0]->GetSignatureInfo()->restVar == @@ -1421,8 +1634,11 @@ static bool AppendSignatureInfoParam(ETSChecker *checker, SignatureInfo *sigInfo if (!param->IsOptional()) { ++sigInfo->minArgCount; } - ES2PANDA_ASSERT(!param->IsOptional() || param->Ident()->TsType()->IsTypeError() || - checker->Relation()->IsSupertypeOf(param->Ident()->TsType(), checker->GlobalETSUndefinedType())); + ERROR_SANITY_CHECK( + checker, + !param->IsOptional() || param->Ident()->TsType()->IsTypeError() || + checker->Relation()->IsSupertypeOf(param->Ident()->TsType(), checker->GlobalETSUndefinedType()), + return false); return true; } @@ -1460,8 +1676,7 @@ SignatureInfo *ETSChecker::ComposeSignatureInfo(ir::TSTypeParameterDeclaration * return nullptr; } ES2PANDA_ASSERT(restParamType != nullptr); - if (!restParamType->IsETSTupleType() && !restParamType->IsETSArrayType() && - !restParamType->IsETSResizableArrayType()) { + if (!restParamType->IsAnyETSArrayOrTupleType()) { LogError(diagnostic::ONLY_ARRAY_OR_TUPLE_FOR_REST, {}, param->Start()); return nullptr; } @@ -1498,6 +1713,10 @@ void ETSChecker::BuildFunctionSignature(ir::ScriptFunction *func, bool isConstru { ES2PANDA_ASSERT(func != nullptr); bool isArrow = func->IsArrow(); + // note(Ekko): For extenal function overload, need to not change ast tree, for arrow type, need perferred type. + if (func->Signature() != nullptr && !isArrow) { + return; + } auto *nameVar = isArrow ? nullptr : func->Id()->Variable(); auto funcName = nameVar == nullptr ? util::StringView() : nameVar->Name(); @@ -1545,13 +1764,8 @@ checker::ETSFunctionType *ETSChecker::BuildMethodType(ir::ScriptFunction *func) ES2PANDA_ASSERT(!func->IsArrow()); ES2PANDA_ASSERT(func != nullptr); auto *nameVar = func->Id()->Variable(); - ETSFunctionType *funcType; - if (func->IsDynamic()) { - funcType = CreateETSDynamicMethodType(nameVar->Name(), {{func->Signature()}, ProgramAllocator()->Adapter()}, - func->Language()); - } else { - funcType = CreateETSMethodType(nameVar->Name(), {{func->Signature()}, ProgramAllocator()->Adapter()}); - } + ETSFunctionType *funcType = + CreateETSMethodType(nameVar->Name(), {{func->Signature()}, ProgramAllocator()->Adapter()}); funcType->SetVariable(nameVar); return funcType; } @@ -1679,14 +1893,14 @@ Signature *ETSChecker::AdjustForTypeParameters(Signature *source, Signature *tar if (sourceTypeParams.empty()) { return target; } - auto *substitution = NewSubstitution(); + auto substitution = Substitution {}; for (size_t ix = 0; ix < sourceTypeParams.size(); ix++) { if (!targetTypeParams[ix]->IsETSTypeParameter()) { continue; } - EmplaceSubstituted(substitution, targetTypeParams[ix]->AsETSTypeParameter(), sourceTypeParams[ix]); + EmplaceSubstituted(&substitution, targetTypeParams[ix]->AsETSTypeParameter(), sourceTypeParams[ix]); } - return target->Substitute(Relation(), substitution); + return target->Substitute(Relation(), &substitution); } void ETSChecker::ReportOverrideError(Signature *signature, Signature *overriddenSignature, @@ -1758,7 +1972,7 @@ bool ETSChecker::CheckOverride(Signature *signature, ETSObjectType *site) auto *target = site->GetProperty(signature->Function()->Id()->Name(), flags); bool isOverridingAnySignature = false; - if (target == nullptr || target->TsType() == nullptr || !target->TsType()->IsETSFunctionType()) { + if (target == nullptr || target->TsType() == nullptr || target->TsType()->IsTypeError()) { return isOverridingAnySignature; } @@ -2013,9 +2227,9 @@ bool ETSChecker::IsReturnTypeSubstitutable(Signature *const s1, Signature *const std::string ETSChecker::GetAsyncImplName(const util::StringView &name) { - std::string implName(name); - implName += "$asyncimpl"; - return implName; + std::string newName = + util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName(util::NameMangler::ASYNC, name); + return newName; } std::string ETSChecker::GetAsyncImplName(ir::MethodDefinition *asyncMethod) @@ -2027,16 +2241,6 @@ std::string ETSChecker::GetAsyncImplName(ir::MethodDefinition *asyncMethod) return GetAsyncImplName(asyncName->Name()); } -bool ETSChecker::IsAsyncImplMethod(ir::MethodDefinition const *method) -{ - auto methodName = method->Key()->AsIdentifier()->Name().Utf8(); - std::string_view asyncSuffix = "$asyncimpl"; - if (methodName.size() < asyncSuffix.size()) { - return false; - } - return methodName.substr(methodName.size() - asyncSuffix.size()) == asyncSuffix; -} - ir::MethodDefinition *ETSChecker::CreateMethod(const util::StringView &name, ir::ModifierFlags modifiers, ir::ScriptFunctionFlags flags, ArenaVector &¶ms, varbinder::FunctionParamScope *paramScope, ir::TypeNode *returnType, @@ -2160,11 +2364,8 @@ void ETSChecker::MoveTrailingBlockToEnclosingBlockStatement(ir::CallExpression * } using SFunctionData = ir::ScriptFunction::ScriptFunctionData; -void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr, Signature *sig) +ir::ScriptFunction *ETSChecker::CreateLambdaFunction(ir::BlockStatement *trailingBlock, Signature *sig) { - auto *trailingBlock = callExpr->TrailingBlock(); - ES2PANDA_ASSERT(trailingBlock != nullptr); - auto *funcParamScope = varbinder::LexicalScope(VarBinder()).GetScope(); auto paramCtx = varbinder::LexicalScope::Enter(VarBinder(), funcParamScope, false); @@ -2211,7 +2412,19 @@ void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr, Signatur funcParamScope->BindNode(funcNode); trailingBlock->SetScope(funcScope); - ReplaceScope(funcNode->Body(), trailingBlock, funcScope); + + return funcNode; +} + +void ETSChecker::TransformTraillingLambda(ir::CallExpression *callExpr, Signature *sig) +{ + auto *trailingBlock = callExpr->TrailingBlock(); + ES2PANDA_ASSERT(trailingBlock != nullptr); + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + auto *funcNode = CreateLambdaFunction(trailingBlock, sig); + funcNode->AddFlag(ir::ScriptFunctionFlags::TRAILING_LAMBDA); + ReplaceScope(funcNode->Body(), trailingBlock, funcNode->Scope()); callExpr->SetTrailingBlock(nullptr); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -2282,13 +2495,19 @@ void ETSChecker::CacheFunctionalInterface(ir::ETSFunctionType *type, ETSObjectTy functionalInterfaceCache_.emplace(hash, ifaceType); } -void ETSChecker::CollectReturnStatements(ir::AstNode *parent) +void ETSChecker::CollectReturnStatements(ir::AstNode *parent) // NOTE: remove with #28178 { parent->Iterate([this](ir::AstNode *childNode) -> void { if (childNode->IsScriptFunction()) { return; } + auto scope = Scope(); + if (childNode->IsBlockStatement()) { + scope = childNode->AsBlockStatement()->Scope(); + } + checker::ScopeContext scopeCtx(this, scope); + if (childNode->IsReturnStatement()) { ir::ReturnStatement *returnStmt = childNode->AsReturnStatement(); returnStmt->Check(this); @@ -2298,7 +2517,7 @@ void ETSChecker::CollectReturnStatements(ir::AstNode *parent) }); } -ArenaVector &ETSChecker::PendingConstraintCheckRecords() +std::vector &ETSChecker::PendingConstraintCheckRecords() { return pendingConstraintCheckRecords_; } @@ -2308,30 +2527,23 @@ size_t &ETSChecker::ConstraintCheckScopesCount() return constraintCheckScopesCount_; } -bool ETSChecker::CmpAssemblerTypesWithRank(Signature const *const sig1, Signature const *const sig2) noexcept +bool ETSChecker::HasSameAssemblySignature(Signature const *const sig1, Signature const *const sig2) noexcept { - for (size_t ix = 0U; ix < sig1->Params().size(); ++ix) { - std::stringstream s1; - std::stringstream s2; - sig1->Params()[ix]->TsType()->ToAssemblerTypeWithRank(s1); - sig2->Params()[ix]->TsType()->ToAssemblerTypeWithRank(s2); - if (s1.str() != s2.str()) { - return false; - break; - } + if (sig1->ReturnType()->ToAssemblerTypeWithRank() != sig2->ReturnType()->ToAssemblerTypeWithRank()) { + return false; } - return true; -} -bool ETSChecker::HasSameAssemblySignature(Signature const *const sig1, Signature const *const sig2) noexcept -{ if (sig1->ArgCount() != sig2->ArgCount()) { return false; } - if (!CmpAssemblerTypesWithRank(sig1, sig2)) { - return false; + for (size_t ix = 0U; ix < sig1->Params().size(); ++ix) { + if (sig1->Params()[ix]->TsType()->ToAssemblerTypeWithRank() != + sig2->Params()[ix]->TsType()->ToAssemblerTypeWithRank()) { + return false; + } } + auto *rv1 = sig1->RestVar(); auto *rv2 = sig2->RestVar(); if (rv1 == nullptr && rv2 == nullptr) { @@ -2340,11 +2552,8 @@ bool ETSChecker::HasSameAssemblySignature(Signature const *const sig1, Signature if (rv1 == nullptr || rv2 == nullptr) { // exactly one of them is null return false; } - std::stringstream s1; - std::stringstream s2; - rv1->TsType()->ToAssemblerTypeWithRank(s1); - rv2->TsType()->ToAssemblerTypeWithRank(s2); - return s1.str() == s2.str(); + + return (rv1->TsType()->ToAssemblerTypeWithRank() == rv2->TsType()->ToAssemblerTypeWithRank()); } bool ETSChecker::HasSameAssemblySignatures(ETSFunctionType const *const func1, @@ -2360,4 +2569,326 @@ bool ETSChecker::HasSameAssemblySignatures(ETSFunctionType const *const func1, return false; } +Signature *ETSChecker::FirstMatchSignatures(ir::CallExpression *expr, checker::Type *calleeType) +{ + if (expr->TrailingBlock() == nullptr) { + auto *signature = + MatchOrderSignatures(calleeType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow(), expr->TypeParams(), + expr->Arguments(), expr->Start(), TypeRelationFlag::NONE); + if (signature == nullptr) { + ThrowOverloadMismatch(calleeType->AsETSFunctionType()->Name(), expr->Arguments(), expr->Start(), "call"); + return nullptr; + } + UpdateDeclarationFromSignature(expr, signature); + return signature; + } + + auto &signatures = expr->IsETSConstructorCall() ? calleeType->AsETSObjectType()->ConstructSignatures() + : calleeType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow(); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return ResolveTrailingLambda(signatures, expr, expr->Start()); +} + +Signature *ETSChecker::MatchOrderSignatures(ArenaVector &signatures, + const ir::TSTypeParameterInstantiation *typeArguments, + const ArenaVector &arguments, + const lexer::SourcePosition &pos, TypeRelationFlag resolveFlags) +{ + Signature *compatibleSignatures = nullptr; + Signature *notVisibleSignature = nullptr; + std::vector argTypeInferenceRequired = FindTypeInferenceArguments(arguments); + + auto collectSignatures = [&](TypeRelationFlag relationFlags) { + for (auto *sig : signatures) { + if (notVisibleSignature != nullptr && + !IsSignatureAccessible(sig, Context().ContainingClass(), Relation())) { + continue; + } + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + auto *concreteSig = ValidateOrderSignature(std::make_tuple(sig, typeArguments, relationFlags), arguments, + pos, argTypeInferenceRequired, signatures.size() == 1); + + if (concreteSig == nullptr) { + CleanArgumentsInformation(arguments); + continue; + } + if (notVisibleSignature == nullptr && + !IsSignatureAccessible(sig, Context().ContainingClass(), Relation())) { + CleanArgumentsInformation(arguments); + notVisibleSignature = concreteSig; + } else { + compatibleSignatures = concreteSig; + return; + } + } + }; + + collectSignatures(signatures.size() == 1 ? TypeRelationFlag::WIDENING | resolveFlags + : TypeRelationFlag::WIDENING | TypeRelationFlag::NO_THROW | resolveFlags); + + if (compatibleSignatures != nullptr) { + return compatibleSignatures; + } + + if (notVisibleSignature != nullptr && ((resolveFlags & TypeRelationFlag::NO_THROW) == 0)) { + LogError(diagnostic::SIG_INVISIBLE, {notVisibleSignature->Function()->Id()->Name(), notVisibleSignature}, pos); + } + + return nullptr; +} + +Signature *ETSChecker::ValidateOrderSignature( + std::tuple info, + const ArenaVector &arguments, const lexer::SourcePosition &pos, + const std::vector &argTypeInferenceRequired, const bool unique) +{ + auto [baseSignature, typeArguments, flags] = info; + // In case of overloads, it is necessary to iterate through the compatible signatures again, + // setting the boxing/unboxing flag for the arguments if needed. + // So handle substitution arguments only in the case of unique function or collecting signature phase. + Signature *const signature = + MaybeSubstituteTypeParameters(this, baseSignature, typeArguments, arguments, pos, flags); + if (signature == nullptr) { + return nullptr; + } + + // When process first match, if current signature is not matched, do not log TypeError + SignatureMatchContext signatureMatchContext(this, util::DiagnosticType::SEMANTIC, + (flags & TypeRelationFlag::NO_THROW) == 0); + + size_t const argCount = arguments.size(); + auto const hasRestParameter = signature->RestVar() != nullptr; + size_t compareCount = argCount; + if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && !signature->Params().empty() && + signature->Params().back()->Declaration()->Node()->AsETSParameterExpression()->IsOptional()) { + compareCount = compareCount - 1; + } + + if (compareCount < signature->MinArgCount() || (argCount > signature->ArgCount() && !hasRestParameter)) { + LogError(diagnostic::PARAM_COUNT_MISMATCH, {signature->MinArgCount(), argCount}, pos); + return nullptr; + } + + if (argCount > signature->ArgCount() && hasRestParameter && (flags & TypeRelationFlag::IGNORE_REST_PARAM) != 0) { + return nullptr; + } + + auto count = std::min(signature->ArgCount(), argCount); + // Check all required formal parameter(s) first + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + if (!ValidateOrderSignatureRequiredParams(signature, arguments, flags, argTypeInferenceRequired) || + !signatureMatchContext.ValidSignatureMatchStatus()) { + return nullptr; + } + + // Check rest parameter(s) if any exists + if (!hasRestParameter || (count >= argCount && !signature->RestVar()->TsType()->IsETSTupleType())) { + return signature; + } + if (!ValidateSignatureRestParams(signature, arguments, flags, true, unique)) { + return nullptr; + } + + return signature; +} + +bool ETSChecker::SetPreferredTypeBeforeValidate(Signature *substitutedSig, ir::Expression *argument, size_t index, + TypeRelationFlag flags, + const std::vector &argTypeInferenceRequired) +{ + auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + if (argument->IsObjectExpression()) { + argument->AsObjectExpression()->SetPreferredType(paramType); + } + + if (argument->IsMemberExpression()) { + SetArrayPreferredTypeForNestedMemberExpressions(argument->AsMemberExpression(), paramType); + } + + if (argTypeInferenceRequired[index]) { + if (!paramType->IsETSFunctionType()) { + return false; + } + ES2PANDA_ASSERT(argument->IsArrowFunctionExpression()); + auto *param = substitutedSig->Params()[index]->Declaration()->Node()->AsETSParameterExpression(); + auto *lambda = argument->AsArrowFunctionExpression(); + if (lambda->Function()->Params().size() > + paramType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow().front()->Params().size()) { + return false; + } + return CheckLambdaInfer(param->TypeAnnotation(), argument->AsArrowFunctionExpression(), paramType); + } + + if (argument->IsArrayExpression()) { + argument->AsArrayExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); + } + + if (argument->IsETSNewArrayInstanceExpression()) { + argument->AsETSNewArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); + } + + if (argument->IsETSNewMultiDimArrayInstanceExpression()) { + argument->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredTypeBasedOnFuncParam(this, paramType, flags); + } + + return true; +} + +bool ETSChecker::ValidateOrderSignatureRequiredParams(Signature *substitutedSig, + const ArenaVector &arguments, + TypeRelationFlag flags, + const std::vector &argTypeInferenceRequired) +{ + auto commonArity = std::min(arguments.size(), substitutedSig->ArgCount()); + if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) { + if (commonArity == 0) { + ES2PANDA_ASSERT(substitutedSig->GetSignatureInfo()->params.empty()); + return false; + } + commonArity = commonArity - 1; + } + for (size_t index = 0; index < commonArity; ++index) { + auto &argument = arguments[index]; + if (!SetPreferredTypeBeforeValidate(substitutedSig, argument, index, flags, argTypeInferenceRequired)) { + return false; + } + + if (argument->IsSpreadElement()) { + LogError(diagnostic::SPREAD_ONTO_SINGLE_PARAM, {}, argument->Start()); + return false; + } + + if (argument->IsIdentifier() && IsInvalidArgumentAsIdentifier(Scope(), argument->AsIdentifier())) { + LogError(diagnostic::ARG_IS_CLASS_ID, {}, argument->Start()); + return false; + } + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + if (!ValidateOrderSignatureInvocationContext(substitutedSig, argument, index, flags)) { + return false; + } + } + + if ((flags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0 && arguments.back()->IsArrowFunctionExpression()) { + ir::ScriptFunction *const lambda = arguments.back()->AsArrowFunctionExpression()->Function(); + auto targetParm = substitutedSig->GetSignatureInfo()->params.back()->Declaration()->Node(); + if (!CheckLambdaAssignable(targetParm->AsETSParameterExpression(), lambda)) { + return false; + } + } + return true; +} + +void ETSChecker::CleanArgumentsInformation(const ArenaVector &arguments) +{ + if (arguments.empty()) { + return; + } + for (auto *argument : arguments) { + argument->CleanCheckInformation(); + } +} + +bool ETSChecker::ValidateOrderSignatureInvocationContext(Signature *substitutedSig, ir::Expression *argument, + std::size_t index, TypeRelationFlag flags) +{ + Type *targetType = substitutedSig->Params()[index]->TsType(); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + Type *argumentType = argument->Check(this); + + flags |= TypeRelationFlag::ONLY_CHECK_WIDENING; + + auto const invocationCtx = + checker::InvocationContext(Relation(), argument, argumentType, targetType, argument->Start(), + {{diagnostic::TYPE_MISMATCH_AT_IDX, {argumentType, targetType, index + 1}}}, flags); + + return invocationCtx.IsInvocable(); +} + +Signature *ETSChecker::ResolveTrailingLambda(ArenaVector &signatures, ir::CallExpression *callExpr, + const lexer::SourcePosition &pos, const TypeRelationFlag reportFlag) +{ + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + auto arguments = ExtendArgumentsWithFakeLamda(callExpr); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + auto sig = ResolvePotentialTrailingLambda(callExpr, signatures, arguments); + if (sig != nullptr) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + TransformTraillingLambda(callExpr, sig); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + TrailingLambdaTypeInference(sig, callExpr->Arguments()); + UpdateDeclarationFromSignature(callExpr, sig); + callExpr->SetIsTrailingCall(true); + return sig; + } + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + sig = MatchOrderSignatures(signatures, callExpr->TypeParams(), callExpr->Arguments(), pos, reportFlag); + if (sig != nullptr) { + EnsureValidCurlyBrace(callExpr); + } + + UpdateDeclarationFromSignature(callExpr, sig); + return sig; +} + +Signature *ETSChecker::ResolvePotentialTrailingLambda(ir::CallExpression *callExpr, + ArenaVector const &signatures, + ArenaVector &arguments) +{ + auto *trailingLambda = arguments.back()->AsArrowFunctionExpression(); + ArenaVector normalSig(ProgramAllocator()->Adapter()); + ArenaVector sigContainLambdaWithReceiverAsParam(ProgramAllocator()->Adapter()); + Signature *signature = nullptr; + for (auto sig : signatures) { + if (!IsLastParameterLambdaWithReceiver(sig)) { + normalSig.emplace_back(sig); + continue; + } + + auto *candidateFunctionType = + sig->Function()->Params().back()->AsETSParameterExpression()->TypeAnnotation()->AsETSFunctionType(); + auto *currentReceiver = candidateFunctionType->Params()[0]; + trailingLambda->Function()->EmplaceParams(currentReceiver); + sigContainLambdaWithReceiverAsParam.emplace_back(sig); + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + signature = MatchOrderSignatures(sigContainLambdaWithReceiverAsParam, callExpr->TypeParams(), arguments, + callExpr->Start(), + TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA); + if (signature != nullptr) { + return signature; + } + sigContainLambdaWithReceiverAsParam.clear(); + trailingLambda->Function()->ClearParams(); + } + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return MatchOrderSignatures(normalSig, callExpr->TypeParams(), arguments, callExpr->Start(), + TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA); +} + +void ETSChecker::ThrowOverloadMismatch(util::StringView callName, const ArenaVector &arguments, + const lexer::SourcePosition &pos, std::string_view signatureKind) +{ + std::string msg {}; + msg.append(callName.Mutf8()); + msg += "("; + + for (std::size_t index = 0U; index < arguments.size(); ++index) { + auto const &argument = arguments[index]; + Type const *const argumentType = argument->Check(this); + if (!argumentType->IsTypeError()) { + msg += argumentType->ToString(); + } else { + // NOTE (DZ): extra cases for some specific nodes can be added here (as for 'ArrowFunctionExpression') + msg += argument->ToString(); + } + + if (index != arguments.size() - 1U) { + msg += ", "; + } + } + msg += ")"; + LogError(diagnostic::NO_MATCHING_SIG, {signatureKind, msg.c_str()}, pos); +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 8009932975710ad27e3b7c86b84995ba88c3c7a9..7730c65f761e4decde9ef61df925a6af35a41b56 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -118,81 +118,82 @@ static void InferUntilFail(Signature const *const signature, const ArenaVectorRemoveStatus(checker::CheckerStatus::IN_TYPE_INFER); } -static const Substitution *BuildImplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature, - const ArenaVector &arguments) +static std::optional BuildImplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature, + const ArenaVector &arguments) { - Substitution *substitution = checker->NewSubstitution(); + auto substitution = Substitution {}; auto *sigInfo = signature->GetSignatureInfo(); auto &sigParams = signature->GetSignatureInfo()->typeParams; - InferUntilFail(signature, arguments, checker, substitution); + InferUntilFail(signature, arguments, checker, &substitution); - if (substitution->size() != sigParams.size()) { + if (substitution.size() != sigParams.size()) { for (const auto typeParam : sigParams) { auto newTypeParam = typeParam->AsETSTypeParameter(); - if (auto it = substitution->find(newTypeParam); it != substitution->cend()) { + if (auto it = substitution.find(newTypeParam); it != substitution.cend()) { continue; } if (newTypeParam->GetDefaultType() == nullptr) { - checker->EmplaceSubstituted(substitution, newTypeParam, checker->GlobalETSNeverType()); + checker->EmplaceSubstituted(&substitution, newTypeParam, checker->GlobalETSNeverType()); continue; } - auto dflt = newTypeParam->GetDefaultType()->Substitute(checker->Relation(), substitution); - if (!checker->EnhanceSubstitutionForType(sigInfo->typeParams, newTypeParam, dflt, substitution)) { - return nullptr; + auto dflt = newTypeParam->GetDefaultType()->Substitute(checker->Relation(), &substitution); + if (!checker->EnhanceSubstitutionForType(sigInfo->typeParams, newTypeParam, dflt, &substitution)) { + return std::nullopt; } } } - if (substitution->size() != sigParams.size() && + if (substitution.size() != sigParams.size() && (signature->Function()->ReturnTypeAnnotation() == nullptr || !checker->EnhanceSubstitutionForType(sigInfo->typeParams, signature->Function()->ReturnTypeAnnotation()->TsType(), - signature->ReturnType(), substitution))) { - return nullptr; + signature->ReturnType(), &substitution))) { + return std::nullopt; } return substitution; } -static const Substitution *BuildExplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature, - const ArenaVector ¶ms, - const lexer::SourcePosition &pos, - TypeRelationFlag flags) +static std::optional BuildExplicitSubstitutionForArguments(ETSChecker *checker, Signature *signature, + const ArenaVector ¶ms, + const lexer::SourcePosition &pos, + TypeRelationFlag flags) { auto &sigParams = signature->GetSignatureInfo()->typeParams; - auto *substitution = checker->NewSubstitution(); - auto *constraintsSubstitution = checker->NewSubstitution(); + auto substitution = Substitution {}; + auto constraintsSubstitution = Substitution {}; ArenaVector instArgs {checker->Allocator()->Adapter()}; for (size_t ix = 0; ix < params.size(); ++ix) { instArgs.push_back(MaybeBoxedType(checker, params[ix]->GetType(checker), params[ix])); if (ix < sigParams.size()) { - checker->EmplaceSubstituted(constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(&constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); } } for (size_t ix = instArgs.size(); ix < sigParams.size(); ++ix) { - auto *dflt = sigParams[ix]->AsETSTypeParameter()->GetDefaultType(); + auto typeParam = sigParams[ix]->AsETSTypeParameter(); + auto *dflt = typeParam->GetDefaultType(); if (dflt == nullptr) { break; } - dflt = dflt->Substitute(checker->Relation(), constraintsSubstitution); + dflt = dflt->Substitute(checker->Relation(), &constraintsSubstitution); instArgs.push_back(dflt); - checker->EmplaceSubstituted(constraintsSubstitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(&constraintsSubstitution, typeParam, instArgs[ix]); } if (sigParams.size() != instArgs.size()) { if ((flags & TypeRelationFlag::NO_THROW) == static_cast>(0U)) { checker->LogError(diagnostic::RTYPE_PARAM_COUNT_MISMATCH, {sigParams.size(), instArgs.size()}, pos); } - return nullptr; + return std::nullopt; } for (size_t ix = 0; ix < sigParams.size(); ix++) { if (!checker->IsCompatibleTypeArgument(sigParams[ix]->AsETSTypeParameter(), instArgs[ix], - constraintsSubstitution)) { - return nullptr; + &constraintsSubstitution)) { + return std::nullopt; } - checker->EmplaceSubstituted(substitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); + checker->EmplaceSubstituted(&substitution, sigParams[ix]->AsETSTypeParameter(), instArgs[ix]); } return substitution; } @@ -206,12 +207,12 @@ static Signature *MaybeSubstituteTypeParameters(ETSChecker *checker, Signature * return signature; } - const Substitution *substitution = + const std::optional substitution = (typeArguments != nullptr) ? BuildExplicitSubstitutionForArguments(checker, signature, typeArguments->Params(), pos, flags) : BuildImplicitSubstitutionForArguments(checker, signature, arguments); - return (substitution == nullptr) ? nullptr : signature->Substitute(checker->Relation(), substitution); + return (!substitution.has_value()) ? nullptr : signature->Substitute(checker->Relation(), &substitution.value()); } static bool CheckInterfaceOverride(ETSChecker *const checker, ETSObjectType *const interface, diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 5887874a0bbbe6aa602d7d92e78aa533fd8d779f..3bf9f1a48fed2dbc4d256cd254dd6ca8f11b8336 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -13,23 +13,32 @@ * limitations under the License. */ +#include +#include #include "checker/ETSchecker.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" +#include "checker/checkerContext.h" +#include "checker/ETSAnalyzerHelpers.h" +#include "checker/types/ets/etsEnumType.h" #include "checker/types/ets/etsTupleType.h" #include "checker/ets/typeRelationContext.h" #include "checker/ets/typeConverter.h" #include "evaluate/scopedDebugInfoPlugin.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "compiler/lowering/util.h" +#include "generated/diagnostic.h" +#include "util/es2pandaMacros.h" #include "util/helpers.h" +#include "util/nameMangler.h" namespace ark::es2panda::checker { -varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name) +varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name, + const varbinder::ResolveBindingOptions options) { - return Scope() != nullptr ? Scope()->FindInFunctionScope(name, varbinder::ResolveBindingOptions::ALL).variable - : nullptr; + return Scope() != nullptr ? Scope()->FindInFunctionScope(name, options).variable : nullptr; } std::pair ETSChecker::FindVariableInClassOrEnclosing( @@ -51,11 +60,10 @@ std::pair ETSChecker::FindVariable return {resolved, classType}; } -varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier) +varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier, + const varbinder::ResolveBindingOptions options) { - return Scope() != nullptr - ? Scope()->FindInGlobal(identifier->Name(), varbinder::ResolveBindingOptions::ALL).variable - : nullptr; + return Scope() != nullptr ? Scope()->FindInGlobal(identifier->Name(), options).variable : nullptr; } bool ETSChecker::IsVariableStatic(const varbinder::Variable *var) @@ -73,6 +81,12 @@ bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var) return var != nullptr && var->TsType() != nullptr && var->TsType()->HasTypeFlag(TypeFlag::GETTER_SETTER); } +bool ETSChecker::IsVariableOverloadDeclaration(const varbinder::Variable *var) +{ + return var != nullptr && var->Declaration() != nullptr && var->Declaration()->Node() != nullptr && + var->Declaration()->Node()->IsOverloadDeclaration(); +} + bool ETSChecker::IsVariableExtensionAccessor(const varbinder::Variable *var) { return var != nullptr && var->TsType() != nullptr && var->TsType()->IsETSFunctionType() && @@ -101,11 +115,13 @@ void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident) varbinder::VariableFlags::TYPE))) { case varbinder::VariableFlags::CLASS: identCategoryName = "Class"; - break; + LogError(diagnostic::NOT_AS_OBJECT, {identCategoryName}, ident->Start()); + return; case varbinder::VariableFlags::NAMESPACE: identCategoryName = "Namespace"; - break; + LogError(diagnostic::NOT_AS_OBJECT, {identCategoryName}, ident->Start()); + return; case varbinder::VariableFlags::METHOD: identCategoryName = "Function"; @@ -135,13 +151,14 @@ void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident) LogError(diagnostic::ID_WRONG_CTX, {ident->Name()}, ident->Start()); return; } + ident->SetTsType(GlobalTypeError()); LogError(diagnostic::ID_IN_WRONG_CTX, {identCategoryName.c_str(), ident->Name()}, ident->Start()); } void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar, const ETSObjectType *classType) { - if (classVar == nullptr) { + if (classVar == nullptr || (classVar->TsType() != nullptr && classVar->TsType()->IsTypeError())) { LogUnresolvedReferenceError(ident); return; } @@ -277,11 +294,14 @@ Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident) return GetTypeOfVariable(resolved); } - auto *resolved = FindVariableInFunctionScope(ident->Name()); + const auto options = varbinder::ResolveBindingOptions::ALL_VARIABLES | + varbinder::ResolveBindingOptions::ALL_METHOD | + varbinder::ResolveBindingOptions::ALL_DECLARATION; + auto *resolved = FindVariableInFunctionScope(ident->Name(), options); if (resolved == nullptr) { // If the reference is not found already in the current class, then it is not bound to the class, so we have to // find the reference in the global class first, then in the global scope - resolved = FindVariableInGlobal(ident); + resolved = FindVariableInGlobal(ident, options); if (UNLIKELY(resolved == nullptr && debugInfoPlugin_ != nullptr)) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) resolved = debugInfoPlugin_->FindIdentifier(ident); @@ -376,31 +396,53 @@ checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker ES2PANDA_UNREACHABLE(); } -Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion, - const bool isCondExpr) +Type *ETSChecker::GetUnaryOperatorPromotedType(Type *type, const bool doPromotion) +{ + auto globalTypesHolder = GetGlobalTypesHolder(); + + if (doPromotion) { + if (type == globalTypesHolder->GlobalByteBuiltinType() || type == globalTypesHolder->GlobalShortBuiltinType() || + type == globalTypesHolder->GlobalCharBuiltinType() || + type == globalTypesHolder->GlobalIntegerBuiltinType()) { + return GlobalIntBuiltinType(); + } + + if (type->IsIntType() || type->IsByteType() || type->IsShortType() || type->IsCharType()) { + return GlobalIntBuiltinType(); + } + } + + return type; +} + +Type *ETSChecker::ApplyUnaryOperatorPromotion(ir::Expression *expr, Type *type, const bool isCondExpr) { Type *unboxedType = isCondExpr ? MaybeUnboxConditionalInRelation(type) : MaybeUnboxInRelation(type); + if (type != nullptr && type->IsETSIntEnumType()) { + expr->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); + unboxedType = type->AsETSEnumType()->GetBaseEnumElementType(this); + } + if (unboxedType == nullptr) { return nullptr; } - if (doPromotion) { - switch (ETSType(unboxedType)) { - case TypeFlag::BYTE: - case TypeFlag::SHORT: - case TypeFlag::CHAR: { - if (!createConst) { - return GlobalIntType(); - } - return CreateIntTypeFromType(unboxedType); - } - default: { - break; - } - } + switch (ETSType(unboxedType)) { + case TypeFlag::BYTE: + [[fallthrough]]; + case TypeFlag::SHORT: + [[fallthrough]]; + case TypeFlag::CHAR: + [[fallthrough]]; + case TypeFlag::INT: + return GlobalIntBuiltinType(); + + default: + break; } - return unboxedType; + + return type; } bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const @@ -412,7 +454,7 @@ bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const std::tuple ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const { auto [isResolve, isValue] = - IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr(); + IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : IsConstantTestValue(expr); const Type *tsType = expr->TsType(); if (tsType->DefinitelyNotETSNullish() && !type->IsETSPrimitiveOrEnumType()) { @@ -459,30 +501,6 @@ Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, return nullptr; } -bool ETSChecker::HandleLogicalPotentialResult(ir::Expression *left, ir::Expression *right, ir::BinaryExpression *expr, - checker::Type *leftType) -{ - if (leftType->IsConstantType() && leftType->IsETSBooleanType()) { - if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { - expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? right : left); - return true; - } - expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? left : right); - return true; - } - - if (!leftType->IsETSPrimitiveType() && !leftType->PossiblyETSValueTyped()) { - expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? right : left); - return true; - } - if (leftType->IsETSNullType() || leftType->IsETSUndefinedType()) { - expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? left : right); - return true; - } - - return false; -} - void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType, ir::ScriptFunction *containingFunc, ir::ReturnStatement *st) { @@ -507,8 +525,6 @@ void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker:: argumentType = MaybeBoxInRelation(argumentType); if (argumentType == nullptr) { LogError(diagnostic::INVALID_EXPR_IN_RETURN, {}, st->Argument()->Start()); - } else { - st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType)); } } @@ -543,7 +559,6 @@ checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init) for (auto *typeFromTuple : elementType->AsETSTupleType()->GetTupleTypesList()) { elementTypes.emplace_back(typeFromTuple); } - continue; } @@ -551,22 +566,90 @@ checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init) elementType = elementType->AsETSArrayType()->ElementType(); } - elementTypes.push_back(GetNonConstantType(elementType)); + elementTypes.emplace_back(elementType); } if (elementTypes.empty()) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return ProgramAllocator()->New(GlobalETSObjectType()); + if (init->PreferredType() != nullptr) { + return init->PreferredType(); + } + LogError(diagnostic::UNRESOLVABLE_ARRAY, {}, init->Start()); + return GetGlobalTypesHolder()->GlobalTypeError(); } - auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); }; - auto const isChar = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::CHAR); }; - auto *const arrayElementType = - std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric) - ? std::all_of(elementTypes.begin(), elementTypes.end(), isChar) ? GlobalCharType() : GlobalDoubleType() - : CreateETSUnionType(std::move(elementTypes)); + auto const isNumericLiteral = [this](checker::Type *&ct) { + auto const rc = + ct->IsConstantType() && Relation()->IsSupertypeOf(GetGlobalTypesHolder()->GlobalNumericBuiltinType(), ct); + ct = GetNonConstantType(ct); + return rc; + }; + auto const isChar = [this](checker::Type *ct) { + return Relation()->IsSupertypeOf(GetGlobalTypesHolder()->GlobalCharBuiltinType(), ct); + }; + auto const elementType = std::all_of(elementTypes.begin(), elementTypes.end(), isNumericLiteral) + ? std::all_of(elementTypes.begin(), elementTypes.end(), isChar) + ? GlobalCharBuiltinType() + : GlobalDoubleBuiltinType() + : CreateETSUnionType(std::move(elementTypes)); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return CreateETSResizableArrayType(arrayElementType); + return CreateETSResizableArrayType(elementType); +} + +static void SetTypeforLambdaParamWithoutTypeAnnotation(ir::ETSParameterExpression *param, Type *type) +{ + auto *const lambdaParam = param->Ident(); + if (lambdaParam->TypeAnnotation() != nullptr) { + return; + } + if (lambdaParam->Variable() != nullptr) { + lambdaParam->Variable()->SetTsType(type); + } + lambdaParam->SetTsType(type); +} + +void ETSChecker::InferLambdaInAssignmentExpression(ir::AssignmentExpression *const expr) +{ + auto *left = expr->Left(); + auto *right = expr->Right(); + + if (!right->IsArrowFunctionExpression()) { + return; + } + + if (left->TsType() == nullptr || !left->TsType()->IsETSFunctionType() || + left->TsType()->AsETSFunctionType()->CallSignaturesOfMethodOrArrow().empty()) { + return; + } + + ArenaVector lambdaParams = right->AsArrowFunctionExpression()->Function()->Params(); + Signature *sig = left->TsType()->AsETSFunctionType()->CallSignaturesOfMethodOrArrow()[0]; + + size_t paramCount = sig->Params().size(); + if (sig->RestVar() != nullptr) { + paramCount++; + } + + if (paramCount != lambdaParams.size()) { + return; + } + + if (std::any_of(lambdaParams.begin(), lambdaParams.end(), + [](auto ¶m) { return !param->IsETSParameterExpression(); })) { + return; + } + + if (sig->RestVar() != nullptr) { + if (!lambdaParams.back()->AsETSParameterExpression()->IsRestParameter()) { + return; + } + SetTypeforLambdaParamWithoutTypeAnnotation(lambdaParams.back()->AsETSParameterExpression(), + sig->RestVar()->TsType()); + paramCount--; + } + for (size_t i = 0; i < paramCount; i++) { + auto *inferredType = sig->Params()[i]->TsType(); + SetTypeforLambdaParamWithoutTypeAnnotation(lambdaParams[i]->AsETSParameterExpression(), inferredType); + } } void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init) @@ -601,20 +684,14 @@ void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::Arr } } -checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags, - ir::Expression *init) +checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags) { ES2PANDA_ASSERT(bindingVar != nullptr); if ((flags & ir::ModifierFlags::OPTIONAL) != 0) { - if (init != nullptr) { - auto *type = bindingVar->TsType(); - ES2PANDA_ASSERT(type != nullptr); - if (type->IsETSPrimitiveType()) { - init->SetBoxingUnboxingFlags(GetBoxingFlag(bindingVar->TsType())); - } - } + ES2PANDA_ASSERT(bindingVar != nullptr); auto *variableType = bindingVar->TsType() != nullptr ? bindingVar->TsType() : GlobalTypeError(); - bindingVar->SetTsType(CreateETSUnionType({GlobalETSUndefinedType(), variableType})); + bindingVar->SetTsType( + !variableType->IsTypeError() ? CreateETSUnionType({GlobalETSUndefinedType(), variableType}) : variableType); } return bindingVar->TsType(); } @@ -651,8 +728,9 @@ checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType) return nullptr; } -bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, ir::TypeNode *typeAnnotation, - ir::Expression *init, checker::Type *annotationType) +// CC-OFFNXT(huge_cyclomatic_complexity, huge_cca_cyclomatic_complexity[C++]) solid logic +static bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, ir::TypeNode *typeAnnotation, + ir::Expression *init, checker::Type *annotationType) { if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) { checker->LogError(diagnostic::MEMBER_OF_OBJECT_LIT, {}, ident->Start()); @@ -666,7 +744,7 @@ bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, i checker->SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType); } - if (init->IsArrayExpression() && (annotationType != nullptr) && !annotationType->IsETSDynamicType()) { + if (init->IsArrayExpression() && (annotationType != nullptr)) { if (annotationType->IsETSTupleType() && !checker->IsArrayExprSizeValidForTuple(init->AsArrayExpression(), annotationType->AsETSTupleType())) { return false; @@ -676,14 +754,17 @@ bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, i } if (init->IsObjectExpression() && annotationType != nullptr) { - init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType)); + init->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType)); } if (init->IsETSNewArrayInstanceExpression() && annotationType != nullptr) { - init->AsETSNewArrayInstanceExpression()->SetPreferredType(annotationType); + init->SetPreferredType(annotationType); } if (init->IsETSNewMultiDimArrayInstanceExpression() && annotationType != nullptr) { - init->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(annotationType); + init->SetPreferredType(annotationType); + } + if (init->IsNumberLiteral() && annotationType != nullptr) { + init->SetPreferredType(annotationType); } if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) { @@ -697,6 +778,9 @@ bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, i bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init, checker::Type *annotationType) { + if (init->IsETSTypeReference()) { + LogError(diagnostic::INVALID_TYPE_AS_VALUE, {init->DumpEtsSrc()}, init->Start()); + } if (typeAnnotation == nullptr) { if (init->IsArrayExpression()) { annotationType = CheckArrayElements(init->AsArrayExpression()); @@ -773,42 +857,87 @@ static void CheckAssignForDeclare(ir::Identifier *ident, ir::TypeNode *typeAnnot } } +static void CheckRecordSpreadElement(ir::SpreadElement *spreadElement, ArenaVector &typeArguments, + ETSChecker *checker, const lexer::SourcePosition &start) +{ + auto spreadArg = spreadElement->Argument(); + auto spreadType = spreadArg->Check(checker); + // Verify spread source is also a Record type + if (!spreadType->IsETSObjectType()) { + checker->LogError(diagnostic::INVALID_RECORD_PROPERTY, start); + return; + } + // Check if spread type is Record or Map using proper type identity checking + auto *spreadObjType = spreadType->AsETSObjectType(); + auto *spreadOriginalBaseType = spreadObjType->GetOriginalBaseType(); + auto *globalTypes = checker->GetGlobalTypesHolder(); + if (!checker->IsTypeIdenticalTo(spreadOriginalBaseType, globalTypes->GlobalMapBuiltinType()) && + !checker->IsTypeIdenticalTo(spreadOriginalBaseType, globalTypes->GlobalRecordBuiltinType())) { + checker->LogError(diagnostic::INVALID_RECORD_PROPERTY, start); + return; + } + // Verify type parameters match + auto spreadTypeArgs = spreadType->AsETSObjectType()->TypeArguments(); + constexpr size_t EXPECTED_TYPE_ARGUMENTS_SIZE = 2; + if (spreadTypeArgs.size() != EXPECTED_TYPE_ARGUMENTS_SIZE) { + checker->LogError(diagnostic::INVALID_RECORD_PROPERTY, start); + return; + } + // Checking if the key type is a subtype of the type argument + if (!checker->Relation()->IsSupertypeOf(typeArguments[0], spreadTypeArgs[0])) { + checker->LogError(diagnostic::TYPE_MISMATCH_AT_IDX, {spreadTypeArgs[0], typeArguments[0], size_t(1)}, start); + } + checker::AssignmentContext(checker->Relation(), spreadArg, spreadTypeArgs[1], typeArguments[1], start, + util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, + {spreadTypeArgs[1], typeArguments[1], size_t(2)}}); +} + +static void CheckRecordProperty(ir::Property *p, ArenaVector &typeArguments, ETSChecker *checker) +{ + p->Key()->SetPreferredType(typeArguments[0]); + p->Value()->SetPreferredType(typeArguments[1]); + + checker::Type *keyType = p->Key()->Check(checker); + checker::Type *valueType = p->Value()->Check(checker); + + // Checking if the key type is a subtype of the type argument + if (!checker->Relation()->IsSupertypeOf(typeArguments[0], keyType)) { + checker->LogError(diagnostic::TYPE_MISMATCH_AT_IDX, {keyType, typeArguments[0], size_t(1)}, p->Key()->Start()); + } + checker::AssignmentContext( + checker->Relation(), p->Value(), valueType, typeArguments[1], p->Value()->Start(), + util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {valueType, typeArguments[1], size_t(2)}}); +} + static void CheckRecordType(ir::Expression *init, checker::Type *annotationType, ETSChecker *checker) { if (!annotationType->IsETSObjectType() || !init->IsObjectExpression()) { return; } + // Check if this is actually a Record or Map type using proper type identity checking + auto *objType = annotationType->AsETSObjectType(); + auto *originalBaseType = objType->GetOriginalBaseType(); + auto *globalTypes = checker->GetGlobalTypesHolder(); - std::stringstream ss; - init->TsType()->ToAssemblerType(ss); - if (ss.str() != "escompat.Record" && ss.str() != "escompat.Map") { + if (!checker->IsTypeIdenticalTo(originalBaseType, globalTypes->GlobalMapBuiltinType()) && + !checker->IsTypeIdenticalTo(originalBaseType, globalTypes->GlobalRecordBuiltinType())) { return; } auto objectExpr = init->AsObjectExpression(); auto typeArguments = annotationType->AsETSObjectType()->TypeArguments(); auto properties = objectExpr->Properties(); - for (const auto &property : properties) { + if (property->IsSpreadElement()) { + CheckRecordSpreadElement(property->AsSpreadElement(), typeArguments, checker, property->Start()); + continue; + } if (!property->IsProperty()) { - checker->LogError(diagnostic::IMPROPER_NESTING_INTERFACE, {}, property->Start()); + checker->LogError(diagnostic::INVALID_RECORD_PROPERTY, property->Start()); continue; } - ES2PANDA_ASSERT(property->IsProperty()); - auto p = property->AsProperty(); - - ETSChecker::SetPreferredTypeIfPossible(p->Key(), typeArguments[0]); - ETSChecker::SetPreferredTypeIfPossible(p->Value(), typeArguments[1]); - - Type *keyType = p->Key()->Check(checker); - Type *valueType = p->Value()->Check(checker); - checker::AssignmentContext( - checker->Relation(), p->Key(), keyType, typeArguments[0], p->Key()->Start(), - util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {keyType, typeArguments[0], size_t(1)}}); - checker::AssignmentContext( - checker->Relation(), p->Value(), valueType, typeArguments[1], p->Value()->Start(), - util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {valueType, typeArguments[1], size_t(2)}}); + CheckRecordProperty(property->AsProperty(), typeArguments, checker); } } @@ -828,7 +957,7 @@ checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::T } if (init == nullptr) { - return FixOptionalVariableType(bindingVar, flags, init); + return FixOptionalVariableType(bindingVar, flags); } CheckAssignForDeclare(ident, typeAnnotation, init, flags, this); } else { @@ -882,7 +1011,7 @@ checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::T bindingVar->SetTsType(needWidening ? GetNonConstantType(initType) : initType); } - return FixOptionalVariableType(bindingVar, flags, init); + return FixOptionalVariableType(bindingVar, flags); } void ETSChecker::VariableTypeFromInitializer(varbinder::Variable *variable, Type *annotationType, Type *initType) @@ -1029,64 +1158,50 @@ checker::Type *ETSChecker::GetExtensionAccessorReturnType(ir::MemberExpression * // Smart cast support //==============================================================================// -checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType) +static checker::Type *MaybeReadonlyType(ETSChecker *checker, checker::Type *sourceType, checker::Type *targetType) { - // For left-hand variable of primitive type leave it as is. - if (targetType->IsETSPrimitiveType()) { + // For left-hand variable of builtin type leave it as is. + if (targetType->IsBuiltinNumeric()) { return targetType; } - // For left-hand variable of tuple type leave it as is. - if (targetType->IsETSTupleType()) { - return targetType; + // Preserve 'Readonly' type flag in smart type if it exists in declared type + if (targetType->HasTypeFlag(TypeFlag::READONLY) && !sourceType->HasTypeFlag(TypeFlag::READONLY)) { + sourceType = sourceType->Clone(checker); + sourceType->AddTypeFlag(TypeFlag::READONLY); } + return sourceType; +} + +checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType, + std::optional value) +{ + // For left-hand variable of primitive type leave it as is. + ES2PANDA_ASSERT(!targetType->IsETSPrimitiveType() && !sourceType->IsETSPrimitiveType()); // For left-hand invalid variable set smart type to right-hand type. if (targetType->IsTypeError()) { return sourceType; } - // For left-hand variable of builtin type leave it as is. - if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - return targetType; - } - - // Nothing to do with identical types: - auto *nonConstSourceType = GetNonConstantType(sourceType); - auto *nonConstTargetType = GetNonConstantType(targetType); - - if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) || - Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) { - return targetType; - } - // For type parameter, null or undefined source type return it as is. if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) { return sourceType; } - // In case of Union left-hand type we have to select the proper type from the Union - // Because now we have logging of errors we have to continue analyze incorrect program, for - // this case we change typeError to source type. - if (targetType->IsETSUnionType()) { - auto component = targetType->AsETSUnionType()->GetAssignableType(this, sourceType); - return component->IsTypeError() ? MaybeBoxType(sourceType) : component; - } - - // If source is reference type, set it as the current and use it for identifier smart cast - if (sourceType->IsETSReferenceType()) { - return sourceType; + // In case of Union left-hand type we try to select the proper type from the Union + if (targetType->IsETSUnionType() && !sourceType->IsUnionType()) { + auto *constituentType = targetType->AsETSUnionType()->GetAssignableType(this, sourceType, value); + if (constituentType != nullptr) { + return MaybeReadonlyType(this, sourceType, constituentType); + } } - // For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int). - if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType() && targetType->IsETSObjectType()) { - return MaybeBoxInRelation(sourceType); + // General case - return more specific subtype + if (Relation()->IsSupertypeOf(targetType, sourceType)) { + return MaybeReadonlyType(this, sourceType, targetType); } - // NOTE - it seems that all the other possible cases are assignments like: - // 'Object = ObjectLiteral' or smth similar ??? - // thus for such cases also leave the target type as is. - // Possible errors in tests should clarify this hypothesis sooner or later :) return targetType; } @@ -1139,8 +1254,7 @@ std::pair ETSChecker::CheckTestObjectCondition(ETSArrayType *tes } // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function. -std::pair ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType, - bool const strict) +std::pair ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType) { if (actualType->IsETSUnionType()) { return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType); @@ -1153,7 +1267,7 @@ std::pair ETSChecker::CheckTestObjectCondition(ETSObjectType *te if (Relation()->IsIdenticalTo(objectType, testedType) || objectType->AssemblerName() == testedType->AssemblerName()) { - return {testedType, strict ? GetGlobalTypesHolder()->GlobalETSNeverType() : actualType}; + return {testedType, actualType}; } if (Relation()->IsSupertypeOf(objectType, testedType)) { @@ -1401,8 +1515,7 @@ std::optional CheckerContext::ResolveSmartCastTypes() checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict); } else if (testCondition_.testedType->IsETSObjectType()) { auto *const testedType = testCondition_.testedType->AsETSObjectType(); - std::tie(consequentType, alternateType) = - checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict); + std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType); } else if (testCondition_.testedType->IsETSArrayType()) { auto *const testedType = testCondition_.testedType->AsETSArrayType(); std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType); @@ -1516,47 +1629,6 @@ void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpre } } -// 22955: type alias should be instantiated with Substitute -static void CollectAliasParametersForBoxing(Type *expandedAliasType, std::set ¶metersNeedToBeBoxed, - bool needToBeBoxed) -{ - if (expandedAliasType->IsETSTypeParameter() && needToBeBoxed) { - parametersNeedToBeBoxed.insert(expandedAliasType); - } else if (expandedAliasType->IsETSObjectType()) { - auto objectType = expandedAliasType->AsETSObjectType(); - needToBeBoxed = objectType->GetDeclNode() != nullptr && (objectType->GetDeclNode()->IsClassDefinition() || - objectType->GetDeclNode()->IsTSInterfaceDeclaration()); - for (const auto typeArgument : objectType->TypeArguments()) { - CollectAliasParametersForBoxing(typeArgument, parametersNeedToBeBoxed, needToBeBoxed); - } - } else if (expandedAliasType->IsETSTupleType()) { - auto tupleType = expandedAliasType->AsETSTupleType(); - needToBeBoxed = false; - for (auto type : tupleType->GetTupleTypesList()) { - CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed); - } - } else if (expandedAliasType->IsETSArrayType()) { - auto arrayType = expandedAliasType->AsETSArrayType(); - needToBeBoxed = false; - auto elementType = arrayType->ElementType(); - CollectAliasParametersForBoxing(elementType, parametersNeedToBeBoxed, needToBeBoxed); - } else if (expandedAliasType->IsETSUnionType()) { - auto unionType = expandedAliasType->AsETSUnionType(); - needToBeBoxed = false; - for (auto type : unionType->ConstituentTypes()) { - CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed); - } - } else if (expandedAliasType->IsETSFunctionType()) { - auto functionType = expandedAliasType->AsETSFunctionType(); - needToBeBoxed = true; - for (auto param : functionType->ArrowSignature()->Params()) { - CollectAliasParametersForBoxing(param->TsType(), parametersNeedToBeBoxed, needToBeBoxed); - } - CollectAliasParametersForBoxing(functionType->ArrowSignature()->ReturnType(), parametersNeedToBeBoxed, - needToBeBoxed); - } -} - bool ETSChecker::CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration *typeAliasNode, const ir::TSTypeParameterInstantiation *typeParams) { @@ -1587,11 +1659,10 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa ir::TSTypeAliasDeclaration *const typeAliasNode) { if (typeParams == nullptr && typeAliasNode->TypeParams() != nullptr) { - auto declTypeParams = typeAliasNode->TypeParams()->Params(); - auto isAllTypeParamsHasDefaultType = - std::find_if(declTypeParams.begin(), declTypeParams.end(), [](ir::TSTypeParameter *param) { - return param->DefaultType() == nullptr; - }) == declTypeParams.end(); + auto const ¶ms = typeAliasNode->TypeParams()->Params(); + auto isAllTypeParamsHasDefaultType = std::find_if(params.begin(), params.end(), [](ir::TSTypeParameter *param) { + return param->DefaultType() == nullptr; + }) == params.end(); if (!isAllTypeParamsHasDefaultType) { LogError(diagnostic::GENERIC_ALIAS_WITHOUT_PARAMS, {}, name->Start()); return GlobalTypeError(); @@ -1619,35 +1690,35 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *const aliasType = GetReferencedTypeBase(name); - auto *substitution = NewSubstitution(); - - std::set parametersNeedToBeBoxed; - auto expandedAliasType = aliasType->Substitute(Relation(), substitution); - CollectAliasParametersForBoxing(expandedAliasType, parametersNeedToBeBoxed, false); + auto substitution = Substitution {}; + auto relation = Relation(); - ES2PANDA_ASSERT(substitution != nullptr); - for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { + for (std::size_t idx = 0U; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); - auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); - if (!typeAliasType->IsETSTypeParameter()) { - continue; - } + auto *typeAliasType = typeAliasTypeName->Variable()->TsType()->MaybeBaseTypeOfGradualType(); + if (typeAliasType->IsETSTypeParameter()) { + ir::TypeNode *typeNode = ResolveTypeNodeForTypeArg(typeAliasNode, typeParams, idx); + auto paramType = typeNode->GetType(this); + + EmplaceSubstituted(&substitution, typeAliasType->AsETSTypeParameter(), paramType); - ir::TypeNode *typeNode = ResolveTypeNodeForTypeArg(typeAliasNode, typeParams, idx); - auto paramType = typeNode->GetType(this); + auto *const maybeIrrelevantTypeArg = paramType->IsETSVoidType() ? GlobalETSUndefinedType() : paramType; + auto *constraintType = typeAliasType->AsETSTypeParameter()->GetConstraintType(); + if (maybeIrrelevantTypeArg->IsTypeError() || constraintType->IsTypeError()) { + continue; // Don't issue extra error notification! + } - if (parametersNeedToBeBoxed.find(typeAliasType) != parametersNeedToBeBoxed.end()) { - if (const auto boxedType = MaybeBoxInRelation(typeNode->GetType(this)); boxedType != nullptr) { - paramType = boxedType; + constraintType = constraintType->Substitute(relation, &substitution); + if (!relation->IsSupertypeOf(constraintType, maybeIrrelevantTypeArg)) { + LogError(diagnostic::TYPEARG_TYPEPARAM_SUBTYPING, {paramType, constraintType}, typeNode->Start()); } } - substitution->insert({typeAliasType->AsETSTypeParameter(), paramType}); // #21835: type argument is not boxed } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams); - return aliasType->Substitute(Relation(), substitution); + return aliasType->Substitute(Relation(), &substitution); } std::vector ETSChecker::GetNameForSynteticObjectType(const util::StringView &source) @@ -1687,6 +1758,26 @@ std::pair FindSpecifierForModuleObject(ir::ETSImportDecl return std::make_pair(false, util::StringView()); } +static void BuildExportedFunctionSignature(ETSChecker *checker, varbinder::Variable *var) +{ + auto method = var->AsLocalVariable()->Declaration()->Node()->AsMethodDefinition(); + ES2PANDA_ASSERT(method->Parent()->IsClassDefinition() && + method->Parent()->AsClassDefinition()->Ident()->Name().Is(compiler::Signatures::ETS_GLOBAL)); + auto classDef = method->Parent()->AsClassDefinition(); + if (classDef->TsType() == nullptr) { + checker->BuildBasicClassProperties(classDef); + } + + auto containingClass = classDef->TsType()->IsGradualType() + ? classDef->TsType()->AsGradualType()->GetBaseType()->AsETSObjectType() + : classDef->TsType()->AsETSObjectType(); + SavedCheckerContext scc(checker, checker->Context().Status(), containingClass); + auto funcType = checker->BuildMethodSignature(method); + funcType->SetVariable(var); + var->SetTsType(funcType); + method->SetTsType(funcType); +} + template void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType, ir::ETSImportDeclaration *importDecl, @@ -1696,9 +1787,16 @@ void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleO for (auto [_, var] : bindings) { (void)_; auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name()); + if (!var->AsLocalVariable()->Declaration()->Node()->IsValidInCurrentPhase()) { + continue; + } if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() || var->AsLocalVariable()->Declaration()->Node()->HasExportAlias()) && found) { + if (var->AsLocalVariable()->Declaration()->Node()->IsMethodDefinition()) { + BuildExportedFunctionSignature(this, var); + } + if (!aliasedName.Empty()) { moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName); } @@ -1708,6 +1806,11 @@ void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleO } } +template void ETSChecker::BindingsModuleObjectAddProperty( + ETSObjectType *, ir::ETSImportDeclaration *, const varbinder::Scope::VariableMap &, const util::StringView &); +template void ETSChecker::BindingsModuleObjectAddProperty( + ETSObjectType *, ir::ETSImportDeclaration *, const varbinder::Scope::VariableMap &, const util::StringView &); + util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName, const util::StringView &importPath) { @@ -1725,8 +1828,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(); @@ -1745,8 +1848,19 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjT ES2PANDA_ASSERT(program != nullptr); if (!program->IsASTChecked()) { // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings + varbinder::RecordTableContext recordTableCtx(VarBinder()->AsETSBinder(), program); + // If external program import current program, the checker status should not contain external + checker::SavedCheckerContext savedContext(this, Context().Status(), Context().ContainingClass()); + if (!VarBinder()->AsETSBinder()->GetGlobalRecordTable()->IsExternal()) { + RemoveStatus(CheckerStatus::IN_EXTERNAL); + } + auto savedProgram = Program(); + VarBinder()->AsETSBinder()->SetProgram(program); + VarBinder()->AsETSBinder()->ResetTopScope(program->GlobalScope()); program->SetASTChecked(); program->Ast()->Check(this); + VarBinder()->AsETSBinder()->SetProgram(savedProgram); + VarBinder()->AsETSBinder()->ResetTopScope(savedProgram->GlobalScope()); } BindingsModuleObjectAddProperty( @@ -1787,10 +1901,18 @@ Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) return name->Check(this); } + if (name->IsMemberExpression()) { + return name->Check(this); + } + + if (name->IsLiteral()) { + return name->Check(this); + } + ES2PANDA_ASSERT(name->IsIdentifier()); if (name->AsIdentifier()->Variable() == nullptr) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - VarBinder()->AsETSBinder()->LookupTypeReference(name->AsIdentifier(), false); + VarBinder()->AsETSBinder()->LookupTypeReference(name->AsIdentifier()); } auto *const var = name->AsIdentifier()->Variable(); @@ -1800,11 +1922,6 @@ Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) return name->SetTsType(GlobalTypeError()); } - auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var); - if (importData != nullptr && importData->import->IsPureDynamic()) { - return name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language())); - } - return name->SetTsType(ResolveReferencedType(var->AsLocalVariable(), name)); } @@ -1818,7 +1935,8 @@ Type *ETSChecker::ResolveReferencedType(varbinder::LocalVariable *refVar, const case ir::AstNodeType::CLASS_DECLARATION: case ir::AstNodeType::STRUCT_DECLARATION: case ir::AstNodeType::CLASS_DEFINITION: - if (refVar->Declaration()->Node()->AsClassDefinition()->IsNamespaceTransformed()) { + if (refVar->Declaration()->Node()->IsClassDefinition() && + refVar->Declaration()->Node()->AsClassDefinition()->IsNamespaceTransformed()) { LogError(diagnostic::NAMESPACE_AS_TYPE, {refVar->Name()}, name->Start()); return GlobalTypeError(); } @@ -1836,7 +1954,7 @@ Type *ETSChecker::ResolveReferencedType(varbinder::LocalVariable *refVar, const } } -checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) +checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) const { if (type->IsTypeError()) { return GlobalTypeError(); @@ -1844,26 +1962,29 @@ checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) if (type->IsETSArrayType()) { return type->AsETSArrayType()->ElementType(); } - - ES2PANDA_ASSERT(type->IsETSResizableArrayType()); - return type->AsETSResizableArrayType()->ElementType(); + if (type->IsETSResizableArrayType()) { + return type->AsETSResizableArrayType()->ElementType(); + } + if (type->IsETSReadonlyArrayType()) { + auto const &typeArgs = type->AsETSObjectType()->TypeArguments(); + ES2PANDA_ASSERT(!typeArgs.empty()); + return typeArgs.front(); + } + ES2PANDA_UNREACHABLE(); } const checker::Type *ETSChecker::GetElementTypeOfArray(const checker::Type *type) const { - if (type->IsETSArrayType()) { - return type->AsETSArrayType()->ElementType(); - } - - ES2PANDA_ASSERT(type->IsETSResizableArrayType()); - return type->AsETSResizableArrayType()->ElementType(); + return GetElementTypeOfArray(const_cast(type)); } void ETSChecker::ConcatConstantString(util::UString &target, Type *type) { switch (ETSType(type)) { case TypeFlag::ETS_OBJECT: { - ES2PANDA_ASSERT(type->IsETSStringType()); + if (!type->IsETSStringType()) { + break; + } target.Append(type->AsETSStringType()->GetValue()); break; } @@ -2048,8 +2169,8 @@ varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *no Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant) { - discriminant->Check(this); - auto *discriminantType = GetNonConstantType(MaybeUnboxExpression(discriminant)); + Type *discriminantType = discriminant->Check(this); + discriminantType = GetNonConstantType(MaybeUnboxType(discriminantType)); ES2PANDA_ASSERT(discriminantType != nullptr); if (!discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) { if (!(discriminantType->IsETSObjectType() && @@ -2062,33 +2183,12 @@ Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant) return discriminantType; } -void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType) -{ - if (boxingUnboxingType->IsETSObjectType()) { - node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType)); - } else if (!boxingUnboxingType->IsETSUnionType()) { - node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType)); - } -} - Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr) { auto *promoted = MaybeBoxType(expr->TsType()); - if (promoted != expr->TsType()) { - expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted)); - } return promoted; } -Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr) -{ - auto *primitive = MaybeUnboxType(expr->TsType()); - if (primitive != expr->TsType()) { - expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive)); - } - return primitive; -} - void ETSChecker::CheckForSameSwitchCases(ArenaVector const &cases) { CheckItemCasesConstant(cases); @@ -2140,6 +2240,7 @@ bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression, bool che if (var == nullptr) { return false; } + ES2PANDA_ASSERT(var->TsType() != nullptr); bool isConst = checkForConst ? (var->TsType()->HasTypeFlag(checker::TypeFlag::CONSTANT)) : true; return ((var->Declaration()->IsConstDecl() && isConst) || (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC))); @@ -2183,7 +2284,7 @@ void ETSChecker::CheckItemCasesConstant(ArenaVector c if (caseTest == nullptr) { continue; } - auto *caseType = caseTest->TsType(); + auto *caseType = MaybeUnboxType(caseTest->TsType()); if (caseType->HasTypeFlag(TypeFlag::TYPE_ERROR)) { continue; } @@ -2261,7 +2362,7 @@ void ETSChecker::CheckItemCasesDuplicate(ArenaVector } if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() && - GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) { + caseTest->AsLiteral()->ToString() != compareCaseTest->AsLiteral()->ToString()) { continue; } @@ -2291,7 +2392,7 @@ bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareVal return caseValue != compareCaseValue; } - return caseValue != GetStringFromLiteral(compareValue); + return caseValue != compareValue->ToString(); } void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase, @@ -2315,23 +2416,6 @@ void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expr } } -std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const -{ - switch (caseTest->Type()) { - case ir::AstNodeType::CHAR_LITERAL: { - return std::to_string(caseTest->AsCharLiteral()->Char()); - } - case ir::AstNodeType::STRING_LITERAL: - case ir::AstNodeType::NULL_LITERAL: - case ir::AstNodeType::UNDEFINED_LITERAL: - case ir::AstNodeType::NUMBER_LITERAL: { - return util::Helpers::LiteralToPropName(caseTest).Mutf8(); - } - default: - ES2PANDA_UNREACHABLE(); - } -} - bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare) { return target->Declaration()->Type() == compare->Declaration()->Type(); @@ -2375,6 +2459,13 @@ ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *con return GetRelevantArgumentedTypeFromChild(child->SuperType(), target); } +Substitution ETSChecker::ArenaSubstitutionToSubstitution(const ArenaSubstitution *orig) +{ + Substitution copied {}; + std::copy(orig->begin(), orig->end(), std::inserter(copied, copied.end())); + return copied; +} + void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg) { // *only* reference type may be substituted, no exceptions @@ -2383,6 +2474,13 @@ void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter substitution->emplace(tparam, typeArg); } +void ETSChecker::EmplaceSubstituted(ArenaSubstitution *substitution, ETSTypeParameter *tparam, Type *typeArg) +{ + // *only* reference type may be substituted, no exceptions + ES2PANDA_ASSERT(typeArg->IsETSReferenceType()); + substitution->emplace(tparam, typeArg); +} + util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector &typeArgTypes) { std::stringstream ss; @@ -2456,14 +2554,6 @@ util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type) ss << ";"; - if (type->IsThrowing()) { - ss << "throws;"; - } - - if (type->IsRethrowing()) { - ss << "rethrows;"; - } - return util::UString(ss.str(), ProgramAllocator()).View(); } @@ -2547,14 +2637,14 @@ bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunc bool assignable = false; for (auto *type : typeAnn->AsETSUnionType()->Types()) { if (type->IsETSFunctionType()) { - assignable |= lambda->Params().size() == type->AsETSFunctionType()->Params().size(); + assignable |= lambda->Params().size() <= type->AsETSFunctionType()->Params().size(); continue; } if (type->IsETSTypeReference()) { auto aliasType = util::Helpers::DerefETSTypeReference(type); assignable |= aliasType->IsETSFunctionType() && - lambda->Params().size() == aliasType->AsETSFunctionType()->Params().size(); + lambda->Params().size() <= aliasType->AsETSFunctionType()->Params().size(); } } @@ -2570,21 +2660,29 @@ void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunction continue; } auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident(); - if (lambdaParam->TypeAnnotation() == nullptr) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - Type *inferredType = calleeType->Params()[i]->AsETSParameterExpression()->TypeAnnotation()->Check(this); - bool isPrimitive = inferredType != nullptr && inferredType->IsETSPrimitiveType(); - if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) { - ES2PANDA_ASSERT(maybeSubstitutedFunctionSig->Params().size() == calleeType->Params().size()); + if (lambdaParam->TypeAnnotation() != nullptr) { + continue; + } + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + Type *inferredType = calleeType->Params()[i]->AsETSParameterExpression()->TypeAnnotation()->Check(this); + bool isPrimitive = inferredType != nullptr && inferredType->IsETSPrimitiveType(); + if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) { + auto sigParamSize = maybeSubstitutedFunctionSig->Params().size(); + ES2PANDA_ASSERT( + sigParamSize == calleeType->Params().size() || + (maybeSubstitutedFunctionSig->HasRestParameter() && sigParamSize <= calleeType->Params().size())); + if (i < sigParamSize) { inferredType = maybeSubstitutedFunctionSig->Params()[i]->TsType(); + } else if (!maybeSubstitutedFunctionSig->RestVar()->TsType()->IsETSTupleType()) { + inferredType = GetElementTypeOfArray(maybeSubstitutedFunctionSig->RestVar()->TsType()); } - lambdaParam->Variable()->SetTsType(inferredType); - lambdaParam->SetTsType(inferredType); } + lambdaParam->Variable()->SetTsType(inferredType); + lambdaParam->SetTsType(inferredType); } if (lambda->ReturnTypeAnnotation() == nullptr) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *inferredReturnType = calleeType->ReturnType()->GetType(this); bool isPrimitive = inferredReturnType != nullptr && inferredReturnType->IsETSPrimitiveType(); if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) { @@ -2604,14 +2702,12 @@ void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, Signature *sign } auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident(); if (lambdaParam->TypeAnnotation() == nullptr) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) lambdaParam->Variable()->SetTsType(signature->Params().at(i)->TsType()); lambdaParam->SetTsType(signature->Params().at(i)->TsType()); } } if (lambda->ReturnTypeAnnotation() == nullptr) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) lambda->SetPreferredReturnType(signature->ReturnType()); } } @@ -2684,10 +2780,10 @@ static void ReInitScopesForTypeAnnotation(ETSChecker *checker, ir::TypeNode *typ ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope) { - classProp->Key()->AsIdentifier()->SetName( - util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(), - ProgramAllocator()) - .View()); + std::string newName = util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName( + util::NameMangler::PROPERTY, classProp->Key()->AsIdentifier()->Name()); + + classProp->Key()->AsIdentifier()->SetName(util::UString(newName, ProgramAllocator()).View()); classProp->AddModifier(ir::ModifierFlags::PRIVATE); auto *fieldDecl = ProgramAllocator()->New(classProp->Key()->AsIdentifier()->Name()); @@ -2772,6 +2868,7 @@ void ETSChecker::GenerateGetterSetterBody(ArenaVector &stmts, A // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto ident = ProgramAllocNode(paramExpression->Ident()->Name(), ProgramAllocator()); ident->SetVariable(paramExpression->Variable()); + ident->SetTsTypeAnnotation(nullptr); auto *assignmentExpression = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) ProgramAllocNode(memberExpression, ident, lexer::TokenType::PUNCTUATOR_SUBSTITUTION); @@ -2810,6 +2907,7 @@ static std::tupleBindParamScope(paramScope); paramScope->BindFunctionScope(functionScope); + auto classCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), classScope); ArenaVector params(checker->ProgramAllocator()->Adapter()); ArenaVector stmts(checker->ProgramAllocator()->Adapter()); @@ -2882,8 +2980,8 @@ ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty functionScope->BindNode(func); - auto classCtx = varbinder::LexicalScope::Enter(checker->VarBinder(), classScope); checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method); + method->Function()->ClearFlag(ir::ScriptFunctionFlags::EXTERNAL); functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName()); method->Check(checker); @@ -2902,16 +3000,17 @@ ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProp auto *const classProp = checker->ClassPropToImplementationProp( interfaceProp->Clone(checker->ProgramAllocator(), originalProp->Parent()), scope); classType->AddProperty(classProp->Key()->Variable()->AsLocalVariable()); - classDef->Body().push_back(classProp); + classDef->EmplaceBody(classProp); return classProp; } - auto *const classProp = classType - ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(), - PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE) - ->Declaration() - ->Node() - ->AsClassProperty(); + auto *const classProp = + classType + ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(), + PropertySearchFlags::SEARCH_INSTANCE_FIELD | PropertySearchFlags::SEARCH_IN_BASE) + ->Declaration() + ->Node() + ->AsClassProperty(); classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER); return classProp; } @@ -2973,7 +3072,7 @@ void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *origin // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this); - classDef->Body().push_back(getter); + classDef->EmplaceBody(getter); const auto &name = getter->Key()->AsIdentifier()->Name(); @@ -3006,14 +3105,45 @@ void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *origin getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature( setter->TsType()->AsETSFunctionType()->CallSignatures()[0]); getter->AddOverload(setter); + setter->SetParent(getter); } } +void ETSChecker::CreateTransformedCallee(ir::Identifier *ident, ir::Identifier *classId, ir::Identifier *methodId, + ir::CallExpression *callExpr) +{ + ir::MemberExpression *transformedCallee = nullptr; + classId->SetRange(ident->Range()); + if (ident->Parent()->IsMemberExpression()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + transformedCallee = ProgramAllocNode( + ident->Parent()->AsMemberExpression(), methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + ident->Parent()->AsMemberExpression()->SetParent(transformedCallee); + } else { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + transformedCallee = ProgramAllocNode( + classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + transformedCallee->SetParent(callExpr); + } + + methodId->SetRange(ident->Range()); + transformedCallee->SetRange(ident->Range()); + // Note: Should not modify the AST + // Related issue: #issue27122 + callExpr->SetCallee(transformedCallee); +} + // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType) { - ES2PANDA_ASSERT(ident->Parent()->IsCallExpression()); - ES2PANDA_ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident); + ir::CallExpression *callExpr = nullptr; + if (ident->Parent()->IsMemberExpression()) { + callExpr = ident->Parent()->Parent()->AsCallExpression(); + } else { + ES2PANDA_ASSERT(ident->Parent()->IsCallExpression()); + ES2PANDA_ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident); + callExpr = ident->Parent()->AsCallExpression(); + } if (!resolvedType->IsETSObjectType()) { return false; @@ -3048,20 +3178,8 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) { methodId->SetVariable(invokeMethod); } - - auto *transformedCallee = - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - ProgramAllocNode(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, - false); - - classId->SetRange(ident->Range()); - methodId->SetRange(ident->Range()); - transformedCallee->SetRange(ident->Range()); - - auto *callExpr = ident->Parent()->AsCallExpression(); - transformedCallee->SetParent(callExpr); - callExpr->SetCallee(transformedCallee); - + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + CreateTransformedCallee(ident, classId, methodId, callExpr); if (instantiateMethod != nullptr) { auto lexScope {varbinder::LexicalScope::Enter(VarBinder(), compiler::NearestScope(callExpr))}; // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -3104,7 +3222,7 @@ void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclarati Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident) { - auto importPath = importDecl->ResolvedSource(); + auto importPath = importDecl->IsPureDynamic() ? importDecl->DeclPath() : importDecl->ResolvedSource(); parser::Program *program = SelectEntryOrExternalProgram(static_cast(VarBinder()), importPath); if (program == nullptr) { @@ -3183,7 +3301,7 @@ void ETSChecker::CheckTypeParameterVariance(ir::ClassDefinition *classDef) return; } - Context().SetContainingClass(classDef->TsType()->AsETSObjectType()); + Context().SetContainingClass(classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType()); auto checkVariance = [this](VarianceFlag varianceFlag, ir::Expression *expression, Type *type) { Relation()->Result(RelationResult::TRUE); Relation()->SetNode(expression); @@ -3217,19 +3335,6 @@ void ETSChecker::CheckTypeParameterVariance(ir::ClassDefinition *classDef) } } -void ETSChecker::SetPreferredTypeIfPossible(ir::Expression *const expr, Type *const targetType) -{ - // Object expression requires that its type be set by the context before checking. in this case, the target type - // provides that context. - if (expr->IsObjectExpression()) { - expr->AsObjectExpression()->SetPreferredType(targetType); - } - - if (expr->IsArrayExpression()) { - expr->AsArrayExpression()->SetPreferredType(targetType); - } -} - checker::ETSFunctionType *ETSChecker::IntersectSignatureSets(const checker::ETSFunctionType *left, const checker::ETSFunctionType *right) { diff --git a/ets2panda/checker/ets/narrowingConverter.cpp b/ets2panda/checker/ets/narrowingConverter.cpp deleted file mode 100644 index 3b2d1cf9e0b4134083188b7a9ede2a7c6df0e92e..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/narrowingConverter.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "narrowingConverter.h" - -namespace ark::es2panda::checker { -} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/narrowingConverter.h b/ets2panda/checker/ets/narrowingConverter.h deleted file mode 100644 index f42051d88b9042d717680e29e899a2d0f472c87b..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/narrowingConverter.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2021-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_ETS_NARROWING_CONVERTER_H -#define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H - -#include "checker/ETSchecker.h" -#include "checker/ets/typeConverter.h" -#include "util/helpers.h" - -namespace ark::es2panda::checker { -class NarrowingConverter : public TypeConverter { -public: - static constexpr TypeFlag NARROWABLE_TO_FLOAT = TypeFlag::DOUBLE; - static constexpr TypeFlag NARROWABLE_TO_LONG = TypeFlag::FLOAT | NARROWABLE_TO_FLOAT; - static constexpr TypeFlag NARROWABLE_TO_INT = TypeFlag::LONG | NARROWABLE_TO_LONG; - static constexpr TypeFlag NARROWABLE_TO_CHAR = TypeFlag::SHORT | TypeFlag::INT | NARROWABLE_TO_INT; - static constexpr TypeFlag NARROWABLE_TO_SHORT = TypeFlag::CHAR | TypeFlag::INT | NARROWABLE_TO_INT; - static constexpr TypeFlag NARROWABLE_TO_BYTE = TypeFlag::CHAR | NARROWABLE_TO_CHAR; - - explicit NarrowingConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) - : TypeConverter(checker, relation, target, source) - { - if (!relation->ApplyNarrowing()) { - return; - } - - ES2PANDA_ASSERT(relation->GetNode()); - - switch (ETSChecker::ETSChecker::ETSType(target)) { - case TypeFlag::BYTE: { - ApplyNarrowing(NARROWABLE_TO_BYTE); - break; - } - case TypeFlag::CHAR: { - ApplyNarrowing(NARROWABLE_TO_CHAR); - break; - } - case TypeFlag::SHORT: { - ApplyNarrowing(NARROWABLE_TO_SHORT); - break; - } - case TypeFlag::INT: { - ApplyNarrowing(NARROWABLE_TO_INT); - break; - } - case TypeFlag::LONG: { - ApplyNarrowing(NARROWABLE_TO_LONG); - break; - } - case TypeFlag::FLOAT: { - ApplyNarrowing(NARROWABLE_TO_FLOAT); - break; - } - - default: { - break; - } - } - } - -private: - template - void ApplyNarrowing(TypeFlag flag) - { - if (!Source()->HasTypeFlag(flag)) { - return; - } - - switch (ETSChecker::ETSChecker::ETSType(Source())) { - case TypeFlag::CHAR: { - ApplyNarrowing(); - break; - } - case TypeFlag::SHORT: { - ApplyNarrowing(); - break; - } - case TypeFlag::INT: { - ApplyNarrowing(); - break; - } - case TypeFlag::LONG: { - ApplyNarrowing(); - break; - } - case TypeFlag::FLOAT: { - ApplyNarrowing(); - break; - } - case TypeFlag::DOUBLE: { - ApplyNarrowing(); - break; - } - default: { - break; - } - } - } - - template - To CastFloatingPointToIntOrLong(From value) - { - if (std::isinf(value)) { - if (std::signbit(value)) { - return std::numeric_limits::min(); - } - return std::numeric_limits::max(); - } - ES2PANDA_ASSERT(std::is_floating_point_v); - ES2PANDA_ASSERT(std::is_integral_v); - To minInt = std::numeric_limits::min(); - To maxInt = std::numeric_limits::max(); - auto floatMinInt = static_cast(minInt); - auto floatMaxInt = static_cast(maxInt); - - if (value > floatMinInt) { - if (value < floatMaxInt) { - return static_cast(value); - } - return maxInt; - } - if (std::isnan(value)) { - return 0; - } - return minInt; - } - - template - TType CalculateNarrowedValue(Type *target, Type *source, SType value) - { - switch (ETSChecker::ETSChecker::ETSType(target)) { - case TypeFlag::BYTE: - case TypeFlag::CHAR: - case TypeFlag::SHORT: { - if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) { - return static_cast(CastFloatingPointToIntOrLong(value)); - } - return static_cast(value); - } - case TypeFlag::INT: - case TypeFlag::LONG: { - if (source->HasTypeFlag(checker::TypeFlag::DOUBLE) || source->HasTypeFlag(checker::TypeFlag::FLOAT)) { - return CastFloatingPointToIntOrLong(value); - } - return static_cast(value); - } - case TypeFlag::FLOAT: - case TypeFlag::DOUBLE: { - return static_cast(value); - } - default: { - ES2PANDA_UNREACHABLE(); - } - } - } - - template - void ApplyNarrowing() - { - using SType = typename SourceType::UType; - using TType = typename TargetType::UType; - - if (Source()->HasTypeFlag(TypeFlag::CONSTANT)) { - SType value = reinterpret_cast(Source())->GetValue(); - if (!Relation()->InCastingContext() && Source()->HasTypeFlag(TypeFlag::ETS_FLOATING_POINT) && - Target()->HasTypeFlag(TypeFlag::ETS_INTEGRAL)) { - auto narrowedValue = CalculateNarrowedValue(Target(), Source(), value); - if (narrowedValue != value) { - Relation()->Result(RelationResult::ERROR); - return; - } - } - - if (Relation()->InCastingContext() || util::Helpers::IsTargetFitInSourceRange(value)) { - Relation()->Result(true); - return; - } - - Relation()->Result(RelationResult::ERROR); - return; - } - - Relation()->Result(true); - } -}; -} // namespace ark::es2panda::checker - -#endif diff --git a/ets2panda/checker/ets/narrowingWideningConverter.cpp b/ets2panda/checker/ets/narrowingWideningConverter.cpp deleted file mode 100644 index 41ed3aeb8cf47902ad35907f636e97f771c1ffd2..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/narrowingWideningConverter.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "narrowingWideningConverter.h" - -namespace ark::es2panda::checker { -} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/narrowingWideningConverter.h b/ets2panda/checker/ets/narrowingWideningConverter.h deleted file mode 100644 index 8aa21fea56ca759e8d6d5d6940406dd6d6c0212e..0000000000000000000000000000000000000000 --- a/ets2panda/checker/ets/narrowingWideningConverter.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021 - 2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_WIDENING_CONVERTER_H -#define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_WIDENING_CONVERTER_H - -#include "checker/ets/narrowingConverter.h" -#include "checker/ets/wideningConverter.h" - -namespace ark::es2panda::checker { -class NarrowingWideningConverter : public NarrowingConverter { -public: - explicit NarrowingWideningConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) - : NarrowingConverter(checker, relation, target, source) - { - if (Relation()->IsTrue() || Relation()->IsError()) { - return; - } - - WideningConverter(checker, relation, target, source); - } -}; -} // namespace ark::es2panda::checker - -#endif diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 01a16ca70c3c962ad4282fdc7d0a66b6bd3acf20..f0869bbb568a3387e1809b3878506e8c004c1cf4 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -13,12 +13,15 @@ * limitations under the License. */ +#include +#include #include "checker/ETSchecker.h" #include "checker/ets/typeRelationContext.h" -#include "checker/types/ets/etsDynamicType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsTupleType.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "checker/types/gradualType.h" +#include "compiler/lowering/phase.h" #include "ir/base/classDefinition.h" #include "ir/base/classElement.h" #include "ir/base/classProperty.h" @@ -80,8 +83,40 @@ static bool CheckGetterSetterDecl(varbinder::LocalVariable const *child, varbind return checkChild && checkParent && (child->TsType()->IsETSFunctionType() || parent->TsType()->IsETSFunctionType()); } +static bool CheckOverloadDecl(varbinder::LocalVariable *child, varbinder::LocalVariable *parent) +{ + if (!child->Declaration()->Node()->IsOverloadDeclaration() && + !parent->Declaration()->Node()->IsOverloadDeclaration()) { + return false; + } + + if (!child->Declaration()->Node()->IsOverloadDeclaration() || + !parent->Declaration()->Node()->IsOverloadDeclaration()) { + return true; + } + + auto *childOverload = child->Declaration()->Node()->AsOverloadDeclaration(); + auto *parentOverload = parent->Declaration()->Node()->AsOverloadDeclaration(); + for (auto *baseMethodName : parentOverload->OverloadedList()) { + ES2PANDA_ASSERT(baseMethodName->IsIdentifier()); + auto res = std::find_if(childOverload->OverloadedList().begin(), childOverload->OverloadedList().end(), + [baseMethodName](ir::Expression *subMethodName) { + return subMethodName->IsIdentifier() && subMethodName->AsIdentifier()->Name() == + baseMethodName->AsIdentifier()->Name(); + }); + if (res == childOverload->OverloadedList().end()) { + return false; + } + } + return true; +} + static bool CheckFunctionDecl(varbinder::LocalVariable *child, varbinder::LocalVariable *parent) { + if (child->Declaration()->Node()->IsOverloadDeclaration() || + parent->Declaration()->Node()->IsOverloadDeclaration()) { + return false; + } ES2PANDA_ASSERT(child->Declaration()->Type() == parent->Declaration()->Type()); if (!child->TsType()->IsETSMethodType() || !parent->TsType()->IsETSMethodType()) { return true; @@ -154,7 +189,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return false; } - Type *superType = classDef->Super()->AsTypeNode()->GetType(this); + auto *superType = classDef->Super()->AsTypeNode()->GetType(this)->MaybeBaseTypeOfGradualType(); if (superType == nullptr) { return true; } @@ -164,7 +199,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return true; } - ETSObjectType *superObj = superType->AsETSObjectType(); + ETSObjectType *superObj = superType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); // struct node has class definition, too if (superObj->GetDeclNode()->Parent()->IsETSStructDeclaration()) { @@ -187,6 +222,11 @@ void ETSChecker::ValidateImplementedInterface(ETSObjectType *type, Type *interfa std::unordered_set *extendsSet, const lexer::SourcePosition &pos) { ES2PANDA_ASSERT(interface != nullptr); + interface = interface->MaybeBaseTypeOfGradualType(); + if (interface->IsETSObjectType() && interface->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + LogError(diagnostic::INTERFACE_EXTENDS_CLASS, {}, pos); + return; + } if (!interface->IsETSObjectType() || !interface->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { LogError(diagnostic::NOT_INTERFACE, {}, pos); return; @@ -322,6 +362,7 @@ bool ETSChecker::CheckDefaultTypeParameter(const ir::TSTypeParameter *param, Typ typeParameterDecls.count(variable) == 0U) { LogError(diagnostic::TYPE_PARAM_USE_BEFORE_DEFINE, {defaultTypePart->Name()->AsIdentifier()->Name().Utf8()}, node->Start()); + variable->SetTsType(GlobalTypeError()); ok = false; } else if (defaultType != nullptr && defaultType->IsTypeError()) { ok = false; @@ -331,6 +372,12 @@ bool ETSChecker::CheckDefaultTypeParameter(const ir::TSTypeParameter *param, Typ }; if (param->DefaultType() != nullptr) { + TypeStackElement tse(this, param->DefaultType(), {{diagnostic::TYPE_PARAM_CIRCULAR_DEFAULT_TYPE}}, + param->DefaultType()->Start()); + if (tse.HasTypeError()) { + return false; + } + param->DefaultType()->Iterate(checkDefault); } @@ -401,7 +448,6 @@ ETSTypeParameter *ETSChecker::SetUpParameterType(ir::TSTypeParameter *const para paramType->SetVariable(param->Variable()); // NOTE: #15026 recursive type parameter workaround paramType->SetConstraintType(GlobalETSAnyType()); - var->SetTsType(paramType); return paramType; } @@ -424,6 +470,14 @@ void ETSChecker::CreateTypeForClassOrInterfaceTypeParameters(ETSObjectType *type type->AddObjectFlag(ETSObjectFlags::INCOMPLETE_INSTANTIATION); } +Type *ETSChecker::MaybeGradualType(ir::AstNode *node, ETSObjectType *type) +{ + ES2PANDA_ASSERT(node->IsClassDefinition() || node->IsTSInterfaceDeclaration()); + auto isDynamic = node->IsClassDefinition() ? node->AsClassDefinition()->Language().IsDynamic() + : node->AsTSInterfaceDeclaration()->Language().IsDynamic(); + return isDynamic ? CreateGradualType(type) : type; +} + Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *interfaceDecl) { auto *var = interfaceDecl->Id()->Variable(); @@ -433,12 +487,15 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte } checker::ETSObjectType *interfaceType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { interfaceType = CreateETSObjectTypeOrBuiltin(interfaceDecl, checker::ETSObjectFlags::INTERFACE); interfaceType->SetVariable(var); - var->SetTsType(interfaceType); - } else if (var->TsType()->IsETSObjectType()) { - interfaceType = var->TsType()->AsETSObjectType(); + type = MaybeGradualType(interfaceDecl, interfaceType); + var->SetTsType(type); + } else if (var->TsType()->MaybeBaseTypeOfGradualType()->IsETSObjectType()) { + interfaceType = var->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + type = MaybeGradualType(interfaceDecl, interfaceType); } else { ES2PANDA_ASSERT(IsAnyError()); return GlobalTypeError(); @@ -468,7 +525,40 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte CheckInterfaceFunctions(interfaceType); } - return interfaceType; + return type; +} + +void ETSChecker::CheckDynamicInheritanceAndImplement(ETSObjectType *const interfaceOrClassType) +{ + auto getTypeString = [](ETSObjectType *type) { return type->IsInterface() ? "interface" : "class"; }; + auto extendsOrImplements = [](ETSObjectType *type) { return type->IsInterface() ? "extends" : "implements"; }; + auto isFromDynamicDecl = [](ETSObjectType *type) { + auto declNode = type->GetDeclNode(); + if (declNode->IsTSInterfaceDeclaration()) { + return declNode->AsTSInterfaceDeclaration()->Language().IsDynamic(); + } + if (declNode->IsClassDefinition()) { + return declNode->AsClassDefinition()->Language().IsDynamic(); + } + return false; + }; + if (isFromDynamicDecl(interfaceOrClassType)) { + return; + } + for (ETSObjectType *interType : interfaceOrClassType->Interfaces()) { + if (isFromDynamicDecl(interType)) { + LogError(diagnostic::INTERFACE_OR_CLASS_CANNOT_IMPL_OR_EXTEND_DYNAMIC, + {getTypeString(interfaceOrClassType), interfaceOrClassType->Name(), + extendsOrImplements(interfaceOrClassType), getTypeString(interType), interType->Name()}, + interfaceOrClassType->GetDeclNode()->Start()); + } + } + if (interfaceOrClassType->SuperType() != nullptr && isFromDynamicDecl(interfaceOrClassType->SuperType())) { + LogError(diagnostic::INTERFACE_OR_CLASS_CANNOT_IMPL_OR_EXTEND_DYNAMIC, + {getTypeString(interfaceOrClassType), interfaceOrClassType->Name(), "extends", + getTypeString(interfaceOrClassType->SuperType()), interfaceOrClassType->SuperType()->Name()}, + interfaceOrClassType->GetDeclNode()->Start()); + } } Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) @@ -484,21 +574,24 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) } checker::ETSObjectType *classType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { classType = CreateETSObjectTypeOrBuiltin(classDef, checker::ETSObjectFlags::CLASS); + type = MaybeGradualType(classDef, classType); classType->SetVariable(var); - var->SetTsType(classType); + var->SetTsType(type); if (classDef->IsAbstract()) { classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); } - } else if (var->TsType()->IsETSObjectType()) { - classType = var->TsType()->AsETSObjectType(); + } else if (var->TsType()->MaybeBaseTypeOfGradualType()->IsETSObjectType()) { + classType = var->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + type = MaybeGradualType(classDef, classType); } else { ES2PANDA_ASSERT(IsAnyError()); return GlobalTypeError(); } - classDef->SetTsType(classType); + classDef->SetTsType(type); ConstraintCheckScope ctScope(this); if (classDef->TypeParams() != nullptr) { @@ -522,7 +615,7 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) GetInterfaces(classType); } ctScope.TryCheckConstraints(); - return classType; + return type; } ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType) @@ -560,50 +653,58 @@ static void ResolveDeclaredMethodsOfObject(ETSChecker *checker, const ETSObjectT { for (auto &[_, it] : scope->InstanceMethodScope()->Bindings()) { (void)_; - auto *method = it->Declaration()->Node()->AsMethodDefinition(); - auto *function = method->Function(); - ES2PANDA_ASSERT(function != nullptr); - if (function->IsProxy()) { - continue; - } + if (it->Declaration()->Node()->IsMethodDefinition()) { + auto *method = it->Declaration()->Node()->AsMethodDefinition(); + auto *function = method->Function(); + ES2PANDA_ASSERT(function != nullptr); + if (function->IsProxy()) { + continue; + } - it->AddFlag(checker->GetAccessFlagFromNode(method)); - auto *funcType = checker->BuildMethodSignature(method); - if (!funcType->IsTypeError()) { - funcType->SetVariable(it); + it->AddFlag(checker->GetAccessFlagFromNode(method)); + auto *funcType = checker->BuildMethodSignature(method); + if (!funcType->IsTypeError()) { + funcType->SetVariable(it); + } + it->SetTsType(funcType); + method->SetTsType(funcType); + type->AddProperty(it->AsLocalVariable()); + } else if (it->Declaration()->Node()->IsOverloadDeclaration()) { + type->AddProperty(it->AsLocalVariable()); + } else { + ES2PANDA_UNREACHABLE(); } - it->SetTsType(funcType); - method->SetTsType(funcType); - type->AddProperty(it->AsLocalVariable()); } for (auto &[_, it] : scope->StaticMethodScope()->Bindings()) { (void)_; - if (!it->Declaration()->Node()->IsMethodDefinition()) { - continue; - } + if (it->Declaration()->Node()->IsMethodDefinition()) { + auto *method = it->Declaration()->Node()->AsMethodDefinition(); + auto *function = method->Function(); + ES2PANDA_ASSERT(function != nullptr); + if (function->IsProxy()) { + continue; + } - auto *method = it->Declaration()->Node()->AsMethodDefinition(); - auto *function = method->Function(); - ES2PANDA_ASSERT(function != nullptr); - if (function->IsProxy()) { - continue; - } + it->AddFlag(checker->GetAccessFlagFromNode(method)); + auto *funcType = checker->BuildMethodSignature(method); + if (!funcType->IsTypeError()) { + funcType->SetVariable(it); + } + it->SetTsType(funcType); + method->SetTsType(funcType); - it->AddFlag(checker->GetAccessFlagFromNode(method)); - auto *funcType = checker->BuildMethodSignature(method); - if (!funcType->IsTypeError()) { - funcType->SetVariable(it); - } - it->SetTsType(funcType); - method->SetTsType(funcType); + if (method->IsConstructor() && funcType->IsETSFunctionType()) { + type->AddConstructSignature(funcType->AsETSFunctionType()->CallSignatures()); + continue; + } - if (method->IsConstructor() && funcType->IsETSFunctionType()) { - type->AddConstructSignature(funcType->AsETSFunctionType()->CallSignatures()); + type->AddProperty(it->AsLocalVariable()); + } else if (it->Declaration()->Node()->IsOverloadDeclaration()) { + type->AddProperty(it->AsLocalVariable()); + } else { continue; } - - type->AddProperty(it->AsLocalVariable()); } } @@ -629,6 +730,10 @@ static void ResolveDeclaredDeclsOfObject(ETSChecker *checker, const ETSObjectTyp void ETSChecker::ResolveDeclaredMembersOfObject(const Type *type) { + if (type->IsGradualType()) { + return ResolveDeclaredMembersOfObject(type->AsGradualType()->GetBaseType()); + } + if (!type->IsETSObjectType() || type->AsETSObjectType()->IsPropertiesInstantiated()) { return; } @@ -709,6 +814,10 @@ std::vector ETSChecker::CollectAbstractSignaturesFromObject(const E { std::vector abstracts; for (const auto &prop : objType->Methods()) { + if (prop->Declaration()->Node()->IsOverloadDeclaration()) { + continue; + } + GetTypeOfVariable(prop); if (!prop->TsType()->IsETSFunctionType()) { @@ -911,6 +1020,10 @@ void ETSChecker::CheckInterfaceFunctions(ETSObjectType *classType) for (auto *const &interface : interfaces) { for (auto *const &prop : interface->Methods()) { + if (prop->Declaration()->Node()->IsOverloadDeclaration()) { + continue; + } + ir::MethodDefinition *node = prop->Declaration()->Node()->AsMethodDefinition(); if (prop->TsType()->IsTypeError()) { continue; @@ -1045,6 +1158,10 @@ void ETSChecker::ApplyModifiersAndRemoveImplementedAbstracts(ArenaVectorFields()) { + if (field->Declaration()->Node()->AsClassProperty()->IsStatic()) { + continue; + } + if (field->Name() == (*it)->Name()) { field->Declaration()->Node()->AddModifier(isGetSetExternal.isGetter && isGetSetExternal.isSetter ? ir::ModifierFlags::GETTER_SETTER @@ -1100,6 +1217,9 @@ void ETSChecker::MaybeReportErrorsForOverridingValidation(ArenaVectorCallSignatures().front(); auto containingObjectName = GetContainingObjectNameFromSignature(unimplementedSignature); + if (unimplementedSignature->HasSignatureFlag(SignatureFlags::DEFAULT)) { + return; + } if (unimplementedSignature->HasSignatureFlag(SignatureFlags::GETTER)) { LogError(diagnostic::GETTER_MISSING_IMPL, {classType->Name(), unimplementedSignature->Function()->Id()->Name(), containingObjectName}, pos); @@ -1119,7 +1239,7 @@ void ETSChecker::MaybeReportErrorsForOverridingValidation(ArenaVectorHasObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS)) { + if (GetCachedComputedAbstracts()->find(classType) != GetCachedComputedAbstracts()->end()) { return; } @@ -1151,8 +1271,6 @@ void ETSChecker::ValidateOverriding(ETSObjectType *classType, const lexer::Sourc } while (superIter != nullptr); ValidateAbstractMethodsToBeImplemented(abstractsToBeImplemented, classType, implementedSignatures); MaybeReportErrorsForOverridingValidation(abstractsToBeImplemented, classType, pos, throwError); - - classType->AddObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS); } void ETSChecker::AddImplementedSignature(std::vector *implementedSignatures, @@ -1205,7 +1323,7 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) return; } - auto *classType = classDef->TsType()->AsETSObjectType(); + auto *classType = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); if (classType->SuperType() != nullptr) { classType->SuperType()->GetDeclNode()->Check(this); } @@ -1263,6 +1381,7 @@ void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) return; } + CheckDynamicInheritanceAndImplement(classType); CheckConstructors(classDef, classType); CheckValidInheritance(classType, classDef); CheckConstFields(classType); @@ -1542,85 +1661,65 @@ void ETSChecker::CheckInnerClassMembers(const ETSObjectType *classType) } } -lexer::Number ETSChecker::ExtractNumericValue(Type const *const indexType) -{ - TypeFlag typeKind = ETSType(indexType); - lexer::Number resNum; - switch (typeKind) { - case TypeFlag::BYTE: { - resNum = lexer::Number(indexType->AsByteType()->GetValue()); - break; - } - case TypeFlag::SHORT: { - resNum = lexer::Number(indexType->AsShortType()->GetValue()); - break; - } - case TypeFlag::INT: { - resNum = lexer::Number(indexType->AsIntType()->GetValue()); - break; - } - case TypeFlag::FLOAT: { - resNum = lexer::Number(indexType->AsFloatType()->GetValue()); - break; - } - case TypeFlag::DOUBLE: { - resNum = lexer::Number(indexType->AsDoubleType()->GetValue()); - break; - } - default: - break; - } - return resNum; -} - bool ETSChecker::ValidateArrayIndex(ir::Expression *const expr, bool relaxed) { - auto const expressionType = expr->Check(this); + auto const expressionType = expr->Check(this)->MaybeBaseTypeOfGradualType(); if (expressionType->IsTypeError()) { return false; } - Type const *const unboxedExpressionType = MaybeUnboxInRelation(expressionType); - if (expressionType->IsETSObjectType() && (unboxedExpressionType != nullptr)) { - expr->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedExpressionType)); + if (!expressionType->IsETSObjectType() || + (!expressionType->AsETSObjectType()->HasObjectFlag(relaxed ? ETSObjectFlags::BUILTIN_ARRAY_NUMERIC + : ETSObjectFlags::BUILTIN_ARRAY_INDEX))) { + LogError(diagnostic::INVALID_INDEX_TYPE, {expressionType->ToString()}, expr->Start()); + return false; } - Type const *const indexType = ApplyUnaryOperatorPromotion(expressionType); - - if (relaxed && indexType != nullptr) { - lexer::Number resNum = ExtractNumericValue(indexType); - double value = resNum.GetDouble(); - double intpart; - if (std::modf(value, &intpart) != 0.0) { - LogError(diagnostic::INDEX_NONINTEGRAL_FLOAT, {}, expr->Start()); - return false; - } - bool tildeFlag = false; - if (expr->IsUnaryExpression() && - expr->AsUnaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_TILDE) { - tildeFlag = true; - } - if ((tildeFlag && value > 0) || (!tildeFlag && value < 0)) { - LogError(diagnostic::NEGATIVE_INDEX, {}, expr->Start()); - return false; - } + if (!relaxed || !expressionType->IsConstantType() || !expr->IsNumberLiteral()) { + return true; } - if (indexType == nullptr || - (!indexType->HasTypeFlag(relaxed ? (TypeFlag::ETS_ARRAY_INDEX | TypeFlag::ETS_FLOATING_POINT) - : TypeFlag::ETS_ARRAY_INDEX))) { - std::stringstream message(""); - expressionType->ToString(message); + ES2PANDA_ASSERT(expr->IsNumberLiteral()); + double value = expr->AsNumberLiteral()->Number().GetDouble(); + + double intPart; + if (std::modf(value, &intPart) != 0.0) { + LogError(diagnostic::INDEX_NONINTEGRAL_FLOAT, {}, expr->Start()); + return false; + } - LogError(diagnostic::INVALID_INDEX_TYPE, {message.str()}, expr->Start()); + if (intPart < 0.0) { + LogError(diagnostic::NEGATIVE_INDEX, {}, expr->Start()); return false; } return true; } -std::optional ETSChecker::GetTupleElementAccessValue(const Type *const type) +std::optional ETSChecker::GetTupleElementAccessValue(const ir::Expression *expr) { + auto checkLongValBounds = [this](int64_t val, const lexer::SourcePosition &p) -> std::optional { + if (val < 0) { + LogError(diagnostic::TUPLE_INDEX_OOB, {}, p); + return std::nullopt; + } + return static_cast(val); + }; + + if (expr->IsNumberLiteral()) { + auto num = expr->AsNumberLiteral()->Number(); + if (num.IsInt()) { + return checkLongValBounds(num.GetInt(), expr->Start()); + } + if (num.IsLong()) { + return checkLongValBounds(num.GetLong(), expr->Start()); + } + ES2PANDA_UNREACHABLE(); + } + + // Below code should be unreachable after removing primitives + auto type = expr->TsType(); + ES2PANDA_ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)); switch (ETSType(type)) { @@ -1649,14 +1748,9 @@ std::optional ETSChecker::GetTupleElementAccessValue(const Type *co bool ETSChecker::ValidateTupleIndex(const ETSTupleType *const tuple, ir::MemberExpression *const expr, const bool reportError) { - auto const expressionType = expr->Property()->Check(this); - auto const *const unboxedExpressionType = MaybeUnboxInRelation(expressionType); - - if (expressionType->IsETSObjectType() && (unboxedExpressionType != nullptr)) { - expr->Property()->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxedExpressionType)); - } + auto const exprType = expr->Property()->Check(this); + auto const *const unboxedExpressionType = MaybeUnboxInRelation(exprType); - const auto *const exprType = expr->Property()->TsType(); ES2PANDA_ASSERT(exprType != nullptr); if (!exprType->HasTypeFlag(TypeFlag::CONSTANT)) { @@ -1669,14 +1763,20 @@ bool ETSChecker::ValidateTupleIndex(const ETSTupleType *const tuple, ir::MemberE return false; } - if (!exprType->HasTypeFlag(TypeFlag::ETS_ARRAY_INDEX | TypeFlag::LONG)) { + if (!Relation()->IsSupertypeOf(GlobalIntBuiltinType(), exprType) && + !Relation()->IsSupertypeOf(GlobalLongBuiltinType(), exprType)) { + LogError(diagnostic::TUPLE_INDEX_NOT_INT, {}, expr->Property()->Start()); + return false; + } + + if (expr->Property()->IsAssignmentExpression()) { if (reportError) { - LogError(diagnostic::TUPLE_INDEX_NOT_INT, {}, expr->Property()->Start()); + LogError(diagnostic::TUPLE_INDEX_NONCONST, {}, expr->Property()->Start()); } return false; } - auto exprValue = GetTupleElementAccessValue(exprType); + auto exprValue = GetTupleElementAccessValue(expr->Property()); if (!exprValue.has_value() || (*exprValue >= tuple->GetTupleSize())) { if (reportError) { LogError(diagnostic::TUPLE_INDEX_OOB, {}, expr->Property()->Start()); @@ -1689,6 +1789,16 @@ bool ETSChecker::ValidateTupleIndex(const ETSTupleType *const tuple, ir::MemberE bool ETSChecker::ValidateTupleIndexFromEtsObject(const ETSTupleType *const tuple, ir::MemberExpression *const expr) { + if (expr->Property() == nullptr || expr->Property()->Variable() == nullptr || + expr->Property()->Variable()->Declaration() == nullptr || + expr->Property()->Variable()->Declaration()->Node() == nullptr || + !(expr->Property()->Variable()->Declaration()->Node()->IsMethodDefinition() || + expr->Property()->Variable()->Declaration()->Node()->IsClassProperty() || + expr->Property()->Variable()->Declaration()->Node()->IsClassStaticBlock() || + expr->Property()->Variable()->Declaration()->Node()->IsOverloadDeclaration())) { + LogError(diagnostic::TUPLE_INDEX_NONCONST, {}, expr->Start()); + return false; + } auto *value = expr->Property()->Variable()->Declaration()->Node()->AsClassElement()->Value(); if (value == nullptr) { LogError(diagnostic::TUPLE_INDEX_NONCONST, {}, expr->Property()->Start()); @@ -1706,7 +1816,7 @@ bool ETSChecker::ValidateTupleIndexFromEtsObject(const ETSTupleType *const tuple return false; } - auto exprValue = GetTupleElementAccessValue(exprType); + auto exprValue = GetTupleElementAccessValue(expr); if (!exprValue.has_value() || (*exprValue >= tuple->GetTupleSize())) { LogError(diagnostic::TUPLE_INDEX_OOB, {}, expr->Property()->Start()); return false; @@ -1747,9 +1857,8 @@ ETSObjectType *ETSChecker::CheckThisOrSuperAccess(ir::Expression *node, ETSObjec return classType; } - if (classType->GetDeclNode()->IsClassDefinition() && - (classType->GetDeclNode()->AsClassDefinition()->IsGlobal() || - classType->GetDeclNode()->AsClassDefinition()->IsNamespaceTransformed())) { + if (classType == nullptr || + (classType->GetDeclNode()->IsClassDefinition() && classType->GetDeclNode()->AsClassDefinition()->IsGlobal())) { LogError(diagnostic::CTOR_REF_INVALID_CTX_GLOBAL, {msg}, node->Start()); return GlobalBuiltinErrorType(); } @@ -1797,6 +1906,9 @@ void ETSChecker::CheckCyclicConstructorCall(Signature *signature) ETSObjectType *ETSChecker::CheckExceptionOrErrorType(checker::Type *type, const lexer::SourcePosition pos) { + if (type->IsGradualType()) { + return CheckExceptionOrErrorType(type->AsGradualType()->GetBaseType(), pos); + } ES2PANDA_ASSERT(type != nullptr); if (!type->IsETSObjectType() || (!Relation()->IsAssignableTo(type, GlobalBuiltinExceptionType()) && !Relation()->IsAssignableTo(type, GlobalBuiltinErrorType()))) { @@ -1827,7 +1939,7 @@ void ETSChecker::ValidateNamespaceProperty(varbinder::Variable *property, const { ir::AstNode *parent = nullptr; if (property->TsType() != nullptr && !property->TsType()->IsTypeError()) { - if (property->TsType()->IsETSMethodType()) { + if (property->TsType()->IsETSMethodType() && !property->HasFlag(varbinder::VariableFlags::OVERLOAD)) { auto funcType = property->TsType()->AsETSFunctionType(); property = funcType->CallSignatures()[0]->OwnerVar(); ES2PANDA_ASSERT(property != nullptr); @@ -1964,10 +2076,14 @@ PropertySearchFlags ETSChecker::GetInitialSearchFlags(const ir::MemberExpression switch (memberExpr->Parent()->Type()) { case ir::AstNodeType::CALL_EXPRESSION: { if (memberExpr->Parent()->AsCallExpression()->Callee() == memberExpr) { - return FUNCTIONAL_FLAGS; + return PropertySearchFlags::SEARCH_ALL; } break; } + case ir::AstNodeType::OVERLOAD_DECLARATION: { + return FUNCTIONAL_FLAGS | GETTER_FLAGS; + break; + } case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { if (memberExpr->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == memberExpr) { return PropertySearchFlags::SEARCH_DECL; @@ -2011,6 +2127,9 @@ static bool ShouldRemoveStaticSearchFlag(const ir::MemberExpression *const membe if (object->IsMemberExpression()) { object = object->AsMemberExpression()->Property(); } + if (object->IsTypeNode()) { + return false; + } if (!object->IsIdentifier() || (object->AsIdentifier()->Variable() == nullptr) || object->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::INITIALIZED)) { return true; @@ -2051,6 +2170,9 @@ const varbinder::Variable *ETSChecker::GetTargetRef(const ir::MemberExpression * if (memberExpr->Object()->IsMemberExpression()) { return memberExpr->Object()->AsMemberExpression()->PropVar(); } + if (memberExpr->Object()->IsTypeNode() && memberExpr->Object()->TsType()->IsETSObjectType()) { + return memberExpr->Object()->TsType()->Variable(); + } return nullptr; } @@ -2220,19 +2342,37 @@ static ResolvedKind DecideResolvedKind(Type *typeOfGlobalFunctionVar) return ResolvedKind::EXTENSION_FUNCTION; } -// NOLINTNEXTLINE(readability-function-size) -std::vector ETSChecker::ResolveMemberReference(const ir::MemberExpression *const memberExpr, - const ETSObjectType *const target) +void ETSChecker::CheckAnnotationReference(const ir::MemberExpression *memberExpr, const varbinder::LocalVariable *prop) +{ + // Note: there might be a better way to handle annotations + if (prop != nullptr && prop->Declaration() != nullptr && prop->Declaration()->IsAnnotationDecl() && + memberExpr->Parent()->IsCallExpression()) { + LogError(diagnostic::ANNOTATION_INSTANTIATION, {prop->Declaration()->Name()}, memberExpr->Start()); + } +} + +std::vector ETSChecker::HandlePropertyResolution(varbinder::LocalVariable *const prop, + ir::MemberExpression *const memberExpr, + varbinder::Variable *const globalFunctionVar, + PropertySearchFlags searchFlag) { std::vector resolveRes {}; - if (target->IsETSDynamicType() && !target->AsETSDynamicType()->HasDecl()) { - auto propName = memberExpr->Property()->AsIdentifier()->Name(); - varbinder::LocalVariable *propVar = target->AsETSDynamicType()->GetPropertyDynamic(propName, this); - resolveRes.emplace_back(ProgramAllocator()->New(propVar, ResolvedKind::PROPERTY)); - return resolveRes; + if (prop != nullptr && IsVariableGetterSetter(prop) && + ((searchFlag & PropertySearchFlags::IS_GETTER) != 0 || (searchFlag & PropertySearchFlags::IS_SETTER) != 0)) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return ValidateAccessor(memberExpr, prop, globalFunctionVar, searchFlag); + } + if (prop != nullptr) { + resolveRes.emplace_back(ProgramAllocator()->New(prop, ResolvedKind::PROPERTY)); } + return resolveRes; +} +// NOLINTNEXTLINE(readability-function-size) +std::vector ETSChecker::ResolveMemberReference(const ir::MemberExpression *const memberExpr, + const ETSObjectType *const target) +{ if (target->GetDeclNode() != nullptr && target->GetDeclNode()->IsClassDefinition() && !target->GetDeclNode()->AsClassDefinition()->IsClassDefinitionChecked()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -2240,16 +2380,21 @@ 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); + + CheckAnnotationReference(memberExpr, prop); + varbinder::Variable *const globalFunctionVar = ResolveInstanceExtension(memberExpr); if (targetRef != nullptr && targetRef->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) { // Note: extension function only for instance. ValidateResolvedProperty(&prop, target, memberExpr->Property()->AsIdentifier(), searchFlag); - if (prop != nullptr) { - resolveRes.emplace_back(ProgramAllocator()->New(prop, ResolvedKind::PROPERTY)); - } - return resolveRes; + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return HandlePropertyResolution(prop, const_cast(memberExpr), globalFunctionVar, + searchFlag); } if (HasStatus(CheckerStatus::IN_GETTER)) { @@ -2263,6 +2408,7 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member return ValidateAccessor(const_cast(memberExpr), prop, globalFunctionVar, searchFlag); } + std::vector resolveRes {}; if (globalFunctionVar != nullptr) { ResolvedKind resolvedKind = DecideResolvedKind(globalFunctionVar->TsType()); if (IsExtensionAccessorCallUse(this, memberExpr, resolvedKind)) { @@ -2280,6 +2426,17 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member return resolveRes; } +varbinder::LocalVariable *ETSChecker::ResolveOverloadReference(const ir::Identifier *ident, ETSObjectType *objType, + PropertySearchFlags searchFlags) +{ + auto *var = objType->GetProperty(ident->Name(), searchFlags); + if (var == nullptr) { + return nullptr; + } + ValidatePropertyAccess(var, objType, ident->Start()); + return var; +} + void ETSChecker::WarnForEndlessLoopInGetterSetter(const ir::MemberExpression *const memberExpr) { if (!memberExpr->Object()->IsThisExpression() || memberExpr->Property() == nullptr || @@ -2308,9 +2465,6 @@ void ETSChecker::CheckValidInheritance(ETSObjectType *classType, ir::ClassDefini if (classType->SuperType() == nullptr) { return; } - if (classType->SuperType()->IsETSDynamicType()) { - LogError(diagnostic::EXTEND_DYNAMIC, {classDef->Ident()->Name()}, classDef->Start()); - } const auto &allProps = classType->GetAllProperties(); @@ -2370,6 +2524,8 @@ void ETSChecker::CheckProperties(ETSObjectType *classType, ir::ClassDefinition * if (CheckGetterSetterDecl(it, found)) { return; } + } else if (CheckOverloadDecl(it, found)) { + return; } else if (CheckFunctionDecl(it, found)) { return; } @@ -2388,6 +2544,8 @@ void ETSChecker::CheckProperties(ETSObjectType *classType, ir::ClassDefinition * targetType = "namespace"; } else if (it->HasFlag(varbinder::VariableFlags::ENUM_LITERAL)) { targetType = "enum"; + } else if (it->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + targetType = "overload"; } else { ES2PANDA_UNREACHABLE(); } @@ -2456,10 +2614,16 @@ void ETSChecker::TransformProperties(ETSObjectType *classType) GenerateGetterSetterPropertyAndMethod(originalProp, classType); } - auto it = classDef->Body().begin(); - while (it != classDef->Body().end()) { + auto &body = classDef->Body(); + if (!std::any_of(body.cbegin(), body.cend(), [](const ir::AstNode *node) { + return node->IsClassProperty() && (node->Modifiers() & ir::ModifierFlags::GETTER_SETTER) != 0U; + })) { + return; + } + auto it = classDef->BodyForUpdate().begin(); + while (it != classDef->BodyForUpdate().end()) { if ((*it)->IsClassProperty() && ((*it)->Modifiers() & ir::ModifierFlags::GETTER_SETTER) != 0U) { - it = classDef->Body().erase(it); + it = classDef->BodyForUpdate().erase(it); } else { ++it; } @@ -2523,21 +2687,40 @@ void ETSChecker::AddElementsToModuleObject(ETSObjectType *moduleObj, const util: // This function computes effective runtime view of type Type *ETSChecker::GetApparentType(Type *type) { - if (auto it = apparentTypes_.find(type); LIKELY(it != apparentTypes_.end())) { + auto currChecker = compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker(); + auto &apparentTypes = currChecker->apparentTypes_; + + if (auto it = apparentTypes.find(type); LIKELY(it != apparentTypes.end())) { return it->second; } - auto cached = [this, type](Type *res) { + + auto cached = [&apparentTypes, type](Type *res) { if (type != res) { - apparentTypes_.insert({type, res}); + apparentTypes.insert({type, res}); } - apparentTypes_.insert({res, res}); - return res; + return apparentTypes.insert({res, res}).first->second; }; + ES2PANDA_ASSERT(type != nullptr); + if (type->IsGradualType()) { + return cached(type->AsGradualType()->GetBaseType()); + } + if (type->IsETSTypeParameter()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return cached(GetApparentType(type->AsETSTypeParameter()->GetConstraintType())); } + + if (type->IsETSTypeAliasType()) { + // For the recursive type aliases its target type is not ready at the moment of union type creation + // (and assemblyLUB type constructing). + if (auto *targetType = type->AsETSTypeAliasType()->GetTargetType(); targetType != nullptr) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return cached(GetApparentType(targetType)); + } + return type; + } + if (type->IsETSNonNullishType()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return cached( @@ -2571,25 +2754,8 @@ Type *ETSChecker::GetApparentType(Type *type) Type const *ETSChecker::GetApparentType(Type const *type) const { - if (auto it = apparentTypes_.find(type); LIKELY(it != apparentTypes_.end())) { - return it->second; - } - ES2PANDA_ASSERT(type != nullptr); - // Relaxed for some types - if (type->IsETSTypeParameter()) { - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - return GetApparentType(type->AsETSTypeParameter()->GetConstraintType()); - } - if (type->IsETSArrayType()) { - return type; - } - if (type->IsETSStringType()) { - return GlobalBuiltinETSStringType(); - } - if (type->IsETSUnionType() || type->IsETSNonNullishType() || type->IsETSPartialTypeParameter()) { - ASSERT_PRINT(false, std::string("Type ") + type->ToString() + " was not found in apparent_types_"); - } - return type; + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return const_cast(const_cast(this)->GetApparentType(const_cast(type))); } ETSObjectType *ETSChecker::GetClosestCommonAncestor(ETSObjectType *source, ETSObjectType *target) diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 295960331c9583195923d7ffc00c2278d7a128fa..811f1cf614c4a31e2016ad1276d5001a8cf59c64 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -14,8 +14,9 @@ */ #include "checker/checker.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "ir/base/catchClause.h" @@ -58,16 +59,13 @@ void ETSChecker::CheckTruthinessOfType(ir::Expression *expr) if (conditionType == nullptr) { return; } + expr->SetTsType(MaybeBoxType(conditionType)); if (conditionType->IsETSVoidType()) { LogError(diagnostic::VOID_IN_LOGIC, {}, expr->Start()); return; } - if (conditionType->IsETSPrimitiveType()) { - FlagExpressionWithUnboxing(testType, conditionType, expr); - } - // For T_S compatibility if (conditionType->IsETSEnumType()) { expr->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); @@ -80,6 +78,10 @@ bool ETSChecker::CheckNonNullish(ir::Expression const *expr) return true; } + if (expr->TsType()->IsETSPartialTypeParameter()) { + return true; + } + if (HasStatus(checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK)) { return false; } @@ -90,6 +92,10 @@ bool ETSChecker::CheckNonNullish(ir::Expression const *expr) Type *ETSChecker::GetNonNullishType(Type *type) { + if (type->IsGradualType()) { + return CreateGradualType(GetNonNullishType(type->AsGradualType()->GetBaseType()), + type->AsGradualType()->Language()); + } if (type->DefinitelyNotETSNullish()) { return type; } @@ -122,6 +128,10 @@ Type *ETSChecker::RemoveNullType(Type *const type) return type; } + if (type->IsETSPartialTypeParameter()) { + return type; + } + if (type->IsETSTypeParameter()) { // Strict equality produces incorrect NonNullish types #21526 return type; @@ -155,6 +165,10 @@ Type *ETSChecker::RemoveUndefinedType(Type *const type) return type; } + if (type->IsETSPartialTypeParameter()) { + return type; + } + if (type->IsETSUndefinedType()) { return GetGlobalTypesHolder()->GlobalETSNeverType(); } @@ -182,6 +196,11 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) return {type, type}; } + if (type->IsETSPartialTypeParameter()) { + return {GetGlobalTypesHolder()->GlobalETSNeverType(), + ProgramAllocator()->New(type->AsETSPartialTypeParameter()->GetUnderlying())}; + } + if (type->IsETSTypeParameter()) { return {GetGlobalTypesHolder()->GlobalETSUnionUndefinedNull(), ProgramAllocator()->New(type->AsETSTypeParameter())}; @@ -195,7 +214,8 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) ArenaVector nullishTypes(ProgramAllocator()->Adapter()); ArenaVector notNullishTypes(ProgramAllocator()->Adapter()); - for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) { + for (auto *ctype : type->AsETSUnionType()->ConstituentTypes()) { + auto constituentType = ctype->MaybeBaseTypeOfGradualType(); if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) { nullishTypes.push_back(constituentType); } else { @@ -217,6 +237,9 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) template static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv) { + if (type->IsGradualType()) { + return MatchConstituentOrConstraint(type->AsGradualType()->GetBaseType(), pred, trv); + } auto const traverse = [&pred, &trv](const Type *ttype) { return MatchConstituentOrConstraint(ttype, pred, trv); }; @@ -268,6 +291,30 @@ bool Type::PossiblyETSUndefined() const [](const Type *t) { return !t->IsETSNonNullishType(); }); } +static bool ObjectPossiblyInForeignDomain(ETSObjectType const *type) +{ + if (type->IsGlobalETSObjectType()) { + return true; + } + auto dnode = type->GetDeclNode(); + if (dnode == nullptr) { + return false; + } + auto lang = dnode->IsClassDefinition() ? dnode->AsClassDefinition()->Language() + : dnode->AsTSInterfaceDeclaration()->Language(); + return lang != Language::Id::ETS; +} + +bool Type::PossiblyInForeignDomain() const +{ + return MatchConstituentOrConstraint( + this, + [](const Type *t) { + return t->IsETSAnyType() || (t->IsETSObjectType() && ObjectPossiblyInForeignDomain(t->AsETSObjectType())); + }, + []([[maybe_unused]] const Type *t) { return true; }); +} + bool Type::PossiblyETSNullish() const { return MatchConstituentOrConstraint( @@ -338,7 +385,7 @@ bool Type::IsETSMethodType() const TypeFlag::ETS_TYPE_PARAMETER | TypeFlag::WILDCARD | TypeFlag::ETS_NONNULLISH | TypeFlag::ETS_REQUIRED_TYPE_PARAMETER | TypeFlag::ETS_ANY | TypeFlag::ETS_NEVER | TypeFlag::ETS_UNION | TypeFlag::ETS_ARRAY | TypeFlag::FUNCTION | TypeFlag::ETS_PARTIAL_TYPE_PARAMETER | TypeFlag::ETS_TUPLE | - TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY; + TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE; // Issues if (type->IsETSVoidType()) { // NOTE(vpukhov): #19701 void refactoring @@ -347,9 +394,6 @@ bool Type::IsETSMethodType() const if (type->IsETSTypeAliasType()) { // NOTE(vpukhov): #20561 return true; } - if (type->IsNeverType()) { // NOTE(vpukhov): #20562 We use ets/never and ts/never simultaneously - ES2PANDA_UNREACHABLE(); - } return type->HasTypeFlag(ETS_SANE_REFERENCE_TYPE); } @@ -395,10 +439,13 @@ Type *ETSChecker::GetNonConstantType(Type *type) } if (type->IsETSUnionType()) { - return CreateETSUnionType(ETSUnionType::GetNonConstantTypes(this, type->AsETSUnionType()->ConstituentTypes())); + return CreateETSUnionType(type->AsETSUnionType()->GetNonConstantTypes(this)); } if (!type->IsETSPrimitiveType()) { + if (type->IsETSObjectType() && type->AsETSObjectType()->IsBoxedPrimitive()) { + type->RemoveTypeFlag(TypeFlag::CONSTANT); + } return type; } @@ -450,12 +497,38 @@ Type *ETSChecker::GetTypeOfSetterGetter(varbinder::Variable *const var) return GlobalTypeError(); } - if (propType->FindSetter()->Params().empty()) { - var->SetTsType(GlobalTypeError()); - return GlobalTypeError(); + return propType->FindSetter()->Params()[0]->TsType(); +} + +Type *ETSChecker::CreateSyntheticTypeFromOverload(varbinder::Variable *const var) +{ + auto *overloadDeclaration = var->Declaration()->Node()->AsOverloadDeclaration(); + std::vector signatures; + ETSFunctionType *syntheticFunctionType = + CreateETSMethodType(overloadDeclaration->Id()->Name(), {{}, Allocator()->Adapter()}); + + for (auto *overloadFunction : overloadDeclaration->OverloadedList()) { + Type *functionType = overloadFunction->Check(this); + if (functionType->IsTypeError()) { + overloadDeclaration->SetTsType(GetGlobalTypesHolder()->GlobalTypeError()); + return GetGlobalTypesHolder()->GlobalTypeError(); + } + ES2PANDA_ASSERT(functionType->IsETSFunctionType()); + auto *signature = functionType->AsETSFunctionType()->CallSignatures().front(); + if (std::find(signatures.begin(), signatures.end(), signature) != signatures.end()) { + continue; + } + signatures.emplace_back(signature); } - return propType->FindSetter()->Params()[0]->TsType(); + for (auto &s : signatures) { + syntheticFunctionType->AddCallSignature(s); + } + + syntheticFunctionType->SetVariable(var); + var->SetTsType(syntheticFunctionType); + overloadDeclaration->SetTsType(syntheticFunctionType); + return syntheticFunctionType; } void ETSChecker::IterateInVariableContext(varbinder::Variable *const var) @@ -476,10 +549,10 @@ void ETSChecker::IterateInVariableContext(varbinder::Variable *const var) Type *containingClass {}; if (classDef->TsType() == nullptr) { - containingClass = BuildBasicClassProperties(classDef); - ResolveDeclaredMembersOfObject(containingClass->AsETSObjectType()); + containingClass = BuildBasicClassProperties(classDef)->MaybeBaseTypeOfGradualType(); + ResolveDeclaredMembersOfObject(containingClass); } else { - containingClass = classDef->TsType()->AsETSObjectType(); + containingClass = classDef->TsType()->MaybeBaseTypeOfGradualType()->AsETSObjectType(); } ES2PANDA_ASSERT(classDef->TsType()); @@ -512,7 +585,7 @@ static Type *GetTypeFromVarLikeVariableDeclaration(ETSChecker *checker, varbinde var->SetTsType(checker->GlobalTypeError()); return checker->GlobalTypeError(); } - return declNode->Check(checker); + return var->SetTsType(declNode->Check(checker)); } Type *ETSChecker::GetTypeFromVariableDeclaration(varbinder::Variable *const var) @@ -578,15 +651,6 @@ Type *ETSChecker::GetTypeOfVariable(varbinder::Variable *const var) return var->TsType(); } - // NOTE: kbaladurin. forbid usage of imported entities as types without declarations - if (VarBinder()->AsETSBinder()->IsDynamicModuleVariable(var)) { - auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var); - ES2PANDA_ASSERT(importData != nullptr); - if (importData->import->IsPureDynamic()) { - return GlobalBuiltinDynamicType(importData->import->Language()); - } - } - checker::SavedCheckerContext savedContext(this, CheckerStatus::NO_OPTS); checker::ScopeContext scopeCtx(this, var->GetScope()); IterateInVariableContext(var); @@ -646,6 +710,7 @@ Type *ETSChecker::GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable * case ir::AstNodeType::METHOD_DEFINITION: case ir::AstNodeType::CLASS_DEFINITION: return GetTypeOfVariable(prop); + case ir::AstNodeType::OVERLOAD_DECLARATION: case ir::AstNodeType::TS_ENUM_DECLARATION: return nullptr; default: @@ -678,8 +743,7 @@ Type *ETSChecker::ResolveUnionUncheckedType(ArenaVector &&appar auto *unionType = CreateETSUnionType(std::move(apparentTypes)); ES2PANDA_ASSERT(unionType != nullptr); if (unionType->IsETSUnionType()) { - checker::Type *typeLUB = unionType->AsETSUnionType()->GetAssemblerLUB(); - return typeLUB; + return unionType->AsETSUnionType(); } // Is case of single apparent type, just return itself return unionType; @@ -690,10 +754,15 @@ Type *ETSChecker::GuaranteedTypeForUnionFieldAccess(ir::MemberExpression *member const auto &types = etsUnionType->ConstituentTypes(); ArenaVector apparentTypes {ProgramAllocator()->Adapter()}; const auto *prop = memberExpression->Property(); - if (!prop->IsIdentifier() && !prop->IsStringLiteral()) { + util::StringView propertyName; + if (prop->IsIdentifier()) { + propertyName = prop->AsIdentifier()->Name(); + } else if (prop->IsStringLiteral()) { + propertyName = prop->AsStringLiteral()->Str(); + } else { return GlobalTypeError(); } - const auto &propertyName = prop->IsIdentifier() ? prop->AsIdentifier()->Name() : prop->AsStringLiteral()->Str(); + for (auto *type : types) { auto searchFlags = PropertySearchFlags::SEARCH_FIELD | PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::SEARCH_IN_BASE; @@ -981,7 +1050,7 @@ void ETSChecker::CheckFunctionSignatureAnnotations(const ArenaVectorIsETSParameterExpression()) { CheckAnnotations(param->AsETSParameterExpression()->Annotations()); if (param->AsETSParameterExpression()->TypeAnnotation() != nullptr) { - param->AsETSParameterExpression()->TypeAnnotation()->Check(this); + CheckAnnotations(param->AsETSParameterExpression()->TypeAnnotation()->Annotations()); } } } @@ -1036,13 +1105,14 @@ void ETSChecker::ValidateThisUsage(const ir::TypeNode *returnTypeAnnotation) return; } if (returnTypeAnnotation->IsETSTypeReference() && - IsFixedArray(returnTypeAnnotation->AsETSTypeReference()->Part()) && - returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams() != nullptr) { - auto elementType = returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams()->Params()[0]; - if (CheckAndLogInvalidThisUsage(elementType, diagnostic::NOT_ALLOWED_THIS_IN_ARRAY_TYPE)) { - return; + IsFixedArray(returnTypeAnnotation->AsETSTypeReference()->Part())) { + if (returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams() != nullptr) { + auto elementType = returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams()->Params()[0]; + if (CheckAndLogInvalidThisUsage(elementType, diagnostic::NOT_ALLOWED_THIS_IN_ARRAY_TYPE)) { + return; + } + ValidateThisUsage(elementType); } - ValidateThisUsage(elementType); return; } } @@ -1134,21 +1204,75 @@ void ETSChecker::CheckStandardAnnotation(ir::AnnotationUsage *anno) } } +static auto IsNonArrayLiteral(ir::Expression *init) +{ + if ((init == nullptr) || init->IsLiteral()) { + return true; + } + + if (init->TsType() != nullptr && init->TsType()->IsETSEnumType() && + init->TsType()->AsETSEnumType()->NodeIsEnumLiteral(init)) { + return true; + } + return false; +} + +static auto IsValidAnnotationPropInitializer(ir::Expression *init) +{ + if (IsNonArrayLiteral(init)) { + return true; + } + + if (init->IsArrayExpression()) { + for (auto elem : init->AsArrayExpression()->Elements()) { + if (!IsValidAnnotationPropInitializer(elem)) { + return false; + } + } + return true; + } + return false; +} + +static bool ValidateAnnotationPropertyType(checker::Type *type, ETSChecker *checker) +{ + if (type == nullptr || type->IsTypeError()) { + ES2PANDA_ASSERT(checker->IsAnyError()); + return false; + } + + if (type->IsETSArrayType() || type->IsETSResizableArrayType()) { + return ValidateAnnotationPropertyType(checker->GetElementTypeOfArray(type), checker); + } + auto relation = checker->Relation(); + return type->IsETSEnumType() || type->IsETSStringType() || + (type->IsETSObjectType() && (relation->IsSupertypeOf(checker->GlobalETSBooleanBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalByteBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalShortBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalIntBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalLongBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalFloatBuiltinType(), type) || + relation->IsSupertypeOf(checker->GlobalDoubleBuiltinType(), type))); +} + void ETSChecker::CheckAnnotationPropertyType(ir::ClassProperty *property) { // typeAnnotation check - if (!ValidateAnnotationPropertyType(property->TsType())) { + if (!ValidateAnnotationPropertyType(property->TsType(), this)) { LogError(diagnostic::ANNOT_FIELD_INVALID_TYPE, {}, property->Start()); } - // The type of the Initializer has been check in the parser, - // except for the enumeration type, because it is a member expression, - // so here is an additional check to the enumeration type. - if (property->Value() != nullptr && - ((property->Value()->IsMemberExpression() && !property->TsType()->IsETSEnumType()) || - property->Value()->IsIdentifier())) { - LogError(diagnostic::ANNOTATION_FIELD_NONLITERAL, {}, property->Value()->Start()); + if (IsValidAnnotationPropInitializer(property->Value())) { + return; } + + // Avoid outputting the same syntax and type errors. + if (property->Value()->IsBrokenExpression()) { + ES2PANDA_ASSERT(IsAnyError()); + return; + } + + LogError(diagnostic::ANNOTATION_FIELD_NONLITERAL, {}, property->Value()->Start()); } void ETSChecker::CheckSinglePropertyAnnotation(ir::AnnotationUsage *st, ir::AnnotationDeclaration *annoDecl) @@ -1157,29 +1281,12 @@ void ETSChecker::CheckSinglePropertyAnnotation(ir::AnnotationUsage *st, ir::Anno if (annoDecl->Properties().size() > 1) { LogError(diagnostic::ANNOT_MULTIPLE_FIELD, {st->GetBaseName()->Name()}, st->Start()); } - auto singleField = annoDecl->Properties().at(0)->AsClassProperty(); - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto clone = singleField->TypeAnnotation()->Clone(ProgramAllocator(), param); - param->SetTypeAnnotation(clone); + ScopeContext scopeCtx(this, st->Scope()); param->Check(this); CheckAnnotationPropertyType(param); } -void ETSChecker::ProcessRequiredFields(ArenaUnorderedMap &fieldMap, - ir::AnnotationUsage *st, ETSChecker *checker) const -{ - for (const auto &entry : fieldMap) { - if (entry.second->Value() == nullptr) { - checker->LogError(diagnostic::ANNOT_FIELD_NO_VAL, {entry.first}, st->Start()); - continue; - } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto *clone = entry.second->Clone(checker->ProgramAllocator(), st); - st->AddProperty(clone); - } -} - void ETSChecker::CheckMultiplePropertiesAnnotation(ir::AnnotationUsage *st, util::StringView const &baseName, ArenaUnorderedMap &fieldMap) { @@ -1193,13 +1300,6 @@ void ETSChecker::CheckMultiplePropertiesAnnotation(ir::AnnotationUsage *st, util continue; } - if (result->second == nullptr || result->second->TypeAnnotation() == nullptr) { - continue; - } - - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - auto clone = result->second->TypeAnnotation()->Clone(ProgramAllocator(), param); - param->SetTypeAnnotation(clone); ScopeContext scopeCtx(this, st->Scope()); param->Check(this); CheckAnnotationPropertyType(param); @@ -1291,85 +1391,15 @@ Type const *ETSChecker::MaybeUnboxType(Type const *type) const return MaybeUnboxType(const_cast(type)); } -ir::BoxingUnboxingFlags ETSChecker::GetBoxingFlag(Type *const boxingType) -{ - auto typeKind = TypeKind(MaybeUnboxInRelation(boxingType)); - switch (typeKind) { - case TypeFlag::ETS_BOOLEAN: - return ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN; - case TypeFlag::BYTE: - return ir::BoxingUnboxingFlags::BOX_TO_BYTE; - case TypeFlag::CHAR: - return ir::BoxingUnboxingFlags::BOX_TO_CHAR; - case TypeFlag::SHORT: - return ir::BoxingUnboxingFlags::BOX_TO_SHORT; - case TypeFlag::INT: - return ir::BoxingUnboxingFlags::BOX_TO_INT; - case TypeFlag::LONG: - return ir::BoxingUnboxingFlags::BOX_TO_LONG; - case TypeFlag::FLOAT: - return ir::BoxingUnboxingFlags::BOX_TO_FLOAT; - case TypeFlag::DOUBLE: - return ir::BoxingUnboxingFlags::BOX_TO_DOUBLE; - default: - ES2PANDA_UNREACHABLE(); - } -} - -ir::BoxingUnboxingFlags ETSChecker::GetUnboxingFlag(Type const *const unboxingType) const -{ - auto typeKind = TypeKind(unboxingType); - switch (typeKind) { - case TypeFlag::ETS_BOOLEAN: - return ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN; - case TypeFlag::BYTE: - return ir::BoxingUnboxingFlags::UNBOX_TO_BYTE; - case TypeFlag::CHAR: - return ir::BoxingUnboxingFlags::UNBOX_TO_CHAR; - case TypeFlag::SHORT: - return ir::BoxingUnboxingFlags::UNBOX_TO_SHORT; - case TypeFlag::INT: - return ir::BoxingUnboxingFlags::UNBOX_TO_INT; - case TypeFlag::LONG: - return ir::BoxingUnboxingFlags::UNBOX_TO_LONG; - case TypeFlag::FLOAT: - return ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT; - case TypeFlag::DOUBLE: - return ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE; - default: - ES2PANDA_UNREACHABLE(); - } -} - -void ETSChecker::MaybeAddBoxingFlagInRelation(TypeRelation *relation, Type *target) -{ - auto boxingResult = MaybeBoxInRelation(target); - if ((boxingResult != nullptr) && !relation->OnlyCheckBoxingUnboxing()) { - relation->GetNode()->RemoveBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOXING_FLAG); - relation->GetNode()->AddBoxingUnboxingFlags(GetBoxingFlag(boxingResult)); - relation->Result(true); - } -} - -void ETSChecker::MaybeAddUnboxingFlagInRelation(TypeRelation *relation, Type *source, Type *self) -{ - auto unboxingResult = UnboxingConverter(this, relation, source, self).Result(); - if ((unboxingResult != nullptr) && relation->IsTrue() && !relation->OnlyCheckBoxingUnboxing()) { - relation->GetNode()->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxingResult)); - } -} - void ETSChecker::CheckUnboxedTypeWidenable(TypeRelation *relation, Type *target, Type *self) { - checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( - relation, TypeRelationFlag::ONLY_CHECK_WIDENING | - (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE)); + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(relation, TypeRelationFlag::ONLY_CHECK_WIDENING); // NOTE: vpukhov. handle union type auto unboxedType = MaybeUnboxInRelation(target); if (unboxedType == nullptr) { return; } - NarrowingWideningConverter(this, relation, unboxedType, self); + WideningConverter(this, relation, unboxedType, self); if (!relation->IsTrue()) { relation->Result(relation->IsAssignableTo(self, unboxedType)); } @@ -1383,10 +1413,6 @@ void ETSChecker::CheckUnboxedTypesAssignable(TypeRelation *relation, Type *sourc return; } relation->IsAssignableTo(unboxedSourceType, unboxedTargetType); - if (relation->IsTrue()) { - relation->GetNode()->AddBoxingUnboxingFlags( - relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType)); - } } void ETSChecker::CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *source, Type *target) @@ -1394,7 +1420,6 @@ void ETSChecker::CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *so ES2PANDA_ASSERT(relation != nullptr); checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( relation, (relation->ApplyWidening() ? TypeRelationFlag::WIDENING : TypeRelationFlag::NONE) | - (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE) | (relation->OnlyCheckBoxingUnboxing() ? TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING : TypeRelationFlag::NONE)); @@ -1403,22 +1428,13 @@ void ETSChecker::CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *so return; } ES2PANDA_ASSERT(target != nullptr); - // Do not box primitive in case of cast to dynamic types - if (target->IsETSDynamicType()) { - return; - } relation->IsAssignableTo(boxedSourceType, target); - if (relation->IsTrue()) { - MaybeAddBoxingFlagInRelation(relation, boxedSourceType); - } else { + if (!relation->IsTrue()) { auto unboxedTargetType = MaybeUnboxInRelation(target); if (unboxedTargetType == nullptr) { return; } - NarrowingWideningConverter(this, relation, unboxedTargetType, source); - if (relation->IsTrue()) { - MaybeAddBoxingFlagInRelation(relation, target); - } + WideningConverter(this, relation, unboxedTargetType, source); } } @@ -1432,10 +1448,6 @@ void ETSChecker::CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation *rela if (!relation->IsTrue() && relation->ApplyWidening()) { relation->GetChecker()->AsETSChecker()->CheckUnboxedTypeWidenable(relation, target, unboxedSourceType); } - if (!relation->OnlyCheckBoxingUnboxing()) { - relation->GetNode()->AddBoxingUnboxingFlags( - relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType)); - } } // #22952: optional arrow leftovers @@ -1446,8 +1458,7 @@ bool ETSChecker::CheckLambdaAssignable(ir::Expression *param, ir::ScriptFunction if (typeAnn == nullptr) { return false; } - if (typeAnn->IsETSTypeReference() && !typeAnn->AsETSTypeReference()->TsType()->IsETSArrayType() && - !typeAnn->AsETSTypeReference()->TsType()->IsETSAnyType()) { + if (typeAnn->IsETSTypeReference() && !typeAnn->AsETSTypeReference()->TsType()->IsETSArrayType()) { typeAnn = util::Helpers::DerefETSTypeReference(typeAnn); } @@ -1512,6 +1523,7 @@ bool ETSChecker::CheckLambdaTypeAnnotation(ir::ETSParameterExpression *param, auto nonNullishParam = param->IsOptional() ? GetNonNullishType(parameterType) : parameterType; ES2PANDA_ASSERT(nonNullishParam != nullptr); if (!nonNullishParam->IsETSFunctionType()) { + arrowFuncExpr->Check(this); return true; } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -1566,7 +1578,9 @@ bool ETSChecker::ResolveLambdaArgumentType(Signature *signature, ir::Expression auto arrowFuncExpr = argument->AsArrowFunctionExpression(); bool typeValid = true; ir::ScriptFunction *const lambda = arrowFuncExpr->Function(); - if (!NeedTypeInference(lambda)) { + // Note: (Issue27688) if lambda is trailing lambda transferred, it must be in recheck. + // its type was cleared before the check, so here we need recheck it. + if (!NeedTypeInference(lambda) && !lambda->IsTrailingLambda()) { return typeValid; } @@ -1678,4 +1692,201 @@ void ETSChecker::CheckExceptionClauseType(const std::vectorLogError(diagnostic::OVERLOAD_SAME_ACCESS_MODIFIERS_STATIC_ASYNC, {}, pos); + return false; + } + + if (((overLoadAliasFlags ^ overloadedMethodFlags) & (ir::ModifierFlags::CONSTRUCTOR)) != 0) { + checker->LogError(diagnostic::OVERLOAD_MUST_BOTH_CONSTRUCT, {}, pos); + return false; + } + + return true; +} + +static bool CheckOverloadedName(ETSChecker *checker, ir::OverloadDeclaration *node, ir::Expression *overloadedName) +{ + if (overloadedName->Variable()->Declaration() == nullptr) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_FUNCTION, {}, overloadedName->Start()); + overloadedName->SetTsType(checker->GlobalTypeError()); + return false; + } + + auto *identDeclNode = overloadedName->Variable()->Declaration()->Node(); + if (!identDeclNode->IsMethodDefinition()) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_FUNCTION, {}, overloadedName->Start()); + overloadedName->SetTsType(checker->GlobalTypeError()); + return false; + } + // Constructor will lowering to multiple Constructor if have rest parameters or optional parameters. + // Need to modify RestTupleConstructionPhase. + if (!identDeclNode->AsMethodDefinition()->Overloads().empty() && !identDeclNode->IsConstructor()) { + checker->LogError(diagnostic::OVERLOADED_NAME_REFER_TO_OVERLOAD_FUNCTION, {overloadedName->Variable()->Name()}, + overloadedName->Start()); + overloadedName->SetTsType(checker->GlobalTypeError()); + return false; + } + + return CheckAccessModifierForOverloadDeclaration(checker, node->Modifiers(), identDeclNode->Modifiers(), + overloadedName->Start()); +} + +void ETSChecker::CheckFunctionOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const +{ + for (auto *overloadedName : node->OverloadedList()) { + if (overloadedName->IsMemberExpression()) { + overloadedName->Check(checker); + overloadedName->SetVariable(overloadedName->AsMemberExpression()->Property()->Variable()); + } else if (overloadedName->IsIdentifier()) { + ir::Identifier *ident = overloadedName->AsIdentifier(); + + Type *classType = node->Parent()->AsClassDefinition()->TsType(); + ES2PANDA_ASSERT(classType->IsETSObjectType()); + + PropertySearchFlags searchFlags = PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::SEARCH_IN_BASE | + PropertySearchFlags::SEARCH_IN_INTERFACES | + PropertySearchFlags::IS_GETTER; + auto *variable = + checker->ResolveOverloadReference(ident->AsIdentifier(), classType->AsETSObjectType(), searchFlags); + if (variable == nullptr) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_FUNCTION, {}, ident->Start()); + ident->SetTsType(checker->GlobalTypeError()); + continue; + } + + ident->SetTsType(variable->TsType()); + ident->SetVariable(variable); + + if (!CheckOverloadedName(checker, node, overloadedName)) { + continue; + } + } else { + overloadedName->SetTsType(checker->GlobalTypeError()); + continue; + } + } +} + +void ETSChecker::CheckClassMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const +{ + PropertySearchFlags searchFlags = + PropertySearchFlags::SEARCH_IN_BASE | PropertySearchFlags::SEARCH_IN_INTERFACES | + PropertySearchFlags::IS_GETTER | PropertySearchFlags::IGNORE_OVERLOAD | + (node->IsStatic() ? PropertySearchFlags::SEARCH_STATIC_METHOD : PropertySearchFlags::SEARCH_INSTANCE_METHOD); + + for (auto *overloadedName : node->OverloadedList()) { + if (!overloadedName->IsIdentifier()) { + overloadedName->SetTsType(checker->GlobalTypeError()); + continue; + } + ir::Identifier *ident = overloadedName->AsIdentifier(); + + Type *classType = node->Parent()->AsClassDefinition()->TsType(); + ES2PANDA_ASSERT(classType->IsETSObjectType()); + + auto *variable = + checker->ResolveOverloadReference(ident->AsIdentifier(), classType->AsETSObjectType(), searchFlags); + + if (variable == nullptr && + checker->ResolveOverloadReference(ident->AsIdentifier(), classType->AsETSObjectType(), + searchFlags | PropertySearchFlags::SEARCH_METHOD) != nullptr) { + checker->LogError(diagnostic::OVERLOAD_SAME_ACCESS_MODIFIERS_STATIC_ASYNC, {}, ident->Start()); + continue; + } + if (variable == nullptr) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_FUNCTION, {}, ident->Start()); + ident->SetTsType(checker->GlobalTypeError()); + continue; + } + + ident->SetTsType(variable->TsType()); + ident->SetVariable(variable); + + if (!CheckOverloadedName(checker, node, overloadedName)) { + continue; + } + } +} + +void ETSChecker::CheckInterfaceMethodOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const +{ + PropertySearchFlags searchFlags = + PropertySearchFlags::SEARCH_IN_BASE | PropertySearchFlags::SEARCH_IN_INTERFACES | + PropertySearchFlags::IS_GETTER | PropertySearchFlags::IGNORE_OVERLOAD | + (node->IsStatic() ? PropertySearchFlags::SEARCH_STATIC_METHOD : PropertySearchFlags::SEARCH_INSTANCE_METHOD); + for (auto *overloadedName : node->OverloadedList()) { + if (!overloadedName->IsIdentifier()) { + overloadedName->SetTsType(checker->GlobalTypeError()); + continue; + } + ir::Identifier *ident = overloadedName->AsIdentifier(); + + Type *interfaceType = node->Parent()->Parent()->AsTSInterfaceDeclaration()->TsType(); + ES2PANDA_ASSERT(interfaceType->IsETSObjectType()); + auto *variable = + checker->ResolveOverloadReference(ident->AsIdentifier(), interfaceType->AsETSObjectType(), searchFlags); + + if (variable == nullptr && + checker->ResolveOverloadReference(ident->AsIdentifier(), interfaceType->AsETSObjectType(), + searchFlags | PropertySearchFlags::SEARCH_METHOD) != nullptr) { + checker->LogError(diagnostic::OVERLOAD_SAME_ACCESS_MODIFIERS_STATIC_ASYNC, {}, ident->Start()); + continue; + } + if (variable == nullptr) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_FUNCTION, {}, ident->Start()); + ident->SetTsType(checker->GlobalTypeError()); + continue; + } + + ident->SetTsType(variable->TsType()); + ident->SetVariable(variable); + + if (!CheckOverloadedName(checker, node, overloadedName)) { + continue; + } + } +} + +void ETSChecker::CheckConstructorOverloadDeclaration(ETSChecker *checker, ir::OverloadDeclaration *node) const +{ + for (auto *overloadedName : node->OverloadedList()) { + if (!overloadedName->IsIdentifier()) { + overloadedName->SetTsType(checker->GlobalTypeError()); + continue; + } + ir::Identifier *ident = overloadedName->AsIdentifier(); + + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + Type *identType = checker->ResolveIdentifier(ident->AsIdentifier()); + ident->SetTsType(identType); + if (identType->IsTypeError()) { + continue; + } + + ES2PANDA_ASSERT(identType->IsETSFunctionType()); + const size_t singleSignatureSize = 1; + if (identType->AsETSFunctionType()->CallSignatures().size() > singleSignatureSize) { + size_t userDefinedConstructorSize = + std::count_if(identType->AsETSFunctionType()->CallSignatures().begin(), + identType->AsETSFunctionType()->CallSignatures().end(), + [](Signature *sig) { return !sig->Function()->IsSynthetic(); }); + if (userDefinedConstructorSize > singleSignatureSize) { + checker->LogError(diagnostic::OVERLOADED_NAME_REFER_TO_OVERLOAD_FUNCTION, {ident->Name()}, + node->Start()); + continue; + } + } + + if (!CheckOverloadedName(checker, node, overloadedName)) { + continue; + } + } +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/typeCreation.cpp b/ets2panda/checker/ets/typeCreation.cpp index 67c37078cd3d6dce9bd858c71bf8d7ae1ec0a27b..0647747376011ea0ca40b6d0e99112785e9e2462 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -17,12 +17,14 @@ #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "checker/types/ets/etsEnumType.h" -#include "checker/types/ets/etsDynamicFunctionType.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" +#include + namespace ark::es2panda::checker { ByteType *ETSChecker::CreateByteType(int8_t value) @@ -50,32 +52,6 @@ IntType *ETSChecker::CreateIntType(int32_t value) return ProgramAllocator()->New(value); } -IntType *ETSChecker::CreateIntTypeFromType(Type *type) -{ - if (!type->HasTypeFlag(TypeFlag::CONSTANT)) { - return GlobalIntType()->AsIntType(); - } - - if (type->IsIntType()) { - return type->AsIntType(); - } - - switch (ETSType(type)) { - case TypeFlag::CHAR: { - return CreateIntType(static_cast(type->AsCharType()->GetValue())); - } - case TypeFlag::BYTE: { - return CreateIntType(static_cast(type->AsByteType()->GetValue())); - } - case TypeFlag::SHORT: { - return CreateIntType(static_cast(type->AsShortType()->GetValue())); - } - default: { - return nullptr; - } - } -} - LongType *ETSChecker::CreateLongType(int64_t value) { return ProgramAllocator()->New(value); @@ -111,10 +87,10 @@ ETSResizableArrayType *ETSChecker::CreateETSMultiDimResizableArrayType(Type *ele Type *baseArrayType = element; for (size_t dim = 0; dim < dimSize; ++dim) { - Substitution *tmpSubstitution = NewSubstitution(); - EmplaceSubstituted(tmpSubstitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), + auto tmpSubstitution = Substitution {}; + EmplaceSubstituted(&tmpSubstitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), MaybeBoxType(baseArrayType)); - baseArrayType = arrayType->Substitute(Relation(), tmpSubstitution); + baseArrayType = arrayType->Substitute(Relation(), &tmpSubstitution); } return baseArrayType->AsETSResizableArrayType(); } @@ -126,10 +102,10 @@ ETSResizableArrayType *ETSChecker::CreateETSResizableArrayType(Type *element) ETSResizableArrayType *arrayType = type->AsETSResizableArrayType(); ES2PANDA_ASSERT(arrayType->TypeArguments().size() == 1U); - Substitution *substitution = NewSubstitution(); - EmplaceSubstituted(substitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), + auto substitution = Substitution {}; + EmplaceSubstituted(&substitution, arrayType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), MaybeBoxType(element)); - return arrayType->Substitute(Relation(), substitution); + return arrayType->Substitute(Relation(), &substitution); } ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePolluting) @@ -154,6 +130,27 @@ ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePoll return arrayType; } +Type *ETSChecker::CreateGradualType(Type *type, Language const lang) +{ + if (type == nullptr) { + return type; + } + if (type->IsGradualType()) { + return type; + } + if (type->IsETSAnyType()) { + return type; + } + if (type->IsETSUnionType()) { + ArenaVector copied(ProgramAllocator()->Adapter()); + for (auto const &t : type->AsETSUnionType()->ConstituentTypes()) { + copied.push_back(CreateGradualType(t, lang)); + } + return CreateETSUnionType(std::move(copied)); + } + return ProgramAllocator()->New(type); +} + Type *ETSChecker::CreateETSUnionType(Span constituentTypes) { if (constituentTypes.empty()) { @@ -167,8 +164,12 @@ Type *ETSChecker::CreateETSUnionType(Span constituentTypes) if (newConstituentTypes.size() == 1) { return newConstituentTypes[0]; } - - return ProgramAllocator()->New(this, std::move(newConstituentTypes)); + auto *un = ProgramAllocator()->New(this, std::move(newConstituentTypes)); + auto ut = un->GetAssemblerType().Mutf8(); + if (std::count_if(ut.begin(), ut.end(), [](char c) { return c == ','; }) > 0) { + unionAssemblerTypes_.insert(un->GetAssemblerType()); + } + return un; } ETSTypeAliasType *ETSChecker::CreateETSTypeAliasType(util::StringView name, const ir::AstNode *declNode, @@ -187,17 +188,6 @@ ETSFunctionType *ETSChecker::CreateETSMethodType(util::StringView name, ArenaVec return ProgramAllocator()->New(this, name, std::move(signatures)); } -ETSFunctionType *ETSChecker::CreateETSDynamicArrowType(Signature *signature, Language lang) -{ - return ProgramAllocator()->New(this, signature, lang); -} - -ETSFunctionType *ETSChecker::CreateETSDynamicMethodType(util::StringView name, ArenaVector &&signatures, - Language lang) -{ - return ProgramAllocator()->New(this, name, std::move(signatures), lang); -} - static SignatureFlags ConvertToSignatureFlags(ir::ModifierFlags inModifiers, ir::ScriptFunctionFlags inFunctionFlags) { SignatureFlags outFlags = SignatureFlags::NO_OPTS; @@ -213,9 +203,6 @@ static SignatureFlags ConvertToSignatureFlags(ir::ModifierFlags inModifiers, ir: } }; - convertFlag(ir::ScriptFunctionFlags::THROWS, SignatureFlags::THROWS); - convertFlag(ir::ScriptFunctionFlags::RETHROWS, SignatureFlags::RETHROWS); - convertFlag(ir::ScriptFunctionFlags::CONSTRUCTOR, SignatureFlags::CONSTRUCTOR); convertFlag(ir::ScriptFunctionFlags::SETTER, SignatureFlags::SETTER); convertFlag(ir::ScriptFunctionFlags::GETTER, SignatureFlags::GETTER); @@ -227,6 +214,7 @@ static SignatureFlags ConvertToSignatureFlags(ir::ModifierFlags inModifiers, ir: convertModifier(ir::ModifierFlags::PUBLIC, SignatureFlags::PUBLIC); convertModifier(ir::ModifierFlags::PRIVATE, SignatureFlags::PRIVATE); convertModifier(ir::ModifierFlags::INTERNAL, SignatureFlags::INTERNAL); + convertModifier(ir::ModifierFlags::DEFAULT, SignatureFlags::DEFAULT); return outFlags; } @@ -339,6 +327,8 @@ static ETSObjectType *InitializeGlobalBuiltinObjectType(ETSChecker *checker, Glo setType(GlobalTypeId::ETS_ARRAY, programAllocator->New(programAllocator, arrayObj)); return arrayObj; } + case GlobalTypeId::ETS_READONLY_ARRAY: + return setType(globalId, create(ETSObjectFlags::BUILTIN_READONLY_ARRAY))->AsETSObjectType(); case GlobalTypeId::ETS_BOOLEAN_BUILTIN: return create(ETSObjectFlags::BUILTIN_BOOLEAN); case GlobalTypeId::ETS_BYTE_BUILTIN: @@ -372,56 +362,27 @@ ETSObjectType *ETSChecker::CreateETSObjectTypeOrBuiltin(ir::AstNode *declNode, E return InitializeGlobalBuiltinObjectType(this, globalId.value(), declNode, flags); } -std::tuple ETSChecker::CheckForDynamicLang(ir::AstNode *declNode, util::StringView assemblerName) -{ - Language lang(Language::Id::ETS); - bool hasDecl = false; - - if (declNode->IsClassDefinition()) { - auto *clsDef = declNode->AsClassDefinition(); - lang = clsDef->Language(); - hasDecl = clsDef->IsDeclare(); - } - - if (declNode->IsTSInterfaceDeclaration()) { - auto *ifaceDecl = declNode->AsTSInterfaceDeclaration(); - lang = ifaceDecl->Language(); - hasDecl = ifaceDecl->IsDeclare(); - } - - auto res = compiler::Signatures::Dynamic::LanguageFromType(assemblerName.Utf8()); - if (res) { - lang = *res; - } - - return std::make_tuple(lang, hasDecl); -} - 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 (auto [lang, hasDecl] = CheckForDynamicLang(declNode, internalName); lang.IsDynamic()) { - return ProgramAllocator()->New(ProgramAllocator(), std::make_tuple(name, internalName, lang), - std::make_tuple(declNode, flags, Relation()), hasDecl); - } - - return ProgramAllocator()->New(ProgramAllocator(), name, internalName, - std::make_tuple(declNode, flags, Relation())); + return objectType; } std::tuple ETSChecker::CreateBuiltinArraySignatureInfo(const ETSArrayType *arrayType, @@ -457,8 +418,11 @@ std::tuple ETSChecker::CreateBuiltinArraySign Signature *ETSChecker::CreateBuiltinArraySignature(const ETSArrayType *arrayType, size_t dim) { - auto res = globalArraySignatures_.find(arrayType); - if (res != globalArraySignatures_.end()) { + auto currentChecker = + compiler::GetPhaseManager()->Context() != nullptr ? compiler::GetPhaseManager()->Context()->GetChecker() : this; + auto &globalArraySignatures = currentChecker->AsETSChecker()->globalArraySignatures_; + auto res = globalArraySignatures.find(arrayType); + if (res != globalArraySignatures.end()) { return res->second; } @@ -466,7 +430,7 @@ Signature *ETSChecker::CreateBuiltinArraySignature(const ETSArrayType *arrayType auto *signature = CreateSignature(info, GlobalVoidType(), ir::ScriptFunctionFlags::NONE, false); ES2PANDA_ASSERT(signature != nullptr); signature->SetInternalName(internalName); - globalArraySignatures_.insert({arrayType, signature}); + globalArraySignatures.insert({arrayType, signature}); return signature; } @@ -476,11 +440,11 @@ ETSObjectType *ETSChecker::CreatePromiseOf(Type *type) ETSObjectType *const promiseType = GlobalBuiltinPromiseType(); ES2PANDA_ASSERT(promiseType->TypeArguments().size() == 1U); - Substitution *substitution = NewSubstitution(); + auto substitution = Substitution {}; ES2PANDA_ASSERT(promiseType != nullptr); - EmplaceSubstituted(substitution, promiseType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), type); + EmplaceSubstituted(&substitution, promiseType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), type); - return promiseType->Substitute(Relation(), substitution); + return promiseType->Substitute(Relation(), &substitution); } static bool IsInValidKeyofTypeNode(ir::AstNode *node) diff --git a/ets2panda/checker/ets/typeRelationContext.cpp b/ets2panda/checker/ets/typeRelationContext.cpp index f4eca541008475b7950cd28665ab4098694d7bab..b8d00b7d982b92728913cc9dfe056d8b8bc49f83 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -80,26 +80,13 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParame result_ = paramType; return; } - - if (paramType->IsETSPrimitiveType()) { - checker_->Relation()->SetNode(it); - - auto *const boxedTypeArg = checker_->MaybeBoxInRelation(paramType); - if (boxedTypeArg != nullptr) { - paramType = boxedTypeArg->Instantiate(checker_->Allocator(), checker_->Relation(), - checker_->GetGlobalTypesHolder()); - } else { - ES2PANDA_UNREACHABLE(); - } - } - + ES2PANDA_ASSERT(!paramType->IsETSPrimitiveType()); typeArgTypes.push_back(paramType); } } while (typeArgTypes.size() < type->TypeArguments().size()) { Type *defaultType = nullptr; - if (type->TypeArguments().at(typeArgTypes.size())->IsETSTypeParameter()) { defaultType = type->TypeArguments().at(typeArgTypes.size())->AsETSTypeParameter()->GetDefaultType(); } else { @@ -114,7 +101,7 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParame } } - auto pos = (typeArgs == nullptr) ? lexer::SourcePosition() : typeArgs->Range().start; + auto pos = (typeArgs == nullptr) ? type->Variable()->Declaration()->Node()->Range().start : typeArgs->Range().start; InstantiateType(type, std::move(typeArgTypes), pos); ES2PANDA_ASSERT(result_->IsETSObjectType()); result_->AsETSObjectType()->AddObjectFlag(ETSObjectFlags::NO_OPTS); @@ -126,6 +113,7 @@ static void CheckInstantiationConstraints(ETSChecker *checker, ArenaVectorRelation(); for (auto type : typeParams) { + type = type->MaybeBaseTypeOfGradualType(); if (!type->IsETSTypeParameter()) { continue; } @@ -139,10 +127,10 @@ static void CheckInstantiationConstraints(ETSChecker *checker, ArenaVectorIsETSReferenceType() || typeArg->IsETSVoidType()); + auto maybeIrrelevantTypeArg = typeArg->IsETSVoidType() ? checker->GlobalETSUndefinedType() : typeArg; auto constraint = typeParam->GetConstraintType()->Substitute(relation, substitution); - if (!relation->IsAssignableTo(typeArg, constraint)) { - // NOTE(vpukhov): refine message - checker->LogError(diagnostic::INIT_NOT_ASSIGNABLE, {typeArg, constraint}, pos); + if (!relation->IsSupertypeOf(constraint, maybeIrrelevantTypeArg)) { + checker->LogError(diagnostic::TYPEARG_TYPEPARAM_SUBTYPING, {typeArg, constraint}, pos); } } } @@ -152,7 +140,7 @@ void ConstraintCheckScope::TryCheckConstraints() if (Unlock()) { auto &records = checker_->PendingConstraintCheckRecords(); for (auto const &[typeParams, substitution, pos] : records) { - CheckInstantiationConstraints(checker_, *typeParams, substitution, pos); + CheckInstantiationConstraints(checker_, *typeParams, &substitution, pos); } records.clear(); } @@ -168,21 +156,21 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ArenaVectorNewSubstitution(); + auto substitution = Substitution {}; for (size_t idx = 0; idx < typeParams.size(); idx++) { if (!typeParams[idx]->IsETSTypeParameter()) { continue; } - checker_->EmplaceSubstituted(substitution, typeParams[idx]->AsETSTypeParameter(), typeArgTypes[idx]); + checker_->EmplaceSubstituted(&substitution, typeParams[idx]->AsETSTypeParameter(), typeArgTypes[idx]); } ConstraintCheckScope ctScope(checker_); + result_ = type->Substitute(checker_->Relation(), &substitution)->AsETSObjectType(); if (!checker_->Relation()->NoThrowGenericTypeAlias()) { - checker_->PendingConstraintCheckRecords().push_back({&typeParams, substitution, pos}); + checker_->PendingConstraintCheckRecords().emplace_back(&typeParams, std::move(substitution), pos); } - result_ = type->Substitute(checker_->Relation(), substitution)->AsETSObjectType(); - type->GetInstantiationMap().try_emplace(hash, result_->AsETSObjectType()); + type->InsertInstantiationMap(hash, result_->AsETSObjectType()); result_->AddTypeFlag(TypeFlag::GENERIC); ctScope.TryCheckConstraints(); diff --git a/ets2panda/checker/ets/typeRelationContext.h b/ets2panda/checker/ets/typeRelationContext.h index 084d01c2a3824f938935cdf1e496c660a761999d..7ba41f94c85585a2607a67f9da63bea20b07c3ff 100644 --- a/ets2panda/checker/ets/typeRelationContext.h +++ b/ets2panda/checker/ets/typeRelationContext.h @@ -48,19 +48,11 @@ public: flags_ |= flags; relation->SetNode(node); - // NOTE (oeotvos) The narrowing flag will be applied here. It means, that the result of "let tmp: int = 1.5" - // will be 1, which could cause problems. - if (source->HasTypeFlag(TypeFlag::CONSTANT)) { - flags_ |= TypeRelationFlag::NARROWING; - } - relation->SetFlags(flags_); if (!relation->IsAssignableTo(source, target)) { if (relation->IsLegalBoxedPrimitiveConversion(target, source)) { - Type *sourceUnboxedType = etsChecker->MaybeUnboxType(source); - relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(sourceUnboxedType)); - relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(target)); + relation->Result(true); } if (((flags_ & TypeRelationFlag::UNBOXING) != 0) && !relation->IsTrue() && source->IsETSObjectType() && !target->IsETSObjectType()) { @@ -110,6 +102,9 @@ public: relation->SetFlags(flags_ | initialFlags); if (!relation->IsAssignableTo(source, target)) { + if (relation->IsLegalBoxedPrimitiveConversion(target, source)) { + relation->Result(true); + } if (((flags_ & TypeRelationFlag::UNBOXING) != 0U) && !relation->IsTrue() && source->IsETSObjectType() && !target->IsETSObjectType()) { etsChecker->CheckUnboxedSourceTypeWithWideningAssignable(relation, source, target); diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index 3009f298cd2d0ddcd438608ad7f8a4fbbb57415d..bb07fc196fc8e50e384368a8aa084c9bbb4e89b8 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -22,6 +22,9 @@ #include "ir/expressions/literals/undefinedLiteral.h" #include "varbinder/ETSBinder.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include "util/nameMangler.h" + +#include namespace ark::es2panda::checker { @@ -35,6 +38,13 @@ std::optional ETSChecker::GetUtilityTypeTypeParamNode( return typeParams->Params().front(); } +static bool ValidBaseTypeOfRequiredAndPartial(Type *baseType) +{ + Type *type = baseType->MaybeBaseTypeOfGradualType(); + return type->IsETSObjectType() && (type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE) || + type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)); +} + Type *ETSChecker::HandleUtilityTypeParameterNode(const ir::TSTypeParameterInstantiation *const typeParams, const ir::Identifier *const ident) { @@ -54,8 +64,10 @@ Type *ETSChecker::HandleUtilityTypeParameterNode(const ir::TSTypeParameterInstan return baseType; } - if (!baseType->IsETSReferenceType()) { - LogError(diagnostic::UTIL_TYPE_OF_NONREFERENCE, {}, typeParams->Start()); + if ((utilityType == compiler::Signatures::PARTIAL_TYPE_NAME || + utilityType == compiler::Signatures::REQUIRED_TYPE_NAME) && + !ValidBaseTypeOfRequiredAndPartial(baseType)) { + LogError(diagnostic::MUST_BE_CLASS_INTERFACE_TYPE, {utilityType}, typeParams->Start()); return GlobalTypeError(); } @@ -88,7 +100,9 @@ static std::pair GetPartialClassName(ETSChec { // Partial class name for class 'T' will be 'T$partial' auto const addSuffix = [checker](util::StringView name) { - return util::UString(name.Mutf8() + PARTIAL_CLASS_SUFFIX, checker->ProgramAllocator()).View(); + std::string newName = + util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName(util::NameMangler::PARTIAL, name); + return util::UString(newName, checker->ProgramAllocator()).View(); }; auto declIdent = typeNode->IsClassDefinition() ? typeNode->AsClassDefinition()->Ident() @@ -122,6 +136,11 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) return typeToBePartial; } + if (typeToBePartial->IsGradualType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return CreatePartialType(typeToBePartial->AsGradualType()->GetBaseType()); + } + if (typeToBePartial->IsETSTypeParameter()) { return CreatePartialTypeParameter(typeToBePartial->AsETSTypeParameter()); } @@ -213,8 +232,11 @@ Type *ETSChecker::HandlePartialInterface(ir::TSInterfaceDeclaration *interfaceDe partialInterDecl->Variable()); } + auto savedScope = VarBinder()->TopScope(); + VarBinder()->ResetTopScope(partialProgram->GlobalScope()); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *partialType = CreatePartialTypeInterfaceDecl(interfaceDecl, typeToBePartial, partialInterDecl); + VarBinder()->ResetTopScope(savedScope); ES2PANDA_ASSERT(partialType != nullptr); NamedTypeStackElement ntse(this, partialType); @@ -235,11 +257,11 @@ ir::ClassProperty *ETSChecker::CreateNullishPropertyFromAccessor(ir::MethodDefin auto *prop = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) ProgramAllocator()->New(ident, nullptr, nullptr, modifierFlag, ProgramAllocator(), false); - ES2PANDA_ASSERT(prop != nullptr); prop->SetParent(newClassDefinition); ident->SetParent(prop); + ES2PANDA_ASSERT(accessor->Function() != nullptr); prop->SetTypeAnnotation(accessor->Function()->IsGetter() ? accessor->Function()->ReturnTypeAnnotation() : accessor->Function()->Params()[0]->AsETSParameterExpression()->TypeAnnotation()); @@ -249,12 +271,10 @@ ir::ClassProperty *ETSChecker::CreateNullishPropertyFromAccessor(ir::MethodDefin return CreateNullishProperty(prop, newClassDefinition); } - if (accessor->TsType() == nullptr) { - accessor->Parent()->Check(this); - } - + ES2PANDA_ASSERT(accessor->TsType()->IsETSFunctionType()); auto callSign = accessor->TsType()->AsETSFunctionType()->CallSignatures()[0]; + ES2PANDA_ASSERT(accessor->Function() != nullptr); auto tsType = accessor->Function()->IsGetter() ? callSign->ReturnType() : callSign->Params()[0]->TsType(); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -314,6 +334,7 @@ ir::ClassProperty *ETSChecker::CreateNullishProperty(ir::ClassProperty *const pr prop->SetValue(nullptr); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *const propClone = prop->Clone(ProgramAllocator(), newClassDefinition)->AsClassProperty(); + propClone->CleanCheckInformation(); // Revert original property value prop->SetValue(propSavedValue); @@ -392,8 +413,8 @@ ir::TSTypeParameterInstantiation *ETSChecker::CreateNewSuperPartialRefTypeParams !originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->IsETSTypeParameter()) { continue; } - auto it = likeSubstitution->find( - originRefParams[ix]->AsETSTypeReference()->Part()->TsType()->AsETSTypeParameter()->GetDeclNode()); + auto type = originRefParams[ix]->AsETSTypeReference()->Part()->TsType(); + auto it = likeSubstitution->find(type->AsETSTypeParameter()->GetDeclNode()); if (it != likeSubstitution->end()) { auto *typeParamRefPart = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -442,6 +463,7 @@ ir::ETSTypeReference *ETSChecker::BuildSuperPartialTypeReference( return superPartialRef; } +// CC-OFFNXT(huge_method[C++], G.FUN.01-CPP, G.FUD.05) solid logic void ETSChecker::CreatePartialClassDeclaration(ir::ClassDefinition *const newClassDefinition, ir::ClassDefinition *classDef) { @@ -484,7 +506,7 @@ void ETSChecker::CreatePartialClassDeclaration(ir::ClassDefinition *const newCla auto *const newProp = CreateNullishProperty(prop->AsClassProperty(), newClassDefinition); // Put the new property into the class declaration - newClassDefinition->Body().emplace_back(newProp); + newClassDefinition->EmplaceBody(newProp); } if (prop->IsMethodDefinition() && prop->AsMethodDefinition()->Function() != nullptr && (prop->AsMethodDefinition()->Function()->IsGetter() || @@ -499,8 +521,15 @@ void ETSChecker::CreatePartialClassDeclaration(ir::ClassDefinition *const newCla ES2PANDA_ASSERT(IsAnyError()); continue; } - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - newClassDefinition->Body().emplace_back(CreateNullishPropertyFromAccessor(method, newClassDefinition)); + + if (method->TsType() == nullptr) { + method->Parent()->Check(this); + } + + if (method->TsType() != nullptr && !method->TsType()->IsTypeError()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + newClassDefinition->EmplaceBody(CreateNullishPropertyFromAccessor(method, newClassDefinition)); + } } } if (classDef->IsDeclare()) { @@ -524,17 +553,18 @@ static void SetupFunctionParams(ir::ScriptFunction *function, varbinder::Functio } else { auto *unionType = // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - checker->AllocNode( - ArenaVector({paramExpr->Ident()->TypeAnnotation(), - // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - checker->AllocNode(checker->Allocator())}, - checker->Allocator()->Adapter()), - checker->Allocator()); + checker->ProgramAllocNode( + ArenaVector( + {paramExpr->Ident()->TypeAnnotation(), + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + checker->ProgramAllocNode(checker->ProgramAllocator())}, + checker->ProgramAllocator()->Adapter()), + checker->ProgramAllocator()); ES2PANDA_ASSERT(unionType != nullptr); paramExpr->Ident()->SetTsTypeAnnotation(unionType); unionType->SetParent(paramExpr->Ident()); } - auto [paramVar, node] = paramScope->AddParamDecl(checker->Allocator(), checker->VarBinder(), paramExpr); + auto [paramVar, node] = paramScope->AddParamDecl(checker->ProgramAllocator(), checker->VarBinder(), paramExpr); if (node != nullptr) { checker->VarBinder()->ThrowRedeclaration(node->Start(), paramVar->Name(), paramVar->Declaration()->Type()); } @@ -637,10 +667,12 @@ ir::TSInterfaceDeclaration *ETSChecker::CreateInterfaceProto(util::StringView na // Put class declaration in global scope, and in program AST partialInterface->SetParent(interfaceDeclProgram->Ast()); - interfaceDeclProgram->Ast()->Statements().push_back(partialInterface); + interfaceDeclProgram->Ast()->AddStatement(partialInterface); interfaceDeclProgram->GlobalScope()->InsertBinding(name, var); partialInterface->AddModifier(flags); + partialInterface->ClearModifier(ir::ModifierFlags::EXPORTED); + partialInterface->ClearModifier(ir::ModifierFlags::DEFAULT_EXPORT); return partialInterface; } @@ -744,7 +776,7 @@ Type *ETSChecker::CreatePartialTypeInterfaceDecl(ir::TSInterfaceDeclaration *con // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) BuildSuperPartialTypeReference(superPartialType, superPartialRefTypeParams); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) - partialInterface->Extends().push_back(ProgramAllocNode(superPartialRef)); + partialInterface->EmplaceExtends(ProgramAllocNode(superPartialRef)); partialInterface->Extends().back()->SetParent(partialInterface); } } @@ -782,7 +814,7 @@ void ETSChecker::CreateConstructorForPartialType(ir::ClassDefinition *const part ctor->Id()->SetVariable(ctorId->Variable()); // Put ctor in partial class body - partialClassDef->Body().emplace_back(ctor); + partialClassDef->EmplaceBody(ctor); } ir::ClassDefinition *ETSChecker::CreateClassPrototype(util::StringView name, parser::Program *const classDeclProgram) @@ -818,7 +850,7 @@ ir::ClassDefinition *ETSChecker::CreateClassPrototype(util::StringView name, par decl->BindNode(classDef); // Put class declaration in global scope, and in program AST - classDeclProgram->Ast()->Statements().push_back(classDecl); + classDeclProgram->Ast()->AddStatement(classDecl); classDeclProgram->GlobalScope()->InsertBinding(name, var); return classDef; @@ -983,6 +1015,10 @@ Type *ETSChecker::GetReadonlyType(Type *type) return *found; } + if (type->IsGradualType()) { + return GetReadonlyType(type->AsGradualType()->GetBaseType()); + } + NamedTypeStackElement ntse(this, type); ES2PANDA_ASSERT(type != nullptr); if (type->IsETSArrayType()) { @@ -1019,7 +1055,7 @@ Type *ETSChecker::GetReadonlyType(Type *type) void ETSChecker::MakePropertiesReadonly(ETSObjectType *const classType) { - classType->UpdateTypeProperties(this, [this](auto *property, auto *propType) { + classType->UpdateTypeProperties([this](auto *property, auto *propType) { auto *newDecl = ProgramAllocator()->New(property->Name(), property->Declaration()->Node()); auto *const propCopy = property->Copy(ProgramAllocator(), newDecl); @@ -1071,7 +1107,7 @@ Type *ETSChecker::HandleRequiredType(Type *typeToBeRequired) typeToBeRequired = typeToBeRequired->Clone(this); - MakePropertiesNonNullish(typeToBeRequired->AsETSObjectType()); + MakePropertiesNonNullish(typeToBeRequired->MaybeBaseTypeOfGradualType()->AsETSObjectType()); return typeToBeRequired; } diff --git a/ets2panda/checker/ets/validateHelpers.cpp b/ets2panda/checker/ets/validateHelpers.cpp index 536fe174886452e63355b2bdf133f3a1273218cc..765c1a3aade945fbaa2738ab2dda010d17c5425a 100644 --- a/ets2panda/checker/ets/validateHelpers.cpp +++ b/ets2panda/checker/ets/validateHelpers.cpp @@ -51,24 +51,57 @@ void ETSChecker::ValidatePropertyAccess(varbinder::Variable *var, ETSObjectType } } +bool ETSChecker::IsStaticInvoke(ir::MemberExpression *const expr) +{ + ir::Identifier *ident = nullptr; + if (expr->Object()->IsIdentifier()) { + ident = expr->Object()->AsIdentifier(); + } else if (expr->Object()->IsMemberExpression()) { + auto object = expr->Object(); + + while (object->IsMemberExpression()) { + object = object->AsMemberExpression()->Object(); + } + if (object->IsIdentifier()) { + ident = object->AsIdentifier(); + } + } + + return (ident != nullptr && + (ident->Variable()->Declaration()->IsClassDecl() || ident->Variable()->Declaration()->IsImportDecl())); +} + void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, Type *const type) { - if (ident->Variable()->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) && - ident->Parent()->AsCallExpression()->Callee() != ident) { + ir::CallExpression *callExpr = nullptr; + if (ident->Parent()->IsMemberExpression() && IsStaticInvoke(ident->Parent()->AsMemberExpression())) { + callExpr = ident->Parent()->Parent()->AsCallExpression(); + } else if (ident->Parent()->IsCallExpression()) { + callExpr = ident->Parent()->AsCallExpression(); + } else { + return; + } + + if (ident->Variable()->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) && callExpr->Callee() != ident && + callExpr->Callee() != ident->Parent()) { std::ignore = TypeError(ident->Variable(), diagnostic::CLASS_OR_IFACE_AS_OBJ, {ident->ToString()}, ident->Start()); } - if (ident->Parent()->AsCallExpression()->Callee() != ident) { + if (callExpr->Callee() != ident && callExpr->Callee() != ident->Parent()) { return; } ES2PANDA_ASSERT(ident->Variable() != nullptr); + if (ident->Variable()->Declaration()->Node() != nullptr && + ident->Variable()->Declaration()->Node()->IsOverloadDeclaration()) { + return; + } if (ident->Variable()->Declaration()->Node() != nullptr && ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) { std::ignore = TypeError(ident->Variable(), diagnostic::NAMESPACE_CALL, {ident->ToString()}, ident->Start()); } - if (type->IsETSFunctionType() || type->IsETSDynamicType()) { + if (type->IsETSFunctionType()) { return; } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -129,7 +162,8 @@ bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, const auto *const binaryExpr = ident->Parent()->AsBinaryExpression(); bool isFinished = false; - if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Left() == ident) { + bool isInstanceOfKeyword = binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF; + if (isInstanceOfKeyword && binaryExpr->Left() == ident) { if (!IsReferenceType(type)) { std::ignore = TypeError(ident->Variable(), diagnostic::INSTANCEOF_NONOBJECT, {ident->Name()}, ident->Start()); @@ -145,6 +179,10 @@ bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, } isFinished = true; } + if (isInstanceOfKeyword && ident->Variable()->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE | + varbinder::VariableFlags::TYPE_ALIAS)) { + LogError(diagnostic::WRONG_LEFT_OF_INSTANCEOF, {}, ident->Start()); + } return isFinished; } @@ -158,7 +196,9 @@ void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident) auto *smartType = Context().GetSmartCast(resolved); // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved)); - + bool isValidResolved = + resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && + !(resolvedType->IsETSFunctionType() && !resolved->Declaration()->Node()->IsTSTypeAliasDeclaration()); switch (ident->Parent()->Type()) { case ir::AstNodeType::CALL_EXPRESSION: // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -174,7 +214,7 @@ void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident) if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) { return; } - if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) { + if (isValidResolved) { WrongContextErrorClassifyByType(ident); } break; @@ -188,28 +228,13 @@ void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident) ValidateAssignmentIdentifier(ident, resolvedType); break; default: - if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) { + if (isValidResolved) { WrongContextErrorClassifyByType(ident); } } } -bool ETSChecker::ValidateAnnotationPropertyType(checker::Type *type) -{ - if (type == nullptr || type->IsTypeError()) { - ES2PANDA_ASSERT(IsAnyError()); - return false; - } - - if (type->IsETSArrayType() || type->IsETSResizableArrayType()) { - return ValidateAnnotationPropertyType(MaybeUnboxType(GetElementTypeOfArray(type))); - } - - return type->HasTypeFlag(TypeFlag::ETS_NUMERIC | TypeFlag::ETS_ENUM | TypeFlag::ETS_BOOLEAN) || - type->IsETSStringType(); -} - -void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable) +void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable, ir::Expression *expr) { if (IsVariableGetterSetter(variable)) { return; @@ -227,7 +252,7 @@ void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable) } if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) { std::ignore = TypeError(variable, diagnostic::FIELD_ASSIGN_TYPE_MISMATCH, {fieldType, variable->Name()}, - variable->Declaration()->Node()->Start()); + expr->Start()); } if (variable->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK)) { diff --git a/ets2panda/checker/ets/wideningConverter.h b/ets2panda/checker/ets/wideningConverter.h index 00f65deda0daa1f03557f22fa6fc7a7228c6ca0a..163f5545e05457cd3404244e9303f1613015cedc 100644 --- a/ets2panda/checker/ets/wideningConverter.h +++ b/ets2panda/checker/ets/wideningConverter.h @@ -30,11 +30,7 @@ public: return; } - if (!Source()->HasTypeFlag(TypeFlag::CONSTANT)) { - ApplyGlobalWidening(); - } else { - ApplyConstWidening(); - } + ApplyGlobalWidening(); } private: @@ -45,39 +41,6 @@ private: static constexpr TypeFlag WIDENABLE_TO_FLOAT = TypeFlag::LONG | WIDENABLE_TO_LONG; static constexpr TypeFlag WIDENABLE_TO_DOUBLE = TypeFlag::FLOAT | WIDENABLE_TO_FLOAT; - void ApplyConstWidening() - { - switch (ETSChecker::ETSChecker::ETSType(Target())) { - case TypeFlag::SHORT: { - ApplyWidening(WIDENABLE_TO_SHORT); - break; - } - case TypeFlag::CHAR: { - ApplyWidening(WIDENABLE_TO_CHAR); - break; - } - case TypeFlag::INT: { - ApplyWidening(WIDENABLE_TO_INT); - break; - } - case TypeFlag::LONG: { - ApplyWidening(WIDENABLE_TO_LONG); - break; - } - case TypeFlag::FLOAT: { - ApplyWidening(WIDENABLE_TO_FLOAT); - break; - } - case TypeFlag::DOUBLE: { - ApplyWidening(WIDENABLE_TO_DOUBLE); - break; - } - default: { - break; - } - } - } - void ApplyGlobalWidening() { switch (ETSChecker::ETSChecker::ETSType(Target())) { @@ -121,31 +84,31 @@ private: ES2PANDA_ASSERT(Relation()->GetNode()); switch (ETSChecker::ETSChecker::ETSType(Source())) { case TypeFlag::BYTE: { - Relation()->GetNode()->SetTsType(Checker()->GlobalByteType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalByteBuiltinType()); break; } case TypeFlag::SHORT: { - Relation()->GetNode()->SetTsType(Checker()->GlobalShortType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalShortBuiltinType()); break; } case TypeFlag::CHAR: { - Relation()->GetNode()->SetTsType(Checker()->GlobalCharType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalCharBuiltinType()); break; } case TypeFlag::INT: { - Relation()->GetNode()->SetTsType(Checker()->GlobalIntType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalIntBuiltinType()); break; } case TypeFlag::LONG: { - Relation()->GetNode()->SetTsType(Checker()->GlobalLongType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalLongBuiltinType()); break; } case TypeFlag::FLOAT: { - Relation()->GetNode()->SetTsType(Checker()->GlobalFloatType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalFloatBuiltinType()); break; } case TypeFlag::DOUBLE: { - Relation()->GetNode()->SetTsType(Checker()->GlobalDoubleType()); + Relation()->GetNode()->SetTsType(Checker()->GlobalDoubleBuiltinType()); break; } default: { @@ -156,62 +119,6 @@ private: Relation()->Result(true); } - - template - void ApplyWidening(TypeFlag flag) - { - if (!Source()->HasTypeFlag(flag)) { - return; - } - - switch (ETSChecker::ETSChecker::ETSType(Source())) { - case TypeFlag::BYTE: { - ApplyWidening(); - break; - } - case TypeFlag::CHAR: { - ApplyWidening(); - break; - } - case TypeFlag::SHORT: { - ApplyWidening(); - break; - } - case TypeFlag::INT: { - ApplyWidening(); - break; - } - case TypeFlag::LONG: { - ApplyWidening(); - break; - } - case TypeFlag::FLOAT: { - ApplyWidening(); - break; - } - case TypeFlag::DOUBLE: { - ApplyWidening(); - break; - } - default: { - return; - } - } - Relation()->Result(true); - } - - template - void ApplyWidening() - { - using SType = typename SourceType::UType; - using TType = typename TargetType::UType; - SType value = reinterpret_cast(Source())->GetValue(); - - if (!Relation()->OnlyCheckWidening()) { - ES2PANDA_ASSERT(Relation()->GetNode()); - Relation()->GetNode()->SetTsType(Checker()->ProgramAllocator()->New(static_cast(value))); - } - } }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ts/destructuringContext.cpp b/ets2panda/checker/ts/destructuringContext.cpp index 965e5cda03606a470bf11c1715917e80d0379f2b..61a705d700197bc02bb6461af11cb3f0a018f526 100644 --- a/ets2panda/checker/ts/destructuringContext.cpp +++ b/ets2panda/checker/ts/destructuringContext.cpp @@ -333,6 +333,7 @@ Type *ArrayDestructuringContext::CreateTupleTypeForRest(TupleType *tuple) ES2PANDA_ASSERT(memberVar != nullptr); memberVar->SetTsType(tupleElementType); elementFlags.push_back(memberFlag); + ES2PANDA_ASSERT(desc != nullptr); desc->properties.push_back(memberVar); index_++; diff --git a/ets2panda/checker/typeChecker/TypeChecker.cpp b/ets2panda/checker/typeChecker/TypeChecker.cpp index b9977ba38af132168115304b4eba1ef966c98192..aeb0e9e5f51bdbc79f0e6346457e0b3e1810c7ae 100644 --- a/ets2panda/checker/typeChecker/TypeChecker.cpp +++ b/ets2panda/checker/typeChecker/TypeChecker.cpp @@ -18,14 +18,6 @@ namespace ark::es2panda::checker { -void ETSTypeChecker::VisitArrowFunctionExpression(ir::ArrowFunctionExpression *node) -{ - Iterate(node); - if (!node->TsType()->IsETSObjectType()) { - LogError(diagnostic::INFER_FAIL_ON_LAMBDA, {node->TsType()}, node->Start()); - } -} - bool RunTypeChecker(Checker *checker, ScriptExtension ext, ir::AstNode *node) { switch (ext) { diff --git a/ets2panda/checker/typeChecker/TypeChecker.h b/ets2panda/checker/typeChecker/TypeChecker.h index 802f884a5c6a5d19d8dd23ba0a4add9ff72346b7..864753b273ad8713ffc5957a7b5456f8c59b6881 100644 --- a/ets2panda/checker/typeChecker/TypeChecker.h +++ b/ets2panda/checker/typeChecker/TypeChecker.h @@ -50,9 +50,6 @@ private: class ETSTypeChecker : public TypeChecker { public: explicit ETSTypeChecker(Checker *checker) : TypeChecker(checker) {} - -private: - void VisitArrowFunctionExpression(ir::ArrowFunctionExpression *node) override; }; class JSTypeChecker : public TypeChecker { diff --git a/ets2panda/checker/types/ets/byteType.cpp b/ets2panda/checker/types/ets/byteType.cpp index 44537a4f5469bd9f109de7b02dc32816a05cf909..e69f7aff80e99cd986e89b4a4b5dc09b98f17023 100644 --- a/ets2panda/checker/types/ets/byteType.cpp +++ b/ets2panda/checker/types/ets/byteType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -15,8 +15,9 @@ #include "byteType.h" +#include "checker/ETSchecker.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingConverter.h" +#include "checker/types/ets/etsObjectType.h" namespace ark::es2panda::checker { void ByteType::Identical(TypeRelation *relation, Type *other) @@ -26,13 +27,7 @@ void ByteType::Identical(TypeRelation *relation, Type *other) } } -void ByteType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) -{ - if (relation->ApplyUnboxing()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); -} +void ByteType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) {} bool ByteType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) { @@ -57,38 +52,9 @@ void ByteType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::SHORT | TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::CHAR)) { - conversion::WideningNarrowingPrimitive(relation, this, target->AsCharType()); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_BYTE)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_BYTE)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/byteType.h b/ets2panda/checker/types/ets/byteType.h index a92f1f637d46d3611ba579ca23e7acc4cd63d9ca..a622ebc4e27e1b8bf50171ba47e1b301816d9c7e 100644 --- a/ets2panda/checker/types/ets/byteType.h +++ b/ets2panda/checker/types/ets/byteType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,11 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_BYTE; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_ != 0}; - } - private: UType value_ {0}; }; diff --git a/ets2panda/checker/types/ets/charType.cpp b/ets2panda/checker/types/ets/charType.cpp index 62fad52d6eb2f8e635e2943810a817976103d996..43bbec66cd258fa3b35bd307b86ec6a804ea5725 100644 --- a/ets2panda/checker/types/ets/charType.cpp +++ b/ets2panda/checker/types/ets/charType.cpp @@ -16,7 +16,7 @@ #include "charType.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" namespace ark::es2panda::checker { void CharType::Identical(TypeRelation *relation, Type *other) @@ -28,10 +28,7 @@ void CharType::Identical(TypeRelation *relation, Type *other) void CharType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } bool CharType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) @@ -57,38 +54,9 @@ void CharType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/charType.h b/ets2panda/checker/types/ets/charType.h index 9ddb9054ff1173910324d71e2ccdb1a65e3854e6..793a64ea4ed52a5ed44f86b1a6439623f9e55375 100644 --- a/ets2panda/checker/types/ets/charType.h +++ b/ets2panda/checker/types/ets/charType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,11 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_CHAR; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_ != '\0'}; - } - private: UType value_ {'\0'}; }; diff --git a/ets2panda/checker/types/ets/doubleType.cpp b/ets2panda/checker/types/ets/doubleType.cpp index 91477c19e4e6d3e13e3bd32caa00fe15102b22c2..ad58d7bc8c9a5470fb94e12a8b57660b0dd10813 100644 --- a/ets2panda/checker/types/ets/doubleType.cpp +++ b/ets2panda/checker/types/ets/doubleType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -28,9 +28,6 @@ void DoubleType::Identical(TypeRelation *relation, Type *other) void DoubleType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing() && !relation->IsTrue()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } @@ -56,34 +53,9 @@ void DoubleType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG | - TypeFlag::FLOAT)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/doubleType.h b/ets2panda/checker/types/ets/doubleType.h index e7e7dde792299f53ba775bea89d0a674dab337d2..fa3d5de326b61b66c9a6eaa2f745f8d82e7044a4 100644 --- a/ets2panda/checker/types/ets/doubleType.h +++ b/ets2panda/checker/types/ets/doubleType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,12 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_DOUBLE; } - std::tuple ResolveConditionExpr() const override - { - // isNan = !(value_ == value_) - return {IsConstantType(), (value_ != 0) && (value_ == value_)}; - } - private: UType value_ {0.0}; }; diff --git a/ets2panda/checker/types/ets/etsAnyType.cpp b/ets2panda/checker/types/ets/etsAnyType.cpp index 63dc4d14176068f7a18e0ad929ab48a862765001..ea399276063cd735fb696ab45e416260bd38d9f9 100644 --- a/ets2panda/checker/types/ets/etsAnyType.cpp +++ b/ets2panda/checker/types/ets/etsAnyType.cpp @@ -14,6 +14,7 @@ */ #include "etsAnyType.h" +#include #include "checker/ETSchecker.h" #include "checker/ets/conversion.h" @@ -22,7 +23,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) @@ -33,8 +34,6 @@ void ETSAnyType::AssignmentTarget(TypeRelation *relation, Type *source) } if (relation->ApplyBoxing()) { - auto checker = relation->GetChecker()->AsETSChecker(); - relation->GetNode()->AddBoxingUnboxingFlags(checker->GetBoxingFlag(checker->MaybeBoxType(source))); relation->Result(true); } } @@ -108,6 +107,6 @@ void ETSAnyType::ToDebugInfoType(std::stringstream &ss) const Type *ETSAnyType::Instantiate(ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, [[maybe_unused]] GlobalTypesHolder *globalTypes) { - return allocator->New(); + return isRelaxedAny_ ? allocator->New(true) : allocator->New(); } -} // namespace ark::es2panda::checker \ No newline at end of file +} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsAnyType.h b/ets2panda/checker/types/ets/etsAnyType.h index 3dbca0c808e843261e5c8ecd42ef2b6e0b43f280..7d12b6a004e77c58e9e50f6f0b2b2a2691dd5668 100644 --- a/ets2panda/checker/types/ets/etsAnyType.h +++ b/ets2panda/checker/types/ets/etsAnyType.h @@ -22,6 +22,7 @@ namespace ark::es2panda::checker { class ETSAnyType : public Type { public: ETSAnyType() : Type(TypeFlag::ETS_ANY) {} + explicit ETSAnyType(bool isRelaxedAny) : Type(TypeFlag::ETS_ANY), isRelaxedAny_(isRelaxedAny) {} void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; @@ -39,10 +40,13 @@ public: Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; - std::tuple ResolveConditionExpr() const override + bool IsRelaxedAny() const { - return {IsConstantType(), false}; + return isRelaxedAny_; } + +private: + bool isRelaxedAny_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsArrayType.cpp b/ets2panda/checker/types/ets/etsArrayType.cpp index 2f0573d664103a80b83e96d590b62ca7d01da44c..79d9cc398389767328cfa2ccc2d9a51868e91f81 100644 --- a/ets2panda/checker/types/ets/etsArrayType.cpp +++ b/ets2panda/checker/types/ets/etsArrayType.cpp @@ -111,7 +111,7 @@ void ETSArrayType::AssignmentTarget(TypeRelation *relation, Type *source) source->AsETSArrayType()->ElementType()->IsETSPrimitiveOrEnumType()) { return; } - relation->IsAssignableTo(source->AsETSArrayType()->ElementType(), element_); + relation->IsSupertypeOf(element_, source->AsETSArrayType()->ElementType()); } } diff --git a/ets2panda/checker/types/ets/etsArrayType.h b/ets2panda/checker/types/ets/etsArrayType.h index 9794adb54f0f11e1a5cc0dfb2a2ffc7b528fdde4..c9dd00904fffa7d09046075fde8e94a49ff87db8 100644 --- a/ets2panda/checker/types/ets/etsArrayType.h +++ b/ets2panda/checker/types/ets/etsArrayType.h @@ -38,11 +38,6 @@ public: element_ = element; } - std::tuple ResolveConditionExpr() const override - { - return {false, false}; - } - void ToString(std::stringstream &ss, bool precise) const override; void ToAssemblerType(std::stringstream &ss) const override; diff --git a/ets2panda/checker/types/ets/etsAsyncFuncReturnType.cpp b/ets2panda/checker/types/ets/etsAsyncFuncReturnType.cpp index 46308e495d546fec72639801ef3c8588619a8f9a..bc2a83fca736a1b978110d1655b64d2fa954bab3 100644 --- a/ets2panda/checker/types/ets/etsAsyncFuncReturnType.cpp +++ b/ets2panda/checker/types/ets/etsAsyncFuncReturnType.cpp @@ -39,6 +39,15 @@ void ETSAsyncFuncReturnType::Identical(TypeRelation *relation, Type *other) relation->Result(false); } +void ETSAsyncFuncReturnType::IsSupertypeOf(TypeRelation *relation, Type *source) +{ + GetPromiseTypeArg()->IsSupertypeOf(relation, source); + if (relation->IsTrue()) { + return; + } + promiseType_->IsSupertypeOf(relation, source); +} + bool ETSAsyncFuncReturnType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) { return false; @@ -47,9 +56,6 @@ bool ETSAsyncFuncReturnType::AssignmentSource([[maybe_unused]] TypeRelation *rel void ETSAsyncFuncReturnType::AssignmentTarget(TypeRelation *relation, Type *source) { relation->IsAssignableTo(source, promiseType_) || relation->IsAssignableTo(source, GetPromiseTypeArg()); - if (relation->IsTrue() && !source->IsETSObjectType() && relation->ApplyBoxing()) { - relation->GetChecker()->AsETSChecker()->MaybeAddBoxingFlagInRelation(relation, source); - } } void ETSAsyncFuncReturnType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) diff --git a/ets2panda/checker/types/ets/etsAsyncFuncReturnType.h b/ets2panda/checker/types/ets/etsAsyncFuncReturnType.h index e1ede4842b6fe35b3dc6ac4d712bf8e26ed064f2..d6b4d58b22b090e2bb0e2202a5d51510d5ead510 100644 --- a/ets2panda/checker/types/ets/etsAsyncFuncReturnType.h +++ b/ets2panda/checker/types/ets/etsAsyncFuncReturnType.h @@ -23,7 +23,7 @@ class GlobalTypesHolder; class ETSAsyncFuncReturnType : public ETSObjectType { public: - ETSAsyncFuncReturnType(ArenaAllocator *allocator, TypeRelation *relation, ETSObjectType *promiseType) + ETSAsyncFuncReturnType(ThreadSafeArenaAllocator *allocator, TypeRelation *relation, ETSObjectType *promiseType) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_OBJECT, std::make_tuple(nullptr, ETSObjectFlags::ASYNC_FUNC_RETURN_TYPE, relation)), promiseType_(promiseType) @@ -33,6 +33,7 @@ public: void ToString(std::stringstream &ss, bool precise) const override; void Identical(TypeRelation *relation, Type *other) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; bool AssignmentSource(TypeRelation *relation, Type *target) override; void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; diff --git a/ets2panda/checker/types/ets/etsBigIntType.h b/ets2panda/checker/types/ets/etsBigIntType.h index 279c5d08170a65b67037458472b7d959bcc61005..8ed5f40ccb1f3ecdda34f88f5fdc60eee0483297 100644 --- a/ets2panda/checker/types/ets/etsBigIntType.h +++ b/ets2panda/checker/types/ets/etsBigIntType.h @@ -21,14 +21,14 @@ namespace ark::es2panda::checker { class ETSBigIntType : public ETSObjectType { public: - explicit ETSBigIntType(ArenaAllocator *allocator, [[maybe_unused]] ETSObjectType *super) + explicit ETSBigIntType(ThreadSafeArenaAllocator *allocator, [[maybe_unused]] ETSObjectType *super) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_BIGINT, nullptr, ETSObjectFlags::CLASS | ETSObjectFlags::BUILTIN_BIGINT | ETSObjectFlags::RESOLVED_SUPER) { SetSuperType(super); } - explicit ETSBigIntType(ArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, + explicit ETSBigIntType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, util::StringView value) : ETSObjectType( allocator, "", compiler::Signatures::BUILTIN_BIGINT, diff --git a/ets2panda/checker/types/ets/etsBooleanType.cpp b/ets2panda/checker/types/ets/etsBooleanType.cpp index e3d661ea8d4435fc18891f6373dceb2ebff8bdcd..f19823daa2f3ab0f1dc703ee184a7708609884c5 100644 --- a/ets2panda/checker/types/ets/etsBooleanType.cpp +++ b/ets2panda/checker/types/ets/etsBooleanType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -26,14 +26,9 @@ void ETSBooleanType::Identical(TypeRelation *relation, Type *other) } } -void ETSBooleanType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) -{ - if (relation->ApplyUnboxing() && !relation->IsTrue()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } -} +void ETSBooleanType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) {} -bool ETSBooleanType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +bool ETSBooleanType::AssignmentSource(TypeRelation *relation, Type *target) { if (relation->ApplyBoxing() && target->IsETSObjectType()) { relation->GetChecker()->AsETSChecker()->CheckBoxedSourceTypeAssignable(relation, this, target); diff --git a/ets2panda/checker/types/ets/etsBooleanType.h b/ets2panda/checker/types/ets/etsBooleanType.h index 421ae63e3b29b5ff3b070f1b25c482a73f8343ef..2bc7ccaddca17d8268de62ba33e7fbaa7a3aa371 100644 --- a/ets2panda/checker/types/ets/etsBooleanType.h +++ b/ets2panda/checker/types/ets/etsBooleanType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -51,11 +51,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_BOOLEAN; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_}; - } - private: UType value_ {false}; }; diff --git a/ets2panda/checker/types/ets/etsDynamicFunctionType.h b/ets2panda/checker/types/ets/etsDynamicFunctionType.h deleted file mode 100644 index 77a3c5731b2a49305766413eb1db370c0a9635e2..0000000000000000000000000000000000000000 --- a/ets2panda/checker/types/ets/etsDynamicFunctionType.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2024-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_ETS_DYNAMIC_FUNCTION_TYPE_H -#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_DYNAMIC_FUNCTION_TYPE_H - -#include "checker/types/ets/etsFunctionType.h" -#include "checker/ETSchecker.h" - -namespace ark::es2panda::checker { - -class ETSDynamicFunctionType : public ETSFunctionType { -public: - explicit ETSDynamicFunctionType(ETSChecker *checker, util::StringView name, ArenaVector &&signatures, - Language lang) - : ETSFunctionType(checker, name, std::move(signatures)), lang_(lang) - { - AddTypeFlag(TypeFlag::ETS_DYNAMIC_FUNCTION_TYPE); - } - - explicit ETSDynamicFunctionType(ETSChecker *checker, Signature *signature, Language lang) - : ETSFunctionType(checker, signature), lang_(lang) - { - AddTypeFlag(TypeFlag::ETS_DYNAMIC_FUNCTION_TYPE); - } - - ETSDynamicFunctionType() = delete; - ~ETSDynamicFunctionType() override = default; - NO_COPY_SEMANTIC(ETSDynamicFunctionType); - NO_MOVE_SEMANTIC(ETSDynamicFunctionType); - - es2panda::Language Language() const - { - return lang_; - } - -private: - es2panda::Language lang_; -}; -} // namespace ark::es2panda::checker - -#endif /* ES2PANDA_COMPILER_CHECKER_TYPES_ETS_DYNAMIC_FUNCTION_TYPE_H */ diff --git a/ets2panda/checker/types/ets/etsDynamicType.cpp b/ets2panda/checker/types/ets/etsDynamicType.cpp deleted file mode 100644 index dece7e63320fc1379835d2166c908127d6c8cfbb..0000000000000000000000000000000000000000 --- a/ets2panda/checker/types/ets/etsDynamicType.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2021-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 "etsDynamicType.h" -#include "checker/ETSchecker.h" -#include "checker/ets/conversion.h" -#include "checker/types/ets/etsDynamicFunctionType.h" - -namespace ark::es2panda::checker { - -varbinder::LocalVariable *ETSDynamicType::GetPropertyDynamic(const util::StringView &name, - const ETSChecker *checker) const -{ - auto it = propertiesCache_.find(name); - if (it != propertiesCache_.end()) { - return it->second; - } - - varbinder::LocalVariable *var = varbinder::Scope::CreateVar( - Allocator(), name, varbinder::VariableFlags::BUILTIN_TYPE, nullptr); - ES2PANDA_ASSERT(var != nullptr); - var->SetTsType(checker->GlobalBuiltinDynamicType(lang_)); - propertiesCache_.emplace(name, var); - - return var; -} - -void ETSDynamicType::AssignmentTarget(TypeRelation *relation, Type *source) -{ - if (hasDecl_) { - return ETSObjectType::AssignmentTarget(relation, source); - } - - if (relation->ApplyBoxing() && !relation->IsTrue() && IsConvertible(source)) { - relation->Result(true); - return; - } - - if (source->IsETSDynamicType()) { - relation->Result(true); - } -} - -bool ETSDynamicType::AssignmentSource(TypeRelation *relation, Type *target) -{ - if (hasDecl_) { - return ETSObjectType::AssignmentSource(relation, target); - } - - if (relation->ApplyUnboxing() && IsConvertible(target)) { - relation->Result(true); - return true; - } - - if (target->IsETSDynamicType()) { - relation->Result(true); - } - return relation->IsTrue(); -} - -void ETSDynamicType::Cast(TypeRelation *relation, Type *target) -{ - if (hasDecl_) { - return ETSObjectType::Cast(relation, target); - } - - if (relation->InCastingContext() || IsConvertible(target)) { - relation->Result(true); - return; - } - - conversion::Forbidden(relation); -} - -void ETSDynamicType::CastTarget(TypeRelation *relation, Type *source) -{ - if (hasDecl_) { - ETSObjectType::CastTarget(relation, source); - return; - } - - if (relation->InCastingContext() || IsConvertible(source)) { - relation->Result(true); - return; - } - - conversion::Forbidden(relation); -} - -bool ETSDynamicType::IsConvertible(Type const *target) -{ - return target->IsETSDynamicType() || target->IsETSObjectType() || target->IsETSArrayType() || - target->IsETSTupleType() || target->IsETSFunctionType() || - target->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC | checker::TypeFlag::ETS_BOOLEAN); -} - -ETSFunctionType *ETSDynamicType::CreateMethodTypeForProp(const util::StringView &name) const -{ - auto checker = GetRelation()->GetChecker()->AsETSChecker(); - return checker->CreateETSDynamicMethodType(name, {{}, Allocator()->Adapter()}, lang_); -} - -void ETSDynamicType::ToAssemblerType(std::stringstream &ss) const -{ - ss << compiler::Signatures::Dynamic::Type(lang_); -} - -} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsEnumType.cpp b/ets2panda/checker/types/ets/etsEnumType.cpp index 61d23b7cbf94ef87d12a88cce69cb24207f4cb97..233a403e0501e8a662fba2b7bd2ad9c056b0df62 100644 --- a/ets2panda/checker/types/ets/etsEnumType.cpp +++ b/ets2panda/checker/types/ets/etsEnumType.cpp @@ -15,11 +15,17 @@ #include "etsEnumType.h" +#include "checker/ETSchecker.h" #include "checker/ets/conversion.h" #include "checker/types/ets/etsUnionType.h" namespace ark::es2panda::checker { +Type *ETSEnumType::GetBaseEnumElementType(ETSChecker *checker) +{ + return checker->MaybeUnboxType(SuperType()->TypeArguments()[0]); +} + bool ETSStringEnumType::AssignmentSource(TypeRelation *relation, Type *target) { bool result = false; @@ -55,6 +61,7 @@ void ETSStringEnumType::Cast(TypeRelation *const relation, Type *const target) return; } if (target->IsETSStringType()) { + relation->RaiseError(diagnostic::ENUM_DEPRECATED_CAST, {this, target}, relation->GetNode()->Start()); relation->Result(true); return; } @@ -64,6 +71,7 @@ void ETSStringEnumType::Cast(TypeRelation *const relation, Type *const target) void ETSStringEnumType::CastTarget(TypeRelation *relation, Type *source) { if (source->IsETSStringType()) { + relation->RaiseError(diagnostic::ENUM_DEPRECATED_CAST, {source, this}, relation->GetNode()->Start()); relation->Result(true); return; } @@ -77,7 +85,7 @@ bool ETSIntEnumType::AssignmentSource(TypeRelation *relation, Type *target) if (target->AsETSObjectType()->IsGlobalETSObjectType() || target->AsETSObjectType()->Name() == compiler::Signatures::NUMERIC) { result = true; - } else if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC)) { + } else if (target->IsBuiltinNumeric()) { result = true; relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); } @@ -108,8 +116,8 @@ void ETSIntEnumType::Cast(TypeRelation *const relation, Type *const target) relation->Result(true); return; } - if (target->HasTypeFlag(TypeFlag::ETS_NUMERIC) || - (target->IsETSObjectType() && target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC))) { + if (target->HasTypeFlag(TypeFlag::ETS_NUMERIC) || target->IsBuiltinNumeric()) { + relation->RaiseError(diagnostic::ENUM_DEPRECATED_CAST, {this, target}, relation->GetNode()->Start()); relation->Result(true); return; } @@ -118,15 +126,12 @@ void ETSIntEnumType::Cast(TypeRelation *const relation, Type *const target) void ETSIntEnumType::CastTarget(TypeRelation *relation, Type *source) { - if (source->IsIntType()) { - relation->Result(true); - return; - } - if (source->IsETSObjectType() && source->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC)) { + if (source->IsIntType() || source->IsBuiltinNumeric()) { + relation->RaiseError(diagnostic::ENUM_DEPRECATED_CAST, {source, this}, relation->GetNode()->Start()); relation->Result(true); return; } conversion::Forbidden(relation); } -} // namespace ark::es2panda::checker \ No newline at end of file +} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsEnumType.h b/ets2panda/checker/types/ets/etsEnumType.h index a5c6bdf33d7a3e767413365b6105f75f9114285b..3005b78053a406f8ef707e2410d7d7cc090593a4 100644 --- a/ets2panda/checker/types/ets/etsEnumType.h +++ b/ets2panda/checker/types/ets/etsEnumType.h @@ -19,16 +19,24 @@ #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsObjectTypeConstants.h" #include "checker/types/typeFlag.h" +#include "ir/base/classProperty.h" +#include "ir/expressions/arrayExpression.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/expressions/memberExpression.h" namespace ark::es2panda::checker { class ETSEnumType : public ETSObjectType { public: - explicit ETSEnumType(ArenaAllocator *allocator, util::StringView name, util::StringView internalName, - ir::AstNode *declNode, TypeRelation *relation) + // CC-OFFNXT(G.FUN.01-CPP) solid logic + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) + explicit ETSEnumType(ThreadSafeArenaAllocator *allocator, util::StringView name, util::StringView internalName, + ir::AstNode *declNode, TypeRelation *relation, ETSObjectFlags const flag) : ETSObjectType(allocator, name, internalName, - std::make_tuple(declNode, ETSObjectFlags::CLASS | ETSObjectFlags::ENUM_OBJECT, relation)) + std::make_tuple(declNode, ETSObjectFlags::CLASS | flag, relation)), + memberNameToOrdinal_(allocator->Adapter()) { + InitElementsShortcuts(declNode->AsClassDefinition()); } NO_COPY_SEMANTIC(ETSEnumType); @@ -37,21 +45,86 @@ public: ETSEnumType() = delete; ~ETSEnumType() override = default; - static constexpr std::string_view const TO_STRING_METHOD_NAME {"toString"}; - static constexpr std::string_view const VALUE_OF_METHOD_NAME {"valueOf"}; - static constexpr std::string_view const GET_NAME_METHOD_NAME {"getName"}; - static constexpr std::string_view const GET_VALUE_OF_METHOD_NAME {"getValueOf"}; - static constexpr std::string_view const FROM_VALUE_METHOD_NAME {"fromValue"}; - static constexpr std::string_view const VALUES_METHOD_NAME {"values"}; - static constexpr std::string_view const GET_ORDINAL_METHOD_NAME {"getOrdinal"}; - static constexpr std::string_view const DOLLAR_GET_METHOD_NAME {"$_get"}; + static constexpr std::string_view TO_STRING_METHOD_NAME {"toString"}; + static constexpr std::string_view VALUE_OF_METHOD_NAME {"valueOf"}; + static constexpr std::string_view GET_NAME_METHOD_NAME {"getName"}; + static constexpr std::string_view GET_VALUE_OF_METHOD_NAME {"getValueOf"}; + static constexpr std::string_view FROM_VALUE_METHOD_NAME {"fromValue"}; + static constexpr std::string_view VALUES_METHOD_NAME {"values"}; + static constexpr std::string_view GET_ORDINAL_METHOD_NAME {"getOrdinal"}; + static constexpr std::string_view DOLLAR_GET_METHOD_NAME {"$_get"}; + + static constexpr std::string_view STRING_VALUES_ARRAY_NAME {"#StringValuesArray"}; + static constexpr std::string_view VALUES_ARRAY_NAME {"#ValuesArray"}; + static constexpr std::string_view NAMES_ARRAY_NAME {"#NamesArray"}; + + auto *Underlying() + { + ES2PANDA_ASSERT(membersValues_->TsType() != nullptr); + return membersValues_->TsType()->AsETSArrayType()->ElementType(); + } + + auto GetOrdinalFromMemberName(std::string_view name) const + { + return memberNameToOrdinal_.at(name); + } + + auto GetValueLiteralFromOrdinal(size_t ord) const + { + ES2PANDA_ASSERT(ord < membersValues_->Elements().size()); + return membersValues_->Elements()[ord]; + } + + bool NodeIsEnumLiteral(ir::Expression *node) const + { + ES2PANDA_ASSERT(node->TsType() == this); + if (!node->IsMemberExpression()) { + return false; + } + + auto mobj = node->AsMemberExpression()->Object(); + if (mobj->TsType() == this) { + // No need to search properties since enum-literals are the only enum-type properties + // NOTE(dkofanov): For some reason, 'enumLowering' changes 'CLASS' to 'ENUM_LITERAL', instead of 'ENUM'. + ES2PANDA_ASSERT(GetDeclNode()->AsClassDefinition()->IsEnumTransformed()); + return true; + } + return false; + } + Type *GetBaseEnumElementType(ETSChecker *checker); + +private: + void InitElementsShortcuts(ir::ClassDefinition *declNode) + { + Span membersNames {}; + for (auto elem : declNode->Body()) { + auto elemName = elem->AsClassElement()->Key()->AsIdentifier()->Name(); + if (elemName == NAMES_ARRAY_NAME) { + membersNames = Span(elem->AsClassProperty()->Value()->AsArrayExpression()->Elements()); + } else if (elemName == VALUES_ARRAY_NAME) { + membersValues_ = elem->AsClassProperty()->Value()->AsArrayExpression(); // int-enum + } else if ((elemName == STRING_VALUES_ARRAY_NAME) && (membersValues_ == nullptr)) { + membersValues_ = elem->AsClassProperty()->Value()->AsArrayExpression(); // string-enum + } + } + auto membersValues = Span {membersValues_->Elements()}; + ES2PANDA_ASSERT(membersValues.size() == membersNames.size()); + for (size_t i = 0; i < membersNames.size(); i++) { + memberNameToOrdinal_.insert({membersNames[i]->AsStringLiteral()->Str(), i}); + ES2PANDA_ASSERT(membersValues[i]->IsStringLiteral() || membersValues[i]->IsNumberLiteral()); + } + } + +private: + ArenaMap memberNameToOrdinal_; + ir::ArrayExpression *membersValues_; }; class ETSIntEnumType : public ETSEnumType { public: - explicit ETSIntEnumType(ArenaAllocator *allocator, util::StringView name, util::StringView internalName, + explicit ETSIntEnumType(ThreadSafeArenaAllocator *allocator, util::StringView name, util::StringView internalName, ir::AstNode *declNode, TypeRelation *relation) - : ETSEnumType(allocator, name, internalName, declNode, relation) + : ETSEnumType(allocator, name, internalName, declNode, relation, ETSObjectFlags::INT_ENUM_OBJECT) { AddTypeFlag(checker::TypeFlag::ETS_INT_ENUM); } @@ -70,9 +143,9 @@ public: class ETSStringEnumType : public ETSEnumType { public: - explicit ETSStringEnumType(ArenaAllocator *allocator, util::StringView name, util::StringView internalName, - ir::AstNode *declNode, TypeRelation *relation) - : ETSEnumType(allocator, name, internalName, declNode, relation) + explicit ETSStringEnumType(ThreadSafeArenaAllocator *allocator, util::StringView name, + util::StringView internalName, ir::AstNode *declNode, TypeRelation *relation) + : ETSEnumType(allocator, name, internalName, declNode, relation, ETSObjectFlags::STRING_ENUM_OBJECT) { AddTypeFlag(checker::TypeFlag::ETS_STRING_ENUM); } @@ -91,4 +164,4 @@ public: } // namespace ark::es2panda::checker -#endif \ No newline at end of file +#endif diff --git a/ets2panda/checker/types/ets/etsFunctionType.cpp b/ets2panda/checker/types/ets/etsFunctionType.cpp index 717d3f8006df5dd4a3a66481f6af21cf3d17b3cf..669ab5d1ed0a67aaf076429635f2f8b91605e10d 100644 --- a/ets2panda/checker/types/ets/etsFunctionType.cpp +++ b/ets2panda/checker/types/ets/etsFunctionType.cpp @@ -15,6 +15,8 @@ #include "checker/ETSchecker.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/typeError.h" +#include "compiler/lowering/phase.h" namespace ark::es2panda::checker { @@ -55,9 +57,8 @@ ETSFunctionType::ETSFunctionType(ETSChecker *checker, Signature *signature) } // #22951: proper this type implementation -static void HackThisParameterInExtensionFunctionInvoke(ETSObjectType *interface, size_t arity) +static void HackThisParameterInExtensionFunctionInvoke(ETSObjectType *interface, std::string &invokeName) { - auto invokeName = FunctionalInterfaceInvokeName(arity, false); auto *property = interface->AsETSObjectType()->GetOwnProperty( util::StringView(invokeName)); ES2PANDA_ASSERT(property != nullptr); @@ -78,53 +79,66 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, auto sigParamsSize = signature->Params().size(); auto nPosParams = arity < sigParamsSize ? arity : sigParamsSize; auto *functionN = checker->GlobalBuiltinFunctionType(nPosParams, true); - auto *substitution = checker->NewSubstitution(); + + if (nPosParams >= checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold()) { + return functionN; + } + + auto substitution = Substitution {}; ES2PANDA_ASSERT(functionN != nullptr && nPosParams <= functionN->TypeArguments().size()); - ES2PANDA_ASSERT(substitution != nullptr); for (size_t i = 0; i < nPosParams; i++) { - substitution->emplace(functionN->TypeArguments()[i]->AsETSTypeParameter(), - checker->MaybeBoxType(signature->Params()[i]->TsType())); + substitution.emplace(functionN->TypeArguments()[i]->AsETSTypeParameter(), + checker->MaybeBoxType(signature->Params()[i]->TsType())); } auto *elementType = !signature->RestVar()->TsType()->IsETSTupleType() ? checker->GetElementTypeOfArray(signature->RestVar()->TsType()) : checker->GlobalETSAnyType(); - substitution->emplace(functionN->TypeArguments()[0]->AsETSTypeParameter(), checker->MaybeBoxType(elementType)); - return functionN->Substitute(checker->Relation(), substitution, true, isExtensionHack); + substitution.emplace(functionN->TypeArguments()[nPosParams]->AsETSTypeParameter(), + checker->MaybeBoxType(elementType)); + substitution.emplace(functionN->TypeArguments()[nPosParams + 1]->AsETSTypeParameter(), + checker->MaybeBoxType(signature->ReturnType())); + auto result = functionN->Substitute(checker->Relation(), &substitution, true, isExtensionHack); + result->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL); + return result; } ES2PANDA_ASSERT(arity >= signature->MinArgCount() && arity <= signature->ArgCount()); - // Note: FunctionN is not supported yet + auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false); if (arity >= checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold()) { - return nullptr; + return funcIface; } - auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false); - auto *substitution = checker->NewSubstitution(); - - ES2PANDA_ASSERT(funcIface != nullptr); - ES2PANDA_ASSERT(substitution != nullptr); + auto substitution = Substitution {}; for (size_t i = 0; i < arity; i++) { - substitution->emplace(funcIface->TypeArguments()[i]->AsETSTypeParameter(), - checker->MaybeBoxType(signature->Params()[i]->TsType())); + substitution.emplace(funcIface->TypeArguments()[i]->AsETSTypeParameter(), + checker->MaybeBoxType(signature->Params()[i]->TsType())); } - substitution->emplace(funcIface->TypeArguments()[arity]->AsETSTypeParameter(), - checker->MaybeBoxType(signature->ReturnType())); - auto result = funcIface->Substitute(checker->Relation(), substitution, true, isExtensionHack); + substitution.emplace(funcIface->TypeArguments()[arity]->AsETSTypeParameter(), + checker->MaybeBoxType(signature->ReturnType())); + auto result = funcIface->Substitute(checker->Relation(), &substitution, true, isExtensionHack); if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) { - HackThisParameterInExtensionFunctionInvoke(result, arity); + auto invokeName = checker->FunctionalInterfaceInvokeName(arity, false); + HackThisParameterInExtensionFunctionInvoke(result, invokeName); } + + result->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL); return result; } ETSObjectType *ETSFunctionType::ArrowToFunctionalInterface(ETSChecker *checker) { - auto &cached = arrowToFuncInterface_; - if (LIKELY(cached != nullptr)) { - return cached; + auto &cached = compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->GetArrowToFuncInterfaces(); + + auto found = cached.find(this); + if (LIKELY(found != cached.end())) { + return found->second; } - return cached = FunctionTypeToFunctionalInterfaceType(checker, ArrowSignature(), ArrowSignature()->MinArgCount()); + return cached + .emplace(this, + FunctionTypeToFunctionalInterfaceType(checker, ArrowSignature(), ArrowSignature()->MinArgCount())) + .first->second; } ETSObjectType *ETSFunctionType::ArrowToFunctionalInterfaceDesiredArity(ETSChecker *checker, size_t arity) @@ -137,13 +151,15 @@ ETSObjectType *ETSFunctionType::ArrowToFunctionalInterfaceDesiredArity(ETSChecke ETSFunctionType *ETSFunctionType::MethodToArrow(ETSChecker *checker) { - auto &cached = invokeToArrowSignature_; - if (LIKELY(cached != nullptr)) { - return cached; + auto &cached = compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->GetInvokeToArrowSignatures(); + + auto found = cached.find(this); + if (LIKELY(found != cached.end())) { + return found->second; } - ES2PANDA_ASSERT(!IsETSArrowType() && CallSignatures().size() == 1); - return cached = checker->CreateETSArrowType(CallSignatures()[0]); + ERROR_SANITY_CHECK(checker, !IsETSArrowType() && CallSignatures().size() == 1, return nullptr); + return cached.emplace(this, checker->CreateETSArrowType(CallSignatures()[0])).first->second; } void ETSFunctionType::AddCallSignature(Signature *signature) @@ -173,29 +189,28 @@ static inline void AssertNoMethodsInFunctionRelation([[maybe_unused]] Type *left static Signature *EnhanceSignatureSubstitution(TypeRelation *relation, Signature *super, Signature *sub) { auto checker = relation->GetChecker()->AsETSChecker(); - auto *substitution = checker->NewSubstitution(); + auto substitution = Substitution {}; - auto const enhance = [checker, sub, substitution](Type *param, Type *arg) { - return checker->EnhanceSubstitutionForType(sub->GetSignatureInfo()->typeParams, param, arg, substitution); + auto const enhance = [checker, sub, &substitution](Type *param, Type *arg) { + return checker->EnhanceSubstitutionForType(sub->GetSignatureInfo()->typeParams, param, arg, &substitution); }; for (size_t ix = 0; ix < sub->ArgCount(); ix++) { if (!enhance(sub->GetSignatureInfo()->params[ix]->TsType(), super->GetSignatureInfo()->params[ix]->TsType())) { return nullptr; } } + + if (!enhance(sub->ReturnType(), super->ReturnType())) { + return nullptr; + } + if (super->RestVar() != nullptr) { if (!enhance(sub->RestVar()->TsType(), super->RestVar()->TsType())) { return nullptr; } } - return sub->Substitute(relation, substitution); -} -static uint8_t SignatureThrowKindToOrder(Signature *sig) -{ - return sig->HasSignatureFlag(SignatureFlags::THROWS) ? 0U - : sig->HasSignatureFlag(SignatureFlags::RETHROWS) ? 1U - : 2U; + return sub->Substitute(relation, &substitution); } static bool SignatureIsSupertypeOf(TypeRelation *relation, Signature *super, Signature *sub) @@ -224,7 +239,7 @@ static bool SignatureIsSupertypeOf(TypeRelation *relation, Signature *super, Sig if (!relation->IsSupertypeOf(super->ReturnType(), sub->ReturnType())) { return false; } - return SignatureThrowKindToOrder(super) <= SignatureThrowKindToOrder(sub); + return true; } static ETSFunctionType *CoerceToArrowType(TypeRelation *relation, Type *type) @@ -257,16 +272,6 @@ bool ETSFunctionType::AssignmentSource(TypeRelation *relation, Type *target) { AssertNoMethodsInFunctionRelation(this, target); - // this should be defined by the dynamic type itself - if (target->IsETSDynamicType()) { - ES2PANDA_ASSERT(relation->GetNode() != nullptr); - if (relation->GetNode()->IsArrowFunctionExpression()) { - ES2PANDA_ASSERT(callSignatures_.size() == 1 && ArrowSignature()->HasSignatureFlag(SignatureFlags::CALL)); - return relation->Result(true); - } - return relation->Result(false); - } - return relation->IsSupertypeOf(target, this); } diff --git a/ets2panda/checker/types/ets/etsFunctionType.h b/ets2panda/checker/types/ets/etsFunctionType.h index f59b65c709b186655a85fb231952f2884b628baa..ffc9e8ea5987fe634c900543de8f21a8e019eb4c 100644 --- a/ets2panda/checker/types/ets/etsFunctionType.h +++ b/ets2panda/checker/types/ets/etsFunctionType.h @@ -134,11 +134,6 @@ public: void Cast(TypeRelation *relation, Type *target) override; void CastTarget(TypeRelation *relation, Type *source) override; - std::tuple ResolveConditionExpr() const override - { - return {false, false}; - } - void SetHelperSignature(Signature *signature) noexcept { helperSignature_ = signature; @@ -165,8 +160,6 @@ private: ArenaVector extensionAccessorSigs_; util::StringView const name_; util::StringView const assemblerName_; - ETSFunctionType *invokeToArrowSignature_ {}; - ETSObjectType *arrowToFuncInterface_ {}; Signature *helperSignature_ {}; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsNeverType.h b/ets2panda/checker/types/ets/etsNeverType.h index 06904ce0581c2dcf74cc495f1c17e960a04cbbf0..7e998eb6b619f7962aea4e1d931ca1b2edff1889 100644 --- a/ets2panda/checker/types/ets/etsNeverType.h +++ b/ets2panda/checker/types/ets/etsNeverType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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 @@ -38,11 +38,6 @@ public: TypeFacts GetTypeFacts() const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; - - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), false}; - } }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsNullishTypes.cpp b/ets2panda/checker/types/ets/etsNullishTypes.cpp index 6e102e16751ae3c4b039af93f2d5a8278a34a2de..e9abc5839f4d6d494453c5aa8d7ad9fab029c3b0 100644 --- a/ets2panda/checker/types/ets/etsNullishTypes.cpp +++ b/ets2panda/checker/types/ets/etsNullishTypes.cpp @@ -59,12 +59,12 @@ void ETSNullType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) void ETSNullType::ToAssemblerType(std::stringstream &ss) const { - ss << compiler::Signatures::BUILTIN_OBJECT; + ss << compiler::Signatures::NULL_ASSEMBLY_TYPE; } void ETSNullType::ToDebugInfoType(std::stringstream &ss) const { - ss << ETSObjectType::NameToDescriptor(compiler::Signatures::BUILTIN_OBJECT); + ss << ETSObjectType::NameToDescriptor(compiler::Signatures::NULL_ASSEMBLY_TYPE); } Type *ETSNullType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, diff --git a/ets2panda/checker/types/ets/etsNullishTypes.h b/ets2panda/checker/types/ets/etsNullishTypes.h index 195332ffd27cb4541ca6d7ee6bdd31beb21f0027..ff11033e5d4d7c2a2af967e67bd0cc3fea8ce87a 100644 --- a/ets2panda/checker/types/ets/etsNullishTypes.h +++ b/ets2panda/checker/types/ets/etsNullishTypes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -37,11 +37,6 @@ public: void ToDebugInfoType([[maybe_unused]] std::stringstream &ss) const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; - - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), false}; - } }; class ETSUndefinedType : public Type { @@ -60,11 +55,6 @@ public: void ToDebugInfoType([[maybe_unused]] std::stringstream &ss) const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; - - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), false}; - } }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index b6bddcd9b16b79efaa194385e3dec637d59f05fd..f4aecd59add6a5ee39d6c2d49ca7b9dd89b37b84 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -18,26 +18,49 @@ #include "checker/ETSchecker.h" #include "checker/ets/conversion.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "checker/types/ets/etsEnumType.h" +#include "compiler/lowering/phase.h" +#include "ir/statements/annotationDeclaration.h" namespace ark::es2panda::checker { void ETSObjectType::Iterate(const PropertyTraverser &cb) const { - for (const auto *prop : GetAllProperties()) { - cb(prop); + ForEachAllOwnProperties(cb); + ForEachAllNonOwnProperties(cb); +} + +void ETSObjectType::AddInterface(ETSObjectType *interfaceType) +{ + if (std::find(interfaces_.begin(), interfaces_.end(), interfaceType) == interfaces_.end()) { + interfaces_.push_back(interfaceType); + CacheSupertypeTransitive(interfaceType); } +} - if (superType_ != nullptr) { - superType_->Iterate(cb); +void ETSObjectType::SetSuperType(ETSObjectType *super) +{ + superType_ = super; + if (super == nullptr) { + return; } + CacheSupertypeTransitive(super); +} - for (const auto *interface : interfaces_) { - interface->Iterate(cb); +void ETSObjectType::CacheSupertypeTransitive(ETSObjectType *type) +{ + auto const insertType = [this](ETSObjectType *t) { + return transitiveSupertypes_.insert(t->GetOriginalBaseType()).second; + }; + if (insertType(type)) { + for (auto &t : type->transitiveSupertypes_) { + insertType(t); + } } } -varbinder::LocalVariable *ETSObjectType::SearchFieldsDecls(const util::StringView &name, - PropertySearchFlags flags) const +varbinder::LocalVariable *ETSObjectType::SearchFieldsDecls(util::StringView name, PropertySearchFlags flags) const { varbinder::LocalVariable *res {}; if ((flags & PropertySearchFlags::SEARCH_INSTANCE_FIELD) != 0) { @@ -58,37 +81,53 @@ varbinder::LocalVariable *ETSObjectType::SearchFieldsDecls(const util::StringVie return res; } -varbinder::LocalVariable *ETSObjectType::GetProperty(const util::StringView &name, PropertySearchFlags flags) const +varbinder::LocalVariable *ETSObjectType::GetProperty(util::StringView name, PropertySearchFlags flags) const { - varbinder::LocalVariable *res = SearchFieldsDecls(name, flags); - if (res == nullptr && (flags & PropertySearchFlags::SEARCH_METHOD) != 0) { - if ((flags & PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION) != 0) { - if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { - res = GetOwnProperty(name); + // CC-OFFNXT(G.FMT.14-CPP) project code style + auto const searchOwnMethod = [this, flags, name]() -> varbinder::LocalVariable * { + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { + if (auto res = GetOwnProperty(name); res != nullptr) { + return res; + } + } + if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { + if (auto res = GetOwnProperty(name); res != nullptr) { + return res; } + } + return nullptr; + }; - if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0)) { - res = GetOwnProperty(name); + if (auto res = SearchFieldsDecls(name, flags); res != nullptr) { + return res; + } + + if ((flags & PropertySearchFlags::SEARCH_METHOD) != 0) { + if ((flags & PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION) != 0) { + if (auto res = searchOwnMethod(); res != nullptr) { + return res; } } else { - res = CreateSyntheticVarFromEverySignature(name, flags); + if (auto res = CreateSyntheticVarFromEverySignature(name, flags)) { + return res; + } } } - if (res == nullptr && (flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) { + if (((flags & PropertySearchFlags::SEARCH_INSTANCE) != 0 || (flags & PropertySearchFlags::SEARCH_STATIC) == 0) && + (flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) { for (auto *interface : interfaces_) { - res = interface->GetProperty(name, flags); - if (res != nullptr) { + if (auto res = interface->GetProperty(name, flags); res != nullptr) { return res; } } } - if (res == nullptr && superType_ != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { - res = superType_->GetProperty(name, flags); + if ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0 && superType_ != nullptr) { + return superType_->GetProperty(name, flags); } - return res; + return nullptr; } bool ETSObjectType::IsPropertyInherited(const varbinder::Variable *var) @@ -160,7 +199,11 @@ static void UpdateDeclarationForGetterSetter(varbinder::LocalVariable *res, cons if (!HasAccessor(flags, funcType) || res->Declaration() != nullptr) { return; } - auto var = funcType->CallSignatures().front()->OwnerVar(); + + auto frontGetter = std::find_if(funcType->CallSignatures().begin(), funcType->CallSignatures().end(), + [](Signature *sig) { return sig->Function()->IsGetter(); }); + auto var = frontGetter == funcType->CallSignatures().end() ? funcType->CallSignatures().front()->OwnerVar() + : (*frontGetter)->OwnerVar(); auto decl = var->Declaration(); if (decl == nullptr || decl->Node() == nullptr) { return; @@ -168,11 +211,51 @@ static void UpdateDeclarationForGetterSetter(varbinder::LocalVariable *res, cons res->Reset(decl, var->Flags()); } +static PropertySearchFlags UpdateOverloadDeclarationSearchFlags(const PropertySearchFlags &flags) +{ + if ((flags & PropertySearchFlags::IGNORE_OVERLOAD) != 0) { + return flags; + } + PropertySearchFlags syntheticFlags = flags; + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { + syntheticFlags &= ~PropertySearchFlags::SEARCH_INSTANCE_METHOD; + syntheticFlags |= PropertySearchFlags::SEARCH_INSTANCE_DECL; + } + if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { + syntheticFlags &= ~PropertySearchFlags::SEARCH_STATIC_METHOD; + syntheticFlags |= PropertySearchFlags::SEARCH_STATIC_DECL; + } + return syntheticFlags; +} + +static PropertySearchFlags UpdateMethodSearchFlags(const PropertySearchFlags &flags) +{ + if ((flags & PropertySearchFlags::IGNORE_OVERLOAD) != 0) { + return flags; + } + PropertySearchFlags syntheticFlags = flags; + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_DECL) != 0) { + syntheticFlags &= ~PropertySearchFlags::SEARCH_INSTANCE_DECL; + syntheticFlags |= PropertySearchFlags::SEARCH_INSTANCE_METHOD; + } + if ((flags & PropertySearchFlags::SEARCH_STATIC_DECL) != 0) { + syntheticFlags &= ~PropertySearchFlags::SEARCH_STATIC_DECL; + syntheticFlags |= PropertySearchFlags::SEARCH_STATIC_METHOD; + } + return syntheticFlags; +} + varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(const util::StringView &name, PropertySearchFlags flags) const { std::vector signatures; - varbinder::LocalVariable *functionalInterface = CollectSignaturesForSyntheticType(signatures, name, flags); + // Since both "first match" and "best match" exist at present, overloadDeclarationCall is temporarily used. After + // "best match" removed, this marking needs to be removed. + auto *overloadDeclaration = SearchFieldsDecls(name, UpdateOverloadDeclarationSearchFlags(flags)); + bool overloadDeclarationCall = overloadDeclaration != nullptr; + PropertySearchFlags syntheticFlags = overloadDeclarationCall ? UpdateOverloadDeclarationSearchFlags(flags) : flags; + + varbinder::LocalVariable *functionalInterface = CollectSignaturesForSyntheticType(signatures, name, syntheticFlags); // #22952: the called function *always* returns nullptr ES2PANDA_ASSERT(functionalInterface == nullptr); (void)functionalInterface; @@ -181,8 +264,11 @@ varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(co return nullptr; } - varbinder::LocalVariable *res = allocator_->New(varbinder::VariableFlags::SYNTHETIC | - varbinder::VariableFlags::METHOD); + varbinder::VariableFlags varianceFlag = + overloadDeclarationCall ? varbinder::VariableFlags::SYNTHETIC | varbinder::VariableFlags::METHOD | + varbinder::VariableFlags::OVERLOAD + : varbinder::VariableFlags::SYNTHETIC | varbinder::VariableFlags::METHOD; + varbinder::LocalVariable *res = allocator_->New(varianceFlag); ETSFunctionType *funcType = CreateMethodTypeForProp(name); ES2PANDA_ASSERT(funcType != nullptr); @@ -193,21 +279,50 @@ varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(co res->SetTsType(funcType); funcType->SetVariable(res); + if (overloadDeclarationCall) { + res->Reset(overloadDeclaration->Declaration(), res->Flags()); + } + UpdateDeclarationForGetterSetter(res, funcType, flags); return res; } -ETSFunctionType *ETSObjectType::CreateMethodTypeForProp(const util::StringView &name) const +ETSFunctionType *ETSObjectType::CreateMethodTypeForProp(util::StringView name) const { - ES2PANDA_ASSERT(GetRelation() != nullptr && GetRelation()->GetChecker() != nullptr); - auto *checker = GetRelation()->GetChecker()->AsETSChecker(); - return checker->CreateETSMethodType(name, {{}, Allocator()->Adapter()}); + ES2PANDA_ASSERT(GetRelation() != nullptr); + return GetRelation()->GetChecker()->AsETSChecker()->CreateETSMethodType(name, {{}, Allocator()->Adapter()}); +} + +bool ETSObjectType::ReplaceArgumentInSignature(std::vector &signatures, Signature *sigToInsert, + TypeRelation *relation) const +{ + for (auto *&sigToReplace : signatures) { + if (sigToReplace->ArgCount() != sigToInsert->ArgCount()) { + continue; + } + if (relation->IsSupertypeOf(sigToInsert->Owner(), sigToReplace->Owner()) && + relation->SignatureIsSupertypeOf(sigToInsert, sigToReplace)) { + // Already overridden by a subtype's signature + return true; + } + if (relation->IsSupertypeOf(sigToReplace->Owner(), sigToInsert->Owner()) && + relation->SignatureIsSupertypeOf(sigToReplace, sigToInsert)) { + sigToReplace = sigToInsert; + return true; + } + } + + return false; } -static void AddSignature(std::vector &signatures, PropertySearchFlags flags, ETSChecker *checker, - varbinder::LocalVariable *found) +void ETSObjectType::AddSignatureFromFunction(std::vector &signatures, PropertySearchFlags flags, + ETSChecker *checker, varbinder::LocalVariable *found) const { + if (found == nullptr || !found->TsType()->IsETSFunctionType()) { + return; + } + for (auto *it : found->TsType()->AsETSFunctionType()->CallSignatures()) { if (std::find(signatures.begin(), signatures.end(), it) != signatures.end()) { continue; @@ -215,20 +330,63 @@ static void AddSignature(std::vector &signatures, PropertySearchFla if (((flags & PropertySearchFlags::IGNORE_ABSTRACT) != 0) && it->HasSignatureFlag(SignatureFlags::ABSTRACT)) { continue; } - if (std::any_of(signatures.begin(), signatures.end(), [&it, &checker](auto sig) { - return checker->AreOverrideCompatible(sig, it) && - it->Owner()->HasObjectFlag(ETSObjectFlags::INTERFACE) && - (checker->Relation()->IsSupertypeOf(it->Owner(), sig->Owner()) || - !sig->Owner()->HasObjectFlag(ETSObjectFlags::INTERFACE)); - })) { + if (ReplaceArgumentInSignature(signatures, it, checker->Relation())) { continue; } - // Issue: #18720 - // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) signatures.emplace_back(it); } } +void ETSObjectType::AddSignatureFromOverload(std::vector &signatures, PropertySearchFlags flags, + varbinder::LocalVariable *found) const +{ + if (found == nullptr || !found->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + return; + } + + ES2PANDA_ASSERT(found->Declaration()->Node()->IsOverloadDeclaration()); + auto *overloadDeclaration = found->Declaration()->Node()->AsOverloadDeclaration(); + std::vector methodSignature; + if (overloadDeclaration->Id()->IsErrorPlaceHolder()) { + return; + } + + if (overloadDeclaration->IsConstructorOverloadDeclaration()) { + return AddSignatureFromConstructor(signatures, found); + } + + for (auto *method : overloadDeclaration->OverloadedList()) { + // Identical type cannot be obtained directly, because typeparamter has not been substitute. + methodSignature.clear(); + util::StringView methodName = + method->IsIdentifier() ? method->AsIdentifier()->Name() : method->AsTSQualifiedName()->Right()->Name(); + CollectSignaturesForSyntheticType(methodSignature, methodName, UpdateMethodSearchFlags(flags)); + if (!methodSignature.empty()) { + signatures.emplace_back(methodSignature.front()); + } + } +} + +void ETSObjectType::AddSignatureFromConstructor(std::vector &signatures, + varbinder::LocalVariable *found) const +{ + auto *overloadDeclaration = found->Declaration()->Node()->AsOverloadDeclaration(); + for (auto *method : overloadDeclaration->OverloadedList()) { + util::StringView orderConstructorName = method->AsIdentifier()->Name(); + + // Constructor will lowering to multiple Constructor if have rest parameters or optional parameters. + // Need to modify RestTupleConstructionPhase. + std::vector matches; + std::copy_if( + constructSignatures_.begin(), constructSignatures_.end(), std::back_inserter(matches), + [orderConstructorName](Signature *sig) { return sig->Function()->Id()->Name() == orderConstructorName; }); + + if (!matches.empty()) { + std::copy(matches.begin(), matches.end(), std::back_inserter(signatures)); + } + } +} + varbinder::LocalVariable *ETSObjectType::CollectSignaturesForSyntheticType(std::vector &signatures, const util::StringView &name, PropertySearchFlags flags) const @@ -236,39 +394,65 @@ varbinder::LocalVariable *ETSObjectType::CollectSignaturesForSyntheticType(std:: auto *checker = GetRelation()->GetChecker()->AsETSChecker(); if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { - if (auto *found = GetOwnProperty(name); - found != nullptr && found->TsType()->IsETSFunctionType()) { - AddSignature(signatures, flags, checker, found); - } + auto *found = GetOwnProperty(name); + AddSignatureFromFunction(signatures, flags, checker, found); } if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { - if (auto *found = GetOwnProperty(name); - found != nullptr && found->TsType()->IsETSFunctionType()) { - AddSignature(signatures, flags, checker, found); - } + auto *found = GetOwnProperty(name); + AddSignatureFromFunction(signatures, flags, checker, found); + } + + if ((flags & PropertySearchFlags::SEARCH_STATIC_DECL) != 0) { + auto *found = GetOwnProperty(name); + AddSignatureFromOverload(signatures, flags, found); + } + + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_DECL) != 0) { + auto *found = GetOwnProperty(name); + AddSignatureFromOverload(signatures, flags, found); + } + + if ((flags & PropertySearchFlags::SEARCH_METHOD) == 0 && (flags & PropertySearchFlags::SEARCH_DECL) == 0) { + return nullptr; } if (superType_ != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { superType_->CollectSignaturesForSyntheticType(signatures, name, flags); } - ArenaVector interfaces(Allocator()->Adapter()); - checker->GetInterfacesOfClass(const_cast(this), interfaces); - - for (auto *const &interface : interfaces) { - if (interface != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) && - !this->IsPartial()) { // NOTE: issue 24548 - if (auto *found = - interface->GetProperty(name, flags | PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION); - found != nullptr && found->TsType()->IsETSFunctionType()) { - AddSignature(signatures, flags, checker, found); - } + if ((flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) { + for (auto *interface : Interfaces()) { + interface->CollectSignaturesForSyntheticType(signatures, name, flags); } } + return nullptr; } +void ETSObjectType::ForEachAllOwnProperties(const PropertyTraverser &cb) const +{ + EnsurePropertiesInstantiated(); + for (size_t i = 0; i < static_cast(PropertyType::COUNT); ++i) { + PropertyMap &map = properties_[i]; + for (const auto &[_, prop] : map) { + (void)_; + cb(prop); + } + } +} + +void ETSObjectType::ForEachAllNonOwnProperties(const PropertyTraverser &cb) const +{ + if (superType_ != nullptr) { + superType_->Iterate(cb); + } + + for (const auto *interface : interfaces_) { + interface->Iterate(cb); + } +} + std::vector ETSObjectType::GetAllProperties() const { std::vector allProperties; @@ -305,16 +489,42 @@ std::vector ETSObjectType::GetAllProperties() const return allProperties; } +std::vector ETSObjectType::Overloads() const +{ + std::vector methods; + for (const auto &[_, prop] : InstanceMethods()) { + (void)_; + if (prop->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + methods.push_back(prop); + } + } + + for (const auto &[_, prop] : StaticMethods()) { + (void)_; + if (prop->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + methods.push_back(prop); + } + } + + return methods; +} + std::vector ETSObjectType::Methods() const { std::vector methods; for (const auto &[_, prop] : InstanceMethods()) { (void)_; + if (prop->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + continue; + } methods.push_back(prop); } for (const auto &[_, prop] : StaticMethods()) { (void)_; + if (prop->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + continue; + } methods.push_back(prop); } @@ -349,35 +559,25 @@ std::vector ETSObjectType::ForeignProperties() ownInstanceProps.reserve(properties_.size()); ownStaticProps.reserve(properties_.size()); - for (const auto *prop : GetAllProperties()) { + ForEachAllOwnProperties([&](const varbinder::LocalVariable *prop) { if (prop->HasFlag(varbinder::VariableFlags::STATIC)) { ownStaticProps.insert(prop->Name()); } else { ownInstanceProps.insert(prop->Name()); } - } - - std::map allInstanceProps {}; - std::map allStaticProps {}; - Iterate([&allInstanceProps, &allStaticProps](const varbinder::LocalVariable *var) { + }); + ForEachAllNonOwnProperties([&](const varbinder::LocalVariable *var) { if (var->HasFlag(varbinder::VariableFlags::STATIC)) { - allStaticProps.emplace(var->Name(), var); + if (ownStaticProps.find(var->Name()) == ownStaticProps.end()) { + foreignProps.push_back(var); + } } else { - allInstanceProps.emplace(var->Name(), var); + if (ownInstanceProps.find(var->Name()) == ownInstanceProps.end()) { + foreignProps.push_back(var); + } } }); - for (const auto &[name, var] : allInstanceProps) { - if (ownInstanceProps.find(name) == ownInstanceProps.end()) { - foreignProps.push_back(var); - } - } - for (const auto &[name, var] : allStaticProps) { - if (ownStaticProps.find(name) == ownStaticProps.end()) { - foreignProps.push_back(var); - } - } - return foreignProps; } @@ -424,7 +624,8 @@ void ETSObjectType::SubstitutePartialTypes(TypeRelation *relation, Type *other) ES2PANDA_ASSERT(IsPartial()); if ((baseType_->IsGeneric() || baseType_->IsETSTypeParameter()) && effectiveSubstitution_ != nullptr) { - if (auto *newBaseType = baseType_->Substitute(relation, effectiveSubstitution_); + auto subst = ETSChecker::ArenaSubstitutionToSubstitution(effectiveSubstitution_); + if (auto *newBaseType = baseType_->Substitute(relation, &subst); newBaseType->IsETSObjectType() && !relation->IsIdenticalTo(newBaseType, baseType_)) { baseType_ = newBaseType->AsETSObjectType(); } @@ -434,7 +635,8 @@ void ETSObjectType::SubstitutePartialTypes(TypeRelation *relation, Type *other) auto *otherPartial = other->AsETSObjectType(); if ((otherPartial->baseType_->IsGeneric() || otherPartial->baseType_->IsETSTypeParameter()) && otherPartial->effectiveSubstitution_ != nullptr) { - if (auto *newBaseType = otherPartial->baseType_->Substitute(relation, otherPartial->effectiveSubstitution_); + auto subst = ETSChecker::ArenaSubstitutionToSubstitution(otherPartial->effectiveSubstitution_); + if (auto *newBaseType = otherPartial->baseType_->Substitute(relation, &subst); newBaseType->IsETSObjectType() && !relation->IsIdenticalTo(newBaseType, otherPartial->baseType_)) { otherPartial->baseType_ = newBaseType->AsETSObjectType(); } @@ -446,15 +648,14 @@ void ETSObjectType::SubstitutePartialTypes(TypeRelation *relation, Type *other) void ETSObjectType::IdenticalUptoTypeArguments(TypeRelation *relation, Type *other) { relation->Result(false); + if (!other->IsETSObjectType() || !CheckIdenticalFlags(other->AsETSObjectType())) { + return; + } if (IsPartial()) { SubstitutePartialTypes(relation, other); } - if (!other->IsETSObjectType() || !CheckIdenticalFlags(other->AsETSObjectType())) { - return; - } - // NOTE: (DZ) only both Partial types can be compatible. if (static_cast(static_cast(IsPartial()) ^ static_cast(other->AsETSObjectType()->IsPartial()))) { @@ -463,7 +664,7 @@ void ETSObjectType::IdenticalUptoTypeArguments(TypeRelation *relation, Type *oth auto *thisBase = GetOriginalBaseType(); auto *otherBase = other->AsETSObjectType()->GetOriginalBaseType(); - if (thisBase->Variable() != otherBase->Variable()) { + if (thisBase->Variable()->Declaration()->Node() != otherBase->Variable()->Declaration()->Node()) { return; } @@ -516,7 +717,6 @@ void ETSObjectType::Identical(TypeRelation *relation, Type *other) bool ETSObjectType::CheckIdenticalFlags(ETSObjectType *other) const { constexpr auto FLAGS_TO_REMOVE = ETSObjectFlags::INCOMPLETE_INSTANTIATION | - ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS | ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY | ETSObjectFlags::EXTENSION_FUNCTION; auto cleanedTargetFlags = other->ObjectFlags(); @@ -536,7 +736,7 @@ bool ETSObjectType::AssignmentSource(TypeRelation *const relation, [[maybe_unuse bool ETSObjectType::IsBoxedPrimitive() const { - if (this->IsETSDynamicType()) { + if (this->IsETSEnumType()) { return false; } @@ -552,12 +752,12 @@ void ETSObjectType::AssignmentTarget(TypeRelation *const relation, Type *source) ETSFunctionType *ETSObjectType::GetFunctionalInterfaceInvokeType() const { ES2PANDA_ASSERT(HasObjectFlag(ETSObjectFlags::FUNCTIONAL)); + auto checker = GetRelation()->GetChecker()->AsETSChecker(); // NOTE(vpukhov): this is still better than to retain any "functional" state in ETSObjectType - auto [foundArity, hasRest] = [this]() { - auto checker = GetRelation()->GetChecker()->AsETSChecker(); + auto [foundArity, hasRest] = [this, checker]() { auto baseType = GetConstOriginalBaseType(); - for (size_t arity = 0; arity < checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold(); ++arity) { + for (size_t arity = 0; arity <= checker->GlobalBuiltinFunctionTypeVariadicThreshold(); ++arity) { if (auto itf = checker->GlobalBuiltinFunctionType(arity, false); itf == baseType) { return std::make_pair(arity, false); } @@ -568,14 +768,15 @@ ETSFunctionType *ETSObjectType::GetFunctionalInterfaceInvokeType() const ES2PANDA_UNREACHABLE(); }(); - std::string invokeName = FunctionalInterfaceInvokeName(foundArity, hasRest); - auto *invoke = GetOwnProperty(util::StringView(invokeName)); + std::string invokeName = checker->FunctionalInterfaceInvokeName(foundArity, hasRest); + auto *invoke = GetProperty(util::StringView(invokeName), + PropertySearchFlags::SEARCH_INSTANCE_METHOD | PropertySearchFlags::SEARCH_IN_INTERFACES); ES2PANDA_ASSERT(invoke != nullptr && invoke->TsType() != nullptr && invoke->TsType()->IsETSFunctionType()); return invoke->TsType()->AsETSFunctionType(); } -bool ETSObjectType::CastWideningNarrowing(TypeRelation *const relation, Type *const target, TypeFlag unboxFlags, - TypeFlag wideningFlags, TypeFlag narrowingFlags) +bool ETSObjectType::CastWidening(TypeRelation *const relation, Type *const target, TypeFlag unboxFlags, + TypeFlag wideningFlags) { if (target->HasTypeFlag(unboxFlags)) { conversion::Unboxing(relation, this); @@ -585,10 +786,6 @@ bool ETSObjectType::CastWideningNarrowing(TypeRelation *const relation, Type *co conversion::UnboxingWideningPrimitive(relation, this, target); return true; } - if (target->HasTypeFlag(narrowingFlags)) { - conversion::UnboxingNarrowingPrimitive(relation, this, target); - return true; - } return false; } @@ -603,7 +800,7 @@ bool ETSObjectType::TryCastByte(TypeRelation *const relation, Type *const target return true; } if (target->HasTypeFlag(TypeFlag::CHAR)) { - conversion::UnboxingWideningNarrowingPrimitive(relation, this, target); + conversion::UnboxingWideningPrimitive(relation, this, target); return true; } return false; @@ -615,25 +812,21 @@ bool ETSObjectType::TryCastIntegral(TypeRelation *const relation, Type *const ta return true; } if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_SHORT) && - CastWideningNarrowing(relation, target, TypeFlag::SHORT, - TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE, - TypeFlag::BYTE | TypeFlag::CHAR)) { + CastWidening(relation, target, TypeFlag::SHORT, + TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { return true; } if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR) && - CastWideningNarrowing(relation, target, TypeFlag::CHAR, - TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE, - TypeFlag::BYTE | TypeFlag::SHORT)) { + CastWidening(relation, target, TypeFlag::CHAR, + TypeFlag::SHORT | TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { return true; } if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_INT) && - CastWideningNarrowing(relation, target, TypeFlag::INT, TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE, - TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR)) { + CastWidening(relation, target, TypeFlag::INT, TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { return true; } if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG) && - CastWideningNarrowing(relation, target, TypeFlag::LONG, TypeFlag::FLOAT | TypeFlag::DOUBLE, - TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT)) { + CastWidening(relation, target, TypeFlag::LONG, TypeFlag::FLOAT | TypeFlag::DOUBLE)) { return true; } return false; @@ -642,14 +835,11 @@ bool ETSObjectType::TryCastIntegral(TypeRelation *const relation, Type *const ta bool ETSObjectType::TryCastFloating(TypeRelation *const relation, Type *const target) { if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_FLOAT) && - CastWideningNarrowing(relation, target, TypeFlag::FLOAT, TypeFlag::DOUBLE, - TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG)) { + CastWidening(relation, target, TypeFlag::FLOAT, TypeFlag::DOUBLE)) { return true; } - if (auto narrowingFlags = - TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT; - this->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE) && - CastWideningNarrowing(relation, target, TypeFlag::DOUBLE, TypeFlag::NONE, narrowingFlags)) { + if (this->HasObjectFlag(ETSObjectFlags::BUILTIN_DOUBLE) && + CastWidening(relation, target, TypeFlag::DOUBLE, TypeFlag::NONE)) { return true; } return false; @@ -671,6 +861,12 @@ bool ETSObjectType::TryCastUnboxable(TypeRelation *const relation, Type *const t conversion::WideningReference(relation, this, target->AsETSObjectType()); return true; } + + if (target->IsETSEnumType()) { + auto unboxedThis = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(this); + return relation->IsCastableTo(unboxedThis, target); + } + conversion::Forbidden(relation); return true; } @@ -696,10 +892,6 @@ bool ETSObjectType::CastNumericObject(TypeRelation *const relation, Type *const if (this->IsETSUnboxableObject()) { return TryCastUnboxable(relation, target); } - if (target->IsETSPrimitiveType()) { - conversion::NarrowingReferenceUnboxing(relation, this, target); - return true; - } return false; } @@ -710,6 +902,11 @@ void ETSObjectType::Cast(TypeRelation *const relation, Type *const target) return; } + if (target->IsGradualType()) { + relation->Result(true); + return; + } + if (CastNumericObject(relation, target)) { return; } @@ -792,6 +989,14 @@ void ETSObjectType::IsSupertypeOf(TypeRelation *relation, Type *source) void ETSObjectType::IsSubtypeOf(TypeRelation *relation, Type *target) { + if (target->IsETSObjectType()) { + auto &transitives = transitiveSupertypes_; + if (transitives.find(target->AsETSObjectType()->GetOriginalBaseType()) == transitives.end()) { + relation->Result(false); + return; + } + } + if (auto super = SuperType(); super != nullptr) { if (relation->IsSupertypeOf(target, super)) { return; @@ -854,6 +1059,7 @@ void ETSObjectType::IsGenericSupertypeOf(TypeRelation *relation, ETSObjectType * Type *ETSObjectType::AsSuper(Checker *checker, varbinder::Variable *sourceVar) { + checker = GetETSChecker(); if (sourceVar == nullptr) { return nullptr; } @@ -918,9 +1124,10 @@ varbinder::LocalVariable *ETSObjectType::CopyProperty(varbinder::LocalVariable * return copiedProp; } -Type *ETSObjectType::Instantiate(ArenaAllocator *const allocator, TypeRelation *const relation, +Type *ETSObjectType::Instantiate(ArenaAllocator *const allocator, TypeRelation *relation, GlobalTypesHolder *const globalTypes) { + relation = relation_; auto *const checker = relation->GetChecker()->AsETSChecker(); std::lock_guard guard {*checker->Mutex()}; auto *const base = GetOriginalBaseType(); @@ -935,8 +1142,7 @@ Type *ETSObjectType::Instantiate(ArenaAllocator *const allocator, TypeRelation * ES2PANDA_ASSERT(copiedType->name_ == name_); ES2PANDA_ASSERT(copiedType != nullptr); copiedType->typeFlags_ = typeFlags_; - copiedType->RemoveObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS | - ETSObjectFlags::INCOMPLETE_INSTANTIATION | ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY); + copiedType->RemoveObjectFlag(ETSObjectFlags::INCOMPLETE_INSTANTIATION | ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY); copiedType->SetVariable(variable_); copiedType->SetSuperType(superType_); @@ -976,7 +1182,7 @@ static varbinder::LocalVariable *CopyPropertyWithTypeArguments(varbinder::LocalV auto *const checker = relation->GetChecker()->AsETSChecker(); auto *const varType = ETSChecker::IsVariableGetterSetter(prop) ? prop->TsType() : checker->GetTypeOfVariable(prop); auto *const copiedPropType = SubstituteVariableType(relation, substitution, varType); - auto *const copiedProp = prop->Copy(checker->ProgramAllocator(), prop->Declaration()); + auto *const copiedProp = prop->Copy(checker->Allocator(), prop->Declaration()); // NOTE: some situation copiedPropType we get here are types cached in Checker, // uncontrolled SetVariable will pollute the cache. if (copiedPropType->Variable() == prop || copiedPropType->Variable() == nullptr) { @@ -1015,13 +1221,13 @@ bool ETSObjectType::SubstituteTypeArgs(TypeRelation *const relation, ArenaVector return anyChange; } -static Substitution *ComputeEffectiveSubstitution(TypeRelation *const relation, - const ArenaVector &baseTypeParams, - ArenaVector &newTypeArgs) +static ArenaSubstitution *ComputeEffectiveSubstitution(TypeRelation *const relation, + const ArenaVector &baseTypeParams, + ArenaVector &newTypeArgs) { ES2PANDA_ASSERT(baseTypeParams.size() == newTypeArgs.size()); auto *const checker = relation->GetChecker()->AsETSChecker(); - auto *effectiveSubstitution = checker->NewSubstitution(); + auto *effectiveSubstitution = checker->NewArenaSubstitution(); for (size_t ix = 0; ix < baseTypeParams.size(); ix++) { checker->EmplaceSubstituted(effectiveSubstitution, baseTypeParams[ix]->AsETSTypeParameter(), newTypeArgs[ix]); @@ -1035,22 +1241,28 @@ void ETSObjectType::SetCopiedTypeProperties(TypeRelation *const relation, ETSObj { ES2PANDA_ASSERT(copiedType != nullptr); copiedType->typeFlags_ = typeFlags_; - copiedType->RemoveObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS | - ETSObjectFlags::INCOMPLETE_INSTANTIATION | ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY); + copiedType->RemoveObjectFlag(ETSObjectFlags::INCOMPLETE_INSTANTIATION | ETSObjectFlags::CHECKED_INVOKE_LEGITIMACY); copiedType->SetVariable(variable_); - copiedType->SetBaseType(base); + + // #25295 Need to do some refactor on baseType for partial + if (IsPartial() && HasObjectFlag(ETSObjectFlags::INTERFACE)) { + copiedType->SetBaseType(this); + } else { + copiedType->SetBaseType(base); + } auto const &baseTypeParams = base->TypeArguments(); copiedType->effectiveSubstitution_ = ComputeEffectiveSubstitution(relation, baseTypeParams, newTypeArgs); copiedType->SetTypeArguments(std::move(newTypeArgs)); + ES2PANDA_ASSERT(relation); copiedType->relation_ = relation; } -void ETSObjectType::UpdateTypeProperty(checker::ETSChecker *checker, varbinder::LocalVariable *const prop, - PropertyType fieldType, PropertyProcesser const &func) +void ETSObjectType::UpdateTypeProperty(varbinder::LocalVariable *const prop, PropertyType fieldType, + PropertyProcesser const &func) { - auto const propType = prop->Declaration()->Node()->Check(checker); + auto const propType = prop->Declaration()->Node()->Check(GetETSChecker()); auto *const propCopy = func(prop, propType); if (fieldType == PropertyType::INSTANCE_FIELD) { @@ -1062,36 +1274,97 @@ void ETSObjectType::UpdateTypeProperty(checker::ETSChecker *checker, varbinder:: } } -void ETSObjectType::UpdateTypeProperties(checker::ETSChecker *checker, PropertyProcesser const &func) +void ETSObjectType::UpdateTypeProperties(PropertyProcesser const &func) { AddTypeFlag(TypeFlag::READONLY); for (auto const &prop : InstanceFields()) { - UpdateTypeProperty(checker, prop.second, PropertyType::INSTANCE_FIELD, func); + UpdateTypeProperty(prop.second, PropertyType::INSTANCE_FIELD, func); } for (auto const &prop : StaticFields()) { - UpdateTypeProperty(checker, prop.second, PropertyType::STATIC_FIELD, func); + UpdateTypeProperty(prop.second, PropertyType::STATIC_FIELD, func); } if (SuperType() != nullptr) { - auto *const superProp = SuperType()->Clone(checker)->AsETSObjectType(); - superProp->UpdateTypeProperties(checker, func); + auto *const superProp = + SuperType() + ->Instantiate(allocator_, relation_, relation_->GetChecker()->GetGlobalTypesHolder()) + ->AsETSObjectType(); + superProp->UpdateTypeProperties(func); SetSuperType(superProp); } } +static util::StringView GetHashFromSubstitution(const Substitution *substitution, const bool extensionFuncFlag, + ArenaAllocator *allocator) +{ + std::vector fields; + for (auto [k, v] : *substitution) { + std::stringstream ss; + k->ToString(ss, true); + ss << ":"; + v->ToString(ss, true); + // NOTE (mmartin): change bare address to something more appropriate unique representation + ss << ":" << k << ":" << v; + fields.push_back(ss.str()); + } + std::sort(fields.begin(), fields.end()); + + std::stringstream ss; + for (auto &fstr : fields) { + ss << fstr; + ss << ";"; + } + + if (extensionFuncFlag) { + ss << "extensionFunctionType;"; + } + return util::UString(ss.str(), allocator).View(); +} + +static std::pair GetObjectTypeDeclNames(ir::AstNode *node) +{ + if (node->IsClassDefinition()) { + return {node->AsClassDefinition()->Ident()->Name(), node->AsClassDefinition()->InternalName()}; + } + if (node->IsTSInterfaceDeclaration()) { + return {node->AsTSInterfaceDeclaration()->Id()->Name(), node->AsTSInterfaceDeclaration()->InternalName()}; + } + return {node->AsAnnotationDeclaration()->GetBaseName()->Name(), node->AsAnnotationDeclaration()->InternalName()}; +} + +ETSObjectType *ETSObjectType::CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags) +{ + auto const [name, internalName] = GetObjectTypeDeclNames(declNode); + + if (declNode->IsClassDefinition() && (declNode->AsClassDefinition()->IsEnumTransformed())) { + if (declNode->AsClassDefinition()->IsIntEnumTransformed()) { + return Allocator()->New(Allocator(), name, internalName, declNode, GetRelation()); + } + ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); + return Allocator()->New(Allocator(), name, internalName, declNode, GetRelation()); + } + if (internalName == compiler::Signatures::BUILTIN_ARRAY) { + return Allocator()->New(Allocator(), name, + std::make_tuple(declNode, flags, GetRelation())); + } + + return Allocator()->New(Allocator(), name, internalName, + std::make_tuple(declNode, flags, GetRelation())); +} + // #22951: remove isExtensionFunctionType flag ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitution *substitution, bool cache, bool isExtensionFunctionType) { + relation = relation_; if (substitution == nullptr || substitution->empty()) { return this; } - auto *const checker = relation->GetChecker()->AsETSChecker(); auto *base = GetOriginalBaseType(); - ArenaVector newTypeArgs {Allocator()->Adapter()}; + ArenaVector newTypeArgs {allocator_->Adapter()}; const bool anyChange = SubstituteTypeArgs(relation, newTypeArgs, substitution); // Lambda types can capture type params in their bodies, normal classes cannot. // NOTE: gogabr. determine precise conditions where we do not need to copy. @@ -1100,7 +1373,7 @@ ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitut return this; } - const util::StringView hash = checker->GetHashFromSubstitution(substitution, isExtensionFunctionType); + const util::StringView hash = GetHashFromSubstitution(substitution, isExtensionFunctionType, allocator_); if (cache) { if (auto *inst = GetInstantiatedType(hash); inst != nullptr) { return inst; @@ -1112,14 +1385,15 @@ ETSObjectType *ETSObjectType::Substitute(TypeRelation *relation, const Substitut } relation->IncreaseTypeRecursionCount(base); - auto *const copiedType = checker->CreateETSObjectType(declNode_, flags_); + auto *const copiedType = CreateETSObjectType(declNode_, flags_); SetCopiedTypeProperties(relation, copiedType, std::move(newTypeArgs), base); if (isExtensionFunctionType) { copiedType->AddObjectFlag(checker::ETSObjectFlags::EXTENSION_FUNCTION); } if (cache) { - GetInstantiationMap().try_emplace(hash, copiedType); + ES2PANDA_ASSERT(copiedType->GetRelation()); + InsertInstantiationMap(hash, copiedType); } if (superType_ != nullptr) { @@ -1147,17 +1421,41 @@ ETSObjectType *ETSObjectType::SubstituteArguments(TypeRelation *relation, ArenaV } auto *checker = relation->GetChecker()->AsETSChecker(); - auto *substitution = checker->NewSubstitution(); + auto substitution = Substitution {}; ES2PANDA_ASSERT(baseType_ == nullptr); - ES2PANDA_ASSERT(substitution != nullptr); ES2PANDA_ASSERT(typeArguments_.size() == arguments.size()); for (size_t ix = 0; ix < typeArguments_.size(); ix++) { - substitution->emplace(typeArguments_[ix]->AsETSTypeParameter(), checker->MaybeBoxType(arguments[ix])); + substitution.emplace(typeArguments_[ix]->AsETSTypeParameter(), + checker->MaybeBoxType(arguments[ix]->MaybeBaseTypeOfGradualType())); } - return Substitute(relation, substitution); + return Substitute(relation, &substitution); +} + +ETSChecker *ETSObjectType::GetETSChecker() +{ + return relation_->GetChecker()->AsETSChecker(); +} + +void ETSObjectType::CheckAndInstantiateProperties() const +{ + auto *checker = relation_->GetChecker()->AsETSChecker(); + auto *declNode = GetDeclNode(); + if (HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) && declNode == nullptr) { + declNode = SuperType()->GetDeclNode(); + } + if (declNode == nullptr) { + ES2PANDA_ASSERT(checker->IsAnyError()); + return; + } + + TypeStackElement tse {checker, this, {{diagnostic::CYCLIC_INHERITANCE, {this->Name()}}}, declNode->Start()}; + if (tse.HasTypeError()) { + return; + } + InstantiateProperties(); } void ETSObjectType::InstantiateProperties() const @@ -1173,44 +1471,48 @@ void ETSObjectType::InstantiateProperties() const ES2PANDA_ASSERT(!propertiesInstantiated_); declNode_->Check(checker); + auto subst = effectiveSubstitution_ == nullptr + ? Substitution {} + : ETSChecker::ArenaSubstitutionToSubstitution(effectiveSubstitution_); + for (auto *const it : baseType_->ConstructSignatures()) { - auto *newSig = it->Substitute(relation_, effectiveSubstitution_); + auto *newSig = it->Substitute(relation_, &subst); constructSignatures_.push_back(newSig); } for (auto const &[_, prop] : baseType_->InstanceFields()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::INSTANCE_FIELD)].emplace(prop->Name(), copiedProp); } for (auto const &[_, prop] : baseType_->StaticFields()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::STATIC_FIELD)].emplace(prop->Name(), copiedProp); } for (auto const &[_, prop] : baseType_->InstanceMethods()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::INSTANCE_METHOD)].emplace(prop->Name(), copiedProp); } for (auto const &[_, prop] : baseType_->StaticMethods()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::STATIC_METHOD)].emplace(prop->Name(), copiedProp); } for (auto const &[_, prop] : baseType_->InstanceDecls()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::INSTANCE_DECL)].emplace(prop->Name(), copiedProp); } for (auto const &[_, prop] : baseType_->StaticDecls()) { (void)_; - auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, effectiveSubstitution_); + auto *copiedProp = CopyPropertyWithTypeArguments(prop, relation_, &subst); properties_[static_cast(PropertyType::STATIC_DECL)].emplace(prop->Name(), copiedProp); } } @@ -1379,4 +1681,40 @@ void ETSObjectType::CheckVarianceRecursively(TypeRelation *relation, VarianceFla } } +ETSObjectType *ETSObjectType::GetInstantiatedType(util::StringView hash) +{ + auto &instantiationMap = + compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->GetObjectInstantiationMap(); + auto found = instantiationMap.find(this); + if (found == instantiationMap.end()) { + return nullptr; + } + + auto found2 = instantiationMap.at(this).find(hash); + if (found2 == instantiationMap.at(this).end()) { + return nullptr; + } + + return found2->second; +} + +void ETSObjectType::InsertInstantiationMap(util::StringView key, ETSObjectType *value) +{ + auto &instantiationMap = + compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->GetObjectInstantiationMap(); + if (instantiationMap.find(this) == instantiationMap.end()) { + ArenaUnorderedMap instantiation( + compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->Allocator()->Adapter()); + instantiation.emplace(key, value); + instantiationMap.emplace(this, std::move(instantiation)); + } + compiler::GetPhaseManager() + ->Context() + ->GetChecker() + ->AsETSChecker() + ->GetObjectInstantiationMap() + .at(this) + .try_emplace(key, value); +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsObjectType.h b/ets2panda/checker/types/ets/etsObjectType.h index 19ae6c8039f4fec5f1fcf526de95ba0bf95aae26..a86f126b974a89484f4a1ec9f5d7795a66d6a498 100644 --- a/ets2panda/checker/types/ets/etsObjectType.h +++ b/ets2panda/checker/types/ets/etsObjectType.h @@ -16,6 +16,10 @@ #ifndef ES2PANDA_COMPILER_CHECKER_TYPES_ETS_OBJECT_TYPE_H #define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_OBJECT_TYPE_H +#include +#include + +#include "checker/checker.h" #include "checker/types/type.h" #include "checker/types/ets/etsObjectTypeConstants.h" #include "checker/types/signature.h" @@ -37,14 +41,14 @@ public: using PropertyTraverser = std::function; using PropertyHolder = std::array(PropertyType::COUNT)>; - explicit ETSObjectType(ArenaAllocator *allocator, util::StringView name, util::StringView internalName, + explicit ETSObjectType(ThreadSafeArenaAllocator *allocator, util::StringView name, util::StringView internalName, ir::AstNode *declNode, ETSObjectFlags flags) : ETSObjectType(allocator, name, internalName, std::make_tuple(declNode, flags, nullptr), std::make_index_sequence(PropertyType::COUNT)> {}) { } - explicit ETSObjectType(ArenaAllocator *allocator, util::StringView name, util::StringView internalName, + explicit ETSObjectType(ThreadSafeArenaAllocator *allocator, util::StringView name, util::StringView internalName, std::tuple info) : ETSObjectType(allocator, name, internalName, info, std::make_index_sequence(PropertyType::COUNT)> {}) @@ -63,17 +67,10 @@ public: propertiesInstantiated_ = true; } - void AddInterface(ETSObjectType *interfaceType) - { - if (std::find(interfaces_.begin(), interfaces_.end(), interfaceType) == interfaces_.end()) { - interfaces_.push_back(interfaceType); - } - } + void AddInterface(ETSObjectType *interfaceType); + void SetSuperType(ETSObjectType *super); - void SetSuperType(ETSObjectType *super) - { - superType_ = super; - } + ETSChecker *GetETSChecker(); void SetTypeArguments(ArenaVector &&typeArgs) { @@ -92,6 +89,7 @@ public: void SetRelation(TypeRelation *relation) { + ES2PANDA_ASSERT(relation); relation_ = relation; } @@ -158,7 +156,7 @@ public: return interfaces_; } - ArenaVector &Interfaces() + const ArenaVector &Interfaces() { return interfaces_; } @@ -234,12 +232,12 @@ public: bool IsDescendantOf(const ETSObjectType *ascendant) const; - const util::StringView &Name() const + util::StringView Name() const { return name_; } - const util::StringView &AssemblerName() const + util::StringView AssemblerName() const { return internalName_; } @@ -264,6 +262,11 @@ public: return (flags_ & flag) != 0; } + bool IsInterface() const + { + return HasObjectFlag(ETSObjectFlags::INTERFACE); + } + bool IsETSStringLiteralType() const { return superType_ != nullptr && superType_->IsETSObjectType() && @@ -282,15 +285,7 @@ public: return static_cast(flags_ & ETSObjectFlags::UNBOXABLE_TYPE); } - ETSObjectType *GetInstantiatedType(util::StringView hash) - { - auto found = instantiationMap_.find(hash); - if (found != instantiationMap_.end()) { - return found->second; - } - - return nullptr; - } + ETSObjectType *GetInstantiatedType(util::StringView hash); varbinder::Scope *GetTypeArgumentScope() const { @@ -301,13 +296,10 @@ public: return typeParams->Scope(); } - InstantiationMap &GetInstantiationMap() - { - return instantiationMap_; - } + void InsertInstantiationMap(const util::StringView key, ETSObjectType *value); template - varbinder::LocalVariable *GetOwnProperty(const util::StringView &name) const + varbinder::LocalVariable *GetOwnProperty(const util::StringView name) const { EnsurePropertiesInstantiated(); auto found = properties_[static_cast(TYPE)].find(name); @@ -355,19 +347,29 @@ public: } std::vector ForeignProperties() const; - varbinder::LocalVariable *GetProperty(const util::StringView &name, PropertySearchFlags flags) const; + varbinder::LocalVariable *GetProperty(util::StringView name, PropertySearchFlags flags) const; std::vector GetAllProperties() const; + void ForEachAllOwnProperties(const PropertyTraverser &cb) const; + void ForEachAllNonOwnProperties(const PropertyTraverser &cb) const; varbinder::LocalVariable *CopyProperty(varbinder::LocalVariable *prop, ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes); std::vector Methods() const; std::vector Fields() const; + std::vector Overloads() const; varbinder::LocalVariable *CreateSyntheticVarFromEverySignature(const util::StringView &name, PropertySearchFlags flags) const; varbinder::LocalVariable *CollectSignaturesForSyntheticType(std::vector &signatures, const util::StringView &name, PropertySearchFlags flags) const; + void AddSignatureFromFunction(std::vector &signatures, PropertySearchFlags flags, ETSChecker *checker, + varbinder::LocalVariable *found) const; + void AddSignatureFromOverload(std::vector &signatures, PropertySearchFlags flags, + varbinder::LocalVariable *found) const; + void AddSignatureFromConstructor(std::vector &signatures, varbinder::LocalVariable *found) const; + bool ReplaceArgumentInSignature(std::vector &signatures, Signature *sigToInsert, + TypeRelation *relation) const; bool CheckIdenticalFlags(ETSObjectType *other) const; - + ETSObjectType *CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags); void Iterate(const PropertyTraverser &cb) const; void ToString(std::stringstream &ss, bool precise) const override; void Identical(TypeRelation *relation, Type *other) override; @@ -375,7 +377,7 @@ public: void AssignmentTarget(TypeRelation *relation, Type *source) override; bool IsBoxedPrimitive() const; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; - void UpdateTypeProperties(checker::ETSChecker *checker, PropertyProcesser const &func); + void UpdateTypeProperties(PropertyProcesser const &func); ETSObjectType *Substitute(TypeRelation *relation, const Substitution *substitution) override; ETSObjectType *Substitute(TypeRelation *relation, const Substitution *substitution, bool cache, bool isExtensionFunctionType = false); @@ -398,16 +400,11 @@ public: const ArenaVector &ReExports() const; bool IsSameBasedGeneric(TypeRelation *relation, Type const *other) const; - ArenaAllocator *Allocator() const + ThreadSafeArenaAllocator *Allocator() const { return allocator_; } - std::tuple ResolveConditionExpr() const override - { - return {false, false}; - } - [[nodiscard]] static std::uint32_t GetPrecedence(checker::ETSChecker *checker, ETSObjectType const *type) noexcept; bool IsPropertiesInstantiated() const @@ -416,11 +413,11 @@ public: } protected: - virtual ETSFunctionType *CreateMethodTypeForProp(const util::StringView &name) const; + virtual ETSFunctionType *CreateMethodTypeForProp(util::StringView name) const; private: template - explicit ETSObjectType(ArenaAllocator *allocator, util::StringView name, util::StringView assemblerName, + explicit ETSObjectType(ThreadSafeArenaAllocator *allocator, util::StringView name, util::StringView assemblerName, std::tuple info, [[maybe_unused]] std::index_sequence s) : Type(TypeFlag::ETS_OBJECT), @@ -432,8 +429,8 @@ private: reExports_(allocator->Adapter()), reExportAlias_(allocator->Adapter()), flags_(std::get(info)), - instantiationMap_(allocator->Adapter()), typeArguments_(allocator->Adapter()), + transitiveSupertypes_(allocator->Adapter()), relation_(std::get(info)), constructSignatures_(allocator->Adapter()), properties_ {(void(IS), PropertyMap {allocator->Adapter()})...} @@ -442,27 +439,22 @@ private: /* Properties and construct signatures are instantiated lazily. */ void InstantiateProperties() const; + void CheckAndInstantiateProperties() const; void EnsurePropertiesInstantiated() const { if (!propertiesInstantiated_) { - if (isInstantiatingProperties_) { - return; // avoid infinite recursive call - } - isInstantiatingProperties_ = true; - InstantiateProperties(); - isInstantiatingProperties_ = false; + CheckAndInstantiateProperties(); propertiesInstantiated_ = true; } } - bool CastWideningNarrowing(TypeRelation *relation, Type *target, TypeFlag unboxFlags, TypeFlag wideningFlags, - TypeFlag narrowingFlags); + bool CastWidening(TypeRelation *relation, Type *target, TypeFlag unboxFlags, TypeFlag wideningFlags); void IdenticalUptoTypeArguments(TypeRelation *relation, Type *other); void SubstitutePartialTypes(TypeRelation *relation, Type *other); void IsGenericSupertypeOf(TypeRelation *relation, ETSObjectType *source); - void UpdateTypeProperty(checker::ETSChecker *checker, varbinder::LocalVariable *const prop, PropertyType fieldType, + void UpdateTypeProperty(varbinder::LocalVariable *const prop, PropertyType fieldType, PropertyProcesser const &func); - varbinder::LocalVariable *SearchFieldsDecls(const util::StringView &name, PropertySearchFlags flags) const; + varbinder::LocalVariable *SearchFieldsDecls(util::StringView name, PropertySearchFlags flags) const; void SetCopiedTypeProperties(TypeRelation *relation, ETSObjectType *copiedType, ArenaVector &&newTypeArgs, ETSObjectType *base); @@ -473,9 +465,11 @@ private: bool TryCastFloating(TypeRelation *const relation, Type *const target); bool TryCastUnboxable(TypeRelation *const relation, Type *const target); + void CacheSupertypeTransitive(ETSObjectType *type); + ir::TSTypeParameterDeclaration *GetTypeParams() const; - ArenaAllocator *const allocator_; + ThreadSafeArenaAllocator *const allocator_; util::StringView const name_; util::StringView const internalName_; ir::AstNode *const declNode_; @@ -483,16 +477,17 @@ private: ArenaVector reExports_; ArenaMap reExportAlias_; ETSObjectFlags flags_; - InstantiationMap instantiationMap_; ArenaVector typeArguments_; ETSObjectType *superType_ {}; ETSObjectType *enclosingType_ {}; ETSObjectType *baseType_ {}; + // optimized subtyping + ArenaSet transitiveSupertypes_; + // for lazy properties instantiation TypeRelation *relation_ = nullptr; - const Substitution *effectiveSubstitution_ = nullptr; - mutable bool isInstantiatingProperties_ = false; + const ArenaSubstitution *effectiveSubstitution_ = nullptr; mutable bool propertiesInstantiated_ = false; mutable ArenaVector constructSignatures_; mutable PropertyHolder properties_; diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index 0a51f2f1d6a1393d49d722194d834fdc8992eb7e..b3a4e93f35c0672ccd9053edd73c7b07650a9c12 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -34,15 +34,14 @@ enum class ETSObjectFlags : std::uint64_t { RESOLVED_INTERFACES = 1U << 7U, RESOLVED_SUPER = 1U << 8U, RESOLVED_TYPE_PARAMS = 1U << 9U, - CHECKED_COMPATIBLE_ABSTRACTS = 1U << 10U, - STRING = 1U << 11U, - INCOMPLETE_INSTANTIATION = 1U << 12U, - INNER = 1U << 13U, - DYNAMIC = 1U << 14U, - ASYNC_FUNC_RETURN_TYPE = 1U << 15U, - CHECKED_INVOKE_LEGITIMACY = 1U << 16U, - REQUIRED = 1U << 17U, - READONLY = 1U << 18U, + STRING = 1U << 10U, + INCOMPLETE_INSTANTIATION = 1U << 11U, + INNER = 1U << 12U, + DYNAMIC = 1U << 13U, + ASYNC_FUNC_RETURN_TYPE = 1U << 14U, + CHECKED_INVOKE_LEGITIMACY = 1U << 15U, + REQUIRED = 1U << 16U, + READONLY = 1U << 17U, BUILTIN_BIGINT = 1U << 22U, BUILTIN_STRING = 1U << 23U, @@ -55,17 +54,29 @@ enum class ETSObjectFlags : std::uint64_t { BUILTIN_FLOAT = 1U << 30U, BUILTIN_DOUBLE = 1U << 31U, BUILTIN_ARRAY = 1ULL << 32U, + BUILTIN_READONLY_ARRAY = 1ULL << 33U, - ENUM_OBJECT = 1ULL << 33U, - EXTENSION_FUNCTION = 1ULL << 34U, + INT_ENUM_OBJECT = 1ULL << 34U, + STRING_ENUM_OBJECT = 1ULL << 35U, - FUNCTIONAL_REFERENCE = 1ULL << 35U, + EXTENSION_FUNCTION = 1ULL << 36U, + FUNCTIONAL_REFERENCE = 1ULL << 37U, + LAZY_IMPORT_OBJECT = 1ULL << 38U, - BUILTIN_NUMERIC = BUILTIN_BYTE | BUILTIN_SHORT | BUILTIN_INT | BUILTIN_LONG | BUILTIN_FLOAT | BUILTIN_DOUBLE, + ENUM_OBJECT = INT_ENUM_OBJECT | STRING_ENUM_OBJECT, + + BUILTIN_FLOATING_POINT = BUILTIN_DOUBLE | BUILTIN_FLOAT, + BUILTIN_INTEGRAL = BUILTIN_BYTE | BUILTIN_SHORT | BUILTIN_INT | BUILTIN_LONG, + + BUILTIN_ARRAY_INDEX = BUILTIN_BYTE | BUILTIN_SHORT | BUILTIN_INT, + BUILTIN_ARRAY_NUMERIC = BUILTIN_ARRAY_INDEX | BUILTIN_FLOATING_POINT, + + BUILTIN_NUMERIC = BUILTIN_INTEGRAL | BUILTIN_FLOATING_POINT, // Complete set includes null|undefined|Object VALUE_TYPED = BUILTIN_BOOLEAN | BUILTIN_CHAR | BUILTIN_NUMERIC | BUILTIN_BIGINT | STRING, UNBOXABLE_TYPE = BUILTIN_BOOLEAN | BUILTIN_CHAR | BUILTIN_NUMERIC, BUILTIN_TYPE = BUILTIN_STRING | BUILTIN_BIGINT | UNBOXABLE_TYPE, + CONVERTIBLE_TO_NUMERIC = BUILTIN_NUMERIC | BUILTIN_CHAR | INT_ENUM_OBJECT, GLOBAL_CLASS = CLASS | GLOBAL, FUNCTIONAL_INTERFACE = INTERFACE | ABSTRACT | FUNCTIONAL, @@ -90,6 +101,7 @@ enum class PropertySearchFlags : std::uint32_t { DISALLOW_SYNTHETIC_METHOD_CREATION = 1U << 10U, IS_SETTER = 1U << 11U, IS_GETTER = 1U << 12U, + IGNORE_OVERLOAD = 1U << 13U, SEARCH_INSTANCE = SEARCH_INSTANCE_FIELD | SEARCH_INSTANCE_METHOD | SEARCH_INSTANCE_DECL, SEARCH_STATIC = SEARCH_STATIC_FIELD | SEARCH_STATIC_METHOD | SEARCH_STATIC_DECL, @@ -110,12 +122,6 @@ enum class PropertyType { COUNT, }; -/* Invoke method name in functional interfaces */ -inline std::string FunctionalInterfaceInvokeName(size_t arity, bool hasRest) -{ - return (hasRest ? "invokeR" : "invoke") + std::to_string(arity); -} - } // namespace ark::es2panda::checker namespace enumbitops { diff --git a/ets2panda/checker/types/ets/etsResizableArrayType.cpp b/ets2panda/checker/types/ets/etsResizableArrayType.cpp index 25fcd72aba950af51699942f1241bbfff595af9a..464207ceda8e4d8f2c71f898a8cdeb89990fdbe4 100644 --- a/ets2panda/checker/types/ets/etsResizableArrayType.cpp +++ b/ets2panda/checker/types/ets/etsResizableArrayType.cpp @@ -14,6 +14,7 @@ */ #include "etsResizableArrayType.h" +#include "etsUnionType.h" namespace ark::es2panda::checker { @@ -24,4 +25,16 @@ ETSResizableArrayType *ETSResizableArrayType::Substitute(TypeRelation *relation, return copiedType; } -} // namespace ark::es2panda::checker \ No newline at end of file +void ETSResizableArrayType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const +{ + if (ElementType() != nullptr) { + if (HasTypeFlag(TypeFlag::READONLY)) { + ss << "readonly "; + } + ss << "Array<"; + ElementType()->ToString(ss, precise); + ss << ">"; + } +} + +} // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsResizableArrayType.h b/ets2panda/checker/types/ets/etsResizableArrayType.h index 43babf404a36dcfdbce983333683b9a94a0666bd..90936ceadfbcb45ac54ccc707dc04d294f067d07 100644 --- a/ets2panda/checker/types/ets/etsResizableArrayType.h +++ b/ets2panda/checker/types/ets/etsResizableArrayType.h @@ -23,7 +23,7 @@ namespace ark::es2panda::checker { class ETSResizableArrayType : public ETSObjectType { public: - explicit ETSResizableArrayType(ArenaAllocator *allocator, ETSObjectType *super) + explicit ETSResizableArrayType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_ARRAY, nullptr, ETSObjectFlags::CLASS | ETSObjectFlags::BUILTIN_ARRAY | ETSObjectFlags::RESOLVED_SUPER), element_(nullptr) @@ -31,13 +31,13 @@ public: SetSuperType(super); } - explicit ETSResizableArrayType(ArenaAllocator *allocator, util::StringView name, + explicit ETSResizableArrayType(ThreadSafeArenaAllocator *allocator, util::StringView name, std::tuple info) : ETSObjectType(allocator, name, compiler::Signatures::BUILTIN_ARRAY, info), element_(nullptr) { } - explicit ETSResizableArrayType(ArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, + explicit ETSResizableArrayType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, Type *element) : ETSObjectType( allocator, "", compiler::Signatures::BUILTIN_ARRAY, @@ -76,10 +76,12 @@ public: ETSResizableArrayType *Substitute(TypeRelation *relation, const Substitution *substitution) override; + void ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const override; + private: Type *element_; }; } // namespace ark::es2panda::checker -#endif \ No newline at end of file +#endif diff --git a/ets2panda/checker/types/ets/etsStringType.cpp b/ets2panda/checker/types/ets/etsStringType.cpp index 1a3c8ba2cc94ec1fc7ea2c58a07b1c419137e8dc..ef98c512f8887eff0ba6f19d14f95fa89d0d081b 100644 --- a/ets2panda/checker/types/ets/etsStringType.cpp +++ b/ets2panda/checker/types/ets/etsStringType.cpp @@ -74,5 +74,4 @@ void ETSStringType::IsSubtypeOf(TypeRelation *relation, Type *source) auto *const checker = relation->GetChecker()->AsETSChecker(); relation->IsSupertypeOf(source, checker->GlobalBuiltinETSStringType()); } - } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsStringType.h b/ets2panda/checker/types/ets/etsStringType.h index 069af7f5c6265297700b8712bce4ce7862eeb9e7..40595517acbb04c5404a68a828a6f2674bc8ffc2 100644 --- a/ets2panda/checker/types/ets/etsStringType.h +++ b/ets2panda/checker/types/ets/etsStringType.h @@ -21,14 +21,14 @@ namespace ark::es2panda::checker { class ETSStringType : public ETSObjectType { public: - explicit ETSStringType(ArenaAllocator *allocator, ETSObjectType *super) + explicit ETSStringType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_STRING, super->GetDeclNode(), ETSObjectFlags::CLASS | ETSObjectFlags::STRING | ETSObjectFlags::RESOLVED_SUPER) { SetSuperType(super); } - explicit ETSStringType(ArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation) + explicit ETSStringType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_STRING, std::make_tuple(super->GetDeclNode(), ETSObjectFlags::CLASS | ETSObjectFlags::STRING | ETSObjectFlags::RESOLVED_SUPER, @@ -37,7 +37,7 @@ public: SetSuperType(super); } - explicit ETSStringType(ArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, + explicit ETSStringType(ThreadSafeArenaAllocator *allocator, ETSObjectType *super, TypeRelation *relation, util::StringView value) : ETSObjectType(allocator, "", compiler::Signatures::BUILTIN_STRING, std::make_tuple(super->GetDeclNode(), @@ -76,11 +76,6 @@ public: return value_; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), IsConstantType() ? (GetValue().Length() != 0) : false}; - } - private: util::StringView value_ {}; }; diff --git a/ets2panda/checker/types/ets/etsTupleType.cpp b/ets2panda/checker/types/ets/etsTupleType.cpp index 8453e085f2d2453b8d09695a7fb2cd15439f8601..38930b20a3a519e024ff6bba7b36b785e0619191 100644 --- a/ets2panda/checker/types/ets/etsTupleType.cpp +++ b/ets2panda/checker/types/ets/etsTupleType.cpp @@ -65,7 +65,9 @@ void ETSTupleType::ToDebugInfoType(std::stringstream &ss) const Type *ETSTupleType::GetTypeAtIndex(const TupleSizeType index) const { - ES2PANDA_ASSERT(index < GetTupleSize()); + if (index >= GetTupleSize()) { // happens when dealing with type errors + return nullptr; + } return GetTupleTypesList().at(index); } diff --git a/ets2panda/checker/types/ets/etsTupleType.h b/ets2panda/checker/types/ets/etsTupleType.h index f28d95d965093ce1a7e8016d9098f65573b82953..f6a76125a9f8d2032f1526238b5be2e100543fef 100644 --- a/ets2panda/checker/types/ets/etsTupleType.h +++ b/ets2panda/checker/types/ets/etsTupleType.h @@ -46,11 +46,6 @@ public: return typeList_; } - std::tuple ResolveConditionExpr() const override - { - return {false, false}; - } - [[nodiscard]] ETSObjectType *GetWrapperType() const { return wrapperType_; diff --git a/ets2panda/checker/types/ets/etsTypeAliasType.cpp b/ets2panda/checker/types/ets/etsTypeAliasType.cpp index 9b1578ba7533426afa38ac969fbd0c085e2cf8ed..c4394f5da3fe266019bd75c6910978978d6bd18b 100644 --- a/ets2panda/checker/types/ets/etsTypeAliasType.cpp +++ b/ets2panda/checker/types/ets/etsTypeAliasType.cpp @@ -250,7 +250,8 @@ bool ETSTypeAliasType::SubstituteTypeArgs(TypeRelation *const relation, ArenaVec for (auto *const arg : typeArguments_) { auto *const newArg = arg->Substitute(relation, substitution); - newTypeArgs.push_back(newArg); + ES2PANDA_ASSERT(newArg->IsETSReferenceType()); + newTypeArgs.emplace_back(newArg); anyChange = anyChange || (newArg != arg); } @@ -283,10 +284,13 @@ Type *ETSTypeAliasType::Substitute(TypeRelation *relation, const Substitution *s return copiedType; } + auto arenaSubst = checker->NewArenaSubstitution(); + std::copy(substitution->begin(), substitution->end(), std::inserter(*arenaSubst, arenaSubst->end())); + copiedType = checker->CreateETSTypeAliasType(name_, declNode_, isRecursive_); copiedType->base_ = base_ == nullptr ? this : base_; copiedType->parent_ = this; - copiedType->substitution_ = substitution; + copiedType->substitution_ = arenaSubst; copiedType->typeArguments_ = newTypeArgs; EmplaceInstantiatedType(hash, copiedType); diff --git a/ets2panda/checker/types/ets/etsTypeAliasType.h b/ets2panda/checker/types/ets/etsTypeAliasType.h index 1f497bf669a57f340b16343c14e0093e88df6211..3146b63f51500953d1a669ee47eb22d94419eaf1 100644 --- a/ets2panda/checker/types/ets/etsTypeAliasType.h +++ b/ets2panda/checker/types/ets/etsTypeAliasType.h @@ -53,11 +53,6 @@ public: targetType_ = targetType; } - std::tuple ResolveConditionExpr() const override - { - return {false, false}; - } - void SetRecursive(bool value = true) { isRecursive_ = value; @@ -110,7 +105,7 @@ private: Type *targetType_ = nullptr; InstantiationMap instantiationMap_; ArenaVector typeArguments_; - const Substitution *substitution_ = nullptr; + const ArenaSubstitution *substitution_ = nullptr; mutable bool recursionCount_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsTypeParameter.cpp b/ets2panda/checker/types/ets/etsTypeParameter.cpp index 0c70fe71e963ac391a5f95cdf544b7671d54497f..53c70fcfcc2cc884654aae6c0b78e1453262d4a5 100644 --- a/ets2panda/checker/types/ets/etsTypeParameter.cpp +++ b/ets2panda/checker/types/ets/etsTypeParameter.cpp @@ -148,7 +148,7 @@ Type *ETSTypeParameter::Substitute([[maybe_unused]] TypeRelation *relation, cons } if (auto repl = substitution->find(type->AsETSTypeParameter()); repl != substitution->end()) { - // 22955: The result is sometimes primitve. Can be reproduced for type aliases + ES2PANDA_ASSERT(repl->second->IsETSReferenceType()); return repl->second; } return this; @@ -164,7 +164,7 @@ void ETSTypeParameter::ToDebugInfoType(std::stringstream &ss) const GetConstraintType()->ToDebugInfoType(ss); } -ETSTypeParameter *ETSTypeParameter::GetOriginal() const noexcept +ETSTypeParameter *ETSTypeParameter::GetOriginal() const { return GetDeclNode()->Name()->Variable()->TsType()->AsETSTypeParameter(); } diff --git a/ets2panda/checker/types/ets/etsTypeParameter.h b/ets2panda/checker/types/ets/etsTypeParameter.h index d38b1b7b1d11ac0349d5ceb04e8d4b8916fc91f0..7eb6908b4b6ed65630b2a4553d4d35ba362cbae8 100644 --- a/ets2panda/checker/types/ets/etsTypeParameter.h +++ b/ets2panda/checker/types/ets/etsTypeParameter.h @@ -38,7 +38,7 @@ public: return declNode_; } - [[nodiscard]] ETSTypeParameter *GetOriginal() const noexcept; + [[nodiscard]] ETSTypeParameter *GetOriginal() const; [[nodiscard]] util::StringView const &Name() const noexcept; void SetDefaultType(Type *type) noexcept diff --git a/ets2panda/checker/types/ets/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index 44abc587587c26af246c896394d4e0d5148029fe..b927ff0afe924d258ec4e245a599525f8585d695 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -34,75 +34,125 @@ void ETSUnionType::ToString(std::stringstream &ss, bool precise) const void ETSUnionType::ToAssemblerType(std::stringstream &ss) const { - assemblerLub_->ToAssemblerTypeWithRank(ss); + ss << GetAssemblerType(); } void ETSUnionType::ToDebugInfoType(std::stringstream &ss) const { - assemblerLub_->ToDebugInfoType(ss); -} - -ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) - : Type(TypeFlag::ETS_UNION), constituentTypes_(std::move(constituentTypes)) -{ - ES2PANDA_ASSERT(constituentTypes_.size() > 1); - assemblerLub_ = ComputeAssemblerLUB(checker, this); + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToDebugInfoType(ss); + return; + } + ss << "{U"; + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + assemblerConstituentTypes_[idx]->ToDebugInfoType(ss); + if (idx != assemblerConstituentTypes_.size() - 1) { + ss << ","; + } + } + ss << "}"; } -bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +static std::string GetAssemblerTypeString(Type *type) { - return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), - [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); + std::stringstream ss; + type->ToAssemblerTypeWithRank(ss); + return ss.str(); } -bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +void ETSUnionType::InitAssemblerTypeCache(ETSChecker *checker) { - return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), - [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); + ES2PANDA_ASSERT(!assemblerConstituentTypes_.empty()); + std::stringstream ss; + if (assemblerConstituentTypes_.size() == 1) { + assemblerConstituentTypes_[0]->ToAssemblerTypeWithRank(ss); + } else { + ss << "{U"; + for (size_t idx = 0; idx < assemblerConstituentTypes_.size(); idx++) { + if (idx != 0) { + ss << ","; + } + if (assemblerConstituentTypes_[idx]->IsETSNullType()) { + ss << compiler::Signatures::NULL_ASSEMBLY_TYPE; + continue; + } + assemblerConstituentTypes_[idx]->ToAssemblerTypeWithRank(ss); + } + ss << "}"; + } + assemblerTypeCache_ = util::UString(ss.str(), checker->ProgramAllocator()).View(); } -// This function computes effective runtime representation of union type -Type *ETSUnionType::ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un) +void ETSUnionType::CanonicalizedAssemblerType(ETSChecker *checker) { - auto *const apparent = checker->GetApparentType(un); - ES2PANDA_ASSERT(apparent != nullptr); + auto *const apparent = checker->GetApparentType(this); if (!apparent->IsETSUnionType()) { - return apparent; + assemblerConstituentTypes_.push_back(apparent); + return; } - if (apparent != un) { - return apparent->AsETSUnionType()->assemblerLub_; + if (apparent != this) { + const auto &types = apparent->AsETSUnionType()->GetAssemblerTypes(); + assemblerConstituentTypes_.insert(assemblerConstituentTypes_.begin(), types.begin(), types.end()); + return; } - un = apparent->AsETSUnionType(); - - Type *lub = nullptr; - for (auto *t : un->ConstituentTypes()) { - if (t->IsTypeError()) { - return checker->GlobalTypeError(); - } - // NOTE(vpukhov): #19701 void refactoring - ES2PANDA_ASSERT(t->IsETSReferenceType() || t->IsETSVoidType()); - t = t->IsETSVoidType() ? checker->GlobalETSUndefinedType() : t; - if (lub == nullptr || lub->IsETSUndefinedType()) { - lub = t; + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + bool hasNull = false; + for (auto *type : constituentTypes_) { + ES2PANDA_ASSERT(!type->IsETSUnionType()); + if (type->IsETSUndefinedType() || type->IsETSVoidType()) { continue; } - if (lub == t || t->IsETSUndefinedType()) { + if (type->IsETSNullType() && !hasNull) { + hasNull = true; + assemblerConstituentTypes_.push_back(type); continue; } - if (t->IsETSNullType()) { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + if (type->IsTypeError()) { + assemblerConstituentTypes_.clear(); + assemblerConstituentTypes_.push_back(checker->GlobalTypeError()); + return; } - if (t->IsETSObjectType() && lub->IsETSObjectType()) { - lub = checker->GetClosestCommonAncestor(lub->AsETSObjectType(), t->AsETSObjectType()); - } else if (t->IsETSArrayType() && lub->IsETSArrayType()) { - // NOTE: can compute "common(lub, t)[]" - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); - } else { - return checker->GetGlobalTypesHolder()->GlobalETSObjectType(); + auto found = + std::find_if(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [&type](Type *t) { return GetAssemblerTypeString(type) == GetAssemblerTypeString(t); }); + if (found == assemblerConstituentTypes_.end()) { + assemblerConstituentTypes_.push_back(type); } } - return checker->GetNonConstantType(lub); + if (assemblerConstituentTypes_.empty()) { + assemblerConstituentTypes_.push_back(checker->GlobalETSObjectType()); + return; + } + if (assemblerConstituentTypes_.size() == 1) { + return; + } + + std::sort(assemblerConstituentTypes_.begin(), assemblerConstituentTypes_.end(), + [](Type *a, Type *b) { return GetAssemblerTypeString(a) < GetAssemblerTypeString(b); }); +} + +ETSUnionType::ETSUnionType(ETSChecker *checker, ArenaVector &&constituentTypes) + : Type(TypeFlag::ETS_UNION), + constituentTypes_(std::move(constituentTypes)), + assemblerConstituentTypes_(checker->ProgramAllocator()->Adapter()) +{ + ES2PANDA_ASSERT(constituentTypes_.size() > 1); + CanonicalizedAssemblerType(checker); + InitAssemblerTypeCache(checker); +} + +bool ETSUnionType::EachTypeRelatedToSomeType(TypeRelation *relation, ETSUnionType *source, ETSUnionType *target) +{ + return std::all_of(source->constituentTypes_.begin(), source->constituentTypes_.end(), + [relation, target](auto *s) { return TypeRelatedToSomeType(relation, s, target); }); +} + +bool ETSUnionType::TypeRelatedToSomeType(TypeRelation *relation, Type *source, ETSUnionType *target) +{ + return std::any_of(target->constituentTypes_.begin(), target->constituentTypes_.end(), + [relation, source](auto *t) { return relation->IsIdenticalTo(source, t); }); } void ETSUnionType::Identical(TypeRelation *relation, Type *other) @@ -139,16 +189,7 @@ void ETSUnionType::RelationTarget(TypeRelation *relation, Type *source, RelFN co return; } - if (std::any_of(constituentTypes_.begin(), constituentTypes_.end(), - [relation, refsource, relFn](auto *t) { return relFn(relation, refsource, t); })) { - if (refsource != source) { - // Some nodes can have both boxing and unboxing flags set. When applying them, first the unboxing happens - // (then a possible primitive conversion), and boxing at last. - // NOTE (smartin): when boxing/unboxing is moved to a lowering, review this part of the code - const auto mergedBoxingFlags = - relation->GetNode()->GetBoxingUnboxingFlags() | checker->GetBoxingFlag(refsource); - relation->GetNode()->SetBoxingUnboxingFlags(mergedBoxingFlags); - } + if (AnyOfConstituentTypes([relation, refsource, relFn](auto *t) { return relFn(relation, refsource, t); })) { relation->Result(true); return; } @@ -163,8 +204,8 @@ void ETSUnionType::RelationTarget(TypeRelation *relation, Type *source, RelFN co if (relFn(relation, source, checker->MaybeUnboxType(ct))) { if (related) { AmbiguousUnionOperation(relation); + return; } - relation->GetNode()->SetBoxingUnboxingFlags(checker->GetBoxingFlag(ct)); related = true; } } @@ -174,97 +215,55 @@ void ETSUnionType::RelationTarget(TypeRelation *relation, Type *source, RelFN co bool ETSUnionType::AssignmentSource(TypeRelation *relation, Type *target) { - auto *const checker = relation->GetChecker()->AsETSChecker(); - if (target->HasTypeFlag(TypeFlag::PRIMITIVE)) { - if (!relation->ApplyUnboxing()) { - return relation->Result(false); - } - relation->GetNode()->SetBoxingUnboxingFlags( - relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(checker->MaybeUnboxType(target))); - } - - return relation->Result(std::all_of(constituentTypes_.begin(), constituentTypes_.end(), - [relation, target](auto *t) { return relation->IsAssignableTo(t, target); })); + ES2PANDA_ASSERT(!target->IsETSPrimitiveType()); + return relation->Result( + AllOfConstituentTypes([relation, target](auto *t) { return relation->IsAssignableTo(t, target); })); } void ETSUnionType::AssignmentTarget(TypeRelation *relation, Type *source) { - auto const relFn = []([[maybe_unused]] TypeRelation *rel, [[maybe_unused]] Type *src, [[maybe_unused]] Type *tgt) { - return rel->IsAssignableTo(src, tgt); - }; + auto const relFn = [](TypeRelation *rel, Type *src, Type *tgt) { return rel->IsAssignableTo(src, tgt); }; RelationTarget(relation, source, relFn); } void ETSUnionType::Cast(TypeRelation *relation, Type *target) { - auto *const checker = relation->GetChecker()->AsETSChecker(); - - if (target->HasTypeFlag(TypeFlag::PRIMITIVE)) { - if (!relation->ApplyUnboxing()) { - relation->Result(false); - return; - } - - relation->GetNode()->SetBoxingUnboxingFlags( - relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(checker->MaybeUnboxType(target))); - } + ES2PANDA_ASSERT(!target->IsETSPrimitiveType()); if (relation->InCastingContext()) { - relation->Result(std::any_of(constituentTypes_.begin(), constituentTypes_.end(), - [relation, target](auto *t) { return relation->IsCastableTo(t, target); })); + relation->Result( + AnyOfConstituentTypes([relation, target](auto *t) { return relation->IsCastableTo(t, target); })); return; } - relation->Result(std::all_of(constituentTypes_.begin(), constituentTypes_.end(), - [relation, target](auto *t) { return relation->IsCastableTo(t, target); })); + relation->Result(AllOfConstituentTypes([relation, target](auto *t) { return relation->IsCastableTo(t, target); })); } void ETSUnionType::CastTarget(TypeRelation *relation, Type *source) { - auto const relFn = []([[maybe_unused]] TypeRelation *rel, [[maybe_unused]] Type *src, [[maybe_unused]] Type *tgt) { - return rel->IsCastableTo(src, tgt); - }; + auto const relFn = [](TypeRelation *rel, Type *src, Type *tgt) -> bool { return rel->IsCastableTo(src, tgt); }; RelationTarget(relation, source, relFn); } -static auto constexpr ETS_NORMALIZABLE_NUMERIC = TypeFlag(TypeFlag::ETS_NUMERIC); - -static Type *LargestNumeric(Type *t1, Type *t2) -{ - static_assert(TypeFlag::DOUBLE > TypeFlag::FLOAT); - static_assert(TypeFlag::FLOAT > TypeFlag::LONG); - static_assert(TypeFlag::LONG > TypeFlag::INT); - static_assert(TypeFlag::INT > TypeFlag::SHORT); - static_assert(TypeFlag::SHORT > TypeFlag::BYTE); - - auto v1 = t1->TypeFlags() & ETS_NORMALIZABLE_NUMERIC; - auto v2 = t2->TypeFlags() & ETS_NORMALIZABLE_NUMERIC; - ES2PANDA_ASSERT(helpers::math::IsPowerOfTwo(v1)); - ES2PANDA_ASSERT(helpers::math::IsPowerOfTwo(v2)); - return v1 > v2 ? t1 : t2; -} - static std::optional TryMergeTypes(TypeRelation *relation, Type *const t1, Type *const t2) { - auto checker = relation->GetChecker()->AsETSChecker(); - auto never = checker->GetGlobalTypesHolder()->GlobalETSNeverType(); + auto *const checker = relation->GetChecker()->AsETSChecker(); + auto *const never = checker->GetGlobalTypesHolder()->GlobalETSNeverType(); + if (relation->IsSupertypeOf(t1, t2) || t2 == never) { return t1; } if (relation->IsSupertypeOf(t2, t1) || t1 == never) { return t2; } - // NOTE(vpukhov): numerics - clarification required return std::nullopt; } void ETSUnionType::LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVector &types) { - auto *const checker = relation->GetChecker()->AsETSChecker(); - // Linearize - size_t const initialSz = types.size(); - for (size_t i = 0; i < initialSz; ++i) { + std::size_t const initialSz = types.size(); + for (std::size_t i = 0U; i < initialSz; ++i) { auto ct = types[i]; ES2PANDA_ASSERT(ct != nullptr); if (ct->IsETSUnionType()) { @@ -275,50 +274,32 @@ void ETSUnionType::LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVecto types[i] = nullptr; } } - size_t insPos = 0; - for (size_t i = 0; i < types.size(); ++i) { - auto *const ct = types[i]; - if (ct != nullptr) { - types[insPos++] = ct; - } - } - types.resize(insPos); - // Promote primitives - for (auto &ct : types) { - ct = checker->MaybeBoxType(ct); - } + // Remove nullptrs + types.erase(std::remove_if(types.begin(), types.end(), [](Type *ct) { return ct == nullptr; }), types.end()); + // Reduce subtypes for (auto cmpIt = types.begin(); cmpIt != types.end(); ++cmpIt) { - for (auto it = std::next(cmpIt); it != types.end();) { - auto merged = TryMergeTypes(relation, *cmpIt, *it); - if (!merged) { + auto it = std::next(cmpIt); + while (it != types.end()) { + if (auto merged = TryMergeTypes(relation, *cmpIt, *it); !merged) { ++it; - continue; - } - - if (merged == *cmpIt) { + } else if (*merged == *cmpIt) { it = types.erase(it); - continue; + } else { + cmpIt = types.erase(cmpIt); + it = cmpIt != types.end() ? std::next(cmpIt) : cmpIt; } - - cmpIt = types.erase(cmpIt); - it = std::next(cmpIt); } } } void ETSUnionType::NormalizeTypes(TypeRelation *relation, ArenaVector &types) { - if (types.size() == 1) { - return; - } - auto const isNumeric = [](auto *ct) { return ct->HasTypeFlag(ETS_NORMALIZABLE_NUMERIC); }; - if (std::all_of(types.begin(), types.end(), isNumeric)) { - types[0] = std::accumulate(std::next(types.begin()), types.end(), types[0], LargestNumeric); - types.resize(1); + if (types.size() == 1U) { return; } + LinearizeAndEraseIdentical(relation, types); } @@ -327,7 +308,7 @@ Type *ETSUnionType::Instantiate(ArenaAllocator *allocator, TypeRelation *relatio auto *const checker = relation->GetChecker()->AsETSChecker(); ArenaVector copiedConstituents(allocator->Adapter()); for (auto *it : constituentTypes_) { - copiedConstituents.push_back(it->Instantiate(allocator, relation, globalTypes)); + copiedConstituents.emplace_back(it->Instantiate(allocator, relation, globalTypes)); } return checker->CreateETSUnionType(std::move(copiedConstituents)); } @@ -337,14 +318,14 @@ Type *ETSUnionType::Substitute(TypeRelation *relation, const Substitution *subst auto *const checker = relation->GetChecker()->AsETSChecker(); ArenaVector substitutedConstituents(checker->Allocator()->Adapter()); for (auto *ctype : constituentTypes_) { - substitutedConstituents.push_back(ctype->Substitute(relation, substitution)); + substitutedConstituents.emplace_back(ctype->Substitute(relation, substitution)); } return checker->CreateETSUnionType(std::move(substitutedConstituents)); } void ETSUnionType::IsSupertypeOf(TypeRelation *relation, Type *source) { - for (auto const &ctype : ConstituentTypes()) { + for (auto const *ctype : ConstituentTypes()) { if (relation->IsSupertypeOf(ctype, source)) { return; } @@ -353,7 +334,7 @@ void ETSUnionType::IsSupertypeOf(TypeRelation *relation, Type *source) void ETSUnionType::IsSubtypeOf(TypeRelation *relation, Type *target) { - for (auto const &ctype : ConstituentTypes()) { + for (auto const *ctype : ConstituentTypes()) { if (!relation->IsSupertypeOf(target, ctype)) { return; } @@ -362,139 +343,74 @@ void ETSUnionType::IsSubtypeOf(TypeRelation *relation, Type *target) void ETSUnionType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) { - for (auto const &ctype : ConstituentTypes()) { + for (auto *ctype : ConstituentTypes()) { relation->CheckVarianceRecursively(ctype, relation->TransferVariant(varianceFlag, VarianceFlag::COVARIANT)); } } -bool ETSUnionType::IsAssignableType(checker::Type *sourceType) const noexcept -{ - if (sourceType->IsETSTypeParameter() || sourceType->IsTypeError()) { - return true; - } - - if (sourceType->IsETSUnionType() || sourceType->IsETSArrayType() || sourceType->IsETSFunctionType()) { - return true; - } - - return false; -} - -checker::Type *ETSUnionType::HandleNumericPrecedence( - checker::ETSChecker *checker, checker::ETSObjectType *objectType, checker::Type *sourceType, - std::map &numericTypes) const noexcept +// ATTENTION! When calling this method we assume that 'AssignmentTarget(...)' check was passes successfully, +// thus the required assignable type (or corresponding supertype) always exists. +checker::Type *ETSUnionType::GetAssignableType(checker::ETSChecker *checker, checker::Type *sourceType, + [[maybe_unused]] std::optional value) const { - auto const sourceId = - (objectType != nullptr) ? ETSObjectType::GetPrecedence(checker, objectType) : Type::GetPrecedence(sourceType); - if (sourceId > 0U) { - for (auto const [id, type] : numericTypes) { - if (id >= sourceId) { - return type; - } - } - if (sourceType->IsConstantType() && !numericTypes.empty()) { - return numericTypes.begin()->second; + for (auto *ctype : ConstituentTypes()) { + if (checker->Relation()->IsSupertypeOf(ctype, sourceType)) { + return ctype; } } - return nullptr; -} -// NOTE! When calling this method we assume that 'AssignmentTarget(...)' check was passes successfully, -// thus the required assignable type always exists. -checker::Type *ETSUnionType::GetAssignableType(checker::ETSChecker *checker, checker::Type *sourceType) const noexcept -{ - if (IsAssignableType(sourceType)) { - return sourceType; - } - - auto *objectType = sourceType->IsETSObjectType() ? sourceType->AsETSObjectType() - : sourceType->IsETSTupleType() ? sourceType->AsETSTupleType()->GetWrapperType() - : nullptr; - std::map numericTypes {}; - bool const isBool = objectType != nullptr ? objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN) - : sourceType->HasTypeFlag(TypeFlag::ETS_BOOLEAN); - bool const isChar = objectType != nullptr ? objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR) - : sourceType->HasTypeFlag(TypeFlag::CHAR); - - if (objectType != nullptr) { - if (objectType->IsETSResizableArrayType() || sourceType->IsETSTupleType()) { - checker::Type *assignableType = GetAssignableBuiltinType(checker, objectType, isBool, isChar, numericTypes); - // NOTE: For array and tuple types, they may be readonly, so we cannot simply use the it - if (assignableType != nullptr && assignableType->HasTypeFlag(TypeFlag::READONLY)) { - return assignableType; - } - } - if ((!objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE) || - objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_STRING))) { - // NOTE: here wo don't cast the actual type to possible base type using in the union, but use it as is! - return sourceType; - } + if (!sourceType->IsBuiltinNumeric()) { + return nullptr; } - if (checker::Type *assignableType = GetAssignableBuiltinType(checker, objectType, isBool, isChar, numericTypes); - assignableType != nullptr) { + // NOTE (DZ): we still keep 'numericTypes` collection for possible processing cases like 'let x: short|double = 1` + // Waiting for complete clearness in spec - now return the highest type in such a case or type itself. + // Maybe 'value' will be used for this purpose + std::map numericTypes {}; + auto *objectType = sourceType->AsETSObjectType(); + if (auto *assignableType = GetAssignableBuiltinType(checker, objectType, numericTypes); assignableType != nullptr) { return assignableType; } - if (auto *assignableType = HandleNumericPrecedence(checker, objectType, sourceType, numericTypes)) { - return assignableType; + if (!numericTypes.empty()) { + return (*std::prev(numericTypes.end())).second; } - - for (auto *constituentType : constituentTypes_) { - if (constituentType->IsETSObjectType() && constituentType->AsETSObjectType()->IsGlobalETSObjectType()) { - return constituentType; - } - } - - return checker->GlobalTypeError(); + return nullptr; } checker::Type *ETSUnionType::GetAssignableBuiltinType( - checker::ETSChecker *checker, checker::ETSObjectType *sourceType, bool const isBool, bool const isChar, - std::map &numericTypes) const noexcept + checker::ETSChecker *checker, checker::ETSObjectType *sourceType, + std::map &numericTypes) const { - checker::Type *assignableType = nullptr; - for (auto *constituentType : constituentTypes_) { if (!constituentType->IsETSObjectType() && !constituentType->IsETSTupleType()) { continue; } - auto *const type = constituentType->IsETSTupleType() ? constituentType->AsETSTupleType()->GetWrapperType() - : constituentType->AsETSObjectType(); - if (type->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN)) { - if (isBool) { - assignableType = constituentType; - break; - } - } else if (type->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) { - if (isChar) { - assignableType = constituentType; - break; - } - } else if (auto const id = ETSObjectType::GetPrecedence(checker, type); id > 0U) { - numericTypes.emplace(id, constituentType); - } else if (assignableType == nullptr && sourceType != nullptr && - checker->Relation()->IsSupertypeOf(type, sourceType)) { - assignableType = constituentType; + ETSObjectType *objectType = constituentType->AsETSObjectType(); + if (!objectType->IsBuiltinNumeric()) { + continue; + } + + if (checker->Relation()->IsIdenticalTo(objectType, sourceType)) { + return sourceType; } + + numericTypes.emplace(ETSObjectType::GetPrecedence(checker, objectType), objectType); } - return assignableType; + return nullptr; } -// CC-OFFNXT(G.FUN.01, huge_method) solid logic -bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::ETSObjectType *sourceType, +bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::Type *source, ArenaVector &unionTypes) noexcept { - std::map::const_iterator> numericTypes {}; - bool const isBool = sourceType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN); - bool const isChar = sourceType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR); + source = checker->GetNonConstantType(source); bool rc = false; auto it = unionTypes.cbegin(); while (it != unionTypes.cend()) { - auto *constituentType = *it; + auto *constituentType = (*it)->MaybeBaseTypeOfGradualType(); // Because 'instanceof' expression does not check for type parameters, then for generic types we should // consider that expressions like 'SomeType' and 'SomeType' are identical for smart casting. // We also have to pass through all the union to process cases like 'C|A|B|C|undefined` @@ -506,71 +422,18 @@ bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::ETSObjectT constituentType->RemoveTypeFlag(checker::TypeFlag::GENERIC); } - if (checker->Relation()->IsIdenticalTo(constituentType, sourceType)) { - rc = true; - it = unionTypes.erase(it); - continue; - } - - if (checker->Relation()->IsSupertypeOf(constituentType, sourceType)) { + if (checker->Relation()->IsIdenticalTo(constituentType, source)) { rc = true; - } else if (!rc && constituentType->IsETSObjectType()) { - auto *const objectType = (*it)->AsETSObjectType(); - if (isBool && objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_BOOLEAN)) { - unionTypes.erase(it); - return true; - } - - if (isChar && objectType->HasObjectFlag(ETSObjectFlags::BUILTIN_CHAR)) { - unionTypes.erase(it); - return true; - } - - if (auto const id = ETSObjectType::GetPrecedence(checker, objectType); id > 0U) { - numericTypes.emplace(id, it); - } - } - - ++it; - } - - if (rc) { - return true; - } - - if (auto const sourceId = ETSObjectType::GetPrecedence(checker, sourceType); sourceId > 0U) { - for (auto const [id, it1] : numericTypes) { - if (id >= sourceId) { - unionTypes.erase(it1); - return true; + if (!(*it)->IsETSTypeParameter()) { + it = unionTypes.erase(it); + continue; } } - } - - return false; -} - -bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::ETSArrayType *sourceType, - ArenaVector &unionTypes) noexcept -{ - auto it = unionTypes.cbegin(); - - bool rc = false; - while (it != unionTypes.cend()) { - auto *constituentType = *it; - if (constituentType->IsETSTypeParameter()) { - constituentType = constituentType->AsETSTypeParameter()->GetConstraintType(); - } - - if (checker->Relation()->IsIdenticalTo(constituentType, sourceType)) { - rc = true; - it = unionTypes.erase(it); - continue; - } - if (checker->Relation()->IsSupertypeOf(constituentType, sourceType)) { + if (checker->Relation()->IsSupertypeOf(constituentType, source)) { rc = true; } + ++it; } @@ -581,33 +444,34 @@ std::pair ETSUnionType::GetComplimentaryType(E checker::Type *sourceType) { ArenaVector unionTypes(checker->Allocator()->Adapter()); - for (auto *it : constituentTypes_) { - unionTypes.emplace_back(it); + for (auto *ct : constituentTypes_) { + unionTypes.emplace_back(ct->Clone(checker)); } - bool ok = true; - if (sourceType->IsETSUnionType()) { - for (auto *const constituentType : sourceType->AsETSUnionType()->ConstituentTypes()) { - if (ok = ExtractType(checker, constituentType->AsETSObjectType(), unionTypes); !ok) { - break; - } + auto const extractType = [checker, &unionTypes](Type *&type) -> bool { + ES2PANDA_ASSERT(!type->IsETSPrimitiveType()); + if (type->IsETSEnumType()) { + return true; } - } else if (sourceType->IsETSArrayType()) { - ok = ExtractType(checker, sourceType->AsETSArrayType(), unionTypes); - } else { - // NOTE(vpukhov): #19701 void refactoring - if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType()) { - sourceType = checker->MaybeBoxInRelation(sourceType); - } else if (sourceType->HasTypeFlag(checker::TypeFlag::GENERIC)) { + if (type->HasTypeFlag(checker::TypeFlag::GENERIC)) { // Because 'instanceof' expression does not check for type parameters, then for generic types we should // consider that expressions like 'SomeType' and 'SomeType' are identical for smart casting. - sourceType = sourceType->Clone(checker); - sourceType->RemoveTypeFlag(checker::TypeFlag::GENERIC); + type = type->Clone(checker); + type->RemoveTypeFlag(checker::TypeFlag::GENERIC); } + return ExtractType(checker, type, unionTypes); + }; - if (sourceType->IsETSObjectType()) { - ok = ExtractType(checker, sourceType->AsETSObjectType(), unionTypes); + bool ok = true; + + if (sourceType->IsETSUnionType()) { + for (auto *constituentType : sourceType->AsETSUnionType()->ConstituentTypes()) { + if (ok = extractType(constituentType); !ok) { + break; + } } + } else { + ok = extractType(sourceType); } if (!ok) { @@ -615,7 +479,9 @@ std::pair ETSUnionType::GetComplimentaryType(E } checker::Type *complimentaryType; - if (unionTypes.size() == 1U) { + if (auto const size = unionTypes.size(); size == 0U) { + complimentaryType = checker->GetGlobalTypesHolder()->GlobalETSNeverType(); + } else if (size == 1U) { complimentaryType = unionTypes.front(); } else { complimentaryType = checker->CreateETSUnionType(std::move(unionTypes)); @@ -624,145 +490,24 @@ std::pair ETSUnionType::GetComplimentaryType(E return std::make_pair(sourceType, complimentaryType); } -Type *ETSUnionType::FindTypeIsCastableToThis(ir::Expression *node, TypeRelation *relation, Type *source) const +Type *ETSUnionType::FindUnboxableType() const noexcept { - ES2PANDA_ASSERT(node); - bool nodeWasSet = false; - if (relation->GetNode() == nullptr) { - nodeWasSet = true; - relation->SetNode(node); - } - // Prioritize object to object conversion - auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) { - relation->IsCastableTo(source, target); - return relation->IsTrue() && source->IsETSReferenceType() && target->IsETSReferenceType(); - }); - if (it != constituentTypes_.end()) { - if (nodeWasSet) { - relation->SetNode(nullptr); - } - return *it; - } - it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [relation, source](Type *target) { - relation->IsCastableTo(source, target); - return relation->IsTrue(); - }); - if (nodeWasSet) { - relation->SetNode(nullptr); - } - if (it != constituentTypes_.end()) { - return *it; - } - return nullptr; + return FindSpecificType([](Type *t) { return t->IsETSUnboxableObject(); }); } -Type *ETSUnionType::FindTypeIsCastableToSomeType(ir::Expression *node, TypeRelation *relation, Type *target) const -{ - ES2PANDA_ASSERT(node); - bool nodeWasSet = false; - if (relation->GetNode() == nullptr) { - nodeWasSet = true; - relation->SetNode(node); - relation->SetFlags(TypeRelationFlag::CASTING_CONTEXT); - } - auto isCastablePred = [](TypeRelation *r, Type *sourceType, Type *targetType) { - if (targetType->IsETSUnionType()) { - auto *foundTargetType = targetType->AsETSUnionType()->FindTypeIsCastableToThis(r->GetNode(), r, sourceType); - r->Result(foundTargetType != nullptr); - } else { - r->IsCastableTo(sourceType, targetType); - } - return r->IsTrue(); - }; - // Prioritize object to object conversion - auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), - [relation, target, &isCastablePred](Type *source) { - return isCastablePred(relation, source, target) && source->IsETSReferenceType() && - target->IsETSReferenceType(); - }); // CC-OFF(G.FMT.02) project code style - if (it != constituentTypes_.end()) { - if (nodeWasSet) { - relation->SetNode(nullptr); - relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT); - } - return *it; - } - it = std::find_if( - constituentTypes_.begin(), constituentTypes_.end(), - [relation, target, &isCastablePred](Type *source) { return isCastablePred(relation, source, target); }); - if (nodeWasSet) { - relation->SetNode(nullptr); - relation->RemoveFlags(TypeRelationFlag::CASTING_CONTEXT); - } - if (it != constituentTypes_.end()) { - return *it; - } - return nullptr; -} - -Type *ETSUnionType::FindUnboxableType() const -{ - auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), - [](Type *t) { return t->IsETSUnboxableObject(); }); - if (it != constituentTypes_.end()) { - return *it; - } - return nullptr; -} - -bool ETSUnionType::HasObjectType(ETSObjectFlags flag) const -{ - auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [flag](Type *t) { - return t->IsETSObjectType() && t->AsETSObjectType()->HasObjectFlag(flag); - }); - return it != constituentTypes_.end(); -} - -Type *ETSUnionType::FindExactOrBoxedType(ETSChecker *checker, Type *const type) const -{ - auto it = std::find_if(constituentTypes_.begin(), constituentTypes_.end(), [checker, type](Type *ct) { - if (ct->IsETSUnboxableObject()) { - auto *const unboxedCt = checker->MaybeUnboxInRelation(ct); - return unboxedCt == type; - } - return ct == type; - }); - if (it != constituentTypes_.end()) { - return *it; - } - return nullptr; -} - -std::tuple ETSUnionType::ResolveConditionExpr() const -{ - if (PossiblyETSString()) { - return {false, false}; - } - if (std::all_of(ConstituentTypes().begin(), ConstituentTypes().end(), - [](checker::Type const *ct) { return ct->DefinitelyETSNullish(); })) { - return {true, false}; - } - // We have to test if union can contain builtin numerics or string types to infer "true" - return {false, false}; -} - -bool ETSUnionType::HasType(Type *type) const -{ - for (const auto &cType : constituentTypes_) { - if (cType == type) { - return true; - } - } - return false; -} - -bool ETSUnionType::IsOverlapWith(TypeRelation *relation, Type *type) +bool ETSUnionType::IsOverlapWith(TypeRelation *relation, Type const *type) const noexcept { // NOTE(aakmaev): replace this func with intersection type when it will be implemented - for (auto const &ct : ConstituentTypes()) { + for (auto *ct : constituentTypes_) { if (type->IsETSUnionType() && type->AsETSUnionType()->IsOverlapWith(relation, ct)) { return true; } + if (type->IsETSObjectType() && ct->IsETSObjectType()) { + if (type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC) && + ct->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC)) { + return true; + } + } if (relation->IsSupertypeOf(ct, type) || relation->IsSupertypeOf(type, ct)) { return true; } @@ -770,11 +515,11 @@ bool ETSUnionType::IsOverlapWith(TypeRelation *relation, Type *type) return false; } -ArenaVector ETSUnionType::GetNonConstantTypes(ETSChecker *checker, const ArenaVector &types) +ArenaVector ETSUnionType::GetNonConstantTypes(ETSChecker *checker) const noexcept { ArenaVector nonConstTypes(checker->Allocator()->Adapter()); - for (const auto &ct : types) { - nonConstTypes.push_back(checker->GetNonConstantType(ct)); + for (auto *ct : constituentTypes_) { + nonConstTypes.emplace_back(checker->GetNonConstantType(ct)); } return nonConstTypes; } diff --git a/ets2panda/checker/types/ets/etsUnionType.h b/ets2panda/checker/types/ets/etsUnionType.h index 12ae980aec9d686f2561d43f4309aec0d3710229..0ae2e8a38bdf30728e7547aefc0292a7dea020fe 100644 --- a/ets2panda/checker/types/ets/etsUnionType.h +++ b/ets2panda/checker/types/ets/etsUnionType.h @@ -45,39 +45,47 @@ public: void IsSupertypeOf(TypeRelation *relation, Type *source) override; void IsSubtypeOf(TypeRelation *relation, Type *target) override; void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; - Type *FindTypeIsCastableToThis(ir::Expression *node, TypeRelation *relation, Type *source) const; - Type *FindTypeIsCastableToSomeType(ir::Expression *node, TypeRelation *relation, Type *target) const; - Type *FindUnboxableType() const; - bool HasObjectType(ETSObjectFlags flag) const; - bool HasType(Type *type) const; + [[nodiscard]] Type *FindUnboxableType() const noexcept; - bool IsOverlapWith(TypeRelation *relation, Type *type); - - Type *FindExactOrBoxedType(ETSChecker *checker, Type *type) const; + [[nodiscard]] bool IsOverlapWith(TypeRelation *relation, Type const *type) const noexcept; static void NormalizeTypes(TypeRelation *relation, ArenaVector &types); - static ArenaVector GetNonConstantTypes(ETSChecker *checker, const ArenaVector &types); - - std::tuple ResolveConditionExpr() const override; + [[nodiscard]] ArenaVector GetNonConstantTypes(ETSChecker *checker) const noexcept; - // Do not use it anywhere except codegen - Type *GetAssemblerLUB() const + const util::StringView &GetAssemblerType() const { - return assemblerLub_; + return assemblerTypeCache_; } template - bool AllOfConstituentTypes(UnaryPredicate p) const + [[nodiscard]] bool AllOfConstituentTypes(UnaryPredicate p) const noexcept { return std::all_of(constituentTypes_.cbegin(), constituentTypes_.cend(), p); } - checker::Type *HandleNumericPrecedence(checker::ETSChecker *checker, checker::ETSObjectType *objectType, - checker::Type *sourceType, - std::map &numericTypes) const noexcept; - [[nodiscard]] checker::Type *GetAssignableType(ETSChecker *checker, checker::Type *sourceType) const noexcept; + template + [[nodiscard]] bool AnyOfConstituentTypes(UnaryPredicate p) const noexcept + { + return std::any_of(constituentTypes_.cbegin(), constituentTypes_.cend(), p); + } + + template + [[nodiscard]] Type *FindSpecificType(UnaryPredicate p) const noexcept + { + auto const it = std::find_if(constituentTypes_.cbegin(), constituentTypes_.cend(), p); + return it != constituentTypes_.cend() ? *it : nullptr; + } + + template + [[nodiscard]] bool HasSpecificType(UnaryPredicate p) const noexcept + { + return FindSpecificType(p) != nullptr; + } + + [[nodiscard]] checker::Type *GetAssignableType(ETSChecker *checker, checker::Type *sourceType, + std::optional value) const; [[nodiscard]] std::pair GetComplimentaryType(ETSChecker *checker, checker::Type *sourceType); @@ -89,21 +97,24 @@ private: void RelationTarget(TypeRelation *relation, Type *source, RelFN const &relFn); static void LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVector &types); - [[nodiscard]] static bool ExtractType(ETSChecker *checker, checker::ETSObjectType *sourceType, - ArenaVector &unionTypes) noexcept; - [[nodiscard]] static bool ExtractType(ETSChecker *checker, checker::ETSArrayType *sourceType, + [[nodiscard]] static bool ExtractType(ETSChecker *checker, checker::Type *source, ArenaVector &unionTypes) noexcept; [[nodiscard]] checker::Type *GetAssignableBuiltinType( - checker::ETSChecker *checker, checker::ETSObjectType *sourceType, bool isBool, bool isChar, - std::map &numericTypes) const noexcept; + checker::ETSChecker *checker, checker::ETSObjectType *sourceType, + std::map &numericTypes) const; - bool IsAssignableType(checker::Type *sourceType) const noexcept; + void CanonicalizedAssemblerType(ETSChecker *checker); + void InitAssemblerTypeCache(ETSChecker *checker); - static Type *ComputeAssemblerLUB(ETSChecker *checker, ETSUnionType *un); + const ArenaVector &GetAssemblerTypes() const + { + return assemblerConstituentTypes_; + } ArenaVector const constituentTypes_; - Type *assemblerLub_ {nullptr}; + ArenaVector assemblerConstituentTypes_; + util::StringView assemblerTypeCache_; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/etsVoidType.cpp b/ets2panda/checker/types/ets/etsVoidType.cpp index bb3e90fb257d0bc0fb0a69f895d449c6e7b0192f..0fe7ede62002e0d284a5238d309d5957cef10b3d 100644 --- a/ets2panda/checker/types/ets/etsVoidType.cpp +++ b/ets2panda/checker/types/ets/etsVoidType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -23,6 +23,11 @@ void ETSVoidType::Identical(TypeRelation *relation, Type *other) } } +void ETSVoidType::IsSupertypeOf(TypeRelation *const relation, Type *source) +{ + relation->Result(source->IsETSUndefinedType()); +} + bool ETSVoidType::AssignmentSource(TypeRelation *relation, Type *target) { // NOTE(vpukhov): #19701 void refactoring diff --git a/ets2panda/checker/types/ets/etsVoidType.h b/ets2panda/checker/types/ets/etsVoidType.h index bf6f9fcf7ff8261ae4ffdec8213aa4f790a524e2..845d638d89b8a49fd592686f29dcbba5ea15f5ba 100644 --- a/ets2panda/checker/types/ets/etsVoidType.h +++ b/ets2panda/checker/types/ets/etsVoidType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -26,6 +26,7 @@ public: void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; bool AssignmentSource(TypeRelation *relation, Type *target) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; void ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const override diff --git a/ets2panda/checker/types/ets/floatType.cpp b/ets2panda/checker/types/ets/floatType.cpp index 7f69458b1896ea368a70785d9f2cf810d9c0f092..958f338480b0afa1b7787edfc1f443222202edb5 100644 --- a/ets2panda/checker/types/ets/floatType.cpp +++ b/ets2panda/checker/types/ets/floatType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -16,7 +16,7 @@ #include "floatType.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" namespace ark::es2panda::checker { void FloatType::Identical(TypeRelation *relation, Type *other) @@ -28,10 +28,7 @@ void FloatType::Identical(TypeRelation *relation, Type *other) void FloatType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } bool FloatType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) @@ -57,38 +54,9 @@ void FloatType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT | TypeFlag::LONG)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_FLOAT)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_FLOAT)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/floatType.h b/ets2panda/checker/types/ets/floatType.h index 61f04f8e0b5b37eb34d2b1da22ac6f064d282b11..936effb4b0f1f59e8d5ccc5824f7e1653aab4277 100644 --- a/ets2panda/checker/types/ets/floatType.h +++ b/ets2panda/checker/types/ets/floatType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,12 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_FLOAT; } - std::tuple ResolveConditionExpr() const override - { - // isNan = !(value_ == value_) - return {IsConstantType(), (value_ != 0) && (value_ == value_)}; - } - private: UType value_ {0.0}; }; diff --git a/ets2panda/checker/types/ets/intType.cpp b/ets2panda/checker/types/ets/intType.cpp index c21e904b086e871881b70036dd39fd804a7eae0c..3587ee1bc0b071c21700acadf8ecc3315412f5a6 100644 --- a/ets2panda/checker/types/ets/intType.cpp +++ b/ets2panda/checker/types/ets/intType.cpp @@ -16,7 +16,7 @@ #include "intType.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" namespace ark::es2panda::checker { void IntType::Identical(TypeRelation *relation, Type *other) @@ -28,10 +28,7 @@ void IntType::Identical(TypeRelation *relation, Type *other) void IntType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing() && !relation->IsTrue()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } bool IntType::AssignmentSource(TypeRelation *relation, Type *target) @@ -57,38 +54,9 @@ void IntType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_INT)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_INT)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/intType.h b/ets2panda/checker/types/ets/intType.h index b1583a869578ff5243ca051353b4a963b54698aa..835cc47d612c7010193530b2196075b5350085c6 100644 --- a/ets2panda/checker/types/ets/intType.h +++ b/ets2panda/checker/types/ets/intType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,11 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_INT; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_ != 0}; - } - private: UType value_ {0}; }; diff --git a/ets2panda/checker/types/ets/longType.cpp b/ets2panda/checker/types/ets/longType.cpp index 7f4d080115b80bcfc8306baef6c79224046bd87c..7b8c5b34fae0af6e8144ecc4412a82528e8c93ba 100644 --- a/ets2panda/checker/types/ets/longType.cpp +++ b/ets2panda/checker/types/ets/longType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -16,7 +16,7 @@ #include "longType.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" namespace ark::es2panda::checker { void LongType::Identical(TypeRelation *relation, Type *other) @@ -28,10 +28,7 @@ void LongType::Identical(TypeRelation *relation, Type *other) void LongType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing() && !relation->IsTrue()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } bool LongType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) @@ -57,38 +54,9 @@ void LongType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::SHORT | TypeFlag::CHAR | TypeFlag::INT)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::FLOAT | TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_LONG)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/longType.h b/ets2panda/checker/types/ets/longType.h index 898b692072905d627d0f84b0138d0faf46161e87..0823bc7608f4840b9e2db740f130590ba8ee6cec 100644 --- a/ets2panda/checker/types/ets/longType.h +++ b/ets2panda/checker/types/ets/longType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,11 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_LONG; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_ != 0}; - } - private: UType value_ {0}; }; diff --git a/ets2panda/checker/types/ets/shortType.cpp b/ets2panda/checker/types/ets/shortType.cpp index bc2011294ba2ee8c3f735743c3f027abcbdacdc7..c7f79a229ea6be5b256578cf30154725121f5e93 100644 --- a/ets2panda/checker/types/ets/shortType.cpp +++ b/ets2panda/checker/types/ets/shortType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2024 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -16,7 +16,7 @@ #include "shortType.h" #include "checker/ets/conversion.h" -#include "checker/ets/narrowingWideningConverter.h" +#include "checker/ets/wideningConverter.h" namespace ark::es2panda::checker { void ShortType::Identical(TypeRelation *relation, Type *other) @@ -28,10 +28,7 @@ void ShortType::Identical(TypeRelation *relation, Type *other) void ShortType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) { - if (relation->ApplyUnboxing() && !relation->IsTrue()) { - relation->GetChecker()->AsETSChecker()->MaybeAddUnboxingFlagInRelation(relation, source, this); - } - NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); } bool ShortType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) @@ -57,38 +54,9 @@ void ShortType::Cast(TypeRelation *const relation, Type *const target) return; } - if (target->HasTypeFlag(TypeFlag::BYTE | TypeFlag::CHAR)) { - conversion::NarrowingPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::INT | TypeFlag::LONG | TypeFlag::FLOAT | TypeFlag::DOUBLE)) { - conversion::WideningPrimitive(relation, this, target); - return; - } - - if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) { - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_SHORT)) { - conversion::Boxing(relation, this); - return; - } - - if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { - auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target); - if (unboxedTarget == nullptr) { - conversion::Forbidden(relation); - return; - } - Cast(relation, unboxedTarget); - if (relation->IsTrue()) { - conversion::Boxing(relation, unboxedTarget); - return; - } - conversion::Forbidden(relation); - return; - } - - conversion::BoxingWideningReference(relation, this, target->AsETSObjectType()); + if (target->HasTypeFlag(TypeFlag::ETS_OBJECT) && + target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_SHORT)) { + conversion::Boxing(relation, this); return; } diff --git a/ets2panda/checker/types/ets/shortType.h b/ets2panda/checker/types/ets/shortType.h index 0cbd73ab16e223a284fa41bd1692c33588f48c6b..4b226806a322536a8c10bb03e5169d534fa8f2b1 100644 --- a/ets2panda/checker/types/ets/shortType.h +++ b/ets2panda/checker/types/ets/shortType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -52,11 +52,6 @@ public: ss << compiler::Signatures::TYPE_DESCRIPTOR_SHORT; } - std::tuple ResolveConditionExpr() const override - { - return {IsConstantType(), value_ != 0}; - } - private: UType value_ {0}; }; diff --git a/ets2panda/checker/types/ets/types.h b/ets2panda/checker/types/ets/types.h index 68b9f0079e01979ddefd35e89163c968e3b2077c..e82a8fa0a503b690ae1cd7a15531ebfa873e4fd8 100644 --- a/ets2panda/checker/types/ets/types.h +++ b/ets2panda/checker/types/ets/types.h @@ -32,7 +32,6 @@ #include "etsBigIntType.h" #include "etsObjectType.h" #include "etsTypeAliasType.h" -#include "etsDynamicType.h" #include "etsArrayType.h" #include "wildcardType.h" #include "etsTypeParameter.h" @@ -42,5 +41,6 @@ #include "etsReadonlyType.h" #include "etsAnyType.h" #include "etsNeverType.h" +#include "etsEnumType.h" #endif /* TYPES_H */ diff --git a/ets2panda/checker/types/globalTypesHolder.cpp b/ets2panda/checker/types/globalTypesHolder.cpp index 300e9b3fbf2d5ffe883189398111f9a2634f32c5..4bf67257acbf5114f924961e48c1706fdf6dca53 100644 --- a/ets2panda/checker/types/globalTypesHolder.cpp +++ b/ets2panda/checker/types/globalTypesHolder.cpp @@ -58,6 +58,7 @@ void GlobalTypesHolder::AddETSEscompatLayer() { // ETS escompat layer builtinNameMappings_.emplace("Array", GlobalTypeId::ETS_ARRAY_BUILTIN); + builtinNameMappings_.emplace("ReadonlyArray", GlobalTypeId::ETS_READONLY_ARRAY); builtinNameMappings_.emplace("Date", GlobalTypeId::ETS_DATE_BUILTIN); builtinNameMappings_.emplace("Error", GlobalTypeId::ETS_ERROR_BUILTIN); builtinNameMappings_.emplace("DivideByZeroError", GlobalTypeId::ETS_DIVIDE_BY_ZERO_ERROR_BUILTIN); @@ -85,7 +86,7 @@ void GlobalTypesHolder::AddFunctionTypes(ArenaAllocator *allocator) addTypes("LambdaR", GlobalTypeId::ETS_LAMBDAR0_CLASS, GlobalTypeId::ETS_LAMBDAR16_CLASS); builtinNameMappings_.emplace("FunctionN", GlobalTypeId::ETS_FUNCTIONN_CLASS); - builtinNameMappings_.emplace("LambdaN", GlobalTypeId::ETS_FUNCTIONN_CLASS); + builtinNameMappings_.emplace("LambdaN", GlobalTypeId::ETS_LAMBDAN_CLASS); } void GlobalTypesHolder::AddTupleTypes(ArenaAllocator *allocator) @@ -150,6 +151,7 @@ void GlobalTypesHolder::AddEtsSpecificTypes(ArenaAllocator *allocator) globalTypes_[static_cast(GlobalTypeId::ETS_WILDCARD)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::TYPE_ERROR)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ETS_ANY)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::ETS_RELAXED_ANY)] = allocator->New(true); globalTypes_[static_cast(GlobalTypeId::ETS_NEVER)] = allocator->New(); } @@ -159,6 +161,7 @@ void GlobalTypesHolder::AddEtsSpecificBuiltinTypes() builtinNameMappings_.emplace("Boolean", GlobalTypeId::ETS_BOOLEAN_BUILTIN); builtinNameMappings_.emplace("Byte", GlobalTypeId::ETS_BYTE_BUILTIN); builtinNameMappings_.emplace("Char", GlobalTypeId::ETS_CHAR_BUILTIN); + builtinNameMappings_.emplace("Class", GlobalTypeId::ETS_CLASS_BUILTIN); builtinNameMappings_.emplace("Comparable", GlobalTypeId::ETS_COMPARABLE_BUILTIN); builtinNameMappings_.emplace("Console", GlobalTypeId::ETS_CONSOLE_BUILTIN); builtinNameMappings_.emplace("Double", GlobalTypeId::ETS_DOUBLE_BUILTIN); @@ -168,6 +171,7 @@ void GlobalTypesHolder::AddEtsSpecificBuiltinTypes() builtinNameMappings_.emplace("Int", GlobalTypeId::ETS_INT_BUILTIN); builtinNameMappings_.emplace("Integral", GlobalTypeId::ETS_INTEGRAL_BUILTIN); builtinNameMappings_.emplace("Long", GlobalTypeId::ETS_LONG_BUILTIN); + builtinNameMappings_.emplace("Numeric", GlobalTypeId::ETS_NUMERIC_BUILTIN); builtinNameMappings_.emplace("Object", GlobalTypeId::ETS_OBJECT_BUILTIN); builtinNameMappings_.emplace("Runtime", GlobalTypeId::ETS_RUNTIME_BUILTIN); builtinNameMappings_.emplace("RuntimeLinker", GlobalTypeId::ETS_RUNTIME_LINKER_BUILTIN); @@ -411,6 +415,11 @@ Type *GlobalTypesHolder::GlobalETSAnyType() return globalTypes_.at(static_cast(GlobalTypeId::ETS_ANY)); } +Type *GlobalTypesHolder::GlobalETSRelaxedAnyType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_RELAXED_ANY)); +} + Type *GlobalTypesHolder::GlobalETSNeverType() { return globalTypes_.at(static_cast(GlobalTypeId::ETS_NEVER)); @@ -441,6 +450,11 @@ Type *GlobalTypesHolder::GlobalByteBuiltinType() return globalTypes_.at(static_cast(GlobalTypeId::ETS_BYTE_BUILTIN)); } +Type *GlobalTypesHolder::GlobalClassBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_CLASS_BUILTIN)); +} + Type *GlobalTypesHolder::GlobalCharBuiltinType() { return globalTypes_.at(static_cast(GlobalTypeId::ETS_CHAR_BUILTIN)); @@ -491,6 +505,11 @@ Type *GlobalTypesHolder::GlobalLongBuiltinType() return globalTypes_.at(static_cast(GlobalTypeId::ETS_LONG_BUILTIN)); } +Type *GlobalTypesHolder::GlobalNumericBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_NUMERIC_BUILTIN)); +} + Type *GlobalTypesHolder::GlobalMapBuiltinType() { return globalTypes_.at(static_cast(GlobalTypeId::ETS_MAP_BUILTIN)); @@ -616,6 +635,11 @@ Type *GlobalTypesHolder::GlobalArrayBuiltinType() return globalTypes_.at(static_cast(GlobalTypeId::ETS_ARRAY_BUILTIN)); } +Type *GlobalTypesHolder::GlobalReadonlyArray() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_READONLY_ARRAY)); +} + Type *GlobalTypesHolder::GlobalBoxBuiltinType() { return globalTypes_.at(static_cast(GlobalTypeId::ETS_BOX_BUILTIN)); diff --git a/ets2panda/checker/types/globalTypesHolder.h b/ets2panda/checker/types/globalTypesHolder.h index 635c8b080e5aed24f6ad9827b259a2d41b5f180c..14e5a384237cd6756970b9da46172f960cf98ef3 100644 --- a/ets2panda/checker/types/globalTypesHolder.h +++ b/ets2panda/checker/types/globalTypesHolder.h @@ -16,7 +16,6 @@ #ifndef ES2PANDA_COMPILER_CHECKER_TYPES_GLOBAL_TYPES_HOLDER_H #define ES2PANDA_COMPILER_CHECKER_TYPES_GLOBAL_TYPES_HOLDER_H -#include "ir/astNodeFlags.h" #include "checker/types/type.h" namespace ark::es2panda::checker { @@ -59,11 +58,13 @@ enum class GlobalTypeId : std::size_t { ETS_UNDEFINED, ETS_UNION_UNDEFINED_NULL, ETS_ANY, + ETS_RELAXED_ANY, ETS_NEVER, ETS_UNION_UNDEFINED_NULL_OBJECT, ETS_WILDCARD, ETS_BOOLEAN_BUILTIN, ETS_BYTE_BUILTIN, + ETS_CLASS_BUILTIN, ETS_CHAR_BUILTIN, ETS_COMPARABLE_BUILTIN, ETS_CONSOLE_BUILTIN, @@ -75,6 +76,7 @@ enum class GlobalTypeId : std::size_t { ETS_INT_BUILTIN, ETS_INTEGRAL_BUILTIN, ETS_LONG_BUILTIN, + ETS_NUMERIC_BUILTIN, ETS_MAP_BUILTIN, ETS_RECORD_BUILTIN, ETS_ERROR_BUILTIN, @@ -99,7 +101,6 @@ enum class GlobalTypeId : std::size_t { ETS_FUNCTION_BUILTIN, ETS_REGEXP_BUILTIN, ETS_ARRAY_BUILTIN, - ETS_ARRAY, ETS_INTEROP_JSRUNTIME_BUILTIN, ETS_INTEROP_JSVALUE_BUILTIN, ETS_BOX_BUILTIN, @@ -113,6 +114,8 @@ enum class GlobalTypeId : std::size_t { ETS_DOUBLE_BOX_BUILTIN, ETS_BIG_INT_BUILTIN, ETS_BIG_INT, + ETS_ARRAY, + ETS_READONLY_ARRAY, ETS_FUNCTION0_CLASS, ETS_FUNCTION1_CLASS, @@ -269,12 +272,14 @@ public: Type *GlobalETSNullType(); Type *GlobalETSUndefinedType(); Type *GlobalETSAnyType(); + Type *GlobalETSRelaxedAnyType(); Type *GlobalETSNeverType(); Type *GlobalETSUnionUndefinedNull(); Type *GlobalETSUnionUndefinedNullObject(); Type *GlobalWildcardType(); Type *GlobalETSBooleanBuiltinType(); Type *GlobalByteBuiltinType(); + Type *GlobalClassBuiltinType(); Type *GlobalCharBuiltinType(); Type *GlobalComparableBuiltinType(); Type *GlobalConsoleBuiltinType(); @@ -285,6 +290,7 @@ public: Type *GlobalIntegerBuiltinType(); Type *GlobalIntegralBuiltinType(); Type *GlobalLongBuiltinType(); + Type *GlobalNumericBuiltinType(); Type *GlobalErrorBuiltinType(); Type *GlobalRuntimeBuiltinType(); Type *GlobalShortBuiltinType(); @@ -322,6 +328,7 @@ public: // ETS escompat layer Type *GlobalArrayBuiltinType(); + Type *GlobalReadonlyArray(); Type *GlobalAssertionErrorBuiltinType(); Type *GlobalDivideByZeroErrorBuiltinType(); Type *GlobalNullPointerErrorBuiltinType(); diff --git a/ets2panda/checker/types/gradualType.cpp b/ets2panda/checker/types/gradualType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccf3f35b6c558b42ac42bcc1b1b6d1710b55c86f --- /dev/null +++ b/ets2panda/checker/types/gradualType.cpp @@ -0,0 +1,129 @@ +/* + * 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) +{ + if (other->IsGradualType()) { + baseType_->Identical(relation, other->AsGradualType()->GetBaseType()); + } else { + baseType_->Identical(relation, other); + } +} + +void GradualType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + if (source->IsGradualType()) { + baseType_->AssignmentTarget(relation, source->AsGradualType()->GetBaseType()); + } else { + baseType_->AssignmentTarget(relation, source); + } +} + +bool GradualType::AssignmentSource(TypeRelation *relation, Type *target) +{ + if (target->IsGradualType()) { + return baseType_->AssignmentSource(relation, target->AsGradualType()->GetBaseType()); + } + return baseType_->AssignmentSource(relation, target); +} + +void GradualType::Compare(TypeRelation *relation, Type *other) +{ + if (other->IsGradualType()) { + baseType_->Compare(relation, other->AsGradualType()->GetBaseType()); + } else { + baseType_->Compare(relation, other); + } +} + +void GradualType::Cast(TypeRelation *relation, Type *target) +{ + if (target->IsGradualType()) { + baseType_->Cast(relation, target->AsGradualType()->GetBaseType()); + } else { + baseType_->Cast(relation, target); + } +} + +void GradualType::CastTarget(TypeRelation *relation, Type *source) +{ + if (source->IsGradualType()) { + baseType_->CastTarget(relation, source->AsGradualType()->GetBaseType()); + } else { + baseType_->CastTarget(relation, source); + } +} + +void GradualType::IsSubtypeOf(TypeRelation *relation, Type *target) +{ + if (target->IsGradualType()) { + baseType_->IsSubtypeOf(relation, target->AsGradualType()->GetBaseType()); + } else { + baseType_->IsSubtypeOf(relation, target); + } +} + +void GradualType::IsSupertypeOf(TypeRelation *relation, Type *source) +{ + if (source->IsGradualType()) { + relation->IsSupertypeOf(baseType_, source->AsGradualType()->GetBaseType()); + } else { + baseType_->IsSupertypeOf(relation, source); + } +} + +void GradualType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const +{ + baseType_->ToString(ss); +} + +Type *GradualType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + auto baseType = baseType_->Instantiate(allocator, relation, globalTypes); + return relation->GetChecker()->AsETSChecker()->CreateGradualType(baseType); +} + +Type *GradualType::Substitute(TypeRelation *relation, const Substitution *substitution) +{ + return baseType_->Substitute(relation, substitution); +} + +void GradualType::ToAssemblerType(std::stringstream &ss) const +{ + baseType_->ToAssemblerType(ss); +} + +void GradualType::ToDebugInfoType(std::stringstream &ss) const +{ + baseType_->ToDebugInfoType(ss); +} + +void GradualType::ToAssemblerTypeWithRank(std::stringstream &ss) const +{ + baseType_->ToAssemblerTypeWithRank(ss); +} + +void GradualType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) +{ + // The type of array should be Invariant + relation->CheckVarianceRecursively(baseType_, varianceFlag); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/ets/etsDynamicType.h b/ets2panda/checker/types/gradualType.h similarity index 37% rename from ets2panda/checker/types/ets/etsDynamicType.h rename to ets2panda/checker/types/gradualType.h index eea79b4fb92a6ea1b1275bdfc17aa8c1a845ad9c..eb1978e8d64d03011ffa14443c8b8607c8559850 100644 --- a/ets2panda/checker/types/ets/etsDynamicType.h +++ b/ets2panda/checker/types/gradualType.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2025 Huawei Device Co., Ltd. + * 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 @@ -12,61 +12,78 @@ * 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 -#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_ETS_DYNAMIC_TYPE_H -#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_DYNAMIC_TYPE_H - -#include "checker/types/ets/etsObjectType.h" +#include "checker/types/type.h" +#include "ir/astNode.h" namespace ark::es2panda::checker { -class ETSDynamicType : public ETSObjectType { - static constexpr auto NAME = 0; - static constexpr auto ASSEMBLER_NAME = 1; - static constexpr auto LANGUAGE = 2; - static constexpr auto DECL_NODE = 0; - static constexpr auto FLAGS = 1; - static constexpr auto RELATION = 2; - +class GradualType : public Type { public: - explicit ETSDynamicType(ArenaAllocator *allocator, std::tuple label, - std::tuple info, bool hasDecl) - : ETSObjectType(allocator, std::get(label), std::get(label), - std::make_tuple(std::get(info), std::get(info) | ETSObjectFlags::DYNAMIC, - std::get(info))), - propertiesCache_ {allocator->Adapter()}, - lang_(std::get(label)), - hasDecl_(hasDecl) + 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) { - AddTypeFlag(TypeFlag::ETS_DYNAMIC_TYPE); } - varbinder::LocalVariable *GetPropertyDynamic(const util::StringView &name, const ETSChecker *checker) const; + void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; bool AssignmentSource(TypeRelation *relation, Type *target) override; + void Compare(TypeRelation *relation, Type *other) override; void Cast(TypeRelation *relation, Type *target) override; void CastTarget(TypeRelation *relation, Type *source) override; + void IsSubtypeOf(TypeRelation *relation, Type *target) override; + void IsSupertypeOf(TypeRelation *relation, Type *source) override; + void ToString(std::stringstream &ss, bool precise) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void ToDebugInfoType(std::stringstream &ss) const override; + void ToAssemblerTypeWithRank(std::stringstream &ss) const override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + Type *Substitute(TypeRelation *relation, const Substitution *substitution) override; + void CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) override; - es2panda::Language Language() const + const Type *GetBaseType() const { - return lang_; + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; } - bool HasDecl() const + Type *GetBaseType() { - return hasDecl_; + auto baseType = baseType_; + while (baseType->IsGradualType()) { + baseType = baseType->AsGradualType()->BaseType(); + } + return baseType; } - ETSFunctionType *CreateMethodTypeForProp(const util::StringView &name) const override; + Type *BaseType() + { + return baseType_; + } - void ToAssemblerType(std::stringstream &ss) const override; + Type *BaseType() const + { + return baseType_; + } - static bool IsConvertible(Type const *target); + es2panda::Language Language() const + { + return lang_; + } private: - mutable PropertyMap propertiesCache_; + Type *baseType_; es2panda::Language lang_; - bool hasDecl_; }; } // namespace ark::es2panda::checker -#endif +#endif \ No newline at end of file diff --git a/ets2panda/checker/types/signature.cpp b/ets2panda/checker/types/signature.cpp index 0bcb243d17a9405ef437857027efa42c087bf025..0aaeab306651b74f0c87a99d36bc33cd095b239f 100644 --- a/ets2panda/checker/types/signature.cpp +++ b/ets2panda/checker/types/signature.cpp @@ -117,7 +117,7 @@ Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, Gl SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); ES2PANDA_ASSERT(copiedInfo != nullptr); for (size_t idx = 0U; idx < signatureInfo_->params.size(); ++idx) { - auto *const paramType = signatureInfo_->params[idx]->TsType(); + auto *const paramType = signatureInfo_->params[idx]->TsType()->MaybeBaseTypeOfGradualType(); if (paramType->HasTypeFlag(TypeFlag::GENERIC) && paramType->IsETSObjectType()) { copiedInfo->params[idx]->SetTsType(paramType->Instantiate(allocator, relation, globalTypes)); auto originalTypeArgs = paramType->AsETSObjectType()->GetOriginalBaseType()->TypeArguments(); @@ -192,12 +192,6 @@ void Signature::ToString(std::stringstream &ss, const varbinder::Variable *varia } returnType_->ToString(ss, precise); - - if (HasSignatureFlag(SignatureFlags::THROWS)) { - ss << " throws"; - } else if (HasSignatureFlag(SignatureFlags::RETHROWS)) { - ss << " rethrows"; - } } std::string Signature::ToString() const diff --git a/ets2panda/checker/types/signature.h b/ets2panda/checker/types/signature.h index 12af6cd4dc8e7a8a80f0a89b12e71e3f0971d509..4051395469791c11047011856c61c70fa52f8dca 100644 --- a/ets2panda/checker/types/signature.h +++ b/ets2panda/checker/types/signature.h @@ -91,6 +91,8 @@ enum class SignatureFlags : uint32_t { RETHROWS = 1U << 17U, EXTENSION_FUNCTION = 1U << 18U, DUPLICATE_ASM = 1U << 19U, + BRIDGE = 1U << 20U, + DEFAULT = 1U << 21U, INTERNAL_PROTECTED = INTERNAL | PROTECTED, GETTER_OR_SETTER = GETTER | SETTER, @@ -224,6 +226,11 @@ public: return signatureInfo_->restVar; } + [[nodiscard]] varbinder::LocalVariable *RestVar() noexcept + { + return signatureInfo_->restVar; + } + [[nodiscard]] uint8_t ProtectionFlag() const noexcept { if ((flags_ & SignatureFlags::PRIVATE) != 0) { diff --git a/ets2panda/checker/types/type.cpp b/ets2panda/checker/types/type.cpp index 92368135f34ab098c1a16ecead974eb3b4a03e5c..694499259016b06b0bea17986aa1a3dc766f89e3 100644 --- a/ets2panda/checker/types/type.cpp +++ b/ets2panda/checker/types/type.cpp @@ -18,15 +18,23 @@ #include "checker/types/typeFlag.h" #include "checker/types/typeRelation.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "checker/checker.h" namespace ark::es2panda::checker { +std::mutex Type::idLock_ {}; + bool Type::IsETSResizableArrayType() const { return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_ARRAY); } +bool Type::IsETSReadonlyArrayType() const +{ + return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_READONLY_ARRAY); +} + bool Type::IsETSStringType() const { return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::STRING); @@ -47,6 +55,11 @@ bool Type::IsETSAsyncFuncReturnType() const return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ASYNC_FUNC_RETURN_TYPE); } +bool Type::IsBuiltinNumeric() const noexcept +{ + return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC); +} + bool Type::IsLambdaObject() const { if (IsETSObjectType() && (AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE) || @@ -149,39 +162,18 @@ Type *Type::Substitute([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] return this; } -std::uint32_t Type::GetPrecedence(Type const *type) noexcept +bool IsTypeError(Type const *tp) { - ES2PANDA_ASSERT(type != nullptr); - if (type->HasTypeFlag(TypeFlag::BYTE)) { - return 1U; - } - if (type->HasTypeFlag(TypeFlag::CHAR)) { - return 2U; - } - if (type->HasTypeFlag(TypeFlag::SHORT)) { - return 3U; - } - if (type->HasTypeFlag(TypeFlag::INT)) { - return 4U; - } - if (type->HasTypeFlag(TypeFlag::LONG)) { - return 5U; - } - if (type->HasTypeFlag(TypeFlag::FLOAT)) { - return 6U; - } - if (type->HasTypeFlag(TypeFlag::DOUBLE)) { - return 7U; - } - if (type->HasTypeFlag(TypeFlag::BIGINT)) { - return 8U; - } - return 0U; + return tp != nullptr && tp->IsTypeError(); } -bool IsTypeError(Type const *tp) +Type *Type::MaybeBaseTypeOfGradualType() { - return tp != nullptr && tp->IsTypeError(); + auto res = this; + while (res->IsGradualType()) { + res = res->AsGradualType()->GetBaseType(); + } + return res; } } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/type.h b/ets2panda/checker/types/type.h index d6d1fb2f7dc9444b1b81ae6e0e5f29c6c4af3e01..b0565a97d768a97e48fe6ff99f1413f9df69d67a 100644 --- a/ets2panda/checker/types/type.h +++ b/ets2panda/checker/types/type.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_H #define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_H +#include #include "generated/signatures.h" #include "checker/types/typeMapping.h" #include "checker/types/typeRelation.h" @@ -28,12 +29,11 @@ class Variable; namespace ark::es2panda::checker { class ObjectDescriptor; class GlobalTypesHolder; -class ETSDynamicType; class ETSAsyncFuncReturnType; class ETSChecker; -class ETSDynamicFunctionType; class ETSTypeParameter; class ETSEnumType; +class GradualType; // CC-OFFNXT(G.PRE.02) name part // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -44,13 +44,16 @@ class ETSStringType; class ETSBigIntType; class ETSResizableArrayType; -using Substitution = ArenaMap; +using Substitution = std::map; +using ArenaSubstitution = ArenaMap; class Type { public: explicit Type(TypeFlag flag) : typeFlags_(flag) { - static uint64_t typeId = 0; + std::lock_guard lock(idLock_); + static uint32_t typeId = 0; + ES2PANDA_ASSERT(typeId < std::numeric_limits::max()); id_ = ++typeId; } @@ -58,6 +61,7 @@ public: NO_MOVE_SEMANTIC(Type); virtual ~Type() = default; + static std::mutex idLock_; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TYPE_IS_CHECKS(typeFlag, typeName) \ @@ -75,12 +79,20 @@ public: /* CC-OFFNXT(G.PRE.02) name part*/ \ typeName *As##typeName() \ { \ + if (IsTypeError()) { \ + LOG(INFO, ES2PANDA) << "Erroneous cast to '" << #typeName << "' type."; \ + throw std::exception(); \ + } \ 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 \ { \ + if (IsTypeError()) { \ + LOG(INFO, ES2PANDA) << "Erroneous cast to '" << #typeName << "' type."; \ + throw std::exception(); \ + } \ ES2PANDA_ASSERT(Is##typeName()); \ /* CC-OFFNXT(G.PRE.05) The macro is used to generate a function. Return is needed*/ \ return reinterpret_cast(this); \ @@ -89,6 +101,7 @@ public: #undef TYPE_AS_CASTS bool IsETSResizableArrayType() const; + bool IsETSReadonlyArrayType() const; bool IsETSStringType() const; bool IsETSCharType() const; bool IsETSBigIntType() const; @@ -110,6 +123,8 @@ public: bool PossiblyETSValueTyped() const; bool PossiblyETSValueTypedExceptNullish() const; + bool PossiblyInForeignDomain() const; + ETSStringType *AsETSStringType() { ES2PANDA_ASSERT(IsETSObjectType()); @@ -140,22 +155,7 @@ public: return reinterpret_cast(this); } - bool IsETSDynamicType() const - { - return IsETSObjectType() && HasTypeFlag(TypeFlag::ETS_DYNAMIC_FLAG); - } - - ETSDynamicType *AsETSDynamicType() - { - ES2PANDA_ASSERT(IsETSDynamicType()); - return reinterpret_cast(this); - } - - const ETSDynamicType *AsETSDynamicType() const - { - ES2PANDA_ASSERT(IsETSDynamicType()); - return reinterpret_cast(this); - } + [[nodiscard]] bool IsBuiltinNumeric() const noexcept; ETSAsyncFuncReturnType *AsETSAsyncFuncReturnType() { @@ -169,27 +169,17 @@ public: return reinterpret_cast(this); } - bool IsETSDynamicFunctionType() const + bool IsConstantType() const { - return TypeFlags() == TypeFlag::ETS_DYNAMIC_FUNCTION_TYPE; + return HasTypeFlag(checker::TypeFlag::CONSTANT); } - ETSDynamicFunctionType *AsETSDynamicFunctionType() + bool IsAnyETSArrayOrTupleType() const { - ES2PANDA_ASSERT(IsETSDynamicFunctionType()); - return reinterpret_cast(this); + return IsETSArrayType() || IsETSResizableArrayType() || IsETSReadonlyArrayType() || IsETSTupleType(); } - const ETSDynamicFunctionType *AsETSDynamicFunctionType() const - { - ES2PANDA_ASSERT(IsETSDynamicFunctionType()); - return reinterpret_cast(this); - } - - bool IsConstantType() const - { - return HasTypeFlag(checker::TypeFlag::CONSTANT); - } + Type *MaybeBaseTypeOfGradualType(); TypeFlag TypeFlags() const { @@ -211,7 +201,7 @@ public: typeFlags_ &= ~typeFlag; } - uint64_t Id() const + uint32_t Id() const { return id_; } @@ -262,15 +252,24 @@ public: ToAssemblerType(ss); } - virtual uint32_t Rank() const + std::string ToAssemblerType() const { - return 0; + std::stringstream ss; + ToAssemblerType(ss); + return ss.str(); } - virtual std::tuple ResolveConditionExpr() const + std::string ToAssemblerTypeWithRank() const { - ES2PANDA_UNREACHABLE(); - }; + std::stringstream ss; + ToAssemblerTypeWithRank(ss); + return ss.str(); + } + + virtual uint32_t Rank() const + { + return 0; + } virtual void Identical(TypeRelation *relation, Type *other); virtual void AssignmentTarget(TypeRelation *relation, Type *source) = 0; @@ -285,7 +284,6 @@ public: [[maybe_unused]] VarianceFlag varianceFlag) { } - [[nodiscard]] static std::uint32_t GetPrecedence(Type const *type) noexcept; virtual Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes); [[nodiscard]] virtual Type *Clone(Checker *checker); @@ -295,7 +293,7 @@ protected: // NOLINTBEGIN(misc-non-private-member-variables-in-classes) TypeFlag typeFlags_; varbinder::Variable *variable_ {}; // Variable associated with the type if any - uint64_t id_; + uint32_t id_; // NOLINTEND(misc-non-private-member-variables-in-classes) }; diff --git a/ets2panda/checker/types/typeError.h b/ets2panda/checker/types/typeError.h index 86f49b40cce7ce36b426c122935ace1da5a8d4e6..df378a1fa6a7f5954c2dcfee7fe5bbf3b4747588 100644 --- a/ets2panda/checker/types/typeError.h +++ b/ets2panda/checker/types/typeError.h @@ -52,4 +52,42 @@ public: } // namespace ark::es2panda::checker +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define EMPTY_VALUE + +// CC-OFFNXT(G.PRE.02-CPP) TypeError handling macro definition +#define ERROR_SANITY_CHECK(etsChecker, test, whatIfFails) \ + if (!(test)) { \ + ES2PANDA_ASSERT((etsChecker)->IsAnyError()); \ + whatIfFails; \ + } + +// CC-OFFNXT(G.PRE.02-CPP) TypeError handling macro definition +#define ERROR_TYPE_CHECK(etsChecker, testType, whatIfError) \ + ES2PANDA_ASSERT((testType) != nullptr); \ + if ((testType)->IsTypeError()) { \ + ES2PANDA_ASSERT((etsChecker)->IsAnyError()); \ + whatIfError; \ + } + +// CC-OFFNXT(G.PRE.02-CPP) TypeError handling macro definition +#define FORWARD_TYPE_ERROR(etsChecker, testType, target) \ + ES2PANDA_ASSERT((testType) != nullptr); \ + if ((testType)->IsTypeError()) { \ + ES2PANDA_ASSERT((etsChecker)->IsAnyError()); \ + /* CC-OFFNXT(G.PRE.05) error handling. */ \ + return (target)->SetTsType((etsChecker)->GlobalTypeError()); \ + } + +// CC-OFFNXT(G.PRE.02-CPP) TypeError handling macro definition +#define FORWARD_VALUE_ON_TYPE_ERROR(etsChecker, testType, target, value) \ + ES2PANDA_ASSERT((testType) != nullptr); \ + if ((testType)->IsTypeError()) { \ + ES2PANDA_ASSERT((etsChecker)->IsAnyError()); \ + (target)->SetTsType((etsChecker)->GlobalTypeError()); \ + /* CC-OFFNXT(G.PRE.05) error handling. */ \ + return value; \ + } +// NOLINTEND(cppcoreguidelines-macro-usage) + #endif diff --git a/ets2panda/checker/types/typeFlag.h b/ets2panda/checker/types/typeFlag.h index feea58ce1294a994cceccb60c8811fb08999593c..5f01ae82252c6d2fcf311bbdfc2804c5f010a98c 100644 --- a/ets2panda/checker/types/typeFlag.h +++ b/ets2panda/checker/types/typeFlag.h @@ -69,11 +69,10 @@ enum class TypeFlag : uint64_t { ETS_ARRAY = 1ULL << 41ULL, // ETS array type WILDCARD = 1ULL << 42ULL, // new A() ETS_TYPE_PARAMETER = 1ULL << 43ULL, // ETS type parameter - ETS_TYPE_REFERENCE = 1ULL << 44ULL, // ETS type reference GENERIC = 1ULL << 45ULL, // ETS Generic ETS_INT_ENUM = 1ULL << 46ULL, // ETS Enum ETS_STRING_ENUM = 1ULL << 47ULL, // ETS string-type Enumeration - ETS_DYNAMIC_FLAG = 1ULL << 48ULL, // ETS Dynamic flag + GRADUAL_TYPE = 1ULL << 48ULL, // gradual type GETTER = 1ULL << 49ULL, // ETS Getter SETTER = 1ULL << 50ULL, // ETS Setter ETS_EXTENSION_FUNC_HELPER = 1ULL << 51ULL, // ETS Extension Function Helper @@ -90,8 +89,6 @@ enum class TypeFlag : uint64_t { ETS_ANY = 1ULL << 22ULL, // ETS any, the value was *stolen* from the CONDITIONAL type kind ETS_NEVER = 1ULL << 62ULL, // ETS never ETS_METHOD = 1ULL << 63ULL, // ETS method (or function in module) (possibly overloaded) - ETS_DYNAMIC_TYPE = ETS_OBJECT | ETS_DYNAMIC_FLAG, - ETS_DYNAMIC_FUNCTION_TYPE = FUNCTION | ETS_DYNAMIC_FLAG, ETS_INTEGRAL_NUMERIC = BYTE | SHORT | INT | LONG, ETS_FLOATING_POINT = FLOAT | DOUBLE, ETS_NUMERIC = ETS_INTEGRAL_NUMERIC | ETS_FLOATING_POINT, diff --git a/ets2panda/checker/types/typeMapping.h b/ets2panda/checker/types/typeMapping.h index a856a49bdbc8561f4e98e87e5be6a1b936804281..84e3ba9cfef6fdf49983f38bf38c92e203b2e5b3 100644 --- a/ets2panda/checker/types/typeMapping.h +++ b/ets2panda/checker/types/typeMapping.h @@ -71,6 +71,7 @@ _(TypeFlag::ETS_TUPLE, ETSTupleType) \ _(TypeFlag::ETS_PARTIAL_TYPE_PARAMETER, ETSPartialTypeParameter) \ _(TypeFlag::TYPE_ERROR, TypeError) \ + _(TypeFlag::GRADUAL_TYPE, GradualType) \ _(TypeFlag::ETS_TYPE_ALIAS, ETSTypeAliasType) #define OBJECT_TYPE_MAPPING(_) \ diff --git a/ets2panda/checker/types/typeRelation.cpp b/ets2panda/checker/types/typeRelation.cpp index f96b0ffa6d4487f7754b8c7bb8fd91ca0694a63b..697f407f038dab7310cf880e6bbe87e716e20f9f 100644 --- a/ets2panda/checker/types/typeRelation.cpp +++ b/ets2panda/checker/types/typeRelation.cpp @@ -29,24 +29,20 @@ ArenaAllocator *TypeRelation::Allocator() RelationResult TypeRelation::CacheLookup(const Type *source, const Type *target, const RelationHolder &holder, RelationType type) const { - if (result_ == RelationResult::CACHE_MISS) { - return result_; - } - ES2PANDA_ASSERT(source != nullptr); ES2PANDA_ASSERT(target != nullptr); - RelationKey relationKey {source->Id(), target->Id()}; - auto res = holder.cached.find(relationKey); - if (res == holder.cached.end()) { + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + auto res = holder.Find(key); + if (res == nullptr) { return RelationResult::CACHE_MISS; } - if (res->second.type >= type && res->second.result == RelationResult::TRUE) { + if (res->type >= type && res->result == RelationResult::TRUE) { return RelationResult::TRUE; } - if (res->second.type <= type && res->second.result == RelationResult::FALSE) { + if (res->type <= type && res->result == RelationResult::FALSE) { return RelationResult::FALSE; } @@ -69,7 +65,8 @@ bool TypeRelation::IsIdenticalTo(Type *source, Type *target) checker_->ResolveStructuredTypeMembers(target); result_ = RelationResult::FALSE; target->Identical(this, source); - checker_->IdenticalResults().cached.insert({{source->Id(), target->Id()}, {result_, RelationType::IDENTICAL}}); + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->IdenticalResults().Insert(key, {result_, RelationType::IDENTICAL}); } return IsTrue(); @@ -114,7 +111,6 @@ bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target) return result_ == RelationResult::TRUE; } -// NOTE: applyNarrowing -> flag bool TypeRelation::IsAssignableTo(Type *source, Type *target) { if (source == target) { @@ -133,7 +129,9 @@ bool TypeRelation::IsAssignableTo(Type *source, Type *target) result_ = RelationResult::FALSE; } + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); if (result_ != RelationResult::FALSE && IsIdenticalTo(source, target)) { + checker_->AssignableResults().Insert(key, {result_, RelationType::ASSIGNABLE}); return true; } @@ -148,8 +146,7 @@ bool TypeRelation::IsAssignableTo(Type *source, Type *target) } if (flags_ == TypeRelationFlag::NONE) { - checker_->AssignableResults().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}}); + checker_->AssignableResults().Insert(key, {result_, RelationType::ASSIGNABLE}); } } @@ -159,16 +156,8 @@ bool TypeRelation::IsAssignableTo(Type *source, Type *target) bool TypeRelation::IsComparableTo(Type *source, Type *target) { result_ = CacheLookup(source, target, checker_->ComparableResults(), RelationType::COMPARABLE); - - // NOTE: vpukhov. reimplement dynamic comparison and remove this check ES2PANDA_ASSERT(source != nullptr); ES2PANDA_ASSERT(target != nullptr); - if (source->IsETSDynamicType() || target->IsETSDynamicType()) { - if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) { - return false; - } - } - if (result_ == RelationResult::CACHE_MISS) { if (IsAssignableTo(source, target)) { return true; @@ -176,8 +165,9 @@ bool TypeRelation::IsComparableTo(Type *source, Type *target) result_ = RelationResult::FALSE; target->Compare(this, source); - checker_->ComparableResults().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::COMPARABLE}}); + ES2PANDA_ASSERT(source != nullptr); + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->ComparableResults().Insert(key, {result_, RelationType::COMPARABLE}); } return result_ == RelationResult::TRUE; @@ -199,12 +189,9 @@ bool TypeRelation::IsCastableTo(Type *const source, Type *const target) return false; } - // NOTE: Can't cache if the node has BoxingUnboxingFlags. These flags should be stored and restored on the node - // on cache hit. - if (UncheckedCast() && node_->GetBoxingUnboxingFlags() == ir::BoxingUnboxingFlags::NONE && - !node_->HasAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF)) { - checker_->UncheckedCastableResult().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}}); + if (UncheckedCast() && !node_->HasAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF)) { + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->UncheckedCastableResult().Insert(key, {result_, RelationType::UNCHECKED_CASTABLE}); } return true; @@ -215,21 +202,44 @@ bool TypeRelation::IsCastableTo(Type *const source, Type *const target) bool TypeRelation::IsLegalBoxedPrimitiveConversion(Type *target, Type *source) { - if (!target->IsETSReferenceType() || !source->IsETSReferenceType()) { + ETSChecker *checker = this->GetChecker()->AsETSChecker(); + + if (target == nullptr || source == nullptr) { return false; } + + if (target->IsETSUnionType() && source->IsETSObjectType()) { + Type *sourceUnboxedType = checker->MaybeUnboxType(source); + if (sourceUnboxedType == nullptr || !sourceUnboxedType->IsETSPrimitiveType()) { + return false; + } + Type *boxedUnionTarget = target->AsETSUnionType()->FindUnboxableType(); + if (boxedUnionTarget == nullptr) { + return false; + } + Type *targetUnboxedType = checker->MaybeUnboxType(boxedUnionTarget); + if (targetUnboxedType == nullptr || !targetUnboxedType->IsETSPrimitiveType()) { + return false; + } + bool res = this->Result(this->IsAssignableTo(sourceUnboxedType, target)); + return res; + } + if (!target->IsETSObjectType() || !source->IsETSObjectType()) { return false; } - if (!target->AsETSObjectType()->IsBoxedPrimitive() || !source->AsETSObjectType()->IsBoxedPrimitive()) { + + if (!target->AsETSObjectType()->IsBoxedPrimitive() && !source->AsETSObjectType()->IsBoxedPrimitive()) { return false; } - ETSChecker *checker = this->GetChecker()->AsETSChecker(); - Type *targetUnboxedType = checker->MaybeUnboxType(target); Type *sourceUnboxedType = checker->MaybeUnboxType(source); + if (source->IsETSIntEnumType()) { + targetUnboxedType = checker->GlobalIntType(); + } + if (targetUnboxedType == nullptr || sourceUnboxedType == nullptr) { return false; } @@ -237,33 +247,33 @@ bool TypeRelation::IsLegalBoxedPrimitiveConversion(Type *target, Type *source) return false; } - return this->Result(this->IsAssignableTo(sourceUnboxedType, targetUnboxedType)); + bool res = this->Result(this->IsAssignableTo(sourceUnboxedType, targetUnboxedType)); + return res; } bool TypeRelation::IsSupertypeOf(Type *super, Type *sub) { - if (super == sub) { + if (LIKELY(super == sub)) { return Result(true); } - if (sub == nullptr) { return false; } + if (super->IsETSPrimitiveType() != sub->IsETSPrimitiveType()) { + return false; + } result_ = CacheLookup(super, sub, checker_->SupertypeResults(), RelationType::SUPERTYPE); if (result_ == RelationResult::CACHE_MISS) { - if (IsIdenticalTo(super, sub)) { - return true; - } - - result_ = RelationResult::FALSE; - if (super->IsSupertypeOf(this, sub), !IsTrue()) { - sub->IsSubtypeOf(this, super); + if (!IsIdenticalTo(super, sub)) { + result_ = RelationResult::FALSE; + if (super->IsSupertypeOf(this, sub), !IsTrue()) { + sub->IsSubtypeOf(this, super); + } } - if (flags_ == TypeRelationFlag::NONE) { - checker_->SupertypeResults().cached.insert({{super->Id(), sub->Id()}, {result_, RelationType::SUPERTYPE}}); - } + auto key = RelationHolder::MakeKey(super->Id(), sub->Id()); + checker_->SupertypeResults().Insert(key, {result_, RelationType::SUPERTYPE}); } return result_ == RelationResult::TRUE; diff --git a/ets2panda/checker/types/typeRelation.h b/ets2panda/checker/types/typeRelation.h index a2fa218eede477c235441499bbbf8aa81a502056..33c32e789cfada5d83580cd839f5aacf80fa48e5 100644 --- a/ets2panda/checker/types/typeRelation.h +++ b/ets2panda/checker/types/typeRelation.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H #define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H +#include #include "lexer/token/sourceLocation.h" #include "generated/tokenType.h" #include "util/ustring.h" @@ -36,7 +37,6 @@ using ENUMBITOPS_OPERATORS; enum class TypeRelationFlag : uint32_t { NONE = 0U, - NARROWING = 1U << 0U, WIDENING = 1U << 1U, BOXING = 1U << 2U, UNBOXING = 1U << 3U, @@ -62,15 +62,18 @@ enum class TypeRelationFlag : uint32_t { NO_THROW_GENERIC_TYPEALIAS = 1U << 24U, OVERRIDING_CONTEXT = 1U << 25U, IGNORE_REST_PARAM = 1U << 26U, + STRING_TO_CHAR = 1U << 27U, + OVERLOADING_CONTEXT = 1U << 28U, + NO_SUBSTITUTION_NEEDED = 1U << 29U, ASSIGNMENT_CONTEXT = WIDENING | BOXING | UNBOXING, - BRIDGE_CHECK = OVERRIDING_CONTEXT | IGNORE_TYPE_PARAMETERS | NO_RETURN_TYPE_CHECK, - CASTING_CONTEXT = NARROWING | WIDENING | BOXING | UNBOXING | UNCHECKED_CAST, + BRIDGE_CHECK = OVERRIDING_CONTEXT | IGNORE_TYPE_PARAMETERS, + CASTING_CONTEXT = WIDENING | BOXING | UNBOXING | UNCHECKED_CAST, }; -enum class RelationResult { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS, ERROR }; +enum class RelationResult : uint8_t { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS, ERROR }; -enum class RelationType { COMPARABLE, ASSIGNABLE, IDENTICAL, UNCHECKED_CASTABLE, SUPERTYPE }; +enum class RelationType : uint8_t { COMPARABLE, ASSIGNABLE, IDENTICAL, UNCHECKED_CASTABLE, SUPERTYPE }; enum class VarianceFlag { COVARIANT, CONTRAVARIANT, INVARIANT }; @@ -82,40 +85,45 @@ struct enumbitops::IsAllowedType : std namespace ark::es2panda::checker { -class RelationKey { +class RelationHolder { public: - uint64_t sourceId; - uint64_t targetId; -}; + using RelationKey = uint64_t; -class RelationKeyHasher { -public: - size_t operator()(const RelationKey &key) const noexcept + class RelationEntry { + public: + RelationResult result; + RelationType type; + }; + + explicit RelationHolder(ThreadSafeArenaAllocator *allocator) : cached_(allocator->Adapter()) {} + + static RelationKey MakeKey(uint32_t sourceId, uint32_t targetId) { - return static_cast(key.sourceId ^ key.targetId); + constexpr size_t U32_NR_BITS = 32; // CC-OFF(G.NAM.03-CPP) project code style + return (static_cast(sourceId) << U32_NR_BITS) | targetId; } -}; -class RelationKeyComparator { -public: - bool operator()(const RelationKey &lhs, const RelationKey &rhs) const + const RelationEntry *Find(RelationKey key) const { - return lhs.sourceId == rhs.sourceId && lhs.targetId == rhs.targetId; + auto it = cached_.find(key); + if (it == cached_.cend()) { + return nullptr; + } + return &it->second; } -}; -class RelationEntry { -public: - RelationResult result; - RelationType type; -}; + void Insert(RelationKey key, RelationEntry entry) + { + cached_.insert({key, entry}); + } -using RelationMap = std::unordered_map; + void Clear() + { + cached_.clear(); + } -class RelationHolder { -public: - RelationMap cached; - RelationType type {}; +private: + ArenaUnorderedMap cached_; }; class TypeRelation { @@ -135,11 +143,6 @@ public: return result_ == RelationResult::ERROR; } - bool ApplyNarrowing() const - { - return (flags_ & TypeRelationFlag::NARROWING) != 0; - } - bool ApplyWidening() const { return (flags_ & TypeRelationFlag::WIDENING) != 0; @@ -245,6 +248,7 @@ public: void IncreaseTypeRecursionCount(Type *const type) { + std::lock_guard lock(mtx_); if (const auto foundType = instantiationRecursionMap_.find(type); foundType != instantiationRecursionMap_.end()) { foundType->second += 1; @@ -261,12 +265,14 @@ public: // possible to reference the correct types of it's members and methods. 2 is possibly enough, because if we // chain expressions, every one of them will be rechecked separately, thus allowing another 2 recursion. constexpr auto MAX_RECURSIVE_TYPE_INST = 2; + std::lock_guard lock(mtx_); const auto foundType = instantiationRecursionMap_.find(type); return foundType == instantiationRecursionMap_.end() ? true : (foundType->second < MAX_RECURSIVE_TYPE_INST); } void DecreaseTypeRecursionCount(Type *const type) { + std::lock_guard lock(mtx_); const auto foundType = instantiationRecursionMap_.find(type); if (foundType == instantiationRecursionMap_.end()) { return; @@ -306,7 +312,6 @@ public: void RaiseError(const diagnostic::DiagnosticKind &kind, const lexer::SourcePosition &loc) const; void RaiseError(const diagnostic::DiagnosticKind &kind, const util::DiagnosticMessageParams &list, const lexer::SourcePosition &loc) const; - void LogError(const util::DiagnosticMessageParams &list, const lexer::SourcePosition &loc) const; bool Result(bool res) { @@ -342,6 +347,7 @@ private: RelationResult CacheLookup(const Type *source, const Type *target, const RelationHolder &holder, RelationType type) const; + std::mutex mtx_; Checker *checker_; RelationResult result_ {}; TypeRelationFlag flags_ {}; diff --git a/ets2panda/compiler/base/condition.cpp b/ets2panda/compiler/base/condition.cpp index dc0b40e8dcf4da57ec0d3704d5ef263e206e8a11..e0ebfb209d394f0c6fa1371fc705cf0490346194 100644 --- a/ets2panda/compiler/base/condition.cpp +++ b/ets2panda/compiler/base/condition.cpp @@ -15,6 +15,7 @@ #include "condition.h" +#include "checker/ETSAnalyzerHelpers.h" #include "compiler/core/pandagen.h" #include "compiler/core/ETSGen.h" #include "ir/expressions/assignmentExpression.h" diff --git a/ets2panda/compiler/base/lreference.cpp b/ets2panda/compiler/base/lreference.cpp index ba3a65053834728ae02975fe54c99bd8b9b9bf70..852eb72ba2fbe9f75eaa01bd76326b106a6a5c3c 100644 --- a/ets2panda/compiler/base/lreference.cpp +++ b/ets2panda/compiler/base/lreference.cpp @@ -187,8 +187,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()) && - !staticObjRef_->IsETSDynamicType()) { + if (!memberExpr->IsComputed() && memberExpr->PropVar() != nullptr && + etsg_->Checker()->IsVariableStatic(memberExpr->PropVar())) { return; } @@ -201,7 +201,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_); } @@ -285,8 +286,12 @@ void ETSLReference::SetValueComputed(const ir::MemberExpression *memberExpr) con { const auto *const objectType = memberExpr->Object()->TsType(); - if (objectType->IsETSDynamicType()) { - etsg_->StoreElementDynamic(Node(), baseReg_, propReg_); + if (objectType->IsETSAnyType()) { + if (memberExpr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg_->StoreByIndexAny(memberExpr, baseReg_, propReg_); + } else { + etsg_->StoreByValueAny(memberExpr, baseReg_, propReg_); + } return; } @@ -299,23 +304,26 @@ void ETSLReference::SetValueComputed(const ir::MemberExpression *memberExpr) con return; } - ES2PANDA_ASSERT(objectType->IsETSArrayType() || objectType->IsETSResizableArrayType()); - auto vRegtype = etsg_->GetVRegType(baseReg_); - ES2PANDA_ASSERT(vRegtype != nullptr); - auto *elementType = vRegtype->IsETSArrayType() ? vRegtype->AsETSArrayType()->ElementType() - : vRegtype->AsETSResizableArrayType()->ElementType(); - etsg_->StoreArrayElement(Node(), baseReg_, propReg_, elementType); + if (objectType->IsETSArrayType() || objectType->IsETSResizableArrayType()) { + auto vRegtype = etsg_->GetVRegType(baseReg_); + ES2PANDA_ASSERT(vRegtype != nullptr); + auto *elementType = vRegtype->IsETSArrayType() ? vRegtype->AsETSArrayType()->ElementType() + : vRegtype->AsETSResizableArrayType()->ElementType(); + etsg_->StoreArrayElement(Node(), baseReg_, propReg_, elementType); + return; + } + + ES2PANDA_ASSERT(objectType->IsETSNeverType()); // nothing to do, we're in dead code anyway } void ETSLReference::SetValueGetterSetter(const ir::MemberExpression *memberExpr) const { ES2PANDA_ASSERT(memberExpr->PropVar() != nullptr); const auto *sig = memberExpr->PropVar()->TsType()->AsETSFunctionType()->FindSetter(); - ES2PANDA_ASSERT(sig != nullptr); + ES2PANDA_ASSERT(sig->Function() != nullptr); auto argReg = etsg_->AllocReg(); etsg_->StoreAccumulator(Node(), argReg); - if (sig->Function()->IsStatic()) { etsg_->CallExact(Node(), sig->InternalName(), argReg); } else if (memberExpr->Object()->IsSuperExpression()) { @@ -334,6 +342,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); @@ -344,6 +353,11 @@ void ETSLReference::SetValue() const return; } + if (objectType->IsETSAnyType()) { + etsg_->StorePropertyByNameAny(memberExpr, baseReg_, memberExpr->Property()->AsIdentifier()->Name()); + return; + } + ES2PANDA_ASSERT(memberExpr->PropVar() != nullptr); if (memberExpr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { SetValueGetterSetter(memberExpr); @@ -354,19 +368,8 @@ void ETSLReference::SetValue() const if (memberExpr->PropVar()->HasFlag(varbinder::VariableFlags::STATIC)) { const util::StringView fullName = etsg_->FormClassPropReference(staticObjRef_->AsETSObjectType(), propName); - if (staticObjRef_->IsETSDynamicType()) { - etsg_->StorePropertyDynamic(Node(), memberExprTsType, baseReg_, propName); - } else { - etsg_->StoreStaticProperty(Node(), memberExprTsType, fullName); - } - - return; - } - - auto const *objectType = memberExpr->Object()->TsType(); + etsg_->StoreStaticProperty(Node(), memberExprTsType, fullName); - if (objectType->IsETSDynamicType()) { - etsg_->StorePropertyDynamic(Node(), memberExprTsType, baseReg_, propName); return; } diff --git a/ets2panda/compiler/core/CFG.cpp b/ets2panda/compiler/core/CFG.cpp index bcef1b23a3f6fd36cf44548a27313bcb22e38d20..68bb2d2a638f7289c7464cfe2ccb609606337851 100644 --- a/ets2panda/compiler/core/CFG.cpp +++ b/ets2panda/compiler/core/CFG.cpp @@ -32,6 +32,7 @@ size_t CFG::BasicBlock::AddNode(ir::AstNode *node) std::pair CFG::BasicBlock::AddSuccessor(BasicBlock *successor) { + ES2PANDA_ASSERT(successor != nullptr); succs_.push_back(successor); ES2PANDA_ASSERT(successor != nullptr); successor->preds_.push_back(this); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 9eec575447344449f3057650db98ec23dcbcfa8a..ca3005aa0facbd78a24e1fb1f649f4a5aee093ca 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -16,15 +16,18 @@ #include "ETSCompiler.h" #include "compiler/base/catchTable.h" -#include "checker/ets/dynamic/dynamicCall.h" #include "compiler/base/condition.h" -#include "compiler/core/ETSGen-inl.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/etsDynamicFunctionType.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 { @@ -53,15 +56,11 @@ void ETSCompiler::Compile(const ir::ClassProperty *st) const auto ttctx = compiler::TargetTypeContext(etsg, st->TsType()); compiler::RegScope rs(etsg); - ir::BoxingUnboxingFlags flags = - (st->Value() != nullptr) ? st->Value()->GetBoxingUnboxingFlags() : ir::BoxingUnboxingFlags::NONE; - if (st->Value() == nullptr) { etsg->LoadDefaultValue(st, st->TsType()); } else { st->Value()->Compile(etsg); etsg->ApplyConversion(st->Value(), st->TsType()); - st->Value()->SetBoxingUnboxingFlags(flags); } if (st->IsStatic()) { @@ -138,8 +137,8 @@ void ETSCompiler::Compile(const ir::ETSNewArrayInstanceExpression *expr) const if (expr->Signature() != nullptr) { const compiler::TargetTypeContext ttctx2(etsg, elementType); - ArenaVector arguments(GetCodeGen()->Allocator()->Adapter()); - etsg->InitObject(expr, expr->Signature(), arguments); + static const ArenaVector ARGUMENTS(GetCodeGen()->Allocator()->Adapter()); + etsg->InitObject(expr, expr->Signature(), ARGUMENTS); } else { etsg->LoadAccumulatorPoison(expr, elementType); } @@ -156,41 +155,6 @@ void ETSCompiler::Compile(const ir::ETSNewArrayInstanceExpression *expr) const ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); } -static std::pair LoadDynamicName(compiler::ETSGen *etsg, const ir::AstNode *node, - const ArenaVector &dynName, bool isConstructor) -{ - auto *checker = const_cast(etsg->Checker()->AsETSChecker()); - auto *callNames = checker->DynamicCallNames(isConstructor); - - auto qnameStart = etsg->AllocReg(); - auto qnameLen = etsg->AllocReg(); - - TargetTypeContext ttctx(etsg, nullptr); // without this ints will be cast to JSValue - etsg->LoadAccumulatorInt(node, callNames->at(dynName)); - etsg->StoreAccumulator(node, qnameStart); - etsg->LoadAccumulatorInt(node, dynName.size()); - etsg->StoreAccumulator(node, qnameLen); - return {qnameStart, qnameLen}; -} - -static void CreateDynamicObject(const ir::AstNode *node, compiler::ETSGen *etsg, const ir::Expression *typeRef, - checker::Signature *signature, const ArenaVector &arguments) -{ - auto objReg = etsg->AllocReg(); - - auto callInfo = checker::DynamicCall::ResolveCall(etsg->VarBinder(), typeRef); - if (callInfo.obj->IsETSImportDeclaration()) { - etsg->LoadAccumulatorDynamicModule(node, callInfo.obj->AsETSImportDeclaration()); - } else { - callInfo.obj->Compile(etsg); - } - - etsg->StoreAccumulator(node, objReg); - - auto [qnameStart, qnameLen] = LoadDynamicName(etsg, node, callInfo.name, true); - etsg->CallDynamic(ETSGen::CallDynamicData {node, objReg, qnameStart}, qnameLen, signature, arguments); -} - static void ConvertRestArguments(checker::ETSChecker *const checker, const ir::ETSNewClassInstanceExpression *expr) { if (expr->GetSignature()->RestVar() != nullptr && (expr->GetSignature()->RestVar()->TsType()->IsETSArrayType() || @@ -258,11 +222,7 @@ static void HandleUnionTypeInForOf(compiler::ETSGen *etsg, checker::Type const * } else if (currentType->IsETSResizableArrayType()) { etsg->LoadResizableArrayElement(st, unionReg, *countReg); } else { - etsg->LoadStringChar(st, unionReg, *countReg); - // NOTE(vpukhov): #20510 use a single unboxing convertor - etsg->ApplyCastToBoxingFlags(st, ir::BoxingUnboxingFlags::BOX_TO_CHAR); - etsg->EmitBoxingConversion(ir::BoxingUnboxingFlags::BOX_TO_CHAR, st); - etsg->CastToChar(st); + etsg->LoadStringChar(st, unionReg, *countReg, true); } } @@ -289,15 +249,17 @@ static void GetSizeInForOf(compiler::ETSGen *etsg, checker::Type const *const ex void ETSCompiler::Compile(const ir::ETSNewClassInstanceExpression *expr) const { ETSGen *etsg = GetETSGen(); - if (expr->TsType()->IsETSDynamicType()) { + + if (expr->TsType()->IsETSAnyType()) { compiler::RegScope rs(etsg); - auto *name = expr->GetTypeRef(); - CreateDynamicObject(expr, etsg, name, expr->signature_, expr->GetArguments()); + 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()); } @@ -335,6 +297,11 @@ void ETSCompiler::Compile(const ir::ETSTypeReferencePart *node) const ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), node->TsType())); } +void ETSCompiler::Compile(const ir::OpaqueTypeNode *node) const +{ + GetETSGen()->SetAccumulatorType(node->TsType()); +} + void ETSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *node) const { ES2PANDA_UNREACHABLE(); @@ -524,6 +491,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); @@ -534,13 +512,10 @@ static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression expr->Left()->Compile(etsg); etsg->ApplyConversionAndStoreAccumulator(expr->Left(), lhs, expr->OperationType()); - if (expr->Left()->TsType()->IsETSDynamicType() || expr->Right()->TsType()->IsETSDynamicType()) { - auto rhs = etsg->AllocReg(); - expr->Right()->Compile(etsg); - etsg->StoreAccumulator(expr, rhs); - etsg->IsInstanceDynamic(expr, lhs, rhs); + auto target = expr->Right()->TsType(); + if (target->IsETSAnyType() && target->AsETSAnyType()->IsRelaxedAny()) { + CompileAnyInstanceOf(etsg, lhs, expr->Right()); } else { - auto target = expr->Right()->TsType(); etsg->IsInstance(expr, lhs, target); } ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->TsType())); @@ -638,7 +613,7 @@ void ETSCompiler::Compile(const ir::BinaryExpression *expr) const compiler::VReg lhs = etsg->AllocReg(); if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS && expr->OperationType()->IsETSStringType()) { - etsg->BuildString(expr); + etsg->BuildString(expr, lhs); return; } @@ -702,46 +677,56 @@ void ETSCompiler::Compile(const ir::BlockExpression *expr) const expr->Scope()->SetParent(oldParent); } -void ETSCompiler::CompileDynamic(const ir::CallExpression *expr, compiler::VReg &calleeReg) const +bool IsCastCallName(util::StringView name) { - ETSGen *etsg = GetETSGen(); - auto callInfo = checker::DynamicCall::ResolveCall(etsg->VarBinder(), expr->Callee()); - if (callInfo.obj->IsETSImportDeclaration()) { - etsg->LoadAccumulatorDynamicModule(expr, callInfo.obj->AsETSImportDeclaration()); - } else { - callInfo.obj->Compile(etsg); - } - etsg->StoreAccumulator(expr, calleeReg); - - if (!callInfo.name.empty()) { - auto [qnameStart, qnameLen] = LoadDynamicName(etsg, expr, callInfo.name, false); - etsg->CallDynamic(ETSGen::CallDynamicData {expr, calleeReg, qnameStart}, qnameLen, expr->Signature(), - expr->Arguments()); - } else { - compiler::VReg dynParam2 = etsg->AllocReg(); + return name == Signatures::BYTE_CAST || name == Signatures::SHORT_CAST || name == Signatures::INT_CAST || + name == Signatures::LONG_CAST || name == Signatures::FLOAT_CAST || name == Signatures::DOUBLE_CAST || + name == Signatures::CHAR_CAST; +} - auto lang = expr->Callee()->TsType()->IsETSDynamicFunctionType() - ? expr->Callee()->TsType()->AsETSDynamicFunctionType()->Language() - : expr->Callee()->TsType()->AsETSDynamicType()->Language(); - etsg->LoadUndefinedDynamic(expr, lang); - etsg->StoreAccumulator(expr, dynParam2); - etsg->CallDynamic(ETSGen::CallDynamicData {expr, calleeReg, dynParam2}, expr->Signature(), expr->Arguments()); - } - etsg->SetAccumulatorType(expr->Signature()->ReturnType()); +bool IsCastCall(checker::Signature *signature) +{ + ES2PANDA_ASSERT(signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); + auto *func = signature->Function(); + return (func->Parent()->Parent()->IsMethodDefinition() && IsCastCallName(func->Id()->Name()) && + util::Helpers::ContainingClass(func)->AsETSObjectType()->IsBoxedPrimitive() && + (signature->Params().size() == 1) && signature->Params()[0]->TsType()->IsETSPrimitiveType()); +} - if (etsg->GetAccumulatorType() != expr->TsType()) { - etsg->ApplyConversion(expr, expr->TsType()); +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); } + etsg->EmitAnyCheckCast(expr, expr->TsType()); } void ETSCompiler::EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const { ETSGen *etsg = GetETSGen(); - if (expr->Callee()->GetBoxingUnboxingFlags() != ir::BoxingUnboxingFlags::NONE) { - etsg->ApplyConversionAndStoreAccumulator(expr->Callee(), calleeReg, nullptr); - } if (signature->HasSignatureFlag(checker::SignatureFlags::STATIC)) { + if (IsCastCall(signature)) { + ES2PANDA_ASSERT(expr->Arguments().size() == 1); + auto *param = expr->Arguments()[0]; + param->Compile(etsg); + + const auto *const targetType = etsg->Checker()->GetApparentType(expr->TsType()); + auto ttctx = compiler::TargetTypeContext(etsg, nullptr); + CompileCastPrimitives(param, targetType); + return; + } etsg->CallExact(expr, expr->Signature(), expr->Arguments()); } else if (expr->Callee()->IsMemberExpression()) { auto me = expr->Callee()->AsMemberExpression(); @@ -773,14 +758,16 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const auto const callee = expr->Callee(); checker::Signature *const signature = expr->Signature(); + ES2PANDA_ASSERT(signature != nullptr); + ES2PANDA_ASSERT(!callee->TsType()->IsETSArrowType()); // should have been lowered bool const isStatic = signature->HasSignatureFlag(checker::SignatureFlags::STATIC); ConvertRestArguments(const_cast(etsg->Checker()->AsETSChecker()), expr, signature); - if (callee->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)) { - CompileDynamic(expr, calleeReg); + if (expr->IsDynamicCall()) { + CompileAny(expr, callee, calleeReg); } else if (callee->IsIdentifier()) { if (!isStatic) { etsg->LoadThis(expr); @@ -801,10 +788,6 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const } else { ES2PANDA_UNREACHABLE(); } - - if (expr->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG | ir::BoxingUnboxingFlags::BOXING_FLAG)) { - etsg->ApplyConversion(expr, expr->TsType()); - } } void ETSCompiler::Compile(const ir::ConditionalExpression *expr) const @@ -858,15 +841,15 @@ void ETSCompiler::Compile(const ir::Identifier *expr) const etsg->SetAccumulatorType(smartType); } -static void LoadETSDynamicTypeFromMemberExpr(compiler::ETSGen *etsg, const ir::MemberExpression *expr, - compiler::VReg objReg) +static void LoadETSAnyTypeFromMemberExpr(compiler::ETSGen *etsg, const ir::MemberExpression *expr, + compiler::VReg objReg) { - if (etsg->Checker()->AsETSChecker()->Relation()->IsSupertypeOf(etsg->Checker()->GlobalBuiltinETSStringType(), - expr->Property()->TsType())) { - etsg->LoadPropertyDynamic(expr, expr->TsType(), objReg, expr->Property()); + if (expr->Property()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_NUMERIC)) { + etsg->LoadByIndexAny(expr, objReg); } else { - etsg->LoadElementDynamic(expr, objReg); + etsg->LoadByValueAny(expr, objReg); } + etsg->EmitAnyCheckCast(expr, expr->TsType()); } bool ETSCompiler::CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr) @@ -894,8 +877,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->IsETSDynamicType()) { - LoadETSDynamicTypeFromMemberExpr(etsg, expr, objReg); + } else if (objectType->IsETSAnyType()) { + LoadETSAnyTypeFromMemberExpr(etsg, expr, objReg); } else { ES2PANDA_ASSERT(objectType->IsETSArrayType()); etsg->LoadArrayElement(expr, objReg); @@ -908,6 +891,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(); @@ -918,6 +918,10 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const return; } + if (CompileAny(etsg, expr)) { + return; + } + if (HandleArrayTypeLengthProperty(expr, etsg)) { return; } @@ -939,18 +943,30 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); ES2PANDA_ASSERT(expr->PropVar()->TsType() != nullptr); const checker::Type *const variableType = expr->PropVar()->TsType(); + ES2PANDA_ASSERT(variableType != nullptr); if (variableType->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { if (expr->Object()->IsSuperExpression()) { etsg->CallExact(expr, variableType->AsETSFunctionType()->FindGetter()->InternalName(), objReg); } else { etsg->CallVirtual(expr, variableType->AsETSFunctionType()->FindGetter(), objReg); } - } else if (objectType->IsETSDynamicType()) { - etsg->LoadPropertyDynamic(expr, expr->TsType(), objReg, propName); } else if (objectType->IsETSUnionType()) { etsg->LoadPropertyByName(expr, objReg, checker::ETSChecker::FormNamedAccessMetadata(expr->PropVar())); } else { - const auto fullName = etsg->FormClassPropReference(objectType->AsETSObjectType(), propName); + auto *id = expr->Property()->AsIdentifier(); + auto *var = id->Variable(); + ES2PANDA_ASSERT(var != nullptr && var->Declaration() != nullptr); + + auto *decl = var->Declaration(); + ES2PANDA_ASSERT(decl->Node() != nullptr); + + auto *declNode = decl->Node(); + ES2PANDA_ASSERT(declNode->Parent() != nullptr && declNode->Parent()->IsTyped()); + + const checker::Type *expectedObjType = declNode->Parent()->AsTyped()->TsType(); + ES2PANDA_ASSERT(expectedObjType != nullptr && expectedObjType->IsETSObjectType()); + + const auto fullName = etsg->FormClassPropReference(expectedObjType->AsETSObjectType(), propName); etsg->LoadProperty(expr, variableType, objReg, fullName); } etsg->GuardUncheckedType(expr, expr->UncheckedType(), expr->TsType()); @@ -1003,15 +1019,25 @@ bool ETSCompiler::HandleStaticProperties(const ir::MemberExpression *expr, ETSGe return false; } +void ETSCompiler::Compile(const ir::ETSIntrinsicNode *expr) const +{ + ETSGen *etsg = GetETSGen(); + // Note (daizihan): #27074, make it more scalable when IntrinsicNodeType is extended. + if (expr->Type() == ir::IntrinsicNodeType::TYPE_REFERENCE) { + // Note (daizihan): #27086, we should not use stringLiteral as argument in ETSIntrinsicNode, should be TypeNode. + etsg->EmitLdaType(expr, expr->Arguments()[0]->AsStringLiteral()->Str()); + etsg->SetAccumulatorType(expr->TsType()); + return; + } + ES2PANDA_UNREACHABLE(); +} + void ETSCompiler::Compile(const ir::ObjectExpression *expr) const { ETSGen *etsg = GetETSGen(); compiler::RegScope rs {etsg}; compiler::VReg objReg = etsg->AllocReg(); - // NOTE: object expressions of dynamic type are not handled in objectLiteralLowering phase - ES2PANDA_ASSERT(expr->TsType()->IsETSDynamicType()); - auto *signatureInfo = etsg->Allocator()->New(etsg->Allocator()); auto *createObjSig = etsg->Allocator()->New(signatureInfo, nullptr, nullptr); ES2PANDA_ASSERT(createObjSig != nullptr); @@ -1040,11 +1066,7 @@ void ETSCompiler::Compile(const ir::ObjectExpression *expr) const value->Compile(etsg); etsg->ApplyConversion(value, key->TsType()); - if (expr->TsType()->IsETSDynamicType()) { - etsg->StorePropertyDynamic(expr, value->TsType(), objReg, pname); - } else { - etsg->StoreProperty(expr, key->TsType(), objReg, pname); - } + etsg->StoreProperty(expr, key->TsType(), objReg, pname); } etsg->LoadAccumulator(expr, objReg); @@ -1421,9 +1443,9 @@ void ETSCompiler::Compile(const ir::ReturnStatement *st) const return; } - auto ttctx = compiler::TargetTypeContext(etsg, etsg->ReturnType()); + auto ttctx = compiler::TargetTypeContext(etsg, st->ReturnType()); argument->Compile(etsg); - etsg->ApplyConversion(argument, etsg->ReturnType()); + etsg->ApplyConversion(argument, st->ReturnType()); if (etsg->ExtendWithFinalizer(st->Parent(), st)) { return; @@ -1448,7 +1470,7 @@ static void CompileImpl(const ir::SwitchStatement *self, ETSGen *etsg) compiler::VReg tag = etsg->AllocReg(); builder.CompileTagOfSwitch(tag); - uint32_t defaultIndex = 0; + int32_t defaultIndex = -1; for (size_t i = 0; i < self->Cases().size(); i++) { const auto *clause = self->Cases()[i]; @@ -1461,7 +1483,7 @@ static void CompileImpl(const ir::SwitchStatement *self, ETSGen *etsg) builder.JumpIfCase(tag, i); } - if (defaultIndex > 0) { + if (defaultIndex >= 0) { builder.JumpToDefault(defaultIndex); } else { builder.Break(); @@ -1535,6 +1557,7 @@ void ETSCompiler::Compile(const ir::VariableDeclarator *st) const etsg->LoadDefaultValue(st, st->Id()->AsIdentifier()->Variable()->TsType()); } + // ????????????????????????????????????? is this meaningfull???? etsg->ApplyConversion(st, st->TsType()); lref.SetValue(); } @@ -1577,46 +1600,9 @@ void ETSCompiler::Compile(const ir::TSArrayType *node) const etsg->LoadAccumulatorPoison(node, node->TsType()); } -void ETSCompiler::CompileCastUnboxable(const ir::TSAsExpression *expr) const +void ETSCompiler::CompileCastPrimitives(const ir::Expression *expr, checker::Type const *targetType) const { ETSGen *etsg = GetETSGen(); - auto *targetType = etsg->Checker()->GetApparentType(expr->TsType()); - ES2PANDA_ASSERT(targetType != nullptr && targetType->IsETSObjectType()); - - switch (targetType->AsETSObjectType()->UnboxableKind()) { - case checker::ETSObjectFlags::BUILTIN_BOOLEAN: - etsg->CastToBoolean(expr); - break; - case checker::ETSObjectFlags::BUILTIN_BYTE: - etsg->CastToByte(expr); - break; - case checker::ETSObjectFlags::BUILTIN_CHAR: - etsg->CastToChar(expr); - break; - case checker::ETSObjectFlags::BUILTIN_SHORT: - etsg->CastToShort(expr); - break; - case checker::ETSObjectFlags::BUILTIN_INT: - etsg->CastToInt(expr); - break; - case checker::ETSObjectFlags::BUILTIN_LONG: - etsg->CastToLong(expr); - break; - case checker::ETSObjectFlags::BUILTIN_FLOAT: - etsg->CastToFloat(expr); - break; - case checker::ETSObjectFlags::BUILTIN_DOUBLE: - etsg->CastToDouble(expr); - break; - default: - ES2PANDA_UNREACHABLE(); - } -} - -void ETSCompiler::CompileCastPrimitives(const ir::TSAsExpression *expr) const -{ - ETSGen *etsg = GetETSGen(); - auto *targetType = etsg->Checker()->GetApparentType(expr->TsType()); switch (checker::ETSChecker::TypeKind(targetType)) { case checker::TypeFlag::ETS_BOOLEAN: { @@ -1655,12 +1641,25 @@ void ETSCompiler::CompileCastPrimitives(const ir::TSAsExpression *expr) const ES2PANDA_UNREACHABLE(); } } + etsg->SetAccumulatorType(targetType); +} + +// NOTE(gogabr): will be needed once we forbid as conversions +/* +static void CastIfDynamic(ETSGen *etsg, ir::Expression const *expr, checker::TypeFlag typeFlag) +{ + // CC-OFFNXT(redundant_code[C++]) tmp code + if (checker::ETSChecker::TypeKind(expr->TsType()) == checker::TypeFlag::ETS_DYNAMIC_TYPE) { + etsg->CastDynamicToe(expr, typeFlag); + return; + } + ES2PANDA_UNREACHABLE(); } +*/ -void ETSCompiler::CompileCast(const ir::TSAsExpression *expr) const +void ETSCompiler::CompileCast(const ir::TSAsExpression *expr, checker::Type const *targetType) const { ETSGen *etsg = GetETSGen(); - auto *targetType = etsg->Checker()->GetApparentType(expr->TsType()); switch (checker::ETSChecker::TypeKind(targetType)) { case checker::TypeFlag::ETS_ARRAY: @@ -1677,12 +1676,29 @@ void ETSCompiler::CompileCast(const ir::TSAsExpression *expr) const etsg->CastToReftype(expr, targetType, expr->isUncheckedCast_); break; } - case checker::TypeFlag::ETS_DYNAMIC_TYPE: { - etsg->CastToDynamic(expr, targetType->AsETSDynamicType()); + // NOTE(gogabr): will be needed once we forbid as conversion + /* + // CC-OFFNXT(redundant_code[C++]) tmp code + case checker::TypeFlag::DOUBLE: + case checker::TypeFlag::STRING: + case checker::TypeFlag::ETS_BOOLEAN: { + CastIfDynamic(etsg, expr->Expr(), targetType->TypeFlags()); + break; // no further conversion + } + case checker::TypeFlag::BYTE: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: + case checker::TypeFlag::LONG: + case checker::TypeFlag::FLOAT: + case checker::TypeFlag::CHAR: { + CastIfDynamic(etsg, expr->Expr(), targetType->TypeFlags()); + CompileCastPrimitives(expr, targetType); break; } + */ default: { - CompileCastPrimitives(expr); + CompileCastPrimitives(expr, targetType); + break; } } } @@ -1694,26 +1710,11 @@ void ETSCompiler::Compile(const ir::TSAsExpression *expr) const const auto *const targetType = etsg->Checker()->GetApparentType(expr->TsType()); - auto ttctx = compiler::TargetTypeContext(etsg, nullptr); - if ((expr->Expr()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U) { - etsg->ApplyUnboxingConversion(expr->Expr()); - } - - if (targetType->IsETSObjectType() && - ((expr->Expr()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U || - (expr->Expr()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0U) && - checker::ETSChecker::TypeKind(etsg->GetAccumulatorType()) != checker::TypeFlag::ETS_OBJECT) { - if (targetType->IsETSUnboxableObject()) { - CompileCastUnboxable(expr); - } - } - - if ((expr->Expr()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0U) { - etsg->ApplyBoxingConversion(expr->Expr()); + if (!etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), targetType)) { + auto ttctx = compiler::TargetTypeContext(etsg, nullptr); + CompileCast(expr, targetType); + ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), targetType)); } - - CompileCast(expr); - ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), targetType)); } void ETSCompiler::Compile([[maybe_unused]] const ir::TSInterfaceDeclaration *st) const {} @@ -1726,18 +1727,22 @@ void ETSCompiler::Compile(const ir::TSNonNullExpression *expr) const expr->Expr()->Compile(etsg); if (etsg->GetAccumulatorType()->PossiblyETSNullish()) { - auto arg = etsg->AllocReg(); - etsg->StoreAccumulator(expr, arg); - etsg->LoadAccumulator(expr, arg); + if (!etsg->GetAccumulatorType()->PossiblyETSNull()) { + etsg->EmitNullcheck(expr); + etsg->SetAccumulatorType(expr->OriginalType()); + } else { + auto arg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, arg); - auto endLabel = etsg->AllocLabel(); + auto endLabel = etsg->AllocLabel(); - etsg->BranchIfNotNullish(expr, endLabel); - etsg->EmitNullishException(expr); + etsg->BranchIfNotNullish(expr, endLabel); + etsg->EmitNullishException(expr); - etsg->SetLabel(expr, endLabel); - etsg->LoadAccumulator(expr, arg); - etsg->AssumeNonNullish(expr, expr->OriginalType()); + etsg->SetLabel(expr, endLabel); + etsg->LoadAccumulator(expr, arg); + etsg->AssumeNonNullish(expr, expr->OriginalType()); + } } ES2PANDA_ASSERT(etsg->Checker()->Relation()->IsIdenticalTo(etsg->GetAccumulatorType(), expr->OriginalType())); @@ -1745,4 +1750,17 @@ void ETSCompiler::Compile(const ir::TSNonNullExpression *expr) const void ETSCompiler::Compile([[maybe_unused]] const ir::TSTypeAliasDeclaration *st) const {} +void ETSCompiler::Compile(const ir::TSQualifiedName *expr) const +{ + ES2PANDA_ASSERT(expr->Left()->IsMemberExpression()); + + ETSGen *etsg = GetETSGen(); + expr->Left()->Compile(etsg); + + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr->Left(), objReg); + etsg->LoadPropertyByNameAny(expr->Left(), objReg, expr->Right()->AsIdentifier()->Name()); + etsg->EmitAnyCheckCast(expr, expr->Right()->AsIdentifier()->Variable()->TsType()); +} + } // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/core/ETSCompiler.h b/ets2panda/compiler/core/ETSCompiler.h index 94cd1e8f35b8cdbe204d965a029984772d864185..868ce81e497e15dbcb51d9a6dd65191d2249dc58 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -35,10 +35,9 @@ public: private: void GetDynamicNameParts(const ir::CallExpression *expr, ArenaVector &parts) const; - void CompileDynamic(const ir::CallExpression *expr, compiler::VReg &calleeReg) const; - void CompileCastUnboxable(const ir::TSAsExpression *expr) const; - void CompileCastPrimitives(const ir::TSAsExpression *expr) const; - void CompileCast(const ir::TSAsExpression *expr) 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; bool HandleArrayTypeLengthProperty(const ir::MemberExpression *expr, ETSGen *etsg) const; bool HandleStaticProperties(const ir::MemberExpression *expr, ETSGen *etsg) const; @@ -46,6 +45,7 @@ private: void CompileTupleCreation(const ir::ArrayExpression *tupleInitializer) const; static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr); + bool CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp index edcf0a2205a199f8fffcfa611f29d53ae946bcee..77abdb301f86cd6b76c5033bbadab54716439844 100644 --- a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp +++ b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp @@ -52,6 +52,11 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::MethodDefinition *node) con ES2PANDA_UNREACHABLE(); } +void ETSCompiler::Compile([[maybe_unused]] const ir::OverloadDeclaration *node) const +{ + ES2PANDA_UNREACHABLE(); +} + void ETSCompiler::Compile([[maybe_unused]] const ir::Property *expr) const { ES2PANDA_UNREACHABLE(); @@ -193,11 +198,6 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::OmittedExpression *expr) co ES2PANDA_UNREACHABLE(); } -void ETSCompiler::Compile([[maybe_unused]] const ir::OpaqueTypeNode *node) const -{ - ES2PANDA_UNREACHABLE(); -} - void ETSCompiler::Compile([[maybe_unused]] const ir::BrokenTypeNode *node) const { ES2PANDA_UNREACHABLE(); @@ -424,11 +424,6 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::TSParenthesizedType *node) ES2PANDA_UNREACHABLE(); } -void ETSCompiler::Compile([[maybe_unused]] const ir::TSQualifiedName *expr) const -{ - ES2PANDA_UNREACHABLE(); -} - void ETSCompiler::Compile([[maybe_unused]] const ir::TSStringKeyword *node) const { ES2PANDA_UNREACHABLE(); diff --git a/ets2panda/compiler/core/ETSGen.cpp b/ets2panda/compiler/core/ETSGen.cpp index 040790ec1bbe6291835f0531d64e083dda06ee76..5d2309adfc26f256187fe0b4765b793858193941 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -13,12 +13,15 @@ * limitations under the License. */ -#include "ETSGen.h" #include "ETSGen-inl.h" +#include "compiler/core/codeGen.h" +#include "compiler/core/regScope.h" +#include "generated/isa.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" +#include "ir/expression.h" #include "ir/statement.h" #include "ir/expressions/assignmentExpression.h" #include "ir/expressions/identifier.h" @@ -30,14 +33,15 @@ #include "ir/statements/continueStatement.h" #include "ir/statements/tryStatement.h" #include "ir/ts/tsInterfaceDeclaration.h" -#include "varbinder/variableFlags.h" #include "compiler/base/lreference.h" #include "compiler/base/catchTable.h" #include "compiler/core/dynamicContext.h" +#include "macros.h" +#include "util/es2pandaMacros.h" #include "varbinder/ETSBinder.h" #include "varbinder/variable.h" #include "checker/types/type.h" -#include "checker/types/typeFlag.h" +#include "checker/types/signature.h" #include "checker/checker.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsObjectType.h" @@ -98,7 +102,7 @@ void ETSGen::CompileAndCheck(const ir::Expression *expr) const checker::ETSChecker *ETSGen::Checker() const noexcept { - return Context()->checker->AsETSChecker(); + return Context()->GetChecker()->AsETSChecker(); } const varbinder::ETSBinder *ETSGen::VarBinder() const noexcept @@ -238,73 +242,8 @@ void ETSGen::LoadAccumulatorPoison(const ir::AstNode *node, const checker::Type SetAccumulatorType(type); } -util::StringView ETSGen::FormDynamicModulePropReference(const varbinder::Variable *var) -{ - ES2PANDA_ASSERT(VarBinder()->IsDynamicModuleVariable(var) || VarBinder()->IsDynamicNamespaceVariable(var)); - - auto *data = VarBinder()->DynamicImportDataForVar(var); - ES2PANDA_ASSERT(data != nullptr); - - auto *import = data->import; - - return FormDynamicModulePropReference(import); -} - -void ETSGen::LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import) -{ - ES2PANDA_ASSERT(import->Language().IsDynamic()); - LoadStaticProperty(node, Checker()->GlobalBuiltinDynamicType(import->Language()), - FormDynamicModulePropReference(import)); -} - -util::StringView ETSGen::FormDynamicModulePropReference(const ir::ETSImportDeclaration *import) -{ - std::stringstream ss; - ss << VarBinder()->Program()->ModulePrefix(); - ss << compiler::Signatures::DYNAMIC_MODULE_CLASS; - ss << '.'; - ss << import->AssemblerName(); - return util::UString(ss.str(), Allocator()).View(); -} - -void ETSGen::LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *const var) -{ - RegScope rs(this); - - auto *data = VarBinder()->DynamicImportDataForVar(var); - ES2PANDA_ASSERT(data != nullptr); - - auto *import = data->import; - - LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var)); - - auto objReg = AllocReg(); - StoreAccumulator(node, objReg); - - auto *id = data->specifier->AsImportSpecifier()->Imported(); - auto lang = import->Language(); - LoadPropertyDynamic(node, Checker()->GlobalBuiltinDynamicType(lang), objReg, id->Name()); - - ApplyConversion(node); -} - -void ETSGen::LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *const var) -{ - LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var)); -} - void ETSGen::LoadVar(const ir::Identifier *node, varbinder::Variable const *const var) { - if (VarBinder()->IsDynamicModuleVariable(var)) { - LoadDynamicModuleVariable(node, var); - return; - } - - if (VarBinder()->IsDynamicNamespaceVariable(var)) { - LoadDynamicNamespaceVariable(node, var); - return; - } - auto *local = var->AsLocalVariable(); switch (ETSLReference::ResolveReferenceKind(var)) { @@ -396,12 +335,30 @@ void ETSGen::StoreStaticProperty(const ir::AstNode *const node, const checker::T } } +static bool StaticAccessRequiresReferenceSafetyCheck(const ir::AstNode *const node, const checker::Type *propType) +{ + if (propType->PossiblyETSUndefined()) { + return false; + } + auto parent = node->Parent(); + if (parent->IsMemberExpression()) { + return false; + } + if (parent->IsCallExpression() && parent->AsCallExpression()->Callee() == node) { + return false; + } + return true; +} + void ETSGen::LoadStaticProperty(const ir::AstNode *const node, const checker::Type *propType, const util::StringView &fullName) { ES2PANDA_ASSERT(propType != nullptr); if (propType->IsETSReferenceType()) { Sa().Emit(node, fullName); + if (StaticAccessRequiresReferenceSafetyCheck(node, propType)) { + EmitNullcheck(node); + } } else if (IsWidePrimitiveType(propType)) { Sa().Emit(node, fullName); } else { @@ -427,6 +384,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) { @@ -480,141 +453,46 @@ void ETSGen::LoadPropertyByName([[maybe_unused]] const ir::AstNode *const node, #endif // PANDA_WITH_ETS } -void ETSGen::StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, - const util::StringView &propName) -{ - ES2PANDA_ASSERT(GetVRegType(objReg) != nullptr); - auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language(); - std::string_view methodName {}; - if (propType->IsETSBooleanType()) { - methodName = Signatures::Dynamic::SetPropertyBooleanBuiltin(lang); - } else if (propType->IsByteType()) { - methodName = Signatures::Dynamic::SetPropertyByteBuiltin(lang); - } else if (propType->IsCharType()) { - methodName = Signatures::Dynamic::SetPropertyCharBuiltin(lang); - } else if (propType->IsShortType()) { - methodName = Signatures::Dynamic::SetPropertyShortBuiltin(lang); - } else if (propType->IsIntType()) { - methodName = Signatures::Dynamic::SetPropertyIntBuiltin(lang); - } else if (propType->IsLongType()) { - methodName = Signatures::Dynamic::SetPropertyLongBuiltin(lang); - } else if (propType->IsFloatType()) { - methodName = Signatures::Dynamic::SetPropertyFloatBuiltin(lang); - } else if (propType->IsDoubleType()) { - methodName = Signatures::Dynamic::SetPropertyDoubleBuiltin(lang); - } else if (propType->IsETSStringType()) { - methodName = Signatures::Dynamic::SetPropertyStringBuiltin(lang); - } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) { - methodName = Signatures::Dynamic::SetPropertyDynamicBuiltin(lang); - // NOTE: vpukhov. add non-dynamic builtin - if (!propType->IsETSDynamicType()) { - CastToDynamic(node, Checker()->GlobalBuiltinDynamicType(lang)->AsETSDynamicType()); - } - } else { - ASSERT_PRINT(false, "Unsupported property type"); - } - - RegScope rs(this); - VReg propValueReg = AllocReg(); - VReg propNameReg = AllocReg(); - - StoreAccumulator(node, propValueReg); - - // Load property name - LoadAccumulatorString(node, propName); - StoreAccumulator(node, propNameReg); - - // Set property by name - Ra().Emit(node, methodName, objReg, propNameReg, propValueReg, dummyReg_); - SetAccumulatorType(Checker()->GlobalBuiltinJSValueType()); -} - -void ETSGen::LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, - std::variant property) -{ - ES2PANDA_ASSERT(propType != nullptr && GetVRegType(objReg) != nullptr); - auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language(); - auto *type = propType; - std::string_view methodName {}; - if (propType->IsETSBooleanType()) { - methodName = Signatures::Dynamic::GetPropertyBooleanBuiltin(lang); - } else if (propType->IsByteType()) { - methodName = Signatures::Dynamic::GetPropertyByteBuiltin(lang); - } else if (propType->IsCharType()) { - methodName = Signatures::Dynamic::GetPropertyCharBuiltin(lang); - } else if (propType->IsShortType()) { - methodName = Signatures::Dynamic::GetPropertyShortBuiltin(lang); - } else if (propType->IsIntType()) { - methodName = Signatures::Dynamic::GetPropertyIntBuiltin(lang); - } else if (propType->IsLongType()) { - methodName = Signatures::Dynamic::GetPropertyLongBuiltin(lang); - } else if (propType->IsFloatType()) { - methodName = Signatures::Dynamic::GetPropertyFloatBuiltin(lang); - } else if (propType->IsDoubleType()) { - methodName = Signatures::Dynamic::GetPropertyDoubleBuiltin(lang); - } else if (propType->IsETSStringType()) { - methodName = Signatures::Dynamic::GetPropertyStringBuiltin(lang); - } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) { - methodName = Signatures::Dynamic::GetPropertyDynamicBuiltin(lang); - type = Checker()->GlobalBuiltinDynamicType(lang); - } else { - ASSERT_PRINT(false, "Unsupported property type"); - } - +void ETSGen::StoreByIndexAny(const ir::MemberExpression *node, VReg objectReg, VReg index) +{ RegScope rs(this); - VReg propNameObject; - - if (node->IsMemberExpression() && node->AsMemberExpression()->IsComputed()) { - (std::get(property))->Compile(this); - } else { - // Load property name - LoadAccumulatorString(node, std::get(property)); - } - - propNameObject = AllocReg(); - StoreAccumulator(node, propNameObject); + // Store property by index + Ra().Emit(node, objectReg, index); + SetAccumulatorType(Checker()->GlobalVoidType()); +} - // Get property - Ra().Emit(node, methodName, objReg, propNameObject); +void ETSGen::LoadByIndexAny(const ir::MemberExpression *node, VReg objectReg) +{ + RegScope rs(this); - SetAccumulatorType(type); + VReg indexReg = AllocReg(); + StoreAccumulator(node, indexReg); - if (propType != type && !propType->IsETSDynamicType()) { - CastDynamicToObject(node, propType); - } + // Get property by index + Ra().Emit(node, objectReg); + SetAccumulatorType(node->TsType()); } -void ETSGen::StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index) +void ETSGen::StoreByValueAny(const ir::MemberExpression *node, VReg objectReg, VReg value) { - ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr); - auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language(); - std::string_view methodName = Signatures::Dynamic::SetElementDynamicBuiltin(lang); - RegScope rs(this); - VReg valueReg = AllocReg(); - StoreAccumulator(node, valueReg); - - // Set property by index - Ra().Emit(node, methodName, objectReg, index, valueReg, dummyReg_); + // Store property by value + Ra().Emit(node, objectReg, value); SetAccumulatorType(Checker()->GlobalVoidType()); } -void ETSGen::LoadElementDynamic(const ir::AstNode *node, VReg objectReg) +void ETSGen::LoadByValueAny(const ir::MemberExpression *node, VReg objectReg) { - ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr); - auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language(); - std::string_view methodName = Signatures::Dynamic::GetElementDynamicBuiltin(lang); - RegScope rs(this); - VReg indexReg = AllocReg(); - StoreAccumulator(node, indexReg); + VReg valueReg = AllocReg(); + StoreAccumulator(node, valueReg); - // Get property by index - Ra().Emit(node, methodName, objectReg, indexReg); - SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang)); + // Get property by value + Ra().Emit(node, objectReg, valueReg); + SetAccumulatorType(node->TsType()); } void ETSGen::CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, @@ -671,6 +549,18 @@ const checker::Type *ETSGen::LoadDefaultValue(const ir::AstNode *node, const che if (type->IsETSReferenceType()) { if (checker->Relation()->IsSupertypeOf(type, checker->GlobalETSUndefinedType())) { LoadAccumulatorUndefined(node); + } else if (type->IsETSObjectType() && type->AsETSObjectType()->IsBoxedPrimitive()) { + // Call default constructor for boxed primitive types. + static auto const DUMMY_ARGS = ArenaVector(checker->Allocator()->Adapter()); + auto const &signatures = type->AsETSObjectType()->ConstructSignatures(); + auto const it = std::find_if(signatures.cbegin(), signatures.cend(), [](checker::Signature *signature) { + return signature->ArgCount() == 0U && !signature->HasRestParameter(); + }); + if (it != signatures.cend()) { + InitObject(node, *it, DUMMY_ARGS); + } else { + LoadAccumulatorPoison(node, type); + } } else { LoadAccumulatorPoison(node, type); } @@ -705,92 +595,19 @@ void ETSGen::ReturnAcc(const ir::AstNode *node) } } -static bool IsNullUnsafeObjectType(checker::Type const *type) +bool ETSGen::IsNullUnsafeObjectType(checker::Type const *type) const { ES2PANDA_ASSERT(type != nullptr); - return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType(); -} - -void ETSGen::IsInstanceDynamic(const ir::BinaryExpression *const node, const VReg srcReg, - [[maybe_unused]] const VReg tgtReg) -{ - ES2PANDA_ASSERT(node->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF); - const checker::Type *lhsType = node->Left()->TsType(); - const checker::Type *rhsType = node->Right()->TsType(); - ES2PANDA_ASSERT(rhsType->IsETSDynamicType() || lhsType->IsETSDynamicType()); - - const RegScope rs(this); - if (rhsType->IsETSDynamicType()) { - ES2PANDA_ASSERT(node->Right()->TsType()->AsETSDynamicType()->HasDecl()); - if (lhsType->IsETSDynamicType()) { - VReg dynTypeReg = MoveAccToReg(node); - // Semantics: - // let dyn_val: JSValue = ... - // dyn_value instanceof DynamicDecl - // Bytecode: - // call runtime intrinsic_dynamic - CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg); - } else if (lhsType == Checker()->GlobalETSObjectType()) { - // Semantics: - // let obj: Object = ... - // obj instanceof DynamicDecl - // Bytecode: - // if isinstance : - // checkcast - // return call runtime intrinsic_dynamic - // return false - Label *ifFalse = AllocLabel(); - Language lang = rhsType->AsETSDynamicType()->Language(); - ES2PANDA_ASSERT(Checker()->GlobalBuiltinDynamicType(lang) != nullptr); - VReg dynTypeReg = MoveAccToReg(node); - LoadAccumulator(node, srcReg); - EmitIsInstance(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName()); - BranchIfFalse(node, ifFalse); - LoadAccumulator(node, srcReg); - EmitCheckCast(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName()); - CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg); - SetLabel(node, ifFalse); - } else { - // Semantics: - // let obj: EtsType = ... - // obj instanceof DynamicDecl - // Bytecode: - // False - Sa().Emit(node, 0); - } - } else { - if (lhsType->IsETSDynamicType()) { - if (rhsType == Checker()->GlobalETSObjectType()) { - // Semantics: - // let dyn_val: JSValue = ... - // dyn_val instanceof Object - // Bytecode: - // True - Sa().Emit(node, 1); - } else { - // Semantics: - // let dyn_val: JSValue = ... - // dyn_val instanceof EtsType - // Bytecode: - // lda.type + call runtime instrinsic_static - Sa().Emit(node, rhsType->AsETSObjectType()->AssemblerName()); - VReg typeReg = MoveAccToReg(node); - CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_STATIC, srcReg, typeReg); - } - } else { - ES2PANDA_UNREACHABLE(); - } - } - SetAccumulatorType(Checker()->GlobalETSBooleanType()); + auto const checker = const_cast(Checker()); + return checker->Relation()->IsSupertypeOf(checker->GetApparentType(type), checker->GlobalETSObjectType()); } // Implemented on top of the runtime type system, do not relax checks, do not introduce new types -void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple