From e55a0bb129e00ba399ca8bc7294a7a3ccbf29bee Mon Sep 17 00:00:00 2001 From: Aleksandr Semenov Date: Sat, 11 Nov 2023 11:02:49 +0300 Subject: [PATCH 1/2] es2panda: Parse jit-compiler and bco options Signed-off-by: Aleksandr Semenov --- ets2panda/test/CMakeLists.txt | 2 + ets2panda/test/options/CMakeLists.txt | 36 +++++++++++ ets2panda/util/options.cpp | 88 ++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 ets2panda/test/options/CMakeLists.txt diff --git a/ets2panda/test/CMakeLists.txt b/ets2panda/test/CMakeLists.txt index a602e480e5..ba88fb843f 100644 --- a/ets2panda/test/CMakeLists.txt +++ b/ets2panda/test/CMakeLists.txt @@ -118,3 +118,5 @@ if(PANDA_WITH_ETS) add_subdirectory(tsconfig) endif() + +add_subdirectory(options) diff --git a/ets2panda/test/options/CMakeLists.txt b/ets2panda/test/options/CMakeLists.txt new file mode 100644 index 0000000000..dd46aabd24 --- /dev/null +++ b/ets2panda/test/options/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) 2021-2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(CMAKE_CROSSCOMPILING) + return() +endif() + +add_custom_target(es2panda_tests_options) +add_dependencies(es2panda_tests es2panda_tests_options) + +function(check_option_help_otput target_name INPUT_ARGS OUTPUT_HELP_LINE) + separate_arguments(INPUT_ARGS) + add_custom_target(es2panda_check_opts_${target_name} + COMMENT "es2panda: checking option ${INPUT_ARGS}" + COMMAND es2panda ${INPUT_ARGS} 2> ${CMAKE_BINARY_DIR}/es2panda_check_opts_${target_name}.out || true + COMMAND grep -q ${OUTPUT_HELP_LINE} ${CMAKE_BINARY_DIR}/es2panda_check_opts_${target_name}.out + DEPENDS es2panda + ) + + add_dependencies(es2panda_tests_options es2panda_check_opts_${target_name}) +endfunction() + +check_option_help_otput(bco_opt "--help" "bco-optimizer:") +check_option_help_otput(bco_opt_help "--bco-optimizer --help" "bytecode-opt-peepholes:") +check_option_help_otput(comp_opt "--help" "bco-compiler:") +check_option_help_otput(comp_opt_help "--bco-compiler --help" "compiler-disasm-dump:") diff --git a/ets2panda/util/options.cpp b/ets2panda/util/options.cpp index c7f2350ca8..60e35b3075 100644 --- a/ets2panda/util/options.cpp +++ b/ets2panda/util/options.cpp @@ -19,6 +19,11 @@ #include +#ifdef PANDA_WITH_BYTECODE_OPTIMIZER +#include "bytecode_optimizer/bytecodeopt_options.h" +#include "compiler/compiler_options.h" +#endif + namespace panda::es2panda::util { template T RemoveExtension(T const &filename) @@ -55,9 +60,84 @@ static std::unordered_set StringToStringSet(const std::string &str) return res; } +// NOLINTNEXTLINE(modernize-avoid-c-arrays, hicpp-avoid-c-arrays) +static void SplitArgs(int argc, const char *argv[], std::vector &es2panda_args, + std::vector &bco_compiler_args, std::vector &bytecodeopt_args) +{ + constexpr std::string_view COMPILER_PREFIX = "--bco-compiler"; + constexpr std::string_view OPTIMIZER_PREFIX = "--bco-optimizer"; + + enum class OptState { ES2PANDA, JIT_COMPILER, OPTIMIZER }; + OptState opt_state = OptState::ES2PANDA; + + std::unordered_map *> args_map = {{OptState::ES2PANDA, &es2panda_args}, + {OptState::JIT_COMPILER, &bco_compiler_args}, + {OptState::OPTIMIZER, &bytecodeopt_args}}; + + for (int i = 1; i < argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const char *arg_i = argv[i]; + if (COMPILER_PREFIX == arg_i) { + opt_state = OptState::JIT_COMPILER; + continue; + } + + if (OPTIMIZER_PREFIX == arg_i) { + opt_state = OptState::OPTIMIZER; + continue; + } + + args_map[opt_state]->emplace_back(arg_i); + opt_state = OptState::ES2PANDA; + } +} + +template +static bool ParseComponentArgs(const std::vector &args, T &options) +{ + panda::PandArgParser parser; + options.AddOptions(&parser); + if (!parser.Parse(args)) { + std::cerr << parser.GetErrorString(); + std::cerr << parser.GetHelpString(); + return false; + } + + if (auto options_err = options.Validate(); options_err) { + std::cerr << "Error: " << options_err.value().GetMessage() << std::endl; + return false; + } + + return true; +} + +static bool ParseBCOCompilerOptions([[maybe_unused]] const std::vector &compiler_args, + [[maybe_unused]] const std::vector &bytecodeopt_args) +{ +#ifdef PANDA_WITH_BYTECODE_OPTIMIZER + if (!ParseComponentArgs(compiler_args, panda::compiler::OPTIONS)) { + return false; + } + if (!ParseComponentArgs(bytecodeopt_args, panda::bytecodeopt::OPTIONS)) { + return false; + } +#endif + + return true; +} + // NOLINTNEXTLINE(readability-function-size) bool Options::Parse(int argc, const char **argv) { + std::vector es2panda_args; + std::vector bco_compiler_args; + std::vector bytecodeopt_args; + + SplitArgs(argc, argv, es2panda_args, bco_compiler_args, bytecodeopt_args); + if (!ParseBCOCompilerOptions(bco_compiler_args, bytecodeopt_args)) { + return false; + } + panda::PandArg op_help("help", false, "Print this message and exit"); // parser @@ -128,7 +208,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->EnableTail(); argparser_->EnableRemainder(); - if (!argparser_->Parse(argc, argv) || op_help.GetValue()) { + if (!argparser_->Parse(es2panda_args) || op_help.GetValue()) { std::stringstream ss; ss << argparser_->GetErrorString() << std::endl; @@ -139,6 +219,12 @@ bool Options::Parse(int argc, const char **argv) ss << "optional arguments:" << std::endl; ss << argparser_->GetHelpString() << std::endl; + ss << std::endl; + ss << "--bco-optimizer: Argument directly to bytecode optimizer can be passed after this prefix" << std::endl; + ss << "--bco-compiler: Argument directly to jit-compiler inside bytecode optimizer can be passed after this " + "prefix" + << std::endl; + error_msg_ = ss.str(); return false; } -- Gitee From c20a284d93aac7c041dd72a7468d36d9d440c9f0 Mon Sep 17 00:00:00 2001 From: Anna Antipina Date: Thu, 9 Nov 2023 17:39:09 +0300 Subject: [PATCH 2/2] Title: Fix imported paths Description: Fixed different type signatures for same class imported with different paths Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I8FAPJ Testing: ./tests/tests-u-runner/runner.sh $ARK_SOURCE_DIR --parser --build-dir=$ARK_BUILD_BIR --test-file parser/ets/import_tests/import_diff_paths.ets Signed-off-by: Anna Antipina --- ets2panda/checker/ets/helpers.cpp | 7 +- ets2panda/compiler/core/compilerImpl.cpp | 5 +- ets2panda/parser/ETSparser.cpp | 90 +++- ets2panda/parser/ETSparser.h | 14 +- .../import_diff_paths-expected.txt | 491 ++++++++++++++++++ .../ets/import_tests/import_diff_paths.ets | 21 + .../modules/test_lib1-expected.txt | 290 +++++++++++ .../ets/import_tests/modules/test_lib1.ets | 17 + .../modules/test_lib2-expected.txt | 389 ++++++++++++++ .../ets/import_tests/modules/test_lib2.ets | 18 + ets2panda/util/helpers.cpp | 35 ++ ets2panda/util/helpers.h | 2 + ets2panda/varbinder/ETSBinder.cpp | 45 +- ets2panda/varbinder/ETSBinder.h | 26 +- 14 files changed, 1406 insertions(+), 44 deletions(-) create mode 100644 ets2panda/test/parser/ets/import_tests/import_diff_paths-expected.txt create mode 100644 ets2panda/test/parser/ets/import_tests/import_diff_paths.ets create mode 100644 ets2panda/test/parser/ets/import_tests/modules/test_lib1-expected.txt create mode 100644 ets2panda/test/parser/ets/import_tests/modules/test_lib1.ets create mode 100644 ets2panda/test/parser/ets/import_tests/modules/test_lib2-expected.txt create mode 100644 ets2panda/test/parser/ets/import_tests/modules/test_lib2.ets diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 7331139366..d1e74e0835 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1057,8 +1057,11 @@ void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *module_obj { auto *ets_binder = static_cast(VarBinder()); - auto res = ets_binder->GetGlobalRecordTable()->Program()->ExternalSources().find(import_path); - + auto ext_records = ets_binder->GetGlobalRecordTable()->Program()->ExternalSources(); + auto res = [ets_binder, ext_records, import_path]() { + auto r = ext_records.find(import_path); + return r != ext_records.end() ? r : ext_records.find(ets_binder->GetResolvedImportPath(import_path)); + }(); for (auto [_, var] : res->second.front()->GlobalClassScope()->StaticFieldScope()->Bindings()) { (void)_; if (var->AsLocalVariable()->Declaration()->Node()->IsExported()) { diff --git a/ets2panda/compiler/core/compilerImpl.cpp b/ets2panda/compiler/core/compilerImpl.cpp index 0d33af9dd5..858ca20a05 100644 --- a/ets2panda/compiler/core/compilerImpl.cpp +++ b/ets2panda/compiler/core/compilerImpl.cpp @@ -115,7 +115,10 @@ static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const Phase context.SetParser(&parser); parser.ParseScript(unit.input, unit.options.compilation_mode == CompilationMode::GEN_STD_LIB); - + if constexpr (std::is_same_v && std::is_same_v) { + reinterpret_cast(varbinder)->FillResolvedImportPathes(parser.ResolvedParsedSourcesMap(), + &allocator); + } for (auto *phase : get_phases()) { if (!phase->Apply(&context, &program)) { return nullptr; diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 1100af3be5..0becad2529 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -173,11 +173,21 @@ void ETSParser::ParseETSGlobalScript(lexer::SourcePosition start_loc, ArenaVecto // remove external sources from paths because already parsed them paths.erase(remove_if(begin(paths), end(paths), [this](auto x) { - return find(begin(parsed_sources_), end(parsed_sources_), x) != end(parsed_sources_); + auto resolved = ResolveImportPath(x); + auto path_iter = + std::find_if(resolved_parsed_sources_.begin(), resolved_parsed_sources_.end(), + [resolved](const auto &p) { return p.second == resolved; }); + auto found = path_iter != resolved_parsed_sources_.end(); + if (found) { + resolved_parsed_sources_.emplace(x, resolved); + } + return found; }), end(paths)); - parsed_sources_.insert(parsed_sources_.end(), paths.begin(), paths.end()); + for (const auto &path : paths) { + parsed_sources_.push_back(ResolveImportPath(path)); + } ParseSources(paths, false); ParseTopLevelDeclaration(statements); @@ -231,9 +241,12 @@ ArenaVector ETSParser::PrepareExternalGlobalClass([[maybe_unuse if (!statements.empty()) { res = ext_sources.find(name); } else { - const util::UString source_file_path( - GetProgram()->SourceFilePath().Mutf8() + GetProgram()->GetPackageName().Mutf8(), Allocator()); - GetProgram()->SetSource(GetProgram()->SourceCode(), GetProgram()->SourceFile(), source_file_path.View()); + auto path = GetProgram()->SourceFilePath().Mutf8() + panda::os::file::File::GetPathDelim().at(0) + + GetProgram()->GetPackageName().Mutf8(); + auto resolved = ResolveImportPath(path); + resolved_parsed_sources_.emplace(path, resolved); + GetProgram()->SetSource(GetProgram()->SourceCode(), GetProgram()->SourceFile(), + util::UString(resolved, Allocator()).View()); } if (res == ext_sources.end()) { @@ -344,14 +357,30 @@ ETSParser::ImportData ETSParser::GetImportData(const std::string &path) return {ToLanguage(Extension()), path, true}; } +std::string ETSParser::ResolveFullPathFromRelative(const std::string &path) +{ + char path_delimiter = panda::os::file::File::GetPathDelim().at(0); + auto resolved_fp = GetProgram()->ResolvedFilePath().Mutf8(); + auto source_fp = GetProgram()->SourceFilePath().Mutf8(); + if (resolved_fp.empty()) { + auto fp = source_fp + path_delimiter + path; + return util::Helpers::IsRealPath(fp) ? fp : path; + } + auto fp = resolved_fp + path_delimiter + path; + if (util::Helpers::IsRealPath(fp)) { + return fp; + } + if (path.find(source_fp) == 0) { + return resolved_fp + path_delimiter + path.substr(source_fp.size()); + } + return path; +} + std::string ETSParser::ResolveImportPath(const std::string &path) { char path_delimiter = panda::os::file::File::GetPathDelim().at(0); if (util::Helpers::IsRelativePath(path)) { - if (GetProgram()->ResolvedFilePath().Mutf8().empty()) { - return GetProgram()->SourceFilePath().Mutf8() + path_delimiter + path; - } - return GetProgram()->ResolvedFilePath().Mutf8() + path_delimiter + path; + return util::Helpers::GetAbsPath(ResolveFullPathFromRelative(path)); } std::string base_url; @@ -395,11 +424,29 @@ std::string ETSParser::ResolveImportPath(const std::string &path) return base_url; } +std::tuple ETSParser::GetSourceRegularPath(const std::string &path, const std::string &resolved_path) +{ + if (!panda::os::file::File::IsRegularFile(resolved_path)) { + std::string import_extension = ".ets"; + + if (!panda::os::file::File::IsRegularFile(resolved_path + import_extension)) { + import_extension = ".ts"; + + if (!panda::os::file::File::IsRegularFile(resolved_path + import_extension)) { + ThrowSyntaxError("Incorrect path: " + resolved_path); + } + } + return {path + import_extension, true}; + } + return {path, false}; +} + std::tuple, bool> ETSParser::CollectUserSources(const std::string &path) { std::vector user_paths; const std::string resolved_path = ResolveImportPath(path); + resolved_parsed_sources_.emplace(path, resolved_path); const auto data = GetImportData(resolved_path); if (!data.has_decl) { @@ -407,23 +454,11 @@ std::tuple, bool> ETSParser::CollectUserSources(const s } if (!panda::os::file::File::IsDirectory(resolved_path)) { - if (!panda::os::file::File::IsRegularFile(resolved_path)) { - std::string import_extension = ".ets"; - - if (!panda::os::file::File::IsRegularFile(resolved_path + import_extension)) { - import_extension = ".ts"; - - if (!panda::os::file::File::IsRegularFile(resolved_path + import_extension)) { - ThrowSyntaxError("Incorrect path: " + resolved_path); - } - } - - user_paths.emplace_back(path + import_extension); - return {user_paths, true}; - } - - user_paths.emplace_back(path); - return {user_paths, false}; + std::string regular_path; + bool is_module = false; + std::tie(regular_path, is_module) = GetSourceRegularPath(path, resolved_path); + user_paths.emplace_back(regular_path); + return {user_paths, is_module}; } #ifdef USE_UNIX_SYSCALL @@ -479,6 +514,8 @@ void ETSParser::ParseSources(const std::vector &paths, bool is_exte const std::size_t path_count = paths.size(); for (std::size_t idx = 0; idx < path_count; idx++) { std::string resolved_path = ResolveImportPath(paths[idx]); + resolved_parsed_sources_.emplace(paths[idx], resolved_path); + const auto data = GetImportData(resolved_path); if (!data.has_decl) { @@ -2862,6 +2899,7 @@ std::tuple> ETSParser::ParseFromCla bool is_module = false; auto import_path = Lexer()->GetToken().Ident(); auto resolved_import_path = ResolveImportPath(import_path.Mutf8()); + resolved_parsed_sources_.emplace(import_path.Mutf8(), resolved_import_path); ir::StringLiteral *resolved_source; if (*import_path.Bytes() == '/') { diff --git a/ets2panda/parser/ETSparser.h b/ets2panda/parser/ETSparser.h index d1eb419aa8..cf643c0d57 100644 --- a/ets2panda/parser/ETSparser.h +++ b/ets2panda/parser/ETSparser.h @@ -41,7 +41,10 @@ inline constexpr char const DEFAULT_SOURCE_FILE[] = ".ets"; class ETSParser final : public TypedParser { public: ETSParser(Program *program, const CompilerOptions &options, ParserStatus status = ParserStatus::NO_OPTS) - : TypedParser(program, options, status), global_program_(GetProgram()), parsed_sources_({}) + : TypedParser(program, options, status), + global_program_(GetProgram()), + parsed_sources_({}), + resolved_parsed_sources_({}) { } @@ -109,8 +112,10 @@ private: void ParseTopLevelDeclaration(ArenaVector &statements); void CollectDefaultSources(); std::string ResolveImportPath(const std::string &path); + std::string ResolveFullPathFromRelative(const std::string &path); ImportData GetImportData(const std::string &path); std::tuple, bool> CollectUserSources(const std::string &path); + std::tuple GetSourceRegularPath(const std::string &path, const std::string &resolved_path); void ParseSources(const std::vector &paths, bool is_external = true); std::tuple> ParseFromClause(bool require_from); void ParseNamedImportSpecifiers(ArenaVector *specifiers); @@ -328,10 +333,17 @@ private: friend class ExternalSourceParser; friend class InnerSourceParser; +public: + const std::unordered_map &ResolvedParsedSourcesMap() const + { + return resolved_parsed_sources_; + } + private: parser::Program *global_program_; std::vector parsed_sources_; std::vector inserting_nodes_ {}; + std::unordered_map resolved_parsed_sources_; }; class ExternalSourceParser { diff --git a/ets2panda/test/parser/ets/import_tests/import_diff_paths-expected.txt b/ets2panda/test/parser/ets/import_tests/import_diff_paths-expected.txt new file mode 100644 index 0000000000..36509b5515 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/import_diff_paths-expected.txt @@ -0,0 +1,491 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "./modules", + "loc": { + "start": { + "line": 16, + "column": 19 + }, + "end": { + "line": 16, + "column": 40 + } + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "local": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + }, + "imported": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 16, + "column": 40 + } + } + }, + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "./modules", + "loc": { + "start": { + "line": 17, + "column": 19 + }, + "end": { + "line": 17, + "column": 40 + } + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "local": { + "type": "Identifier", + "name": "f", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 11 + } + } + }, + "imported": { + "type": "Identifier", + "name": "f", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 11 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 11 + } + } + } + ], + "loc": { + "start": { + "line": 17, + "column": 1 + }, + "end": { + "line": 17, + "column": 40 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 10 + }, + "end": { + "line": 19, + "column": 14 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 10 + }, + "end": { + "line": 19, + "column": 14 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "f", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 6 + } + } + }, + "arguments": [ + { + "type": "ETSNewClassInstanceExpression", + "typeReference": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 11 + }, + "end": { + "line": 20, + "column": 12 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 11 + }, + "end": { + "line": 20, + "column": 13 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 11 + }, + "end": { + "line": 20, + "column": 13 + } + } + }, + "arguments": [], + "loc": { + "start": { + "line": 20, + "column": 7 + }, + "end": { + "line": 20, + "column": 15 + } + } + } + ], + "optional": false, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 15 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 16 + } + } + } + ], + "loc": { + "start": { + "line": 19, + "column": 17 + }, + "end": { + "line": 21, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 14 + }, + "end": { + "line": 21, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 14 + }, + "end": { + "line": 21, + "column": 2 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 1 + }, + "end": { + "line": 21, + "column": 2 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 21, + "column": 2 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/import_diff_paths.ets b/ets2panda/test/parser/ets/import_tests/import_diff_paths.ets new file mode 100644 index 0000000000..84d9983f77 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/import_diff_paths.ets @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { C } from "./modules/test_lib1" +import { f } from "./modules/test_lib2" + +function main() { + f(new C()); +} \ No newline at end of file diff --git a/ets2panda/test/parser/ets/import_tests/modules/test_lib1-expected.txt b/ets2panda/test/parser/ets/import_tests/modules/test_lib1-expected.txt new file mode 100644 index 0000000000..8386d8940f --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/test_lib1-expected.txt @@ -0,0 +1,290 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 14 + }, + "end": { + "line": 16, + "column": 15 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "constructor", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "constructor", + "static": false, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "constructor", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 18 + }, + "end": { + "line": 16, + "column": 18 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 16 + }, + "end": { + "line": 16, + "column": 18 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 18 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 18, + "column": 1 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/modules/test_lib1.ets b/ets2panda/test/parser/ets/import_tests/modules/test_lib1.ets new file mode 100644 index 0000000000..ead48512d3 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/test_lib1.ets @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class C {} + diff --git a/ets2panda/test/parser/ets/import_tests/modules/test_lib2-expected.txt b/ets2panda/test/parser/ets/import_tests/modules/test_lib2-expected.txt new file mode 100644 index 0000000000..51e505772f --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/test_lib2-expected.txt @@ -0,0 +1,389 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "./", + "loc": { + "start": { + "line": 16, + "column": 19 + }, + "end": { + "line": 16, + "column": 32 + } + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "local": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + }, + "imported": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 11 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 16, + "column": 32 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "f", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 17 + }, + "end": { + "line": 17, + "column": 18 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "f", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 17 + }, + "end": { + "line": 17, + "column": 18 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [ + { + "type": "ETSParameterExpression", + "name": { + "type": "Identifier", + "name": "c", + "typeAnnotation": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "C", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 22 + }, + "end": { + "line": 17, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 22 + }, + "end": { + "line": 17, + "column": 24 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 22 + }, + "end": { + "line": 17, + "column": 24 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 19 + }, + "end": { + "line": 17, + "column": 24 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 19 + }, + "end": { + "line": 17, + "column": 24 + } + } + } + ], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 27 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 18 + }, + "end": { + "line": 17, + "column": 27 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 18 + }, + "end": { + "line": 17, + "column": 27 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 8 + }, + "end": { + "line": 17, + "column": 27 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 19, + "column": 1 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/modules/test_lib2.ets b/ets2panda/test/parser/ets/import_tests/modules/test_lib2.ets new file mode 100644 index 0000000000..df27898b16 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/modules/test_lib2.ets @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { C } from "./test_lib1" +export function f(c: C) {} + diff --git a/ets2panda/util/helpers.cpp b/ets2panda/util/helpers.cpp index aafd8d43e7..b1f4abe0f9 100644 --- a/ets2panda/util/helpers.cpp +++ b/ets2panda/util/helpers.cpp @@ -43,6 +43,7 @@ #include "ir/module/importDeclaration.h" #include "lexer/token/letters.h" #include "libpandabase/utils/utf.h" +#include "libpandabase/os/filesystem.h" namespace panda::es2panda::util { // Helpers @@ -169,6 +170,40 @@ bool Helpers::IsRelativePath(const std::string &path) return ((path.find(current_dir_reference) == 0) || (path.find(parent_dir_reference) == 0)); } +std::string Helpers::GetAbsPath(const std::string &path) +{ + std::string full_file_path = path; + std::string import_extension; + if (!panda::os::file::File::IsRegularFile(path) && (panda::os::GetAbsolutePath(path).empty())) { + import_extension = ".ets"; + full_file_path = path + import_extension; + if (!panda::os::file::File::IsRegularFile(full_file_path)) { + import_extension = ".ts"; + full_file_path = path + import_extension; + if (!panda::os::file::File::IsRegularFile(full_file_path)) { + return path; + } + } + } + std::string abs_file_path = panda::os::GetAbsolutePath(full_file_path); + abs_file_path.erase(abs_file_path.find(import_extension), import_extension.size()); + return abs_file_path; +} + +bool Helpers::IsRealPath(const std::string &path) +{ + if (!panda::os::file::File::IsRegularFile(path) && (panda::os::GetAbsolutePath(path).empty())) { + auto import_extension = ".ets"; + if (!panda::os::file::File::IsRegularFile(path + import_extension)) { + import_extension = ".ts"; + if (!panda::os::file::File::IsRegularFile(path + import_extension)) { + return false; + } + } + } + return true; +} + const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node) { const ir::ScriptFunction *iter = GetContainingFunction(node); diff --git a/ets2panda/util/helpers.h b/ets2panda/util/helpers.h index 2611e79141..bd32ed03a5 100644 --- a/ets2panda/util/helpers.h +++ b/ets2panda/util/helpers.h @@ -79,6 +79,8 @@ public: static util::StringView ToStringView(ArenaAllocator *allocator, int32_t number); static util::StringView ToStringView(ArenaAllocator *allocator, uint32_t number); static bool IsRelativePath(const std::string &path); + static bool IsRealPath(const std::string &path); + static std::string GetAbsPath(const std::string &path); static const ir::ScriptFunction *GetContainingConstructor(const ir::AstNode *node); static const ir::ScriptFunction *GetContainingConstructor(const ir::ClassProperty *node); diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index d12f33d16e..61c93cd5b9 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -582,6 +582,23 @@ varbinder::Variable *ETSBinder::FindStaticBinding(const ArenaVectorsecond; } +ArenaVector ETSBinder::GetExternalProgram(const util::StringView &source_name, + const ir::StringLiteral *import_path) +{ + const auto &ext_records = global_record_table_.Program()->ExternalSources(); + auto record_res = [this, ext_records, source_name]() { + auto res = ext_records.find(source_name); + return (res != ext_records.end()) ? res : ext_records.find(GetResolvedImportPath(source_name)); + }(); + if (record_res == ext_records.end()) { + ThrowError(import_path->Start(), "Cannot find import: " + std::string(source_name)); + } + + ASSERT(!record_res->second.empty()); + + return record_res->second; +} + void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import) { const ir::StringLiteral *const import_path = import->Source(); @@ -591,18 +608,20 @@ void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const i return; } - const auto &ext_records = global_record_table_.Program()->ExternalSources(); - const util::StringView source_name = - (import->Module() == nullptr) - ? import_path->Str() - : util::UString(import_path->Str().Mutf8() + import->Module()->Str().Mutf8(), Allocator()).View(); - const auto record_res = ext_records.find(source_name); - if (record_res == ext_records.end()) { - ThrowError(import_path->Start(), "Cannot find import: " + std::string(source_name)); - } + const util::StringView source_name = [import, import_path, this]() { + if (import->Module() == nullptr) { + return import_path->Str(); + } + char path_delimiter = panda::os::file::File::GetPathDelim().at(0); + auto str_import_path = import_path->Str().Mutf8(); + if (str_import_path.find(path_delimiter) == (str_import_path.size() - 1)) { + return util::UString(str_import_path + import->Module()->Str().Mutf8(), Allocator()).View(); + } + return util::UString(str_import_path + path_delimiter + import->Module()->Str().Mutf8(), Allocator()).View(); + }(); - ASSERT(!record_res->second.empty()); - const auto *const import_program = record_res->second.front(); + auto record = GetExternalProgram(source_name, import_path); + const auto *const import_program = record.front(); const auto *const import_global_scope = import_program->GlobalScope(); const auto &global_bindings = import_global_scope->Bindings(); @@ -618,7 +637,7 @@ void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const i return; } - if (AddImportSpecifiersToTopBindings(specifier, global_bindings, import, record_res->second)) { + if (AddImportSpecifiersToTopBindings(specifier, global_bindings, import, record)) { return; } @@ -628,7 +647,7 @@ void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const i auto item = std::find_if(global_bindings.begin(), global_bindings.end(), predicate_func); if (item == global_bindings.end()) { insert_foreign_binding(specifier->AsImportDefaultSpecifier()->Local()->Name(), - FindStaticBinding(record_res->second, import_path)); + FindStaticBinding(record, import_path)); return; } diff --git a/ets2panda/varbinder/ETSBinder.h b/ets2panda/varbinder/ETSBinder.h index 82f49cd9ae..48d7cf964e 100644 --- a/ets2panda/varbinder/ETSBinder.h +++ b/ets2panda/varbinder/ETSBinder.h @@ -43,7 +43,8 @@ public: dynamic_imports_(Allocator()->Adapter()), lambda_objects_(Allocator()->Adapter()), dynamic_import_vars_(Allocator()->Adapter()), - import_specifiers_(Allocator()->Adapter()) + import_specifiers_(Allocator()->Adapter()), + resolved_import_pathes_map_(Allocator()->Adapter()) { InitImplicitThisParam(); } @@ -119,6 +120,8 @@ public: void BuildImportDeclaration(ir::ETSImportDeclaration *decl); void BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *class_instance); void AddSpecifiersToTopBindings(ir::AstNode *specifier, const ir::ETSImportDeclaration *import); + ArenaVector GetExternalProgram(const util::StringView &source_name, + const ir::StringLiteral *import_path); bool AddImportNamespaceSpecifiersToTopBindings(ir::AstNode *specifier, const varbinder::Scope::VariableMap &global_bindings, const parser::Program *import_program, @@ -184,6 +187,26 @@ public: default_export_ = default_export; } + const ArenaUnorderedMap &ResolvedImportPathesMap() const + { + return resolved_import_pathes_map_; + } + + const util::StringView &GetResolvedImportPath(const util::StringView &path) const + { + ASSERT(resolved_import_pathes_map_.find(path) != resolved_import_pathes_map_.end()); + + return resolved_import_pathes_map_.find(path)->second; + } + + void FillResolvedImportPathes(const std::unordered_map &map, ArenaAllocator *allocator) + { + for (const auto &path : map) { + resolved_import_pathes_map_.emplace(util::UString(path.first, allocator).View(), + util::UString(path.second, allocator).View()); + } + } + bool IsDynamicModuleVariable(const Variable *var) const; bool IsDynamicNamespaceVariable(const Variable *var) const; const DynamicImportData *DynamicImportDataForVar(const Variable *var) const; @@ -218,6 +241,7 @@ private: DynamicImportVariables dynamic_import_vars_; ir::Identifier *this_param_ {}; ArenaVector> import_specifiers_; + ArenaUnorderedMap resolved_import_pathes_map_; ir::AstNode *default_export_ {}; }; -- Gitee