diff --git a/README.md b/README.md index 9b53b82..9f38c2c 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,8 @@ 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 +- **[OvumExamples](https://github.com/Ovum-Programming-Language/OvumExamples)**: Examples for the Ovum programming language and Ovum Intermediate Language ## Language Features Supported 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..8201caa 100644 --- a/cmake/SetCompilerOptions.cmake +++ b/cmake/SetCompilerOptions.cmake @@ -17,6 +17,18 @@ 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") + 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") + 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}") diff --git a/lib/bytecode_parser/ParsingSession.cpp b/lib/bytecode_parser/ParsingSession.cpp index b1b4b65..9b32be6 100644 --- a/lib/bytecode_parser/ParsingSession.cpp +++ b/lib/bytecode_parser/ParsingSession.cpp @@ -184,4 +184,25 @@ std::expected ParsingSession::ConsumeBoolLiteral() { std::to_string(token->GetPosition().GetColumn()))); } +std::vector ParsingSession::CopyUntilBlockEnd() { + std::vector 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]); + ++pos; + } + + return result; +} + } // namespace ovum::bytecode::parser diff --git a/lib/bytecode_parser/ParsingSession.hpp b/lib/bytecode_parser/ParsingSession.hpp index cad6d44..09019ed 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); + std::vector CopyUntilBlockEnd(); + 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..4acdfbe 100644 --- a/lib/bytecode_parser/scenarios/FunctionFactory.cpp +++ b/lib/bytecode_parser/scenarios/FunctionFactory.cpp @@ -20,13 +20,13 @@ vm::execution_tree::PureFunction FunctionFactory::WrapPure(Base&& base, template std::expected, std::runtime_error> FunctionFactory::WrapJit( - Base&& base) { + Base&& base, std::shared_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 +35,8 @@ std::unique_ptr FunctionFactory::Create std::unique_ptr body, bool pure, std::vector pure_argument_types, - bool no_jit) { + bool no_jit, + std::shared_ptr> jit_body) { RegularFunction regular = MakeRegular(id, arity, std::move(body)); if (!pure || pure_argument_types.empty()) { @@ -43,7 +44,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 +57,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..830ee9d 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" @@ -28,12 +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::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, @@ -44,7 +48,8 @@ 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::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 568cc32..be2a042 100644 --- a/lib/bytecode_parser/scenarios/FunctionParser.cpp +++ b/lib/bytecode_parser/scenarios/FunctionParser.cpp @@ -89,6 +89,12 @@ std::expected FunctionParser::Handle(ParsingSessionPt std::unique_ptr body = std::make_unique(); + 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()); while (!ctx->IsPunct('}') && !ctx->IsEof()) { @@ -112,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::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/execution_tree/BytecodeCommands.cpp b/lib/execution_tree/BytecodeCommands.cpp index c8a540e..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) { @@ -1201,6 +1205,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 +1581,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/lib/execution_tree/JitCompilingFunction.hpp b/lib/execution_tree/JitCompilingFunction.hpp index 8ae0a23..c9f1406 100644 --- a/lib/execution_tree/JitCompilingFunction.hpp +++ b/lib/execution_tree/JitCompilingFunction.hpp @@ -26,11 +26,34 @@ 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); + 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()); + + 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); + 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/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/CMakeLists.txt b/lib/executor/CMakeLists.txt index a117e5f..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) +target_link_libraries(executor PUBLIC runtime execution_tree tokens) + +if (JIT_PROVIDED) + target_link_libraries(executor PUBLIC jit) +endif () diff --git a/lib/executor/IJitExecutor.hpp b/lib/executor/IJitExecutor.hpp index 7ed3530..f6152a0 100644 --- a/lib/executor/IJitExecutor.hpp +++ b/lib/executor/IJitExecutor.hpp @@ -7,16 +7,17 @@ #include #include +#include "lib/execution_tree/PassedExecutionData.hpp" + namespace ovum::vm::executor { 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( - std::stack>& stack) = 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 291459e..5c6a0fa 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,8 @@ 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::shared_ptr> jit_body) const = 0; }; } // namespace ovum::vm::executor diff --git a/lib/executor/PlaceholderJitExecutor.cpp b/lib/executor/PlaceholderJitExecutor.cpp index 9d8a59c..d19c5e5 100644 --- a/lib/executor/PlaceholderJitExecutor.cpp +++ b/lib/executor/PlaceholderJitExecutor.cpp @@ -2,12 +2,11 @@ namespace ovum::vm::executor { -bool PlaceholderJitExecutor::TryCompile() const { +bool PlaceholderJitExecutor::TryCompile() { return false; } -std::expected PlaceholderJitExecutor::Run( - std::stack>& /* stack */) { +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 0e7d48c..1d862c3 100644 --- a/lib/executor/PlaceholderJitExecutor.hpp +++ b/lib/executor/PlaceholderJitExecutor.hpp @@ -3,16 +3,17 @@ #include "IJitExecutor.hpp" +#include "lib/execution_tree/PassedExecutionData.hpp" + namespace ovum::vm::executor { class PlaceholderJitExecutor : public IJitExecutor { public: PlaceholderJitExecutor() = default; - [[nodiscard]] bool TryCompile() const override; + [[nodiscard]] bool TryCompile() override; - [[nodiscard]] std::expected Run( - std::stack>& stack) 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 b3ee733..ec5f0a5 100644 --- a/lib/executor/PlaceholderJitExecutorFactory.cpp +++ b/lib/executor/PlaceholderJitExecutorFactory.cpp @@ -4,7 +4,8 @@ namespace ovum::vm::executor { -std::unique_ptr PlaceholderJitExecutorFactory::Create(const std::string&) 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 bc9f6f4..22193d8 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::shared_ptr>) const override; }; } // namespace ovum::vm::executor 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/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}) diff --git a/lib/vm_ui/vm_ui_functions.cpp b/lib/vm_ui/vm_ui_functions.cpp index 47f8614..7aa5410 100644 --- a/lib/vm_ui/vm_ui_functions.cpp +++ b/lib/vm_ui/vm_ui_functions.cpp @@ -56,21 +56,26 @@ 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.AddUnsignedLongLongArgument('m', "max-objects", "Maximum number of objects to keep in memory") .Default(kDefaultMaxObjects); - 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/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/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); + } +} diff --git a/tests/bytecode_commands_tests.cpp b/tests/bytecode_commands_tests.cpp index d7495fa..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); @@ -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()); diff --git a/tests/jit_tests.cpp b/tests/jit_tests.cpp new file mode 100644 index 0000000..cf82810 --- /dev/null +++ b/tests/jit_tests.cpp @@ -0,0 +1,29 @@ + +#include + +#include "test_suites/X64JitTestSuite.hpp" + +TEST_F(X64JitTestSuite, Test1JitEnable) { + RunSingleTest(JitTestData{ + .test_name = "jit-float-test.oil", + .arguments = "", + .input = "", + .expected_output = "", + .expected_error = "", + .expected_return_code = 0, + .jit_action_bound = 1, + }); +} + +TEST_F(X64JitTestSuite, Test2JitDisable) { + constexpr size_t kJitActionBound = 10000000; + RunSingleTest(JitTestData{ + .test_name = "jit-float-test.oil", + .arguments = "", + .input = "", + .expected_output = "", + .expected_error = "", + .expected_return_code = 0, + .jit_action_bound = kJitActionBound, + }); +} diff --git a/tests/main_test.cpp b/tests/main_test.cpp index 4b6c049..e5f3c45 100644 --- a/tests/main_test.cpp +++ b/tests/main_test.cpp @@ -15,19 +15,23 @@ 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" + "-m, --max-objects=: Maximum number of objects to keep in memory [default = 10000]\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\nShow this help message\n\nOPTIONS:\n" - "-f, --file=: Path to the bytecode file\n" - "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n" - "-m, --max-objects=: Maximum number of objects to keep in memory [default = 10000]\n\n" - "-h, --help: Display this help and exit\n\n"); + ASSERT_EQ(err.str(), "Not enough values were passed to argument --file.\n" + kHelpMessage); } TEST_F(ProjectIntegrationTestSuite, HelpTest) { @@ -35,13 +39,7 @@ 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" - "-f, --file=: Path to the bytecode file\n" - "-j, --jit-boundary=: JIT compilation boundary [default = 100000]\n" - "-m, --max-objects=: Maximum number of objects to keep in memory [default = 10000]\n\n" - "-h, --help: Display this help and exit\n\n"); + ASSERT_EQ(out.str(), kHelpMessage); } TEST_F(ProjectIntegrationTestSuite, FibTest1) { diff --git a/tests/test_data/examples b/tests/test_data/examples index 51cb78d..28c2ab6 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 51cb78d7778c97ac9d4561a210a003e94b59fe83 +Subproject commit 28c2ab6170e182bb13a1ad9edd2308f5cb95c895 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_