From 02227b12c09f28c0f79ed0122a941edade4449a1 Mon Sep 17 00:00:00 2001 From: bialger Date: Tue, 6 Jan 2026 06:19:00 +0300 Subject: [PATCH 01/22] feat: add support for Ovum JIT X64 architecture in CMake configuration --- cmake/IncludeExternalLibraries.cmake | 15 +++++++++++++++ cmake/SetCompilerOptions.cmake | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/cmake/IncludeExternalLibraries.cmake b/cmake/IncludeExternalLibraries.cmake index ae76e63..d227d9a 100644 --- a/cmake/IncludeExternalLibraries.cmake +++ b/cmake/IncludeExternalLibraries.cmake @@ -31,3 +31,18 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(argparser) + + +# Ovum JIT X64 (only for x86_64 architecture) +if(OVUM_JIT_X64_AVAILABLE) + FetchContent_Declare( + ovumjitx64 + GIT_REPOSITORY https://github.com/Ovum-Programming-Language/OvumJitX64.git + GIT_TAG main + ) + + FetchContent_MakeAvailable(ovumjitx64) + message(STATUS "OvumJitX64: Enabled for x86_64 architecture") +else() + message(STATUS "OvumJitX64: Skipped (not x86_64 architecture)") +endif() diff --git a/cmake/SetCompilerOptions.cmake b/cmake/SetCompilerOptions.cmake index 0557d92..c71ed75 100644 --- a/cmake/SetCompilerOptions.cmake +++ b/cmake/SetCompilerOptions.cmake @@ -17,6 +17,16 @@ else() set(CMAKE_CXX_FLAGS_RELEASE "-O3") endif() +# Detect target architecture for JIT support +# x86_64 is reported as "AMD64" on Windows, "x86_64" on Linux/Mac +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|x86_64)$") + set(OVUM_JIT_X64_AVAILABLE ON CACHE BOOL "JIT compiler available for x86_64 architecture") + message(STATUS "Architecture: ${CMAKE_SYSTEM_PROCESSOR} - JIT support available") +else() + set(OVUM_JIT_X64_AVAILABLE OFF CACHE BOOL "JIT compiler available for x86_64 architecture") + message(STATUS "Architecture: ${CMAKE_SYSTEM_PROCESSOR} - JIT support not available") +endif() + message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Compiler version: ${CMAKE_CXX_COMPILER_VERSION}") From b2bb021d524f538ab88d896015dfabd66ee57851 Mon Sep 17 00:00:00 2001 From: bialger Date: Tue, 6 Jan 2026 20:24:46 +0300 Subject: [PATCH 02/22] feat: enable JIT support in vm_ui CMake configuration and link jit library if provided --- cmake/SetCompilerOptions.cmake | 2 ++ lib/vm_ui/CMakeLists.txt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/cmake/SetCompilerOptions.cmake b/cmake/SetCompilerOptions.cmake index c71ed75..8201caa 100644 --- a/cmake/SetCompilerOptions.cmake +++ b/cmake/SetCompilerOptions.cmake @@ -21,6 +21,8 @@ endif() # x86_64 is reported as "AMD64" on Windows, "x86_64" on Linux/Mac if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|x86_64)$") set(OVUM_JIT_X64_AVAILABLE ON CACHE BOOL "JIT compiler available for x86_64 architecture") + set(JIT_PROVIDED ON CACHE BOOL "JIT compiler provided") + add_compile_definitions(JIT_PROVIDED) message(STATUS "Architecture: ${CMAKE_SYSTEM_PROCESSOR} - JIT support available") else() set(OVUM_JIT_X64_AVAILABLE OFF CACHE BOOL "JIT compiler available for x86_64 architecture") diff --git a/lib/vm_ui/CMakeLists.txt b/lib/vm_ui/CMakeLists.txt index 82a8ad1..67306dc 100644 --- a/lib/vm_ui/CMakeLists.txt +++ b/lib/vm_ui/CMakeLists.txt @@ -12,4 +12,8 @@ target_link_libraries(vm_ui PUBLIC argparser ) +if (JIT_PROVIDED) + target_link_libraries(vm_ui PUBLIC jit) +endif () + target_include_directories(vm_ui PUBLIC ${PROJECT_SOURCE_DIR}) From 71c18cfa63e6691f437b5177bfa89604992c549c Mon Sep 17 00:00:00 2001 From: bialger Date: Tue, 6 Jan 2026 22:51:57 +0300 Subject: [PATCH 03/22] fix: update help message in StartVmConsoleUI to include description of the Ovum Virtual Machine and JIT availability --- lib/vm_ui/vm_ui_functions.cpp | 11 ++++++++--- tests/main_test.cpp | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/vm_ui/vm_ui_functions.cpp b/lib/vm_ui/vm_ui_functions.cpp index 1a8584b..5ced773 100644 --- a/lib/vm_ui/vm_ui_functions.cpp +++ b/lib/vm_ui/vm_ui_functions.cpp @@ -55,19 +55,24 @@ int32_t StartVmConsoleUI(const std::vector& args, std::ostream& out } auto is_file = [](std::string& arg) { return std::filesystem::exists(arg); }; + std::string description = "Ovum Virtual Machine that executes Ovum Intermediate Language."; +#ifdef JIT_PROVIDED + description += " JIT compiler is available."; +#endif + ArgumentParser::ArgParser arg_parser("ovum-vm", PassArgumentTypes()); arg_parser.AddCompositeArgument('f', "file", "Path to the bytecode file").AddIsGood(is_file).AddValidate(is_file); arg_parser.AddUnsignedLongLongArgument('j', "jit-boundary", "JIT compilation boundary").Default(kDefaultJitBoundary); - arg_parser.AddHelp('h', "help", "Show this help message"); + arg_parser.AddHelp('h', "help", description); bool parse_result = arg_parser.Parse(parser_args, {.out_stream = err, .print_messages = true}); if (!parse_result) { - err << arg_parser.HelpDescription() << "\n"; + err << arg_parser.HelpDescription(); return 1; } if (arg_parser.Help()) { - err << arg_parser.HelpDescription() << "\n"; + out << arg_parser.HelpDescription(); return 0; } diff --git a/tests/main_test.cpp b/tests/main_test.cpp index 9ce2568..e1ca882 100644 --- a/tests/main_test.cpp +++ b/tests/main_test.cpp @@ -22,10 +22,14 @@ TEST_F(ProjectIntegrationTestSuite, NegitiveOutputTest1) { StartVmConsoleUI(SplitString("test"), out, in, err); ASSERT_EQ(err.str(), "Not enough values were passed to argument --file.\n" - "ovum-vm\nShow this help message\n\nOPTIONS:\n" + "ovum-vm\nOvum Virtual Machine that executes Ovum Intermediate Language." +#ifdef JIT_PROVIDED + " JIT compiler is available." +#endif + "\n\nOPTIONS:\n" "-f, --file=: Path to the bytecode file\n" "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n\n" - "-h, --help: Display this help and exit\n\n"); + "-h, --help: Display this help and exit\n"); } TEST_F(ProjectIntegrationTestSuite, HelpTest) { @@ -33,11 +37,15 @@ TEST_F(ProjectIntegrationTestSuite, HelpTest) { std::istringstream in; std::ostringstream err; StartVmConsoleUI(SplitString("test --help"), out, in, err); - ASSERT_EQ(err.str(), - "ovum-vm\nShow this help message\n\nOPTIONS:\n" + ASSERT_EQ(out.str(), + "ovum-vm\nOvum Virtual Machine that executes Ovum Intermediate Language." +#ifdef JIT_PROVIDED + " JIT compiler is available." +#endif + "\n\nOPTIONS:\n" "-f, --file=: Path to the bytecode file\n" "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n\n" - "-h, --help: Display this help and exit\n\n"); + "-h, --help: Display this help and exit\n"); } TEST_F(ProjectIntegrationTestSuite, FibTest1) { From 538e0d24107b612ceba2ff259455f70d77efccda Mon Sep 17 00:00:00 2001 From: bialger Date: Tue, 6 Jan 2026 23:16:12 +0300 Subject: [PATCH 04/22] refactor: extract help message into a constant for improved readability and maintainability in main_test.cpp --- tests/main_test.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/tests/main_test.cpp b/tests/main_test.cpp index e1ca882..a73b45e 100644 --- a/tests/main_test.cpp +++ b/tests/main_test.cpp @@ -15,21 +15,22 @@ TEST_F(ProjectIntegrationTestSuite, NegativeTest1) { ASSERT_EQ(StartVmConsoleUI(SplitString("test"), out, in, err), 1); } +const std::string kHelpMessage = + "ovum-vm\nOvum Virtual Machine that executes Ovum Intermediate Language." +#ifdef JIT_PROVIDED + " JIT compiler is available." +#endif + "\n\nOPTIONS:\n" + "-f, --file=: Path to the bytecode file\n" + "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n\n" + "-h, --help: Display this help and exit\n"; + TEST_F(ProjectIntegrationTestSuite, NegitiveOutputTest1) { std::ostringstream out; std::istringstream in; std::ostringstream err; StartVmConsoleUI(SplitString("test"), out, in, err); - ASSERT_EQ(err.str(), - "Not enough values were passed to argument --file.\n" - "ovum-vm\nOvum Virtual Machine that executes Ovum Intermediate Language." -#ifdef JIT_PROVIDED - " JIT compiler is available." -#endif - "\n\nOPTIONS:\n" - "-f, --file=: Path to the bytecode file\n" - "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n\n" - "-h, --help: Display this help and exit\n"); + ASSERT_EQ(err.str(), "Not enough values were passed to argument --file.\n" + kHelpMessage); } TEST_F(ProjectIntegrationTestSuite, HelpTest) { @@ -37,15 +38,7 @@ TEST_F(ProjectIntegrationTestSuite, HelpTest) { std::istringstream in; std::ostringstream err; StartVmConsoleUI(SplitString("test --help"), out, in, err); - ASSERT_EQ(out.str(), - "ovum-vm\nOvum Virtual Machine that executes Ovum Intermediate Language." -#ifdef JIT_PROVIDED - " JIT compiler is available." -#endif - "\n\nOPTIONS:\n" - "-f, --file=: Path to the bytecode file\n" - "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n\n" - "-h, --help: Display this help and exit\n"); + ASSERT_EQ(out.str(), kHelpMessage); } TEST_F(ProjectIntegrationTestSuite, FibTest1) { From 725ccdcc9685228d44edf5cb4b4a90cc1638b010 Mon Sep 17 00:00:00 2001 From: sashbek Date: Fri, 9 Jan 2026 12:42:34 +0300 Subject: [PATCH 05/22] refactor: added jit_body copiing and translation to JITExecutorFactory --- lib/bytecode_parser/ParsingSession.cpp | 18 ++++++++++++++++++ lib/bytecode_parser/ParsingSession.hpp | 2 ++ .../scenarios/FunctionFactory.cpp | 12 +++++++----- .../scenarios/FunctionFactory.hpp | 10 ++++++++-- .../scenarios/FunctionParser.cpp | 5 ++++- lib/executor/CMakeLists.txt | 2 +- lib/executor/IJitExecutorFactory.hpp | 8 +++++++- lib/executor/PlaceholderJitExecutorFactory.cpp | 5 ++++- lib/executor/PlaceholderJitExecutorFactory.hpp | 6 +++++- 9 files changed, 56 insertions(+), 12 deletions(-) diff --git a/lib/bytecode_parser/ParsingSession.cpp b/lib/bytecode_parser/ParsingSession.cpp index 967f42f..859c953 100644 --- a/lib/bytecode_parser/ParsingSession.cpp +++ b/lib/bytecode_parser/ParsingSession.cpp @@ -181,4 +181,22 @@ std::expected ParsingSession::ConsumeBoolLiteral() { std::to_string(token->GetPosition().GetColumn()))); } + +void ParsingSession::CopyUntilBlockEnd(std::unique_ptr> result) { + size_t pos = pos_; + size_t cnt = 1; + while (pos < tokens_.size() && tokens_[pos]->GetStringType() != "EOF") { + if (tokens_[pos]->GetStringType() == "PUNCT" && tokens_[pos]->GetLexeme() == std::string(1, '}')) { + --cnt; + if (cnt == 0) { + break; + } + } else if (tokens_[pos]->GetStringType() == "PUNCT" && tokens_[pos]->GetLexeme() == std::string(1, '{')) { + ++cnt; + } + + result->push_back(tokens_[pos]); + } +} + } // namespace ovum::bytecode::parser diff --git a/lib/bytecode_parser/ParsingSession.hpp b/lib/bytecode_parser/ParsingSession.hpp index cad6d44..62637ab 100644 --- a/lib/bytecode_parser/ParsingSession.hpp +++ b/lib/bytecode_parser/ParsingSession.hpp @@ -46,6 +46,8 @@ class ParsingSession { std::unique_ptr GetInitStaticBlock(); void SetInitStaticBlock(std::unique_ptr block); + void CopyUntilBlockEnd(std::unique_ptr> result); + private: const std::vector& tokens_; size_t pos_ = 0; diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.cpp b/lib/bytecode_parser/scenarios/FunctionFactory.cpp index 4df3503..e564088 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.cpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.cpp @@ -20,13 +20,14 @@ vm::execution_tree::PureFunction FunctionFactory::WrapPure(Base&& base, template std::expected, std::runtime_error> FunctionFactory::WrapJit( - Base&& base) { + Base&& base, + std::unique_ptr> jit_body) { if (!jit_factory_.has_value()) { return std::unexpected(std::runtime_error("Jit factory not set")); } return {vm::execution_tree::JitCompilingFunction( - jit_factory_->get().Create(base.GetId()), std::forward(base), jit_boundary_)}; + jit_factory_->get().Create(base.GetId(), std::move(jit_body)), std::forward(base), jit_boundary_)}; } std::unique_ptr FunctionFactory::Create( @@ -35,7 +36,8 @@ std::unique_ptr FunctionFactory::Create std::unique_ptr body, bool pure, std::vector pure_argument_types, - bool no_jit) { + bool no_jit, + std::unique_ptr> jit_body) { RegularFunction regular = MakeRegular(id, arity, std::move(body)); if (!pure || pure_argument_types.empty()) { @@ -43,7 +45,7 @@ std::unique_ptr FunctionFactory::Create return std::make_unique(std::move(regular)); } - std::expected jit_func = WrapJit(std::move(regular)); + std::expected jit_func = WrapJit(std::move(regular), std::move(jit_body)); if (!jit_func) { return nullptr; @@ -56,7 +58,7 @@ std::unique_ptr FunctionFactory::Create return std::make_unique(WrapPure(std::move(regular), std::move(pure_argument_types))); } - std::expected jit_func = WrapJit(std::move(regular)); + std::expected jit_func = WrapJit(std::move(regular), std::move(jit_body)); if (!jit_func) { return nullptr; diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.hpp b/lib/bytecode_parser/scenarios/FunctionFactory.hpp index bd764a9..99b69ee 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.hpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include "lib/execution_tree/Block.hpp" #include "lib/execution_tree/Function.hpp" #include "lib/execution_tree/IFunctionExecutable.hpp" @@ -33,7 +35,8 @@ class FunctionFactory { std::unique_ptr body, bool pure = false, std::vector pure_argument_types = {}, - bool no_jit = false); + bool no_jit = false, + std::unique_ptr> jit_body = nullptr); private: vm::execution_tree::Function MakeRegular(const vm::runtime::FunctionId& id, @@ -44,7 +47,10 @@ class FunctionFactory { vm::execution_tree::PureFunction WrapPure(Base&& base, std::vector&& argument_types); template - std::expected, std::runtime_error> WrapJit(Base&& base); + std::expected, std::runtime_error> WrapJit( + Base&& base, + std::unique_ptr> jit_body + ); std::optional> jit_factory_; size_t jit_boundary_; diff --git a/lib/bytecode_parser/scenarios/FunctionParser.cpp b/lib/bytecode_parser/scenarios/FunctionParser.cpp index 568cc32..cbf149d 100644 --- a/lib/bytecode_parser/scenarios/FunctionParser.cpp +++ b/lib/bytecode_parser/scenarios/FunctionParser.cpp @@ -88,6 +88,9 @@ std::expected FunctionParser::Handle(ParsingSessionPt } std::unique_ptr body = std::make_unique(); + std::unique_ptr> jit_body = std::make_unique>(); + + ctx->CopyUntilBlockEnd(std::move(jit_body)); ctx->SetCurrentBlock(body.get()); @@ -113,7 +116,7 @@ std::expected FunctionParser::Handle(ParsingSessionPt FunctionFactory factory(ctx->GetJitFactory(), ctx->GetJitBoundary()); std::unique_ptr func = - factory.Create(name_res.value(), arity, std::move(body), is_pure, std::move(pure_types), no_jit); + factory.Create(name_res.value(), arity, std::move(body), is_pure, std::move(pure_types), no_jit, std::move(jit_body)); if (func == nullptr) { return std::unexpected(BytecodeParserError("Failed to create function: JIT compilation failed")); diff --git a/lib/executor/CMakeLists.txt b/lib/executor/CMakeLists.txt index a117e5f..8df4cef 100644 --- a/lib/executor/CMakeLists.txt +++ b/lib/executor/CMakeLists.txt @@ -7,4 +7,4 @@ add_library(executor STATIC ) target_include_directories(executor PUBLIC ${PROJECT_SOURCE_DIR}) -target_link_libraries(executor PUBLIC runtime execution_tree) +target_link_libraries(executor PUBLIC runtime execution_tree tokens) diff --git a/lib/executor/IJitExecutorFactory.hpp b/lib/executor/IJitExecutorFactory.hpp index 291459e..d9de88c 100644 --- a/lib/executor/IJitExecutorFactory.hpp +++ b/lib/executor/IJitExecutorFactory.hpp @@ -3,6 +3,9 @@ #include #include +#include + +#include #include "IJitExecutor.hpp" @@ -12,7 +15,10 @@ class IJitExecutorFactory { // NOLINT(cppcoreguidelines-special-member-functions public: virtual ~IJitExecutorFactory() = default; - [[nodiscard]] virtual std::unique_ptr Create(const std::string& function_name) const = 0; + [[nodiscard]] virtual std::unique_ptr Create( + const std::string& function_name, + std::unique_ptr> jit_body + ) const = 0; }; } // namespace ovum::vm::executor diff --git a/lib/executor/PlaceholderJitExecutorFactory.cpp b/lib/executor/PlaceholderJitExecutorFactory.cpp index b3ee733..1a8e2a3 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.cpp +++ b/lib/executor/PlaceholderJitExecutorFactory.cpp @@ -4,7 +4,10 @@ namespace ovum::vm::executor { -std::unique_ptr PlaceholderJitExecutorFactory::Create(const std::string&) const { +std::unique_ptr PlaceholderJitExecutorFactory::Create( + const std::string&, + std::unique_ptr> + ) const { return std::make_unique(); } diff --git a/lib/executor/PlaceholderJitExecutorFactory.hpp b/lib/executor/PlaceholderJitExecutorFactory.hpp index bc9f6f4..b3de2ed 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.hpp +++ b/lib/executor/PlaceholderJitExecutorFactory.hpp @@ -2,6 +2,9 @@ #define EXECUTOR_PLACEHOLDERJITEXECUTORFACTORY_HPP #include +#include + +#include #include "IJitExecutorFactory.hpp" @@ -9,7 +12,8 @@ namespace ovum::vm::executor { class PlaceholderJitExecutorFactory : public IJitExecutorFactory { public: - [[nodiscard]] std::unique_ptr Create(const std::string&) const override; + [[nodiscard]] std::unique_ptr Create(const std::string&, + std::unique_ptr>) const override; }; } // namespace ovum::vm::executor From 82b454d9b68e74a50584bb8e26bceb725bd071f8 Mon Sep 17 00:00:00 2001 From: sashbek Date: Fri, 9 Jan 2026 13:11:37 +0300 Subject: [PATCH 06/22] refactor: fixed uniqueptr usage to shared ptr, CopyUntilBlockEnd() --- lib/bytecode_parser/ParsingSession.cpp | 7 +++++-- lib/bytecode_parser/ParsingSession.hpp | 2 +- lib/bytecode_parser/scenarios/FunctionFactory.cpp | 4 ++-- lib/bytecode_parser/scenarios/FunctionFactory.hpp | 4 ++-- lib/bytecode_parser/scenarios/FunctionParser.cpp | 5 ++--- lib/executor/IJitExecutorFactory.hpp | 2 +- lib/executor/PlaceholderJitExecutorFactory.cpp | 2 +- lib/executor/PlaceholderJitExecutorFactory.hpp | 2 +- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/bytecode_parser/ParsingSession.cpp b/lib/bytecode_parser/ParsingSession.cpp index 859c953..6d7e047 100644 --- a/lib/bytecode_parser/ParsingSession.cpp +++ b/lib/bytecode_parser/ParsingSession.cpp @@ -182,7 +182,8 @@ std::expected ParsingSession::ConsumeBoolLiteral() { } -void ParsingSession::CopyUntilBlockEnd(std::unique_ptr> result) { +std::vector ParsingSession::CopyUntilBlockEnd() { + std::vector result; size_t pos = pos_; size_t cnt = 1; while (pos < tokens_.size() && tokens_[pos]->GetStringType() != "EOF") { @@ -195,8 +196,10 @@ void ParsingSession::CopyUntilBlockEnd(std::unique_ptr> re ++cnt; } - result->push_back(tokens_[pos]); + result.push_back(tokens_[pos]); } + + return result; } } // namespace ovum::bytecode::parser diff --git a/lib/bytecode_parser/ParsingSession.hpp b/lib/bytecode_parser/ParsingSession.hpp index 62637ab..09019ed 100644 --- a/lib/bytecode_parser/ParsingSession.hpp +++ b/lib/bytecode_parser/ParsingSession.hpp @@ -46,7 +46,7 @@ class ParsingSession { std::unique_ptr GetInitStaticBlock(); void SetInitStaticBlock(std::unique_ptr block); - void CopyUntilBlockEnd(std::unique_ptr> result); + std::vector CopyUntilBlockEnd(); private: const std::vector& tokens_; diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.cpp b/lib/bytecode_parser/scenarios/FunctionFactory.cpp index e564088..469c7d5 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.cpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.cpp @@ -21,7 +21,7 @@ vm::execution_tree::PureFunction FunctionFactory::WrapPure(Base&& base, template std::expected, std::runtime_error> FunctionFactory::WrapJit( Base&& base, - std::unique_ptr> jit_body) { + std::shared_ptr> jit_body) { if (!jit_factory_.has_value()) { return std::unexpected(std::runtime_error("Jit factory not set")); } @@ -37,7 +37,7 @@ std::unique_ptr FunctionFactory::Create bool pure, std::vector pure_argument_types, bool no_jit, - std::unique_ptr> jit_body) { + std::shared_ptr> jit_body) { RegularFunction regular = MakeRegular(id, arity, std::move(body)); if (!pure || pure_argument_types.empty()) { diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.hpp b/lib/bytecode_parser/scenarios/FunctionFactory.hpp index 99b69ee..2760f18 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.hpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.hpp @@ -36,7 +36,7 @@ class FunctionFactory { bool pure = false, std::vector pure_argument_types = {}, bool no_jit = false, - std::unique_ptr> jit_body = nullptr); + std::shared_ptr> jit_body = nullptr); private: vm::execution_tree::Function MakeRegular(const vm::runtime::FunctionId& id, @@ -49,7 +49,7 @@ class FunctionFactory { template std::expected, std::runtime_error> WrapJit( Base&& base, - std::unique_ptr> jit_body + std::shared_ptr> jit_body ); std::optional> jit_factory_; diff --git a/lib/bytecode_parser/scenarios/FunctionParser.cpp b/lib/bytecode_parser/scenarios/FunctionParser.cpp index cbf149d..2f982c3 100644 --- a/lib/bytecode_parser/scenarios/FunctionParser.cpp +++ b/lib/bytecode_parser/scenarios/FunctionParser.cpp @@ -88,9 +88,8 @@ std::expected FunctionParser::Handle(ParsingSessionPt } std::unique_ptr body = std::make_unique(); - std::unique_ptr> jit_body = std::make_unique>(); - - ctx->CopyUntilBlockEnd(std::move(jit_body)); + auto jit_body_vec = ctx->CopyUntilBlockEnd(); + std::shared_ptr> jit_body = std::make_shared>(jit_body_vec); ctx->SetCurrentBlock(body.get()); diff --git a/lib/executor/IJitExecutorFactory.hpp b/lib/executor/IJitExecutorFactory.hpp index d9de88c..ced2388 100644 --- a/lib/executor/IJitExecutorFactory.hpp +++ b/lib/executor/IJitExecutorFactory.hpp @@ -17,7 +17,7 @@ class IJitExecutorFactory { // NOLINT(cppcoreguidelines-special-member-functions [[nodiscard]] virtual std::unique_ptr Create( const std::string& function_name, - std::unique_ptr> jit_body + std::shared_ptr> jit_body ) const = 0; }; diff --git a/lib/executor/PlaceholderJitExecutorFactory.cpp b/lib/executor/PlaceholderJitExecutorFactory.cpp index 1a8e2a3..8661bf1 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.cpp +++ b/lib/executor/PlaceholderJitExecutorFactory.cpp @@ -6,7 +6,7 @@ namespace ovum::vm::executor { std::unique_ptr PlaceholderJitExecutorFactory::Create( const std::string&, - std::unique_ptr> + std::shared_ptr> ) const { return std::make_unique(); } diff --git a/lib/executor/PlaceholderJitExecutorFactory.hpp b/lib/executor/PlaceholderJitExecutorFactory.hpp index b3de2ed..10414fe 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.hpp +++ b/lib/executor/PlaceholderJitExecutorFactory.hpp @@ -13,7 +13,7 @@ namespace ovum::vm::executor { class PlaceholderJitExecutorFactory : public IJitExecutorFactory { public: [[nodiscard]] std::unique_ptr Create(const std::string&, - std::unique_ptr>) const override; + std::shared_ptr>) const override; }; } // namespace ovum::vm::executor From 471181678f9f553d4b1802e766c3e8d9c0f36260 Mon Sep 17 00:00:00 2001 From: sashbek Date: Sat, 10 Jan 2026 13:30:19 +0300 Subject: [PATCH 07/22] fix: CopyUntilBlockEnd --- lib/bytecode_parser/ParsingSession.cpp | 1 + lib/bytecode_parser/scenarios/FunctionParser.cpp | 8 ++++++-- lib/execution_tree/JitCompilingFunction.hpp | 2 +- lib/executor/IJitExecutor.hpp | 4 +++- lib/executor/PlaceholderJitExecutor.cpp | 2 +- lib/executor/PlaceholderJitExecutor.hpp | 4 +++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/bytecode_parser/ParsingSession.cpp b/lib/bytecode_parser/ParsingSession.cpp index 6d7e047..4ea3ba1 100644 --- a/lib/bytecode_parser/ParsingSession.cpp +++ b/lib/bytecode_parser/ParsingSession.cpp @@ -197,6 +197,7 @@ std::vector ParsingSession::CopyUntilBlockEnd() { } result.push_back(tokens_[pos]); + ++pos; } return result; diff --git a/lib/bytecode_parser/scenarios/FunctionParser.cpp b/lib/bytecode_parser/scenarios/FunctionParser.cpp index 2f982c3..7afdf5b 100644 --- a/lib/bytecode_parser/scenarios/FunctionParser.cpp +++ b/lib/bytecode_parser/scenarios/FunctionParser.cpp @@ -88,8 +88,12 @@ std::expected FunctionParser::Handle(ParsingSessionPt } std::unique_ptr body = std::make_unique(); - auto jit_body_vec = ctx->CopyUntilBlockEnd(); - std::shared_ptr> jit_body = std::make_shared>(jit_body_vec); + + std::shared_ptr> jit_body; + if (!no_jit) { + auto jit_body_vec = ctx->CopyUntilBlockEnd(); + jit_body = std::make_shared>(jit_body_vec); + } ctx->SetCurrentBlock(body.get()); diff --git a/lib/execution_tree/JitCompilingFunction.hpp b/lib/execution_tree/JitCompilingFunction.hpp index 8ae0a23..289cb57 100644 --- a/lib/execution_tree/JitCompilingFunction.hpp +++ b/lib/execution_tree/JitCompilingFunction.hpp @@ -26,7 +26,7 @@ class JitCompilingFunction : public IFunctionExecutable { std::expected Execute(PassedExecutionData& execution_data) override { if (function_.GetTotalActionCount() > jit_action_boundary_) { if (executor_->TryCompile()) { - const std::expected jit_result = executor_->Run(execution_data.memory.machine_stack); + const std::expected jit_result = executor_->Run(execution_data); if (jit_result.has_value()) { return ExecutionResult::kNormal; diff --git a/lib/executor/IJitExecutor.hpp b/lib/executor/IJitExecutor.hpp index 7ed3530..7726e67 100644 --- a/lib/executor/IJitExecutor.hpp +++ b/lib/executor/IJitExecutor.hpp @@ -7,6 +7,8 @@ #include #include +#include "lib/execution_tree/PassedExecutionData.hpp" + namespace ovum::vm::executor { class IJitExecutor { // NOLINT(cppcoreguidelines-special-member-functions) @@ -16,7 +18,7 @@ class IJitExecutor { // NOLINT(cppcoreguidelines-special-member-functions) [[nodiscard]] virtual bool TryCompile() const = 0; [[nodiscard]] virtual std::expected Run( - std::stack>& stack) = 0; + execution_tree::PassedExecutionData& data) = 0; }; } // namespace ovum::vm::executor diff --git a/lib/executor/PlaceholderJitExecutor.cpp b/lib/executor/PlaceholderJitExecutor.cpp index 9d8a59c..43543c6 100644 --- a/lib/executor/PlaceholderJitExecutor.cpp +++ b/lib/executor/PlaceholderJitExecutor.cpp @@ -7,7 +7,7 @@ bool PlaceholderJitExecutor::TryCompile() const { } std::expected PlaceholderJitExecutor::Run( - std::stack>& /* stack */) { + execution_tree::PassedExecutionData& /* data */) { return std::unexpected(std::runtime_error("PlaceholderJitExecutor::Run: not implemented")); } diff --git a/lib/executor/PlaceholderJitExecutor.hpp b/lib/executor/PlaceholderJitExecutor.hpp index 0e7d48c..17aed22 100644 --- a/lib/executor/PlaceholderJitExecutor.hpp +++ b/lib/executor/PlaceholderJitExecutor.hpp @@ -3,6 +3,8 @@ #include "IJitExecutor.hpp" +#include "lib/execution_tree/PassedExecutionData.hpp" + namespace ovum::vm::executor { class PlaceholderJitExecutor : public IJitExecutor { @@ -12,7 +14,7 @@ class PlaceholderJitExecutor : public IJitExecutor { [[nodiscard]] bool TryCompile() const override; [[nodiscard]] std::expected Run( - std::stack>& stack) override; + execution_tree::PassedExecutionData& data) override; }; } // namespace ovum::vm::executor From ab986f72883c163b57794b60c3df2a09c2f18daa Mon Sep 17 00:00:00 2001 From: sashbek Date: Sun, 11 Jan 2026 12:12:27 +0300 Subject: [PATCH 08/22] config: add jit library --- lib/executor/CMakeLists.txt | 2 +- tests/test_data/examples | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/executor/CMakeLists.txt b/lib/executor/CMakeLists.txt index 8df4cef..e729c83 100644 --- a/lib/executor/CMakeLists.txt +++ b/lib/executor/CMakeLists.txt @@ -7,4 +7,4 @@ add_library(executor STATIC ) target_include_directories(executor PUBLIC ${PROJECT_SOURCE_DIR}) -target_link_libraries(executor PUBLIC runtime execution_tree tokens) +target_link_libraries(executor PUBLIC runtime execution_tree tokens jit) \ No newline at end of file diff --git a/tests/test_data/examples b/tests/test_data/examples index 7c3c96c..51cb78d 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 7c3c96c065f2e1904f66b3f2d5c926863fb2a201 +Subproject commit 51cb78d7778c97ac9d4561a210a003e94b59fe83 From 6bb05391246a954a12b4ca4aa00b615fa843de41 Mon Sep 17 00:00:00 2001 From: bialger Date: Sun, 11 Jan 2026 12:46:24 +0300 Subject: [PATCH 09/22] build: conditionally link JIT library in executor CMake configuration --- lib/executor/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/executor/CMakeLists.txt b/lib/executor/CMakeLists.txt index e729c83..b506cc1 100644 --- a/lib/executor/CMakeLists.txt +++ b/lib/executor/CMakeLists.txt @@ -7,4 +7,8 @@ add_library(executor STATIC ) target_include_directories(executor PUBLIC ${PROJECT_SOURCE_DIR}) -target_link_libraries(executor PUBLIC runtime execution_tree tokens jit) \ No newline at end of file +target_link_libraries(executor PUBLIC runtime execution_tree tokens) + +if (JIT_PROVIDED) + target_link_libraries(executor PUBLIC jit) +endif () From 4dbad7c46731b04f2cbd6220dd9ec75535815111 Mon Sep 17 00:00:00 2001 From: bialger Date: Sun, 11 Jan 2026 12:46:47 +0300 Subject: [PATCH 10/22] refactor: improve code formatting and consistency in function signatures across multiple files --- lib/bytecode_parser/ParsingSession.cpp | 3 +-- .../scenarios/FunctionFactory.cpp | 3 +-- .../scenarios/FunctionFactory.hpp | 19 +++++++++---------- .../scenarios/FunctionParser.cpp | 6 +++--- lib/executor/IJitExecutor.hpp | 3 +-- lib/executor/IJitExecutorFactory.hpp | 6 ++---- lib/executor/PlaceholderJitExecutor.cpp | 3 +-- lib/executor/PlaceholderJitExecutor.hpp | 3 +-- .../PlaceholderJitExecutorFactory.cpp | 6 ++---- .../PlaceholderJitExecutorFactory.hpp | 2 +- 10 files changed, 22 insertions(+), 32 deletions(-) diff --git a/lib/bytecode_parser/ParsingSession.cpp b/lib/bytecode_parser/ParsingSession.cpp index 66b8ef5..9b32be6 100644 --- a/lib/bytecode_parser/ParsingSession.cpp +++ b/lib/bytecode_parser/ParsingSession.cpp @@ -184,10 +184,9 @@ std::expected ParsingSession::ConsumeBoolLiteral() { std::to_string(token->GetPosition().GetColumn()))); } - std::vector ParsingSession::CopyUntilBlockEnd() { std::vector result; - size_t pos = pos_; + size_t pos = pos_; size_t cnt = 1; while (pos < tokens_.size() && tokens_[pos]->GetStringType() != "EOF") { if (tokens_[pos]->GetStringType() == "PUNCT" && tokens_[pos]->GetLexeme() == std::string(1, '}')) { diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.cpp b/lib/bytecode_parser/scenarios/FunctionFactory.cpp index 469c7d5..4acdfbe 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.cpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.cpp @@ -20,8 +20,7 @@ vm::execution_tree::PureFunction FunctionFactory::WrapPure(Base&& base, template std::expected, std::runtime_error> FunctionFactory::WrapJit( - Base&& base, - std::shared_ptr> jit_body) { + Base&& base, std::shared_ptr> jit_body) { if (!jit_factory_.has_value()) { return std::unexpected(std::runtime_error("Jit factory not set")); } diff --git a/lib/bytecode_parser/scenarios/FunctionFactory.hpp b/lib/bytecode_parser/scenarios/FunctionFactory.hpp index 2760f18..830ee9d 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.hpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.hpp @@ -30,13 +30,14 @@ class FunctionFactory { FunctionFactory(std::optional> jit_factory, size_t jit_boundary); - std::unique_ptr Create(const vm::runtime::FunctionId& id, - size_t arity, - std::unique_ptr body, - bool pure = false, - std::vector pure_argument_types = {}, - bool no_jit = false, - std::shared_ptr> jit_body = nullptr); + std::unique_ptr Create( + const vm::runtime::FunctionId& id, + size_t arity, + std::unique_ptr body, + bool pure = false, + std::vector pure_argument_types = {}, + bool no_jit = false, + std::shared_ptr> jit_body = nullptr); private: vm::execution_tree::Function MakeRegular(const vm::runtime::FunctionId& id, @@ -48,9 +49,7 @@ class FunctionFactory { template std::expected, std::runtime_error> WrapJit( - Base&& base, - std::shared_ptr> jit_body - ); + Base&& base, std::shared_ptr> jit_body); std::optional> jit_factory_; size_t jit_boundary_; diff --git a/lib/bytecode_parser/scenarios/FunctionParser.cpp b/lib/bytecode_parser/scenarios/FunctionParser.cpp index 7afdf5b..be2a042 100644 --- a/lib/bytecode_parser/scenarios/FunctionParser.cpp +++ b/lib/bytecode_parser/scenarios/FunctionParser.cpp @@ -90,7 +90,7 @@ std::expected FunctionParser::Handle(ParsingSessionPt std::unique_ptr body = std::make_unique(); std::shared_ptr> jit_body; - if (!no_jit) { + if (!no_jit) { auto jit_body_vec = ctx->CopyUntilBlockEnd(); jit_body = std::make_shared>(jit_body_vec); } @@ -118,8 +118,8 @@ std::expected FunctionParser::Handle(ParsingSessionPt FunctionFactory factory(ctx->GetJitFactory(), ctx->GetJitBoundary()); - std::unique_ptr func = - factory.Create(name_res.value(), arity, std::move(body), is_pure, std::move(pure_types), no_jit, std::move(jit_body)); + std::unique_ptr func = factory.Create( + name_res.value(), arity, std::move(body), is_pure, std::move(pure_types), no_jit, std::move(jit_body)); if (func == nullptr) { return std::unexpected(BytecodeParserError("Failed to create function: JIT compilation failed")); diff --git a/lib/executor/IJitExecutor.hpp b/lib/executor/IJitExecutor.hpp index 7726e67..f89e878 100644 --- a/lib/executor/IJitExecutor.hpp +++ b/lib/executor/IJitExecutor.hpp @@ -17,8 +17,7 @@ class IJitExecutor { // NOLINT(cppcoreguidelines-special-member-functions) [[nodiscard]] virtual bool TryCompile() const = 0; - [[nodiscard]] virtual std::expected Run( - execution_tree::PassedExecutionData& data) = 0; + [[nodiscard]] virtual std::expected Run(execution_tree::PassedExecutionData& data) = 0; }; } // namespace ovum::vm::executor diff --git a/lib/executor/IJitExecutorFactory.hpp b/lib/executor/IJitExecutorFactory.hpp index ced2388..5c6a0fa 100644 --- a/lib/executor/IJitExecutorFactory.hpp +++ b/lib/executor/IJitExecutorFactory.hpp @@ -15,10 +15,8 @@ class IJitExecutorFactory { // NOLINT(cppcoreguidelines-special-member-functions public: virtual ~IJitExecutorFactory() = default; - [[nodiscard]] virtual std::unique_ptr Create( - const std::string& function_name, - std::shared_ptr> jit_body - ) const = 0; + [[nodiscard]] virtual std::unique_ptr Create(const std::string& function_name, + std::shared_ptr> jit_body) const = 0; }; } // namespace ovum::vm::executor diff --git a/lib/executor/PlaceholderJitExecutor.cpp b/lib/executor/PlaceholderJitExecutor.cpp index 43543c6..a156374 100644 --- a/lib/executor/PlaceholderJitExecutor.cpp +++ b/lib/executor/PlaceholderJitExecutor.cpp @@ -6,8 +6,7 @@ bool PlaceholderJitExecutor::TryCompile() const { return false; } -std::expected PlaceholderJitExecutor::Run( - execution_tree::PassedExecutionData& /* data */) { +std::expected PlaceholderJitExecutor::Run(execution_tree::PassedExecutionData& /* data */) { return std::unexpected(std::runtime_error("PlaceholderJitExecutor::Run: not implemented")); } diff --git a/lib/executor/PlaceholderJitExecutor.hpp b/lib/executor/PlaceholderJitExecutor.hpp index 17aed22..5ced551 100644 --- a/lib/executor/PlaceholderJitExecutor.hpp +++ b/lib/executor/PlaceholderJitExecutor.hpp @@ -13,8 +13,7 @@ class PlaceholderJitExecutor : public IJitExecutor { [[nodiscard]] bool TryCompile() const override; - [[nodiscard]] std::expected Run( - execution_tree::PassedExecutionData& data) override; + [[nodiscard]] std::expected Run(execution_tree::PassedExecutionData& data) override; }; } // namespace ovum::vm::executor diff --git a/lib/executor/PlaceholderJitExecutorFactory.cpp b/lib/executor/PlaceholderJitExecutorFactory.cpp index 8661bf1..ec5f0a5 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.cpp +++ b/lib/executor/PlaceholderJitExecutorFactory.cpp @@ -4,10 +4,8 @@ namespace ovum::vm::executor { -std::unique_ptr PlaceholderJitExecutorFactory::Create( - const std::string&, - std::shared_ptr> - ) const { +std::unique_ptr PlaceholderJitExecutorFactory::Create(const std::string&, + std::shared_ptr>) const { return std::make_unique(); } diff --git a/lib/executor/PlaceholderJitExecutorFactory.hpp b/lib/executor/PlaceholderJitExecutorFactory.hpp index 10414fe..22193d8 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.hpp +++ b/lib/executor/PlaceholderJitExecutorFactory.hpp @@ -13,7 +13,7 @@ namespace ovum::vm::executor { class PlaceholderJitExecutorFactory : public IJitExecutorFactory { public: [[nodiscard]] std::unique_ptr Create(const std::string&, - std::shared_ptr>) const override; + std::shared_ptr>) const override; }; } // namespace ovum::vm::executor From 6be4d25c8a9501f09a056c0d545800e678ac4769 Mon Sep 17 00:00:00 2001 From: bialger Date: Sun, 11 Jan 2026 23:32:16 +0300 Subject: [PATCH 11/22] feat: add copy assignment methods for fundamental types and arrays, including tests for functionality --- lib/executor/BuiltinFunctions.cpp | 156 +++++++++++++ lib/executor/BuiltinFunctions.hpp | 20 ++ lib/executor/builtin_factory.cpp | 160 ++++++++++++++ tests/builtin_functions_tests.cpp | 349 ++++++++++++++++++++++++++++++ 4 files changed, 685 insertions(+) diff --git a/lib/executor/BuiltinFunctions.cpp b/lib/executor/BuiltinFunctions.cpp index 0c66632..b23fa6a 100644 --- a/lib/executor/BuiltinFunctions.cpp +++ b/lib/executor/BuiltinFunctions.cpp @@ -99,6 +99,42 @@ std::expected FundamentalTypeCopyConstructo return ExecutionResult::kNormal; } +// Template helper for fundamental type copy assignment from same type +// Arguments: object (this) is first, source is second +template +std::expected FundamentalTypeCopyAssignment(PassedExecutionData& data) { + if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0]) || + !std::holds_alternative(data.memory.stack_frames.top().local_variables[1])) { + return std::unexpected(std::runtime_error("CopyAssignment: invalid argument types")); + } + + void* obj_ptr = std::get(data.memory.stack_frames.top().local_variables[0]); + void* source_obj = std::get(data.memory.stack_frames.top().local_variables[1]); + const T* source_data = runtime::GetDataPointer(source_obj); + T* data_ptr = runtime::GetDataPointer(obj_ptr); + *data_ptr = *source_data; + + return ExecutionResult::kNormal; +} + +// Template helper for fundamental type copy assignment from fundamental type +// Arguments: object (this) is first, value is second +template +std::expected FundamentalTypeCopyAssignmentFromFundamental( + PassedExecutionData& data) { + if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0]) || + !std::holds_alternative(data.memory.stack_frames.top().local_variables[1])) { + return std::unexpected(std::runtime_error("CopyAssignmentFromFundamental: invalid argument types")); + } + + void* obj_ptr = std::get(data.memory.stack_frames.top().local_variables[0]); + T value = std::get(data.memory.stack_frames.top().local_variables[1]); + T* data_ptr = runtime::GetDataPointer(obj_ptr); + *data_ptr = value; + + return ExecutionResult::kNormal; +} + // Template helper for fundamental type destructors (trivial, no cleanup needed) // Arguments: object is first template @@ -285,6 +321,24 @@ std::expected ArrayCopyConstructor(PassedEx return ExecutionResult::kNormal; } +// Template helper for array copy assignment +// Arguments: object (this) is first, source is second +template +std::expected ArrayCopyAssignment(PassedExecutionData& data) { + if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0]) || + !std::holds_alternative(data.memory.stack_frames.top().local_variables[1])) { + return std::unexpected(std::runtime_error("ArrayCopyAssignment: invalid argument types")); + } + + void* obj_ptr = std::get(data.memory.stack_frames.top().local_variables[0]); + void* source_obj = std::get(data.memory.stack_frames.top().local_variables[1]); + const auto* source_vec = runtime::GetDataPointer>(source_obj); + auto* vec_data = runtime::GetDataPointer>(obj_ptr); + *vec_data = *source_vec; + + return ExecutionResult::kNormal; +} + // Template helper for array destructors // Arguments: object is first template @@ -713,6 +767,14 @@ std::expected IntCopyConstructor(PassedExec return FundamentalTypeCopyConstructor(data); } +std::expected IntCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + +std::expected IntCopyAssignmentFromInt(PassedExecutionData& data) { + return FundamentalTypeCopyAssignmentFromFundamental(data); +} + std::expected IntDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } @@ -752,6 +814,14 @@ std::expected FloatCopyConstructor(PassedEx return FundamentalTypeCopyConstructor(data); } +std::expected FloatCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + +std::expected FloatCopyAssignmentFromFloat(PassedExecutionData& data) { + return FundamentalTypeCopyAssignmentFromFundamental(data); +} + std::expected FloatDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } @@ -782,6 +852,14 @@ std::expected CharCopyConstructor(PassedExe return FundamentalTypeCopyConstructor(data); } +std::expected CharCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + +std::expected CharCopyAssignmentFromChar(PassedExecutionData& data) { + return FundamentalTypeCopyAssignmentFromFundamental(data); +} + std::expected CharDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } @@ -813,6 +891,14 @@ std::expected ByteCopyConstructor(PassedExe return FundamentalTypeCopyConstructor(data); } +std::expected ByteCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + +std::expected ByteCopyAssignmentFromByte(PassedExecutionData& data) { + return FundamentalTypeCopyAssignmentFromFundamental(data); +} + std::expected ByteDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } @@ -843,6 +929,14 @@ std::expected BoolCopyConstructor(PassedExe return FundamentalTypeCopyConstructor(data); } +std::expected BoolCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + +std::expected BoolCopyAssignmentFromBool(PassedExecutionData& data) { + return FundamentalTypeCopyAssignmentFromFundamental(data); +} + std::expected BoolDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } @@ -988,6 +1082,21 @@ std::expected StringCopyConstructor(PassedE return ExecutionResult::kNormal; } +std::expected StringCopyAssignment(PassedExecutionData& data) { + if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0]) || + !std::holds_alternative(data.memory.stack_frames.top().local_variables[1])) { + return std::unexpected(std::runtime_error("String::CopyAssignment: invalid argument types")); + } + + void* obj_ptr = std::get(data.memory.stack_frames.top().local_variables[0]); + void* source_obj = std::get(data.memory.stack_frames.top().local_variables[1]); + const auto* source_string = runtime::GetDataPointer(source_obj); + auto* string_data = runtime::GetDataPointer(obj_ptr); + *string_data = *source_string; + + return ExecutionResult::kNormal; +} + std::expected StringDestructor(PassedExecutionData& data) { if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0])) { return std::unexpected(std::runtime_error("String::Destructor: invalid argument types")); @@ -1594,6 +1703,10 @@ std::expected IntArrayCopyConstructor(Passe return ArrayCopyConstructor(data); } +std::expected IntArrayCopyAssignment(PassedExecutionData& data) { + return ArrayCopyAssignment(data); +} + std::expected IntArrayDestructor(PassedExecutionData& data) { return ArrayDestructor(data); } @@ -1615,6 +1728,10 @@ std::expected FloatArrayCopyConstructor(Pas return ArrayCopyConstructor(data); } +std::expected FloatArrayCopyAssignment(PassedExecutionData& data) { + return ArrayCopyAssignment(data); +} + std::expected FloatArrayDestructor(PassedExecutionData& data) { return ArrayDestructor(data); } @@ -1636,6 +1753,10 @@ std::expected CharArrayCopyConstructor(Pass return ArrayCopyConstructor(data); } +std::expected CharArrayCopyAssignment(PassedExecutionData& data) { + return ArrayCopyAssignment(data); +} + std::expected CharArrayDestructor(PassedExecutionData& data) { return ArrayDestructor(data); } @@ -1685,6 +1806,21 @@ std::expected ByteArrayCopyConstructor(Pass return ExecutionResult::kNormal; } +std::expected ByteArrayCopyAssignment(PassedExecutionData& data) { + if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0]) || + !std::holds_alternative(data.memory.stack_frames.top().local_variables[1])) { + return std::unexpected(std::runtime_error("ByteArrayCopyAssignment: invalid argument types")); + } + + void* obj_ptr = std::get(data.memory.stack_frames.top().local_variables[0]); + void* source_obj = std::get(data.memory.stack_frames.top().local_variables[1]); + const auto* source_byte_array = runtime::GetDataPointer(source_obj); + auto* byte_array_data = runtime::GetDataPointer(obj_ptr); + *byte_array_data = *source_byte_array; + + return ExecutionResult::kNormal; +} + std::expected ByteArrayDestructor(PassedExecutionData& data) { if (!std::holds_alternative(data.memory.stack_frames.top().local_variables[0])) { return std::unexpected(std::runtime_error("ByteArrayDestructor: invalid argument types")); @@ -1787,6 +1923,10 @@ std::expected BoolArrayCopyConstructor(Pass return ArrayCopyConstructor(data); } +std::expected BoolArrayCopyAssignment(PassedExecutionData& data) { + return ArrayCopyAssignment(data); +} + std::expected BoolArrayDestructor(PassedExecutionData& data) { return ArrayDestructor(data); } @@ -1822,6 +1962,10 @@ std::expected ObjectArrayCopyConstructor(Pa return ArrayCopyConstructor(data); } +std::expected ObjectArrayCopyAssignment(PassedExecutionData& data) { + return ArrayCopyAssignment(data); +} + std::expected ObjectArrayDestructor(PassedExecutionData& data) { return ArrayDestructor(data); } @@ -1843,6 +1987,10 @@ std::expected StringArrayCopyConstructor(Pa return ObjectArrayCopyConstructor(data); } +std::expected StringArrayCopyAssignment(PassedExecutionData& data) { + return ObjectArrayCopyAssignment(data); +} + std::expected StringArrayDestructor(PassedExecutionData& data) { return ObjectArrayDestructor(data); } @@ -1864,6 +2012,10 @@ std::expected PointerArrayCopyConstructor(P return ObjectArrayCopyConstructor(data); } +std::expected PointerArrayCopyAssignment(PassedExecutionData& data) { + return ObjectArrayCopyAssignment(data); +} + std::expected PointerArrayDestructor(PassedExecutionData& data) { return ObjectArrayDestructor(data); } @@ -1885,6 +2037,10 @@ std::expected PointerCopyConstructor(Passed return FundamentalTypeCopyConstructor(data); } +std::expected PointerCopyAssignment(PassedExecutionData& data) { + return FundamentalTypeCopyAssignment(data); +} + std::expected PointerDestructor(PassedExecutionData& data) { return FundamentalTypeDestructor(data); } diff --git a/lib/executor/BuiltinFunctions.hpp b/lib/executor/BuiltinFunctions.hpp index 9e77b28..4a58e9b 100644 --- a/lib/executor/BuiltinFunctions.hpp +++ b/lib/executor/BuiltinFunctions.hpp @@ -28,6 +28,8 @@ namespace ovum::vm::execution_tree { // Int methods std::expected IntConstructor(PassedExecutionData& data); std::expected IntCopyConstructor(PassedExecutionData& data); +std::expected IntCopyAssignment(PassedExecutionData& data); +std::expected IntCopyAssignmentFromInt(PassedExecutionData& data); std::expected IntDestructor(PassedExecutionData& data); std::expected IntEquals(PassedExecutionData& data); std::expected IntIsLess(PassedExecutionData& data); @@ -37,6 +39,8 @@ std::expected IntGetHash(PassedExecutionDat // Float methods std::expected FloatConstructor(PassedExecutionData& data); std::expected FloatCopyConstructor(PassedExecutionData& data); +std::expected FloatCopyAssignment(PassedExecutionData& data); +std::expected FloatCopyAssignmentFromFloat(PassedExecutionData& data); std::expected FloatDestructor(PassedExecutionData& data); std::expected FloatEquals(PassedExecutionData& data); std::expected FloatIsLess(PassedExecutionData& data); @@ -46,6 +50,8 @@ std::expected FloatGetHash(PassedExecutionD // Char methods std::expected CharConstructor(PassedExecutionData& data); std::expected CharCopyConstructor(PassedExecutionData& data); +std::expected CharCopyAssignment(PassedExecutionData& data); +std::expected CharCopyAssignmentFromChar(PassedExecutionData& data); std::expected CharDestructor(PassedExecutionData& data); std::expected CharEquals(PassedExecutionData& data); std::expected CharIsLess(PassedExecutionData& data); @@ -55,6 +61,8 @@ std::expected CharGetHash(PassedExecutionDa // Byte methods std::expected ByteConstructor(PassedExecutionData& data); std::expected ByteCopyConstructor(PassedExecutionData& data); +std::expected ByteCopyAssignment(PassedExecutionData& data); +std::expected ByteCopyAssignmentFromByte(PassedExecutionData& data); std::expected ByteDestructor(PassedExecutionData& data); std::expected ByteEquals(PassedExecutionData& data); std::expected ByteIsLess(PassedExecutionData& data); @@ -64,6 +72,8 @@ std::expected ByteGetHash(PassedExecutionDa // Bool methods std::expected BoolConstructor(PassedExecutionData& data); std::expected BoolCopyConstructor(PassedExecutionData& data); +std::expected BoolCopyAssignment(PassedExecutionData& data); +std::expected BoolCopyAssignmentFromBool(PassedExecutionData& data); std::expected BoolDestructor(PassedExecutionData& data); std::expected BoolEquals(PassedExecutionData& data); std::expected BoolIsLess(PassedExecutionData& data); @@ -76,6 +86,7 @@ std::expected NullableDestructor(PassedExec // String methods std::expected StringCopyConstructor(PassedExecutionData& data); +std::expected StringCopyAssignment(PassedExecutionData& data); std::expected StringDestructor(PassedExecutionData& data); std::expected StringEquals(PassedExecutionData& data); std::expected StringIsLess(PassedExecutionData& data); @@ -87,6 +98,7 @@ std::expected StringToUtf8Bytes(PassedExecu // Array methods std::expected IntArrayConstructor(PassedExecutionData& data); std::expected IntArrayCopyConstructor(PassedExecutionData& data); +std::expected IntArrayCopyAssignment(PassedExecutionData& data); std::expected IntArrayDestructor(PassedExecutionData& data); std::expected IntArrayEquals(PassedExecutionData& data); std::expected IntArrayIsLess(PassedExecutionData& data); @@ -104,6 +116,7 @@ std::expected IntArrayGetAt(PassedExecution std::expected FloatArrayConstructor(PassedExecutionData& data); std::expected FloatArrayCopyConstructor(PassedExecutionData& data); +std::expected FloatArrayCopyAssignment(PassedExecutionData& data); std::expected FloatArrayDestructor(PassedExecutionData& data); std::expected FloatArrayEquals(PassedExecutionData& data); std::expected FloatArrayIsLess(PassedExecutionData& data); @@ -121,6 +134,7 @@ std::expected FloatArrayGetAt(PassedExecuti std::expected CharArrayConstructor(PassedExecutionData& data); std::expected CharArrayCopyConstructor(PassedExecutionData& data); +std::expected CharArrayCopyAssignment(PassedExecutionData& data); std::expected CharArrayDestructor(PassedExecutionData& data); std::expected CharArrayEquals(PassedExecutionData& data); std::expected CharArrayIsLess(PassedExecutionData& data); @@ -138,6 +152,7 @@ std::expected CharArrayGetAt(PassedExecutio std::expected ByteArrayConstructor(PassedExecutionData& data); std::expected ByteArrayCopyConstructor(PassedExecutionData& data); +std::expected ByteArrayCopyAssignment(PassedExecutionData& data); std::expected ByteArrayDestructor(PassedExecutionData& data); std::expected ByteArrayEquals(PassedExecutionData& data); std::expected ByteArrayIsLess(PassedExecutionData& data); @@ -158,6 +173,7 @@ std::expected ByteArrayFromObject(PassedExe std::expected BoolArrayConstructor(PassedExecutionData& data); std::expected BoolArrayCopyConstructor(PassedExecutionData& data); +std::expected BoolArrayCopyAssignment(PassedExecutionData& data); std::expected BoolArrayDestructor(PassedExecutionData& data); std::expected BoolArrayEquals(PassedExecutionData& data); std::expected BoolArrayIsLess(PassedExecutionData& data); @@ -175,6 +191,7 @@ std::expected BoolArrayGetAt(PassedExecutio std::expected ObjectArrayConstructor(PassedExecutionData& data); std::expected ObjectArrayCopyConstructor(PassedExecutionData& data); +std::expected ObjectArrayCopyAssignment(PassedExecutionData& data); std::expected ObjectArrayDestructor(PassedExecutionData& data); std::expected ObjectArrayEquals(PassedExecutionData& data); std::expected ObjectArrayIsLess(PassedExecutionData& data); @@ -192,6 +209,7 @@ std::expected ObjectArrayGetAt(PassedExecut std::expected StringArrayConstructor(PassedExecutionData& data); std::expected StringArrayCopyConstructor(PassedExecutionData& data); +std::expected StringArrayCopyAssignment(PassedExecutionData& data); std::expected StringArrayDestructor(PassedExecutionData& data); std::expected StringArrayEquals(PassedExecutionData& data); std::expected StringArrayIsLess(PassedExecutionData& data); @@ -210,6 +228,7 @@ std::expected StringArrayGetAt(PassedExecut // Pointer methods std::expected PointerConstructor(PassedExecutionData& data); std::expected PointerCopyConstructor(PassedExecutionData& data); +std::expected PointerCopyAssignment(PassedExecutionData& data); std::expected PointerDestructor(PassedExecutionData& data); std::expected PointerEquals(PassedExecutionData& data); std::expected PointerIsLess(PassedExecutionData& data); @@ -217,6 +236,7 @@ std::expected PointerGetHash(PassedExecutio std::expected PointerArrayConstructor(PassedExecutionData& data); std::expected PointerArrayCopyConstructor(PassedExecutionData& data); +std::expected PointerArrayCopyAssignment(PassedExecutionData& data); std::expected PointerArrayDestructor(PassedExecutionData& data); std::expected PointerArrayEquals(PassedExecutionData& data); std::expected PointerArrayIsLess(PassedExecutionData& data); diff --git a/lib/executor/builtin_factory.cpp b/lib/executor/builtin_factory.cpp index 70ab73c..9efac20 100644 --- a/lib/executor/builtin_factory.cpp +++ b/lib/executor/builtin_factory.cpp @@ -386,6 +386,22 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Int_copy__Int", 2, IntCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + + { + auto function = CreateMethodFunction("_Int_copy__int", 2, IntCopyAssignmentFromInt); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // Float methods { auto function = CreateMethodFunction("_Float_float", 2, FloatConstructor); @@ -443,6 +459,22 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Float_copy__Float", 2, FloatCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + + { + auto function = CreateMethodFunction("_Float_copy__float", 2, FloatCopyAssignmentFromFloat); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // Char methods { auto function = CreateMethodFunction("_Char_char", 2, CharConstructor); @@ -500,6 +532,22 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Char_copy__Char", 2, CharCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + + { + auto function = CreateMethodFunction("_Char_copy__char", 2, CharCopyAssignmentFromChar); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // Byte methods { auto function = CreateMethodFunction("_Byte_byte", 2, ByteConstructor); @@ -557,6 +605,22 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Byte_copy__Byte", 2, ByteCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + + { + auto function = CreateMethodFunction("_Byte_copy__byte", 2, ByteCopyAssignmentFromByte); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // Bool methods { auto function = CreateMethodFunction("_Bool_bool", 2, BoolConstructor); @@ -614,6 +678,22 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Bool_copy__Bool", 2, BoolCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + + { + auto function = CreateMethodFunction("_Bool_copy__bool", 2, BoolCopyAssignmentFromBool); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // Nullable methods { auto function = CreateMethodFunction("_Nullable_Object", 2, NullableConstructor); @@ -696,6 +776,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_String_copy__String", 2, StringCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // IntArray methods { auto function = CreateMethodFunction("_IntArray_int_int", 3, IntArrayConstructor); @@ -825,6 +913,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_IntArray_copy__IntArray", 2, IntArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // FloatArray methods { auto function = CreateMethodFunction("_FloatArray_int_float", 3, FloatArrayConstructor); @@ -954,6 +1050,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_FloatArray_copy__FloatArray", 2, FloatArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // CharArray methods { auto function = CreateMethodFunction("_CharArray_int_char", 3, CharArrayConstructor); @@ -1083,6 +1187,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_CharArray_copy__CharArray", 2, CharArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // ByteArray methods { auto function = CreateMethodFunction("_ByteArray_int_byte", 3, ByteArrayConstructor); @@ -1212,6 +1324,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_ByteArray_copy__ByteArray", 2, ByteArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // ByteArray constructor from Object (creates a view) { auto function = CreateMethodFunction("_ByteArray_Object", 2, ByteArrayFromObject); @@ -1350,6 +1470,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_BoolArray_copy__BoolArray", 2, BoolArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // ObjectArray methods { auto function = CreateMethodFunction("_ObjectArray_int_Object", 3, ObjectArrayConstructor); @@ -1479,6 +1607,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_ObjectArray_copy__ObjectArray", 2, ObjectArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // StringArray methods { auto function = CreateMethodFunction("_StringArray_int_String", 3, StringArrayConstructor); @@ -1608,6 +1744,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_StringArray_copy__StringArray", 2, StringArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // PointerArray methods { auto function = CreateMethodFunction("_PointerArray_int_Pointer", 3, PointerArrayConstructor); @@ -1698,6 +1842,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_Pointer_copy__Pointer", 2, PointerCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + { auto function = CreateMethodFunction("_PointerArray_Length_", 1, PointerArrayLength); auto result = repository.Add(std::move(function)); @@ -1786,6 +1938,14 @@ std::expected RegisterBuiltinFunctions(FunctionReposit } } + { + auto function = CreateMethodFunction("_PointerArray_copy__PointerArray", 2, PointerArrayCopyAssignment); + auto result = repository.Add(std::move(function)); + if (!result.has_value()) { + return std::unexpected(result.error()); + } + } + // File methods { auto function = CreateMethodFunction("_File", 1, FileConstructor); diff --git a/tests/builtin_functions_tests.cpp b/tests/builtin_functions_tests.cpp index bd1cdd1..a6bae15 100644 --- a/tests/builtin_functions_tests.cpp +++ b/tests/builtin_functions_tests.cpp @@ -499,3 +499,352 @@ TEST_F(BuiltinTestSuite, FileMethods) { EXPECT_TRUE(ExecuteFunction(*this, "_File_Close_", file_obj).has_value()); } + +TEST_F(BuiltinTestSuite, CopyAssignmentMethods) { + // Test Int copy assignment from Int + { + constexpr int64_t kInitialValue = 10; + constexpr int64_t kAssignedValue = 20; + void* int_obj1 = AllocateObjectByName(*this, "Int"); + void* int_obj2 = AllocateObjectByName(*this, "Int"); + ASSERT_NE(int_obj1, nullptr); + ASSERT_NE(int_obj2, nullptr); + *ovum::vm::runtime::GetDataPointer(int_obj1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(int_obj2) = kAssignedValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Int_copy__Int", int_obj1, int_obj2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(int_obj1), kAssignedValue); + } + + // Test Int copy assignment from int (fundamental) + { + constexpr int64_t kInitialValue = 5; + constexpr int64_t kAssignedValue = 15; + void* int_obj = AllocateObjectByName(*this, "Int"); + ASSERT_NE(int_obj, nullptr); + *ovum::vm::runtime::GetDataPointer(int_obj) = kInitialValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Int_copy__int", int_obj, kAssignedValue).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(int_obj), kAssignedValue); + } + + // Test Float copy assignment from Float + { + constexpr double kInitialValue = 1.5; + constexpr double kAssignedValue = 2.75; + void* float_obj1 = AllocateObjectByName(*this, "Float"); + void* float_obj2 = AllocateObjectByName(*this, "Float"); + ASSERT_NE(float_obj1, nullptr); + ASSERT_NE(float_obj2, nullptr); + *ovum::vm::runtime::GetDataPointer(float_obj1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(float_obj2) = kAssignedValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Float_copy__Float", float_obj1, float_obj2).has_value()); + EXPECT_DOUBLE_EQ(*ovum::vm::runtime::GetDataPointer(float_obj1), kAssignedValue); + } + + // Test Float copy assignment from float (fundamental) + { + constexpr double kInitialValue = 0.5; + constexpr double kAssignedValue = 3.14; + void* float_obj = AllocateObjectByName(*this, "Float"); + ASSERT_NE(float_obj, nullptr); + *ovum::vm::runtime::GetDataPointer(float_obj) = kInitialValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Float_copy__float", float_obj, kAssignedValue).has_value()); + EXPECT_DOUBLE_EQ(*ovum::vm::runtime::GetDataPointer(float_obj), kAssignedValue); + } + + // Test Char copy assignment from Char + { + constexpr char kInitialValue = 'a'; + constexpr char kAssignedValue = 'z'; + void* char_obj1 = AllocateObjectByName(*this, "Char"); + void* char_obj2 = AllocateObjectByName(*this, "Char"); + ASSERT_NE(char_obj1, nullptr); + ASSERT_NE(char_obj2, nullptr); + *ovum::vm::runtime::GetDataPointer(char_obj1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(char_obj2) = kAssignedValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Char_copy__Char", char_obj1, char_obj2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(char_obj1), kAssignedValue); + } + + // Test Char copy assignment from char (fundamental) + { + constexpr char kInitialValue = 'x'; + constexpr char kAssignedValue = 'y'; + void* char_obj = AllocateObjectByName(*this, "Char"); + ASSERT_NE(char_obj, nullptr); + *ovum::vm::runtime::GetDataPointer(char_obj) = kInitialValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Char_copy__char", char_obj, kAssignedValue).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(char_obj), kAssignedValue); + } + + // Test Byte copy assignment from Byte + { + constexpr uint8_t kInitialValue = 0x10; + constexpr uint8_t kAssignedValue = 0xFF; + void* byte_obj1 = AllocateObjectByName(*this, "Byte"); + void* byte_obj2 = AllocateObjectByName(*this, "Byte"); + ASSERT_NE(byte_obj1, nullptr); + ASSERT_NE(byte_obj2, nullptr); + *ovum::vm::runtime::GetDataPointer(byte_obj1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(byte_obj2) = kAssignedValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Byte_copy__Byte", byte_obj1, byte_obj2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(byte_obj1), kAssignedValue); + } + + // Test Byte copy assignment from byte (fundamental) + { + constexpr uint8_t kInitialValue = 0x00; + constexpr uint8_t kAssignedValue = 0xAB; + void* byte_obj = AllocateObjectByName(*this, "Byte"); + ASSERT_NE(byte_obj, nullptr); + *ovum::vm::runtime::GetDataPointer(byte_obj) = kInitialValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Byte_copy__byte", byte_obj, kAssignedValue).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(byte_obj), kAssignedValue); + } + + // Test Bool copy assignment from Bool + { + constexpr bool kInitialValue = false; + constexpr bool kAssignedValue = true; + void* bool_obj1 = AllocateObjectByName(*this, "Bool"); + void* bool_obj2 = AllocateObjectByName(*this, "Bool"); + ASSERT_NE(bool_obj1, nullptr); + ASSERT_NE(bool_obj2, nullptr); + *ovum::vm::runtime::GetDataPointer(bool_obj1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(bool_obj2) = kAssignedValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Bool_copy__Bool", bool_obj1, bool_obj2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(bool_obj1), kAssignedValue); + } + + // Test Bool copy assignment from bool (fundamental) + { + constexpr bool kInitialValue = true; + constexpr bool kAssignedValue = false; + void* bool_obj = AllocateObjectByName(*this, "Bool"); + ASSERT_NE(bool_obj, nullptr); + *ovum::vm::runtime::GetDataPointer(bool_obj) = kInitialValue; + + ASSERT_TRUE(ExecuteFunction(*this, "_Bool_copy__bool", bool_obj, kAssignedValue).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(bool_obj), kAssignedValue); + } + + // Test String copy assignment from String + { + constexpr std::string_view kInitialText = "initial"; + constexpr std::string_view kAssignedText = "assigned"; + void* string_obj1 = AllocateObjectByName(*this, "String"); + void* string_obj2 = AllocateObjectByName(*this, "String"); + ASSERT_NE(string_obj1, nullptr); + ASSERT_NE(string_obj2, nullptr); + new (ovum::vm::runtime::GetDataPointer(string_obj1)) std::string(kInitialText); + new (ovum::vm::runtime::GetDataPointer(string_obj2)) std::string(kAssignedText); + + ASSERT_TRUE(ExecuteFunction(*this, "_String_copy__String", string_obj1, string_obj2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(string_obj1), kAssignedText); + } + + // Test IntArray copy assignment + { + constexpr int64_t kSize = 3; + constexpr int64_t kDefault1 = 1; + constexpr int64_t kDefault2 = 2; + void* array1 = AllocateObjectByName(*this, "IntArray"); + void* array2 = AllocateObjectByName(*this, "IntArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_IntArray_int_int", array1, kSize, kDefault1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_IntArray_int_int", array2, kSize, kDefault2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_IntArray_copy__IntArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test FloatArray copy assignment + { + constexpr int64_t kSize = 2; + constexpr double kDefault1 = 1.1; + constexpr double kDefault2 = 2.2; + void* array1 = AllocateObjectByName(*this, "FloatArray"); + void* array2 = AllocateObjectByName(*this, "FloatArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_FloatArray_int_float", array1, kSize, kDefault1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_FloatArray_int_float", array2, kSize, kDefault2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_FloatArray_copy__FloatArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test CharArray copy assignment + { + constexpr int64_t kSize = 2; + constexpr char kDefault1 = 'a'; + constexpr char kDefault2 = 'b'; + void* array1 = AllocateObjectByName(*this, "CharArray"); + void* array2 = AllocateObjectByName(*this, "CharArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_CharArray_int_char", array1, kSize, kDefault1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_CharArray_int_char", array2, kSize, kDefault2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_CharArray_copy__CharArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test ByteArray copy assignment + { + constexpr int64_t kSize = 3; + constexpr uint8_t kDefault1 = 0x01; + constexpr uint8_t kDefault2 = 0x02; + void* array1 = AllocateObjectByName(*this, "ByteArray"); + void* array2 = AllocateObjectByName(*this, "ByteArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_ByteArray_int_byte", array1, kSize, kDefault1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_ByteArray_int_byte", array2, kSize, kDefault2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_ByteArray_copy__ByteArray", array1, array2).has_value()); + auto* ba1 = ovum::vm::runtime::GetDataPointer(array1); + auto* ba2 = ovum::vm::runtime::GetDataPointer(array2); + EXPECT_EQ(*ba1, *ba2); + } + + // Test BoolArray copy assignment + { + constexpr int64_t kSize = 2; + constexpr bool kDefault1 = false; + constexpr bool kDefault2 = true; + void* array1 = AllocateObjectByName(*this, "BoolArray"); + void* array2 = AllocateObjectByName(*this, "BoolArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_BoolArray_int_bool", array1, kSize, kDefault1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_BoolArray_int_bool", array2, kSize, kDefault2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_BoolArray_copy__BoolArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test ObjectArray copy assignment + { + constexpr int64_t kSize = 1; + void* str1 = AllocateObjectByName(*this, "String"); + void* str2 = AllocateObjectByName(*this, "String"); + ASSERT_NE(str1, nullptr); + ASSERT_NE(str2, nullptr); + new (ovum::vm::runtime::GetDataPointer(str1)) std::string("first"); + new (ovum::vm::runtime::GetDataPointer(str2)) std::string("second"); + + void* array1 = AllocateObjectByName(*this, "ObjectArray"); + void* array2 = AllocateObjectByName(*this, "ObjectArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_ObjectArray_int_Object", array1, kSize, str1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_ObjectArray_int_Object", array2, kSize, str2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_ObjectArray_copy__ObjectArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test StringArray copy assignment + { + constexpr int64_t kSize = 1; + void* str1 = AllocateObjectByName(*this, "String"); + void* str2 = AllocateObjectByName(*this, "String"); + ASSERT_NE(str1, nullptr); + ASSERT_NE(str2, nullptr); + new (ovum::vm::runtime::GetDataPointer(str1)) std::string("first"); + new (ovum::vm::runtime::GetDataPointer(str2)) std::string("second"); + + void* array1 = AllocateObjectByName(*this, "StringArray"); + void* array2 = AllocateObjectByName(*this, "StringArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_StringArray_int_String", array1, kSize, str1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_StringArray_int_String", array2, kSize, str2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_StringArray_copy__StringArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } + + // Test Pointer copy assignment + { + constexpr int64_t kInitialValue = 10; + constexpr int64_t kAssignedValue = 20; + void* pointee1 = AllocateObjectByName(*this, "Int"); + void* pointee2 = AllocateObjectByName(*this, "Int"); + ASSERT_NE(pointee1, nullptr); + ASSERT_NE(pointee2, nullptr); + *ovum::vm::runtime::GetDataPointer(pointee1) = kInitialValue; + *ovum::vm::runtime::GetDataPointer(pointee2) = kAssignedValue; + + void* pointer1 = AllocateObjectByName(*this, "Pointer"); + void* pointer2 = AllocateObjectByName(*this, "Pointer"); + ASSERT_NE(pointer1, nullptr); + ASSERT_NE(pointer2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_Pointer_pointer", pointer1, pointee1).has_value()); + ExpectStackTopPointer(*this, pointer1); + ASSERT_TRUE(ExecuteFunction(*this, "_Pointer_pointer", pointer2, pointee2).has_value()); + ExpectStackTopPointer(*this, pointer2); + + ASSERT_TRUE(ExecuteFunction(*this, "_Pointer_copy__Pointer", pointer1, pointer2).has_value()); + EXPECT_EQ(*ovum::vm::runtime::GetDataPointer(pointer1), pointee2); + } + + // Test PointerArray copy assignment + { + constexpr int64_t kSize = 1; + void* pointee1 = AllocateObjectByName(*this, "Int"); + void* pointee2 = AllocateObjectByName(*this, "Int"); + ASSERT_NE(pointee1, nullptr); + ASSERT_NE(pointee2, nullptr); + + void* array1 = AllocateObjectByName(*this, "PointerArray"); + void* array2 = AllocateObjectByName(*this, "PointerArray"); + ASSERT_NE(array1, nullptr); + ASSERT_NE(array2, nullptr); + ASSERT_TRUE(ExecuteFunction(*this, "_PointerArray_int_Pointer", array1, kSize, pointee1).has_value()); + ExpectStackTopPointer(*this, array1); + ASSERT_TRUE(ExecuteFunction(*this, "_PointerArray_int_Pointer", array2, kSize, pointee2).has_value()); + ExpectStackTopPointer(*this, array2); + + ASSERT_TRUE(ExecuteFunction(*this, "_PointerArray_copy__PointerArray", array1, array2).has_value()); + auto* vec1 = ovum::vm::runtime::GetDataPointer>(array1); + auto* vec2 = ovum::vm::runtime::GetDataPointer>(array2); + EXPECT_EQ(*vec1, *vec2); + } +} From bdfe350094e5cf18619ea79ebb44ae5f06c5d960 Mon Sep 17 00:00:00 2001 From: sashbek Date: Tue, 13 Jan 2026 16:03:24 +0300 Subject: [PATCH 12/22] fix: non const IJITExecutor::TryCompile --- lib/executor/IJitExecutor.hpp | 2 +- lib/executor/PlaceholderJitExecutor.cpp | 2 +- lib/executor/PlaceholderJitExecutor.hpp | 2 +- tests/test_data/examples | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/executor/IJitExecutor.hpp b/lib/executor/IJitExecutor.hpp index f89e878..f6152a0 100644 --- a/lib/executor/IJitExecutor.hpp +++ b/lib/executor/IJitExecutor.hpp @@ -15,7 +15,7 @@ class IJitExecutor { // NOLINT(cppcoreguidelines-special-member-functions) public: virtual ~IJitExecutor() = default; - [[nodiscard]] virtual bool TryCompile() const = 0; + [[nodiscard]] virtual bool TryCompile() = 0; [[nodiscard]] virtual std::expected Run(execution_tree::PassedExecutionData& data) = 0; }; diff --git a/lib/executor/PlaceholderJitExecutor.cpp b/lib/executor/PlaceholderJitExecutor.cpp index a156374..d19c5e5 100644 --- a/lib/executor/PlaceholderJitExecutor.cpp +++ b/lib/executor/PlaceholderJitExecutor.cpp @@ -2,7 +2,7 @@ namespace ovum::vm::executor { -bool PlaceholderJitExecutor::TryCompile() const { +bool PlaceholderJitExecutor::TryCompile() { return false; } diff --git a/lib/executor/PlaceholderJitExecutor.hpp b/lib/executor/PlaceholderJitExecutor.hpp index 5ced551..1d862c3 100644 --- a/lib/executor/PlaceholderJitExecutor.hpp +++ b/lib/executor/PlaceholderJitExecutor.hpp @@ -11,7 +11,7 @@ class PlaceholderJitExecutor : public IJitExecutor { public: PlaceholderJitExecutor() = default; - [[nodiscard]] bool TryCompile() const override; + [[nodiscard]] bool TryCompile() override; [[nodiscard]] std::expected Run(execution_tree::PassedExecutionData& data) override; }; diff --git a/tests/test_data/examples b/tests/test_data/examples index 51cb78d..ea0bb00 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 51cb78d7778c97ac9d4561a210a003e94b59fe83 +Subproject commit ea0bb00fec0fbac94eb5e2881bc60285255bca32 From a76ca0676d8f60a2c7f04a4bc79626447fe3b710 Mon Sep 17 00:00:00 2001 From: sashbek Date: Wed, 14 Jan 2026 01:45:58 +0300 Subject: [PATCH 13/22] merge: solved conflict with div branches --- tests/CMakeLists.txt | 20 ++++++++++ tests/jit_tests.cpp | 18 +++++++++ tests/test_data/examples | 2 +- tests/test_suites/BytecodeParserTestSuite.hpp | 3 +- tests/test_suites/X64JitTestSuite.cpp | 39 +++++++++++++++++++ tests/test_suites/X64JitTestSuite.hpp | 29 ++++++++++++++ 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 tests/jit_tests.cpp create mode 100644 tests/test_suites/X64JitTestSuite.cpp create mode 100644 tests/test_suites/X64JitTestSuite.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11bbab1..449a1eb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,24 @@ enable_testing() + + +If (JIT_PROVIDED) +add_executable( + ${PROJECT_NAME}_tests + main_test.cpp + test_functions.cpp + test_suites/ProjectIntegrationTestSuite.cpp + test_suites/X64JitTestSuite.cpp + test_suites/BytecodeLexerTestSuite.cpp + test_suites/BytecodeParserTestSuite.cpp + test_suites/BuiltinTestSuite.cpp + bytecode_lexer_tests.cpp + bytecode_parser_tests.cpp + bytecode_commands_tests.cpp + builtin_functions_tests.cpp + jit_tests.cpp +) +else() add_executable( ${PROJECT_NAME}_tests main_test.cpp @@ -15,6 +34,7 @@ add_executable( gc_tests.cpp test_suites/GcTestSuite.cpp ) +endif() target_link_libraries( ${PROJECT_NAME}_tests diff --git a/tests/jit_tests.cpp b/tests/jit_tests.cpp new file mode 100644 index 0000000..b82b04b --- /dev/null +++ b/tests/jit_tests.cpp @@ -0,0 +1,18 @@ + +#include + +#include "lib/vm_ui/vm_ui_functions.hpp" +#include "test_functions.hpp" +#include "test_suites/X64JitTestSuite.hpp" + +TEST_F(X64JitTestSuite, Test1) { + RunSingleTest(JitTestData{ + .test_name = "jit-float-test.oil", + .arguments = "", + .input = "", + .expected_output = "", + .expected_error = "", + .expected_return_code = 0, + .jit_action_bound = 1, + }); +} \ No newline at end of file diff --git a/tests/test_data/examples b/tests/test_data/examples index ea0bb00..1b99591 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit ea0bb00fec0fbac94eb5e2881bc60285255bca32 +Subproject commit 1b99591bf42da096b45f7fe19b3944152118de9e diff --git a/tests/test_suites/BytecodeParserTestSuite.hpp b/tests/test_suites/BytecodeParserTestSuite.hpp index 6834a8d..6a88b16 100644 --- a/tests/test_suites/BytecodeParserTestSuite.hpp +++ b/tests/test_suites/BytecodeParserTestSuite.hpp @@ -14,10 +14,11 @@ #include "lib/execution_tree/Block.hpp" #include "lib/execution_tree/FunctionRepository.hpp" #include "lib/execution_tree/IFunctionExecutable.hpp" -#include "lib/executor/PlaceholderJitExecutorFactory.hpp" #include "lib/runtime/RuntimeMemory.hpp" #include "lib/runtime/VirtualTableRepository.hpp" +#include "lib/executor/PlaceholderJitExecutorFactory.hpp" + struct BytecodeParserTestSuite : public testing::Test { static constexpr size_t kJitBoundary = 10; void SetUp() override; diff --git a/tests/test_suites/X64JitTestSuite.cpp b/tests/test_suites/X64JitTestSuite.cpp new file mode 100644 index 0000000..853f68a --- /dev/null +++ b/tests/test_suites/X64JitTestSuite.cpp @@ -0,0 +1,39 @@ +#include "X64JitTestSuite.hpp" + +#include + +#include "lib/vm_ui/vm_ui_functions.hpp" +#include "tests/test_functions.hpp" + +void X64JitTestSuite::SetUp() { + std::filesystem::create_directories(kTemporaryDirectoryName); +} + +void X64JitTestSuite::TearDown() { + std::filesystem::remove_all(kTemporaryDirectoryName); +} + +void X64JitTestSuite::RunSingleTest(const JitTestData& test_data) const { + std::filesystem::path test_file = kTestDataDir; + test_file /= "examples"; + test_file /= "compiled"; + test_file /= "jit"; + test_file /= test_data.test_name; + std::string cmd = "ovum-vm -f \""; + cmd += test_file.string(); + cmd += "\""; + cmd += " -j "; + cmd += std::to_string(test_data.jit_action_bound); + + if (!test_data.arguments.empty()) { + cmd += " -- "; + cmd += test_data.arguments; + } + + std::istringstream in(test_data.input); + std::ostringstream out; + std::ostringstream err; + ASSERT_EQ(StartVmConsoleUI(SplitString(cmd), std::cout, in, std::cout), test_data.expected_return_code); + ASSERT_EQ(out.str(), test_data.expected_output); + ASSERT_EQ(err.str(), test_data.expected_error); +} diff --git a/tests/test_suites/X64JitTestSuite.hpp b/tests/test_suites/X64JitTestSuite.hpp new file mode 100644 index 0000000..a96b384 --- /dev/null +++ b/tests/test_suites/X64JitTestSuite.hpp @@ -0,0 +1,29 @@ +#ifndef X64JITTESTSUITE_HPP_ +#define X64JITTESTSUITE_HPP_ + +#include + +#include + +struct JitTestData { + std::string test_name; + std::string arguments; + std::string input; + std::string expected_output; + std::string expected_error; + int32_t expected_return_code = 0; + uint64_t jit_action_bound = 0; +}; + +struct X64JitTestSuite : public testing::Test { // special test structure + const std::string kTemporaryDirectoryName = "./gtest_tmp"; + const std::string kTestDataDir = TEST_DATA_DIR; + + void SetUp() override; // method that is called at the beginning of every test + + void TearDown() override; // method that is called at the end of every test + + void RunSingleTest(const JitTestData& test_data) const; +}; + +#endif // X64JITTESTSUITE_HPP_ From 29e2ddf0e608e8aa6917a300e7f8c60aa37628a6 Mon Sep 17 00:00:00 2001 From: bialger Date: Wed, 14 Jan 2026 03:40:08 +0300 Subject: [PATCH 14/22] fix: correct argument extraction order in FormatDateTime and enhance class name parsing in CallConstructor --- lib/execution_tree/BytecodeCommands.cpp | 8 +++++--- tests/bytecode_commands_tests.cpp | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/execution_tree/BytecodeCommands.cpp b/lib/execution_tree/BytecodeCommands.cpp index c8a540e..9afb1df 100644 --- a/lib/execution_tree/BytecodeCommands.cpp +++ b/lib/execution_tree/BytecodeCommands.cpp @@ -1201,6 +1201,8 @@ std::expected CallConstructor(PassedExecuti if (first_underscore != std::string::npos && second_underscore != std::string::npos && second_underscore > first_underscore + 1) { class_name = constructor_name.substr(first_underscore + 1, second_underscore - first_underscore - 1); + } else if (first_underscore != std::string::npos) { + class_name = constructor_name.substr(first_underscore + 1); } else { class_name = constructor_name; } @@ -1575,15 +1577,15 @@ std::expected NanoTime(PassedExecutionData& } std::expected FormatDateTime(PassedExecutionData& data) { - auto arguments = TryExtractTwoArguments(data, "FormatDateTime"); + auto arguments = TryExtractTwoArguments(data, "FormatDateTime"); if (!arguments) { return std::unexpected(arguments.error()); } - auto timestamp_var = arguments.value().second; + auto timestamp_var = arguments.value().first; - void* string_obj1 = arguments.value().first; + void* string_obj1 = arguments.value().second; auto* format_str_ptr = runtime::GetDataPointer(string_obj1); try { diff --git a/tests/bytecode_commands_tests.cpp b/tests/bytecode_commands_tests.cpp index d7495fa..2067e75 100644 --- a/tests/bytecode_commands_tests.cpp +++ b/tests/bytecode_commands_tests.cpp @@ -1053,6 +1053,7 @@ TEST_F(BuiltinTestSuite, TimeCommands) { constexpr int64_t kSleepMs = 1; constexpr int64_t kEpochSeconds = 0; constexpr std::string_view kYearFormat = "%Y"; + constexpr std::string_view kFormatString = "%Y-%m-%d %H:%M:%S.%f"; constexpr std::string_view kDateFormat = "%Y-%m-%d"; constexpr std::string_view kEpochDate = "1970-01-01"; constexpr int64_t kPositiveThreshold = 0; @@ -1085,9 +1086,9 @@ TEST_F(BuiltinTestSuite, TimeCommands) { auto nano_val = PopInt(); EXPECT_GT(nano_val, kPositiveThreshold); - auto format_str = MakeString(std::string{kYearFormat}); - PushInt(kEpochSeconds); + auto format_str = MakeString(std::string{kFormatString}); PushObject(format_str); + PushInt(second); auto format_cmd = MakeSimple("FormatDateTime"); ASSERT_TRUE(format_cmd); EXPECT_TRUE(format_cmd->Execute(data_).has_value()); From 44dbe7330b8bd766f286a3fd009047d0d7ceca83 Mon Sep 17 00:00:00 2001 From: bialger Date: Wed, 14 Jan 2026 12:08:06 +0300 Subject: [PATCH 15/22] fix: improve FloatToString precision handling by using stringstream for better formatting --- lib/execution_tree/BytecodeCommands.cpp | 6 +++++- tests/test_data/examples | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/execution_tree/BytecodeCommands.cpp b/lib/execution_tree/BytecodeCommands.cpp index 9afb1df..3570b64 100644 --- a/lib/execution_tree/BytecodeCommands.cpp +++ b/lib/execution_tree/BytecodeCommands.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -1006,7 +1007,10 @@ std::expected FloatToString(PassedExecution return std::unexpected(argument.error()); } - return PushString(data, std::to_string(argument.value())); + std::ostringstream oss; + oss << std::setprecision(std::numeric_limits::max_digits10 - 2) << argument.value(); + + return PushString(data, oss.str()); } std::expected IntToFloat(PassedExecutionData& data) { diff --git a/tests/test_data/examples b/tests/test_data/examples index 1b99591..1c168c2 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 1b99591bf42da096b45f7fe19b3944152118de9e +Subproject commit 1c168c29c0d6a9003a0d1b873791fd30f69a98fc From b36c1928ea3fee6c44989f31767e5181157d2aea Mon Sep 17 00:00:00 2001 From: bialger Date: Wed, 14 Jan 2026 12:33:08 +0300 Subject: [PATCH 16/22] fix: correct float string conversion to match expected precision in tests --- tests/bytecode_commands_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bytecode_commands_tests.cpp b/tests/bytecode_commands_tests.cpp index 2067e75..ac16f87 100644 --- a/tests/bytecode_commands_tests.cpp +++ b/tests/bytecode_commands_tests.cpp @@ -722,7 +722,7 @@ TEST_F(BuiltinTestSuite, StringAndNumericConversions) { constexpr int64_t kIntToStrValue = 42; constexpr double kFloatToStrValue = 2.5; constexpr std::string_view kIntToStrExpected = "42"; - constexpr std::string_view kFloatToStrExpected = "2.500000"; + constexpr std::string_view kFloatToStrExpected = "2.5"; auto push_num_str = MakeString(std::string{kIntString}); PushObject(push_num_str); From 89d335a937f90f8cdd85eebcb6b21c4af3f6403d Mon Sep 17 00:00:00 2001 From: sashbek Date: Thu, 15 Jan 2026 14:07:14 +0300 Subject: [PATCH 17/22] fix: stack frame for jit executable --- lib/execution_tree/JitCompilingFunction.hpp | 14 ++++++++++++++ tests/test_data/examples | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/execution_tree/JitCompilingFunction.hpp b/lib/execution_tree/JitCompilingFunction.hpp index 289cb57..063884c 100644 --- a/lib/execution_tree/JitCompilingFunction.hpp +++ b/lib/execution_tree/JitCompilingFunction.hpp @@ -26,8 +26,22 @@ class JitCompilingFunction : public IFunctionExecutable { std::expected Execute(PassedExecutionData& execution_data) override { if (function_.GetTotalActionCount() > jit_action_boundary_) { if (executor_->TryCompile()) { + + runtime::StackFrame local_frame{}; + local_frame.function_name = function_.GetId(); + local_frame.local_variables.reserve(function_.GetArity()); + + for (size_t i = 0; i < function_.GetArity(); ++i) { + local_frame.local_variables.emplace_back(execution_data.memory.machine_stack.top()); + execution_data.memory.machine_stack.pop(); + } + + execution_data.memory.stack_frames.push(std::move(local_frame)); + const std::expected jit_result = executor_->Run(execution_data); + execution_data.memory.stack_frames.pop(); + if (jit_result.has_value()) { return ExecutionResult::kNormal; } diff --git a/tests/test_data/examples b/tests/test_data/examples index 1c168c2..e14bfe0 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 1c168c29c0d6a9003a0d1b873791fd30f69a98fc +Subproject commit e14bfe0e528292cf2a596813b89213126884fb9f From 801c8e10991d0ac57762f9d277f2ad602fa3647d Mon Sep 17 00:00:00 2001 From: sashbek Date: Thu, 15 Jan 2026 14:07:37 +0300 Subject: [PATCH 18/22] tests: two tests for jit to compare time --- tests/jit_tests.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/jit_tests.cpp b/tests/jit_tests.cpp index b82b04b..18f939f 100644 --- a/tests/jit_tests.cpp +++ b/tests/jit_tests.cpp @@ -5,7 +5,7 @@ #include "test_functions.hpp" #include "test_suites/X64JitTestSuite.hpp" -TEST_F(X64JitTestSuite, Test1) { +TEST_F(X64JitTestSuite, Test1JitEnable) { RunSingleTest(JitTestData{ .test_name = "jit-float-test.oil", .arguments = "", @@ -15,4 +15,16 @@ TEST_F(X64JitTestSuite, Test1) { .expected_return_code = 0, .jit_action_bound = 1, }); +} + +TEST_F(X64JitTestSuite, Test2JitDisable) { + RunSingleTest(JitTestData{ + .test_name = "jit-float-test.oil", + .arguments = "", + .input = "", + .expected_output = "", + .expected_error = "", + .expected_return_code = 0, + .jit_action_bound = 10000000, + }); } \ No newline at end of file From 4455e3a2eec819997772d1636c34d4425c4592fb Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 15:40:09 +0300 Subject: [PATCH 19/22] refactor: clean up whitespace in JIT execution logic and improve test readability --- lib/execution_tree/JitCompilingFunction.hpp | 7 +++---- tests/jit_tests.cpp | 7 +++---- tests/test_data/examples | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/execution_tree/JitCompilingFunction.hpp b/lib/execution_tree/JitCompilingFunction.hpp index 063884c..ac66b5b 100644 --- a/lib/execution_tree/JitCompilingFunction.hpp +++ b/lib/execution_tree/JitCompilingFunction.hpp @@ -26,22 +26,21 @@ class JitCompilingFunction : public IFunctionExecutable { std::expected Execute(PassedExecutionData& execution_data) override { if (function_.GetTotalActionCount() > jit_action_boundary_) { if (executor_->TryCompile()) { - runtime::StackFrame local_frame{}; local_frame.function_name = function_.GetId(); local_frame.local_variables.reserve(function_.GetArity()); - + for (size_t i = 0; i < function_.GetArity(); ++i) { local_frame.local_variables.emplace_back(execution_data.memory.machine_stack.top()); execution_data.memory.machine_stack.pop(); } - + execution_data.memory.stack_frames.push(std::move(local_frame)); const std::expected jit_result = executor_->Run(execution_data); execution_data.memory.stack_frames.pop(); - + if (jit_result.has_value()) { return ExecutionResult::kNormal; } diff --git a/tests/jit_tests.cpp b/tests/jit_tests.cpp index 18f939f..cf82810 100644 --- a/tests/jit_tests.cpp +++ b/tests/jit_tests.cpp @@ -1,8 +1,6 @@ #include -#include "lib/vm_ui/vm_ui_functions.hpp" -#include "test_functions.hpp" #include "test_suites/X64JitTestSuite.hpp" TEST_F(X64JitTestSuite, Test1JitEnable) { @@ -18,6 +16,7 @@ TEST_F(X64JitTestSuite, Test1JitEnable) { } TEST_F(X64JitTestSuite, Test2JitDisable) { + constexpr size_t kJitActionBound = 10000000; RunSingleTest(JitTestData{ .test_name = "jit-float-test.oil", .arguments = "", @@ -25,6 +24,6 @@ TEST_F(X64JitTestSuite, Test2JitDisable) { .expected_output = "", .expected_error = "", .expected_return_code = 0, - .jit_action_bound = 10000000, + .jit_action_bound = kJitActionBound, }); -} \ No newline at end of file +} diff --git a/tests/test_data/examples b/tests/test_data/examples index e14bfe0..3e0f26f 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit e14bfe0e528292cf2a596813b89213126884fb9f +Subproject commit 3e0f26f0e47d78b97b75d8943ff73a32f0c354fb From f846d92cd89e87eda8fdb3485a732eeb6f355e2a Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 15:48:23 +0300 Subject: [PATCH 20/22] docs: update README to clarify JIT compiler availability for x86_64 architecture --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b53b82..785f0e6 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The VM consists of several core components: - **System Interface**: Provides access to system functions and FFI > **Note**: Due to the architecture-dependent nature of JIT compilation, JIT implementations are located in separate repositories for each target architecture. +> **Note**: The JIT compiler is currently only available for x86_64 architecture. ## Quick Start @@ -112,7 +113,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines. - **[Ovum Language](https://github.com/Ovum-Programming-Language/OvumLanguage)**: The main Ovum programming language repository - **[Ovum Documentation](https://ovum-programming-language.github.io/OvumDocs/)**: Complete language and VM documentation -- **JIT Implementations**: Architecture-specific JIT compilers (separate repositories) +- **[Ovum JIT X64](https://github.com/Ovum-Programming-Language/OvumJitX64)**: JIT compiler for x86_64 architecture ## Language Features Supported From 3834e258637216a7a11f1f87a3ba3b489a4e210d Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 16:06:54 +0300 Subject: [PATCH 21/22] docs: add link to OvumExamples repository in README for additional resources --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 785f0e6..9f38c2c 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines. - **[Ovum Language](https://github.com/Ovum-Programming-Language/OvumLanguage)**: The main Ovum programming language repository - **[Ovum Documentation](https://ovum-programming-language.github.io/OvumDocs/)**: Complete language and VM documentation - **[Ovum JIT X64](https://github.com/Ovum-Programming-Language/OvumJitX64)**: JIT compiler for x86_64 architecture +- **[OvumExamples](https://github.com/Ovum-Programming-Language/OvumExamples)**: Examples for the Ovum programming language and Ovum Intermediate Language ## Language Features Supported From 97d7b182baab00be375d258c9f6ab6eefb5692da Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 17:38:16 +0300 Subject: [PATCH 22/22] fix: ensure sufficient arguments on stack for JIT-compiled function calls --- lib/execution_tree/JitCompilingFunction.hpp | 10 ++++++++++ tests/test_data/examples | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/execution_tree/JitCompilingFunction.hpp b/lib/execution_tree/JitCompilingFunction.hpp index ac66b5b..c9f1406 100644 --- a/lib/execution_tree/JitCompilingFunction.hpp +++ b/lib/execution_tree/JitCompilingFunction.hpp @@ -26,6 +26,11 @@ class JitCompilingFunction : public IFunctionExecutable { std::expected Execute(PassedExecutionData& execution_data) override { if (function_.GetTotalActionCount() > jit_action_boundary_) { if (executor_->TryCompile()) { + if (execution_data.memory.machine_stack.size() < function_.GetArity()) { + return std::unexpected(std::runtime_error( + "Not enough arguments on the stack to call JIT-compiled function " + function_.GetId())); + } + runtime::StackFrame local_frame{}; local_frame.function_name = function_.GetId(); local_frame.local_variables.reserve(function_.GetArity()); @@ -38,12 +43,17 @@ class JitCompilingFunction : public IFunctionExecutable { execution_data.memory.stack_frames.push(std::move(local_frame)); const std::expected jit_result = executor_->Run(execution_data); + const runtime::StackFrame& local_frame_ref = execution_data.memory.stack_frames.top(); execution_data.memory.stack_frames.pop(); if (jit_result.has_value()) { return ExecutionResult::kNormal; } + + for (size_t i = 0; i < function_.GetArity(); ++i) { + execution_data.memory.machine_stack.push(local_frame_ref.local_variables[function_.GetArity() - i - 1]); + } } } diff --git a/tests/test_data/examples b/tests/test_data/examples index 3e0f26f..28c2ab6 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 3e0f26f0e47d78b97b75d8943ff73a32f0c354fb +Subproject commit 28c2ab6170e182bb13a1ad9edd2308f5cb95c895