From f89f643870127e4d602d215c1d01bef7a5386acb Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sat, 4 Jul 2026 18:41:06 +0200 Subject: [PATCH] feat: implement module loading I couldn't care less to commit all the changes separately after fight with this stupid codebase. Also I am closing the issue since serializing contexts meant doing something like jars which I don't want to do right now. I'll reopen the issue though for sure clueless. Closes: #23 --- furvm/include/furvm/detail/serialization.hpp | 97 +++++++++++++++++ furvm/include/furvm/function.hpp | 14 ++- furvm/include/furvm/handle.hpp | 15 ++- furvm/include/furvm/module.hpp | 28 ++++- furvm/include/furvm/type.hpp | 1 - furvm/src/detail/serialization.cpp | 108 ++++++++++++++++--- furvm/src/executor.cpp | 2 +- furvm/src/main.cpp | 38 ++++--- furvm/src/module.cpp | 99 ++++++++++++++++- 9 files changed, 352 insertions(+), 50 deletions(-) diff --git a/furvm/include/furvm/detail/serialization.hpp b/furvm/include/furvm/detail/serialization.hpp index a406b1f..f535751 100644 --- a/furvm/include/furvm/detail/serialization.hpp +++ b/furvm/include/furvm/detail/serialization.hpp @@ -16,6 +16,14 @@ namespace detail { * @return The output stream. */ std::ostream& serialize(std::ostream& os, std::int8_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ std::ostream& serialize(std::ostream& os, std::int16_t value); /** @@ -26,6 +34,14 @@ std::ostream& serialize(std::ostream& os, std::int16_t value); * @return The output stream. */ std::ostream& serialize(std::ostream& os, std::int32_t value); + +/** + * @brief Serializes an integer. + * + * @param os Output stream. + * @param value Integer. + * @return The output stream. + */ std::ostream& serialize(std::ostream& os, std::int64_t value); /** @@ -73,6 +89,87 @@ std::ostream& serialize(std::ostream& os, std::uint64_t value); */ std::ostream& serialize(std::ostream& os, const std::string& value); +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::int8_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::int16_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::int32_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::int64_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::uint8_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::uint16_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::uint32_t& value); + +/** + * @brief Deserializes an integer. + * + * @param is Input stream. + * @param value Integer. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::uint64_t& value); + +/** + * @brief Deserializes a string. + * + * @param is Input stream. + * @param value String. + * @return The input stream. + */ +std::istream& load(std::istream& is, std::string& value); + } // namespace detail } // namespace furvm diff --git a/furvm/include/furvm/function.hpp b/furvm/include/furvm/function.hpp index 88aed83..dfc95fa 100644 --- a/furvm/include/furvm/function.hpp +++ b/furvm/include/furvm/function.hpp @@ -63,8 +63,18 @@ public: /** * @brief Constructs an import function. * - * @param paramCount Parameter count. - * @param imp Import function. + * @param mod Module's id. + * @param function Function's id. + */ + template >> + function(ModFwd&& mod, function_id function) + : m_type(function_t::Import), m_paramCount(0), m_value(import_function{ std::forward(mod), function }) {} + + /** + * @brief Constructs an import function. + * + * @param mod Module. + * @param function Function. */ function(const mod_h& mod, const function_h& function); diff --git a/furvm/include/furvm/handle.hpp b/furvm/include/furvm/handle.hpp index 17993b2..7016d17 100644 --- a/furvm/include/furvm/handle.hpp +++ b/furvm/include/furvm/handle.hpp @@ -98,6 +98,8 @@ public: using const_pointer = const Value*; /** Constant pointer type. */ public: using id_type = typename Header::id_type; /** Id type of the header. */ + + using header_type = Header; public: using pair_type = std::pair; /** Type of a header-value pair. */ public: @@ -362,9 +364,16 @@ public: delete m_pairs[id]; } - pair_type* newPair = new pair_type(std::piecewise_construct, - std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }), - std::forward_as_tuple(std::forward(args)...)); + pair_type* newPair = nullptr; + if constexpr (detail::header_has_refcount_v) { + newPair = new pair_type(std::piecewise_construct, + std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }), + std::forward_as_tuple(std::forward(args)...)); + } else { + newPair = new pair_type(std::piecewise_construct, + std::forward_as_tuple(id), + std::forward_as_tuple(std::forward(args)...)); + } m_pairs[id] = newPair; return { newPair }; diff --git a/furvm/include/furvm/module.hpp b/furvm/include/furvm/module.hpp index ecbd900..5b50e3e 100644 --- a/furvm/include/furvm/module.hpp +++ b/furvm/include/furvm/module.hpp @@ -7,8 +7,10 @@ #include "furvm/type.hpp" // IWYU pragma: keep #include +#include #include #include +#include #include #include #include @@ -82,7 +84,13 @@ public: */ template function_h emplace_function(Args&&... args) { - function_h function = m_functions.emplace_back(std::forward(args)...); + function_h function; + if constexpr (std::is_constructible_v) { + function = m_functions.emplace_back(std::forward(args)...); + } else { + function = m_functions.emplace(std::forward(args)...); + } + m_functionMap[function->name()] = function.id(); m_publicFunctions[function->name()] = function.id(); return std::move(function); @@ -145,7 +153,7 @@ public: * @param name Name of the function. * @return A handle to the function. */ - template + template >> auto function_at(NameFwd&& name) { return function_at(m_publicFunctions.at(std::forward(name))); } @@ -156,7 +164,7 @@ public: * @param name Name of the function. * @return A handle to the function. */ - template + template >> auto function_at(NameFwd&& name) const { return function_at(m_publicFunctions.at(std::forward(name))); } @@ -197,7 +205,11 @@ public: */ template auto emplace_type(Args&&... args) { - return m_types.emplace_back(std::forward(args)...); + if constexpr (std::is_constructible_v) { + return m_types.emplace_back(std::forward(args)...); + } else { + return m_types.emplace(std::forward(args)...); + } } /** @@ -239,6 +251,14 @@ public: * @return The output stream. */ std::ostream& serialize(std::ostream& os) const; + + /** + * @brief Loads a module in a bytecode form from an input stream. + * + * @param is Input stream. + * @return The loaded module. + */ + static mod load(std::istream& is); private: bytecode_t m_bytecode; diff --git a/furvm/include/furvm/type.hpp b/furvm/include/furvm/type.hpp index 2c492cb..9b06031 100644 --- a/furvm/include/furvm/type.hpp +++ b/furvm/include/furvm/type.hpp @@ -5,7 +5,6 @@ #include "furvm/handle.hpp" // IWYU pragma: keep #include - namespace furvm { enum class type_t : std::uint32_t { diff --git a/furvm/src/detail/serialization.cpp b/furvm/src/detail/serialization.cpp index 49a5d5d..1b8378b 100644 --- a/furvm/src/detail/serialization.cpp +++ b/furvm/src/detail/serialization.cpp @@ -1,49 +1,127 @@ #include "furvm/detail/serialization.hpp" +#include #include namespace furvm::detail { +namespace { + +template +void serialize_integral_impl(std::ostream& os, std::make_unsigned_t u_value, std::index_sequence) { + ((os << static_cast((u_value >> (I * 8)) & 0xFF)), ...); +} + +template +void load_integral_impl(const char* bs, std::make_unsigned_t& u_value, std::index_sequence) { + using UnsignedT = std::make_unsigned_t; + ((u_value |= (static_cast(static_cast(bs[I])) << (I * 8))), ...); +} + +template +std::ostream& serialize_integral(std::ostream& os, T value) { + static_assert(std::is_integral_v, "Type must be an integral type."); + using UnsignedT = std::make_unsigned_t; + + serialize_integral_impl(os, static_cast(value), std::make_index_sequence{}); + return os; +} + +template +std::istream& load_integral(std::istream& is, T& value) { + static_assert(std::is_integral_v, "Type must be an integral type."); + + char bs[sizeof(T)]; + if (!is.read(bs, sizeof(T))) { + return is; + } + + using UnsignedT = std::make_unsigned_t; + UnsignedT uVal = 0; + + load_integral_impl(bs, uVal, std::make_index_sequence{}); + + value = static_cast(uVal); + return is; +} + +} // namespace + std::ostream& serialize(std::ostream& os, std::int8_t value) { - return os << (char)((value >> 0ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::int16_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::int32_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) - << (char)((value >> 24ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::int64_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) - << (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF) - << (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::uint8_t value) { - return os << (char)((value >> 0ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::uint16_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::uint32_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) - << (char)((value >> 24ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, std::uint64_t value) { - return os << (char)((value >> 0ULL) & 0xFF) << (char)((value >> 8ULL) & 0xFF) << (char)((value >> 16ULL) & 0xFF) - << (char)((value >> 24ULL) & 0xFF) << (char)((value >> 32ULL) & 0xFF) << (char)((value >> 40ULL) & 0xFF) - << (char)((value >> 48ULL) & 0xFF) << (char)((value >> 56ULL) & 0xFF); + return serialize_integral(os, value); } std::ostream& serialize(std::ostream& os, const std::string& value) { - return serialize(os, std::uint16_t(value.size())).write(value.data(), static_cast(value.size())); + return serialize(os, static_cast(value.size())) + .write(value.data(), static_cast(value.size())); +} + +std::istream& load(std::istream& is, std::int8_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::int16_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::int32_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::int64_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::uint8_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::uint16_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::uint32_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::uint64_t& value) { + return load_integral(is, value); +} + +std::istream& load(std::istream& is, std::string& value) { + std::uint16_t length = 0; + load(is, length); + + value.resize(length); + return is.read(value.data(), static_cast(length)); } } // namespace furvm::detail diff --git a/furvm/src/executor.cpp b/furvm/src/executor.cpp index 1deea4f..75ee38f 100644 --- a/furvm/src/executor.cpp +++ b/furvm/src/executor.cpp @@ -31,7 +31,7 @@ void executor::push_frame(const mod_h& mod, function function) { } break; case function_t::Native: { m_frames.emplace((struct executor::frame){ mod, 0, m_stack.size(), std::move(args) }); - modInst->get_native_function(function.name())(*this); + modInst->get_native_function(function.native())(*this); m_frames.pop(); } break; default: throw std::runtime_error("unexpected function type"); diff --git a/furvm/src/main.cpp b/furvm/src/main.cpp index bcd353d..cf2a4f3 100644 --- a/furvm/src/main.cpp +++ b/furvm/src/main.cpp @@ -1,5 +1,6 @@ #ifndef LIBFURVM +#include "furvm/detail/serialization.hpp" #include "furvm/furvm.hpp" #include @@ -7,33 +8,30 @@ #include #include #include +#include -static constexpr std::array s_bytecode = { - furvm::byte(furvm::instruction_t::PushB2I), - 67, - furvm::byte(furvm::instruction_t::Duplicate), - furvm::byte(furvm::instruction_t::Reference), - furvm::byte(furvm::instruction_t::Add), - furvm::byte(furvm::instruction_t::Call), - 1, - 0, - furvm::byte(furvm::instruction_t::Return), -}; +int main(int argc, char** argv) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } -int main(void) { auto context = std::make_shared(); - furvm::mod_h furlangModule = context->emplace("furlang"); - furvm::function_h printFunc = furlangModule->emplace_function("print", 1, "print"); - furlangModule->set_native_function("print", + std::ifstream file(argv[1]); + if (!file.is_open()) { + std::cerr << "Failed to open " << argv[1] << '\n'; + return 1; + } + + furvm::mod_h mod = context->emplace("main", furvm::mod::load(file)); + furvm::function_h func = mod->function_at("main"); + + mod->set_native_function("print", [](furvm::executor& executor) { std::cout << executor.load_thing(0)->integer() << '\n'; }); - furvm::mod_h mainModule = context->emplace("main", s_bytecode.begin(), s_bytecode.end()); - furvm::function_h mainFunc = mainModule->emplace_function("main", 0, 0); - mainModule->emplace_function(furlangModule, printFunc).dispatch(); - furvm::executor_h executor = context->emplace_executor(context); - executor->push_frame(mainModule, *mainFunc); + executor->push_frame(mod, *func); while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) { executor->step(); diff --git a/furvm/src/module.cpp b/furvm/src/module.cpp index fd8f962..1a921b0 100644 --- a/furvm/src/module.cpp +++ b/furvm/src/module.cpp @@ -4,19 +4,25 @@ #include "furvm/fwd.hpp" #include "furvm/type.hpp" +#include +#include #include +#include #include +#include namespace furvm { std::ostream& mod::serialize(std::ostream& os) const { - os << MAGIC; + os.write(MAGIC, sizeof(MAGIC)); detail::serialize(os, std::uint32_t(0)); // version - detail::serialize(os, function_id(m_functionMap.size())); - for (function_id id = 0; id < m_functions.cend() - m_functions.cbegin(); ++id) { + function_id funcCount = m_functions.cend() - m_functions.cbegin(); + detail::serialize(os, funcCount); + for (function_id id = 0; id < funcCount; ++id) { if (!m_functions.contains(id)) { detail::serialize(os, ""); + detail::serialize(os, 0); detail::serialize(os, std::uint8_t(0xFF)); // null function continue; } @@ -24,12 +30,15 @@ std::ostream& mod::serialize(std::ostream& os) const { function_h func = m_functions.at(id); bool isPublic = m_publicFunctions.find(func->name()) != m_publicFunctions.end(); detail::serialize(os, isPublic ? func->name() : ""); // private functions have empty names + detail::serialize(os, func->param_count()); detail::serialize(os, std::uint8_t(func->type())); switch (func->type()) { case function_t::Normal: { detail::serialize(os, func->position()); } break; - case function_t::Native: break; + case function_t::Native: { + detail::serialize(os, func->native()); + } break; case function_t::Import: { detail::serialize(os, func->imp().mod); detail::serialize(os, func->imp().function); @@ -60,4 +69,86 @@ std::ostream& mod::serialize(std::ostream& os) const { detail::serialize(os, std::uint64_t(m_bytecode.size())); return os.write(reinterpret_cast(m_bytecode.data()), static_cast(m_bytecode.size())); } + +mod mod::load(std::istream& is) { + std::uint32_t magic = 0; + detail::load(is, magic); + std::uint32_t version = 0; + detail::load(is, version); + if (std::memcmp(&magic, MAGIC, sizeof(MAGIC)) != 0) throw std::runtime_error("invalid magic"); + if (version != 0) throw std::runtime_error("unsupported version"); + + mod mod; + + function_id functionCount = 0; + detail::load(is, functionCount); + for (function_id id = 0; id < functionCount; ++id) { + std::string name; + detail::load(is, name); + std::uint32_t paramCount = 0; + detail::load(is, paramCount); + std::uint8_t type = 0; + detail::load(is, type); + if (type == 0xFF) continue; + + switch ((function_t)type) { + case function_t::Normal: { + decltype(std::declval().position()) offset = 0; + detail::load(is, offset); + mod.emplace_function(id, std::move(name), paramCount, offset).dispatch(); + } break; + case function_t::Native: { + std::string native; + detail::load(is, native); + mod.emplace_function(id, std::move(name), paramCount, std::move(native)).dispatch(); + } break; + case function_t::Import: { + std::string modName; + detail::load(is, modName); + function_id function = 0; + detail::load(is, function); + mod.emplace_function(id, std::move(modName), function).dispatch(); + } break; + default: throw std::runtime_error("unknown function type"); + } + } + + type_id typeCount = 0; + detail::load(is, typeCount); + for (type_id id = 0; id < typeCount; ++id) { + std::uint8_t type = 0; + detail::load(is, type); + if (type == 0xFF) continue; + + switch ((type_t)type) { + case type_t::Primitive: { + primitive_type primitive = 0; + detail::load(is, primitive); + mod.emplace_type(id, std::make_shared(primitive)).dispatch(); + } break; + case type_t::Reference: throw std::runtime_error("reference type serialization is unimplemented"); + case type_t::List: { + type_id typeId = 0; + detail::load(is, typeId); + mod.emplace_type(id, std::make_shared(mod.type_at(typeId))).dispatch(); + } break; + case type_t::Import: { + std::string modName; + detail::load(is, modName); + type_id typeId = 0; + detail::load(is, typeId); + mod.emplace_type(id, std::make_shared(import_type{ std::move(modName), typeId })).dispatch(); + } break; + default: throw std::runtime_error("unknown type type"); + } + } + + std::uint64_t bytecodeLength = 0; + detail::load(is, bytecodeLength); + mod.bytecode().resize(bytecodeLength); + is.read(reinterpret_cast(mod.bytecode().data()), static_cast(bytecodeLength)); + + return mod; +} + } // namespace furvm