diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md index 2ab6c799899617a7abf336004400c3e8d69cccb3..88f741c6186ee818d97a7573724c92e25b60fc81 100644 --- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -108,3 +108,8 @@ debug模式: - [ ] 不涉及 **Email:** chenqiuyao@huawei.com + +### 是否已执行L0用例 +- [ ] 已验证 +- [ ] 不涉及。如不涉及,请写明理由 + diff --git a/es2panda/lexer/token/tokenType.h b/es2panda/lexer/token/tokenType.h index 6fe7fb0f0ef21bdbfce9999104ec0aa67ec70489..45a23d0ed2344b6d356d119d746c9e442c3ce040 100644 --- a/es2panda/lexer/token/tokenType.h +++ b/es2panda/lexer/token/tokenType.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021-2022 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 @@ -142,6 +142,7 @@ enum class TokenType { KEYW_RETURN, KEYW_STRING, KEYW_SUPER, + KEYW_STRUCT, KEYW_SWITCH, KEYW_SYMBOL, KEYW_THIS, diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 42c9d7f0552227bd4afef4395e6bdd883d438bd7..e9c4dee5ec57e0abaae65fc96db6e672de60da28 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -31,6 +31,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", @@ -59,7 +65,6 @@ libes2panda_sources = [ "checker/ETSAnalyzerHelpers.cpp", "checker/ETSAnalyzerUnreachable.cpp", "checker/ETSchecker.cpp", - "checker/IsolatedDeclgenChecker.cpp", "checker/JSchecker.cpp", "checker/TSAnalyzer.cpp", "checker/TSAnalyzerUnreachable.cpp", @@ -74,7 +79,6 @@ 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", @@ -99,11 +103,11 @@ libes2panda_sources = [ "checker/types/ets/byteType.cpp", "checker/types/ets/charType.cpp", "checker/types/ets/doubleType.cpp", + "checker/types/ets/etsAnyType.cpp", "checker/types/ets/etsArrayType.cpp", "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", @@ -124,9 +128,9 @@ libes2panda_sources = [ "checker/types/ets/intType.cpp", "checker/types/ets/longType.cpp", "checker/types/ets/shortType.cpp", - "checker/types/ets/etsAnyType.cpp", "checker/types/ets/wildcardType.cpp", "checker/types/globalTypesHolder.cpp", + "checker/types/gradualType.cpp", "checker/types/signature.cpp", "checker/types/ts/anyType.cpp", "checker/types/ts/arrayType.cpp", @@ -202,6 +206,8 @@ 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", @@ -211,9 +217,10 @@ libes2panda_sources = [ "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/dynamicImport.cpp", "compiler/lowering/ets/enumLowering.cpp", "compiler/lowering/ets/enumPostCheckLowering.cpp", "compiler/lowering/ets/enumPropertiesInAnnotationsLowering.cpp", @@ -222,11 +229,12 @@ libes2panda_sources = [ "compiler/lowering/ets/expressionLambdaLowering.cpp", "compiler/lowering/ets/extensionAccessorLowering.cpp", "compiler/lowering/ets/genericBridgesLowering.cpp", + "compiler/lowering/ets/gradualTypeNarrowing.cpp", "compiler/lowering/ets/insertOptionalParametersAnnotation.cpp", "compiler/lowering/ets/interfaceObjectLiteralLowering.cpp", "compiler/lowering/ets/interfacePropertyDeclarations.cpp", - "compiler/lowering/ets/lateInitialization.cpp", "compiler/lowering/ets/lambdaLowering.cpp", + "compiler/lowering/ets/lateInitialization.cpp", "compiler/lowering/ets/localClassLowering.cpp", "compiler/lowering/ets/objectIndexAccess.cpp", "compiler/lowering/ets/objectIterator.cpp", @@ -234,6 +242,7 @@ 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", @@ -254,8 +263,6 @@ libes2panda_sources = [ "compiler/lowering/ets/typeFromLowering.cpp", "compiler/lowering/ets/unboxLowering.cpp", "compiler/lowering/ets/unionLowering.cpp", - "compiler/lowering/ets/annotationCopyLowering.cpp", - "compiler/lowering/ets/annotationCopyPostLowering.cpp", "compiler/lowering/phase.cpp", "compiler/lowering/plugin_phase.cpp", "compiler/lowering/resolveIdentifiers.cpp", @@ -288,6 +295,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", @@ -300,6 +308,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", @@ -492,6 +501,7 @@ libes2panda_sources = [ "util/helpers.cpp", "util/importPathManager.cpp", "util/path.cpp", + "util/perfMetrics.cpp", "util/plugin.cpp", "util/ustring.cpp", "varbinder/ASBinder.cpp", @@ -534,6 +544,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", @@ -542,7 +553,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", @@ -630,7 +640,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", @@ -720,6 +729,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", @@ -774,6 +784,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 = [ @@ -803,7 +816,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", @@ -813,6 +825,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", @@ -927,7 +940,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", @@ -967,6 +979,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", @@ -1041,6 +1054,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 = [ @@ -1058,6 +1074,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", ] @@ -1100,6 +1117,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", @@ -1119,6 +1137,7 @@ libes2panda_configs = [ ":libes2panda_config", "$ark_root/libpandabase:arkbase_public_config", "$ark_root/libpandafile:arkfile_public_config", + "$ark_root/abc2program:arkts_abc2program_public_config", ] libes2panda_public_configs = [ @@ -1150,10 +1169,10 @@ ohos_source_set("libes2panda_frontend_static") { ":es2panda_options_gen_options_h", ":gen_${LIB_NAME}_decl_inc", ":gen_${LIB_NAME}_enums_inc", + ":gen_${LIB_NAME}_idl", ":gen_${LIB_NAME}_impl_inc", ":gen_${LIB_NAME}_include_inc", ":gen_${LIB_NAME}_list_inc", - ":gen_${LIB_NAME}_idl", ":gen_es2panda_compiler_signatures_h", ":gen_es2panda_lexer_keywords_h", ":gen_es2panda_lexer_tokenType_h", @@ -1168,6 +1187,7 @@ ohos_source_set("libes2panda_frontend_static") { "runtime_core:libarktsbase_package", "runtime_core:libarktscompiler_package", "runtime_core:libarktsfile_package", + "runtime_core:libarktsabc2program_package", sdk_libc_secshared_dep, ] part_name = "ets_frontend" @@ -1208,7 +1228,14 @@ ohos_source_set("libes2panda_public_frontend_static") { deps = [ ":libes2panda_frontend_static" ] if (ark_standalone_build || ark_static_standalone_build) { - deps += [ "$ark_root/bytecode_optimizer:libarktsbytecodeopt_package" ] + deps += [ + "$ark_root/bytecode_optimizer:libarktsbytecodeopt_package", + "$ark_root/abc2program:libarktsabc2program_package", + "$ark_root/assembler:libarktsassembler", + "$ark_root/compiler:libarktscompiler", + "$ark_root/libpandabase:libarktsbase", + "$ark_root/libpandafile:libarktsfile", + ] } if (target_os != "win" && target_os != "mingw" && target_os != "winuwp") { @@ -1218,6 +1245,7 @@ ohos_source_set("libes2panda_public_frontend_static") { external_deps = [ "runtime_core:libarktsbytecodeopt_package", + "runtime_core:libarktsabc2program_package", sdk_libc_secshared_dep, ] part_name = "ets_frontend" @@ -1247,9 +1275,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" ] @@ -1292,3 +1320,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 695f3a76f90b267286f977370a759498dd551868..1f0676370bc241208d064691145f51d9051aded0 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -73,10 +73,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" @@ -144,8 +144,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} @@ -276,6 +276,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/expressionLambdaLowering.cpp compiler/lowering/ets/extensionAccessorLowering.cpp compiler/lowering/ets/genericBridgesLowering.cpp + compiler/lowering/ets/gradualTypeNarrowing.cpp compiler/lowering/ets/arrayLiteralLowering.cpp compiler/lowering/ets/boxingForLocals.cpp compiler/lowering/ets/capturedVariables.cpp @@ -283,12 +284,13 @@ set(ES2PANDA_LIB_SRC 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/localClassLowering.cpp @@ -306,6 +308,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 @@ -340,6 +343,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 @@ -427,6 +431,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 @@ -542,7 +547,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 @@ -553,7 +557,6 @@ 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 @@ -577,6 +580,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 @@ -587,7 +591,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 @@ -646,8 +649,9 @@ set(ES2PANDA_LIB_SRC util/helpers.cpp util/importPathManager.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 @@ -686,9 +690,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 8d77d323397036b4d93f79d93de652a9c8d5840c..6835ef0e177c1a0d670ba4032586e508f06d7e6e 100644 --- a/ets2panda/REVIEWERS +++ b/ets2panda/REVIEWERS @@ -47,7 +47,6 @@ /ets2panda/util/diagnostic* @chernykhsergey ^igelhaus ^Prof1983 /ets2panda/util/importPathManager* @dreamdoomwalker ^trubachevilya ^igelhaus ^Prof1983 /test/workload/ignored*.txt @shirunova_viktoria @kuchkovairina @gavin1012_hw @zhuheng27 -/ets2panda/checker/ets/dynamic.cpp @akmaevaleksey ^vpukhov ^igelhaus ^Prof1983 /ets2panda/checker/ets/function.cpp ^vpukhov @gogabr ^igelhaus ^Prof1983 /ets2panda/checker/ets/helpers.cpp @zelentsovdmitry ^igelhaus ^Prof1983 /ets2panda/checker/ets/object.cpp @zelentsovdmitry ^vpukhov ^igelhaus ^Prof1983 @@ -61,7 +60,6 @@ /ets2panda/test/test-lists/parser @chernykhsergey ^igelhaus ^Prof1983 /ets2panda/test/unit/lsp ^igelhaus ^Prof1983 @dreamdoomwalker @Ascnbio ^muhammet-fevzi-bayiroglu @utkugursel /ets2panda/checker/types/ets/Nullish.* ^vpukhov @gogabr ^igelhaus ^Prof1983 -/ets2panda/checker/types/ets/etsDynamicType.* ^vpukhov @gogabr ^igelhaus ^Prof1983 /ets2panda/checker/types/ets/etsFunctionType.* ^vpukhov @gogabr ^igelhaus ^Prof1983 /ets2panda/checker/types/ets/etsTypeParameter.* ^vpukhov @gogabr ^igelhaus ^Prof1983 /ets2panda/checker/types/ets/etsUnionType.* @akmaevaleksey ^vpukhov @gogabr ^igelhaus ^Prof1983 diff --git a/ets2panda/aot/BUILD.gn b/ets2panda/aot/BUILD.gn index 2a76c53b3d2214e54eb8041d586d43d357d518bf..7efccf65854505a222c43bd299160c92bb3e5e2f 100644 --- a/ets2panda/aot/BUILD.gn +++ b/ets2panda/aot/BUILD.gn @@ -36,6 +36,7 @@ ohos_executable("ets2panda") { "$ark_root/bytecode_optimizer:bytecodeopt_public_config", "$ark_root/compiler:arkcompiler_public_config", "$ark_root/runtime:arkruntime_public_config", + "$ark_root/abc2program:arkts_abc2program_public_config", ] deps = [ @@ -57,6 +58,7 @@ ohos_executable("ets2panda") { "runtime_core:libarktsbytecodeopt_package", "runtime_core:libarktscompiler_package", "runtime_core:libarktsfile_package", + "runtime_core:libarktsabc2program_package", sdk_libc_secshared_dep, ] diff --git a/ets2panda/aot/main.cpp b/ets2panda/aot/main.cpp index 411df8364a29cccacefe03a517ccd368f2658b63..510014c943a54b1d10bf95bda850eed19bf6c41b 100644 --- a/ets2panda/aot/main.cpp +++ b/ets2panda/aot/main.cpp @@ -24,6 +24,7 @@ #include "util/generateBin.h" #include "util/options.h" #include "util/plugin.h" +#include "util/perfMetrics.h" #include "libpandabase/os/stacktrace.h" #include "generated/diagnostic.h" @@ -100,8 +101,8 @@ static int CompileMultipleFiles(es2panda::Compiler &compiler, std::vectorArkTSConfig()); if (compilationList.empty()) { @@ -185,22 +186,27 @@ static int Run(Span args) return 1; } + int res; if (options->GetCompilationMode() == CompilationMode::PROJECT) { - return CompileFromConfig(compiler, options.get(), diagnosticEngine); - } - - std::string sourceFile; - std::string_view parserInput; - if (options->GetCompilationMode() == CompilationMode::GEN_STD_LIB) { - sourceFile = "etsstdlib.ets"; - parserInput = ""; + res = CompileFromConfig(compiler, options.get(), diagnosticEngine); } else { - sourceFile = options->SourceFileName(); - auto [buf, size] = options->CStrParserInputContents(); - parserInput = std::string_view(buf, size); + std::string sourceFile; + std::string_view parserInput; + if (options->GetCompilationMode() == CompilationMode::GEN_STD_LIB) { + sourceFile = "etsstdlib.ets"; + parserInput = ""; + } else { + sourceFile = options->SourceFileName(); + auto [buf, size] = options->CStrParserInputContents(); + parserInput = std::string_view(buf, size); + } + es2panda::SourceFile input(sourceFile, parserInput, options->IsModule(), options->GetOutput()); + res = CompileFromSource(compiler, input, *options.get(), diagnosticEngine); + } + if (options->IsDumpPerfMetrics()) { + util::DumpPerfMetrics(); } - es2panda::SourceFile input(sourceFile, parserInput, options->IsModule(), options->GetOutput()); - return CompileFromSource(compiler, input, *options.get(), diagnosticEngine); + return res; } } // namespace ark::es2panda::aot diff --git a/ets2panda/ast_verifier/helpers.cpp b/ets2panda/ast_verifier/helpers.cpp index d8a9998848ce9a4f5d6124d41c6587f385abb866..a2179fc411ba0b24db631a82db400ba318fc1b32 100644 --- a/ets2panda/ast_verifier/helpers.cpp +++ b/ets2panda/ast_verifier/helpers.cpp @@ -19,6 +19,7 @@ #include "checker/types/type.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsUnionType.h" +#include "checker/types/gradualType.h" #include "ir/statements/blockStatement.h" #include "ir/ets/etsModule.h" #include "parser/program/program.h" @@ -50,17 +51,17 @@ bool IsBooleanType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); + return type->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || type->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE); } bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) @@ -74,29 +75,29 @@ bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); if (IsBooleanType(ast)) { return isBitwise; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) && + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) { return true; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && - !typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) && + !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT) || - typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); + return type->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) || + type->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) || type->HasTypeFlag(checker::TypeFlag::BIGINT) || + type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL); } bool IsStringType(const ir::AstNode *ast) @@ -110,17 +111,18 @@ bool IsStringType(const ir::AstNode *ast) } auto typedAst = static_cast(ast); - if (typedAst->TsType() == nullptr) { return false; } - if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { - return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || - typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); + auto type = + typedAst->TsType()->IsGradualType() ? typedAst->TsType()->AsGradualType()->GetBaseType() : typedAst->TsType(); + if (type->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) || + type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING); } - return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE); + return type->HasTypeFlag(checker::TypeFlag::STRING_LIKE); } bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode) @@ -154,7 +156,8 @@ const checker::Type *GetClassDefinitionType(const ir::AstNode *ast) return nullptr; } auto *classDefinition = tmpNode->AsClassDefinition(); - return classDefinition->TsType(); + return classDefinition->TsType()->IsGradualType() ? classDefinition->TsType()->AsGradualType()->GetBaseType() + : classDefinition->TsType(); } const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) @@ -167,7 +170,9 @@ const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast) return nullptr; } auto *tsInterfaceDeclaration = tmpNode->AsTSInterfaceDeclaration(); - return tsInterfaceDeclaration->TsType(); + return tsInterfaceDeclaration->TsType()->IsGradualType() + ? tsInterfaceDeclaration->TsType()->AsGradualType()->GetBaseType() + : tsInterfaceDeclaration->TsType(); } bool ValidateMethodAccessForClass(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode, @@ -323,8 +328,8 @@ bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir } if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) && memberObjType->SuperType() != nullptr && - memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | - checker::ETSObjectFlags::GLOBAL)) { + memberObjType->SuperType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE | + checker::ETSObjectFlags::GLOBAL)) { return true; } const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode(); diff --git a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index bf7351956a7b3bae812a360c9c28f6a777633fae..6d6ff354876263cca9636e8da9367292bb816629 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -78,13 +78,6 @@ bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_s const ir::AstNode *callExpr, util::StringView name) { auto *signature = callExpr->AsCallExpression()->Signature(); - if (signature == nullptr || signature->Owner() == nullptr) { - // NOTE(vpukhov): Add a synthetic owner for dynamic signatures - ES2PANDA_ASSERT( - callExpr->AsCallExpression()->Callee()->TsType()->HasTypeFlag(checker::TypeFlag::ETS_DYNAMIC_FLAG)); - return true; - } - if (signature != nullptr && varCallee->Declaration() != nullptr && varCallee->Declaration()->Node() != nullptr && !IsContainedIn(varCallee->Declaration()->Node(), signature->Owner()->GetDeclNode()) && varCallee->Declaration()->Node() != signature->Owner()->GetDeclNode()) { diff --git a/ets2panda/ast_verifier/invariants/nodeHasType.cpp b/ets2panda/ast_verifier/invariants/nodeHasType.cpp index 4cfa95f5ca829f7252625b4821008f7f71411770..e9f4d335a76d94795237156775c4a8c36d1f0d32 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/bindings/BUILD.gn b/ets2panda/bindings/BUILD.gn index e6dbc5c5f4c3b2c6b686a994dcb23a4d4493a693..0e6be72fd93968839af67b16371f60a78528a488 100644 --- a/ets2panda/bindings/BUILD.gn +++ b/ets2panda/bindings/BUILD.gn @@ -22,7 +22,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", @@ -184,7 +183,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", @@ -339,24 +337,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/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/common/arkTSConfigGenerator.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 2757d4b9efc5081982efaa7c6d2959a303405bd9..b4f7bfdc24a1c97389df0e12cfda66fb76eaa250 100644 --- a/ets2panda/bindings/native/src/bridges.cpp +++ b/ets2panda/bindings/native/src/bridges.cpp @@ -37,13 +37,13 @@ KNativePointer impl_CreateContextFromString(KNativePointer configPtr, KStringPtr TS_INTEROP_3(CreateContextFromString, KNativePointer, KNativePointer, KStringPtr, KStringPtr) KInt impl_GenerateTsDeclarationsFromContext(KNativePointer contextPtr, KStringPtr &outputDeclEts, KStringPtr &outputEts, - KBoolean exportAll) + KBoolean exportAll, KBoolean isolated) { auto context = reinterpret_cast(contextPtr); - return static_cast(GetPublicImpl()->GenerateTsDeclarationsFromContext(context, outputDeclEts.data(), - outputEts.data(), exportAll != 0)); + return static_cast(GetPublicImpl()->GenerateTsDeclarationsFromContext( + context, outputDeclEts.data(), outputEts.data(), exportAll != 0, isolated != 0)); } -TS_INTEROP_4(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean) +TS_INTEROP_5(GenerateTsDeclarationsFromContext, KInt, KNativePointer, KStringPtr, KStringPtr, KBoolean, KBoolean) KNativePointer impl_CreateContextFromFile(KNativePointer configPtr, KStringPtr &filenamePtr) { @@ -66,3 +66,10 @@ KNativePointer impl_ContextErrorMessage(KNativePointer contextPtr) return new std::string(GetPublicImpl()->ContextErrorMessage(context)); } TS_INTEROP_1(ContextErrorMessage, KNativePointer, KNativePointer) + +KNativePointer impl_GetAllErrorMessages(KNativePointer contextPtr) +{ + auto context = reinterpret_cast(contextPtr); + return new std::string(GetPublicImpl()->GetAllErrorMessages(context)); +} +TS_INTEROP_1(GetAllErrorMessages, KNativePointer, KNativePointer) 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 286efef5979db72e531f34aaf04047fea3954b6e..95a5fd779514ee81c73ddd7ad7b24dc1c6bba506 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -15,20 +15,19 @@ #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; using ark::es2panda::lsp::ClassHierarchyInfo; +using ark::es2panda::lsp::ClassHierarchyItem; using ark::es2panda::lsp::ClassMethodItem; +using ark::es2panda::lsp::ClassPropertyItem; } // namespace char *GetStringCopy(KStringPtr &ptr) @@ -63,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 &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); @@ -117,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) { @@ -722,14 +913,14 @@ KNativePointer impl_getClassNameFromClassHierarchyInfo(KNativePointer info) } TS_INTEROP_1(getClassNameFromClassHierarchyInfo, KNativePointer, KNativePointer) -KNativePointer impl_getMethodListFromClassHierarchyInfo(KNativePointer info) +KNativePointer impl_getMethodItemsFromClassHierarchyInfo(KNativePointer info) { auto *infoPtr = reinterpret_cast(info); if (infoPtr == nullptr) { return nullptr; } std::vector ptrs; - for (const auto &element : infoPtr->GetMethodList()) { + for (const auto &element : infoPtr->GetMethodItemList()) { if (element.second == nullptr) { continue; } @@ -737,17 +928,34 @@ KNativePointer impl_getMethodListFromClassHierarchyInfo(KNativePointer info) } return new std::vector(ptrs); } -TS_INTEROP_1(getMethodListFromClassHierarchyInfo, KNativePointer, KNativePointer) +TS_INTEROP_1(getMethodItemsFromClassHierarchyInfo, KNativePointer, KNativePointer) -KNativePointer impl_getDetailFromClassMethodItem(KNativePointer item) +KNativePointer impl_getPropertyItemsFromClassHierarchyInfo(KNativePointer info) { - auto *itemPtr = reinterpret_cast(item); + auto *infoPtr = reinterpret_cast(info); + if (infoPtr == nullptr) { + return nullptr; + } + std::vector ptrs; + for (const auto &element : infoPtr->GetPropertyItemList()) { + if (element.second == nullptr) { + continue; + } + ptrs.push_back(new ClassPropertyItem(*(element.second))); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getPropertyItemsFromClassHierarchyInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getDetailFromClassHierarchyItem(KNativePointer item) +{ + auto *itemPtr = reinterpret_cast(item); if (itemPtr == nullptr) { return nullptr; } - return new std::string(itemPtr->GetFunctionDetail()); + return new std::string(itemPtr->GetDetail()); } -TS_INTEROP_1(getDetailFromClassMethodItem, KNativePointer, KNativePointer) +TS_INTEROP_1(getDetailFromClassHierarchyItem, KNativePointer, KNativePointer) KInt impl_getSetterStyleFromClassMethodItem(KNativePointer item) { @@ -759,15 +967,15 @@ KInt impl_getSetterStyleFromClassMethodItem(KNativePointer item) } TS_INTEROP_1(getSetterStyleFromClassMethodItem, KInt, KNativePointer) -KInt impl_getAccessModifierStyleFromClassMethodItem(KNativePointer item) +KInt impl_getAccessModifierStyleFromClassHierarchyItem(KNativePointer item) { - auto *itemPtr = reinterpret_cast(item); + auto *itemPtr = reinterpret_cast(item); if (itemPtr == nullptr) { return 0; } return static_cast(itemPtr->GetAccessModifierStyle()); } -TS_INTEROP_1(getAccessModifierStyleFromClassMethodItem, KInt, KNativePointer) +TS_INTEROP_1(getAccessModifierStyleFromClassHierarchyItem, KInt, KNativePointer) KInt impl_getAliasScriptElementKind(KNativePointer context, KInt position) { @@ -781,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) { @@ -798,12 +1020,74 @@ KNativePointer impl_getClassHierarchies(KNativePointer context, KStringPtr &file } TS_INTEROP_3(getClassHierarchies, KNativePointer, KNativePointer, KStringPtr, KInt) +KNativePointer impl_getApplicableRefactors(KNativePointer context, KStringPtr &kindPtr, KInt position) +{ + LSPAPI const *ctx = GetImpl(); + auto *result = new std::vector(ctx->getApplicableRefactors( + reinterpret_cast(context), GetStringCopy(kindPtr), static_cast(position))); + return result; +} +TS_INTEROP_3(getApplicableRefactors, KNativePointer, KNativePointer, KStringPtr, KInt) + +KNativePointer impl_getApplicableRefactorInfoList(KNativePointer infosPtr) +{ + auto *infos = reinterpret_cast *>(infosPtr); + std::vector ptrs; + for (auto &info : *infos) { + ptrs.push_back(new ark::es2panda::lsp::ApplicableRefactorInfo(info)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getApplicableRefactorInfoList, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionName(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->name); +} +TS_INTEROP_1(getRefactorActionName, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionDescription(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->description); +} +TS_INTEROP_1(getRefactorActionDescription, KNativePointer, KNativePointer) + +KNativePointer impl_getRefactorActionKind(KNativePointer refactorActionPtr) +{ + auto *refactorAction = reinterpret_cast(refactorActionPtr); + return new std::string(refactorAction->kind); +} +TS_INTEROP_1(getRefactorActionKind, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorName(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new std::string(applRefsInfo->name); +} +TS_INTEROP_1(getApplicableRefactorName, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorDescription(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new std::string(applRefsInfo->description); +} +TS_INTEROP_1(getApplicableRefactorDescription, KNativePointer, KNativePointer) + +KNativePointer impl_getApplicableRefactorAction(KNativePointer applRefsPtr) +{ + auto *applRefsInfo = reinterpret_cast(applRefsPtr); + return new ark::es2panda::lsp::RefactorAction(applRefsInfo->action); +} +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); } @@ -837,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)); } @@ -851,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)); } @@ -865,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)); } @@ -879,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)); } @@ -1136,13 +1416,12 @@ KNativePointer impl_getLocationFromList(KNativePointer listPtr) } TS_INTEROP_1(getLocationFromList, KNativePointer, KNativePointer) -KBoolean impl_getSafeDeleteInfo(KNativePointer context, KInt position, KStringPtr &path) +KBoolean impl_getSafeDeleteInfo(KNativePointer context, KInt position) { LSPAPI const *ctx = GetImpl(); - return static_cast( - ctx->getSafeDeleteInfo(reinterpret_cast(context), position, GetStringCopy(path))); + return static_cast(ctx->getSafeDeleteInfo(reinterpret_cast(context), position)); } -TS_INTEROP_3(getSafeDeleteInfo, KBoolean, KNativePointer, KInt, KStringPtr) +TS_INTEROP_2(getSafeDeleteInfo, KBoolean, KNativePointer, KInt) KNativePointer impl_toLineColumnOffset(KNativePointer context, KInt position) { @@ -1258,6 +1537,74 @@ KInt impl_getTypeFromTypeHierarchies(KNativePointer infoPtr) } TS_INTEROP_1(getTypeFromTypeHierarchies, KInt, KNativePointer) +KNativePointer impl_getCodeFixesAtPosition(KNativePointer context, KInt startPosition, KInt endPosition, + KInt *errorCodesPtr, KInt codeLength) +{ + CodeFixOptions emptyOptions; + std::vector errorCodesInt; + if (errorCodesPtr != nullptr && codeLength > 0) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,-warnings-as-errors) + errorCodesInt = std::vector(reinterpret_cast(errorCodesPtr), + reinterpret_cast(errorCodesPtr) + codeLength); + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,-warnings-as-errors) + } + LSPAPI const *ctx = GetImpl(); + auto autofix = ctx->getCodeFixesAtPosition(reinterpret_cast(context), startPosition, + endPosition, errorCodesInt, emptyOptions); + return new std::vector(autofix); +} +TS_INTEROP_5(getCodeFixesAtPosition, KNativePointer, KNativePointer, KInt, KInt, KInt *, KInt) + +KNativePointer impl_getCodeFixActionInfos(KNativePointer codeFixActionInfoListPtr) +{ + auto *getCodeFixActionInfoList = reinterpret_cast(codeFixActionInfoListPtr); + std::vector ptrs; + for (auto &el : getCodeFixActionInfoList->infos_) { + ptrs.push_back(new CodeFixActionInfo(el)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getCodeFixActionInfos, KNativePointer, KNativePointer) + +KNativePointer impl_getFileTextChangesFromCodeActionInfo(KNativePointer infoPtr) +{ + auto *info = reinterpret_cast(infoPtr); + std::vector ptrs; + for (auto &el : info->changes_) { + ptrs.push_back(new FileTextChanges(el)); + } + return new std::vector(ptrs); +} +TS_INTEROP_1(getFileTextChangesFromCodeActionInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getDescriptionFromCodeActionInfo(KNativePointer infoPtr) +{ + auto *info = reinterpret_cast(infoPtr); + return new std::string(info->description_); +} +TS_INTEROP_1(getDescriptionFromCodeActionInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getFixNameFromCodeFixActionInfo(KNativePointer infoPtr) +{ + auto *info = reinterpret_cast(infoPtr); + return new std::string(info->fixName_); +} +TS_INTEROP_1(getFixNameFromCodeFixActionInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getFixIdFromCodeFixActionInfo(KNativePointer infoPtr) +{ + auto *info = reinterpret_cast(infoPtr); + return new std::string(info->fixId_); +} +TS_INTEROP_1(getFixIdFromCodeFixActionInfo, KNativePointer, KNativePointer) + +KNativePointer impl_getFixAllDescriptionFromCodeFixActionInfo(KNativePointer infoPtr) +{ + auto *info = reinterpret_cast(infoPtr); + return new std::string(info->fixAllDescription_); +} +TS_INTEROP_1(getFixAllDescriptionFromCodeFixActionInfo, KNativePointer, KNativePointer) + KNativePointer impl_getSpanOfEnclosingComment(KNativePointer context, KInt position, KBoolean onlyMultiLine) { LSPAPI const *ctx = GetImpl(); diff --git a/ets2panda/bindings/package.json b/ets2panda/bindings/package.json index 92d680af8d08a135101997e4752810d95b16fca5..61b96b42af3c19fb49cd8a46736b6a6db2c776ee 100644 --- a/ets2panda/bindings/package.json +++ b/ets2panda/bindings/package.json @@ -7,8 +7,7 @@ "@types/node": "^18.0.0", "prettier": "latest", "rimraf": "^6.0.1", - "typescript": "4.9.5", - "node-api-headers": "^1.4.0" + "typescript": "4.9.5" }, "main": "dist/index.js", "scripts": { 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 82% rename from ets2panda/bindings/src/Es2pandaNativeModule.ts rename to ets2panda/bindings/src/common/Es2pandaNativeModule.ts index 210ad83e1cdbc4ee92919e027e49b61c61c67017..94c9d9f76efa61c03ba9fea68e9558f3b5fed460 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 } 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'; @@ -45,6 +54,10 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _GetAllErrorMessages(context: KPtr): KPtr { + throw new Error('Not implemented'); + } + _AstNodeDumpModifiers(context: KPtr, node: KPtr): KPtr { throw new Error('Not implemented'); } @@ -65,7 +78,8 @@ export class Es2pandaNativeModule { config: KPtr, outputDeclEts: String, outputEts: String, - exportAll: KBoolean + exportAll: KBoolean, + isolated: KBoolean ): KPtr { throw new Error('Not implemented'); } @@ -205,19 +219,23 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getMethodListFromClassHierarchyInfo(ptr: KNativePointer): KPtr { + _getMethodItemsFromClassHierarchyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getDetailFromClassMethodItem(ptr: KNativePointer): KPtr { + _getPropertyItemsFromClassHierarchyInfo(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getSetterStyleFromClassMethodItem(ptr: KNativePointer): KInt { + _getDetailFromClassHierarchyItem(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getAccessModifierStyleFromClassMethodItem(ptr: KNativePointer): KInt { + _getAccessModifierStyleFromClassHierarchyItem(ptr: KNativePointer): KInt { + throw new Error('Not implemented'); + } + + _getSetterStyleFromClassMethodItem(ptr: KNativePointer): KInt { throw new Error('Not implemented'); } @@ -341,19 +359,23 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getRefactorAction(ptr: KNativePointer): KPtr { + _getApplicableRefactorAction(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactors(context: KNativePointer, kind: String, position: number): KPtr { + _getApplicableRefactorName(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactorName(ptr: KNativePointer): KPtr { + _getApplicableRefactorDescription(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } - _getApplicableRefactorDescription(ptr: KNativePointer): KPtr { + _getApplicableRefactors(context: KNativePointer, kind: String, position: KInt): KPtr { + throw new Error('Not implemented'); + } + + _getApplicableRefactorInfoList(ptr: KNativePointer): KPtr { throw new Error('Not implemented'); } @@ -385,6 +407,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'); } @@ -716,7 +742,41 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getSafeDeleteInfo(context: KNativePointer, position: KInt, path: String): boolean { + _getSafeDeleteInfo(context: KNativePointer, position: KInt): boolean { + throw new Error('Not implemented'); + } + + _getCodeFixesAtPosition( + context: KNativePointer, + startPosition: KInt, + endPosition: KInt, + errorCodesPtr: KInt32ArrayPtr, + codeLength: KInt + ): KPtr { + throw new Error('Not implemented'); + } + + _getCodeFixActionInfos(infoPtr: KNativePointer): KPtr { + throw new Error('Not implemented'); + } + + _getFileTextChangesFromCodeActionInfo(infoPtr: KNativePointer): KPtr { + throw new Error('Not implemented'); + } + + _getDescriptionFromCodeActionInfo(infoPtr: KNativePointer): KPtr { + throw new Error('Not implemented'); + } + + _getFixNameFromCodeFixActionInfo(infoPtr: KNativePointer): KPtr { + throw new Error('Not implemented'); + } + + _getFixIdFromCodeFixActionInfo(infoPtr: KNativePointer): KPtr { + throw new Error('Not implemented'); + } + + _getFixAllDescriptionFromCodeFixActionInfo(infoPtr: KNativePointer): KPtr { throw new Error('Not implemented'); } @@ -748,6 +808,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'); } @@ -808,12 +940,49 @@ export class Es2pandaNativeModule { 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'); } @@ -829,7 +998,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'); } @@ -845,7 +1014,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'); } @@ -861,7 +1030,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 29f82f3d0133095d21558faa4e4d3a501234b6ac..4bd68da844cfb88951afb8fa9b857efc8856deeb 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): void { + public generateTsDecl(declOutPath: string, etsOutPath: string, exportAll: boolean, isolated: boolean): void { let exportAll_: KBoolean = exportAll ? 1 : 0; - global.es2panda._GenerateTsDeclarationsFromContext(this._cfg.peer, declOutPath, etsOutPath, exportAll_); + let isolated_: KBoolean = isolated ? 1 : 0; + global.es2panda._GenerateTsDeclarationsFromContext(this._cfg.peer, declOutPath, etsOutPath, exportAll_, isolated_); } } 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 82% rename from ets2panda/bindings/src/preDefine.ts rename to ets2panda/bindings/src/common/preDefine.ts index b0dc4d7f85d7414a5494160cebb02e6b18b62b68..0de1510a8f34aa704a76e10152b60582694c7459 100644 --- a/ets2panda/bindings/src/preDefine.ts +++ b/ets2panda/bindings/src/common/preDefine.ts @@ -15,13 +15,14 @@ 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'; 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 74% rename from ets2panda/bindings/src/types.ts rename to ets2panda/bindings/src/common/types.ts index 2a34d968b7c5a540cb7d7cfa7f17e0c7622eb623..c8bc63bb52f1c5d0818dce52e2f8b8cc90d89b64 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)]); @@ -109,6 +110,25 @@ export class Context extends ArktsObject { } 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 +136,21 @@ 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; } export interface DeclgenConfig { @@ -148,51 +159,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 100% rename from ets2panda/bindings/src/ui_plugins_driver.ts rename to ets2panda/bindings/src/common/ui_plugins_driver.ts diff --git a/ets2panda/bindings/src/utils.ts b/ets2panda/bindings/src/common/utils.ts similarity index 93% rename from ets2panda/bindings/src/utils.ts rename to ets2panda/bindings/src/common/utils.ts index 5e21866cc6fe8a7bfe6763bde7ce076f6abf8ea2..7c900df5ad61e1de643d0922e47a31e0c1bdf364 100644 --- a/ets2panda/bindings/src/utils.ts +++ b/ets2panda/bindings/src/common/utils.ts @@ -15,6 +15,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as os from 'os'; export function throwError(error: string): never { throw new Error(error); @@ -43,3 +44,7 @@ export function ensurePathExists(filePath: string): void { } } } + +export function isMac(): boolean { + return os.type() === 'Darwin'; +} 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 65% rename from ets2panda/bindings/src/buildConfigGenerate.ts rename to ets2panda/bindings/src/lsp/generateBuildConfig.ts index 4d1b1c34d1b2f17fc3f4695db8601732c24d72e6..88206a3e5ffb1b254894443e9fcb7495bac34fe7 100644 --- a/ets2panda/bindings/src/buildConfigGenerate.ts +++ b/ets2panda/bindings/src/lsp/generateBuildConfig.ts @@ -16,7 +16,8 @@ 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; @@ -92,9 +93,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,81 +150,91 @@ 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 { + return path.join(buildSdkPath, 'build-tools', 'ui-plugins', 'lib', pluginName, 'index'); +} + +function createPluginMap(buildSdkPath: string): Record { + let pluginMap: Record = {}; + const pluginList: string[] = ['ui-syntax-plugins', 'ui-plugins', 'memo-plugins']; + for (const plugin of pluginList) { + pluginMap[plugin] = createMapEntryForPlugin(buildSdkPath, plugin); + } + return pluginMap; } 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(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: { - 'ui-plugins': path.join(buildSdkPath, 'build-tools', 'ui-plugins', 'lib', 'ui-plugins', 'index'), - 'memo-plugin': path.join(buildSdkPath, 'build-tools', 'ui-plugins', 'lib', 'memo-plugins', 'index') - }, - arkts: {}, - arktsGlobal: {}, + plugins: pluginMap, 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; }) }; } - 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 83% rename from ets2panda/bindings/src/lspNode.ts rename to ets2panda/bindings/src/lsp/lspNode.ts index 17f846fce21df036fb1b966dca1ac0218aeff665..492350d708ede2822d13b30c4aa46f007edb110b 100644 --- a/ets2panda/bindings/src/lspNode.ts +++ b/ets2panda/bindings/src/lsp/lspNode.ts @@ -13,17 +13,21 @@ * 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 { - METHOD = 0, + NONE = 0, SETTER, GETTER } @@ -34,7 +38,19 @@ 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, + METHOD +} export abstract class LspNode { readonly peer: KNativePointer; @@ -228,30 +244,50 @@ export class LspSymbolDisplayPart extends LspNode { readonly kind: String; } -export class LspClassMethodItem extends LspNode { - constructor(peer: KNativePointer) { +export class LspClassHierarchyItem extends LspNode { + constructor(peer: KNativePointer, style: ClassDefinitionStyle) { super(peer); - this.functionDetail = unpackString(global.es2panda._getDetailFromClassMethodItem(this.peer)); + this.style = style; + this.detail = unpackString(global.es2panda._getDetailFromClassHierarchyItem(this.peer)); + this.accessModifier = global.es2panda._getAccessModifierStyleFromClassHierarchyItem(this.peer); + } + readonly detail: string; + readonly accessModifier: AccessModifierStyle; + readonly style: ClassDefinitionStyle; +} + +export class LspClassMethodItem extends LspClassHierarchyItem { + constructor(peer: KNativePointer) { + super(peer, ClassDefinitionStyle.METHOD); this.setter = global.es2panda._getSetterStyleFromClassMethodItem(this.peer); - this.accessModifier = global.es2panda._getAccessModifierStyleFromClassMethodItem(this.peer); } - readonly functionDetail: string; readonly setter: SetterStyle; - readonly accessModifier: AccessModifierStyle; +} + +export class LspClassPropertyItem extends LspClassHierarchyItem { + constructor(peer: KNativePointer) { + super(peer, ClassDefinitionStyle.FIELD); + } } export class LspClassHierarchyInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); this.className = unpackString(global.es2panda._getClassNameFromClassHierarchyInfo(this.peer)); - this.items = new NativePtrDecoder() - .decode(global.es2panda._getMethodListFromClassHierarchyInfo(this.peer)) + this.methodItems = new NativePtrDecoder() + .decode(global.es2panda._getMethodItemsFromClassHierarchyInfo(this.peer)) .map((elPeer: KNativePointer) => { return new LspClassMethodItem(elPeer); }); + this.fieldItems = new NativePtrDecoder() + .decode(global.es2panda._getPropertyItemsFromClassHierarchyInfo(this.peer)) + .map((elPeer: KNativePointer) => { + return new LspClassPropertyItem(elPeer); + }); } readonly className: string; - readonly items: LspClassMethodItem[]; + readonly methodItems: LspClassMethodItem[]; + readonly fieldItems: LspClassPropertyItem[]; } export class LspClassHierarchy extends LspNode { @@ -299,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); @@ -464,7 +500,8 @@ export enum LspCompletionEntryKind { STRUCT = 22, EVENT = 23, OPERATOR = 24, - TYPE_PARAMETER = 25 + TYPE_PARAMETER = 25, + ALIAS_TYPE = 26 } export enum ResolutionStatus { @@ -644,6 +681,43 @@ export class FileTextChanges extends LspNode { readonly textChanges: TextChange[]; } +export class CodeActionInfo extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.changes = new NativePtrDecoder() + .decode(global.es2panda._getFileTextChangesFromCodeActionInfo(peer)) + .map((elPeer: KNativePointer) => { + return new FileTextChanges(elPeer); + }); + this.description = unpackString(global.es2panda._getDescriptionFromCodeActionInfo(peer)); + } + readonly changes: FileTextChanges[]; + readonly description: String; +} + +export class CodeFixActionInfo extends CodeActionInfo { + constructor(peer: KNativePointer) { + super(peer); + this.fixName = unpackString(global.es2panda._getFixNameFromCodeFixActionInfo(peer)); + this.fixId_ = unpackString(global.es2panda._getFixIdFromCodeFixActionInfo(peer)); + this.fixAllDescription_ = unpackString(global.es2panda._getFixAllDescriptionFromCodeFixActionInfo(peer)); + } + readonly fixName: String; + readonly fixId_: String; + readonly fixAllDescription_: String; +} + +export class CodeFixActionInfoList extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.codeFixActionInfos = new NativePtrDecoder() + .decode(global.es2panda._getCodeFixActionInfos(peer)) + .map((elPeer: KNativePointer) => { + return new CodeFixActionInfo(elPeer); + }); + } + readonly codeFixActionInfos: CodeFixActionInfo[]; +} export class LspFileTextChanges extends LspNode { constructor(peer: KNativePointer) { @@ -681,7 +755,7 @@ export class LspSafeDeleteLocation extends LspNode { readonly safeDeleteLocationInfos: LspSafeDeleteLocationInfo[]; } -export class LspRefactorAction extends LspNode { +export class RefactorAction extends LspNode { constructor(peer: KNativePointer) { super(peer); this.name = unpackString(global.es2panda._getRefactorActionName(peer)); @@ -693,17 +767,30 @@ export class LspRefactorAction extends LspNode { readonly kind: String; } -export class LspApplicableRefactorInfo extends LspNode { +export class ApplicableRefactorItemInfo extends LspNode { constructor(peer: KNativePointer) { super(peer); this.name = unpackString(global.es2panda._getApplicableRefactorName(peer)); this.description = unpackString(global.es2panda._getApplicableRefactorDescription(peer)); - this.action = new LspRefactorAction(global.es2panda._getRefactorAction(peer)); + this.action = new RefactorAction(global.es2panda._getApplicableRefactorAction(peer)); } readonly name: String; readonly description: String; - readonly action: LspRefactorAction; + readonly action: RefactorAction; +} + +export class LspApplicableRefactorInfo extends LspNode { + constructor(peer: KNativePointer) { + super(peer); + this.applicableRefactorInfo = new NativePtrDecoder() + .decode(global.es2panda._getApplicableRefactorInfoList(peer)) + .map((elPeer: KNativePointer) => { + return new ApplicableRefactorItemInfo(elPeer); + }); + } + + readonly applicableRefactorInfo: ApplicableRefactorItemInfo[]; } export class LspTypeHierarchies extends LspNode { @@ -853,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_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts similarity index 51% rename from ets2panda/bindings/src/lsp_helper.ts rename to ets2panda/bindings/src/lsp/lsp_helper.ts index b543a2b07ea6fb3041a82eb9e609208085c3fd45..8500c1a2012e78608b849c1827e77a182a02de88 100644 --- a/ets2panda/bindings/src/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -13,8 +13,8 @@ * limitations under the License. */ -import { LspDriverHelper } from './driver_helper'; -import { global } from './global'; +import { LspDriverHelper } from '../common/driver_helper'; +import { global } from '../common/global'; import { LspDefinitionData, LspDiagsNode, @@ -30,6 +30,7 @@ import { LspLineAndCharacter, LspReferenceData, LspClassConstructorInfo, + ApplicableRefactorItemInfo, LspApplicableRefactorInfo, CompletionEntryDetails, LspFileTextChanges, @@ -40,51 +41,100 @@ import { LspInlayHint, LspInlayHintList, TextSpan, - LspSignatureHelpItems + LspSignatureHelpItems, + CodeFixActionInfo, + CodeFixActionInfoList, + LspRenameLocation, + LspRenameInfoType, + LspRenameInfoSuccess, + LspRenameInfoFailure } 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 { 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 { 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 } from '../common/preDefine'; +import * as crypto from 'crypto'; +import * as os from 'os'; const ets2pandaCmdPrefix = ['-', '--extension', 'ets', '--arktsconfig']; function initBuildEnv(): void { const currentPath: string | undefined = process.env.PATH; - let pandaLibPath: string = path.resolve(__dirname, '../../ets2panda/lib'); + 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 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 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; - constructor(projectPath: string, getContentCallback?: (filePath: string) => string) { + constructor(pathConfig: PathConfig, getContentCallback?: (filePath: string) => string, modules?: ModuleDescriptor[]) { 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.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]); + } + + // 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 { @@ -95,14 +145,6 @@ export class Lsp { 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) { @@ -114,11 +156,11 @@ export class Lsp { getDefinitionAtPosition(filename: String, offset: number): LspDefinitionData { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -133,12 +175,11 @@ export class Lsp { getSemanticDiagnostics(filename: String): LspDiagsNode { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); - let pandaLibPath: string = path.resolve(__dirname, '../../ets2panda/lib'); - let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, pandaLibPath); + let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); const source = this.getFileSource(filePath); - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -153,11 +194,11 @@ export class Lsp { getCurrentTokenValue(filename: String, offset: number): string { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -172,11 +213,11 @@ export class Lsp { getImplementationAtPosition(filename: String, offset: number): LspDefinitionData { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -191,11 +232,11 @@ export class Lsp { getFileReferences(filename: String): LspReferenceData[] { let lspDriverHelper = new LspDriverHelper(); let searchFilePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[searchFilePath]; + let arktsconfig = this.moduleInfos[searchFilePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, searchFilePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -205,15 +246,14 @@ export class Lsp { 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 compileFiles = this.moduleInfos[searchFilePath].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -235,11 +275,11 @@ export class Lsp { getReferencesAtPosition(filename: String, offset: number): LspReferenceData[] { let lspDriverHelper = new LspDriverHelper(); let searchFilePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[searchFilePath]; + let arktsconfig = this.moduleInfos[searchFilePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, searchFilePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -249,15 +289,14 @@ export class Lsp { 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 compileFiles = this.moduleInfos[searchFilePath].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -275,11 +314,11 @@ export class Lsp { getTypeHierarchies(filename: String, offset: number): LspTypeHierarchiesInfo | null { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -293,15 +332,14 @@ export class Lsp { 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 compileFiles = this.moduleInfos[filePath].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let searchCtx = lspDriverHelper.createCtx(source, filePath, searchCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(searchCtx); lspDriverHelper.proceedToState(searchCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -312,17 +350,24 @@ export class Lsp { lspDriverHelper.destroyConfig(searchCfg); let refs = new LspTypeHierarchiesInfo(ptr); if (i > 0) { - result[0].subHierarchies.subOrSuper = result[0].subHierarchies.subOrSuper.concat(refs.subHierarchies.subOrSuper); + 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); + 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() + new Map( + subOrSuperTmp.map((item) => [`${item.fileName}-${item.type}-${item.pos}-${item.name}`, item]) + ).values() ); } } @@ -332,10 +377,10 @@ export class Lsp { getClassHierarchyInfo(filename: String, offset: number): LspClassHierarchy { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); + const source = this.getFileSource(filePath); let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); @@ -351,10 +396,10 @@ export class Lsp { getAliasScriptElementKind(filename: String, offset: number): LspCompletionEntryKind { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); + const source = this.getFileSource(filePath); let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); @@ -368,31 +413,58 @@ export class Lsp { } getClassHierarchies(filename: String, offset: number): LspClassHierarchies { + let contextList = []; let lspDriverHelper = new LspDriverHelper(); - let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let localFilePath = path.resolve(filename.valueOf()); + let arktsconfig = this.moduleInfos[localFilePath].arktsConfigFile; 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); + let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, localFilePath, this.pandaLibPath); + const source = this.getFileSource(localFilePath); + let localCtx = lspDriverHelper.createCtx(source, localFilePath, 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); + contextList.push({ ctx: localCtx, cfg: localCfg }); + let nativeContextList = global.es2panda._pushBackToNativeContextVector(localCtx, localCtx, 1); + let compileFiles = this.moduleInfos[localFilePath].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + if (localFilePath === filePath) { + continue; + } + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; + 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); + 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); - lspDriverHelper.destroyContext(localCtx); - lspDriverHelper.destroyConfig(localCfg); + for (const { ctx, cfg } of contextList) { + lspDriverHelper.destroyContext(ctx); + lspDriverHelper.destroyConfig(cfg); + } return new LspClassHierarchies(ptr); } - getClassPropertyInfo(filename: String, offset: number, shouldCollectInherited: boolean = false): LspClassPropertyInfo { + 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 arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); - const source = this.getFileContent(filePath).replace(/\r\n/g, '\n'); + const source = this.getFileSource(filePath); let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); @@ -408,11 +480,11 @@ export class Lsp { getOrganizeImports(filename: String): LspFileTextChanges { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -427,11 +499,11 @@ export class Lsp { findSafeDeleteLocation(filename: String, offset: number): LspSafeDeleteLocationInfo[] { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -441,15 +513,14 @@ export class Lsp { 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 compileFiles = this.moduleInfos[filePath].compileFiles; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -467,11 +538,11 @@ export class Lsp { getCompletionEntryDetails(filename: String, offset: number, entryName: String): CompletionEntryDetails { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -483,33 +554,36 @@ export class Lsp { return new CompletionEntryDetails(ptr); } - getApplicableRefactors(filename: String, kind: String, offset: number): LspApplicableRefactorInfo { + getApplicableRefactors(filename: String, kind: String, offset: number): ApplicableRefactorItemInfo[] { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); 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); - return new LspApplicableRefactorInfo(ptr); + 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 arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -524,11 +598,14 @@ export class Lsp { getSyntacticDiagnostics(filename: String): LspDiagsNode { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); + const packageName = this.moduleInfos[filePath].packageName; + const buildConfig = this.buildConfigs[packageName]; + PluginDriver.getInstance().getPluginContext().setProjectConfig(buildConfig); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -543,11 +620,11 @@ export class Lsp { getSuggestionDiagnostics(filename: String): LspDiagsNode { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -562,11 +639,11 @@ export class Lsp { getQuickInfoAtPosition(filename: String, offset: number): LspQuickInfo { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -581,11 +658,11 @@ export class Lsp { getDocumentHighlights(filename: String, offset: number): LspDocumentHighlightsReferences { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -600,7 +677,7 @@ export class Lsp { getCompletionAtPosition(filename: String, offset: number): LspCompletionInfo { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); let source = this.getFileSource(filePath); @@ -614,7 +691,7 @@ export class Lsp { } offset += wildcard.length; } - let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -629,11 +706,11 @@ export class Lsp { toLineColumnOffset(filename: String, offset: number): LspLineAndCharacter { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -648,30 +725,105 @@ export class Lsp { getSafeDeleteInfo(filename: String, position: number): boolean { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); 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, path.resolve(__dirname, '../../..')); + let result = global.es2panda._getSafeDeleteInfo(localCtx, position); PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); lspDriverHelper.destroyContext(localCtx); lspDriverHelper.destroyConfig(localCfg); return result; } + findRenameLocations(filename: String, offset: number): LspRenameLocation[] { + 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); + const source = this.getFileSource(filePath); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); + 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 compileFiles = this.moduleInfos[filePath].compileFiles; + const fileContexts: KPointer[] = []; + const fileConfigs: Config[] = [localCfg]; + for (let i = 0; i < compileFiles.length; i++) { + let filePath = path.resolve(compileFiles[i]); + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; + let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); + let compileFileCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); + const source = this.getFileSource(filePath); + let compileFileCtx = lspDriverHelper.createCtx(source, filePath, compileFileCfg, this.globalContextPtr); + PluginDriver.getInstance().getPluginContext().setContextPtr(compileFileCtx); + lspDriverHelper.proceedToState(compileFileCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); + PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); + lspDriverHelper.proceedToState(compileFileCtx, Es2pandaContextState.ES2PANDA_STATE_CHECKED); + fileContexts.push(compileFileCtx); + fileConfigs.push(compileFileCfg); + } + const ptr = global.es2panda._findRenameLocations( + fileContexts.length, + passPointerArray(fileContexts), + localCtx, + offset + ); + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + const result: LspRenameLocation[] = new NativePtrDecoder().decode(ptr).map((elPeer: KPointer) => { + return new LspRenameLocation(elPeer); + }); + for (let i = 0; i < fileContexts.length; i++) { + lspDriverHelper.destroyContext(fileContexts[i]); + } + lspDriverHelper.destroyContext(localCtx); + for (const cfg of fileConfigs) { + lspDriverHelper.destroyConfig(cfg); + } + return Array.from(new Set(result)); + } + + getRenameInfo(filename: String, offset: number): LspRenameInfoType { + 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); + const source = this.getFileSource(filePath); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); + 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._getRenameInfo(localCtx, offset, this.pandaLibPath); + const success = global.es2panda._getRenameInfoIsSuccess(ptr); + let res: LspRenameInfoType; + if (success) { + res = new LspRenameInfoSuccess(global.es2panda._getRenameInfoSuccess(ptr)); + } else { + res = new LspRenameInfoFailure(global.es2panda._getRenameInfoFailure(ptr)); + } + PluginDriver.getInstance().runPluginHook(PluginHook.CLEAN); + lspDriverHelper.destroyContext(localCtx); + lspDriverHelper.destroyConfig(localCfg); + return res; + } + getSpanOfEnclosingComment(filename: String, offset: number, onlyMultiLine: boolean): LspTextSpan { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -683,10 +835,10 @@ export class Lsp { return new LspTextSpan(ptr); } - provideInlayHints(filename: String, span: TextSpan): LspInlayHint[] { + 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 arktsconfig = this.moduleInfos[filePath].arktsConfigFile; let ets2pandaCmd = ets2pandaCmdPrefix.concat(arktsconfig); let localCfg = lspDriverHelper.createCfg(ets2pandaCmd, filePath, this.pandaLibPath); const source = this.getFileSource(filePath); @@ -695,6 +847,34 @@ export class Lsp { 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.moduleInfos[filePath].arktsConfigFile; + 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, this.globalContextPtr); + 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); @@ -709,11 +889,11 @@ export class Lsp { getSignatureHelpItems(filename: String, offset: number): LspSignatureHelpItems { let lspDriverHelper = new LspDriverHelper(); let filePath = path.resolve(filename.valueOf()); - let arktsconfig = this.fileNameToArktsconfig[filePath]; + let arktsconfig = this.moduleInfos[filePath].arktsConfigFile; 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); + let localCtx = lspDriverHelper.createCtx(source, filePath, localCfg, this.globalContextPtr); PluginDriver.getInstance().getPluginContext().setContextPtr(localCtx); lspDriverHelper.proceedToState(localCtx, Es2pandaContextState.ES2PANDA_STATE_PARSED); PluginDriver.getInstance().runPluginHook(PluginHook.PARSED); @@ -724,4 +904,540 @@ export class Lsp { lspDriverHelper.destroyConfig(localCfg); 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/test/cases.ts b/ets2panda/bindings/test/cases.ts index 87a87dc23edfadbc4ea23f8aae2bb3eec115032d..f353848c71171c8daa1a077116a00d32cde1724e 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; @@ -44,7 +44,8 @@ export const testCases: TestCases = { '7': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition13.ets'), 664], '8': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition15.ets'), 617], '9': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition17.ets'), 677], - '10': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition18.ets'), 930] + '10': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition18.ets'), 930], + '11': [resolveTestPath('test/testcases/getDefinitionAtPosition/getDefinitionAtPosition19.ets'), 634] }, getSemanticDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSemanticDiagnostics.json'), @@ -133,6 +134,17 @@ export const testCases: TestCases = { '1': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 613], '2': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 620], '3': [resolveTestPath('test/testcases/getSignatureHelpItems/getSignatureHelpItems1.ets'), 678] + }, + 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] } }; diff --git a/ets2panda/bindings/test/expected/findRenameLocations.json b/ets2panda/bindings/test/expected/findRenameLocations.json new file mode 100644 index 0000000000000000000000000000000000000000..cc8a6bac0ead6eaad0dd130117507f0be5854a05 --- /dev/null +++ b/ets2panda/bindings/test/expected/findRenameLocations.json @@ -0,0 +1,178 @@ +{ + "1": [ + { + "fileName": "findRenameLocations1.ets", + "start": 708, + "end": 711, + "line": 21, + "prefixText": "export class ", + "suffixText": " {" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 630, + "end": 633, + "line": 15, + "prefixText": "import { dummy, abc, ", + "suffixText": " } from \"./findRenameLocations1.ets\";" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 738, + "end": 741, + "line": 23, + "prefixText": "let myfoo = new ", + "suffixText": "(\"apples\", 1, 2, 3);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 781, + "end": 784, + "line": 24, + "prefixText": "let otherfoo = new ", + "suffixText": "(\"oranges\", 4, 5, 6);" + } + ], + "2": [ + { + "fileName": "findRenameLocations1.ets", + "start": 625, + "end": 628, + "line": 15, + "prefixText": "export function ", + "suffixText": "(x: number): void {" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1259, + "end": 1262, + "line": 49, + "prefixText": "", + "suffixText": "(2);" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1267, + "end": 1270, + "line": 50, + "prefixText": "", + "suffixText": "(3);" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1275, + "end": 1278, + "line": 51, + "prefixText": "", + "suffixText": "(4);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 625, + "end": 628, + "line": 15, + "prefixText": "import { dummy, ", + "suffixText": ", Foo } from \"./findRenameLocations1.ets\";" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 694, + "end": 697, + "line": 19, + "prefixText": "", + "suffixText": "(5);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 702, + "end": 705, + "line": 20, + "prefixText": "", + "suffixText": "(55);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 711, + "end": 714, + "line": 21, + "prefixText": "", + "suffixText": "(555);" + } + ], + "3": [ + { + "fileName": "findRenameLocations1.ets", + "start": 667, + "end": 672, + "line": 18, + "prefixText": "export function ", + "suffixText": "(x: number): void {" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1239, + "end": 1244, + "line": 47, + "prefixText": "", + "suffixText": "(0);" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 1249, + "end": 1254, + "line": 48, + "prefixText": "", + "suffixText": "(1);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 618, + "end": 623, + "line": 15, + "prefixText": "import { ", + "suffixText": ", abc, Foo } from \"./findRenameLocations1.ets\";" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 673, + "end": 678, + "line": 17, + "prefixText": "", + "suffixText": "(4);" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 683, + "end": 688, + "line": 18, + "prefixText": "", + "suffixText": "(44);" + } + ], + "4": [ + { + "fileName": "findRenameLocations1.ets", + "start": 718, + "end": 722, + "line": 22, + "prefixText": " ", + "suffixText": ": string = \"unassigned\";" + }, + { + "fileName": "findRenameLocations1.ets", + "start": 882, + "end": 886, + "line": 27, + "prefixText": " this.", + "suffixText": " = name;" + }, + { + "fileName": "findRenameLocations2.ets", + "start": 866, + "end": 870, + "line": 28, + "prefixText": "console.log(myfoo.", + "suffixText": ")" + } + ] +} \ No newline at end of file diff --git a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json index 74bd51b3c26f3f49e8838394eed310a2e59b72d1..73b133987606044ddbf101795a4e7b21a1110a15 100644 --- a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json +++ b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json @@ -48,5 +48,10 @@ "fileName": "text.d.ets", "start": 7586, "length": 4 + }, + "11": { + "fileName": "taskpool.ets", + "start": 686, + "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..269aac96696f681ad622fee6587b90a272bf69cc --- /dev/null +++ b/ets2panda/bindings/test/expected/getRenameInfo.json @@ -0,0 +1,14 @@ +{ + "1": { + "canRenameSuccess": true, + "fileToRename": "", + "kind": "property", + "displayName": "aaa", + "fullDisplayName": "aaa", + "kindModifiers": "", + "triggerSpan": { + "start": 613, + "length": 3 + } + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/expected/getSemanticDiagnostics.json b/ets2panda/bindings/test/expected/getSemanticDiagnostics.json index 679a81c0565df7ffcaec412a54ad8a473b295304..8c5983e344c9d934e82d615714ffba32e6034f6d 100644 --- a/ets2panda/bindings/test/expected/getSemanticDiagnostics.json +++ b/ets2panda/bindings/test/expected/getSemanticDiagnostics.json @@ -5,7 +5,7 @@ "2": { "diagnostics": [ { - "message": "Type '\"1\"' is not compatible with type 'double' at index 1", + "message": "Type '\"1\"' is not compatible with type 'Double' at index 1", "source": "\"1\"", "range": { "start": { @@ -27,7 +27,7 @@ } }, { - "message": "No matching call signature for add(\"1\", int)", + "message": "No matching call signature for add(\"1\", Int)", "source": "add", "range": { "start": { @@ -49,7 +49,7 @@ } }, { - "message": "Type '\"hello\"' cannot be assigned to type 'double'", + "message": "Type '\"hello\"' cannot be assigned to type 'Double'", "source": "\"hello\"", "range": { "start": { diff --git a/ets2panda/bindings/test/expected/getSignatureHelpItems.json b/ets2panda/bindings/test/expected/getSignatureHelpItems.json index 51881ad082d114fead94544196db25c846c52d88..77744cd4ecdd2bac58b079393195563b2bc8fd99 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" }, { diff --git a/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json b/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json index 2ed128d561f1f5ac1426fe6fbeabf5752a076545..baa3c0c3ad58cc3a339fcb4b84e70d6fc018dd53 100644 --- a/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json +++ b/ets2panda/bindings/test/expected/getSyntacticDiagnostics.json @@ -93,16 +93,16 @@ } }, { - "message": "Unexpected token ':'.", - "source": "*ERROR_LITERAL*", + "message": "Unexpected token 'number'.", + "source": "number", "range": { "start": { "line": 16, - "character": 14 + "character": 16 }, "end": { "line": 16, - "character": 15 + "character": 22 } }, "tags": [], @@ -159,16 +159,16 @@ } }, { - "message": "Unexpected token ','.", - "source": "*ERROR_LITERAL*", + "message": "Unexpected token 'b'.", + "source": "", "range": { "start": { "line": 16, - "character": 22 + "character": 24 }, "end": { "line": 16, - "character": 23 + "character": 24 } }, "tags": [], @@ -247,16 +247,16 @@ } }, { - "message": "Unexpected token ')'.", - "source": "*ERROR_LITERAL*", + "message": "Unexpected token '{'.", + "source": "{\n return ((a) + (b));\n}", "range": { "start": { "line": 16, - "character": 33 + "character": 35 }, "end": { - "line": 16, - "character": 34 + "line": 18, + "character": 2 } }, "tags": [], diff --git a/ets2panda/bindings/test/run_tests.ts b/ets2panda/bindings/test/run_tests.ts index ecb0adaa1651af5c538924461a4f880994f960d2..9220c53c917f33f75723dd47b730ec8a668ce23d 100644 --- a/ets2panda/bindings/test/run_tests.ts +++ b/ets2panda/bindings/test/run_tests.ts @@ -15,16 +15,9 @@ import path from 'path'; import fs from 'fs'; -import { - Lsp, - LspDefinitionData, - LspCompletionInfo, - LspDiagsNode, - ModuleDescriptor, - generateArkTsConfigByModules -} from '../src/index'; +import { Lsp, LspDefinitionData, LspCompletionInfo, LspDiagsNode, ModuleDescriptor, PathConfig } from '../src/index'; import { testCases } from './cases'; -import { LspCompletionEntry } from '../src/lspNode'; +import { LspCompletionEntry } from '../src/lsp/lspNode'; interface ComparisonOptions { subMatch?: boolean; @@ -241,6 +234,21 @@ function findTextDefinitionPosition(sourceCode: string): number { throw new Error('Could not find Text definition in source code'); } +// CC-OFFNXT(huge_cyclomatic_complexity, huge_depth, huge_method) false positive +function findTaskDefinitionPosition(sourceCode: string): number { + const taskDefinitionPattern = /export\s+class\s+Task\s+{/; + const match = taskDefinitionPattern.exec(sourceCode); + if (match) { + const classTaskPattern = /class\s+Task\s+{/; + const subMatch = classTaskPattern.exec(sourceCode.substring(match.index)); + if (subMatch) { + const positionOfT = match.index + subMatch.index + 'class '.length; + return positionOfT; + } + } + throw new Error('Could not find Task definition in source code'); +} + function compareGetDefinitionResult(testName: string, actual: any, expected: Record): boolean { // This is the definition info for the UI component. // File in the SDK might changed, so the offset needs to be checked dynamically. @@ -255,6 +263,19 @@ function compareGetDefinitionResult(testName: string, actual: any, expected: Rec }; 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 = { + ...expected, + start: expectedStart + }; + return compareResultsHelper(testName, normalizeData(actual), expectedResult); + } return compareResultsHelper(testName, normalizeData(actual), expected); } @@ -336,13 +357,16 @@ if (require.main === module) { 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); + const pathConfig: PathConfig = { + buildSdkPath: path.join(testDir, 'ets', 'ets1.2'), + projectPath: path.join(testDir, 'testcases'), + declgenOutDir: '' + }; + + const modules = getModules(pathConfig.projectPath); - generateArkTsConfigByModules(buildSdkPath, projectRoot, modules); - const lsp = new Lsp(projectRoot); + const lsp = new Lsp(pathConfig, undefined, modules); - process.env.BINDINGS_PATH = path.join(buildSdkPath, 'build-tools', 'bindings'); + process.env.BINDINGS_PATH = path.join(pathConfig.buildSdkPath, 'build-tools', 'bindings'); runTests(testDir, lsp); } 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/test/isolated_declgen/infer_function_return_type_with_variable-expected.txt b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition19.ets similarity index 91% rename from ets2panda/test/isolated_declgen/infer_function_return_type_with_variable-expected.txt rename to ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition19.ets index 6eced866112266e6fc719f40a73bfa8ce070d9a3..fc811ab595deb7697c8748ef83cea4f1b3a609eb 100644 --- a/ets2panda/test/isolated_declgen/infer_function_return_type_with_variable-expected.txt +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition19.ets @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -13,4 +13,4 @@ * limitations under the License. */ - export declare function foo(a: int): String|B|Int|A; \ No newline at end of file +const task = new taskpool.Task(()=>{}); 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/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 43ab9c71bfe6a71dddf2fa19452ff439d2b9b10a..a9d88755ddc9d50fbfc2334208ac774cfad0e4af 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -19,10 +19,15 @@ #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 "compiler/lowering/util.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" +#include "types/ts/nullType.h" +#include "types/type.h" +#include "types/typeFlag.h" #include "util/es2pandaMacros.h" #include @@ -305,6 +310,77 @@ 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); + } +} + +checker::Type *ETSAnalyzer::Check(ir::OverloadDeclaration *node) const +{ + ETSChecker *checker = GetETSChecker(); + ES2PANDA_ASSERT(node != nullptr); + ES2PANDA_ASSERT(node->Key()); + + CheckDuplicationInOverloadDeclaration(checker, node); + + if (!node->Key()->IsIdentifier()) { + checker->LogError(diagnostic::OVERLOAD_NAME_MUST_BE_IDENTIFIER, {}, node->Key()->Start()); + } + + if (node->IsConstructorOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->IsClassDefinition()); + checker->CheckConstructorOverloadDeclaration(checker, node); + return nullptr; + } + + if (node->IsFunctionOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->IsClassDefinition() && compiler::HasGlobalClassParent(node)); + checker->CheckFunctionOverloadDeclaration(checker, node); + return nullptr; + } + + if (node->IsClassMethodOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->IsClassDefinition()); + checker->CheckClassMethodOverloadDeclaration(checker, node); + return nullptr; + } + + if (node->IsInterfaceMethodOverloadDeclaration()) { + ES2PANDA_ASSERT(node->Parent()->Parent()->IsTSInterfaceDeclaration()); + checker->CheckInterfaceMethodOverloadDeclaration(checker, node); + return nullptr; + } + + return nullptr; +} + checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::Property *expr) const { ETSChecker *checker = GetETSChecker(); @@ -318,7 +394,11 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const } ETSChecker *checker = GetETSChecker(); - Type *exprType = expr->AsSpreadElement()->Argument()->Check(checker); + if (expr->PreferredType() != nullptr) { + expr->Argument()->SetPreferredType(expr->PreferredType()); + } + auto type = expr->AsSpreadElement()->Argument()->Check(checker); + Type *exprType = type->MaybeBaseTypeOfGradualType(); if (exprType->IsETSResizableArrayType()) { return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front()); @@ -332,7 +412,7 @@ checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const return checker->InvalidateType(expr); } - checker::Type *const elementType = exprType->IsETSTupleType() ? exprType : checker->GetElementTypeOfArray(exprType); + checker::Type *const elementType = exprType->IsETSTupleType() ? type : checker->GetElementTypeOfArray(exprType); return expr->SetTsType(elementType); } @@ -398,7 +478,7 @@ static bool CheckArrayElementType(ETSChecker *checker, T *newArrayInstanceExpr) ES2PANDA_ASSERT(checker != nullptr); ES2PANDA_ASSERT(newArrayInstanceExpr != nullptr); - checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker); + checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker)->MaybeBaseTypeOfGradualType(); if (elementType->IsETSPrimitiveType()) { return true; } @@ -467,7 +547,8 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewClassInstanceExpression *expr) { - checker::Type *calleeType = expr->GetTypeRef()->Check(checker); + checker::Type *res = expr->GetTypeRef()->Check(checker); + auto calleeType = res->MaybeBaseTypeOfGradualType(); if (calleeType->IsTypeError()) { return checker->InvalidateType(expr->GetTypeRef()); } @@ -498,7 +579,7 @@ static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewCl return checker->GlobalTypeError(); } - return calleeType; + return res; } checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const @@ -511,32 +592,20 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const if (calleeType->IsTypeError()) { return checker->InvalidateType(expr); } - auto *calleeObj = calleeType->AsETSObjectType(); - expr->SetTsType(calleeObj); + auto *calleeObj = calleeType->MaybeBaseTypeOfGradualType()->AsETSObjectType(); + expr->SetTsType(calleeType); - 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 *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start()); - if (signature == nullptr) { - return checker->InvalidateType(expr); - } + if (signature == nullptr) { + return checker->InvalidateType(expr); + } - checker->CheckObjectLiteralArguments(signature, expr->GetArguments()); + checker->CheckObjectLiteralArguments(signature, expr->GetArguments()); - checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start()); + checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start()); - 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); - } - } + expr->SetSignature(signature); return expr->TsType(); } @@ -630,11 +699,12 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNonNullishTypeNode *node) const return node->TsType(); } ETSChecker *checker = GetETSChecker(); - checker::Type *originalType = node->GetTypeNode()->Check(checker); + auto type = node->GetTypeNode()->Check(checker); + checker::Type *originalType = type->MaybeBaseTypeOfGradualType(); if (!originalType->IsETSTypeParameter()) { checker->LogError(diagnostic::ILLEGAL_NON_NULLISH_TYPE, {}, node->GetTypeNode()->Start()); } - return node->SetTsType(checker->GetNonNullishType(originalType)); + return node->SetTsType(checker->GetNonNullishType(type)); } checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const @@ -682,7 +752,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()) { @@ -720,6 +790,7 @@ static ArenaVector> GetElementTypes(ETSCheck ir::Expression *const element = expr->Elements()[idx]; if (element->IsSpreadElement()) { + element->SetPreferredType(exprPreferredType); AddSpreadElementTypes(checker, element->AsSpreadElement(), elementTypes); continue; } @@ -905,7 +976,8 @@ checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const expr->SetPreferredType(preferredType); - if (!CheckArrayExpressionElements(checker, expr)) { + if (!ValidArrayExprSizeForTupleSize(checker, preferredType, expr) || + !CheckArrayExpressionElements(checker, expr)) { return checker->InvalidateType(expr); } } @@ -1123,7 +1195,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}}}); @@ -1148,6 +1223,13 @@ static checker::Type *HandleSubstitution(ETSChecker *checker, ir::AssignmentExpr checker->TryInferTypeForLambdaTypeAlias(expr->Right()->AsArrowFunctionExpression(), preferredType->AsETSFunctionType()); } + } else if (expr->Right()->IsObjectExpression()) { + if (leftType->IsETSObjectType() && leftType->IsGradualType() && + (leftType->HasTypeFlag(TypeFlag::READONLY) || + leftType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::REQUIRED))) { + checker->LogError(diagnostic::DYMANIC_INIT_WITH_OBJEXPR, {leftType}, expr->Right()->Start()); + } + expr->Right()->AsObjectExpression()->SetPreferredType(leftType); } else { expr->Right()->SetPreferredType(leftType); } @@ -1330,6 +1412,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 { @@ -1343,6 +1437,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); @@ -1370,7 +1468,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()) { @@ -1395,6 +1493,7 @@ Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, Type *calleeType) con } Signature *const signature = ResolveSignature(checker, expr, calleeType); + if (signature == nullptr) { return checker->GlobalTypeError(); } @@ -1406,13 +1505,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)) { @@ -1439,10 +1532,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()); } @@ -1469,16 +1567,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(); @@ -1496,6 +1585,10 @@ checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr return returnType; } + if (!signature->HasFunction()) { + return checker->GlobalTypeError(); + } + auto owner = const_cast(util::Helpers::GetContainingObjectType(signature->Function())); SavedCheckerContext savedCtx(ReconstructOwnerClassContext(checker, owner)); @@ -1543,6 +1636,11 @@ checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const CheckCallee(checker, expr); + checker::TypeStackElement tse(checker, expr, {{diagnostic::CYCLIC_CALLEE, {}}}, expr->Start()); + if (tse.HasTypeError()) { + return checker->GlobalTypeError(); + } + checker::Type *const returnType = GetCallExpressionReturnType(expr, calleeType); expr->SetTsType(returnType); if (returnType->IsTypeError()) { @@ -1662,7 +1760,9 @@ checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression *const use, Type *type) { ES2PANDA_ASSERT(use->IsIdentifier() || use->IsMemberExpression()); - if (!type->IsETSMethodType()) { + if (!type->IsETSMethodType() || + (use->IsMemberExpression() && use->AsMemberExpression()->PropVar() != nullptr && + use->AsMemberExpression()->PropVar()->HasFlag(varbinder::VariableFlags::DYNAMIC))) { return type; } auto const getUseSite = [use]() { @@ -1676,6 +1776,9 @@ 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(); @@ -1711,6 +1814,9 @@ checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const auto *identType = TransformTypeForMethodReference(checker, expr, checker->ResolveIdentifier(expr)); + if (expr->TsType() != nullptr && expr->TsType()->IsTypeError()) { + return expr->TsType(); + } ES2PANDA_ASSERT(expr->Variable() != nullptr); if (expr->Parent() == nullptr || !expr->Parent()->IsAssignmentExpression() || expr != expr->Parent()->AsAssignmentExpression()->Left()) { @@ -1769,6 +1875,10 @@ checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checke return checker->InvalidateType(expr); } + if (baseType->IsGradualType()) { + return ResolveMemberExpressionByBaseType(checker, baseType->AsGradualType()->GetBaseType(), expr); + } + if (baseType->IsETSArrayType()) { if (expr->Property()->AsIdentifier()->Name().Is("length")) { return expr->AdjustType(checker, checker->GlobalIntBuiltinType()); @@ -1807,8 +1917,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); @@ -1905,7 +2014,15 @@ static void SetTypeforRecordProperties(const ir::ObjectExpression *expr, checker auto *const valueType = typeArguments[1]; // Record type arguments for (auto *const recordProperty : recordProperties) { - auto *const recordPropertyExpr = recordProperty->AsProperty()->Value(); + ir::Expression *recordPropertyExpr = nullptr; + if (recordProperty->IsProperty()) { + recordPropertyExpr = recordProperty->AsProperty()->Value(); + } else if (recordProperty->IsSpreadElement()) { + recordPropertyExpr = recordProperty->AsSpreadElement()->Argument(); + } else { + ES2PANDA_UNREACHABLE(); + } + recordPropertyExpr->SetPreferredType(valueType); recordPropertyExpr->Check(checker); } @@ -2091,12 +2208,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()); } } } @@ -2117,7 +2234,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) @@ -2128,7 +2244,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); @@ -2193,8 +2309,9 @@ checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *check std::vector candidateObjectTypes; // Phase 1: Gather all ETSObjectTypes from the union for (auto *constituentType : unionType->ConstituentTypes()) { - if (constituentType->IsETSObjectType()) { - candidateObjectTypes.push_back(constituentType->AsETSObjectType()); + auto type = constituentType->MaybeBaseTypeOfGradualType(); + if (type->IsETSObjectType()) { + candidateObjectTypes.push_back(type->AsETSObjectType()); } } @@ -2231,6 +2348,10 @@ static checker::ETSObjectType *ResolveObjectTypeFromPreferredType(ETSChecker *ch // Assume not null, checked by caller in Check() checker::Type *preferredType = expr->PreferredType(); + if (preferredType->IsETSAsyncFuncReturnType()) { + preferredType = preferredType->AsETSAsyncFuncReturnType()->GetPromiseTypeArg(); + } + if (preferredType->IsETSUnionType()) { return ResolveUnionObjectTypeForObjectLiteral(checker, expr, preferredType->AsETSUnionType()); } @@ -2277,16 +2398,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) { @@ -2365,6 +2481,7 @@ void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, } value->SetPreferredType(propType); + propExpr->SetTsType(propType); key->SetTsType(propType); value->SetTsType(value->Check(checker)); @@ -2416,6 +2533,11 @@ checker::Type *ETSAnalyzer::Check(ir::SuperExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::TemplateLiteral *expr) const { ETSChecker *checker = GetETSChecker(); + + for (auto *it : expr->Expressions()) { + it->Check(checker); + } + if (expr->TsType() != nullptr) { return expr->TsType(); } @@ -2426,10 +2548,6 @@ checker::Type *ETSAnalyzer::Check(ir::TemplateLiteral *expr) const return expr->TsType(); } - for (auto *it : expr->Expressions()) { - it->Check(checker); - } - for (auto *it : expr->Quasis()) { it->Check(checker); } @@ -2558,8 +2676,7 @@ 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, isCondExpr); - + 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: { @@ -2702,6 +2819,10 @@ static bool CheckIfLiteralValueIsAppropriate(ETSChecker *checker, Type *type, ir 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()) { return relation->IsIdenticalTo(checker->GlobalDoubleBuiltinType(), type); } @@ -2751,6 +2872,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(); @@ -3042,13 +3182,13 @@ checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point. auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt); - checker::Type *const exprType = st->Right()->Check(checker); + checker::Type *const exprType = st->Right()->Check(checker)->MaybeBaseTypeOfGradualType(); if (exprType == nullptr) { checker->LogError(diagnostic::FOROF_CANT_INFER_SOURCE, {}, st->Right()->Start()); return checker->GlobalTypeError(); } - checker::Type *elemType = nullptr; + checker::Type *elemType = checker->GlobalTypeError(); if (exprType->IsETSStringType()) { elemType = checker->GlobalCharBuiltinType(); @@ -3058,7 +3198,7 @@ checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const elemType = st->CheckIteratorMethod(checker); } - if (elemType == nullptr) { + if (elemType == checker->GlobalTypeError()) { checker->LogError(diagnostic::FOROF_SOURCE_NONITERABLE, {}, st->Right()->Start()); return checker->GlobalTypeError(); } @@ -3208,15 +3348,16 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S // Case when function's return type is defined explicitly: if (st->argument_ == nullptr) { - if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() && - !funcReturnType->IsETSAsyncFuncReturnType()) { + if (!funcReturnType->MaybeBaseTypeOfGradualType()->IsETSVoidType() && + funcReturnType != checker->GlobalVoidType() && + !funcReturnType->MaybeBaseTypeOfGradualType()->IsETSAsyncFuncReturnType()) { checker->LogError(diagnostic::RETURN_WITHOUT_VALUE, {}, st->Start()); return false; } funcReturnType = checker->GlobalVoidType(); } else { const auto name = containingFunc->Scope()->InternalName().Mutf8(); - if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) { + if (!CheckArgumentVoidType(funcReturnType->MaybeBaseTypeOfGradualType(), checker, name, st)) { return false; } @@ -3228,7 +3369,8 @@ bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::S } checker::Type *argumentType = st->argument_->Check(checker); - return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc); + return CheckReturnType(checker, funcReturnType->MaybeBaseTypeOfGradualType(), argumentType, st->argument_, + containingFunc); } return true; } @@ -3545,12 +3687,6 @@ checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const {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. @@ -3587,13 +3723,14 @@ checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const return st->SetTsType(stmtType); } - auto *interfaceType = stmtType->AsETSObjectType(); + auto *interfaceType = stmtType->IsGradualType() ? stmtType->AsGradualType()->GetBaseType()->AsETSObjectType() + : stmtType->AsETSObjectType(); checker->CheckInterfaceAnnotations(st); interfaceType->SetSuperType(checker->GlobalETSObjectType()); checker->CheckInvokeMethodsLegitimacy(interfaceType); - st->SetTsType(interfaceType); + st->SetTsType(stmtType); checker::ScopeContext scopeCtx(checker, st->Scope()); auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType); @@ -3632,10 +3769,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(); @@ -3645,7 +3834,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()); @@ -3664,6 +3857,7 @@ checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const checker::Type *ETSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const { ETSChecker *checker = GetETSChecker(); + auto checkerContext = SavedCheckerContext(checker, CheckerStatus::NO_OPTS, checker->Context().ContainingClass()); checker->CheckAnnotations(st->Annotations()); diff --git a/ets2panda/checker/ETSAnalyzerHelpers.cpp b/ets2panda/checker/ETSAnalyzerHelpers.cpp index cec0d02e2dbb18471cb6691dd0006334b5b6ef36..cad55b124fb6a929919767772a59153dd6004d04 100644 --- a/ets2panda/checker/ETSAnalyzerHelpers.cpp +++ b/ets2panda/checker/ETSAnalyzerHelpers.cpp @@ -244,7 +244,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()); @@ -306,7 +306,7 @@ static bool HasIteratorInterface(ETSObjectType const *const objectType) void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc, const lexer::SourcePosition &position, const std::string &methodName) { - const auto *returnType = scriptFunc->Signature()->ReturnType(); + const auto *returnType = scriptFunc->Signature()->ReturnType()->MaybeBaseTypeOfGradualType(); if (returnType == nullptr) { checker->LogError(diagnostic::MISSING_RETURN_TYPE_2, {util::StringView(methodName)}, position); @@ -535,11 +535,14 @@ void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, case lexer::TokenType::PUNCTUATOR_TILDE: { 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: { diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index f345e2e86b47ee574095c901b3bedcd9c6498834..abf418beae8b15f7a9979144cac3a715ede08fe4 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -25,6 +25,8 @@ #include "ir/expressions/callExpression.h" #include "ir/ts/tsInterfaceDeclaration.h" #include "ir/statements/blockStatement.h" +#include "types/type.h" +#include "utils/arena_containers.h" #include "varbinder/ETSBinder.h" #include "parser/program/program.h" #include "checker/ets/aliveAnalyzer.h" @@ -34,6 +36,7 @@ #include "ir/base/scriptFunction.h" #include "util/helpers.h" #include "evaluate/scopedDebugInfoPlugin.h" +#include "checker/types/ets/etsTupleType.h" namespace ark::es2panda::checker { @@ -97,13 +100,15 @@ static util::StringView InitBuiltin(ETSChecker *checker, std::string_view signat ES2PANDA_ASSERT(iterator != varMap.end()); auto *var = iterator->second; Type *type {nullptr}; - if (var->Declaration()->Node()->IsClassDefinition()) { - type = checker->BuildBasicClassProperties(var->Declaration()->Node()->AsClassDefinition()); - } else { - ES2PANDA_ASSERT(var->Declaration()->Node()->IsTSInterfaceDeclaration()); - type = checker->BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); + if (var->HasFlag(varbinder::VariableFlags::BUILTIN_TYPE)) { + if (var->Declaration()->Node()->IsClassDefinition()) { + type = checker->BuildBasicClassProperties(var->Declaration()->Node()->AsClassDefinition()); + } else { + ES2PANDA_ASSERT(var->Declaration()->Node()->IsTSInterfaceDeclaration()); + type = checker->BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); + } + checker->GetGlobalTypesHolder()->InitializeBuiltin(iterator->first, type); } - checker->GetGlobalTypesHolder()->InitializeBuiltin(iterator->first, type); return iterator->first; } @@ -318,13 +323,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) @@ -378,10 +376,15 @@ void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) for (auto *extProg : extPrograms) { 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()); + CheckProgram(extProg, VarBinder()->IsGenStdLib() || extProg->IsGenAbcForExternal()); + VarBinder()->AsETSBinder()->SetProgram(savedProgram2); + VarBinder()->AsETSBinder()->ResetTopScope(savedProgram2->GlobalScope()); } } } @@ -425,16 +428,13 @@ bool ETSChecker::IsClassStaticMethod(checker::ETSObjectType *objType, checker::S [[nodiscard]] TypeFlag ETSChecker::TypeKind(const Type *const type) noexcept { - // These types were not present in the ETS_TYPE list. Some of them are omited intentionally, other are just bugs + // These types were not present in the ETS_TYPE list. Some of them are omitted intentionally, other are just bugs static constexpr auto TO_CLEAR = TypeFlag::CONSTANT | TypeFlag::GENERIC | TypeFlag::ETS_INT_ENUM | TypeFlag::ETS_STRING_ENUM | TypeFlag::READONLY | TypeFlag::BIGINT_LITERAL | TypeFlag::ETS_TYPE_ALIAS | TypeFlag::TYPE_ERROR; - // Bugs: these types do not appear as a valid TypeKind, as the TypeKind has more then one bit set - [[maybe_unused]] static constexpr auto NOT_A_TYPE_KIND = TypeFlag::ETS_DYNAMIC_FLAG; - auto res = static_cast(type->TypeFlags() & ~(TO_CLEAR)); - ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(NOT_A_TYPE_KIND)), + ES2PANDA_ASSERT_POS(res == TypeFlag::NONE || helpers::math::IsPowerOfTwo(res & ~(TypeFlag::NONE)), ark::es2panda::GetPositionForDiagnostic()); return res; } @@ -545,6 +545,11 @@ Type *ETSChecker::GlobalETSAnyType() const return GetGlobalTypesHolder()->GlobalETSAnyType(); } +Type *ETSChecker::GlobalETSRelaxedAnyType() const +{ + return GetGlobalTypesHolder()->GlobalETSRelaxedAnyType(); +} + Type *ETSChecker::GlobalETSNeverType() const { return GetGlobalTypesHolder()->GlobalETSNeverType(); @@ -582,6 +587,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); @@ -686,9 +696,9 @@ ETSObjectType *ETSChecker::GlobalBuiltinBoxType(Type *contents) return AsETSObjectType(&GlobalTypesHolder::GlobalDoubleBoxBuiltinType); default: { auto *base = AsETSObjectType(&GlobalTypesHolder::GlobalBoxBuiltinType); - auto *substitution = NewSubstitution(); - substitution->emplace(base->TypeArguments()[0]->AsETSTypeParameter(), contents); - return base->Substitute(Relation(), substitution); + auto substitution = Substitution {}; + substitution.emplace(base->TypeArguments()[0]->AsETSTypeParameter(), contents); + return base->Substitute(Relation(), &substitution); } } } diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 97ae5b8f2bbd9232c0c1e110eb8b6442e6e63c55..ff33e279e6b45a6eff9a4ea14cb1e9ba9f5f5019 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -26,6 +26,7 @@ #include "checker/types/ets/types.h" #include "checker/resolveResult.h" #include "ir/visitor/AstVisitor.h" +#include "types/type.h" #include "util/helpers.h" namespace ark::es2panda::varbinder { @@ -74,11 +75,12 @@ using DynamicLambdaObjectSignatureMap = 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: @@ -87,7 +89,6 @@ public: // NOLINTNEXTLINE(readability-redundant-member-init) : Checker(allocator, diagnosticEngine, programAllocator), arrayTypes_(Allocator()->Adapter()), - pendingConstraintCheckRecords_(Allocator()->Adapter()), objectInstantiationMap_(Allocator()->Adapter()), invokeToArrowSignatures_(Allocator()->Adapter()), arrowToFuncInterfaces_(Allocator()->Adapter()), @@ -130,6 +131,7 @@ public: Type *GlobalETSNullType() const; Type *GlobalETSUndefinedType() const; Type *GlobalETSAnyType() const; + Type *GlobalETSRelaxedAnyType() const; Type *GlobalETSNeverType() const; Type *GlobalETSStringLiteralType() const; Type *GlobalETSBigIntType() const; @@ -147,6 +149,7 @@ public: ETSObjectType *GlobalETSObjectType() const; ETSUnionType *GlobalETSUnionUndefinedNull() const; ETSUnionType *GlobalETSUnionUndefinedNullObject() const; + ETSObjectType *GlobalBuiltinClassType() const; ETSObjectType *GlobalBuiltinETSResizableArrayType() const; ETSObjectType *GlobalBuiltinETSStringType() const; ETSObjectType *GlobalBuiltinETSBigIntType() const; @@ -267,6 +270,8 @@ public: void CheckCyclicConstructorCall(Signature *signature); 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); @@ -311,6 +316,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) @@ -329,9 +335,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); @@ -341,7 +344,6 @@ public: std::tuple CreateBuiltinArraySignatureInfo(const ETSArrayType *arrayType, size_t dim); Signature *CreateBuiltinArraySignature(const ETSArrayType *arrayType, size_t dim); - std::tuple CheckForDynamicLang(ir::AstNode *declNode, util::StringView assemblerName); ETSObjectType *CreatePromiseOf(Type *type); Signature *CreateSignature(SignatureInfo *info, Type *returnType, ir::ScriptFunction *func); @@ -392,6 +394,7 @@ public: 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); @@ -420,21 +423,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, @@ -474,6 +476,31 @@ public: 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, @@ -565,6 +592,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); @@ -607,7 +635,7 @@ public: 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 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); @@ -662,6 +690,7 @@ public: 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); @@ -679,6 +708,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); @@ -783,15 +818,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); @@ -842,7 +868,7 @@ public: return util::NodeAllocator::ForceSetParent(ProgramAllocator(), std::forward(args)...); } - ArenaVector &PendingConstraintCheckRecords(); + std::vector &PendingConstraintCheckRecords(); size_t &ConstraintCheckScopesCount(); ETSObjectType *GetCachedFunctionalInterface(ir::ETSFunctionType *type); @@ -982,6 +1008,7 @@ private: PropertySearchFlags GetInitialSearchFlags(const ir::MemberExpression *memberExpr); const varbinder::Variable *GetTargetRef(const ir::MemberExpression *memberExpr); Type *GetTypeOfSetterGetter([[maybe_unused]] varbinder::Variable *var); + ETSFunctionType *CreateSyntheticTypeFromOverload(varbinder::Variable *const var); void IterateInVariableContext([[maybe_unused]] varbinder::Variable *const var); bool CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init, checker::Type *annotationType, varbinder::Variable *const bindingVar); @@ -1007,7 +1034,6 @@ private: ir::MethodDefinition *CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature, ir::TypeNode *retTypeAnnotation); - void ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector *statements); void EmitDynamicModuleClassInitCall(); DynamicCallIntrinsicsMap *DynamicCallIntrinsics(bool isConstruct) { @@ -1023,6 +1049,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); @@ -1066,7 +1093,7 @@ private: std::unordered_set &typeAliases); ArrayMap arrayTypes_; - ArenaVector pendingConstraintCheckRecords_; + std::vector pendingConstraintCheckRecords_ {}; ObjectInstantiationMap objectInstantiationMap_; FunctionSignatureMap invokeToArrowSignatures_; FunctionInterfaceMap arrowToFuncInterfaces_; 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/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.h b/ets2panda/checker/TSchecker.h index 4d360a1f4fa9a948ca4eace745779c1adb8b7aaf..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 { diff --git a/ets2panda/checker/checker.cpp b/ets2panda/checker/checker.cpp index 39c4be1b5982acc392442724bc58a9d76b98126a..ab4e17a2e079d1182cf78b6a0e32ecc6eedbaa1d 100644 --- a/ets2panda/checker/checker.cpp +++ b/ets2panda/checker/checker.cpp @@ -164,6 +164,11 @@ bool Checker::IsAnyError() return DiagnosticEngine().IsAnyError(); } +bool Checker::IsDeclForDynamicStaticInterop() const +{ + return Program()->IsDeclForDynamicStaticInterop(); +} + ScopeContext::ScopeContext(Checker *checker, varbinder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_), prevProgram_(checker_->Program()) { @@ -183,11 +188,11 @@ void Checker::CleanUp() } context_ = CheckerContext(this, CheckerStatus::NO_OPTS); relation_ = allocator_->New(this); - identicalResults_.cached.clear(); - assignableResults_.cached.clear(); - comparableResults_.cached.clear(); - uncheckedCastableResults_.cached.clear(); - supertypeResults_.cached.clear(); + 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 c41af2dbd818e3aa0df7266e0b115847fb411979..04d1c376926c01e27f3afc0cb2bc3e37180a3439 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 { @@ -239,6 +239,8 @@ public: return programAllocator_ == nullptr ? allocator_ : programAllocator_; } + bool IsDeclForDynamicStaticInterop() const; + protected: parser::Program *Program() const; void SetProgram(parser::Program *program); @@ -255,11 +257,11 @@ private: varbinder::Scope *scope_ {}; util::DiagnosticEngine &diagnosticEngine_; - RelationHolder identicalResults_ {Allocator(), RelationType::IDENTICAL}; - RelationHolder assignableResults_ {Allocator(), RelationType::ASSIGNABLE}; - RelationHolder comparableResults_ {Allocator(), RelationType::COMPARABLE}; - RelationHolder uncheckedCastableResults_ {Allocator(), RelationType::UNCHECKED_CASTABLE}; - RelationHolder supertypeResults_ {Allocator(), 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_; @@ -481,6 +483,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/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index 3f7cdeda3dd594030a6fbb7f561c4321ae04f191..cbe2c38d4ec82f65b2addbc0f7694aed02f89501 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -96,6 +96,15 @@ bool ETSChecker::CheckIfNumeric(Type *type) return (unboxed != nullptr) && unboxed->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); } +bool ETSChecker::CheckIfFloatingPoint(Type *type) +{ + 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()) { @@ -202,11 +211,36 @@ 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: + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + return true; + default: + break; + } } - if (!right->IsETSBigIntType()) { + if (!leftIsBigInt || !rightIsBigInt) { return false; } @@ -532,10 +566,11 @@ checker::Type *ETSChecker::CheckBinaryOperatorLogical(ir::Expression *left, ir:: right->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF); return CreateETSUnionType({MaybeBoxExpression(left), MaybeBoxExpression(right)}); } - if (right->IsNumberLiteral()) { + + if (right->IsNumberLiteral() && !left->IsNumberLiteral() && leftType->IsBuiltinNumeric()) { return leftType; } - if (left->IsNumberLiteral()) { + if (left->IsNumberLiteral() && !right->IsNumberLiteral() && rightType->IsBuiltinNumeric()) { return rightType; } @@ -655,36 +690,10 @@ std::tuple ETSChecker::CheckBinaryOperatorStrictEqual(ir::Expres } 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(); -} - static Type *HandelReferenceBinaryEquality(ETSChecker *checker, BinaryArithmOperands const &ops) { [[maybe_unused]] auto const [expr, typeL, typeR, reducedL, reducedR] = ops; @@ -722,10 +731,6 @@ static Type *CheckBinaryOperatorEqual(ETSChecker *checker, BinaryArithmOperands return checker->GlobalTypeError(); } - if (typeL->IsETSDynamicType() || typeR->IsETSDynamicType()) { - return CheckOperatorEqualDynamic(checker, ops); - } - if (reducedL->IsETSBooleanType() && reducedR->IsETSBooleanType()) { if (reducedL->IsConstantType() && reducedR->IsConstantType()) { return checker->GetGlobalTypesHolder()->GlobalETSBooleanBuiltinType(); @@ -819,12 +824,7 @@ std::tuple ETSChecker::CheckBinaryOperatorInstanceOf(lexer::Sour return {GlobalETSBooleanBuiltinType(), leftType}; } - if (rightType->IsETSDynamicType() && !rightType->AsETSDynamicType()->HasDecl()) { - LogError(diagnostic::INSTANCEOF_NOT_TYPE, {}, pos); - return {GlobalETSBooleanBuiltinType(), leftType}; - } - - checker::Type *opType = rightType->IsETSDynamicType() ? GlobalBuiltinJSValueType() : GlobalETSObjectType(); + checker::Type *opType = GlobalETSObjectType(); RemoveStatus(checker::CheckerStatus::IN_INSTANCEOF_CONTEXT); return {GlobalETSBooleanBuiltinType(), opType}; @@ -933,6 +933,10 @@ Type *ETSChecker::CheckBinaryOperatorNullishCoalescing(ir::Expression *left, ir: LogError(diagnostic::COALESCE_NOT_REF, {}, pos); } leftType = GetNonNullishType(leftType); + if (leftType->IsTypeError()) { + ES2PANDA_ASSERT(IsAnyError()); + return GlobalTypeError(); + } auto *rightType = MaybeBoxType(right->TsType()); if (IsTypeIdenticalTo(leftType, rightType)) { @@ -1148,6 +1152,12 @@ std::tuple ETSChecker::CheckArithmeticOperations( if (tsType->IsETSPrimitiveType()) { tsType = MaybeBoxType(tsType); } + if (left->TsType()->IsTypeError()) { + left->SetTsType(tsType); + } + if (right->TsType()->IsTypeError()) { + right->SetTsType(tsType); + } return {tsType, tsType}; } @@ -1163,6 +1173,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}; diff --git a/ets2panda/checker/ets/assignAnalyzer.cpp b/ets2panda/checker/ets/assignAnalyzer.cpp index 8b29b6e855f4fd3acb58d88e3409dcc3758e3a40..d5d379b49b766e6bd00725e16be30705bda186f4 100644 --- a/ets2panda/checker/ets/assignAnalyzer.cpp +++ b/ets2panda/checker/ets/assignAnalyzer.cpp @@ -57,6 +57,7 @@ #include "varbinder/scope.h" #include "varbinder/declaration.h" #include "checker/ETSchecker.h" +#include "checker/types/gradualType.h" #include "ir/base/catchClause.h" #include "parser/program/program.h" #include "checker/types/ts/objectType.h" @@ -1212,7 +1213,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: @@ -1351,6 +1352,9 @@ static bool IsDefaultValueType(const Type *type, bool isNonReadonlyField) if (type == nullptr) { return false; } + if (type->IsGradualType()) { + return IsDefaultValueType(type->AsGradualType()->GetBaseType(), isNonReadonlyField); + } return (type->IsETSPrimitiveType() || (type->IsETSObjectType() && type->AsETSObjectType()->IsBoxedPrimitive()) || type->IsETSNeverType() || type->IsETSUndefinedType() || type->IsETSNullType() || (type->PossiblyETSUndefined() && (!type->HasTypeFlag(checker::TypeFlag::GENERIC) || @@ -1394,7 +1398,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); @@ -1459,7 +1463,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/dynamic.cpp b/ets2panda/checker/ets/dynamic.cpp index f5d2544345b31dd7d9677a2248c32ca4b9186ba8..e5530fc8122ee9aa86ca28fe1d56038027ba148f 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" @@ -94,147 +92,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) { @@ -325,33 +182,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) @@ -385,94 +215,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) { @@ -510,227 +252,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->AddStatement(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->SetAssemblerName(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); - auto *invokeSignature = invokeMethod->TsType()->AsETSFunctionType()->CallSignatures()[0]; - - 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/function.cpp b/ets2panda/checker/ets/function.cpp index 40338b0c4a6ec6792eec8ac8b4b5b37f7a48b6d4..d2423c24ca3599392970698148dbf483385ac800 100644 --- a/ets2panda/checker/ets/function.cpp +++ b/ets2panda/checker/ets/function.cpp @@ -23,6 +23,7 @@ #include "checker/ets/typeRelationContext.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/base/catchClause.h" #include "ir/base/classDefinition.h" @@ -82,8 +83,8 @@ bool ETSChecker::IsCompatibleTypeArgument(ETSTypeParameter *typeParam, Type *typ bool ETSChecker::EnhanceSubstitutionForReadonly(const ArenaVector &typeParams, ETSReadonlyType *paramType, Type *argumentType, Substitution *substitution) { - return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying(), GetReadonlyType(argumentType), - substitution); + return EnhanceSubstitutionForType(typeParams, paramType->GetUnderlying()->MaybeBaseTypeOfGradualType(), + GetReadonlyType(argumentType), substitution); } /* A very rough and imprecise partial type inference */ @@ -115,6 +116,10 @@ bool ETSChecker::EnhanceSubstitutionForType(const ArenaVector &typeParam if (paramType->IsETSReadonlyType()) { return EnhanceSubstitutionForReadonly(typeParams, paramType->AsETSReadonlyType(), argumentType, substitution); } + if (paramType->IsGradualType()) { + return EnhanceSubstitutionForType(typeParams, paramType->AsGradualType()->GetBaseType(), argumentType, + substitution); + } if (paramType->IsETSUnionType()) { return EnhanceSubstitutionForUnion(typeParams, paramType->AsETSUnionType(), argumentType, substitution); } @@ -152,7 +157,8 @@ bool ETSChecker::EnhanceSubstitutionForUnion(const ArenaVector &typePara if (!argumentType->IsETSUnionType()) { bool foundValid = false; for (Type *ctype : paramUn->ConstituentTypes()) { - foundValid |= ValidateTypeSubstitution(typeParams, ctype, argumentType, substitution); + foundValid |= + ValidateTypeSubstitution(typeParams, ctype->MaybeBaseTypeOfGradualType(), argumentType, substitution); } return foundValid; } @@ -390,13 +396,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 { @@ -433,7 +439,8 @@ bool ETSChecker::ValidateSignatureRequiredParams(Signature *substitutedSig, auto &argument = arguments[index]; // #22952: infer optional parameter heuristics - auto const paramType = GetNonNullishType(substitutedSig->Params()[index]->TsType()); + auto const paramType = + GetNonNullishType(substitutedSig->Params()[index]->TsType())->MaybeBaseTypeOfGradualType(); if (argument->IsObjectExpression()) { if (!paramType->IsETSObjectType()) { return false; @@ -955,8 +962,9 @@ Signature *ETSChecker::FindMostSpecificSignature(const ArenaVector static Type *GetParameterTypeOrRestAtIdx(checker::ETSChecker *checker, Signature *sig, const size_t idx) { - return idx < sig->ArgCount() ? sig->Params().at(idx)->TsType() - : checker->GetElementTypeOfArray(sig->RestVar()->TsType()); + return idx < sig->ArgCount() + ? sig->Params().at(idx)->TsType()->MaybeBaseTypeOfGradualType() + : checker->GetElementTypeOfArray(sig->RestVar()->TsType())->MaybeBaseTypeOfGradualType(); } static void InitMostSpecificType(TypeRelation *relation, const ArenaVector &signatures, @@ -1095,8 +1103,9 @@ void ETSChecker::CollectSuitableSignaturesForTypeInference( } for (auto *sig : signatures) { - if (paramIdx >= sig->Params().size() || !sig->Params().at(paramIdx)->TsType()->IsETSObjectType() || - !sig->Params().at(paramIdx)->TsType()->AsETSObjectType()->IsGlobalETSObjectType()) { + auto paramType = sig->Params().at(paramIdx)->TsType()->MaybeBaseTypeOfGradualType(); + if (paramIdx >= sig->Params().size() || !paramType->IsETSObjectType() || + !paramType->AsETSObjectType()->IsGlobalETSObjectType()) { bestSignaturesForParameter.insert({paramIdx, sig}); } } @@ -1218,6 +1227,10 @@ Signature *ETSChecker::ResolvePotentialTrailingLambdaWithReceiver(ir::CallExpres ArenaVector sigContainLambdaWithReceiverAsParam(ProgramAllocator()->Adapter()); Signature *signature = nullptr; for (auto sig : signatures) { + if (!sig->HasFunction()) { + continue; + } + if (!IsLastParameterLambdaWithReceiver(sig)) { normalSig.emplace_back(sig); continue; @@ -1276,6 +1289,7 @@ 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); } @@ -1347,6 +1361,12 @@ Signature *ETSChecker::ResolveCallExpressionAndTrailingLambda(ArenaVector &arguments, const lexer::SourcePosition &pos) { + auto *var = type->GetProperty(compiler::Signatures::CONSTRUCTOR_OVERLOAD_DEFINITION_NAME, + PropertySearchFlags::SEARCH_STATIC_METHOD); + if (var != nullptr && var->TsType()->IsETSFunctionType()) { + return MatchOrderSignatures(var->TsType()->AsETSFunctionType()->CallSignatures(), nullptr, arguments, pos, + TypeRelationFlag::NONE); + } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return ValidateSignatures(type->ConstructSignatures(), nullptr, arguments, pos, "construct"); } @@ -1539,10 +1559,6 @@ static varbinder::LocalVariable *SetupSignatureParameter(ir::ETSParameterExpress // Should be moved to original ComposeSignatureInfo after AST fix static bool AppendSignatureInfoParam(ETSChecker *checker, SignatureInfo *sigInfo, ir::ETSParameterExpression *param) { - if (param->IsRestParameter()) { - return true; - } - auto variable = SetupSignatureParameter(param, [checker, param]() { if (param->TypeAnnotation() != nullptr) { auto type = param->TypeAnnotation()->GetType(checker); @@ -1561,6 +1577,9 @@ static bool AppendSignatureInfoParam(ETSChecker *checker, SignatureInfo *sigInfo if (variable == nullptr) { // #23134 return false; } + if (param->IsRestParameter()) { + return true; + } sigInfo->params.push_back(variable); if (!param->IsOptional()) { @@ -1686,13 +1705,8 @@ checker::ETSFunctionType *ETSChecker::BuildMethodType(ir::ScriptFunction *func) { ES2PANDA_ASSERT(!func->IsArrow()); 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; } @@ -1820,14 +1834,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, @@ -2437,7 +2451,7 @@ void ETSChecker::CollectReturnStatements(ir::AstNode *parent) }); } -ArenaVector &ETSChecker::PendingConstraintCheckRecords() +std::vector &ETSChecker::PendingConstraintCheckRecords() { return pendingConstraintCheckRecords_; } @@ -2489,4 +2503,324 @@ 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) { + 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() && ValidateArgumentAsIdentifier(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 += ")"; + LogError(diagnostic::NO_MATCHING_SIG, {signatureKind, msg.c_str()}, pos); + return; + } + + msg += ", "; + } +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ets/function_helpers.h b/ets2panda/checker/ets/function_helpers.h index 2ed4c6b430b254b5237d17726eaa2200711f2c37..7c22e2f83b690b8c753b021b516bb43c0a0e93d7 100644 --- a/ets2panda/checker/ets/function_helpers.h +++ b/ets2panda/checker/ets/function_helpers.h @@ -116,81 +116,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; } @@ -204,12 +205,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 13f3575f66edf748f696885e27d4bbec8a1061e9..d8d087c094a6d0bf5616b26a292be822ed695d17 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -13,11 +13,13 @@ * limitations under the License. */ +#include #include "checker/ETSchecker.h" #include "checker/types/globalTypesHolder.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" @@ -71,6 +73,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() && @@ -99,11 +107,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"; @@ -289,6 +299,9 @@ Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident) if (resolved == nullptr) { resolved = ExtraCheckForResolvedError(ident); if (resolved == nullptr) { + if (VarBinder()->GetScope()->IsClassScope() && this->IsAnyError()) { + return ident->SetTsType(GlobalTypeError()); + } auto [decl, var] = VarBinder()->NewVarDecl( ident->Start(), !ident->IsErrorPlaceHolder() ? ident->Name() : compiler::GenName(ProgramAllocator()).View()); @@ -308,6 +321,10 @@ Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident) ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start()); SaveCapturedVariable(resolved, ident); + if (IsVariableOverloadDeclaration(resolved)) { + return CreateSyntheticTypeFromOverload(resolved); + } + return GetTypeOfVariable(resolved); } @@ -390,10 +407,15 @@ Type *ETSChecker::GetUnaryOperatorPromotedType(Type *type, const bool doPromotio return type; } -Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool isCondExpr) +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; } @@ -652,7 +674,7 @@ static bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *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; @@ -764,38 +786,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) { - ES2PANDA_ASSERT(property->IsProperty()); - auto p = property->AsProperty(); - - p->Key()->SetPreferredType(typeArguments[0]); - p->Value()->SetPreferredType(typeArguments[1]); - - Type *keyType = p->Key()->Check(checker); - Type *valueType = p->Value()->Check(checker); + if (property->IsSpreadElement()) { + CheckRecordSpreadElement(property->AsSpreadElement(), typeArguments, checker, property->Start()); + continue; + } + if (!property->IsProperty()) { + checker->LogError(diagnostic::INVALID_RECORD_PROPERTY, property->Start()); + continue; + } - 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); } } @@ -1544,7 +1615,7 @@ bool ETSChecker::CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration *t ir::TypeNode *ETSChecker::ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclaration *typeAliasNode, const ir::TSTypeParameterInstantiation *typeParams, size_t idx) { - if (typeParams->Params().size() > idx) { + if (typeParams != nullptr && typeParams->Params().size() > idx) { return typeParams->Params().at(idx); } @@ -1554,41 +1625,48 @@ ir::TypeNode *ETSChecker::ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclara Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams, ir::TSTypeAliasDeclaration *const typeAliasNode) { - // NOTE (mmartin): modify for default params - if ((typeParams == nullptr) != (typeAliasNode->TypeParams() == nullptr)) { - if (typeParams == nullptr) { + 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(); + if (!isAllTypeParamsHasDefaultType) { LogError(diagnostic::GENERIC_ALIAS_WITHOUT_PARAMS, {}, name->Start()); return GlobalTypeError(); } + } + if (typeParams != nullptr && typeAliasNode->TypeParams() == nullptr) { LogError(diagnostic::NON_GENERIC_ALIAS_WITH_PARAMS, {}, typeParams->Start()); return GlobalTypeError(); } - if (typeParams == nullptr) { + if (typeParams == nullptr && typeAliasNode->TypeParams() == nullptr) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) return GetReferencedTypeBase(name); } - for (auto *const origTypeParam : typeParams->Params()) { - origTypeParam->Check(this); + if (typeParams != nullptr) { + for (auto *const origTypeParam : typeParams->Params()) { + origTypeParam->Check(this); + } + if (CheckMinimumTypeArgsPresent(typeAliasNode, typeParams)) { + return GlobalTypeError(); + } } // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) Type *const aliasType = GetReferencedTypeBase(name); - auto *substitution = NewSubstitution(); - - if (CheckMinimumTypeArgsPresent(typeAliasNode, typeParams)) { - return GlobalTypeError(); - } + auto substitution = Substitution {}; std::set parametersNeedToBeBoxed; - auto expandedAliasType = aliasType->Substitute(Relation(), substitution); + auto expandedAliasType = aliasType->Substitute(Relation(), &substitution); CollectAliasParametersForBoxing(expandedAliasType, parametersNeedToBeBoxed, false); for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); - auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); + auto *typeAliasType = typeAliasTypeName->Variable()->TsType()->MaybeBaseTypeOfGradualType(); if (!typeAliasType->IsETSTypeParameter()) { continue; } @@ -1601,13 +1679,13 @@ Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypePa paramType = boxedType; } } - substitution->insert({typeAliasType->AsETSTypeParameter(), paramType}); // #21835: type argument is not boxed + 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) @@ -1686,8 +1764,8 @@ util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringVi } // Helps to prevent searching for the imported file among external sources if it is the entry program -static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, - const util::StringView &importPath) +parser::Program *ETSChecker::SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, + const util::StringView &importPath) { if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) { return etsBinder->GetGlobalRecordTable()->Program(); @@ -1708,21 +1786,37 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjT program->SetASTChecked(); program->Ast()->Check(this); } + if (program->IsDeclForDynamicStaticInterop()) { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } else { + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath); - BindingsModuleObjectAddProperty( - moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath); + + BindingsModuleObjectAddProperty( + moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath); + } } void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType) @@ -1747,6 +1841,10 @@ Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) return name->Check(this); } + if (name->IsMemberExpression()) { + return name->Check(this); + } + ES2PANDA_ASSERT(name->IsIdentifier()); auto *const var = name->AsIdentifier()->Variable(); @@ -1756,11 +1854,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)); } @@ -1797,6 +1890,7 @@ checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type) if (type->IsTypeError()) { return GlobalTypeError(); } + if (type->IsETSArrayType()) { return type->AsETSArrayType()->ElementType(); } @@ -2294,6 +2388,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 @@ -2301,6 +2402,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; @@ -2807,6 +2915,8 @@ void ETSChecker::SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObje ir::MethodDefinition *getter, ir::MethodDefinition *setter, const bool inExternal) { + auto getProgram = [](ir::AstNode *node) { return node->Range().start.Program(); }; + auto *const classDef = classType->GetDeclNode()->AsClassDefinition(); for (auto &method : {getter, setter}) { if (method == nullptr) { @@ -2822,7 +2932,7 @@ void ETSChecker::SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObje method->Function()->AddModifier(ir::ModifierFlags::OVERRIDE); } - if (inExternal) { + if (inExternal && !getProgram(originalProp)->IsGenAbcForExternal()) { method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL); } @@ -2947,6 +3057,7 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons callExpr->SetCallee(transformedCallee); if (instantiateMethod != nullptr) { + auto lexScope {varbinder::LexicalScope::Enter(VarBinder(), compiler::NearestScope(callExpr))}; // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) auto *argExpr = GenerateImplicitInstantiateArg(std::string(className)); diff --git a/ets2panda/checker/ets/object.cpp b/ets2panda/checker/ets/object.cpp index 4760ea0ca1595004f80122b448752b58439cec75..7cfe16d58eb609267a1f9c2a2877defcbd7daa76 100644 --- a/ets2panda/checker/ets/object.cpp +++ b/ets2panda/checker/ets/object.cpp @@ -16,10 +16,10 @@ #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" @@ -82,8 +82,35 @@ 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; + } + + 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()) { return true; @@ -156,7 +183,7 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) return false; } - Type *superType = classDef->Super()->AsTypeNode()->GetType(this); + auto *superType = classDef->Super()->AsTypeNode()->GetType(this); if (superType == nullptr) { return true; } @@ -166,7 +193,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()) { @@ -188,6 +215,10 @@ bool ETSChecker::ComputeSuperType(ETSObjectType *type) void ETSChecker::ValidateImplementedInterface(ETSObjectType *type, Type *interface, std::unordered_set *extendsSet, const lexer::SourcePosition &pos) { + 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; @@ -402,7 +433,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; } @@ -434,14 +464,24 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte } checker::ETSObjectType *interfaceType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { interfaceType = CreateETSObjectTypeOrBuiltin(interfaceDecl, checker::ETSObjectFlags::INTERFACE); interfaceType->SetVariable(var); - var->SetTsType(interfaceType); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType) : interfaceType; + var->SetTsType(type); } else { interfaceType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(interfaceType) : interfaceType; } + // Save before we mess with savedContext. + bool builtinsInitialized = HasStatus(CheckerStatus::BUILTINS_INITIALIZED); + + auto *enclosingClass = Context().ContainingClass(); + interfaceType->SetEnclosingType(enclosingClass); + CheckerStatus newStatus = CheckerStatus::IN_INTERFACE; + auto savedContext = checker::SavedCheckerContext(this, newStatus, interfaceType); ConstraintCheckScope ctScope(this); if (interfaceDecl->TypeParams() != nullptr) { interfaceType->AddTypeFlag(TypeFlag::GENERIC); @@ -455,11 +495,11 @@ Type *ETSChecker::BuildBasicInterfaceProperties(ir::TSInterfaceDeclaration *inte // Skip this check if the builtins are not initialized. // They will be initialized in different order, // and it is possible that the FunctionType interface is not yet created. - if (HasStatus(CheckerStatus::BUILTINS_INITIALIZED)) { + if (builtinsInitialized) { CheckInterfaceFunctions(interfaceType); } - return interfaceType; + return type; } Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) @@ -475,21 +515,24 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) } checker::ETSObjectType *classType {}; + checker::Type *type {}; if (var->TsType() == nullptr) { classType = CreateETSObjectTypeOrBuiltin(classDef, checker::ETSObjectFlags::CLASS); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType) : classType; classType->SetVariable(var); - var->SetTsType(classType); + var->SetTsType(type); if (classDef->IsAbstract()) { classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); } } else if (var->TsType()->IsETSObjectType()) { classType = var->TsType()->AsETSObjectType(); + type = Program()->IsDeclForDynamicStaticInterop() ? CreateGradualType(classType) : classType; } else { ES2PANDA_ASSERT(IsAnyError()); return GlobalTypeError(); } - classDef->SetTsType(classType); + classDef->SetTsType(type); ConstraintCheckScope ctScope(this); if (classDef->TypeParams() != nullptr) { @@ -513,7 +556,7 @@ Type *ETSChecker::BuildBasicClassProperties(ir::ClassDefinition *classDef) GetInterfaces(classType); } ctScope.TryCheckConstraints(); - return classType; + return type; } ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType) @@ -549,49 +592,57 @@ static void ResolveDeclaredMethodsOfObject(ETSChecker *checker, const ETSObjectT { for (auto &[_, it] : scope->InstanceMethodScope()->Bindings()) { (void)_; - auto *method = it->Declaration()->Node()->AsMethodDefinition(); - auto *function = method->Function(); - if (function->IsProxy()) { - continue; - } + if (it->Declaration()->Node()->IsMethodDefinition()) { + auto *method = it->Declaration()->Node()->AsMethodDefinition(); + auto *function = method->Function(); + 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(); - auto *method = it->Declaration()->Node()->AsMethodDefinition(); - auto *function = method->Function(); + if (function->IsProxy()) { + continue; + } - 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()); } } @@ -617,6 +668,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; } @@ -695,6 +750,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()) { @@ -893,6 +952,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; @@ -1082,6 +1145,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); @@ -1101,7 +1167,7 @@ void ETSChecker::MaybeReportErrorsForOverridingValidation(ArenaVectorHasObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS)) { + if (GetCachedComputedAbstracts()->find(classType) != GetCachedComputedAbstracts()->end()) { return; } @@ -1133,8 +1199,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, @@ -1187,7 +1251,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); } @@ -1520,7 +1584,7 @@ void ETSChecker::CheckInnerClassMembers(const ETSObjectType *classType) bool ETSChecker::ValidateArrayIndex(ir::Expression *const expr, bool relaxed) { - auto const expressionType = expr->Check(this); + auto const expressionType = expr->Check(this)->MaybeBaseTypeOfGradualType(); if (expressionType->IsTypeError()) { return false; } @@ -1856,7 +1920,11 @@ varbinder::Variable *ETSChecker::GetExtensionFuncVarInGlobalFunction(const ir::M { auto propertyName = memberExpr->Property()->AsIdentifier()->Name(); auto *globalFunctionVar = Scope()->FindInGlobal(propertyName, VO::STATIC_METHODS).variable; - if (globalFunctionVar == nullptr || !IsExtensionETSFunctionType(globalFunctionVar->TsType())) { + if (globalFunctionVar == nullptr) { + return nullptr; + } + + if (!IsExtensionETSFunctionType(GetTypeOfVariable(globalFunctionVar))) { return nullptr; } @@ -1913,6 +1981,10 @@ PropertySearchFlags ETSChecker::GetInitialSearchFlags(const ir::MemberExpression } 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; @@ -2177,13 +2249,6 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member { 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 (target->GetDeclNode() != nullptr && target->GetDeclNode()->IsClassDefinition() && !target->GetDeclNode()->AsClassDefinition()->IsClassDefinitionChecked()) { // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) @@ -2191,6 +2256,9 @@ std::vector ETSChecker::ResolveMemberReference(const ir::Member } const auto *const targetRef = GetTargetRef(memberExpr); auto searchFlag = GetSearchFlags(memberExpr, targetRef); + if (target->HasObjectFlag(ETSObjectFlags::LAZY_IMPORT_OBJECT)) { + searchFlag |= PropertySearchFlags::SEARCH_INSTANCE; + } auto searchName = target->GetReExportAliasValue(memberExpr->Property()->AsIdentifier()->Name()); auto *prop = target->GetProperty(searchName, searchFlag); varbinder::Variable *const globalFunctionVar = ResolveInstanceExtension(memberExpr); @@ -2231,6 +2299,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 || @@ -2258,9 +2337,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(); @@ -2314,6 +2390,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; } @@ -2332,6 +2410,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(); } diff --git a/ets2panda/checker/ets/typeCheckingHelpers.cpp b/ets2panda/checker/ets/typeCheckingHelpers.cpp index 39970f37d6ff2b18773d68e97ec725bc316b7a93..f2a5fd239c3e1880de04b922ddbd820ecd817e54 100644 --- a/ets2panda/checker/ets/typeCheckingHelpers.cpp +++ b/ets2panda/checker/ets/typeCheckingHelpers.cpp @@ -16,6 +16,7 @@ #include "checker/checker.h" #include "checker/ets/wideningConverter.h" #include "checker/types/globalTypesHolder.h" +#include "checker/types/gradualType.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsPartialTypeParameter.h" #include "ir/base/catchClause.h" @@ -87,6 +88,9 @@ bool ETSChecker::CheckNonNullish(ir::Expression const *expr) Type *ETSChecker::GetNonNullishType(Type *type) { + if (type->IsGradualType()) { + return GetNonNullishType(type->AsGradualType()->GetBaseType()); + } if (type->DefinitelyNotETSNullish()) { return type; } @@ -192,7 +196,8 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) ArenaVector nullishTypes(ProgramAllocator()->Adapter()); ArenaVector notNullishTypes(ProgramAllocator()->Adapter()); - for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) { + for (auto *ctype : type->AsETSUnionType()->ConstituentTypes()) { + auto constituentType = ctype->MaybeBaseTypeOfGradualType(); if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) { nullishTypes.push_back(constituentType); } else { @@ -214,6 +219,9 @@ std::pair ETSChecker::RemoveNullishTypes(Type *type) template static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv) { + if (type->IsGradualType()) { + return MatchConstituentOrConstraint(type->AsGradualType()->GetBaseType(), pred, trv); + } auto const traverse = [&pred, &trv](const Type *ttype) { return MatchConstituentOrConstraint(ttype, pred, trv); }; @@ -265,6 +273,30 @@ bool Type::PossiblyETSUndefined() const [](const Type *t) { return !t->IsETSNonNullishType(); }); } +static bool ObjectPossiblyInForeignDomain(ETSObjectType const *type) +{ + if (type->IsGlobalETSObjectType()) { + return true; + } + auto dnode = type->GetDeclNode(); + if (dnode == nullptr) { + return false; + } + auto lang = dnode->IsClassDefinition() ? dnode->AsClassDefinition()->Language() + : dnode->AsTSInterfaceDeclaration()->Language(); + return lang != Language::Id::ETS; +} + +bool Type::PossiblyInForeignDomain() const +{ + return MatchConstituentOrConstraint( + this, + [](const Type *t) { + return t->IsETSAnyType() || (t->IsETSObjectType() && ObjectPossiblyInForeignDomain(t->AsETSObjectType())); + }, + []([[maybe_unused]] const Type *t) { return true; }); +} + bool Type::PossiblyETSNullish() const { return MatchConstituentOrConstraint( @@ -335,7 +367,7 @@ bool Type::IsETSMethodType() const TypeFlag::ETS_TYPE_PARAMETER | TypeFlag::WILDCARD | TypeFlag::ETS_NONNULLISH | TypeFlag::ETS_REQUIRED_TYPE_PARAMETER | TypeFlag::ETS_ANY | TypeFlag::ETS_NEVER | TypeFlag::ETS_UNION | TypeFlag::ETS_ARRAY | TypeFlag::FUNCTION | TypeFlag::ETS_PARTIAL_TYPE_PARAMETER | TypeFlag::ETS_TUPLE | - TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY; + TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY | TypeFlag::GRADUAL_TYPE; // Issues if (type->IsETSVoidType()) { // NOTE(vpukhov): #19701 void refactoring @@ -445,6 +477,30 @@ Type *ETSChecker::GetTypeOfSetterGetter(varbinder::Variable *const var) return propType->FindSetter()->Params()[0]->TsType(); } +ETSFunctionType *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); + 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); + } + + for (auto &s : signatures) { + syntheticFunctionType->AddCallSignature(s); + } + + return syntheticFunctionType; +} + void ETSChecker::IterateInVariableContext(varbinder::Variable *const var) { // Before computing the given variables type, we have to make a new checker context frame so that the checking is @@ -461,10 +517,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()); @@ -563,14 +619,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); - if (importData->import->IsPureDynamic()) { - return GlobalBuiltinDynamicType(importData->import->Language()); - } - } - checker::SavedCheckerContext savedContext(this, CheckerStatus::NO_OPTS); checker::ScopeContext scopeCtx(this, var->GetScope()); IterateInVariableContext(var); @@ -668,7 +716,8 @@ Type *ETSChecker::GuaranteedTypeForUnionFieldAccess(ir::MemberExpression *member { const auto &types = etsUnionType->ConstituentTypes(); ArenaVector apparentTypes {ProgramAllocator()->Adapter()}; - const auto &propertyName = memberExpression->Property()->AsIdentifier()->Name(); + const auto *prop = memberExpression->Property(); + 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; @@ -769,7 +818,6 @@ Type *ETSChecker::GetTypeFromTypeAliasReference(varbinder::Variable *var) aliasTypeNode->Check(this); Type *targetType = aliasTypeNode->TypeAnnotation()->GetType(this); typeAliasType->AsETSTypeAliasType()->SetTargetType(targetType); - typeAliasType->AsETSTypeAliasType()->ApplySubstitution(Relation()); var->SetTsType(targetType); return targetType; @@ -781,8 +829,14 @@ Type *ETSChecker::GetTypeFromInterfaceReference(varbinder::Variable *var) return var->TsType(); } + CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT; + status &= this->Context().Status(); + this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT; + auto *interfaceType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); var->SetTsType(interfaceType); + + this->Context().Status() |= status; return interfaceType; } @@ -794,8 +848,13 @@ Type *ETSChecker::GetTypeFromClassReference(varbinder::Variable *var) auto classDef = var->Declaration()->Node()->AsClassDefinition(); + CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT; + status &= this->Context().Status(); + this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT; + auto *classType = BuildBasicClassProperties(classDef); var->SetTsType(classType); + this->Context().Status() |= status; return classType; } @@ -1308,10 +1367,6 @@ 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()) { auto unboxedTargetType = MaybeUnboxInRelation(target); @@ -1587,4 +1642,180 @@ void ETSChecker::CheckExceptionClauseType(const std::vectorLogError(diagnostic::OVERLOAD_MODIFIERS_ABSTRACT, {}, pos); + return false; + } + + if (((overLoadAliasFlags ^ overloadedMethodFlags) & (ir::ModifierFlags::STATIC | ir::ModifierFlags::ASYNC)) != 0) { + checker->LogError(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; + } + + if ((overLoadAliasFlags & ir::ModifierFlags::EXPORT) != 0 && + (overloadedMethodFlags & ir::ModifierFlags::EXPORT) == 0) { + checker->LogError(diagnostic::OVERLOADED_NAME_MUST_ALSO_EXPORTED, {}, 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 +{ + 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()); + + 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; + } + } +} + +void ETSChecker::CheckInterfaceMethodOverloadDeclaration(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); + ident->SetTsType(identType); + 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 fd835f0b7ae187fb20ab2332e230dc9cace03a73..fda77242a88ff3eb169751f630e4ff9fa0055c92 100644 --- a/ets2panda/checker/ets/typeCreation.cpp +++ b/ets2panda/checker/ets/typeCreation.cpp @@ -17,9 +17,9 @@ #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" @@ -85,10 +85,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(); } @@ -98,10 +98,10 @@ ETSResizableArrayType *ETSChecker::CreateETSResizableArrayType(Type *element) ETSResizableArrayType *arrayType = GlobalBuiltinETSResizableArrayType()->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) @@ -125,6 +125,30 @@ ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType, bool isCachePoll return arrayType; } +Type *ETSChecker::CreateGradualType(Type *type, Language const lang) +{ + if (type == nullptr) { + return type; + } + if (type->IsGradualType()) { + return type; + } + if (!type->PossiblyInForeignDomain()) { + return type; + } + if (type->IsETSAnyType()) { + return type; + } + if (type->IsETSUnionType()) { + ArenaVector copied(ProgramAllocator()->Adapter()); + for (auto const &t : type->AsETSUnionType()->ConstituentTypes()) { + copied.push_back(CreateGradualType(t, lang)); + } + return CreateETSUnionType(std::move(copied)); + } + return ProgramAllocator()->New(type); +} + Type *ETSChecker::CreateETSUnionType(Span constituentTypes) { if (constituentTypes.empty()) { @@ -158,17 +182,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; @@ -198,6 +211,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; } @@ -341,56 +355,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()); - } - - if (internalName == compiler::Signatures::BUILTIN_ARRAY) { - return ProgramAllocator()->New(ProgramAllocator(), name, - std::make_tuple(declNode, flags, Relation())); + } else if (internalName == compiler::Signatures::BUILTIN_ARRAY) { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, + std::make_tuple(declNode, flags, Relation())); + } else { + objectType = ProgramAllocator()->New(ProgramAllocator(), name, internalName, + std::make_tuple(declNode, flags, Relation())); } - - 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, @@ -445,10 +430,10 @@ ETSObjectType *ETSChecker::CreatePromiseOf(Type *type) ETSObjectType *const promiseType = GlobalBuiltinPromiseType(); ES2PANDA_ASSERT(promiseType->TypeArguments().size() == 1U); - Substitution *substitution = NewSubstitution(); - EmplaceSubstituted(substitution, promiseType->TypeArguments()[0]->AsETSTypeParameter()->GetOriginal(), type); + auto substitution = Substitution {}; + 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 f2aecd19fbb5122d9facfc69dc153844cedf8252..80bfff4d4ebc492025a92f10c9e7d15cba978d00 100644 --- a/ets2panda/checker/ets/typeRelationContext.cpp +++ b/ets2panda/checker/ets/typeRelationContext.cpp @@ -84,7 +84,6 @@ void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParame while (typeArgTypes.size() < type->TypeArguments().size()) { Type *defaultType = nullptr; - if (type->TypeArguments().at(typeArgTypes.size())->IsETSTypeParameter()) { defaultType = type->TypeArguments().at(typeArgTypes.size())->AsETSTypeParameter()->GetDefaultType(); } else { @@ -137,7 +136,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(); } @@ -153,20 +152,20 @@ 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->InsertInstantiationMap(hash, result_->AsETSObjectType()); result_->AddTypeFlag(TypeFlag::GENERIC); diff --git a/ets2panda/checker/ets/utilityTypeHandlers.cpp b/ets2panda/checker/ets/utilityTypeHandlers.cpp index c267defa3c8f07b568c3720c739da8bc9d218139..e893a1a9513ca7215bef08aa69d9c30cb8641f05 100644 --- a/ets2panda/checker/ets/utilityTypeHandlers.cpp +++ b/ets2panda/checker/ets/utilityTypeHandlers.cpp @@ -23,6 +23,8 @@ #include "varbinder/ETSBinder.h" #include "checker/types/ets/etsPartialTypeParameter.h" +#include + namespace ark::es2panda::checker { std::optional ETSChecker::GetUtilityTypeTypeParamNode( @@ -123,6 +125,11 @@ Type *ETSChecker::CreatePartialType(Type *const typeToBePartial) return typeToBePartial; } + if (typeToBePartial->IsGradualType()) { + // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) + return CreatePartialType(typeToBePartial->AsGradualType()->GetBaseType()); + } + if (typeToBePartial->IsETSTypeParameter()) { return CreatePartialTypeParameter(typeToBePartial->AsETSTypeParameter()); } @@ -149,7 +156,9 @@ Type *ETSChecker::CreatePartialTypeClass(ETSObjectType *typeToBePartial, ir::Ast // Check if we've already generated the partial class, then don't do it again const auto classNameToFind = - partialProgram == VarBinder()->Program() || VarBinder()->IsGenStdLib() ? partialName : partialQualifiedName; + partialProgram == VarBinder()->Program() || VarBinder()->IsGenStdLib() || partialProgram->IsGenAbcForExternal() + ? partialName + : partialQualifiedName; if (auto *var = SearchNamesInMultiplePrograms({partialProgram, VarBinder()->Program()}, {classNameToFind, partialName}); var != nullptr) { @@ -388,8 +397,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) @@ -858,6 +867,10 @@ Type *ETSChecker::CreatePartialTypeClassDef(ir::ClassDefinition *const partialCl ? GlobalETSObjectType() : classDef->Super()->TsType()); + if (partialSuper == partialType) { + LogError(diagnostic::CYCLIC_CLASS_SUPER_TYPE, {}, classDef->Start()); + return partialType; + } partialType->SetSuperType(partialSuper->AsETSObjectType()); } diff --git a/ets2panda/checker/ets/validateHelpers.cpp b/ets2panda/checker/ets/validateHelpers.cpp index 8d28ba8fd72a31ba17741f4a1d910c139f7da1a0..96463d59ba4ed4f3c938d83f41499a1134094168 100644 --- a/ets2panda/checker/ets/validateHelpers.cpp +++ b/ets2panda/checker/ets/validateHelpers.cpp @@ -64,11 +64,15 @@ void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, T } 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 +133,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 +150,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; } @@ -264,7 +273,7 @@ void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaratio ir::TypeNode *typeParamType = nullptr; - if (exactTypeParams->Params().size() > typeParamIdx) { + if (exactTypeParams != nullptr && exactTypeParams->Params().size() > typeParamIdx) { typeParamType = exactTypeParams->Params().at(typeParamIdx); } else { typeParamType = typeAliasNode->TypeParams()->Params().at(typeParamIdx)->DefaultType(); diff --git a/ets2panda/checker/types/ets/etsAnyType.cpp b/ets2panda/checker/types/ets/etsAnyType.cpp index d9b87e49a24d5bef65de3d0ed9572f21a58fd5ae..52793689c9b76084dfc5ac556f7a9a53ef7d1319 100644 --- a/ets2panda/checker/types/ets/etsAnyType.cpp +++ b/ets2panda/checker/types/ets/etsAnyType.cpp @@ -22,7 +22,7 @@ namespace ark::es2panda::checker { void ETSAnyType::Identical(TypeRelation *relation, Type *other) { - relation->Result(other->IsAnyType()); + relation->Result(other->IsETSAnyType()); } void ETSAnyType::AssignmentTarget(TypeRelation *relation, Type *source) diff --git a/ets2panda/checker/types/ets/etsAnyType.h b/ets2panda/checker/types/ets/etsAnyType.h index 1a315b36d603d3e67d3e199af08e5407844a2eae..7d12b6a004e77c58e9e50f6f0b2b2a2691dd5668 100644 --- a/ets2panda/checker/types/ets/etsAnyType.h +++ b/ets2panda/checker/types/ets/etsAnyType.h @@ -22,6 +22,7 @@ namespace ark::es2panda::checker { class ETSAnyType : public Type { public: ETSAnyType() : Type(TypeFlag::ETS_ANY) {} + explicit ETSAnyType(bool isRelaxedAny) : Type(TypeFlag::ETS_ANY), isRelaxedAny_(isRelaxedAny) {} void Identical(TypeRelation *relation, Type *other) override; void AssignmentTarget(TypeRelation *relation, Type *source) override; @@ -38,6 +39,14 @@ public: TypeFacts GetTypeFacts() const override; Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + bool IsRelaxedAny() const + { + return isRelaxedAny_; + } + +private: + bool isRelaxedAny_ = false; }; } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/ets/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 cd6be095d669aa48aa881fe516e5ed3a4bfb8361..0000000000000000000000000000000000000000 --- a/ets2panda/checker/types/ets/etsDynamicType.cpp +++ /dev/null @@ -1,119 +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); - 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(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 bea6d5044be502ebcb8d4ccb5c7f843fc3f71f85..2ff91ba0ca4a748b24dd7df25ed23dc97d4d1dab 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; diff --git a/ets2panda/checker/types/ets/etsEnumType.h b/ets2panda/checker/types/ets/etsEnumType.h index 79e5212669584ca58dc46e9bc27fe6cb6e23ef2f..3005b78053a406f8ef707e2410d7d7cc090593a4 100644 --- a/ets2panda/checker/types/ets/etsEnumType.h +++ b/ets2panda/checker/types/ets/etsEnumType.h @@ -91,6 +91,7 @@ public: } return false; } + Type *GetBaseEnumElementType(ETSChecker *checker); private: void InitElementsShortcuts(ir::ClassDefinition *declNode) diff --git a/ets2panda/checker/types/ets/etsFunctionType.cpp b/ets2panda/checker/types/ets/etsFunctionType.cpp index 5a9a176d9c1f62b5e781ae8a37928c6eb2c9aed3..0ec3b4ecaca3259907143dc8f1e7358d05407ff4 100644 --- a/ets2panda/checker/types/ets/etsFunctionType.cpp +++ b/ets2panda/checker/types/ets/etsFunctionType.cpp @@ -74,19 +74,19 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, if (signature->RestVar() != nullptr) { auto nPosParams = signature->Params().size(); auto *functionN = checker->GlobalBuiltinFunctionType(nPosParams, true)->AsETSObjectType(); - auto *substitution = checker->NewSubstitution(); + auto substitution = Substitution {}; 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()[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); + 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; } @@ -99,15 +99,15 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, } auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false)->AsETSObjectType(); - auto *substitution = checker->NewSubstitution(); + 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); @@ -179,10 +179,10 @@ 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 < super->MinArgCount(); ix++) { if (!enhance(sub->GetSignatureInfo()->params[ix]->TsType(), super->GetSignatureInfo()->params[ix]->TsType())) { @@ -194,7 +194,7 @@ static Signature *EnhanceSignatureSubstitution(TypeRelation *relation, Signature return nullptr; } } - return sub->Substitute(relation, substitution); + return sub->Substitute(relation, &substitution); } static uint8_t SignatureThrowKindToOrder(Signature *sig) @@ -263,16 +263,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/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index c86dbcd5101999c998dfb9548412cf8df49cefd0..e7d2fb4229839b42e0e1cd6d17c135906d3be3b4 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -25,22 +25,38 @@ namespace ark::es2panda::checker { -static std::multimap GetSignaturesForSyntheticType(ETSObjectType const *owner, - util::StringView name, - PropertySearchFlags flags); - 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); + } } } @@ -67,35 +83,51 @@ varbinder::LocalVariable *ETSObjectType::SearchFieldsDecls(util::StringView name 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 (auto res = SearchFieldsDecls(name, flags); res != nullptr) { + return res; + } - if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0)) { - res = GetOwnProperty(name); + 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) @@ -167,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; @@ -175,22 +211,33 @@ static void UpdateDeclarationForGetterSetter(varbinder::LocalVariable *res, cons res->Reset(decl, var->Flags()); } -varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(util::StringView name, +varbinder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(const util::StringView &name, PropertySearchFlags flags) const { - auto signatureSet = GetSignaturesForSyntheticType(this, name, flags); - if (signatureSet.empty()) { + std::vector signatures; + // Since both "first match" and "best match" exist at present, overloadDeclarationCall is temporarily used. After + // "best match" removed, this marking needs to be removed. + bool overloadDeclarationCall = false; + varbinder::LocalVariable *functionalInterface = + CollectSignaturesForSyntheticType(signatures, name, flags, overloadDeclarationCall); + // #22952: the called function *always* returns nullptr + ES2PANDA_ASSERT(functionalInterface == nullptr); + (void)functionalInterface; + + if (signatures.empty()) { 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); - for (auto &s : signatureSet) { - funcType->AddCallSignature(s.second); + for (auto &s : signatures) { + funcType->AddCallSignature(s); } - res->SetTsType(funcType); funcType->SetVariable(res); @@ -205,85 +252,160 @@ ETSFunctionType *ETSObjectType::CreateMethodTypeForProp(util::StringView name) c return GetRelation()->GetChecker()->AsETSChecker()->CreateETSMethodType(name, {{}, Allocator()->Adapter()}); } -static void ReplaceArgInSig(std::multimap *signatureSet, Signature *sigToInsert, - TypeRelation *relation) +bool ETSObjectType::ReplaceArgumentInSignature(std::vector &signatures, Signature *sigToInsert, + TypeRelation *relation) const { - auto range = signatureSet->equal_range(sigToInsert->ArgCount()); - for (auto it = range.first; it != range.second; ++it) { - auto sigToReplace = it->second; - + 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; + return true; } if (relation->IsSupertypeOf(sigToReplace->Owner(), sigToInsert->Owner()) && relation->SignatureIsSupertypeOf(sigToReplace, sigToInsert)) { - signatureSet->erase(it); - signatureSet->insert({sigToInsert->ArgCount(), sigToInsert}); - return; + sigToReplace = sigToInsert; + return true; } } - signatureSet->insert({sigToInsert->ArgCount(), sigToInsert}); + + return false; } -// CC-OFFNXT(huge_depth) solid logic -static void AddSignatureToSignatureSet(std::multimap *signatureSet, - varbinder::LocalVariable *found, TypeRelation *relation, - PropertySearchFlags flags) +void ETSObjectType::AddSignatureFromFunction(std::vector &signatures, PropertySearchFlags flags, + ETSChecker *checker, varbinder::LocalVariable *found) const { - for (auto *sigToInsert : found->TsType()->AsETSFunctionType()->CallSignatures()) { - if (((flags & PropertySearchFlags::IGNORE_ABSTRACT) != 0) && - sigToInsert->HasSignatureFlag(SignatureFlags::ABSTRACT)) { + for (auto *it : found->TsType()->AsETSFunctionType()->CallSignatures()) { + if (std::find(signatures.begin(), signatures.end(), it) != signatures.end()) { + continue; + } + if (((flags & PropertySearchFlags::IGNORE_ABSTRACT) != 0) && it->HasSignatureFlag(SignatureFlags::ABSTRACT)) { continue; } + if (ReplaceArgumentInSignature(signatures, it, checker->Relation())) { + continue; + } + signatures.emplace_back(it); + } +} - if (signatureSet->count(sigToInsert->ArgCount()) != 0U) { - ReplaceArgInSig(signatureSet, sigToInsert, relation); - } else { - signatureSet->insert({sigToInsert->ArgCount(), sigToInsert}); +void ETSObjectType::AddSignatureFromOverload(std::vector &signatures, PropertySearchFlags flags, + varbinder::LocalVariable *found, bool &overloadDeclarationCall) const +{ + auto *overloadDeclaration = found->Declaration()->Node()->AsOverloadDeclaration(); + std::vector methodSignature; + if (overloadDeclaration->Id()->IsErrorPlaceHolder()) { + return; + } + + overloadDeclarationCall |= true; + 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, flags, overloadDeclarationCall); + if (!methodSignature.empty()) { + signatures.emplace_back(methodSignature.front()); } } } -static void CollectSignaturesForSyntheticType(ETSObjectType const *owner, - std::multimap *signatureSet, util::StringView name, - PropertySearchFlags flags) +void ETSObjectType::AddSignatureFromConstructor(std::vector &signatures, + varbinder::LocalVariable *found) const { - if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { - if (auto *found = owner->GetOwnProperty(name); - found != nullptr && !found->TsType()->IsTypeError()) { - ES2PANDA_ASSERT(found->TsType()->IsETSFunctionType()); - AddSignatureToSignatureSet(signatureSet, found, owner->GetRelation(), flags); + 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)); } } +} - if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { - if (auto *found = owner->GetOwnProperty(name); - found != nullptr && !found->TsType()->IsTypeError()) { - ES2PANDA_ASSERT(found->TsType()->IsETSFunctionType()); - AddSignatureToSignatureSet(signatureSet, found, owner->GetRelation(), flags); +void ETSObjectType::AddSignature(std::vector &signatures, PropertySearchFlags flags, ETSChecker *checker, + varbinder::LocalVariable *found, bool &overloadDeclarationCall) const +{ + if (found != nullptr && found->HasFlag(varbinder::VariableFlags::OVERLOAD)) { + if (!found->Declaration()->Node()->IsOverloadDeclaration()) { + return; } + AddSignatureFromOverload(signatures, flags, found, overloadDeclarationCall); + } else if (found != nullptr && !found->TsType()->IsTypeError()) { + ES2PANDA_ASSERT(found->TsType()->IsETSFunctionType()); + AddSignatureFromFunction(signatures, flags, checker, found); + } +} + +varbinder::LocalVariable *ETSObjectType::CollectSignaturesForSyntheticType(std::vector &signatures, + const util::StringView &name, + PropertySearchFlags flags, + bool &overloadDeclarationCall) const +{ + auto *checker = GetRelation()->GetChecker()->AsETSChecker(); + + if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { + auto *found = GetOwnProperty(name); + AddSignature(signatures, flags, checker, found, overloadDeclarationCall); } - if (owner->SuperType() != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { - CollectSignaturesForSyntheticType(owner->SuperType(), signatureSet, name, flags); + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { + auto *found = GetOwnProperty(name); + AddSignature(signatures, flags, checker, found, overloadDeclarationCall); + } + + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) == 0) { + return nullptr; + } + + if (superType_ != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { + superType_->CollectSignaturesForSyntheticType(signatures, name, flags, overloadDeclarationCall); } if ((flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) { - for (auto *interface : owner->Interfaces()) { - CollectSignaturesForSyntheticType(interface, signatureSet, name, flags); + for (auto *interface : Interfaces()) { + interface->CollectSignaturesForSyntheticType(signatures, name, flags, overloadDeclarationCall); } } + + return nullptr; } -static std::multimap GetSignaturesForSyntheticType(ETSObjectType const *owner, - util::StringView name, - PropertySearchFlags flags) +void ETSObjectType::ForEachAllOwnProperties(const PropertyTraverser &cb) const { - std::multimap signatureSet; - CollectSignaturesForSyntheticType(owner, &signatureSet, name, flags); - return signatureSet; + 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 @@ -322,16 +444,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); } @@ -366,35 +514,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; } @@ -441,7 +579,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(); } @@ -451,7 +590,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(); } @@ -533,7 +673,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(); @@ -553,9 +692,6 @@ bool ETSObjectType::AssignmentSource(TypeRelation *const relation, [[maybe_unuse bool ETSObjectType::IsBoxedPrimitive() const { - if (this->IsETSDynamicType()) { - return false; - } if (this->IsETSEnumType()) { return false; } @@ -803,6 +939,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; @@ -946,8 +1090,7 @@ Type *ETSObjectType::Instantiate(ArenaAllocator *const allocator, TypeRelation * ES2PANDA_ASSERT(copiedType->internalName_ == internalName_); ES2PANDA_ASSERT(copiedType->name_ == name_); 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_); @@ -1025,13 +1168,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]); @@ -1044,8 +1187,7 @@ void ETSObjectType::SetCopiedTypeProperties(TypeRelation *const relation, ETSObj ArenaVector &&newTypeArgs, ETSObjectType *base) { 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_); // #25295 Need to do some refactor on baseType for partial @@ -1137,31 +1279,6 @@ static std::pair GetObjectTypeDeclNames(ir:: return {node->AsAnnotationDeclaration()->GetBaseName()->Name(), node->AsAnnotationDeclaration()->InternalName()}; } -static std::tuple 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 *ETSObjectType::CreateETSObjectType(ir::AstNode *declNode, ETSObjectFlags flags) { auto const [name, internalName] = GetObjectTypeDeclNames(declNode); @@ -1173,12 +1290,6 @@ ETSObjectType *ETSObjectType::CreateETSObjectType(ir::AstNode *declNode, ETSObje ES2PANDA_ASSERT(declNode->AsClassDefinition()->IsStringEnumTransformed()); return Allocator()->New(Allocator(), name, internalName, declNode, GetRelation()); } - - if (auto [lang, hasDecl] = CheckForDynamicLang(declNode, internalName); lang.IsDynamic()) { - return Allocator()->New(Allocator(), std::make_tuple(name, internalName, lang), - std::make_tuple(declNode, flags, GetRelation()), hasDecl); - } - if (internalName == compiler::Signatures::BUILTIN_ARRAY) { return Allocator()->New(Allocator(), name, std::make_tuple(declNode, flags, GetRelation())); @@ -1256,16 +1367,17 @@ ETSObjectType *ETSObjectType::SubstituteArguments(TypeRelation *relation, ArenaV } auto *checker = relation->GetChecker()->AsETSChecker(); - auto *substitution = checker->NewSubstitution(); + auto substitution = Substitution {}; ES2PANDA_ASSERT(baseType_ == 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() @@ -1286,44 +1398,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); } } @@ -1496,7 +1612,7 @@ void ETSObjectType::InsertInstantiationMap(util::StringView key, ETSObjectType * ArenaUnorderedMap instantiation( compiler::GetPhaseManager()->Context()->GetChecker()->AsETSChecker()->Allocator()->Adapter()); instantiation.emplace(key, value); - instantiationMap.emplace(this, instantiation); + instantiationMap.emplace(this, std::move(instantiation)); } compiler::GetPhaseManager() ->Context() diff --git a/ets2panda/checker/types/ets/etsObjectType.h b/ets2panda/checker/types/ets/etsObjectType.h index 4c8538f0828a23af6d6cbff53b20055d760fd126..2f81c4be7c98481b663be75eb3c0a94a6bb28383 100644 --- a/ets2panda/checker/types/ets/etsObjectType.h +++ b/ets2panda/checker/types/ets/etsObjectType.h @@ -67,20 +67,11 @@ 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); ETSChecker *GetETSChecker(); - void SetSuperType(ETSObjectType *super) - { - superType_ = super; - } - void SetTypeArguments(ArenaVector &&typeArgs) { #ifndef NDEBUG @@ -347,12 +338,27 @@ public: std::vector ForeignProperties() 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; - varbinder::LocalVariable *CreateSyntheticVarFromEverySignature(util::StringView name, + 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, + bool &overloadDeclarationCall) const; + void AddSignature(std::vector &signatures, PropertySearchFlags flags, ETSChecker *checker, + varbinder::LocalVariable *found, bool &overloadDeclarationCall) const; + void AddSignatureFromFunction(std::vector &signatures, PropertySearchFlags flags, ETSChecker *checker, + varbinder::LocalVariable *found) const; + void AddSignatureFromOverload(std::vector &signatures, PropertySearchFlags flags, + varbinder::LocalVariable *found, bool &overloadDeclarationCall) 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; @@ -415,6 +421,7 @@ private: reExportAlias_(allocator->Adapter()), flags_(std::get(info)), typeArguments_(allocator->Adapter()), + transitiveSupertypes_(allocator->Adapter()), relation_(std::get(info)), constructSignatures_(allocator->Adapter()), properties_ {(void(IS), PropertyMap {allocator->Adapter()})...} @@ -448,6 +455,8 @@ 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; ThreadSafeArenaAllocator *const allocator_; @@ -463,9 +472,12 @@ private: ETSObjectType *enclosingType_ {}; ETSObjectType *baseType_ {}; + // optimized subtyping + ArenaSet transitiveSupertypes_; + // for lazy properties instantiation TypeRelation *relation_ = nullptr; - const Substitution *effectiveSubstitution_ = nullptr; + 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 1b27c6e7c72cecc11817b206a67f133b115be5de..a13f0d46855cb24e9979b6690211803af8200293 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, @@ -61,6 +60,7 @@ enum class ETSObjectFlags : std::uint64_t { EXTENSION_FUNCTION = 1ULL << 35U, FUNCTIONAL_REFERENCE = 1ULL << 36U, + LAZY_IMPORT_OBJECT = 1ULL << 37U, ENUM_OBJECT = INT_ENUM_OBJECT | STRING_ENUM_OBJECT, diff --git a/ets2panda/checker/types/ets/etsTypeAliasType.cpp b/ets2panda/checker/types/ets/etsTypeAliasType.cpp index 411b0bbe09f49f9b0f6b77f5f48c6c045bd031c6..e1e05bdb25bd611438881d21390abebceeb530df 100644 --- a/ets2panda/checker/types/ets/etsTypeAliasType.cpp +++ b/ets2panda/checker/types/ets/etsTypeAliasType.cpp @@ -257,34 +257,6 @@ bool ETSTypeAliasType::SubstituteTypeArgs(TypeRelation *const relation, ArenaVec return anyChange; } -void ETSTypeAliasType::ApplySubstitution(TypeRelation *relation) -{ - ES2PANDA_ASSERT(base_ == nullptr); - - const util::StringView hash = relation->GetChecker()->AsETSChecker()->GetHashFromTypeArguments(typeArguments_); - EmplaceInstantiatedType(hash, this); - - auto getTypes = [this]() { - std::vector types; - - for (auto [name, type] : instantiationMap_) { - if (type->targetType_ == nullptr) { - types.push_back(type); - } - } - - return types; - }; - - std::vector types; - - while (!(types = getTypes(), types.empty())) { - for (auto type : types) { - type->SetTargetType(type->parent_->targetType_->Substitute(relation, type->substitution_)); - } - } -} - void ETSTypeAliasType::SetTypeArguments(ArenaVector typeArguments) { typeArguments_ = std::move(typeArguments); @@ -311,10 +283,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 f5b249c69257d449d8e86183704cfe796d98c0e0..3146b63f51500953d1a669ee47eb22d94419eaf1 100644 --- a/ets2panda/checker/types/ets/etsTypeAliasType.h +++ b/ets2panda/checker/types/ets/etsTypeAliasType.h @@ -82,8 +82,6 @@ public: Type *Substitute(TypeRelation *relation, const Substitution *substitution) override; - void ApplySubstitution(TypeRelation *relation); - void SetTypeArguments(ArenaVector typeArguments); private: @@ -107,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/etsUnionType.cpp b/ets2panda/checker/types/ets/etsUnionType.cpp index cb112121c82df4692d0fcbafdbee36da4bc05c13..34f539ad85602e085170611432a02f385a66d2e2 100644 --- a/ets2panda/checker/types/ets/etsUnionType.cpp +++ b/ets2panda/checker/types/ets/etsUnionType.cpp @@ -214,6 +214,7 @@ void ETSUnionType::LinearizeAndEraseIdentical(TypeRelation *relation, ArenaVecto 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()) { auto const &otherTypes = ct->AsETSUnionType()->ConstituentTypes(); types.insert(types.end(), otherTypes.begin(), otherTypes.end()); @@ -359,7 +360,7 @@ bool ETSUnionType::ExtractType(checker::ETSChecker *checker, checker::Type *sour bool rc = false; auto it = unionTypes.cbegin(); while (it != unionTypes.cend()) { - auto *constituentType = *it; + auto *constituentType = (*it)->MaybeBaseTypeOfGradualType(); // Because 'instanceof' expression does not check for type parameters, then for generic types we should // consider that expressions like 'SomeType' and 'SomeType' are identical for smart casting. // We also have to pass through all the union to process cases like 'C|A|B|C|undefined` @@ -463,6 +464,12 @@ bool ETSUnionType::IsOverlapWith(TypeRelation *relation, Type const *type) const 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; } diff --git a/ets2panda/checker/types/ets/types.h b/ets2panda/checker/types/ets/types.h index ce05b8b154c9e4404c094fa174ca7b7508c4499c..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" diff --git a/ets2panda/checker/types/globalTypesHolder.cpp b/ets2panda/checker/types/globalTypesHolder.cpp index 8aa8a44cb742427b6de4fa69db8db6e22c2d5513..1a3dd612203487fdf9636bb105b19026846b5efa 100644 --- a/ets2panda/checker/types/globalTypesHolder.cpp +++ b/ets2panda/checker/types/globalTypesHolder.cpp @@ -150,6 +150,7 @@ void GlobalTypesHolder::AddEtsSpecificTypes(ArenaAllocator *allocator) globalTypes_[static_cast(GlobalTypeId::ETS_WILDCARD)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::TYPE_ERROR)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ETS_ANY)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::ETS_RELAXED_ANY)] = allocator->New(true); globalTypes_[static_cast(GlobalTypeId::ETS_NEVER)] = allocator->New(); } @@ -159,6 +160,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); @@ -412,6 +414,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)); @@ -442,6 +449,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)); diff --git a/ets2panda/checker/types/globalTypesHolder.h b/ets2panda/checker/types/globalTypesHolder.h index 9374659544914794f727dfec9f3e5a07618f87f7..16d39147b08f7c7280db47bf3362ccff000bba4b 100644 --- a/ets2panda/checker/types/globalTypesHolder.h +++ b/ets2panda/checker/types/globalTypesHolder.h @@ -59,11 +59,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, @@ -270,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(); diff --git a/ets2panda/checker/types/gradualType.cpp b/ets2panda/checker/types/gradualType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cdb568ab47c0aa5a9947f45ba4b76f2c15c0c50e --- /dev/null +++ b/ets2panda/checker/types/gradualType.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gradualType.h" + +#include "checker/ETSchecker.h" +#include "checker/ets/conversion.h" + +namespace ark::es2panda::checker { +void GradualType::Identical(TypeRelation *relation, Type *other) +{ + baseType_->Identical(relation, other); +} + +void GradualType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + baseType_->AssignmentTarget(relation, source); +} + +bool GradualType::AssignmentSource(TypeRelation *relation, Type *target) +{ + return baseType_->AssignmentSource(relation, target); +} + +void GradualType::Compare(TypeRelation *relation, Type *other) +{ + return baseType_->Compare(relation, other); +} + +void GradualType::Cast(TypeRelation *relation, Type *target) +{ + return baseType_->Cast(relation, target); +} + +void GradualType::CastTarget(TypeRelation *relation, Type *source) +{ + return baseType_->CastTarget(relation, source); +} + +void GradualType::IsSubtypeOf(TypeRelation *relation, Type *target) +{ + return baseType_->IsSubtypeOf(relation, target); +} + +void GradualType::IsSupertypeOf(TypeRelation *relation, Type *source) +{ + if (source->IsGradualType()) { + relation->IsSupertypeOf(baseType_, source->AsGradualType()->GetBaseType()); + } else { + baseType_->IsSupertypeOf(relation, source); + } +} + +void GradualType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const +{ + baseType_->ToString(ss); +} + +Type *GradualType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + return baseType_->Instantiate(allocator, relation, globalTypes); +} + +Type *GradualType::Substitute(TypeRelation *relation, const Substitution *substitution) +{ + return baseType_->Substitute(relation, substitution); +} + +void GradualType::ToAssemblerType(std::stringstream &ss) const +{ + baseType_->ToAssemblerType(ss); +} + +void GradualType::ToDebugInfoType(std::stringstream &ss) const +{ + baseType_->ToDebugInfoType(ss); +} + +void GradualType::ToAssemblerTypeWithRank(std::stringstream &ss) const +{ + baseType_->ToAssemblerTypeWithRank(ss); +} + +void GradualType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag) +{ + // The type of array should be Invariant + relation->CheckVarianceRecursively(baseType_, varianceFlag); +} +} // namespace ark::es2panda::checker \ No newline at end of file diff --git a/ets2panda/checker/types/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 47b99e31023cad62a3711fe1788676854774d6a5..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,62 +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(ThreadSafeArenaAllocator *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(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 5ced79c41120e511b5f411031ffe5a045c8275b0..be45282d5f95b09d4a98d5cc6a18a04096a96d88 100644 --- a/ets2panda/checker/types/signature.cpp +++ b/ets2panda/checker/types/signature.cpp @@ -112,7 +112,7 @@ Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, Gl SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); for (size_t idx = 0U; idx < signatureInfo_->params.size(); ++idx) { - auto *const paramType = signatureInfo_->params[idx]->TsType(); + auto *const paramType = signatureInfo_->params[idx]->TsType()->MaybeBaseTypeOfGradualType(); if (paramType->HasTypeFlag(TypeFlag::GENERIC) && paramType->IsETSObjectType()) { copiedInfo->params[idx]->SetTsType(paramType->Instantiate(allocator, relation, globalTypes)); auto originalTypeArgs = paramType->AsETSObjectType()->GetOriginalBaseType()->TypeArguments(); diff --git a/ets2panda/checker/types/signature.h b/ets2panda/checker/types/signature.h index d94e607e8b2a5ab473eb2081fefd38dd9c6fc806..8515f6b0ca5eb8ee717b40fc2b80770db1fc5c31 100644 --- a/ets2panda/checker/types/signature.h +++ b/ets2panda/checker/types/signature.h @@ -91,6 +91,7 @@ enum class SignatureFlags : uint32_t { EXTENSION_FUNCTION = 1U << 18U, DUPLICATE_ASM = 1U << 19U, BRIDGE = 1U << 20U, + DEFAULT = 1U << 21U, INTERNAL_PROTECTED = INTERNAL | PROTECTED, GETTER_OR_SETTER = GETTER | SETTER, diff --git a/ets2panda/checker/types/type.cpp b/ets2panda/checker/types/type.cpp index 090981812fedbf7f10a5ff7e16016de06995cc1d..61a6b8568e82040ac0fe3ebbed737ee8846d6c40 100644 --- a/ets2panda/checker/types/type.cpp +++ b/ets2panda/checker/types/type.cpp @@ -18,6 +18,7 @@ #include "checker/types/typeFlag.h" #include "checker/types/typeRelation.h" #include "checker/types/ets/etsObjectType.h" +#include "checker/types/gradualType.h" #include "checker/checker.h" namespace ark::es2panda::checker { @@ -161,4 +162,13 @@ bool IsTypeError(Type const *tp) return tp != nullptr && tp->IsTypeError(); } +Type *Type::MaybeBaseTypeOfGradualType() +{ + auto res = this; + while (res->IsGradualType()) { + res = res->AsGradualType()->GetBaseType(); + } + return res; +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/types/type.h b/ets2panda/checker/types/type.h index c3d06181decc7f389f81ca16c7184cf7ddfa870e..3840d6ab66b98a8570b957cad0c6b5cde073c655 100644 --- a/ets2panda/checker/types/type.h +++ b/ets2panda/checker/types/type.h @@ -29,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) @@ -45,14 +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) { std::lock_guard lock(idLock_); - static uint64_t typeId = 0; + static uint32_t typeId = 0; + ES2PANDA_ASSERT(typeId < std::numeric_limits::max()); id_ = ++typeId; } @@ -113,6 +114,8 @@ public: bool PossiblyETSValueTyped() const; bool PossiblyETSValueTypedExceptNullish() const; + bool PossiblyInForeignDomain() const; + ETSStringType *AsETSStringType() { ES2PANDA_ASSERT(IsETSObjectType()); @@ -143,25 +146,8 @@ public: return reinterpret_cast(this); } - [[nodiscard]] bool IsETSDynamicType() const noexcept - { - return IsETSObjectType() && HasTypeFlag(TypeFlag::ETS_DYNAMIC_FLAG); - } - [[nodiscard]] bool IsBuiltinNumeric() const noexcept; - ETSDynamicType *AsETSDynamicType() - { - ES2PANDA_ASSERT(IsETSDynamicType()); - return reinterpret_cast(this); - } - - const ETSDynamicType *AsETSDynamicType() const - { - ES2PANDA_ASSERT(IsETSDynamicType()); - return reinterpret_cast(this); - } - ETSAsyncFuncReturnType *AsETSAsyncFuncReturnType() { ES2PANDA_ASSERT(IsETSAsyncFuncReturnType()); @@ -174,28 +160,13 @@ public: return reinterpret_cast(this); } - bool IsETSDynamicFunctionType() const - { - return TypeFlags() == TypeFlag::ETS_DYNAMIC_FUNCTION_TYPE; - } - - ETSDynamicFunctionType *AsETSDynamicFunctionType() - { - ES2PANDA_ASSERT(IsETSDynamicFunctionType()); - return reinterpret_cast(this); - } - - const ETSDynamicFunctionType *AsETSDynamicFunctionType() const - { - ES2PANDA_ASSERT(IsETSDynamicFunctionType()); - return reinterpret_cast(this); - } - bool IsConstantType() const { return HasTypeFlag(checker::TypeFlag::CONSTANT); } + Type *MaybeBaseTypeOfGradualType(); + TypeFlag TypeFlags() const { return typeFlags_; @@ -216,7 +187,7 @@ public: typeFlags_ &= ~typeFlag; } - uint64_t Id() const + uint32_t Id() const { return id_; } @@ -308,7 +279,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/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 80a8ad001a584dbb859a1dbd3ab14acf798ae4dc..130fa19dbbf95318c6de95f56091dc5d28084585 100644 --- a/ets2panda/checker/types/typeRelation.cpp +++ b/ets2panda/checker/types/typeRelation.cpp @@ -36,17 +36,17 @@ RelationResult TypeRelation::CacheLookup(const Type *source, const Type *target, 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 +69,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(); @@ -143,8 +144,8 @@ bool TypeRelation::IsAssignableTo(Type *source, Type *target) } if (flags_ == TypeRelationFlag::NONE) { - checker_->AssignableResults().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}}); + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->AssignableResults().Insert(key, {result_, RelationType::ASSIGNABLE}); } } @@ -154,14 +155,6 @@ 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 - if (source->IsETSDynamicType() || target->IsETSDynamicType()) { - if (!(source->IsETSDynamicType() && target->IsETSDynamicType())) { - return false; - } - } - if (result_ == RelationResult::CACHE_MISS) { if (IsAssignableTo(source, target)) { return true; @@ -169,8 +162,8 @@ 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}}); + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->ComparableResults().Insert(key, {result_, RelationType::COMPARABLE}); } return result_ == RelationResult::TRUE; @@ -193,8 +186,8 @@ bool TypeRelation::IsCastableTo(Type *const source, Type *const target) } if (UncheckedCast() && !node_->HasAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF)) { - checker_->UncheckedCastableResult().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::UNCHECKED_CASTABLE}}); + auto key = RelationHolder::MakeKey(source->Id(), target->Id()); + checker_->UncheckedCastableResult().Insert(key, {result_, RelationType::UNCHECKED_CASTABLE}); } return true; @@ -256,13 +249,15 @@ bool TypeRelation::IsLegalBoxedPrimitiveConversion(Type *target, Type *source) 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) { @@ -276,7 +271,8 @@ bool TypeRelation::IsSupertypeOf(Type *super, Type *sub) } 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}); } } diff --git a/ets2panda/checker/types/typeRelation.h b/ets2panda/checker/types/typeRelation.h index c5abfa2e85516d6e9f262ae4fea83702a2c7a99c..ac87200fbce06c222e7618258bcee72f2aa1a2a8 100644 --- a/ets2panda/checker/types/typeRelation.h +++ b/ets2panda/checker/types/typeRelation.h @@ -71,9 +71,9 @@ enum class TypeRelationFlag : uint32_t { 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 }; @@ -85,47 +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; -}; - -using RelationMap = ArenaUnorderedMap; + void Insert(RelationKey key, RelationEntry entry) + { + cached_.insert({key, entry}); + } -class RelationHolder { -public: - RelationHolder(ThreadSafeArenaAllocator *allocator, RelationType relationType) - : cached(allocator->Adapter()), type(relationType) + void Clear() { + cached_.clear(); } - // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) - RelationMap cached; - // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) - RelationType type {}; +private: + ArenaUnorderedMap cached_; }; class TypeRelation { diff --git a/ets2panda/compiler/base/lreference.cpp b/ets2panda/compiler/base/lreference.cpp index 9f9a3c45c0093b1aa12e160ac823708c87c9a73b..ca4f4e66523d3ef277fe98842c6260cdce2e7bfb 100644 --- a/ets2panda/compiler/base/lreference.cpp +++ b/ets2panda/compiler/base/lreference.cpp @@ -69,6 +69,10 @@ LReference::LReferenceBase LReference::CreateBase(CodeGen *cg, const ir::AstNode case ir::AstNodeType::TS_NON_NULL_EXPRESSION: { return CreateBase(cg, node->AsTSNonNullExpression()->Expr(), isDeclaration); } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + ES2PANDA_ASSERT(node->AsETSNewClassInstanceExpression()->GetArguments().size() == 1); + return CreateBase(cg, node->AsETSNewClassInstanceExpression()->GetArguments()[0], isDeclaration); + } default: { ES2PANDA_UNREACHABLE(); } @@ -183,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; } @@ -197,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_); } @@ -281,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; } @@ -331,6 +340,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); @@ -341,6 +351,11 @@ void ETSLReference::SetValue() const return; } + if (objectType->IsETSAnyType()) { + etsg_->StorePropertyByNameAny(memberExpr, baseReg_, memberExpr->Property()->AsIdentifier()->Name()); + return; + } + if (memberExpr->PropVar()->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) { SetValueGetterSetter(memberExpr); return; @@ -350,19 +365,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/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index bae951abe47ab29e710e5e646aeb405aa9c4ea1e..8992e4463266032d54ade146aee52b4d6f28f6a7 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -16,14 +16,18 @@ #include "ETSCompiler.h" #include "compiler/base/catchTable.h" -#include "checker/ets/dynamic/dynamicCall.h" #include "compiler/base/condition.h" #include "compiler/base/lreference.h" #include "compiler/core/switchBuilder.h" +#include "compiler/core/targetTypeContext.h" +#include "compiler/core/vReg.h" +#include "compiler/function/functionBuilder.h" #include "checker/ETSchecker.h" -#include "checker/types/ets/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 { @@ -151,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() || @@ -279,15 +248,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()); } @@ -519,6 +490,17 @@ static void CompileLogical(compiler::ETSGen *etsg, const ir::BinaryExpression *e etsg->SetAccumulatorType(expr->TsType()); } +static void CompileAnyInstanceOf(compiler::ETSGen *etsg, const VReg lhs, const ir::Expression *expr) +{ + RegScope rs(etsg); + VReg objReg = etsg->AllocReg(); + expr->Compile(etsg); + etsg->StoreAccumulator(expr, objReg); + etsg->LoadAccumulator(expr, lhs); + etsg->EmitAnyIsInstance(expr, objReg); + etsg->SetAccumulatorType(etsg->Checker()->GlobalETSBooleanType()); +} + static void CompileInstanceof(compiler::ETSGen *etsg, const ir::BinaryExpression *expr) { ES2PANDA_ASSERT(expr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF); @@ -529,13 +511,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())); @@ -696,38 +675,6 @@ void ETSCompiler::Compile(const ir::BlockExpression *expr) const expr->Scope()->SetParent(oldParent); } -void ETSCompiler::CompileDynamic(const ir::CallExpression *expr, compiler::VReg &calleeReg) const -{ - 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(); - - 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()); - - if (etsg->GetAccumulatorType() != expr->TsType()) { - etsg->ApplyConversion(expr, expr->TsType()); - } -} - bool IsCastCallName(util::StringView name) { return name == Signatures::BYTE_CAST || name == Signatures::SHORT_CAST || name == Signatures::INT_CAST || @@ -744,6 +691,26 @@ bool IsCastCall(checker::Signature *signature) (signature->Params().size() == 1) && signature->Params()[0]->TsType()->IsETSPrimitiveType()); } +void ETSCompiler::CompileAny(const ir::CallExpression *expr, const ir::Expression *callee, + compiler::VReg &calleeReg) const +{ + ETSGen *etsg = GetETSGen(); + auto memberExpr = callee->AsMemberExpression(); + memberExpr->Object()->Compile(etsg); + compiler::VReg objReg = etsg->AllocReg(); + etsg->StoreAccumulator(expr, objReg); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + if (expr->Signature()->Function() != nullptr && expr->Signature()->Function()->IsStatic()) { + etsg->LoadPropertyByNameAny(memberExpr, objReg, memberExpr->Property()->AsIdentifier()->Name()); + etsg->StoreAccumulator(expr, calleeReg); + etsg->CallAny(callee->AsMemberExpression()->Object(), expr->Arguments(), calleeReg); + } else { + etsg->CallAnyThis(expr, memberExpr->Property()->AsIdentifier(), expr->Arguments(), objReg); + } + auto returnType = expr->Signature()->ReturnType(); + etsg->EmitAnyCheckCast(expr, returnType); +} + void ETSCompiler::EmitCall(const ir::CallExpression *expr, compiler::VReg &calleeReg, checker::Signature *signature) const { @@ -795,8 +762,8 @@ void ETSCompiler::Compile(const ir::CallExpression *expr) const 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); @@ -848,6 +815,7 @@ void ETSCompiler::Compile(const ir::Identifier *expr) const ETSGen *etsg = GetETSGen(); auto const *smartType = expr->TsType(); + ES2PANDA_ASSERT(smartType != nullptr); if (smartType->IsETSTypeParameter() || smartType->IsETSPartialTypeParameter() || smartType->IsETSNonNullishType()) { smartType = etsg->Checker()->GetApparentType(smartType); } @@ -869,15 +837,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) @@ -905,8 +873,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); @@ -919,6 +887,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(); @@ -929,6 +914,10 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const return; } + if (CompileAny(etsg, expr)) { + return; + } + if (HandleArrayTypeLengthProperty(expr, etsg)) { return; } @@ -956,8 +945,6 @@ void ETSCompiler::Compile(const ir::MemberExpression *expr) const } 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 { @@ -1012,15 +999,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); createObjSig->SetInternalName(compiler::Signatures::BUILTIN_JSRUNTIME_CREATE_OBJECT); @@ -1048,11 +1045,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); @@ -1662,10 +1655,6 @@ void ETSCompiler::CompileCast(const ir::TSAsExpression *expr, checker::Type cons etsg->CastToReftype(expr, targetType, expr->isUncheckedCast_); break; } - case checker::TypeFlag::ETS_DYNAMIC_TYPE: { - etsg->CastToDynamic(expr, targetType->AsETSDynamicType()); - break; - } // NOTE(gogabr): will be needed once we forbid as conversion /* // CC-OFFNXT(redundant_code[C++]) tmp code @@ -1740,4 +1729,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 42f81ca50ae99557ef91baac2839c21895562c99..868ce81e497e15dbcb51d9a6dd65191d2249dc58 100644 --- a/ets2panda/compiler/core/ETSCompiler.h +++ b/ets2panda/compiler/core/ETSCompiler.h @@ -35,7 +35,7 @@ public: private: void GetDynamicNameParts(const ir::CallExpression *expr, ArenaVector &parts) const; - void CompileDynamic(const ir::CallExpression *expr, compiler::VReg &calleeReg) 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; @@ -45,6 +45,7 @@ private: void CompileTupleCreation(const ir::ArrayExpression *tupleInitializer) const; static bool CompileComputed(compiler::ETSGen *etsg, const ir::MemberExpression *expr); + bool CompileAny(compiler::ETSGen *etsg, const ir::MemberExpression *expr) const; ETSGen *GetETSGen() const; }; diff --git a/ets2panda/compiler/core/ETSCompilerUnrechable.cpp b/ets2panda/compiler/core/ETSCompilerUnrechable.cpp index 44669b1241826c6c824fbe63ccac691226ab45b4..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(); @@ -419,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 fde13df42381adde94276013525b53b1b5a05ecf..4e62393a954b62facfe7f9675d6a93d5568f3c26 100644 --- a/ets2panda/compiler/core/ETSGen.cpp +++ b/ets2panda/compiler/core/ETSGen.cpp @@ -15,6 +15,8 @@ #include "ETSGen-inl.h" +#include "compiler/core/regScope.h" +#include "generated/isa.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" @@ -234,71 +236,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); - 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)) { @@ -435,6 +374,22 @@ void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *p } } +void ETSGen::StorePropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + +void ETSGen::LoadPropertyByNameAny(const ir::AstNode *const node, const VReg objReg, const util::StringView &fullName) +{ + ES2PANDA_ASSERT(node->IsMemberExpression() && + Checker()->GetApparentType(node->AsMemberExpression()->Object()->TsType())->IsETSAnyType()); + Ra().Emit(node, objReg, fullName); + SetAccumulatorType(node->AsMemberExpression()->TsType()); +} + void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg, const util::StringView &fullName) { @@ -488,137 +443,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) -{ - 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) -{ - 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) { - 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) { - 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, @@ -725,83 +589,10 @@ static bool IsNullUnsafeObjectType(checker::Type const *type) 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(); - 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()); -} - // 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