#ifndef FURVM_MODULE_HPP #define FURVM_MODULE_HPP #include "furlang/utility/hash.hpp" #include "furlang/view.hpp" #include "furvm/constant.hpp" #include "furvm/function.hpp" #include "furvm/fwd.hpp" #include "furvm/handle.hpp" #include "furvm/thing.hpp" #include #include #include #include #include #include #include #include #include namespace furvm { struct mod_type { struct array_value { mod_type_id typeId; std::size_t size; }; struct slice_value { mod_type_id typeId; }; struct import_value { mod_id modId; mod_type_id typeId; }; enum type { S8 = 0, S16, S32, S64, U8, U16, U32, U64, Ptr, Ref, Array, Slice, Import, Count, } type; union value { std::nullptr_t null = nullptr; mod_type_id typeRef; array_value array; slice_value slice; import_value imprt; value() = default; value(mod_type_id id) : typeRef(id) {} value(mod_type_id id, std::size_t size) : array({}) { array.typeId = id; array.size = size; } template >> value(ModIdFwd&& modId, mod_type_id typeId) : imprt({}) { imprt.modId = std::forward(modId); imprt.typeId = typeId; } ~value() {} value(value&& other) = delete; value& operator=(value&& other) = delete; value(const value& other) = delete; value& operator=(const value& other) = delete; } value; mod_type(enum type type) : type(type) {} mod_type(enum type type, mod_type_id typeRef) : type(type), value(typeRef) {} mod_type(mod_type_id id, std::size_t size) : type(Array), value(id, size) {} template >> mod_type(ModIdFwd&& modId, mod_type_id typeId) : type(Import), value(std::forward(modId), typeId) {} ~mod_type() { switch (type) { case Array: value.array.~array_value(); break; case Slice: value.slice.~slice_value(); break; case Import: value.imprt.~import_value(); break; default: break; } } mod_type(mod_type&& other) noexcept : type(other.type) { switch (type) { case Array: new (&value.array) array_value(other.value.array); break; case Slice: new (&value.slice) slice_value(other.value.slice); break; case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break; default: break; } other.type = Count; } mod_type& operator=(mod_type&& other) noexcept { if (this == &other) return *this; type = other.type; switch (type) { case Array: new (&value.array) array_value(other.value.array); break; case Slice: new (&value.slice) slice_value(other.value.slice); break; case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break; default: break; } other.type = Count; return *this; } mod_type(const mod_type& other) : type(other.type) { switch (type) { case Array: new (&value.array) array_value(other.value.array); break; case Slice: new (&value.slice) slice_value(other.value.slice); break; case Import: new (&value.imprt) import_value(other.value.imprt); break; default: break; } } mod_type& operator=(const mod_type& other) { if (this == &other) return *this; type = other.type; switch (type) { case Array: new (&value.array) array_value(other.value.array); break; case Slice: new (&value.slice) slice_value(other.value.slice); break; case Import: new (&value.imprt) import_value(other.value.imprt); break; default: break; } return *this; } }; struct breakpoint { std::function callback; void* data = nullptr; }; class mod { friend class function; friend class serializer; public: using bytecode_t = std::vector; /**< An alias to a vector of bytes. */ static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */ using native_function = std::function; public: /** * @brief Constructs a module. * * @param name Name of the module. * @param args Arguments forwarded to bytecode's constructor. */ template >> mod(Args&&... args) : m_bytecode(std::forward(args)...) {} ~mod() = default; /** * @brief Move constructor. */ mod(mod&&) = default; /** * @brief Move constructor. */ mod& operator=(mod&&) = default; mod(const mod&) = delete; mod& operator=(const mod&) = delete; public: /** * @brief Returns a byte from bytecode of this module. * * @param offset An offset of the byte. * @return The byte. */ byte byte_at(std::size_t offset) const { return m_bytecode.at(offset); } /** * @brief Returns the module's bytecode. * * @return A reference to the bytecode. */ constexpr bytecode_t& bytecode() { return m_bytecode; } /** * @brief Returns the module's bytecode. * * @return A constant reference to the bytecode. */ furlang::view bytecode_view() const { return { m_bytecode.data(), m_bytecode.size() }; } public: /** * @brief Emplaces a function in the module's function container. * * Emplaces the function in module's function container and name to function map and public functions map. * * @param args Arguments forwarded into the container's emplace_back function. * @return A handle to the emplaced function. */ template function_h emplace_function(Args&&... args) { function_h function; if constexpr (std::is_constructible_v) { function = std::move(m_functions.emplace_back(std::forward(args)...)); } else { function = std::move(m_functions.emplace(std::forward(args)...)); } return std::move(function); } /** * @brief Emplaces a function in the module's function container. * * Emplaces the function in module's function container and name to function map. * * @param name Name of the function. * @param args Arguments forwarded into the container's emplace_back function. * @return A handle to the emplaced function. */ template >> function_h emplace_function(NameFwd&& name, Args&&... args) { function_h function; if constexpr (std::is_constructible_v) { function = std::move(m_functions.emplace_back(std::forward(args)...)); } else { function = std::move(m_functions.emplace(std::forward(args)...)); } auto pair = std::make_pair(std::forward(name), function->signature()); m_functionMap[function.id()] = pair; m_functionSigs[std::move(pair)] = function.id(); return std::move(function); } /** * @brief Returns a function from the module. * * @param id Identifier of the function. * @return A handle to the function. */ auto function_at(function_id id) { return m_functions.at(id); } /** * @brief Returns a function from the module. * * @param id Identifier of the function. * @return A handle to the function. */ auto function_at(function_id id) const { return m_functions.at(id); } /** * @brief Returns a function from the module. * * @param name Name of the function. * @return A handle to the function. */ template && std::is_constructible_v>> auto function_at(NameFwd&& name, SigFwd&& signature) { return function_at( m_functionSigs.at(std::make_pair<>(std::forward(name), std::forward(signature)))); } /** * @brief Erases a function from the module's function container. * * @param id Identifier of the function. */ void erase_function(function_id id) { m_functions.erase(id); if (auto it = m_functionMap.find(id); it != m_functionMap.end()) { m_functionSigs.erase(it->second); m_functionMap.erase(it); } } const handle_container& functions() const { return m_functions; } const auto& function_map() const { return m_functionMap; } public: template void set_native_function(NameFwd&& name, Func&& func) { m_nativeFunctions.emplace(std::forward(name), std::forward(func)); } template native_function get_native_function(NameFwd&& name) const { return m_nativeFunctions.at(std::forward(name)); } public: /** * @brief Emplaces a type in the context. * * @param args Arguments forwarded to the type constructor. * @return The emplaced type. */ template auto emplace_type(Args&&... args) { if constexpr (std::is_constructible_v) { return m_types.emplace_back(std::forward(args)...); } else { return m_types.emplace(std::forward(args)...); } } /** * @brief Returns a type from the context. * * @param args type's id. * @return A handle to the type. */ template auto type_at(Args&&... args) { return m_types.at(std::forward(args)...); } /** * @brief Returns a type from the context. * * @param args type's id. * @return A handle to the type. */ template auto type_at(Args&&... args) const { return m_types.at(std::forward(args)...); } /** * @brief Erases a type from the context. * * @param args type's id. */ template void erase_type(Args&&... args) { m_types.erase(std::forward(args)...); } const handle_container& types() const { return m_types; } public: void set_global_variable_count(std::uint16_t count) { m_globalVariables.resize(count); m_globalVariables.shrink_to_fit(); } std::uint16_t get_global_variable_count() const { return static_cast(m_globalVariables.size()); } void store_global_variable(std::uint16_t var, thing<>&& thing) { if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot"); m_globalVariables.emplace(m_globalVariables.cbegin() + var, std::move(thing)); } void store_global_variable(std::uint16_t var, const thing<>& thing) { if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot"); m_globalVariables.emplace(m_globalVariables.cbegin() + var, thing); } thing<>& load_global_variable(std::uint16_t var) { if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot"); return m_globalVariables[var]; } const thing<>& load_global_variable(std::uint16_t var) const { if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot"); return m_globalVariables[var]; } public: template >> void emplace_constant(Args&&... args) { m_constants.emplace_back(std::forward(args)...); } const constant& constant_at(constant_index index) const { return m_constants.at(index); } public: template >> void set_breakpoint(bytecode_pos pos, Fwd&& breakpoint) { m_breakpoints[pos] = std::forward(breakpoint); } bool has_breakpoint(bytecode_pos pos) const { return m_breakpoints.find(pos) != m_breakpoints.end(); } const breakpoint& breakpoint_at(bytecode_pos pos) const { return m_breakpoints.at(pos); } public: /** * @brief Prints the module in a bytecode form to an output stream. * * @param os Output stream. * @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; using pair_type = std::pair; using pair_hash = furlang::utility::pair_hash, detail::function_sig_hash>; std::unordered_map m_functionSigs; std::unordered_map m_functionMap; handle_container m_functions; handle_container m_types; std::vector> m_globalVariables; std::vector m_constants; std::unordered_map m_nativeFunctions; std::unordered_map m_breakpoints; }; } // namespace furvm #endif // FURVM_MODULE_HPP