diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cc811d..bde9c19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ else () endif () set_target_properties(MunRuntime PROPERTIES - IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/bin/${MUN_OS}/mun_runtime${CMAKE_SHARED_LIBRARY_SUFFIX} + IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/bin/${MUN_OS}/${CMAKE_SHARED_LIBRARY_PREFIX}mun_runtime${CMAKE_SHARED_LIBRARY_SUFFIX} ) if (WIN32) diff --git a/external/md5 b/external/md5 index fdbf42f..c2ebd8c 160000 --- a/external/md5 +++ b/external/md5 @@ -1 +1 @@ -Subproject commit fdbf42f042839e652d19ec25a3e5de067dc0146a +Subproject commit c2ebd8c7d784756d40631b60622110666eb2a795 diff --git a/include/mun/function.h b/include/mun/function.h new file mode 100644 index 0000000..05f141d --- /dev/null +++ b/include/mun/function.h @@ -0,0 +1,37 @@ +#ifndef MUN_FUNCTION_H_ +#define MUN_FUNCTION_H_ + +#include +#include + +#include "mun/runtime_capi.h" +#include "mun/type_info.h" + +namespace mun { +/** + * A wrapper around a C function with type information. + */ +struct RuntimeFunction { + /** + * Constructs a `RuntimeFunction` from a generic function pointer and a name. + * \param name The name of the function used when added to the runtime + * \param fn_ptr The function pointer to add + */ + template + RuntimeFunction(std::string_view name, TRet(__cdecl* fn_ptr)(TArgs...)) + : name(name), + arg_types({arg_type_info()...}), + ret_type(return_type_info()), + fn_ptr(reinterpret_cast(fn_ptr)) {} + + RuntimeFunction(const RuntimeFunction&) = default; + RuntimeFunction(RuntimeFunction&&) = default; + + std::string name; + std::vector arg_types; + std::optional ret_type; + const void* fn_ptr; +}; +} // namespace mun + +#endif diff --git a/include/mun/gc.h b/include/mun/gc.h index 07fcc07..f06fc66 100644 --- a/include/mun/gc.h +++ b/include/mun/gc.h @@ -4,7 +4,6 @@ #include #include "mun/error.h" -#include "runtime.h" namespace mun { class Runtime; diff --git a/include/mun/invoke_fn.h b/include/mun/invoke_fn.h index 57f45e3..51cba87 100644 --- a/include/mun/invoke_fn.h +++ b/include/mun/invoke_fn.h @@ -7,6 +7,7 @@ #include "mun/invoke_result.h" #include "mun/marshal.h" +#include "mun/reflection.h" #include "mun/runtime.h" namespace mun { @@ -80,12 +81,10 @@ InvokeResult invoke_fn(Runtime& runtime, std::string_view fn_na << std::endl; return make_error(runtime, fn_name, args...); - ; } - auto fn = - reinterpret_cast::type (*)(typename Marshal::type...)>( - fn_info->fn_ptr); + auto fn = reinterpret_cast::type(__cdecl*)( + typename Marshal::type...)>(const_cast(fn_info->fn_ptr)); if constexpr (std::is_same_v) { fn(args...); return InvokeResult(std::monostate{}); diff --git a/include/mun/reflection.h b/include/mun/reflection.h index e039bb2..2688296 100644 --- a/include/mun/reflection.h +++ b/include/mun/reflection.h @@ -1,27 +1,15 @@ #ifndef MUN_REFLECTION_H_ #define MUN_REFLECTION_H_ -#include - #include #include #include #include #include "mun/runtime_capi.h" -#include "mun/struct_ref.h" +#include "mun/type_info.h" namespace mun { -namespace details { -constexpr MunGuid type_guid(const char* type_name) noexcept { - const auto hash = md5::compute(type_name); - return MunGuid{ - hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], - hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], - }; -} -} // namespace details - constexpr inline bool operator==(const MunGuid& lhs, const MunGuid& rhs) noexcept { for (auto idx = 0; idx < 16; ++idx) { if (lhs.b[idx] != rhs.b[idx]) { @@ -35,6 +23,24 @@ constexpr inline bool operator!=(const MunGuid& lhs, const MunGuid& rhs) noexcep return !(lhs == rhs); } +template +struct ArgumentReflection { + static constexpr const char* type_name(const T&) noexcept { return TypeInfo::Type.name; } + static constexpr MunGuid type_guid(const T&) noexcept { return TypeInfo::Type.guid; } +}; + +template +struct ReturnTypeReflection { + static constexpr const char* type_name() noexcept { return TypeInfo::Type.name; } + static constexpr MunGuid type_guid() noexcept { return TypeInfo::Type.guid; } +}; + +template <> +struct ReturnTypeReflection { + static constexpr const char* type_name() noexcept { return "core::empty"; } + static constexpr MunGuid type_guid() noexcept { return details::type_guid(type_name()); } +}; + namespace reflection { template constexpr bool equal_types() noexcept { @@ -54,6 +60,18 @@ inline std::optional> equals_argument_type( template inline std::optional> equals_return_type( + const MunTypeInfo& type_info) noexcept; + +} // namespace reflection + +} // namespace mun + +#include "struct_ref.h" + +namespace mun { +namespace reflection { +template +std::optional> equals_return_type( const MunTypeInfo& type_info) noexcept { if (type_info.group == MunTypeGroup::FundamentalTypes) { if (type_info.guid != ReturnTypeReflection::type_guid()) { @@ -66,49 +84,6 @@ inline std::optional> equals_return_type( return std::nullopt; } } // namespace reflection - -template -struct ArgumentReflection; - -template -struct ReturnTypeReflection; - -#define IMPL_PRIMITIVE_TYPE_REFLECTION(ty, name_literal) \ - template <> \ - struct ReturnTypeReflection { \ - static constexpr const char* type_name() noexcept { return name_literal; } \ - static constexpr MunGuid type_guid() noexcept { return details::type_guid(type_name()); } \ - }; \ - \ - template <> \ - struct ArgumentReflection { \ - static constexpr const char* type_name(const ty&) noexcept { \ - return ReturnTypeReflection::type_name(); \ - } \ - static constexpr MunGuid type_guid(const ty&) noexcept { \ - return ReturnTypeReflection::type_guid(); \ - } \ - }; - -IMPL_PRIMITIVE_TYPE_REFLECTION(bool, "core::bool"); -IMPL_PRIMITIVE_TYPE_REFLECTION(float, "core::f32"); -IMPL_PRIMITIVE_TYPE_REFLECTION(double, "core::f64"); -IMPL_PRIMITIVE_TYPE_REFLECTION(int8_t, "core::i8"); -IMPL_PRIMITIVE_TYPE_REFLECTION(int16_t, "core::i16"); -IMPL_PRIMITIVE_TYPE_REFLECTION(int32_t, "core::i32"); -IMPL_PRIMITIVE_TYPE_REFLECTION(int64_t, "core::i64"); -// IMPL_PRIMITIVE_TYPE_REFLECTION(int128_t, "core::i128"); -IMPL_PRIMITIVE_TYPE_REFLECTION(uint8_t, "core::u8"); -IMPL_PRIMITIVE_TYPE_REFLECTION(uint16_t, "core::u16"); -IMPL_PRIMITIVE_TYPE_REFLECTION(uint32_t, "core::u32"); -IMPL_PRIMITIVE_TYPE_REFLECTION(uint64_t, "core::u64"); -// IMPL_PRIMITIVE_TYPE_REFLECTION(uint128_t, "core::u128"); - -template <> -struct ReturnTypeReflection { - static constexpr const char* type_name() noexcept { return "core::empty"; } - static constexpr MunGuid type_guid() noexcept { return details::type_guid(type_name()); } -}; } // namespace mun #endif diff --git a/include/mun/runtime.h b/include/mun/runtime.h index b9d37a9..2f76d6e 100644 --- a/include/mun/runtime.h +++ b/include/mun/runtime.h @@ -6,15 +6,20 @@ #include #include "mun/error.h" +#include "mun/function.h" #include "mun/runtime_capi.h" namespace mun { + +struct RuntimeOptions; + /** A wrapper around a `MunRuntimeHandle`. * * Frees the corresponding runtime object on destruction, if it exists. */ class Runtime { friend std::optional make_runtime(std::string_view library_path, + const RuntimeOptions& options, Error* out_error) noexcept; /** Constructs a runtime from an instantiated `MunRuntimeHandle`. @@ -148,19 +153,53 @@ class Runtime { MunRuntimeHandle m_handle; }; -/** Construct a new runtime that loads the library at `library_path` and its -dependencies. +struct RuntimeOptions { + /** + * The interval at which changes to the disk are detected. `0` will initialize this value to + * default. + */ + uint32_t delay_ms = 0; + + /** + * A list of functions to add to the runtime, these functions can be called from Mun as *extern* + * functions. + */ + std::vector functions; +}; + +/** Construct a new runtime that loads the library at `library_path` and its dependencies. * * On failure, the error is returned through the `out_error` pointer, if set. * * \param library_path the path to a Mun library + * \param options Additional options used by the construction of a runtime * \param out_error optionally, a pointer to an `Error` instance * \return potentially, a runtime .*/ inline std::optional make_runtime(std::string_view library_path, + const RuntimeOptions& options = {}, Error* out_error = nullptr) noexcept { + std::vector function_definitions(options.functions.size()); + for (size_t i = 0; i < options.functions.size(); ++i) { + auto& definition = function_definitions[i]; + const auto& func = options.functions[i]; + definition = MunFunctionDefinition{ + MunFunctionPrototype{ + func.name.c_str(), + MunFunctionSignature{func.arg_types.data(), + func.ret_type.has_value() ? func.ret_type.value() : nullptr, + static_cast(func.arg_types.size())}}, + func.fn_ptr}; + } + + MunRuntimeOptions runtime_options; + runtime_options.delay_ms = options.delay_ms; + runtime_options.functions = + function_definitions.empty() ? nullptr : function_definitions.data(); + runtime_options.num_functions = static_cast(function_definitions.size()); + MunRuntimeHandle handle; - if (auto error = Error(mun_runtime_create(library_path.data(), &handle))) { + if (auto error = Error(mun_runtime_create(library_path.data(), runtime_options, &handle))) { if (out_error) { *out_error = std::move(error); } diff --git a/include/mun/runtime_capi.h b/include/mun/runtime_capi.h index af5d656..77f3d61 100644 --- a/include/mun/runtime_capi.h +++ b/include/mun/runtime_capi.h @@ -195,6 +195,34 @@ typedef struct { const void *fn_ptr; } MunFunctionDefinition; +/** + * Options required to construct a [`RuntimeHandle`] through [`mun_runtime_create`] + * + * # Safety + * + * This struct contains raw pointers as parameters. Passing pointers to invalid data, will lead to + * undefined behavior. + */ +typedef struct { + /** + * The interval at which changes to the disk are detected. `0` will initialize this value to + * default. + */ + uint32_t delay_ms; + /** + * Function definitions that should be inserted in the runtime before a mun library is loaded. + * This is useful to initialize `extern` functions used in a mun library. + * + * If the [`num_functions`] fields is non-zero this field must contain a pointer to an array + * of [`abi::FunctionDefinition`]s. + */ + const MunFunctionDefinition *functions; + /** + * The number of functions in the [`functions`] array. + */ + uint32_t num_functions; +} MunRuntimeOptions; + /** * Represents a struct declaration. * @@ -339,7 +367,9 @@ MunErrorHandle mun_gc_unroot(MunRuntimeHandle handle, MunGcPtr obj); * This function receives raw pointers as parameters. If any of the arguments is a null pointer, * an error will be returned. Passing pointers to invalid data, will lead to undefined behavior. */ -MunErrorHandle mun_runtime_create(const char *library_path, MunRuntimeHandle *handle); +MunErrorHandle mun_runtime_create(const char *library_path, + MunRuntimeOptions options, + MunRuntimeHandle *handle); /** * Destructs the runtime corresponding to `handle`. diff --git a/include/mun/struct_ref.h b/include/mun/struct_ref.h index 72bfa17..3aa727c 100644 --- a/include/mun/struct_ref.h +++ b/include/mun/struct_ref.h @@ -9,7 +9,6 @@ #include "mun/gc.h" #include "mun/marshal.h" -#include "mun/reflection.h" #include "mun/runtime.h" namespace mun { @@ -96,34 +95,7 @@ class StructRef { * \return possibly, the value of the desired field */ template - std::optional get(std::string_view field_name) const noexcept { - const auto type_info = info(); - - // Safety: `type_info_as_struct` is guaranteed to return a value for - // `StructRef`s. - const auto struct_info = *type_info_as_struct(*type_info); - if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { - const auto* field_type = struct_info.field_types[*idx]; - if (auto diff = reflection::equals_return_type(*field_type)) { - const auto& [expected, found] = *diff; - - std::cerr << "Mismatched types for `" - << details::format_struct_field(type_info->name, field_name) - << "`. Expected: `" << expected << "`. Found: `" << found << "`." - << std::endl; - - return std::nullopt; - } - - const auto offset = static_cast(struct_info.field_offsets[*idx]); - const auto byte_ptr = reinterpret_cast(*raw()); - return std::make_optional(Marshal::copy_from( - reinterpret_cast::type*>(byte_ptr + offset), *m_runtime, - field_type ? std::make_optional(field_type) : std::nullopt)); - } else { - return std::nullopt; - } - } + std::optional get(std::string_view field_name) const noexcept; /** Tries to replace the value of the field corresponding to * `field_name`, returning its original value. @@ -132,35 +104,7 @@ class StructRef { * \return possibly, the value of the replaced field */ template - std::optional replace(std::string_view field_name, T value) noexcept { - const auto type_info = info(); - - // Safety: `type_info_as_struct` is guaranteed to return a value for - // `StructRef`s. - const auto struct_info = *type_info_as_struct(*type_info); - if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { - const auto* field_type = struct_info.field_types[*idx]; - if (auto diff = reflection::equals_return_type(*field_type)) { - const auto& [expected, found] = *diff; - - std::cerr << "Mismatched types for `" - << details::format_struct_field(type_info->name, field_name) - << "`. Expected: `" << expected << "`. Found: `" << found << "`." - << std::endl; - - return std::nullopt; - } - - const auto offset = static_cast(struct_info.field_offsets[*idx]); - auto byte_ptr = reinterpret_cast(*raw()); - return std::make_optional(Marshal::swap_at( - Marshal::to(std::move(value)), - reinterpret_cast::type*>(byte_ptr + offset), *m_runtime, - field_type ? std::make_optional(field_type) : std::nullopt)); - } else { - return std::nullopt; - } - } + std::optional replace(std::string_view field_name, T value) noexcept; /** Tries to set the value of the field corresponding to * `field_name` to the provided `value`. @@ -170,36 +114,7 @@ class StructRef { * \return whether the field was set successfully */ template - bool set(std::string_view field_name, T value) noexcept { - const auto type_info = info(); - - // Safety: `type_info_as_struct` is guaranteed to return a value for - // `StructRef`s. - const auto struct_info = *type_info_as_struct(*type_info); - if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { - const auto* field_type = struct_info.field_types[*idx]; - if (auto diff = reflection::equals_return_type(*field_type)) { - const auto& [expected, found] = *diff; - - std::cerr << "Mismatched types for `" - << details::format_struct_field(type_info->name, field_name) - << "`. Expected: `" << expected << "`. Found: `" << found << "`." - << std::endl; - - return false; - } - - const auto offset = static_cast(struct_info.field_offsets[*idx]); - auto byte_ptr = reinterpret_cast(*raw()); - - Marshal::move_to(Marshal::to(std::move(value)), - reinterpret_cast::type*>(byte_ptr + offset), - field_type ? std::make_optional(field_type) : std::nullopt); - return true; - } else { - return false; - } - } + bool set(std::string_view field_name, T value) noexcept; private: const Runtime* m_runtime; @@ -276,6 +191,11 @@ struct Marshal { return StructRef(runtime, gc_handle); } }; +} // namespace mun + +#include "mun/reflection.h" + +namespace mun { template <> struct ArgumentReflection { @@ -288,6 +208,97 @@ struct ReturnTypeReflection { static constexpr const char* type_name() noexcept { return "struct"; } static constexpr MunGuid type_guid() noexcept { return details::type_guid(type_name()); } }; + +template +std::optional StructRef::get(std::string_view field_name) const noexcept { + const auto type_info = info(); + + // Safety: `type_info_as_struct` is guaranteed to return a value for + // `StructRef`s. + const auto struct_info = *type_info_as_struct(*type_info); + if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { + const auto* field_type = struct_info.field_types[*idx]; + if (auto diff = reflection::equals_return_type(*field_type)) { + const auto& [expected, found] = *diff; + + std::cerr << "Mismatched types for `" + << details::format_struct_field(type_info->name, field_name) + << "`. Expected: `" << expected << "`. Found: `" << found << "`." + << std::endl; + + return std::nullopt; + } + + const auto offset = static_cast(struct_info.field_offsets[*idx]); + const auto byte_ptr = reinterpret_cast(*raw()); + return std::make_optional(Marshal::copy_from( + reinterpret_cast::type*>(byte_ptr + offset), *m_runtime, + field_type ? std::make_optional(field_type) : std::nullopt)); + } else { + return std::nullopt; + } +} +template +std::optional StructRef::replace(std::string_view field_name, T value) noexcept { + const auto type_info = info(); + + // Safety: `type_info_as_struct` is guaranteed to return a value for + // `StructRef`s. + const auto struct_info = *type_info_as_struct(*type_info); + if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { + const auto* field_type = struct_info.field_types[*idx]; + if (auto diff = reflection::equals_return_type(*field_type)) { + const auto& [expected, found] = *diff; + + std::cerr << "Mismatched types for `" + << details::format_struct_field(type_info->name, field_name) + << "`. Expected: `" << expected << "`. Found: `" << found << "`." + << std::endl; + + return std::nullopt; + } + + const auto offset = static_cast(struct_info.field_offsets[*idx]); + auto byte_ptr = reinterpret_cast(*raw()); + return std::make_optional(Marshal::swap_at( + Marshal::to(std::move(value)), + reinterpret_cast::type*>(byte_ptr + offset), *m_runtime, + field_type ? std::make_optional(field_type) : std::nullopt)); + } else { + return std::nullopt; + } +} +template +bool StructRef::set(std::string_view field_name, T value) noexcept { + const auto type_info = info(); + + // Safety: `type_info_as_struct` is guaranteed to return a value for + // `StructRef`s. + const auto struct_info = *type_info_as_struct(*type_info); + if (const auto idx = details::find_index(type_info->name, struct_info, field_name)) { + const auto* field_type = struct_info.field_types[*idx]; + if (auto diff = reflection::equals_return_type(*field_type)) { + const auto& [expected, found] = *diff; + + std::cerr << "Mismatched types for `" + << details::format_struct_field(type_info->name, field_name) + << "`. Expected: `" << expected << "`. Found: `" << found << "`." + << std::endl; + + return false; + } + + const auto offset = static_cast(struct_info.field_offsets[*idx]); + auto byte_ptr = reinterpret_cast(*raw()); + + Marshal::move_to(Marshal::to(std::move(value)), + reinterpret_cast::type*>(byte_ptr + offset), + field_type ? std::make_optional(field_type) : std::nullopt); + return true; + } else { + return false; + } +} } // namespace mun #endif diff --git a/include/mun/type_info.h b/include/mun/type_info.h new file mode 100644 index 0000000..e237d4f --- /dev/null +++ b/include/mun/type_info.h @@ -0,0 +1,78 @@ +#ifndef MUN_TYPE_INFO_H +#define MUN_TYPE_INFO_H + +#include + +#include + +#include "mun/runtime_capi.h" + +namespace mun { +namespace details { +constexpr MunGuid type_guid(const char* type_name) noexcept { + const auto hash = md5::compute(type_name); + return MunGuid{ + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], + hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], + }; +} +} // namespace details + +template +struct TypeInfo; + +#define IMPL_PRIMITIVE_TYPE_INFO(ty, name_literal) \ + template <> \ + struct TypeInfo { \ + static constexpr MunTypeInfo Type{ \ + details::type_guid(name_literal), \ + name_literal, \ + sizeof(ty), \ + std::alignment_of::value, \ + MunTypeGroup::FundamentalTypes, \ + }; \ + } + +IMPL_PRIMITIVE_TYPE_INFO(bool, "core::bool"); +IMPL_PRIMITIVE_TYPE_INFO(float, "core::f32"); +IMPL_PRIMITIVE_TYPE_INFO(double, "core::f64"); +IMPL_PRIMITIVE_TYPE_INFO(int8_t, "core::i8"); +IMPL_PRIMITIVE_TYPE_INFO(int16_t, "core::i16"); +IMPL_PRIMITIVE_TYPE_INFO(int32_t, "core::i32"); +IMPL_PRIMITIVE_TYPE_INFO(int64_t, "core::i64"); +// IMPL_PRIMITIVE_TYPE_REFLECTION(int128_t, "core::i128"); +IMPL_PRIMITIVE_TYPE_INFO(uint8_t, "core::u8"); +IMPL_PRIMITIVE_TYPE_INFO(uint16_t, "core::u16"); +IMPL_PRIMITIVE_TYPE_INFO(uint32_t, "core::u32"); +IMPL_PRIMITIVE_TYPE_INFO(uint64_t, "core::u64"); +// IMPL_PRIMITIVE_TYPE_REFLECTION(uint128_t, "core::u128"); + +/** + * Returns the return type `MunTypeInfo` corresponding to type T, or none if the return type is + * void. + */ +template +std::optional return_type_info() { + return &TypeInfo::Type; +} + +/** + * Returns the return type `MunTypeInfo` corresponding to type T, or none if the return type is + * void. + */ +template <> +inline std::optional return_type_info() { + return std::nullopt; +} + +/** + * Returns the argument type `MunTypeInfo` corresponding to type T. + */ +template +MunTypeInfo const* arg_type_info() { + return &TypeInfo::Type; +} + +} // namespace mun + +#endif // MUN_TYPE_INFO_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1c2ebf4..d0c01c7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -55,6 +55,7 @@ add_executable(MunRuntimeTests catch_main.cc marshal.cc runtime.cc + extern.cc ) target_include_directories(MunRuntimeTests PRIVATE ${MUN_FOLDER}/external/catch2/single_include ${MUN_FOLDER}/include) diff --git a/tests/extern.cc b/tests/extern.cc new file mode 100644 index 0000000..1d6d0d0 --- /dev/null +++ b/tests/extern.cc @@ -0,0 +1,48 @@ +#include + +#include +#include + +/// Returns the absolute path to the munlib with the specified name +inline std::string get_munlib_path(std::string_view name) { + std::stringstream ss; + ss << MUN_TEST_DIR << name; + return ss.str(); +} + +uint32_t internal_function(uint32_t a, uint32_t b) { return a + b; } +uint32_t some_function() { return 0; } + +TEST_CASE("functions must be inserted into the runtime", "[extern]") { + mun::RuntimeOptions options; + + mun::Error err; + auto runtime = mun::make_runtime(get_munlib_path("sources/extern.munlib"), options, &err); + REQUIRE(!runtime); + REQUIRE(err); +} + +TEST_CASE("function must have correct signature", "[extern]") { + mun::RuntimeOptions options; + options.functions.emplace_back(mun::RuntimeFunction("extern_fn", some_function)); + + mun::Error err; + auto runtime = mun::make_runtime(get_munlib_path("sources/extern.munlib"), options, &err); + REQUIRE(!runtime); + REQUIRE(err); +} + +TEST_CASE("functions can be inserted into the runtime", "[extern]") { + mun::RuntimeOptions options; + options.functions.emplace_back(mun::RuntimeFunction("extern_fn", internal_function)); + + mun::Error err; + auto runtime = mun::make_runtime(get_munlib_path("sources/extern.munlib"), options, &err); + if (!runtime) { + REQUIRE(err); + FAIL(err.message()); + } + + REQUIRE(mun::invoke_fn(*runtime, "main", 90, 2648).unwrap() == + 90 + 2648); +} diff --git a/tests/marshal.cc b/tests/marshal.cc index 51ac56a..f1079d2 100644 --- a/tests/marshal.cc +++ b/tests/marshal.cc @@ -10,67 +10,69 @@ inline std::string get_munlib_path(std::string_view name) { return ss.str(); } -#define TEST_MARSHALLING(ty, lhs, rhs, expected) \ - TEST_CASE("function can marshal " #ty, "[marshal]") { \ - mun::Error err; \ - if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), &err)) { \ - REQUIRE(!err); \ - \ - const ty a = (lhs), b = (rhs); \ - auto res = mun::invoke_fn(*runtime, "marshal_" #ty, a, b); \ - REQUIRE(res.is_ok()); \ - REQUIRE(res.wait() == (expected)); \ - } else { \ - REQUIRE(err); \ - FAIL(err.message()); \ - } \ - } \ - TEST_CASE("struct can get, set, and replace " #ty, "[marshal]") { \ - mun::Error err; \ - if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), &err)) { \ - REQUIRE(!err); \ - \ - const ty a = (lhs), b = (rhs); \ - auto res = mun::invoke_fn(*runtime, "new_" #ty, a, b); \ - REQUIRE(res.is_ok()); \ - \ - auto s = res.wait(); \ - { \ - const auto first = s.get("0"); \ - REQUIRE(first.has_value()); \ - REQUIRE(*first == a); \ - } \ - { \ - const auto second = s.get("1"); \ - REQUIRE(second.has_value()); \ - REQUIRE(*second == b); \ - } \ - REQUIRE(s.set("0", b)); \ - REQUIRE(s.set("1", a)); \ - { \ - const auto first = s.replace("0", a); \ - REQUIRE(first.has_value()); \ - REQUIRE(*first == b); \ - } \ - { \ - const auto second = s.replace("1", b); \ - REQUIRE(second.has_value()); \ - REQUIRE(*second == a); \ - } \ - { \ - const auto first = s.get("0"); \ - REQUIRE(first.has_value()); \ - REQUIRE(*first == a); \ - } \ - { \ - const auto second = s.get("1"); \ - REQUIRE(second.has_value()); \ - REQUIRE(*second == b); \ - } \ - } else { \ - REQUIRE(err); \ - FAIL(err.message()); \ - } \ +#define TEST_MARSHALLING(ty, lhs, rhs, expected) \ + TEST_CASE("function can marshal " #ty, "[marshal]") { \ + mun::Error err; \ + if (auto runtime = \ + mun::make_runtime(get_munlib_path("sources/marshal.munlib"), {}, &err)) { \ + REQUIRE(!err); \ + \ + const ty a = (lhs), b = (rhs); \ + auto res = mun::invoke_fn(*runtime, "marshal_" #ty, a, b); \ + REQUIRE(res.is_ok()); \ + REQUIRE(res.wait() == (expected)); \ + } else { \ + REQUIRE(err); \ + FAIL(err.message()); \ + } \ + } \ + TEST_CASE("struct can get, set, and replace " #ty, "[marshal]") { \ + mun::Error err; \ + if (auto runtime = \ + mun::make_runtime(get_munlib_path("sources/marshal.munlib"), {}, &err)) { \ + REQUIRE(!err); \ + \ + const ty a = (lhs), b = (rhs); \ + auto res = mun::invoke_fn(*runtime, "new_" #ty, a, b); \ + REQUIRE(res.is_ok()); \ + \ + auto s = res.wait(); \ + { \ + const auto first = s.get("0"); \ + REQUIRE(first.has_value()); \ + REQUIRE(*first == a); \ + } \ + { \ + const auto second = s.get("1"); \ + REQUIRE(second.has_value()); \ + REQUIRE(*second == b); \ + } \ + REQUIRE(s.set("0", b)); \ + REQUIRE(s.set("1", a)); \ + { \ + const auto first = s.replace("0", a); \ + REQUIRE(first.has_value()); \ + REQUIRE(*first == b); \ + } \ + { \ + const auto second = s.replace("1", b); \ + REQUIRE(second.has_value()); \ + REQUIRE(*second == a); \ + } \ + { \ + const auto first = s.get("0"); \ + REQUIRE(first.has_value()); \ + REQUIRE(*first == a); \ + } \ + { \ + const auto second = s.get("1"); \ + REQUIRE(second.has_value()); \ + REQUIRE(*second == b); \ + } \ + } else { \ + REQUIRE(err); \ + FAIL(err.message()); \ + } \ } // TODO: Add 128-bit integers @@ -91,7 +93,7 @@ TEST_MARSHALLING(uint64_t, 1, 64, 1 + 64); TEST_CASE("struct can get, set, and replace struct", "[marshal]") { mun::Error err; - if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), &err)) { + if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), {}, &err)) { REQUIRE(!err); float a = -3.14f, b = 6.28f; diff --git a/tests/runtime.cc b/tests/runtime.cc index c2b7da5..1c21253 100644 --- a/tests/runtime.cc +++ b/tests/runtime.cc @@ -12,7 +12,7 @@ inline std::string get_munlib_path(std::string_view name) { TEST_CASE("runtime can be constructed", "[runtime]") { mun::Error err; - if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), &err)) { + if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), {}, &err)) { REQUIRE(!err); } else { REQUIRE(err); @@ -22,7 +22,7 @@ TEST_CASE("runtime can be constructed", "[runtime]") { TEST_CASE("runtime can find `FunctionInfo`", "[runtime]") { mun::Error err; - if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), &err)) { + if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), {}, &err)) { REQUIRE(!err); REQUIRE(runtime.has_value()); @@ -41,7 +41,7 @@ TEST_CASE("runtime can find `FunctionInfo`", "[runtime]") { // TODO: Test hot reloading TEST_CASE("runtime can update", "[runtime]") { mun::Error err; - if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), &err)) { + if (auto runtime = mun::make_runtime(get_munlib_path("sources/fibonacci.munlib"), {}, &err)) { REQUIRE(!err); runtime->update(&err); @@ -57,7 +57,7 @@ TEST_CASE("runtime can update", "[runtime]") { TEST_CASE("runtime can garbage collect", "[runtime]") { mun::Error err; - if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), &err)) { + if (auto runtime = mun::make_runtime(get_munlib_path("sources/marshal.munlib"), {}, &err)) { REQUIRE(!err); { auto res = mun::invoke_fn(*runtime, "new_bool", true, false); diff --git a/tests/sources/extern.mun b/tests/sources/extern.mun new file mode 100644 index 0000000..0cd8c61 --- /dev/null +++ b/tests/sources/extern.mun @@ -0,0 +1,5 @@ +extern fn extern_fn(a: u32, b: u32) -> u32; + +pub fn main(a: u32, b: u32) -> u32 { + extern_fn(a,b) +}