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 54aa5710280193a41be3096603bd9e19f26fa5ab Mon Sep 17 00:00:00 2001 From: Robert Sipka Date: Wed, 15 Nov 2023 12:51:41 +0100 Subject: [PATCH 2/2] Support extensions provided within import paths Signed-off-by: Robert Sipka --- ets2panda/parser/ETSparser.cpp | 22 +- ...mport_with_provided_extension-expected.txt | 369 ++++++++++++++++++ .../import_with_provided_extension.ets | 20 + ets2panda/util/helpers.cpp | 14 + ets2panda/util/helpers.h | 2 + ets2panda/varbinder/ETSBinder.cpp | 21 +- 6 files changed, 436 insertions(+), 12 deletions(-) create mode 100644 ets2panda/test/parser/ets/import_tests/import_with_provided_extension-expected.txt create mode 100644 ets2panda/test/parser/ets/import_tests/import_with_provided_extension.ets diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index 1100af3be5..592b50af32 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -232,7 +232,13 @@ ArenaVector ETSParser::PrepareExternalGlobalClass([[maybe_unuse res = ext_sources.find(name); } else { const util::UString source_file_path( - GetProgram()->SourceFilePath().Mutf8() + GetProgram()->GetPackageName().Mutf8(), Allocator()); + GetProgram()->SourceFilePath().Mutf8() + + (!util::Helpers::EndsWith(GetProgram()->SourceFilePath().Mutf8(), + std::string(panda::os::file::File::GetPathDelim())) + ? std::string(panda::os::file::File::GetPathDelim()) + : "") + + GetProgram()->GetPackageName().Mutf8(), + Allocator()); GetProgram()->SetSource(GetProgram()->SourceCode(), GetProgram()->SourceFile(), source_file_path.View()); } @@ -254,11 +260,6 @@ ArenaVector ETSParser::PrepareExternalGlobalClass([[maybe_unuse return statements; } -static bool IsCompitableExtension(const std::string &extension) -{ - return extension == ".ets" || extension == ".ts"; -} - void ETSParser::CollectDefaultSources() { std::vector paths; @@ -283,7 +284,7 @@ void ETSParser::CollectDefaultSources() std::string file_name = entry->d_name; std::string::size_type pos = file_name.find_last_of('.'); - if (pos == std::string::npos || !IsCompitableExtension(file_name.substr(pos))) { + if (pos == std::string::npos || !util::Helpers::IsCompatibleExtension(file_name.substr(pos))) { continue; } @@ -301,7 +302,8 @@ void ETSParser::CollectDefaultSources() #else for (auto const &path : stdlib) { for (auto const &entry : fs::directory_iterator(ResolveImportPath(path))) { - if (!fs::is_regular_file(entry) || !IsCompitableExtension(entry.path().extension().string())) { + if (!fs::is_regular_file(entry) || + !util::Helpers::IsCompatibleExtension(entry.path().extension().string())) { continue; } @@ -442,7 +444,7 @@ std::tuple, bool> ETSParser::CollectUserSources(const s std::string file_name = entry->d_name; std::string::size_type pos = file_name.find_last_of('.'); - if (pos == std::string::npos || !IsCompitableExtension(file_name.substr(pos))) { + if (pos == std::string::npos || !util::Helpers::IsCompatibleExtension(file_name.substr(pos))) { continue; } @@ -458,7 +460,7 @@ std::tuple, bool> ETSParser::CollectUserSources(const s closedir(dir); #else for (auto const &entry : fs::directory_iterator(resolved_path)) { - if (!fs::is_regular_file(entry) || !IsCompitableExtension(entry.path().extension().string())) { + if (!fs::is_regular_file(entry) || !util::Helpers::IsCompatibleExtension(entry.path().extension().string())) { continue; } diff --git a/ets2panda/test/parser/ets/import_tests/import_with_provided_extension-expected.txt b/ets2panda/test/parser/ets/import_tests/import_with_provided_extension-expected.txt new file mode 100644 index 0000000000..02141753f9 --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/import_with_provided_extension-expected.txt @@ -0,0 +1,369 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "./modules/default_export.ets", + "loc": { + "start": { + "line": 16, + "column": 15 + }, + "end": { + "line": 16, + "column": 45 + } + } + }, + "specifiers": [ + { + "type": "ImportNamespaceSpecifier", + "local": { + "type": "Identifier", + "name": "", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 14 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 16, + "column": 46 + } + } + }, + { + "type": "ImportDeclaration", + "source": { + "type": "StringLiteral", + "value": "./check_exported_3.ets", + "loc": { + "start": { + "line": 17, + "column": 19 + }, + "end": { + "line": 17, + "column": 43 + } + } + }, + "specifiers": [ + { + "type": "ImportSpecifier", + "local": { + "type": "Identifier", + "name": "bar", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 9 + }, + "end": { + "line": 17, + "column": 12 + } + } + }, + "imported": { + "type": "Identifier", + "name": "bar", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 9 + }, + "end": { + "line": 17, + "column": 12 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 9 + }, + "end": { + "line": 17, + "column": 12 + } + } + } + ], + "loc": { + "start": { + "line": 17, + "column": 1 + }, + "end": { + "line": 17, + "column": 44 + } + } + }, + { + "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": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "NonDefaultExportedFunc", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 1 + }, + "end": { + "line": 19, + "column": 23 + } + } + }, + "arguments": [], + "optional": false, + "loc": { + "start": { + "line": 19, + "column": 1 + }, + "end": { + "line": 19, + "column": 25 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 1 + }, + "end": { + "line": 19, + "column": 25 + } + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "bar", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 1 + }, + "end": { + "line": 20, + "column": 4 + } + } + }, + "arguments": [], + "optional": false, + "loc": { + "start": { + "line": 20, + "column": 1 + }, + "end": { + "line": 20, + "column": 6 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 1 + }, + "end": { + "line": 20, + "column": 6 + } + } + } + ], + "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": 21, + "column": 1 + } + } +} diff --git a/ets2panda/test/parser/ets/import_tests/import_with_provided_extension.ets b/ets2panda/test/parser/ets/import_tests/import_with_provided_extension.ets new file mode 100644 index 0000000000..8cc52059fd --- /dev/null +++ b/ets2panda/test/parser/ets/import_tests/import_with_provided_extension.ets @@ -0,0 +1,20 @@ +/* + * 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 * from "./modules/default_export.ets"; +import {bar} from "./check_exported_3.ets"; + +NonDefaultExportedFunc() +bar() diff --git a/ets2panda/util/helpers.cpp b/ets2panda/util/helpers.cpp index aafd8d43e7..840393c3d4 100644 --- a/ets2panda/util/helpers.cpp +++ b/ets2panda/util/helpers.cpp @@ -169,6 +169,20 @@ bool Helpers::IsRelativePath(const std::string &path) return ((path.find(current_dir_reference) == 0) || (path.find(parent_dir_reference) == 0)); } +bool Helpers::IsCompatibleExtension(const std::string &extension) +{ + return extension == ".ets" || extension == ".ts"; +} + +bool Helpers::EndsWith(const std::string &str, const std::string &suffix) +{ + if (str.length() < suffix.length()) { + return false; + } + size_t expect_pos = str.length() - suffix.length(); + return str.find(suffix, expect_pos) == expect_pos; +} + 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..0fb8798ea0 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 IsCompatibleExtension(const std::string &extension); + static bool EndsWith(const std::string &str, const std::string &suffix); 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..c23806a58f 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -592,10 +592,27 @@ void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const i } const auto &ext_records = global_record_table_.Program()->ExternalSources(); - const util::StringView source_name = + util::StringView source_name = (import->Module() == nullptr) ? import_path->Str() - : util::UString(import_path->Str().Mutf8() + import->Module()->Str().Mutf8(), Allocator()).View(); + : util::UString(import_path->Str().Mutf8() + + (!util::Helpers::EndsWith(import_path->Str().Mutf8(), + std::string(panda::os::file::File::GetPathDelim())) + ? std::string(panda::os::file::File::GetPathDelim()) + : "") + + import->Module()->Str().Mutf8(), + Allocator()) + .View(); + + // TODO(user): reconsider the file handling in case of import: dismemberment could be omitted? + if (!panda::os::file::File::IsDirectory(import->ResolvedSource()->Str().Mutf8())) { + const size_t idx = source_name.Utf8().find_last_of('.'); + if (idx != std::string::npos && + util::Helpers::IsCompatibleExtension(source_name.Substr(idx, source_name.Length()).Mutf8())) { + source_name = source_name.Substr(0, idx); + } + } + 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)); -- Gitee