diff --git a/BUILD.gn b/BUILD.gn index 99b4a26efe5d858895b44d50fc60166ab698188e..1d6b674bdb8f60d58102ab7c02482d14de29b610 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -163,6 +163,7 @@ libes2panda_sources = [ "compiler/function/generatorFunctionBuilder.cpp", "compiler/lowering/checkerPhase.cpp", "compiler/lowering/phase.cpp", + "compiler/lowering/plugin_phase.cpp", "compiler/lowering/util.cpp", "es2panda.cpp", "ir/as/namedType.cpp", @@ -347,6 +348,7 @@ libes2panda_sources = [ "util/bitset.cpp", "util/declgenEts2Ts.cpp", "util/helpers.cpp", + "util/plugin.cpp", "util/ustring.cpp", ] diff --git a/CMakeLists.txt b/CMakeLists.txt index e49880048b39a7862c1959de208a79d92c2719a7..7f93befbd65d2161ddbff4cb82a591a804b0eddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,7 @@ set(ES2PANDA_LIB_SRC compiler/function/generatorFunctionBuilder.cpp compiler/lowering/checkerPhase.cpp compiler/lowering/phase.cpp + compiler/lowering/plugin_phase.cpp compiler/lowering/util.cpp compiler/lowering/ets/generateDeclarations.cpp compiler/lowering/ets/opAssignment.cpp @@ -455,6 +456,7 @@ set(ES2PANDA_PUBLIC_SOURCES public/es2panda_lib.cpp util/generateBin.cpp util/options.cpp + util/plugin.cpp ) diff --git a/aot/main.cpp b/aot/main.cpp index cfe42af47936d3a7876724678d17f600fc9c23a7..4f5a09fcd3d288cfe12546898f3327e55fb38a95 100644 --- a/aot/main.cpp +++ b/aot/main.cpp @@ -22,6 +22,7 @@ #include "util/arktsconfig.h" #include "util/generateBin.h" #include "util/options.h" +#include "util/plugin.h" #include #include @@ -106,6 +107,21 @@ static int CompileFromConfig(es2panda::Compiler &compiler, util::Options *option return overall_res; } +static std::pair, bool> InitializePlugins(std::vector const &names) +{ + std::vector res; + for (auto &name : names) { + auto plugin = util::Plugin(util::StringView {name}); + if (!plugin.IsOk()) { + std::cerr << "Error: Failed to load plugin " << name << std::endl; + return {std::vector(), false}; + } + plugin.Initialize(); + res.push_back(std::move(plugin)); + } + return {std::move(res), true}; +} + static int Run(int argc, const char **argv) { auto options = std::make_unique(); @@ -118,7 +134,11 @@ static int Run(int argc, const char **argv) Logger::ComponentMask mask {}; mask.set(Logger::Component::ES2PANDA); Logger::InitializeStdLogging(Logger::LevelFromString(options->LogLevel()), mask); - es2panda::Compiler compiler(options->Extension(), options->ThreadCount()); + auto [plugins, ok] = InitializePlugins(options->CompilerOptions().plugins); + if (!ok) { + return 1; + } + es2panda::Compiler compiler(options->Extension(), options->ThreadCount(), std::move(plugins)); if (options->CompilerOptions().compilation_mode == CompilationMode::PROJECT) { return CompileFromConfig(compiler, options.get()); diff --git a/checker/checker.h b/checker/checker.h index 223836ae38acce4da3260c7bbff70389d990d4b6..90f4541a826087e4fcaa708d41cbb4f630055f8b 100644 --- a/checker/checker.h +++ b/checker/checker.h @@ -69,7 +69,7 @@ using ArgRange = std::pair; class Checker { public: explicit Checker(); - ~Checker() = default; + virtual ~Checker() = default; NO_COPY_SEMANTIC(Checker); NO_MOVE_SEMANTIC(Checker); diff --git a/compiler/core/compilerImpl.cpp b/compiler/core/compilerImpl.cpp index f5d80e417cab48fc5eccdaffbc61f9d4b8426568..a7fd8f8c2b2964459d704ef487319b0af35bfc37 100644 --- a/compiler/core/compilerImpl.cpp +++ b/compiler/core/compilerImpl.cpp @@ -15,6 +15,7 @@ #include "compilerImpl.h" +#include "es2panda.h" #include "compiler/core/compilerContext.h" #include "compiler/core/compileQueue.h" #include "compiler/core/compilerImpl.h" @@ -41,7 +42,7 @@ #include "checker/ETSchecker.h" #include "checker/ASchecker.h" #include "checker/JSchecker.h" -#include "es2panda.h" +#include "public/public.h" #include "util/declgenEts2Ts.h" #include @@ -89,13 +90,28 @@ static CompilerContext::CodeGenCb MakeCompileJob() }; } +static void SetupPublicContext(public_lib::Context *context, const SourceFile *source_file, ArenaAllocator *allocator, + CompileQueue *queue, std::vector const *plugins, + parser::ParserImpl *parser, CompilerContext *compiler_context) +{ + context->source_file = source_file; + context->allocator = allocator; + context->queue = queue; + context->plugins = plugins; + context->parser = parser; + context->checker = compiler_context->Checker(); + context->analyzer = context->checker->GetAnalyzer(); + context->compiler_context = compiler_context; + context->emitter = compiler_context->GetEmitter(); +} + using EmitCb = std::function; using PhaseListGetter = std::function()>; template static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const PhaseListGetter &get_phases, - const EmitCb &emit_cb) + CompilerImpl *compiler_impl) { ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); auto program = parser::Program::NewProgram(&allocator); @@ -115,47 +131,49 @@ static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const Phase auto emitter = Emitter(&context); context.SetEmitter(&emitter); + public_lib::Context public_context; + SetupPublicContext(&public_context, &unit.input, &allocator, compiler_impl->Queue(), &compiler_impl->Plugins(), + &parser, &context); + parser.ParseScript(unit.input, unit.options.compilation_mode == CompilationMode::GEN_STD_LIB); for (auto *phase : get_phases()) { - if (!phase->Apply(&context, &program)) { + if (!phase->Apply(&public_context, &program)) { return nullptr; } } emitter.GenAnnotation(); - return emit_cb(&context); + return compiler_impl->Emit(&context); } pandasm::Program *CompilerImpl::Compile(const CompilationUnit &unit) { - auto emit_cb = [this](CompilerContext *context) -> pandasm::Program * { return Emit(context); }; - switch (unit.ext) { case ScriptExtension::TS: { return CreateCompiler(unit, compiler::GetTrivialPhaseList, - emit_cb); + this); } case ScriptExtension::AS: { return CreateCompiler(unit, compiler::GetTrivialPhaseList, - emit_cb); + this); } case ScriptExtension::ETS: { return CreateCompiler(unit, compiler::GetETSPhaseList, - emit_cb); + this); } case ScriptExtension::JS: { return CreateCompiler(unit, compiler::GetTrivialPhaseList, - emit_cb); + this); } default: { UNREACHABLE(); diff --git a/compiler/core/compilerImpl.h b/compiler/core/compilerImpl.h index cee0090c005d3e6cede14ae5c38d18c58b436d61..9ed854e5394b837053315b4b2a1bb77fa81e52d8 100644 --- a/compiler/core/compilerImpl.h +++ b/compiler/core/compilerImpl.h @@ -49,20 +49,35 @@ public: class CompilerImpl { public: - explicit CompilerImpl(size_t thread_count) : queue_(thread_count) {} + explicit CompilerImpl(size_t thread_count, std::vector const *plugins) + : queue_(thread_count), plugins_(plugins) + { + } NO_COPY_SEMANTIC(CompilerImpl); NO_MOVE_SEMANTIC(CompilerImpl); ~CompilerImpl() = default; pandasm::Program *Compile(const CompilationUnit &unit); + std::vector const &Plugins() + { + return *plugins_; + } + static void DumpAsm(const panda::pandasm::Program *prog); -private: panda::pandasm::Program *Emit(CompilerContext *context); + + CompileQueue *Queue() + { + return &queue_; + } + +private: static void HandleContextLiterals(CompilerContext *context); CompileQueue queue_; + std::vector const *plugins_; }; } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/checkerPhase.cpp b/compiler/lowering/checkerPhase.cpp index 18e9df4221cc05bf5a67c7cecea55bed349c22cc..a697c150fe0db8b13dbca65ccf500e82f111a6a5 100644 --- a/compiler/lowering/checkerPhase.cpp +++ b/compiler/lowering/checkerPhase.cpp @@ -18,9 +18,9 @@ #include "compiler/core/compilerContext.h" namespace panda::es2panda::compiler { -bool CheckerPhase::Perform(CompilerContext *ctx, [[maybe_unused]] parser::Program *program) +bool CheckerPhase::Perform(public_lib::Context *ctx, [[maybe_unused]] parser::Program *program) { - return ctx->Checker()->StartChecker(ctx->Binder(), *ctx->Options()); + return ctx->checker->StartChecker(ctx->compiler_context->Binder(), *ctx->compiler_context->Options()); } } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/checkerPhase.h b/compiler/lowering/checkerPhase.h index 1fac2594bdedb9b13810871d0fd33ada56a722da..23927c7ff9eb9250059ccf1a439b9768852c3ea4 100644 --- a/compiler/lowering/checkerPhase.h +++ b/compiler/lowering/checkerPhase.h @@ -25,7 +25,7 @@ class CheckerPhase : public Phase { return "checker"; } - bool Perform(CompilerContext *ctx, parser::Program *program) override; + bool Perform(public_lib::Context *ctx, parser::Program *program) override; }; } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/ets/generateDeclarations.cpp b/compiler/lowering/ets/generateDeclarations.cpp index e29ed817310f4e723e5a64bf3472a5eab0d1268e..ce65bab27f95a7b154c37aca16d5b2ea78c0938c 100644 --- a/compiler/lowering/ets/generateDeclarations.cpp +++ b/compiler/lowering/ets/generateDeclarations.cpp @@ -19,11 +19,12 @@ #include "util/declgenEts2Ts.h" namespace panda::es2panda::compiler { -bool GenerateTsDeclarationsPhase::Perform(CompilerContext *ctx, parser::Program *program) +bool GenerateTsDeclarationsPhase::Perform(public_lib::Context *ctx, parser::Program *program) { - auto *checker = ctx->Checker(); - return (ctx->Options()->ts_decl_out.empty() || - util::GenerateTsDeclarations(checker->AsETSChecker(), program, ctx->Options()->ts_decl_out)); + auto *checker = ctx->checker; + return ( + ctx->compiler_context->Options()->ts_decl_out.empty() || + util::GenerateTsDeclarations(checker->AsETSChecker(), program, ctx->compiler_context->Options()->ts_decl_out)); } } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/ets/generateDeclarations.h b/compiler/lowering/ets/generateDeclarations.h index a3fd6bdebabd1ee74547de7b34fc951288d74f78..28c0de7ce8ca1f4fa7a7d2596ea2af2cba33b045 100644 --- a/compiler/lowering/ets/generateDeclarations.h +++ b/compiler/lowering/ets/generateDeclarations.h @@ -26,7 +26,7 @@ public: { return "generate-ts-declarations"; } - bool Perform(CompilerContext *ctx, parser::Program *program) override; + bool Perform(public_lib::Context *ctx, parser::Program *program) override; }; } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/ets/opAssignment.cpp b/compiler/lowering/ets/opAssignment.cpp index 31913c1a395f74a6ed6f065381c2876bdd962dcd..0c4c7845b3076d777e57b047aca9d2682fc638c9 100644 --- a/compiler/lowering/ets/opAssignment.cpp +++ b/compiler/lowering/ets/opAssignment.cpp @@ -245,9 +245,9 @@ ir::Expression *HandleOpAssignment(checker::ETSChecker *checker, ir::AssignmentE return res; } -bool OpAssignmentLowering::Perform(CompilerContext *ctx, parser::Program *program) +bool OpAssignmentLowering::Perform(public_lib::Context *ctx, parser::Program *program) { - if (ctx->Options()->compilation_mode == CompilationMode::GEN_STD_LIB) { + if (ctx->compiler_context->Options()->compilation_mode == CompilationMode::GEN_STD_LIB) { for (auto &[_, ext_programs] : program->ExternalSources()) { (void)_; for (auto *ext_prog : ext_programs) { @@ -256,7 +256,7 @@ bool OpAssignmentLowering::Perform(CompilerContext *ctx, parser::Program *progra } } - checker::ETSChecker *checker = ctx->Checker()->AsETSChecker(); + checker::ETSChecker *checker = ctx->checker->AsETSChecker(); program->Ast()->TransformChildrenRecursively([checker](ir::AstNode *ast) -> ir::AstNode * { if (ast->IsAssignmentExpression() && @@ -270,9 +270,9 @@ bool OpAssignmentLowering::Perform(CompilerContext *ctx, parser::Program *progra return true; } -bool OpAssignmentLowering::Postcondition(CompilerContext *ctx, const parser::Program *program) +bool OpAssignmentLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program) { - if (ctx->Options()->compilation_mode == CompilationMode::GEN_STD_LIB) { + if (ctx->compiler_context->Options()->compilation_mode == CompilationMode::GEN_STD_LIB) { for (auto &[_, ext_programs] : program->ExternalSources()) { (void)_; for (auto *ext_prog : ext_programs) { diff --git a/compiler/lowering/ets/opAssignment.h b/compiler/lowering/ets/opAssignment.h index f85facad8559bcdd92720fa0b337ce700bff768d..d8e974f749e0e34718bf9448f74bd695c36c1bea 100644 --- a/compiler/lowering/ets/opAssignment.h +++ b/compiler/lowering/ets/opAssignment.h @@ -23,8 +23,8 @@ namespace panda::es2panda::compiler { class OpAssignmentLowering : public Phase { public: util::StringView Name() override; - bool Perform(CompilerContext *ctx, parser::Program *program) override; - bool Postcondition(CompilerContext *ctx, const parser::Program *program) override; + bool Perform(public_lib::Context *ctx, parser::Program *program) override; + bool Postcondition(public_lib::Context *ctx, const parser::Program *program) override; }; } // namespace panda::es2panda::compiler diff --git a/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 5877c6e4907ef23c263a0290514724a6c3c3becb..eae7feb7592cb608d4eb7fbe4ee00fcf3cfb79c4 100644 --- a/compiler/lowering/phase.cpp +++ b/compiler/lowering/phase.cpp @@ -18,8 +18,10 @@ #include "compiler/core/compilerContext.h" #include "lexer/token/sourceLocation.h" #include "compiler/lowering/checkerPhase.h" +#include "compiler/lowering/plugin_phase.h" #include "compiler/lowering/ets/generateDeclarations.h" #include "compiler/lowering/ets/opAssignment.h" +#include "public/es2panda_lib.h" namespace panda::es2panda::compiler { @@ -34,31 +36,35 @@ std::vector GetTrivialPhaseList() static GenerateTsDeclarationsPhase GENERATE_TS_DECLARATIONS_PHASE; static OpAssignmentLowering OP_ASSIGNMENT_LOWERING; +static PluginPhase PLUGINS_AFTER_PARSE {"plugins-after-parse", ES2PANDA_STATE_PARSED, &util::Plugin::AfterParse}; +static PluginPhase PLUGINS_AFTER_CHECK {"plugins-after-check", ES2PANDA_STATE_CHECKED, &util::Plugin::AfterCheck}; +static PluginPhase PLUGINS_AFTER_LOWERINGS {"plugins-after-lowering", ES2PANDA_STATE_LOWERED, + &util::Plugin::AfterLowerings}; std::vector GetETSPhaseList() { return std::vector { - &CHECKER_PHASE, - &GENERATE_TS_DECLARATIONS_PHASE, - &OP_ASSIGNMENT_LOWERING, + &PLUGINS_AFTER_PARSE, &CHECKER_PHASE, &PLUGINS_AFTER_CHECK, &GENERATE_TS_DECLARATIONS_PHASE, + &OP_ASSIGNMENT_LOWERING, &PLUGINS_AFTER_LOWERINGS, }; } -bool Phase::Apply(CompilerContext *ctx, parser::Program *program) +bool Phase::Apply(public_lib::Context *ctx, parser::Program *program) { - const auto *options = ctx->Options(); - if (options->skip_phases.count(Name()) > 0) { + const auto *options = ctx->compiler_context->Options(); + const auto name = std::string {Name()}; + if (options->skip_phases.count(name) > 0) { return true; } - if (options->dump_before_phases.count(Name()) > 0) { + if (options->dump_before_phases.count(name) > 0) { std::cout << "Before phase " << Name() << ":" << std::endl; std::cout << program->Dump() << std::endl; } #ifndef NDEBUG if (!Precondition(ctx, program)) { - ctx->Checker()->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); + ctx->checker->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); } #endif @@ -66,14 +72,14 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) return false; } - if (options->dump_after_phases.count(Name()) > 0) { + if (options->dump_after_phases.count(name) > 0) { std::cout << "After phase " << Name() << ":" << std::endl; std::cout << program->Dump() << std::endl; } #ifndef NDEBUG if (!Postcondition(ctx, program)) { - ctx->Checker()->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); + ctx->checker->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); } #endif diff --git a/compiler/lowering/phase.h b/compiler/lowering/phase.h index 07a304620e38b37dfc8f5dea29289bde36d0b8ba..f100938968274cfbcdceb3a18291d8db5921667a 100644 --- a/compiler/lowering/phase.h +++ b/compiler/lowering/phase.h @@ -17,22 +17,25 @@ #define ES2PANDA_COMPILER_LOWERING_PHASE_H #include "parser/program/program.h" +#include "public/public.h" namespace panda::es2panda::compiler { class Phase { public: /* If Apply returns false, processing is stopped. */ - bool Apply(CompilerContext *ctx, parser::Program *program); + bool Apply(public_lib::Context *ctx, parser::Program *program); virtual util::StringView Name() = 0; - virtual bool Precondition([[maybe_unused]] CompilerContext *ctx, [[maybe_unused]] const parser::Program *program) + virtual bool Precondition([[maybe_unused]] public_lib::Context *ctx, + [[maybe_unused]] const parser::Program *program) { return true; } - virtual bool Perform(CompilerContext *ctx, parser::Program *program) = 0; - virtual bool Postcondition([[maybe_unused]] CompilerContext *ctx, [[maybe_unused]] const parser::Program *program) + virtual bool Perform(public_lib::Context *ctx, parser::Program *program) = 0; + virtual bool Postcondition([[maybe_unused]] public_lib::Context *ctx, + [[maybe_unused]] const parser::Program *program) { return true; } diff --git a/compiler/lowering/plugin_phase.cpp b/compiler/lowering/plugin_phase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f52b834e307d9ad9c3b425bda8cf1cc5497b3ee3 --- /dev/null +++ b/compiler/lowering/plugin_phase.cpp @@ -0,0 +1,39 @@ +/** + * 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. + */ + +#include "plugin_phase.h" + +namespace panda::es2panda::compiler { + +bool PluginPhase::Perform(public_lib::Context *ctx, [[maybe_unused]] parser::Program *program) +{ + ctx->state = context_state_; + + if (ctx->plugins == nullptr) { + return true; + } + + for (auto &plugin : *(ctx->plugins)) { + (plugin.*method_call_)(reinterpret_cast(ctx)); + if (ctx->state == ES2PANDA_STATE_ERROR) { + lexer::SourcePosition pos; // TODO(gogabr): how to initialize? + ctx->checker->ThrowTypeError(ctx->error_message, pos); + } + } + + return true; +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/lowering/plugin_phase.h b/compiler/lowering/plugin_phase.h new file mode 100644 index 0000000000000000000000000000000000000000..d0f8ce0174b4e1d3ae96c93cdf41126646556297 --- /dev/null +++ b/compiler/lowering/plugin_phase.h @@ -0,0 +1,47 @@ +/** + * 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. + */ + +#ifndef ES2PANDA_COMPILER_PLUGIN_PHASE_H +#define ES2PANDA_COMPILER_PLUGIN_PHASE_H + +#include "compiler/lowering/phase.h" +#include "util/plugin.h" + +namespace panda::es2panda::compiler { + +class PluginPhase : public Phase { +public: + constexpr PluginPhase(char const *name, es2panda_ContextState context_state, + void (util::Plugin::*method_call)(es2panda_Context *) const) noexcept + : name_(name), context_state_(context_state), method_call_(method_call) + { + } + + util::StringView Name() override + { + return name_; + } + + bool Perform(public_lib::Context *ctx, parser::Program *program) override; + +private: + char const *name_; + es2panda_ContextState context_state_; + void (util::Plugin::*method_call_)(es2panda_Context *) const; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/lowering/util.cpp b/compiler/lowering/util.cpp index 38bcd67f3d888ba42b30525914e7858f36fd7e47..3f33736c61ec91de187fbdb18cb2b6c8a09ec1d9 100644 --- a/compiler/lowering/util.cpp +++ b/compiler/lowering/util.cpp @@ -21,6 +21,10 @@ namespace panda::es2panda::compiler { binder::Scope *NearestScope(const ir::AstNode *ast) { + if (ast == nullptr) { + return nullptr; + } + while (!ast->IsScopeBearer()) { ast = ast->Parent(); ASSERT(ast != nullptr); diff --git a/es2panda.cpp b/es2panda.cpp index 458f00dd7b422b8a611d8d79e4f9c0ca49bf797c..b05f938f180f3aef0a7e4bda8b84c008cef54063 100644 --- a/es2panda.cpp +++ b/es2panda.cpp @@ -56,10 +56,12 @@ SourceFile::SourceFile(std::string_view fn, std::string_view s, std::string_view { } -Compiler::Compiler(ScriptExtension ext) : Compiler(ext, DEFAULT_THREAD_COUNT) {} +Compiler::Compiler(ScriptExtension ext) : Compiler(ext, DEFAULT_THREAD_COUNT, {}) {} -Compiler::Compiler(ScriptExtension ext, size_t thread_count) - : compiler_(new compiler::CompilerImpl(thread_count)), ext_(ext) +Compiler::Compiler(ScriptExtension ext, size_t thread_count) : Compiler(ext, thread_count, {}) {} + +Compiler::Compiler(ScriptExtension ext, size_t thread_count, std::vector &&plugins) + : plugins_(std::move(plugins)), compiler_(new compiler::CompilerImpl(thread_count, &plugins_)), ext_(ext) { } diff --git a/es2panda.h b/es2panda.h index e0bfd92142537c09b70e3357831e2dd3d15d7260..b3777523fae6f2e90fc1af429da4682d6eb8699e 100644 --- a/es2panda.h +++ b/es2panda.h @@ -18,6 +18,7 @@ #include "macros.h" #include "util/arktsconfig.h" +#include "util/plugin.h" #include "util/ustring.h" #include @@ -99,9 +100,10 @@ struct CompilerOptions { bool parse_only {}; std::string std_lib {}; std::string ts_decl_out {}; - std::unordered_set skip_phases {}; - std::unordered_set dump_before_phases {}; - std::unordered_set dump_after_phases {}; + std::vector plugins {}; + std::unordered_set skip_phases {}; + std::unordered_set dump_before_phases {}; + std::unordered_set dump_after_phases {}; std::shared_ptr arkts_config {}; CompilationMode compilation_mode {}; // NOLINTEND(misc-non-private-member-variables-in-classes) @@ -191,6 +193,7 @@ class Compiler { public: explicit Compiler(ScriptExtension ext); explicit Compiler(ScriptExtension ext, size_t thread_count); + explicit Compiler(ScriptExtension ext, size_t thread_count, std::vector &&plugins); ~Compiler(); NO_COPY_SEMANTIC(Compiler); NO_MOVE_SEMANTIC(Compiler); @@ -211,7 +214,13 @@ public: return error_; } + std::vector const &Plugins() + { + return plugins_; + } + private: + std::vector const plugins_; compiler::CompilerImpl *compiler_; Error error_; ScriptExtension ext_; diff --git a/ir/astNode.h b/ir/astNode.h index b9558663a3a25fd3e987cc93a64866b1fa0e4b12..62ec441c42c196818a9c4a167d2ace5db099f6b2 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -351,6 +351,12 @@ public: variable_ = variable; } + // When no decorators are allowed, we cannot return a reference to an empty vector. + virtual const ArenaVector *DecoratorsPtr() const + { + return nullptr; + } + virtual void AddDecorators([[maybe_unused]] ArenaVector &&decorators) { UNREACHABLE(); diff --git a/ir/base/classDefinition.h b/ir/base/classDefinition.h index c90fee1e2d578b2a8f12bfe15e374458a4ef51ba..1ce01e19866083a5f64286e8882c131b771d2b5c 100644 --- a/ir/base/classDefinition.h +++ b/ir/base/classDefinition.h @@ -144,6 +144,11 @@ public: return super_class_; } + void SetSuper(Expression *super_class) + { + super_class_ = super_class; + } + bool IsGlobal() const { return (modifiers_ & ClassDefinitionModifiers::GLOBAL) != 0; @@ -208,6 +213,11 @@ public: return ctor_; } + void SetCtor(MethodDefinition *ctor) + { + ctor_ = ctor; + } + ArenaVector &Implements() { return implements_; @@ -228,6 +238,11 @@ public: return type_params_; } + void SetTypeParams(ir::TSTypeParameterDeclaration *type_params) + { + type_params_ = type_params; + } + const FunctionExpression *Ctor() const; bool HasPrivateMethod() const; bool HasComputedInstanceField() const; diff --git a/ir/base/classElement.h b/ir/base/classElement.h index 836083e64fdf75040df2273a2d4a99ae15f9d2a0..ca728032cd88f55202fdd7273dcebd15b6c29223 100644 --- a/ir/base/classElement.h +++ b/ir/base/classElement.h @@ -63,6 +63,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + bool IsComputed() const { return is_computed_; @@ -73,6 +78,11 @@ public: decorators_ = std::move(decorators); } + bool CanHaveDecorator([[maybe_unused]] bool in_ts) const override + { + return true; + } + virtual PrivateFieldKind ToPrivateFieldKind(bool is_static) const = 0; protected: diff --git a/ir/base/spreadElement.h b/ir/base/spreadElement.h index 037cc59ca40d90174d64c54e4380f659c96ae2b5..7c56b7813f10f146b368586c5f9f2bc5fe1d3d06 100644 --- a/ir/base/spreadElement.h +++ b/ir/base/spreadElement.h @@ -58,12 +58,22 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators(ArenaVector &&decorators) override { decorators_ = std::move(decorators); } - void SetOptional(bool const optional) noexcept + bool CanHaveDecorator([[maybe_unused]] bool in_ts) const override + { + return true; + } + + void SetOptional(bool optional) noexcept { optional_ = optional; } diff --git a/ir/ets/etsFunctionType.h b/ir/ets/etsFunctionType.h index b0b0e646f5a043b879d432d5013d86adcb0b2e2e..27aff7f422ec6543737425378621a8263a9fd1f3 100644 --- a/ir/ets/etsFunctionType.h +++ b/ir/ets/etsFunctionType.h @@ -80,6 +80,11 @@ public: functional_interface_ = functional_interface; } + ir::ScriptFunctionFlags Flags() + { + return func_flags_; + } + bool IsThrowing() const { return (func_flags_ & ir::ScriptFunctionFlags::THROWS) != 0; diff --git a/ir/ets/etsStructDeclaration.h b/ir/ets/etsStructDeclaration.h index 7a52e9b804388b7722ac9993ddced626184610ec..ce95928cbb1fbaec9c3710098559ff8119668a25 100644 --- a/ir/ets/etsStructDeclaration.h +++ b/ir/ets/etsStructDeclaration.h @@ -41,6 +41,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators(ArenaVector &&decorators) override { decorators_ = std::move(decorators); diff --git a/ir/expressions/arrayExpression.h b/ir/expressions/arrayExpression.h index 5848b480b54f641968a51e5abaddad04fa2a09ed..2ea377ae67c66600a857c8b7081bc3de34d51e00 100644 --- a/ir/expressions/arrayExpression.h +++ b/ir/expressions/arrayExpression.h @@ -93,17 +93,26 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators([[maybe_unused]] ArenaVector &&decorators) override { decorators_ = std::move(decorators); } + bool CanHaveDecorator([[maybe_unused]] bool in_ts) const override + { + return true; + } + // NOLINTNEXTLINE(google-default-arguments) [[nodiscard]] Expression *Clone(ArenaAllocator *allocator, AstNode *parent = nullptr) override; [[nodiscard]] bool ConvertibleToArrayPattern(); [[nodiscard]] ValidationInfo ValidateExpression(); - void TransformChildren(const NodeTransformer &cb) override; void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; diff --git a/ir/expressions/assignmentExpression.h b/ir/expressions/assignmentExpression.h index c16b3780ca2c7a53b29ac486ec018c1a47f7dab0..29a67d7fa5a9cad79e159f18281467fd778187bd 100644 --- a/ir/expressions/assignmentExpression.h +++ b/ir/expressions/assignmentExpression.h @@ -70,7 +70,7 @@ public: return operator_; } - [[nodiscard]] lexer::TokenType SetOperatorType(lexer::TokenType token_type) noexcept + lexer::TokenType SetOperatorType(lexer::TokenType token_type) noexcept { return operator_ = token_type; } diff --git a/ir/expressions/identifier.h b/ir/expressions/identifier.h index e4b7f7122811eb3268a62b134a817e7e5b8f68a3..3d622c6fe0bf33ec11573ff86dde9f35c8832697 100644 --- a/ir/expressions/identifier.h +++ b/ir/expressions/identifier.h @@ -83,6 +83,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + [[nodiscard]] bool IsOptional() const noexcept { return (flags_ & IdentifierFlags::OPTIONAL) != 0; @@ -188,6 +193,11 @@ public: // NOLINTNEXTLINE(google-default-arguments) [[nodiscard]] Expression *Clone(ArenaAllocator *allocator, AstNode *parent = nullptr) override; + bool CanHaveDecorator([[maybe_unused]] bool in_ts) const override + { + return true; + } + [[nodiscard]] ValidationInfo ValidateExpression(); void TransformChildren(const NodeTransformer &cb) override; diff --git a/ir/expressions/objectExpression.h b/ir/expressions/objectExpression.h index b0803651c6966355b9c0b9b38743d2dff597eb01..2f8dd66856566eef74c15572964aa5edbaa7ec51 100644 --- a/ir/expressions/objectExpression.h +++ b/ir/expressions/objectExpression.h @@ -77,17 +77,26 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators([[maybe_unused]] ArenaVector &&decorators) override { decorators_ = std::move(decorators); } + bool CanHaveDecorator([[maybe_unused]] bool in_ts) const override + { + return true; + } + // NOLINTNEXTLINE(google-default-arguments) [[nodiscard]] Expression *Clone(ArenaAllocator *allocator, AstNode *parent = nullptr) override; [[nodiscard]] ValidationInfo ValidateExpression(); [[nodiscard]] bool ConvertibleToObjectPattern(); - void SetDeclaration(); void SetOptional(bool optional); void TransformChildren(const NodeTransformer &cb) override; diff --git a/ir/statements/classDeclaration.h b/ir/statements/classDeclaration.h index c2e535c0740205f0be808f6d4e772d51b8f8e7b1..7e4b65d78fd0b94b64f827e8f2eea8b7ac882a18 100644 --- a/ir/statements/classDeclaration.h +++ b/ir/statements/classDeclaration.h @@ -41,6 +41,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators(ArenaVector &&decorators) override { decorators_ = std::move(decorators); diff --git a/ir/statements/variableDeclaration.h b/ir/statements/variableDeclaration.h index 601170366b6d7bc04feb7395bc72542d65953875..fac991570d54d21800e1fcdf469654a2c48fbd11 100644 --- a/ir/statements/variableDeclaration.h +++ b/ir/statements/variableDeclaration.h @@ -55,6 +55,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators([[maybe_unused]] ArenaVector &&decorators) override { decorators_ = std::move(decorators); diff --git a/ir/ts/tsEnumDeclaration.h b/ir/ts/tsEnumDeclaration.h index 7661f213950e3f9d819dc497096eb8e2006ca26e..de6da8b05cadfabbad7ce4bdf05d05b8031a749d 100644 --- a/ir/ts/tsEnumDeclaration.h +++ b/ir/ts/tsEnumDeclaration.h @@ -89,6 +89,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators([[maybe_unused]] ArenaVector &&decorators) override { decorators_ = std::move(decorators); diff --git a/ir/ts/tsInterfaceDeclaration.h b/ir/ts/tsInterfaceDeclaration.h index be5c2d258cb966ab756f82d7befe38a115db23fc..8bd08657750c3c6ef8cd5a6b4c347f5a22c41563 100644 --- a/ir/ts/tsInterfaceDeclaration.h +++ b/ir/ts/tsInterfaceDeclaration.h @@ -120,6 +120,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddDecorators([[maybe_unused]] ArenaVector &&decorators) override { decorators_ = std::move(decorators); diff --git a/ir/ts/tsTypeAliasDeclaration.h b/ir/ts/tsTypeAliasDeclaration.h index f38a2cb58c7d4c4a76954f37695eb08a9546952b..815e8dd921dac25794d47da52c0685ffd5798d70 100644 --- a/ir/ts/tsTypeAliasDeclaration.h +++ b/ir/ts/tsTypeAliasDeclaration.h @@ -72,6 +72,11 @@ public: return decorators_; } + const ArenaVector *DecoratorsPtr() const override + { + return &Decorators(); + } + void AddTypeParameters(ir::TSTypeParameterDeclaration *type_params) { type_params_ = type_params; diff --git a/parser/ETSparser.h b/parser/ETSparser.h index 87d1fbc9800bc10fbf2d939ca74c8ece6e7d46cd..cff5228070728bc6599c1134c162845b2aacceec 100644 --- a/parser/ETSparser.h +++ b/parser/ETSparser.h @@ -37,7 +37,7 @@ public: NO_COPY_SEMANTIC(ETSParser); NO_MOVE_SEMANTIC(ETSParser); - ~ETSParser() = default; + ~ETSParser() final = default; ir::Expression *CreateExpression(ExpressionParseFlags flags, std::string_view source_code, std::string_view file_name = DEFAULT_SOURCE_FILE); diff --git a/parser/parserImpl.h b/parser/parserImpl.h index 66d5d46710a956e48b68dc4457956c0c06558c82..1b2859705ab3446b42deef64e3f99037068d0222 100644 --- a/parser/parserImpl.h +++ b/parser/parserImpl.h @@ -174,7 +174,7 @@ public: explicit ParserImpl(Program *program, const CompilerOptions &options, ParserStatus status = ParserStatus::NO_OPTS); NO_COPY_SEMANTIC(ParserImpl); NO_MOVE_SEMANTIC(ParserImpl); - ~ParserImpl() = default; + virtual ~ParserImpl() = default; void ParseScript(const SourceFile &source_file, bool gen_std_lib); diff --git a/public/es2panda_lib.cpp b/public/es2panda_lib.cpp index beea4e9fd7c05f5b386bf9464baae6b7ea60575f..7491f980a3ae67ebc60b674d8df9ee6ffbd2ef94 100644 --- a/public/es2panda_lib.cpp +++ b/public/es2panda_lib.cpp @@ -1,6 +1,25 @@ +/** + * Copyright (c) 2021-2022 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 "es2panda_lib.h" #include +#include "binder/binder.h" +#include "binder/scope.h" +#include "binder/variable.h" +#include "public/public.h" #include "generated/signatures.h" #include "es2panda.h" #include "assembler/assembly-program.h" @@ -13,39 +32,199 @@ #include "compiler/core/ETSGen.h" #include "compiler/core/regSpiller.h" #include "compiler/lowering/phase.h" +#include "compiler/lowering/util.h" #include "ir/astNode.h" +#include "ir/expressions/arrowFunctionExpression.h" +#include "ir/ts/tsAsExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/binaryExpression.h" +#include "ir/statements/blockStatement.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/chainExpression.h" +#include "ir/statements/classDeclaration.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classElement.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/base/classProperty.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/functionDeclaration.h" +#include "ir/ets/etsFunctionType.h" #include "ir/expressions/identifier.h" +#include "ir/statements/ifStatement.h" #include "parser/ETSparser.h" #include "parser/context/parserContext.h" #include "parser/program/program.h" #include "util/generateBin.h" +#include "util/language.h" #include "util/options.h" namespace panda::es2panda::public_lib { -struct ConfigImpl { - std::unique_ptr options; -}; -struct Context { - ConfigImpl *config = nullptr; - std::string source_file_name; - std::string input; - std::unique_ptr source_file; - std::unique_ptr allocator; - std::unique_ptr queue; - - std::unique_ptr parser_program; - std::unique_ptr parser; - std::unique_ptr checker; - std::unique_ptr analyzer; - std::unique_ptr compiler_context; - std::unique_ptr emitter; - std::unique_ptr program; - - es2panda_ContextState state = ES2PANDA_STATE_NEW; - std::string error_message; +struct TokenTypeToStr { + lexer::TokenType token; + char const *str; }; +static lexer::TokenType StrToToken(TokenTypeToStr const *table, char const *str) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (auto *tp = table; tp->str != nullptr; tp++) { + if (strcmp(str, tp->str) == 0) { + return tp->token; + } + } + UNREACHABLE(); +} + +static char const *TokenToStr(TokenTypeToStr const *table, lexer::TokenType token) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (auto *tp = table; tp->str != nullptr; tp++) { + if (tp->token == token) { + return tp->str; + } + } + UNREACHABLE(); +} + +static char const *StringViewToCString(ArenaAllocator *allocator, util::StringView const sv) +{ + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic, readability-simplify-subscript-expr) + std::string_view utf8 = sv.Utf8(); + if (utf8.data()[utf8.size()] == '\0') { + // Avoid superfluous allocation. + return utf8.data(); + } + char *res = reinterpret_cast(allocator->Alloc(utf8.size() + 1)); + memmove(res, utf8.cbegin(), utf8.size()); + res[utf8.size()] = '\0'; + return res; + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic, readability-simplify-subscript-expr) +} + +static char const *ArenaStrdup(ArenaAllocator *allocator, char const *src) +{ + size_t len = strlen(src); + char *res = reinterpret_cast(allocator->Alloc(len + 1)); + memmove(res, src, len); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + res[len] = '\0'; + return res; +} + +#define FOR_ALL_MODIFIER_FLAGS(_) \ + _(STATIC) \ + _(ASYNC) \ + _(PUBLIC) \ + _(PROTECTED) \ + _(PRIVATE) \ + _(DECLARE) \ + _(READONLY) \ + _(OPTIONAL) \ + _(DEFINITE) \ + _(ABSTRACT) \ + _(CONST) \ + _(FINAL) \ + _(NATIVE) \ + _(OVERRIDE) \ + _(CONSTRUCTOR) \ + _(SYNCHRONIZED) \ + _(FUNCTIONAL) \ + _(IN) \ + _(OUT) \ + _(INTERNAL) \ + _(NULLABLE) \ + _(EXPORT) \ + _(SETTER) \ + _(DEFAULT_EXPORT) + +static ir::ModifierFlags E2pToIrModifierFlags(es2panda_ModifierFlags e2p_flags) +{ + ir::ModifierFlags ir_flags {ir::ModifierFlags::NONE}; + +#define DO_FLAG(FL) \ + if ((e2p_flags & ES2PANDA_MODIFIER_##FL) != 0) { \ + ir_flags |= ir::ModifierFlags::FL; \ + } + + FOR_ALL_MODIFIER_FLAGS(DO_FLAG) + +#undef DO_FLAG + + return ir_flags; +} + +static es2panda_ModifierFlags IrToE2pModifierFlags(ir::ModifierFlags ir_flags) +{ + es2panda_ModifierFlags e2p_flags {ES2PANDA_MODIFIER_NONE}; + +#define DO_FLAG(FL) \ + if ((ir_flags & ir::ModifierFlags::FL) != 0) { \ + e2p_flags = (es2panda_ModifierFlags)(e2p_flags | ES2PANDA_MODIFIER_##FL); \ + } + + FOR_ALL_MODIFIER_FLAGS(DO_FLAG) + +#undef DO_FLAG + + return e2p_flags; +} + +#define FOR_ALL_SCRIPT_FUNCTION_FLAGS(_) \ + _(GENERATOR) \ + _(ASYNC) \ + _(ARROW) \ + _(EXPRESSION) \ + _(OVERLOAD) \ + _(CONSTRUCTOR) \ + _(METHOD) \ + _(STATIC_BLOCK) \ + _(HIDDEN) \ + _(IMPLICIT_SUPER_CALL_NEEDED) \ + _(ENUM) \ + _(EXTERNAL) \ + _(PROXY) \ + _(THROWS) \ + _(RETHROWS) \ + _(GETTER) \ + _(SETTER) \ + _(DEFAULT_PARAM_PROXY) \ + _(ENTRY_POINT) \ + _(INSTANCE_EXTENSION_METHOD) \ + _(HAS_RETURN) + +static ir::ScriptFunctionFlags E2pToIrScriptFunctionFlags(es2panda_ScriptFunctionFlags e2p_flags) +{ + ir::ScriptFunctionFlags ir_flags {ir::ScriptFunctionFlags::NONE}; + +#define DO_FLAG(FL) \ + if ((e2p_flags & ES2PANDA_SCRIPT_FUNCTION_##FL) != 0) { \ + ir_flags |= ir::ScriptFunctionFlags::FL; \ + } + + FOR_ALL_SCRIPT_FUNCTION_FLAGS(DO_FLAG) + +#undef DO_FLAG + + return ir_flags; +} + +static es2panda_ScriptFunctionFlags IrToE2pScriptFunctionFlags(ir::ScriptFunctionFlags ir_flags) +{ + es2panda_ScriptFunctionFlags e2p_flags {ES2PANDA_SCRIPT_FUNCTION_NONE}; + +#define DO_FLAG(FL) \ + if ((ir_flags & ir::ScriptFunctionFlags::FL) != 0) { \ + e2p_flags = (es2panda_ScriptFunctionFlags)(e2p_flags | ES2PANDA_SCRIPT_FUNCTION_##FL); \ + } + + FOR_ALL_SCRIPT_FUNCTION_FLAGS(DO_FLAG) + +#undef DO_FLAG + + return e2p_flags; +} + extern "C" es2panda_Config *CreateConfig(int args, char const **argv) { constexpr auto COMPILER_SIZE = 256_MB; @@ -53,7 +232,7 @@ extern "C" es2panda_Config *CreateConfig(int args, char const **argv) mem::MemConfig::Initialize(0, 0, COMPILER_SIZE, 0, 0, 0); PoolManager::Initialize(PoolType::MMAP); - auto options = std::make_unique(); + auto *options = new util::Options(); if (!options->Parse(args, argv)) { // TODO(gogabr): report option errors properly. std::cerr << options->ErrorMsg() << std::endl; @@ -64,7 +243,7 @@ extern "C" es2panda_Config *CreateConfig(int args, char const **argv) Logger::InitializeStdLogging(Logger::LevelFromString(options->LogLevel()), mask); auto *res = new ConfigImpl; - res->options = std::move(options); + res->options = options; return reinterpret_cast(res); } @@ -73,7 +252,10 @@ extern "C" void DestroyConfig(es2panda_Config *config) PoolManager::Finalize(); mem::MemConfig::Finalize(); - delete reinterpret_cast(config); + auto *cfg = reinterpret_cast(config); + + delete cfg->options; + delete cfg; } static void CompileJob(compiler::CompilerContext *context, binder::FunctionScope *scope, @@ -97,26 +279,26 @@ static es2panda_Context *CreateContext(es2panda_Config *config, std::string cons res->source_file_name = file_name; try { - res->source_file = std::make_unique(res->source_file_name, res->input, cfg->options->ParseModule()); - res->allocator = std::make_unique(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); - res->queue = std::make_unique(cfg->options->ThreadCount()); + res->source_file = new SourceFile(res->source_file_name, res->input, cfg->options->ParseModule()); + res->allocator = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + res->queue = new compiler::CompileQueue(cfg->options->ThreadCount()); - auto *binder = res->allocator->New(res->allocator.get()); - res->parser_program = std::make_unique(res->allocator.get(), binder); + auto *binder = res->allocator->New(res->allocator); + res->parser_program = new parser::Program(res->allocator, binder); res->parser_program->MarkEntry(); - res->parser = std::make_unique(res->parser_program.get(), cfg->options->CompilerOptions(), - parser::ParserStatus::NO_OPTS); - res->checker = std::make_unique(); - res->analyzer = std::make_unique(res->checker.get()); - res->checker->SetAnalyzer(res->analyzer.get()); - - binder->SetProgram(res->parser_program.get()); - - res->compiler_context = std::make_unique( - binder, res->checker.get(), cfg->options->CompilerOptions(), CompileJob); - binder->SetCompilerContext(res->compiler_context.get()); - res->emitter = std::make_unique(res->compiler_context.get()); - res->compiler_context->SetEmitter(res->emitter.get()); + res->parser = + new parser::ETSParser(res->parser_program, cfg->options->CompilerOptions(), parser::ParserStatus::NO_OPTS); + res->checker = new checker::ETSChecker(); + res->analyzer = new checker::ETSAnalyzer(res->checker); + res->checker->SetAnalyzer(res->analyzer); + + binder->SetProgram(res->parser_program); + + res->compiler_context = + new compiler::CompilerContext(binder, res->checker, cfg->options->CompilerOptions(), CompileJob); + binder->SetCompilerContext(res->compiler_context); + res->emitter = new compiler::ETSEmitter(res->compiler_context); + res->compiler_context->SetEmitter(res->emitter); res->program = nullptr; res->state = ES2PANDA_STATE_NEW; } catch (Error &e) { @@ -136,7 +318,7 @@ extern "C" es2panda_Context *CreateContextFromFile(es2panda_Config *config, char auto *res = new Context; res->error_message = "Failed to open file: "; res->error_message.append(source_file_name); - return reinterpret_cast(res); + return reinterpret_cast(res); } std::stringstream ss; ss << input_stream.rdbuf(); @@ -144,7 +326,7 @@ extern "C" es2panda_Context *CreateContextFromFile(es2panda_Config *config, char auto *res = new Context; res->error_message = "Failed to read file: "; res->error_message.append(source_file_name); - return reinterpret_cast(res); + return reinterpret_cast(res); } return CreateContext(config, ss.str(), source_file_name); } @@ -215,7 +397,7 @@ static Context *Lower(Context *ctx) try { for (auto *phase : compiler::GetETSPhaseList()) { - phase->Apply(ctx->compiler_context.get(), ctx->compiler_context->Binder()->Program()); + phase->Apply(ctx, ctx->compiler_context->Binder()->Program()); } ctx->state = ES2PANDA_STATE_LOWERED; @@ -254,13 +436,12 @@ static Context *GenerateAsm(Context *ctx) emitter->LiteralBufferIndex() += ctx->compiler_context->ContextLiterals().size(); /* Main thread can also be used instead of idling */ - ctx->queue->Schedule(ctx->compiler_context.get()); + ctx->queue->Schedule(ctx->compiler_context); ctx->queue->Consume(); ctx->queue->Wait( [emitter](compiler::CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); }); ASSERT(ctx->program == nullptr); - ctx->program = std::unique_ptr { - emitter->Finalize(ctx->compiler_context->DumpDebugInfo(), compiler::Signatures::ETS_GLOBAL)}; + ctx->program = emitter->Finalize(ctx->compiler_context->DumpDebugInfo(), compiler::Signatures::ETS_GLOBAL); ctx->state = ES2PANDA_STATE_ASM_GENERATED; } catch (Error &e) { @@ -286,7 +467,7 @@ Context *GenerateBin(Context *ctx) try { ASSERT(ctx->program != nullptr); - util::GenerateProgram(ctx->program.get(), ctx->config->options.get(), + util::GenerateProgram(ctx->program, ctx->config->options, [ctx](const std::string &str) { ctx->error_message = str; }); ctx->state = ES2PANDA_STATE_BIN_GENERATED; @@ -330,8 +511,18 @@ extern "C" es2panda_Context *ProceedToState(es2panda_Context *context, es2panda_ extern "C" void DestroyContext(es2panda_Context *context) { - auto *s = reinterpret_cast(context); - delete s; + auto *ctx = reinterpret_cast(context); + delete ctx->program; + delete ctx->emitter; + delete ctx->compiler_context; + delete ctx->analyzer; + delete ctx->checker; + delete ctx->parser; + delete ctx->parser_program; + delete ctx->queue; + delete ctx->allocator; + delete ctx->source_file; + delete ctx; } extern "C" es2panda_ContextState ContextState(es2panda_Context *context) @@ -346,13 +537,944 @@ extern "C" char const *ContextErrorMessage(es2panda_Context *context) return s->error_message.c_str(); } -es2panda_Impl IMPL = {ES2PANDA_LIB_VERSION, +extern "C" es2panda_Program *ContextProgram(es2panda_Context *context) +{ + auto *ctx = reinterpret_cast(context); + return reinterpret_cast(ctx->compiler_context->Binder()->Program()); +} - CreateConfig, DestroyConfig, +extern "C" es2panda_AstNode *ProgramAst(es2panda_Program *program) +{ + auto *pgm = reinterpret_cast(program); + return reinterpret_cast(pgm->Ast()); +} + +using ExternalSourceEntry = std::pair *>; + +extern "C" es2panda_ExternalSource **ProgramExternalSources(es2panda_Program *program, size_t *len_p) +{ + auto *pgm = reinterpret_cast(program); + auto *allocator = pgm->Binder()->Allocator(); + auto *vec = allocator->New>(allocator->Adapter()); + + for (auto &[e_name, e_programs] : pgm->ExternalSources()) { + vec->push_back(allocator->New(StringViewToCString(allocator, e_name), &e_programs)); + } - CreateContextFromFile, CreateContextFromString, ProceedToState, DestroyContext, + *len_p = vec->size(); + return reinterpret_cast(vec->data()); +} + +extern "C" char const *ExternalSourceName(es2panda_ExternalSource *e_source) +{ + auto *entry = reinterpret_cast(e_source); + return entry->first; +} - ContextState, ContextErrorMessage}; +extern "C" es2panda_Program **ExternalSourcePrograms(es2panda_ExternalSource *e_source, size_t *len_p) +{ + auto *entry = reinterpret_cast(e_source); + *len_p = entry->second->size(); + return reinterpret_cast(entry->second->data()); +} + +extern "C" es2panda_Type *AstNodeType(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast); + // Need to work with other TypeAstNodes + if (node->IsExpression()) { + return reinterpret_cast(node->AsExpression()->TsType()); + } + return nullptr; +} + +extern "C" es2panda_AstNode *const *AstNodeDecorators(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast); + if (node->CanHaveDecorator(false)) { + auto *decorators = node->DecoratorsPtr(); + *size_p = decorators->size(); + return reinterpret_cast(decorators->data()); + } + *size_p = 0; + return nullptr; +} + +extern "C" es2panda_ModifierFlags AstNodeModifierFlags(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast); + return IrToE2pModifierFlags(node->Modifiers()); +} + +extern "C" void AstNodeSetDecorators(es2panda_Context *context, es2panda_AstNode *ast, es2panda_AstNode **decorators, + size_t n_decorators) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *node = reinterpret_cast(ast); + + ArenaVector decorators_vector {allocator->Adapter()}; + for (size_t i = 0; i < n_decorators; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + decorators_vector.push_back(reinterpret_cast(decorators[i])->AsDecorator()); + } + node->AddDecorators(std::move(decorators_vector)); +} + +extern "C" void AstNodeSetType(es2panda_AstNode *ast, es2panda_Type *type) +{ + auto *node = reinterpret_cast(ast); + auto *tp = reinterpret_cast(type); + // Need to work with other TypedAstNodes + if (node->IsExpression()) { + node->AsExpression()->SetTsType(tp); + } else { + UNREACHABLE(); + } +} + +extern "C" void AstNodeForEach(es2panda_AstNode *ast, void (*func)(es2panda_AstNode *, void *), void *arg) +{ + auto *node = reinterpret_cast(ast); + func(ast, arg); + node->IterateRecursively([=](ir::AstNode *child) { func(reinterpret_cast(child), arg); }); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define IS(public_name, e2p_name) \ + extern "C" bool Is##public_name(es2panda_AstNode *ast) \ + { \ + auto *node = reinterpret_cast(ast); \ + return node->Is##e2p_name(); \ + } + +IS(ArrowFunctionExpression, ArrowFunctionExpression) +IS(AsExpression, TSAsExpression) +IS(AssignmentExpression, AssignmentExpression) +IS(BinaryExpression, BinaryExpression) +IS(BlockStatement, BlockStatement) +IS(CallExpression, CallExpression) +IS(ChainExpression, ChainExpression) +IS(ClassDeclaration, ClassDeclaration) +IS(ClassDefinition, ClassDefinition) +IS(ClassImplementsClause, TSClassImplements) +IS(ClassProperty, ClassProperty) +IS(ExpressionStatement, ExpressionStatement) +IS(FunctionDeclaration, FunctionDeclaration) +IS(FunctionTypeNode, TSFunctionType) +IS(Identifier, Identifier) +IS(IfStatement, IfStatement) +IS(ImportDeclaration, ImportDeclaration) +IS(ImportExpression, ImportExpression) +IS(ImportSpecifier, ImportSpecifier) +IS(MemberExpression, MemberExpression) +IS(MethodDefinition, MethodDefinition) +IS(NewExpression, NewExpression) +IS(NonNullExpression, TSNonNullExpression) +IS(NumberLiteral, NumberLiteral) +IS(ObjectExpression, ObjectExpression) +IS(ParameterDeclaration, ETSParameterExpression) +IS(PrimitiveTypeNode, ETSPrimitiveType) +IS(ReturnStatement, ReturnStatement) +IS(ScriptFunction, ScriptFunction) +IS(StringLiteral, StringLiteral) +IS(ThisExpression, ThisExpression) +IS(TypeParameterDeclaration, TSTypeParameterDeclaration) +IS(TypeParameterInstantiation, TSTypeParameterInstantiation) +IS(TypeReferenceNode, ETSTypeReference) +IS(TypeReferencePart, ETSTypeReferencePart) +IS(UnionTypeNode, TSUnionType) +IS(VariableDeclaration, VariableDeclaration) +IS(VariableDeclarator, VariableDeclarator) + +#undef IS + +extern "C" es2panda_AstNode *CreateArrowFunctionExpression(es2panda_Context *context, es2panda_AstNode *script_function) +{ + auto *ctx = reinterpret_cast(context); + auto *func = reinterpret_cast(script_function)->AsScriptFunction(); + auto *allocator = ctx->allocator; + + return reinterpret_cast(allocator->New(allocator, func)); +} + +extern "C" es2panda_AstNode *ArrowFunctionExpressionScriptFunction(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsArrowFunctionExpression(); + return reinterpret_cast(node->Function()); +} + +extern "C" es2panda_AstNode *CreateAsExpression(es2panda_Context *context, es2panda_AstNode *expr, + es2panda_AstNode *type_annotation, bool is_const) +{ + auto *ctx = reinterpret_cast(context); + auto *left_expr = reinterpret_cast(expr)->AsExpression(); + auto *tp = reinterpret_cast(type_annotation)->AsExpression()->AsTypeNode(); + auto *allocator = ctx->allocator; + + return reinterpret_cast(allocator->New(left_expr, tp, is_const)); +} + +extern "C" es2panda_AstNode *AsExpressionExpr(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsTSAsExpression(); + return reinterpret_cast(node->Expr()); +} + +extern "C" es2panda_AstNode *AsExpressionTypeAnnotation(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsTSAsExpression(); + return reinterpret_cast(node->Type()); +} + +extern "C" bool AsExpressionIsConst(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsTSAsExpression(); + return node->IsConst(); +} + +extern "C" void AsExpressionSetExpr(es2panda_AstNode *ast, es2panda_AstNode *expr) +{ + auto *node = reinterpret_cast(ast)->AsTSAsExpression(); + auto *new_expr = reinterpret_cast(expr)->AsExpression(); + node->SetExpr(new_expr); +} + +extern "C" void AsExpressionSetTypeAnnotation(es2panda_AstNode *ast, es2panda_AstNode *type_annotation) +{ + auto *node = reinterpret_cast(ast)->AsTSAsExpression(); + auto *tp = reinterpret_cast(type_annotation)->AsExpression()->AsTypeNode(); + node->SetTsTypeAnnotation(tp); +} + +static constexpr std::array ASSIGNMENT_TOKEN_TYPES { + {{lexer::TokenType::PUNCTUATOR_SUBSTITUTION, "="}, + {lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL, ">>>="}, + {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL, ">>="}, + {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL, "<<="}, + {lexer::TokenType::PUNCTUATOR_PLUS_EQUAL, "+="}, + {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, "-="}, + {lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL, "*="}, + {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, "/="}, + {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, "%="}, + {lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL, "&="}, + {lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL, "|="}, + {lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL, "^="}, + {lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL, "&&="}, + {lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL, "||="}, + {lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL, "\?\?="}, + {lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL, "**="}, + {lexer::TokenType::EOS, nullptr}}}; + +extern "C" es2panda_AstNode *CreateAssignmentExpression(es2panda_Context *context, es2panda_AstNode *left, + es2panda_AstNode *right, char const *operator_type) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *left_node = reinterpret_cast(left)->AsExpression(); + auto *right_node = reinterpret_cast(right)->AsExpression(); + lexer::TokenType tok = StrToToken(ASSIGNMENT_TOKEN_TYPES.data(), operator_type); + return reinterpret_cast(allocator->New(left_node, right_node, tok)); +} + +extern "C" es2panda_AstNode *AssignmentExpressionLeft(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsAssignmentExpression(); + return reinterpret_cast(node->Left()); +} + +extern "C" es2panda_AstNode *AssignmentExpressionRight(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsAssignmentExpression(); + return reinterpret_cast(node->Right()); +} + +extern "C" char const *AssignmentExpressionOperatorType(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsAssignmentExpression(); + return TokenToStr(ASSIGNMENT_TOKEN_TYPES.data(), node->OperatorType()); +} + +extern "C" void AssignmentExpressionSetOperatorType(es2panda_AstNode *ast, char const *operator_type) +{ + auto *node = reinterpret_cast(ast)->AsAssignmentExpression(); + auto tok = StrToToken(ASSIGNMENT_TOKEN_TYPES.data(), operator_type); + node->SetOperatorType(tok); +} + +static constexpr std::array BINARY_OP_TOKEN_TYPES { + {{lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT, ">>>"}, + {lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT, ">>"}, + {lexer::TokenType::PUNCTUATOR_LEFT_SHIFT, "<<"}, + {lexer::TokenType::PUNCTUATOR_PLUS, "+"}, + {lexer::TokenType::PUNCTUATOR_MINUS, "-"}, + {lexer::TokenType::PUNCTUATOR_MULTIPLY, "*"}, + {lexer::TokenType::PUNCTUATOR_DIVIDE, "/"}, + {lexer::TokenType::PUNCTUATOR_MOD, "%"}, + {lexer::TokenType::PUNCTUATOR_BITWISE_AND, "&"}, + {lexer::TokenType::PUNCTUATOR_BITWISE_OR, "|"}, + {lexer::TokenType::PUNCTUATOR_BITWISE_XOR, "^"}, + {lexer::TokenType::PUNCTUATOR_LOGICAL_AND, "&&"}, + {lexer::TokenType::PUNCTUATOR_LOGICAL_OR, "||"}, + {lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING, "??"}, + {lexer::TokenType::PUNCTUATOR_EXPONENTIATION, "**"}, + {lexer::TokenType::PUNCTUATOR_EQUAL, "=="}, + {lexer::TokenType::PUNCTUATOR_NOT_EQUAL, "/="}, + {lexer::TokenType::PUNCTUATOR_STRICT_EQUAL, "==="}, + {lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, "/=="}, + {lexer::TokenType::PUNCTUATOR_LESS_THAN, "<"}, + {lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL, "<="}, + {lexer::TokenType::PUNCTUATOR_GREATER_THAN, ">"}, + {lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL, ">="}, + {lexer::TokenType::KEYW_IN, "in"}, + {lexer::TokenType::KEYW_INSTANCEOF, "instanceof"}, + {lexer::TokenType::EOS, nullptr}}}; + +extern "C" es2panda_AstNode *CreateBinaryExpression(es2panda_Context *context, es2panda_AstNode *left, + es2panda_AstNode *right, char const *operator_type) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *left_expr = reinterpret_cast(left)->AsExpression(); + auto *right_expr = reinterpret_cast(right)->AsExpression(); + auto tok = StrToToken(BINARY_OP_TOKEN_TYPES.data(), operator_type); + + return reinterpret_cast(allocator->New(left_expr, right_expr, tok)); +} + +extern "C" es2panda_AstNode *BinaryExpressionLeft(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsBinaryExpression(); + return reinterpret_cast(node->Left()); +} + +extern "C" es2panda_AstNode *BinaryExpressionRight(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsBinaryExpression(); + return reinterpret_cast(node->Right()); +} + +extern "C" char const *BinaryExpressionOperator(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsBinaryExpression(); + return TokenToStr(BINARY_OP_TOKEN_TYPES.data(), node->OperatorType()); +} + +extern "C" void BinaryExpressionSetOperator(es2panda_AstNode *ast, char const *operator_type) +{ + auto *node = reinterpret_cast(ast)->AsBinaryExpression(); + auto op = StrToToken(BINARY_OP_TOKEN_TYPES.data(), operator_type); + node->SetOperator(op); +} + +extern "C" es2panda_AstNode *CreateBlockStatement(es2panda_Context *context, es2panda_AstNode *in_scope_of) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *parent = reinterpret_cast(in_scope_of); + auto *parent_scope = compiler::NearestScope(parent); + + auto *scope = allocator->New(allocator, parent_scope); + ArenaVector stmts {allocator->Adapter()}; + return reinterpret_cast(allocator->New(allocator, scope, std::move(stmts))); +} + +extern "C" es2panda_AstNode **BlockStatementStatements(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast)->AsBlockStatement(); + *size_p = node->Statements().size(); + return reinterpret_cast(node->Statements().data()); +} + +extern "C" void BlockStatementAddStatement(es2panda_AstNode *ast, es2panda_AstNode *statement) +{ + auto *node = reinterpret_cast(ast)->AsBlockStatement(); + auto *stmt = reinterpret_cast(statement)->AsBlockStatement(); + node->Statements().push_back(stmt); +} + +extern "C" es2panda_AstNode *CreateCallExpression(es2panda_Context *context, es2panda_AstNode *callee, + es2panda_AstNode *type_arguments, es2panda_AstNode **arguments, + size_t n_arguments, bool is_optional) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *callee_node = reinterpret_cast(callee)->AsExpression(); + + ir::TSTypeParameterInstantiation *type_args = nullptr; + if (type_arguments != nullptr) { + type_args = reinterpret_cast(type_arguments)->AsTSTypeParameterInstantiation(); + } + + ArenaVector args {allocator->Adapter()}; + for (size_t i = 0; i < n_arguments; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + args.push_back(reinterpret_cast(arguments[i])->AsExpression()); + } + return reinterpret_cast( + allocator->New(callee_node, std::move(args), type_args, is_optional)); +} + +extern "C" es2panda_AstNode const *CallExpressionCallee(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsCallExpression(); + return reinterpret_cast(node->Callee()); +} + +extern "C" es2panda_AstNode const *CallExpressionTypeArguments(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsCallExpression(); + return reinterpret_cast(node->TypeParams()); +} + +extern "C" es2panda_AstNode **CallExpressionArguments(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast)->AsCallExpression(); + *size_p = node->Arguments().size(); + return reinterpret_cast(node->Arguments().data()); +} + +extern "C" bool CallExpressionIsOptional(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsCallExpression(); + return node->IsOptional(); +} + +extern "C" void CallExpressionSetTypeArguments(es2panda_AstNode *ast, es2panda_AstNode *type_arguments) +{ + auto *node = reinterpret_cast(ast)->AsCallExpression(); + auto *type_args = reinterpret_cast(type_arguments)->AsTSTypeParameterInstantiation(); + node->SetTypeParams(type_args); +} + +extern "C" es2panda_AstNode *CreateChainExpression(es2panda_Context *context, es2panda_AstNode *child) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *child_expr = reinterpret_cast(child)->AsExpression(); + + return reinterpret_cast(allocator->New(child_expr)); +} + +extern "C" es2panda_AstNode const *ChainExpressionChild(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsChainExpression(); + return reinterpret_cast(node->GetExpression()); +} + +extern "C" es2panda_AstNode *CreateClassDeclaration(es2panda_Context *context, es2panda_AstNode *definition) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *dfn = reinterpret_cast(definition)->AsClassDefinition(); + + return reinterpret_cast(allocator->New(dfn, allocator)); +} + +extern "C" es2panda_AstNode *ClassDeclarationDefinition(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassDeclaration(); + return reinterpret_cast(node->Definition()); +} + +extern "C" es2panda_AstNode *CreateClassDefinition(es2panda_Context *context, es2panda_AstNode *in_scope_of, + es2panda_AstNode *identifier, es2panda_ModifierFlags flags) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *parent = reinterpret_cast(in_scope_of); + auto *parent_scope = compiler::NearestScope(parent); + auto *id = reinterpret_cast(identifier)->AsIdentifier(); + + auto *scope = allocator->New(allocator, parent_scope); + return reinterpret_cast( + allocator->New(allocator, scope, id, ir::ClassDefinitionModifiers::NONE, + E2pToIrModifierFlags(flags), Language::FromString("ets").value())); +} + +extern "C" es2panda_AstNode *ClassDefinitionIdentifier(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + return reinterpret_cast(node->Ident()); +} + +extern "C" es2panda_AstNode *ClassDefinitionTypeParameters(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + return reinterpret_cast(node->TypeParams()); +} + +extern "C" es2panda_AstNode *ClassDefinitionSuperClass(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + return reinterpret_cast(node->Super()); +} + +extern "C" es2panda_AstNode **ClassDefinitionImplements(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto &impl_vec = node->Implements(); + *size_p = impl_vec.size(); + return reinterpret_cast(impl_vec.data()); +} + +extern "C" es2panda_AstNode *ClassDefinitionConstructor(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + return reinterpret_cast(node->Ctor()); +} + +extern "C" es2panda_AstNode **ClassDefinitionBody(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto &body_vec = node->Body(); + *size_p = body_vec.size(); + return reinterpret_cast(body_vec.data()); +} + +extern "C" void ClassDefinitionSetIdentifier(es2panda_AstNode *ast, es2panda_AstNode *identifier) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto *id = reinterpret_cast(identifier)->AsIdentifier(); + node->SetIdent(id); +} + +extern "C" void ClassDefinitionSetTypeParameters(es2panda_AstNode *ast, es2panda_AstNode *type_params) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto *tpd = reinterpret_cast(type_params)->AsTSTypeParameterDeclaration(); + node->SetTypeParams(tpd); +} + +extern "C" void ClassDefinitionSetSuperClass(es2panda_AstNode *ast, es2panda_AstNode *super_class) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto *super = reinterpret_cast(super_class)->AsExpression(); + node->SetSuper(super); +} + +extern "C" void ClassDefinitionSetImplements(es2panda_AstNode *ast, es2panda_AstNode **implements, size_t n_implements) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto &impl_vec = node->Implements(); + impl_vec.resize(0); + for (size_t i = 0; i < n_implements; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + impl_vec.push_back(reinterpret_cast(implements[i])->AsTSClassImplements()); + } +} + +extern "C" void ClassDefinitionAddImplements(es2panda_AstNode *ast, es2panda_AstNode *implements) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto &impl_vec = node->Implements(); + impl_vec.push_back(reinterpret_cast(implements)->AsTSClassImplements()); +} + +extern "C" void ClassDefinitionSetConstructor(es2panda_AstNode *ast, es2panda_AstNode *constructor) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto *ctor = reinterpret_cast(constructor)->AsMethodDefinition(); + node->SetCtor(ctor); +} + +extern "C" void ClassDefinitionSetBody(es2panda_AstNode *ast, es2panda_AstNode **body, size_t n_elems) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto &body_vec = node->Body(); + body_vec.resize(0); + for (size_t i = 0; i < n_elems; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + body_vec.push_back(reinterpret_cast(body[i])); + } +} + +extern "C" void ClassDefinitionAddToBody(es2panda_AstNode *ast, es2panda_AstNode *body_elem) +{ + auto *node = reinterpret_cast(ast)->AsClassDefinition(); + auto *elem = reinterpret_cast(body_elem); + auto &body_vec = node->Body(); + body_vec.push_back(reinterpret_cast(elem)); +} + +extern "C" es2panda_AstNode *ClassElementKey(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassElement(); + return reinterpret_cast(node->Key()); +} + +extern "C" es2panda_AstNode *ClassElementValue(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassElement(); + return reinterpret_cast(node->Value()); +} + +extern "C" es2panda_AstNode *CreateClassImplementsClause(es2panda_Context *context, es2panda_AstNode *expression, + es2panda_AstNode *type_arguments) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *expr = reinterpret_cast(expression)->AsExpression(); + auto *targs = type_arguments == nullptr + ? nullptr + : reinterpret_cast(type_arguments)->AsTSTypeParameterInstantiation(); + + return reinterpret_cast(allocator->New(expr, targs)); +} + +extern "C" es2panda_AstNode *ClassImplementsClauseExpression(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsTSClassImplements(); + return reinterpret_cast(node->Expr()); +} + +extern "C" es2panda_AstNode const *ClassImplementsClauseTypeArguments(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsTSClassImplements(); + return reinterpret_cast(node->TypeParameters()); +} + +extern "C" es2panda_AstNode *CreateClassProperty(es2panda_Context *context, es2panda_AstNode *key, + es2panda_AstNode *value, es2panda_AstNode *type_annotation, + es2panda_ModifierFlags modifier_flags, bool is_computed) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *ekey = reinterpret_cast(key)->AsExpression(); + auto *evalue = value == nullptr ? nullptr : reinterpret_cast(value)->AsExpression(); + auto *tp_ann = type_annotation == nullptr + ? nullptr + : reinterpret_cast(type_annotation)->AsExpression()->AsTypeNode(); + auto modifiers = E2pToIrModifierFlags(modifier_flags); + + return reinterpret_cast( + allocator->New(ekey, evalue, tp_ann, modifiers, allocator, is_computed)); +} + +extern "C" es2panda_AstNode *ClassPropertyTypeAnnotation(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsClassProperty(); + return reinterpret_cast(node->TypeAnnotation()); +} + +extern "C" es2panda_AstNode *CreateExpressionStatement(es2panda_Context *context, es2panda_AstNode *expression) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *expr = reinterpret_cast(expression)->AsExpression(); + + return reinterpret_cast(allocator->New(expr)); +} + +extern "C" es2panda_AstNode *ExpressionStatementExpression(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsExpressionStatement(); + return reinterpret_cast(node->GetExpression()); +} + +extern "C" es2panda_AstNode *CreateFunctionDeclaration(es2panda_Context *context, es2panda_AstNode *function) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *func = reinterpret_cast(function)->AsScriptFunction(); + + return reinterpret_cast(allocator->New(allocator, func)); +} + +extern "C" es2panda_AstNode *FunctionDeclarationFunction(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsFunctionDeclaration(); + return reinterpret_cast(node->Function()); +} + +extern "C" es2panda_AstNode *CreateFunctionTypeNode(es2panda_Context *context, es2panda_AstNode *in_scope_of, + es2panda_AstNode *type_params, es2panda_AstNode **params, + size_t n_params, es2panda_AstNode *return_type, + es2panda_ScriptFunctionFlags func_flags) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *parent = reinterpret_cast(in_scope_of); + auto *parent_scope = compiler::NearestScope(parent); + auto *tpar = + type_params == nullptr ? nullptr : reinterpret_cast(type_params)->AsTSTypeParameterDeclaration(); + auto *tret = + return_type == nullptr ? nullptr : reinterpret_cast(return_type)->AsExpression()->AsTypeNode(); + auto flags = E2pToIrScriptFunctionFlags(func_flags); + + auto *scope = allocator->New(allocator, parent_scope); + + ArenaVector par {allocator->Adapter()}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for (size_t i = 0; i < n_params; i++) { + par.push_back(reinterpret_cast(params[i])->AsExpression()); + } + + return reinterpret_cast( + allocator->New(scope, std::move(par), tpar, tret, flags)); +} + +extern "C" es2panda_AstNode const *FunctionTypeNodeTypeParams(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsETSFunctionType(); + return reinterpret_cast(node->TypeParams()); +} + +extern "C" es2panda_AstNode *const *FunctionTypeNodeParams(es2panda_AstNode *ast, size_t *size_p) +{ + auto *node = reinterpret_cast(ast)->AsETSFunctionType(); + auto ¶ms = node->Params(); + *size_p = params.size(); + return reinterpret_cast(params.data()); +} + +extern "C" es2panda_AstNode *FunctionTypeNodeReturnType(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsETSFunctionType(); + return reinterpret_cast(node->ReturnType()); +} + +extern "C" es2panda_ScriptFunctionFlags FunctionTypeNodeFlags(es2panda_AstNode *ast) +{ + auto *node = reinterpret_cast(ast)->AsETSFunctionType(); + return IrToE2pScriptFunctionFlags(node->Flags()); +} + +extern "C" es2panda_AstNode *CreateIdentifier(es2panda_Context *context, char const *name, + es2panda_AstNode *type_annotations) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *name_copy = ArenaStrdup(ctx->allocator, name); + auto *tp_ann = type_annotations == nullptr + ? nullptr + : reinterpret_cast(type_annotations)->AsExpression()->AsTypeNode(); + + auto *res = allocator->New(util::StringView {name_copy}, tp_ann, allocator); + + return reinterpret_cast(res); +} + +extern "C" char const *IdentifierName(es2panda_Context *context, es2panda_AstNode *identifier) +{ + auto *ctx = reinterpret_cast(context); + auto *id = reinterpret_cast(identifier); + ASSERT(id->IsIdentifier()); + + return StringViewToCString(ctx->allocator, id->AsIdentifier()->Name()); +} + +extern "C" es2panda_AstNode *IdentifierTypeAnnotation(es2panda_AstNode *identifier) +{ + auto *id = reinterpret_cast(identifier)->AsIdentifier(); + return reinterpret_cast(id->TypeAnnotation()); +} + +extern "C" es2panda_Variable *IdentifierVariable(es2panda_AstNode *identifier) +{ + auto *id = reinterpret_cast(identifier)->AsIdentifier(); + + return reinterpret_cast(id->Variable()); +} + +extern "C" void IdentifierSetVariable(es2panda_AstNode *identifier, es2panda_Variable *variable) +{ + auto *id = reinterpret_cast(identifier)->AsIdentifier(); + auto *var = reinterpret_cast(variable); + + id->SetVariable(var); +} + +extern "C" es2panda_AstNode *CreateIfStatement(es2panda_Context *context, es2panda_AstNode *test, + es2panda_AstNode *consequent, es2panda_AstNode *alternate) +{ + auto *ctx = reinterpret_cast(context); + auto *allocator = ctx->allocator; + auto *t = reinterpret_cast(test)->AsExpression(); + auto *conseq = reinterpret_cast(consequent)->AsStatement(); + auto *alt = reinterpret_cast(alternate)->AsStatement(); + + return reinterpret_cast(allocator->New(t, conseq, alt)); +} + +extern "C" es2panda_AstNode const *IfStatementTest(es2panda_AstNode *identifier) +{ + auto *if_stat = reinterpret_cast(identifier)->AsIfStatement(); + + return reinterpret_cast(if_stat->Test()); +} + +extern "C" es2panda_AstNode const *IfStatementConsequent(es2panda_AstNode *identifier) +{ + auto *if_stat = reinterpret_cast(identifier)->AsIfStatement(); + + return reinterpret_cast(if_stat->Consequent()); +} + +extern "C" es2panda_AstNode const *IfStatementAlternate(es2panda_AstNode *identifier) +{ + auto *if_stat = reinterpret_cast(identifier)->AsIfStatement(); + + return reinterpret_cast(if_stat->Alternate()); +} + +es2panda_Impl IMPL = { + ES2PANDA_LIB_VERSION, + + CreateConfig, + DestroyConfig, + + CreateContextFromFile, + CreateContextFromString, + ProceedToState, + DestroyContext, + + ContextState, + ContextErrorMessage, + + ContextProgram, + ProgramAst, + ProgramExternalSources, + ExternalSourceName, + ExternalSourcePrograms, + + AstNodeType, + AstNodeSetType, + + AstNodeDecorators, + AstNodeSetDecorators, + + AstNodeModifierFlags, + + AstNodeForEach, + + IsArrowFunctionExpression, + CreateArrowFunctionExpression, + ArrowFunctionExpressionScriptFunction, + + IsAsExpression, + CreateAsExpression, + AsExpressionExpr, + AsExpressionTypeAnnotation, + AsExpressionIsConst, + AsExpressionSetExpr, + AsExpressionSetTypeAnnotation, + + IsAssignmentExpression, + CreateAssignmentExpression, + AssignmentExpressionLeft, + AssignmentExpressionRight, + AssignmentExpressionOperatorType, + AssignmentExpressionSetOperatorType, + + IsBinaryExpression, + CreateBinaryExpression, + BinaryExpressionLeft, + BinaryExpressionRight, + BinaryExpressionOperator, + BinaryExpressionSetOperator, + + IsBlockStatement, + CreateBlockStatement, + BlockStatementStatements, + BlockStatementAddStatement, + + IsCallExpression, + CreateCallExpression, + CallExpressionCallee, + CallExpressionTypeArguments, + CallExpressionArguments, + CallExpressionIsOptional, + CallExpressionSetTypeArguments, + + IsChainExpression, + CreateChainExpression, + ChainExpressionChild, + + IsClassDeclaration, + CreateClassDeclaration, + ClassDeclarationDefinition, + + IsClassDefinition, + CreateClassDefinition, + ClassDefinitionIdentifier, + ClassDefinitionTypeParameters, + ClassDefinitionSuperClass, + ClassDefinitionImplements, + ClassDefinitionConstructor, + ClassDefinitionBody, + ClassDefinitionSetIdentifier, + ClassDefinitionSetTypeParameters, + ClassDefinitionSetSuperClass, + ClassDefinitionSetImplements, + ClassDefinitionAddImplements, + ClassDefinitionSetConstructor, + ClassDefinitionSetBody, + ClassDefinitionAddToBody, + + ClassElementKey, + ClassElementValue, + + IsClassImplementsClause, + CreateClassImplementsClause, + ClassImplementsClauseExpression, + ClassImplementsClauseTypeArguments, + + IsClassProperty, + CreateClassProperty, + ClassPropertyTypeAnnotation, + + IsExpressionStatement, + CreateExpressionStatement, + ExpressionStatementExpression, + + IsFunctionDeclaration, + CreateFunctionDeclaration, + FunctionDeclarationFunction, + + IsFunctionTypeNode, + CreateFunctionTypeNode, + FunctionTypeNodeTypeParams, + FunctionTypeNodeParams, + FunctionTypeNodeReturnType, + FunctionTypeNodeFlags, + + IsIdentifier, + CreateIdentifier, + IdentifierName, + IdentifierTypeAnnotation, + IdentifierVariable, + IdentifierSetVariable, + + IsIfStatement, + CreateIfStatement, + IfStatementTest, + IfStatementConsequent, + IfStatementAlternate, + + IsImportDeclaration, + IsImportExpression, + IsImportSpecifier, + IsMemberExpression, + IsMethodDefinition, + IsNewExpression, + IsNonNullExpression, + IsNumberLiteral, + IsObjectExpression, + IsParameterDeclaration, + IsPrimitiveTypeNode, + IsReturnStatement, + IsScriptFunction, + IsStringLiteral, + IsThisExpression, + IsTypeParameterDeclaration, + IsTypeParameterInstantiation, + IsTypeReferenceNode, + IsTypeReferencePart, + IsUnionTypeNode, + IsVariableDeclaration, + IsVariableDeclarator, +}; } // namespace panda::es2panda::public_lib diff --git a/public/es2panda_lib.h b/public/es2panda_lib.h index f7402d2203552009cadf80da15425517e70cd9e9..78770fc97e13284345b5974a02f42475682e4d5e 100644 --- a/public/es2panda_lib.h +++ b/public/es2panda_lib.h @@ -1,17 +1,42 @@ +/** + * Copyright (c) 2021-2022 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_LIB #define ES2PANDA_LIB +// Switch off the linter for C header +// NOLINTBEGIN + +#include +#include + #ifdef __cplusplus extern "C" { #endif -// Switch off the linter for C header -// NOLINTBEGIN - #define ES2PANDA_LIB_VERSION 0 -typedef void es2panda_Config; -typedef void es2panda_Context; +typedef struct es2panda_Config es2panda_Config; +typedef struct es2panda_Context es2panda_Context; + +typedef struct es2panda_Program es2panda_Program; +typedef struct es2panda_ExternalSource es2panda_ExternalSource; +typedef struct es2panda_AstNode es2panda_AstNode; +typedef struct es2panda_Type es2panda_Type; +typedef struct es2panda_Variable es2panda_Variable; +typedef struct es2panda_Scope es2panda_Scope; enum es2panda_ContextState { ES2PANDA_STATE_NEW, @@ -25,6 +50,63 @@ enum es2panda_ContextState { }; typedef enum es2panda_ContextState es2panda_ContextState; +// NB: has to be synchronized with astNode.h +enum es2panda_ModifierFlags { + ES2PANDA_MODIFIER_NONE = 0U, + ES2PANDA_MODIFIER_STATIC = 1U << 0U, + ES2PANDA_MODIFIER_ASYNC = 1U << 1U, + ES2PANDA_MODIFIER_PUBLIC = 1U << 2U, + ES2PANDA_MODIFIER_PROTECTED = 1U << 3U, + ES2PANDA_MODIFIER_PRIVATE = 1U << 4U, + ES2PANDA_MODIFIER_DECLARE = 1U << 5U, + ES2PANDA_MODIFIER_READONLY = 1U << 6U, + ES2PANDA_MODIFIER_OPTIONAL = 1U << 7U, + ES2PANDA_MODIFIER_DEFINITE = 1U << 8U, + ES2PANDA_MODIFIER_ABSTRACT = 1U << 9U, + ES2PANDA_MODIFIER_CONST = 1U << 10U, + ES2PANDA_MODIFIER_FINAL = 1U << 11U, + ES2PANDA_MODIFIER_NATIVE = 1U << 12U, + ES2PANDA_MODIFIER_OVERRIDE = 1U << 13U, + ES2PANDA_MODIFIER_CONSTRUCTOR = 1U << 14U, + ES2PANDA_MODIFIER_SYNCHRONIZED = 1U << 15U, + ES2PANDA_MODIFIER_FUNCTIONAL = 1U << 16U, + ES2PANDA_MODIFIER_IN = 1U << 17U, + ES2PANDA_MODIFIER_OUT = 1U << 18U, + ES2PANDA_MODIFIER_INTERNAL = 1U << 19U, + ES2PANDA_MODIFIER_NULLABLE = 1U << 20U, + ES2PANDA_MODIFIER_EXPORT = 1U << 21U, + ES2PANDA_MODIFIER_SETTER = 1U << 22U, + ES2PANDA_MODIFIER_DEFAULT_EXPORT = 1U << 23U, +}; +typedef enum es2panda_ModifierFlags es2panda_ModifierFlags; + +// Has to be synchronized with astNode.h +enum es2panda_ScriptFunctionFlags { + ES2PANDA_SCRIPT_FUNCTION_NONE = 0, + ES2PANDA_SCRIPT_FUNCTION_GENERATOR = 1U << 0U, + ES2PANDA_SCRIPT_FUNCTION_ASYNC = 1U << 1U, + ES2PANDA_SCRIPT_FUNCTION_ARROW = 1U << 2U, + ES2PANDA_SCRIPT_FUNCTION_EXPRESSION = 1U << 3U, + ES2PANDA_SCRIPT_FUNCTION_OVERLOAD = 1U << 4U, + ES2PANDA_SCRIPT_FUNCTION_CONSTRUCTOR = 1U << 5U, + ES2PANDA_SCRIPT_FUNCTION_METHOD = 1U << 6U, + ES2PANDA_SCRIPT_FUNCTION_STATIC_BLOCK = 1U << 7U, + ES2PANDA_SCRIPT_FUNCTION_HIDDEN = 1U << 8U, + ES2PANDA_SCRIPT_FUNCTION_IMPLICIT_SUPER_CALL_NEEDED = 1U << 9U, + ES2PANDA_SCRIPT_FUNCTION_ENUM = 1U << 10U, + ES2PANDA_SCRIPT_FUNCTION_EXTERNAL = 1U << 11U, + ES2PANDA_SCRIPT_FUNCTION_PROXY = 1U << 12U, + ES2PANDA_SCRIPT_FUNCTION_THROWS = 1U << 13U, + ES2PANDA_SCRIPT_FUNCTION_RETHROWS = 1U << 14U, + ES2PANDA_SCRIPT_FUNCTION_GETTER = 1U << 15U, + ES2PANDA_SCRIPT_FUNCTION_SETTER = 1U << 16U, + ES2PANDA_SCRIPT_FUNCTION_DEFAULT_PARAM_PROXY = 1U << 17U, + ES2PANDA_SCRIPT_FUNCTION_ENTRY_POINT = 1U << 18U, + ES2PANDA_SCRIPT_FUNCTION_INSTANCE_EXTENSION_METHOD = 1U << 19U, + ES2PANDA_SCRIPT_FUNCTION_HAS_RETURN = 1U << 20U +}; +typedef enum es2panda_ScriptFunctionFlags es2panda_ScriptFunctionFlags; + struct es2panda_Impl { int version; @@ -38,6 +120,164 @@ struct es2panda_Impl { es2panda_ContextState (*ContextState)(es2panda_Context *context); char const *(*ContextErrorMessage)(es2panda_Context *context); + + es2panda_Program *(*ContextProgram)(es2panda_Context *context); + es2panda_AstNode *(*ProgramAst)(es2panda_Program *program); + es2panda_ExternalSource **(*ProgramExternalSources)(es2panda_Program *program, size_t *len_p); + char const *(*ExternalSourceName)(es2panda_ExternalSource *e_source); + es2panda_Program **(*ExternalSourcePrograms)(es2panda_ExternalSource *e_source, size_t *len_p); + + es2panda_Type *(*AstNodeType)(es2panda_AstNode *ast); + void (*AstNodeSetType)(es2panda_AstNode *ast, es2panda_Type *type); + + es2panda_AstNode *const *(*AstNodeDecorators)(es2panda_AstNode *ast, size_t *size_p); + void (*AstNodeSetDecorators)(es2panda_Context *context, es2panda_AstNode *ast, es2panda_AstNode **decorators, + size_t n_decorators); + + es2panda_ModifierFlags (*AstNodeModifierFlags)(es2panda_AstNode *ast); + + void (*AstNodeForEach)(es2panda_AstNode *ast, void (*func)(es2panda_AstNode *, void *), void *arg); + + bool (*IsArrowFunctionExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateArrowFunctionExpression)(es2panda_Context *context, es2panda_AstNode *script_function); + es2panda_AstNode *(*ArrowFunctionExpressionScriptFunction)(es2panda_AstNode *ast); + + bool (*IsAsExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateAsExpression)(es2panda_Context *context, es2panda_AstNode *expr, + es2panda_AstNode *type_annotation, bool is_const); + es2panda_AstNode *(*AsExpressionExpr)(es2panda_AstNode *ast); + es2panda_AstNode *(*AsExpressionTypeAnnotation)(es2panda_AstNode *ast); + bool (*AsExpressionIsConst)(es2panda_AstNode *ast); + void (*AsExpressionSetExpr)(es2panda_AstNode *ast, es2panda_AstNode *expr); + void (*AsExpressionSetTypeAnnotation)(es2panda_AstNode *ast, es2panda_AstNode *type_annotation); + + bool (*IsAssignmentExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateAssignmentExpression)(es2panda_Context *context, es2panda_AstNode *left, + es2panda_AstNode *right, char const *operator_type); + es2panda_AstNode *(*AssignmentExpressionLeft)(es2panda_AstNode *ast); + es2panda_AstNode *(*AssignmentExpressionRight)(es2panda_AstNode *ast); + char const *(*AssignmentExpressionOperatorType)(es2panda_AstNode *ast); + void (*AssignmentExpressionSetOperatorType)(es2panda_AstNode *ast, char const *operator_type); + + bool (*IsBinaryExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreatebinaryExpression)(es2panda_Context *context, es2panda_AstNode *left, + es2panda_AstNode *right, char const *operator_type); + es2panda_AstNode *(*BinaryExpressionLeft)(es2panda_AstNode *ast); + es2panda_AstNode *(*BinaryExpressionRight)(es2panda_AstNode *ast); + char const *(*BinaryExpressionOperator)(es2panda_AstNode *ast); + void (*BinaryExpressionSetOperator)(es2panda_AstNode *ast, char const *operator_type); + + bool (*IsBlockStatement)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateBlockStatement)(es2panda_Context *context, es2panda_AstNode *in_scope_of); + es2panda_AstNode **(*BlockStatementStatements)(es2panda_AstNode *ast, size_t *size_p); + void (*BlockStatementAddStatement)(es2panda_AstNode *ast, es2panda_AstNode *statement); + + bool (*IsCallExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateCallExpression)(es2panda_Context *context, es2panda_AstNode *callee, + /* maybe_null */ es2panda_AstNode *type_arguments, + es2panda_AstNode **arguments, size_t n_arguments, bool optional); + es2panda_AstNode const *(*CallExpressionCallee)(es2panda_AstNode *ast); + /*maybe_null*/ es2panda_AstNode const *(*CallExpressionTypeArguments)(es2panda_AstNode *ast); + es2panda_AstNode **(*CallExpressionArguments)(es2panda_AstNode *ast, size_t *size_p); + bool (*CallExpressionIsOptional)(es2panda_AstNode *ast); + void (*CallExpressionSetTypeArguments)(es2panda_AstNode *ast, es2panda_AstNode *type_arguments); + + bool (*IsChainExpression)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateChainExpression)(es2panda_Context *context, es2panda_AstNode *child); + es2panda_AstNode const *(*ChainExpressionChild)(es2panda_AstNode *ast); + + bool (*IsClassDeclaration)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateClassDeclaration)(es2panda_Context *context, es2panda_AstNode *definition); + es2panda_AstNode *(*ClassDeclarationDefinition)(es2panda_AstNode *ast); + + bool (*IsClassDefinition)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateClassDefinition)(es2panda_Context *context, es2panda_AstNode *in_scope_of, + es2panda_AstNode *identifier, es2panda_ModifierFlags flags); + es2panda_AstNode *(*ClassDefinitionIdentifier)(es2panda_AstNode *ast); + es2panda_AstNode *(*ClassDefinitionTypeParameters)(es2panda_AstNode *ast); + es2panda_AstNode *(*ClassDefinitionSuperClass)(es2panda_AstNode *ast); + es2panda_AstNode **(*ClassDefinitionImplements)(es2panda_AstNode *ast, size_t *size_p); + es2panda_AstNode *(*ClassDefinitionConstructor)(es2panda_AstNode *ast); + es2panda_AstNode **(*ClassDefinitionBody)(es2panda_AstNode *ast, size_t *size_p); + void (*ClassDefinitionSetIdentifier)(es2panda_AstNode *ast, es2panda_AstNode *identifier); + void (*ClassDefinitionSetTypeParameters)(es2panda_AstNode *ast, es2panda_AstNode *type_params); + void (*ClassDefinitionSetSuperClass)(es2panda_AstNode *ast, es2panda_AstNode *super_class); + void (*ClassDefinitionSetImplements)(es2panda_AstNode *ast, es2panda_AstNode **implements, size_t n_implements); + void (*ClassDefinitionAddImplements)(es2panda_AstNode *ast, es2panda_AstNode *implements); + void (*ClassDefinitionSetConstructor)(es2panda_AstNode *ast, es2panda_AstNode *constructor); + void (*ClassDefinitonSetBody)(es2panda_AstNode *ast, es2panda_AstNode **body, size_t n_elems); + void (*ClassDefinitonAddToBody)(es2panda_AstNode *ast, es2panda_AstNode *statement); + + es2panda_AstNode *(*ClassElementKey)(es2panda_AstNode *ast); + es2panda_AstNode *(*ClassElementValue)(es2panda_AstNode *ast); + + bool (*IsClassImplementsClause)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateClassImplementsClause)(es2panda_Context *context, es2panda_AstNode *expression, + es2panda_AstNode *type_arguments); + es2panda_AstNode *(*ClassImplementsClauseExpression)(es2panda_AstNode *ast); + es2panda_AstNode const *(*ClassImplementsClauseTypeArguments)(es2panda_AstNode *ast); + + bool (*IsClassProperty)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateClassProperty)(es2panda_Context *context, es2panda_AstNode *key, es2panda_AstNode *value, + es2panda_AstNode *type_annotation, es2panda_ModifierFlags modifier_flags, + bool is_computed); + es2panda_AstNode *(*ClassPropertyTypeAnnotation)(es2panda_AstNode *ast); + + bool (*IsExpressionStatement)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateExpressionStatement)(es2panda_Context *context, es2panda_AstNode *expression); + es2panda_AstNode *(*ExpressionStatementExpression)(es2panda_AstNode *ast); + + bool (*IsFunctionDeclaration)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateFunctionDeclaration)(es2panda_Context *context, es2panda_AstNode *function); + es2panda_AstNode *(*FunctionDeclarationFunction)(es2panda_AstNode *ast); + + bool (*IsFunctionTypeNode)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateFunctionTypeNode)(es2panda_Context *context, es2panda_AstNode *in_scope_of, + es2panda_AstNode *type_params, es2panda_AstNode **params, + size_t n_params, es2panda_AstNode *return_type, + es2panda_ScriptFunctionFlags func_flags); + es2panda_AstNode const *(*FunctionTypeNodeTypeParams)(es2panda_AstNode *ast); + es2panda_AstNode *const *(*FunctionTypeNodeParams)(es2panda_AstNode *ast, size_t *size_p); + es2panda_AstNode *(*FunctionTypeNodeReturnType)(es2panda_AstNode *ast); + es2panda_ScriptFunctionFlags (*FunctionTypeNodeFlags)(es2panda_AstNode *ast); + + bool (*IsIdentifier)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateIdentifier)(es2panda_Context *context, char const *name, + es2panda_AstNode *type_annotation); + char const *(*IdentifierName)(es2panda_Context *context, es2panda_AstNode *identifier); + es2panda_AstNode *(*IdentifierTypeAnnotation)(es2panda_AstNode *identifier); + es2panda_Variable *(*IdentifierVariable)(es2panda_AstNode *identifier); + void (*IdentifierSetVariable)(es2panda_AstNode *identifier, es2panda_Variable *variable); + + bool (*IsIfStatement)(es2panda_AstNode *ast); + es2panda_AstNode *(*CreateIfStatement)(es2panda_Context *context, es2panda_AstNode *test, + es2panda_AstNode *consequent, es2panda_AstNode *alternate); + es2panda_AstNode const *(*IfStatementTest)(es2panda_AstNode *ast); + es2panda_AstNode const *(*IfStatementConsequent)(es2panda_AstNode *ast); + es2panda_AstNode const *(*IfStatementAlternate)(es2panda_AstNode *ast); + + bool (*IsImportDeclaration)(es2panda_AstNode *ast); + bool (*IsImportExpression)(es2panda_AstNode *ast); + bool (*IsImportSpecifier)(es2panda_AstNode *ast); + bool (*IsMemberExpression)(es2panda_AstNode *ast); + bool (*IsMethodDefinition)(es2panda_AstNode *ast); + bool (*IsNewExpression)(es2panda_AstNode *ast); + bool (*IsNonNullExpression)(es2panda_AstNode *ast); + bool (*IsNumberLiteral)(es2panda_AstNode *ast); + bool (*IsObjectExpression)(es2panda_AstNode *ast); + bool (*IsParameterDeclaration)(es2panda_AstNode *ast); + bool (*IsPrimitiveTypeNode)(es2panda_AstNode *ast); + bool (*IsReturnStatement)(es2panda_AstNode *ast); + bool (*IsScriptFunction)(es2panda_AstNode *ast); + bool (*IsStringLiteral)(es2panda_AstNode *ast); + bool (*IsThisExpression)(es2panda_AstNode *ast); + bool (*IsTypeParameterDeclaration)(es2panda_AstNode *ast); + bool (*IsTypeParameterInstantiation)(es2panda_AstNode *ast); + bool (*IsTypeReferenceNode)(es2panda_AstNode *ast); + bool (*IsTypeReferencePart)(es2panda_AstNode *ast); + bool (*IsUnionTypeNode)(es2panda_AstNode *ast); + bool (*IsVariableDeclaration)(es2panda_AstNode *ast); + bool (*IsVariableDeclarator)(es2panda_AstNode *ast); }; struct es2panda_Impl const *es2panda_GetImpl(int version); diff --git a/public/public.h b/public/public.h new file mode 100644 index 0000000000000000000000000000000000000000..d7b30a39a9178b6231f8275a4aec5d0ad87cb44b --- /dev/null +++ b/public/public.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2021-2022 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_PUBLIC_PUBLIC_H +#define ES2PANDA_PUBLIC_PUBLIC_H + +#include "public/es2panda_lib.h" + +#include "assembler/assembly-program.h" +#include "libpandabase/mem/arena_allocator.h" + +#include "es2panda.h" +#include "compiler/core/compileQueue.h" +#include "parser/ETSparser.h" +#include "checker/checker.h" +#include "compiler/core/emitter.h" +#include "util/options.h" + +namespace panda::es2panda::public_lib { +struct ConfigImpl { + util::Options *options; +}; + +struct Context { + ConfigImpl *config = nullptr; + std::string source_file_name; + std::string input; + SourceFile const *source_file = nullptr; + ArenaAllocator *allocator = nullptr; + compiler::CompileQueue *queue = nullptr; + std::vector const *plugins = nullptr; + + parser::Program *parser_program = nullptr; + parser::ParserImpl *parser = nullptr; + checker::Checker *checker = nullptr; + checker::SemanticAnalyzer *analyzer = nullptr; + compiler::CompilerContext *compiler_context = nullptr; + compiler::Emitter *emitter = nullptr; + pandasm::Program *program = nullptr; + + es2panda_ContextState state = ES2PANDA_STATE_NEW; + std::string error_message; +}; +} // namespace panda::es2panda::public_lib + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f40aed24be528f99fc880954a9bb04380f30fce6..1a2ded857db03be8efa0d14d66f794e76f920b83 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -85,7 +85,21 @@ if(PANDA_WITH_ETS) ${CMAKE_CURRENT_SOURCE_DIR}/.. SANITIZERS ${PANDA_SANITIZERS_LIST} + ) + + panda_add_library(e2p_test_plugin SHARED public/e2p_test_plugin.c) + panda_target_include_directories(e2p_test_plugin PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") + panda_target_link_libraries(e2p_test_plugin es2panda-public) + + add_custom_target(es2panda-plugin-test + COMMENT "Test es2panda plugin functionality" + COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${CMAKE_CURRENT_LIBRARY_DIR} $ --plugins=e2p_test_plugin + "${CMAKE_CURRENT_SOURCE_DIR}/public/t.ets" > "${CMAKE_CURRENT_BINARY_DIR}/plugin_test.out" + COMMAND ${CMAKE_COMMAND} -E compare_files + "${CMAKE_CURRENT_BINARY_DIR}/plugin_test.out" "${CMAKE_CURRENT_SOURCE_DIR}/public/plugin_test.expected" ) + add_dependencies(es2panda-plugin-test es2panda e2p_test_plugin) + add_dependencies(es2panda_tests es2panda-plugin-test) add_dependencies(es2panda_tests es2panda-regression-tests) if(TARGET ets_tests) diff --git a/test/public/e2p_test_plugin.c b/test/public/e2p_test_plugin.c new file mode 100644 index 0000000000000000000000000000000000000000..2de3ca54a7544505776c8c793151fc32847cd4b5 --- /dev/null +++ b/test/public/e2p_test_plugin.c @@ -0,0 +1,57 @@ +/** + * 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. + */ + +// No linting for pure C file +// NOLINTBEGIN + +#include +#include + +#include "public/es2panda_lib.h" + +static struct es2panda_Impl const *impl = NULL; + +void e2p_test_plugin_Initialize() +{ + puts("Hi there!"); + impl = es2panda_GetImpl(ES2PANDA_LIB_VERSION); +} + +static void PrintIfIdentifier(es2panda_AstNode *node, void *arg) +{ + es2panda_Context *ctx = arg; + if (impl->IsIdentifier(node)) { + puts(impl->IdentifierName(ctx, node)); + } +} + +void e2p_test_plugin_AfterParse(es2panda_Context *ctx) +{ + puts("After parse"); + es2panda_AstNode *ast = impl->ProgramAst(impl->ContextProgram(ctx)); + impl->AstNodeForEach(ast, PrintIfIdentifier, ctx); +} + +void e2p_test_plugin_AfterCheck(es2panda_Context *ctx) +{ + puts("After check"); +} + +void e2p_test_plugin_AfterLowerings(es2panda_Context *ctx) +{ + puts("After lowerings"); +} + +// NOLINTEND diff --git a/test/public/es2panda_public_test.cpp b/test/public/es2panda_public_test.cpp index ac368c3b7c5503f6eaf14dd6cd46208e4b3d31ce..d391325694b61b781659fb948c2d84d3a540fdcf 100644 --- a/test/public/es2panda_public_test.cpp +++ b/test/public/es2panda_public_test.cpp @@ -53,10 +53,52 @@ TEST_F(Es2PandaLibTest, NoError) TEST_F(Es2PandaLibTest, TypeError) { es2panda_Context *ctx = - impl_->CreateContextFromString(cfg_, "function main() { let x: int = \"\" }", "no-error.ets"); - impl_->ProceedToState(ctx, ES2PANDA_STATE_ASM_GENERATED); // don't produce any object files + impl_->CreateContextFromString(cfg_, "function main() { let x: int = \"\" }", "type-error.ets"); + impl_->ProceedToState(ctx, ES2PANDA_STATE_ASM_GENERATED); ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_ERROR); ASSERT_EQ(std::string(impl_->ContextErrorMessage(ctx)), - "TypeError: Initializers type is not assignable to the target type[no-error.ets:1,32]"); + "TypeError: Initializers type is not assignable to the target type[type-error.ets:1,32]"); + impl_->DestroyContext(ctx); +} + +TEST_F(Es2PandaLibTest, ListIdentifiers) +{ + char const *text = R"XXX( +class C { + n: string = "oh" +} + +function main() { + let c = new C + console.log(c.n + 1) // type error, but not syntax error +} +)XXX"; + es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "list-ids.ets"); + ctx = impl_->ProceedToState(ctx, ES2PANDA_STATE_PARSED); + ASSERT_EQ(impl_->ContextState(ctx), ES2PANDA_STATE_PARSED); + + struct Arg { + es2panda_Impl const *impl = nullptr; + es2panda_Context *ctx = nullptr; + std::vector ids; + } arg; + arg.impl = impl_; + arg.ctx = ctx; + + auto func = [](es2panda_AstNode *ast, void *argp) { + auto *a = reinterpret_cast(argp); + if (a->impl->IsIdentifier(ast)) { + a->ids.emplace_back(a->impl->IdentifierName(a->ctx, ast)); + } + }; + + impl_->AstNodeForEach(impl_->ProgramAst(impl_->ContextProgram(ctx)), func, &arg); + + // TODO(gogabr): the list is too fragile + std::vector expected {"C", "n", "string", "constructor", "constructor", "ETSGLOBAL", + "_$init$_", "_$init$_", "main", "main", "c", "C", + "console", "log", "c", "n", ""}; + ASSERT_EQ(arg.ids, expected); + impl_->DestroyContext(ctx); } diff --git a/test/public/plugin_test.expected b/test/public/plugin_test.expected new file mode 100644 index 0000000000000000000000000000000000000000..f578824e8435dd77777c35b1233f0e02b4ea338d --- /dev/null +++ b/test/public/plugin_test.expected @@ -0,0 +1,16 @@ +Hi there! +After parse +ETSGLOBAL +_$init$_ +_$init$_ +main +main +m +n +console +log +m +n + +After check +After lowerings diff --git a/test/public/t.ets b/test/public/t.ets new file mode 100644 index 0000000000000000000000000000000000000000..5057e93a2733420a90ad548cbeebd37bae0f14c3 --- /dev/null +++ b/test/public/t.ets @@ -0,0 +1,5 @@ +function main() { + let m: int = 1 + let n: int = 2 + console.log(m + n) +} diff --git a/util/options.cpp b/util/options.cpp index a5c73f877dac0102aeb9d5b33c3da6c6c148cda9..0c1cfb4692534865dced8108e54c52570176bba5 100644 --- a/util/options.cpp +++ b/util/options.cpp @@ -36,21 +36,31 @@ Options::~Options() delete argparser_; } -static std::unordered_set StringToStringSet(const std::string &str) +static std::vector StringToStringVector(std::string const &str) { - std::unordered_set res; + std::vector res; std::string_view curr_str {str}; auto ix = curr_str.find(','); while (ix != std::string::npos) { if (ix != 0) { - res.insert(curr_str.substr(0, ix)); + res.emplace_back(curr_str.substr(0, ix)); } curr_str = curr_str.substr(ix + 1); ix = curr_str.find(','); } if (!curr_str.empty()) { - res.insert(curr_str); + res.emplace_back(curr_str); + } + return res; +} + +static std::unordered_set StringToStringSet(std::string const &str) +{ + std::vector vec = StringToStringVector(str); + std::unordered_set res; + for (auto &elem : vec) { + res.emplace(elem); } return res; } @@ -85,6 +95,7 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg log_level("log-level", "error", "Log-level"); panda::PandArg std_lib("stdlib", "", "Path to standard library"); panda::PandArg gen_std_lib("gen-stdlib", false, "Gen standard library"); + panda::PandArg plugins("plugins", "", "Plugins"); panda::PandArg skip_phases("skip-phases", "", "Phases to skip"); panda::PandArg dump_before_phases("dump-before-phases", "", "Generate program dump before running phases in the list"); @@ -115,6 +126,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&log_level); argparser_->Add(&std_lib); argparser_->Add(&gen_std_lib); + argparser_->Add(&plugins); argparser_->Add(&skip_phases); argparser_->Add(&dump_before_phases); argparser_->Add(&dump_after_phases); @@ -297,6 +309,7 @@ bool Options::Parse(int argc, const char **argv) compiler_options_.std_lib = std_lib.GetValue(); compiler_options_.compilation_mode = compilation_mode; compiler_options_.is_ets_module = op_ets_module.GetValue(); + compiler_options_.plugins = StringToStringVector(plugins.GetValue()); compiler_options_.skip_phases = StringToStringSet(skip_phases.GetValue()); compiler_options_.dump_before_phases = StringToStringSet(dump_before_phases.GetValue()); compiler_options_.dump_after_phases = StringToStringSet(dump_after_phases.GetValue()); diff --git a/util/plugin.cpp b/util/plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..74c1e273fdf397e8568e098809249f668265318d --- /dev/null +++ b/util/plugin.cpp @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021-2022 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 "plugin.h" +#include "os/library_loader.h" + +namespace panda::es2panda::util { + +std::string Plugin::FullNameForProcedure(std::string const &short_name) +{ + return std::string(name_.Utf8()) + "_" + short_name; +} + +Plugin::Plugin(util::StringView const &name) : name_ {name}, err_ {0}, h_ {nullptr} +{ + std::string so_name = + os::library_loader::DYNAMIC_LIBRARY_PREFIX + std::string(name) + os::library_loader::DYNAMIC_LIBRARY_SUFFIX; + if (auto load_res = os::library_loader::Load(so_name); load_res.HasValue()) { + h_ = std::move(load_res.Value()); + } else { + err_ = load_res.Error(); + ok_ = false; + } + + if (auto init_res = os::library_loader::ResolveSymbol(h_, FullNameForProcedure("Initialize")); + init_res.HasValue()) { + initialize_ = reinterpret_cast(init_res.Value()); + } + + if (auto ap_res = os::library_loader::ResolveSymbol(h_, FullNameForProcedure("AfterParse")); ap_res.HasValue()) { + after_parse_ = reinterpret_cast(ap_res.Value()); + } + + if (auto ac_res = os::library_loader::ResolveSymbol(h_, FullNameForProcedure("AfterCheck")); ac_res.HasValue()) { + after_check_ = reinterpret_cast(ac_res.Value()); + } + + if (auto al_res = os::library_loader::ResolveSymbol(h_, FullNameForProcedure("AfterLowerings")); + al_res.HasValue()) { + after_lowerings_ = reinterpret_cast(al_res.Value()); + } +} + +} // namespace panda::es2panda::util diff --git a/util/plugin.h b/util/plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..74fd3c88563c537aa91323be61beff536075fc04 --- /dev/null +++ b/util/plugin.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2021-2022 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_UTIL_PLUGINS_H +#define ES2PANDA_UTIL_PLUGINS_H + +#include "macros.h" +#include "os/library_loader.h" +#include "public/es2panda_lib.h" +#include "util/ustring.h" + +namespace panda::es2panda::util { + +class Plugin { +public: + explicit Plugin(util::StringView const &name); + ~Plugin() = default; + + NO_COPY_SEMANTIC(Plugin); + DEFAULT_MOVE_SEMANTIC(Plugin); + + bool IsOk() + { + return ok_; + } + + os::Error Error() + { + return err_; + } + + void Initialize() const + { + if (initialize_ != nullptr) { + initialize_(); + } + } + + void AfterParse(es2panda_Context *context) const + { + if (after_parse_ != nullptr) { + after_parse_(context); + } + } + + void AfterCheck(es2panda_Context *context) const + { + if (after_check_ != nullptr) { + after_check_(context); + } + } + + void AfterLowerings(es2panda_Context *context) const + { + if (after_lowerings_ != nullptr) { + after_lowerings_(context); + } + } + +private: + std::string FullNameForProcedure(std::string const &short_name); + + util::StringView name_; + bool ok_ {true}; + os::Error err_; + os::library_loader::LibraryHandle h_; + + void (*initialize_)() = nullptr; + void (*after_parse_)(es2panda_Context *) = nullptr; + void (*after_check_)(es2panda_Context *) = nullptr; + void (*after_lowerings_)(es2panda_Context *) = nullptr; +}; + +} // namespace panda::es2panda::util + +#endif