diff --git a/BUILD.bazel b/BUILD.bazel index ef198d9..3a58354 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -23,7 +23,10 @@ filegroup( cc_library( name = "ecsact_si_wasm", copts = copts, - defines = ["ECSACTSI_WASM_API="], + defines = [ + "ECSACTSI_WASM_API=", + "ECSACT_DYNAMIC_API=", + ], hdrs = [":headers"], srcs = [":sources"], deps = [ @@ -43,7 +46,14 @@ cc_library( "ecsact/wasm/detail/minst/minst.cc", ], deps = [ + ":cpp_util", "@ecsact_runtime//:common", "@wasmer", ], ) + +cc_library( + name = "cpp_util", + copts = copts, + hdrs = ["ecsact/wasm/detail/cpp_util.hh"], +) diff --git a/bazel/copts.bzl b/bazel/copts.bzl index 5895e7d..891e653 100644 --- a/bazel/copts.bzl +++ b/bazel/copts.bzl @@ -6,11 +6,11 @@ copts = selects.with_or({ "-std=c++20", ], ("@rules_cc//cc/compiler:clang"): [ - "-std=c++2b", + "-std=c++20", "-fexperimental-library", ], ("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [ - "/std:c++latest", + "/std:c++20", "/permissive-", "/Zc:preprocessor", ], diff --git a/ecsact/wasm.h b/ecsact/wasm.h index 1865334..0783502 100644 --- a/ecsact/wasm.h +++ b/ecsact/wasm.h @@ -90,6 +90,11 @@ typedef enum ecsactsi_wasm_error { * WASM file contains correctly named guest import, but was not a function */ ECSACTSI_WASM_ERR_GUEST_IMPORT_INVALID, + + /** + * Invoking `_initialize()` resulted in a wasm trap + */ + ECSACTSI_WASM_ERR_INITIALIZE_FAIL, } ecsactsi_wasm_error; ECSACTSI_WASM_API_FN(void, ecsactsi_wasm_last_error_message) diff --git a/ecsact/wasm.hh b/ecsact/wasm.hh index 855234d..cdda9bf 100644 --- a/ecsact/wasm.hh +++ b/ecsact/wasm.hh @@ -1,12 +1,14 @@ #pragma once +#include +#include +#include #include #include #include #include #include #include -#include #include "ecsact/wasm.h" namespace ecsact::wasm { @@ -130,13 +132,13 @@ inline auto consume_and_print_logs() -> void { switch(l) { default: case log_level::info: - std::println(stdout, "[INFO] {}", m); + std::cout << std::format("[INFO] {}", m); break; case log_level::warning: - std::println(stderr, "[WARNING] {}", m); + std::cerr << std::format("[WARNING] {}", m); break; case log_level::error: - std::println(stderr, "[ERROR] {}", m); + std::cerr << std::format("[ERROR] {}", m); break; } }); diff --git a/ecsact/wasm/detail/cpp_util.hh b/ecsact/wasm/detail/cpp_util.hh new file mode 100644 index 0000000..1db6dbc --- /dev/null +++ b/ecsact/wasm/detail/cpp_util.hh @@ -0,0 +1,33 @@ +#pragma once + +#include + +#ifndef defer +struct deferred_invoker {}; + +template +struct deferred { + F _defer_fn; + + ~deferred() { + _defer_fn(); + } +}; + +template +deferred operator*(deferred_invoker, F&& f) { + return {std::forward(f)}; +} + +/** + * USAGE: defer { statements; } + */ +# define defer auto _deferred##__LINE__ = deferred_invoker{}* [&]() +#endif // defer + +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...) -> overloaded; diff --git a/ecsact/wasm/detail/globals.cc b/ecsact/wasm/detail/globals.cc new file mode 100644 index 0000000..3802308 --- /dev/null +++ b/ecsact/wasm/detail/globals.cc @@ -0,0 +1,12 @@ +#include "ecsact/wasm/detail/globals.hh" + +namespace { +wasm_engine_t* _engine = nullptr; +} + +auto ecsact::wasm::detail::engine() -> wasm_engine_t* { + if(!_engine) { + _engine = wasm_engine_new(); + } + return _engine; +} diff --git a/ecsact/wasm/detail/globals.hh b/ecsact/wasm/detail/globals.hh new file mode 100644 index 0000000..3aeda87 --- /dev/null +++ b/ecsact/wasm/detail/globals.hh @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace ecsact::wasm::detail { + auto engine() -> wasm_engine_t*; +} diff --git a/ecsact/wasm/detail/guest_imports.hh b/ecsact/wasm/detail/guest_imports.hh index b266fd2..2c1e3f1 100644 --- a/ecsact/wasm/detail/guest_imports.hh +++ b/ecsact/wasm/detail/guest_imports.hh @@ -1,16 +1,17 @@ #pragma once -#include +#include #include #include #include +#include "ecsact/wasm/detail/minst/minst.hh" -namespace ecsactsi_wasm::detail { +namespace ecsact::wasm::detail { using allowed_guest_imports_t = std::unordered_map< - std::string, // Function name - std::function>; + std::string_view, // Function name + std::function>; using allowed_guest_modules_t = std::unordered_map< - std::string, // Module name + std::string_view, // Module name allowed_guest_imports_t>; } // namespace ecsactsi_wasm::detail diff --git a/ecsact/wasm/detail/guest_imports/env.hh b/ecsact/wasm/detail/guest_imports/env.hh index 97b6731..88ac25d 100644 --- a/ecsact/wasm/detail/guest_imports/env.hh +++ b/ecsact/wasm/detail/guest_imports/env.hh @@ -9,207 +9,141 @@ namespace ecsact::wasm::detail { const auto guest_env_module_imports = allowed_guest_imports_t{ { "ecsact_system_execution_context_action", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32) // out_action_data - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_action - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32) // out_action_data + ), + &wasm_ecsact_system_execution_context_action, + }; }, }, { "ecsact_system_execution_context_parent", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_1_1( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32) // parent context (return) - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_parent - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_1_1( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32) // parent context (return) + ), + &wasm_ecsact_system_execution_context_parent, + }; }, }, { "ecsact_system_execution_context_same", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_1( - wasm_valtype_new(WASM_I32), // context a - wasm_valtype_new(WASM_I32), // context b - wasm_valtype_new(WASM_I32) // same bool (return) - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_same - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_1( + wasm_valtype_new(WASM_I32), // context a + wasm_valtype_new(WASM_I32), // context b + wasm_valtype_new(WASM_I32) // same bool (return) + ), + &wasm_ecsact_system_execution_context_same, + }; }, }, { "ecsact_system_execution_context_get", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_3_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32), // component_id - wasm_valtype_new(WASM_I32) // out_component_data - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_get - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_3_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32), // component_id + wasm_valtype_new(WASM_I32) // out_component_data + ), + &wasm_ecsact_system_execution_context_get, + }; }, }, { "ecsact_system_execution_context_update", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_3_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32), // component_id - wasm_valtype_new(WASM_I32) // component_data - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_update - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_3_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32), // component_id + wasm_valtype_new(WASM_I32) // component_data + ), + &wasm_ecsact_system_execution_context_update, + }; }, }, { "ecsact_system_execution_context_has", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32) // component_id - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_has - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32) // component_id + ), + &wasm_ecsact_system_execution_context_has, + }; }, }, { "ecsact_system_execution_context_generate", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_4_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32), // component_count - wasm_valtype_new(WASM_I32), // component_ids - wasm_valtype_new(WASM_I32) // components_data - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_generate - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_4_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32), // component_count + wasm_valtype_new(WASM_I32), // component_ids + wasm_valtype_new(WASM_I32) // components_data + ), + &wasm_ecsact_system_execution_context_generate, + }; }, }, { "ecsact_system_execution_context_add", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_3_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32), // component_id - wasm_valtype_new(WASM_I32) // component_data - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_add - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_3_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32), // component_id + wasm_valtype_new(WASM_I32) // component_data + ), + &wasm_ecsact_system_execution_context_add, + }; }, }, { "ecsact_system_execution_context_remove", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_0( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32) // component_id - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_remove - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_0( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32) // component_id + ), + &wasm_ecsact_system_execution_context_remove, + }; }, }, { "ecsact_system_execution_context_other", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_1( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32), // entity_id - wasm_valtype_new(WASM_I32) // other context (return) - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_other - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_1( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32), // entity_id + wasm_valtype_new(WASM_I32) // other context (return) + ), + &wasm_ecsact_system_execution_context_other, + }; }, }, { "ecsact_system_execution_context_entity", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_1_1( - wasm_valtype_new(WASM_I32), // context - wasm_valtype_new(WASM_I32) // entity 9return) - ); - wasm_func_t* fn = wasm_func_new( - store, - fn_type, - &wasm_ecsact_system_execution_context_entity - ); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_1_1( + wasm_valtype_new(WASM_I32), // context + wasm_valtype_new(WASM_I32) // entity 9return) + ), + &wasm_ecsact_system_execution_context_entity, + }; }, }, }; diff --git a/ecsact/wasm/detail/guest_imports/wasi_snapshot_preview1.hh b/ecsact/wasm/detail/guest_imports/wasi_snapshot_preview1.hh index 8447eec..34f309e 100644 --- a/ecsact/wasm/detail/guest_imports/wasi_snapshot_preview1.hh +++ b/ecsact/wasm/detail/guest_imports/wasi_snapshot_preview1.hh @@ -8,128 +8,109 @@ namespace ecsact::wasm::detail { const auto guest_wasi_module_imports = allowed_guest_imports_t{ { "proc_exit", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = - wasm_functype_new_1_0(wasm_valtype_new(WASM_I32) // exit_code - ); - wasm_func_t* fn = wasm_func_new(store, fn_type, &ecsactsi_wasi_proc_exit); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_1_0( // + wasm_valtype_new(WASM_I32) // exit_code + ), + &ecsactsi_wasi_proc_exit, + }; }, }, { "fd_seek", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_4_1( - wasm_valtype_new_i32(), // fd - wasm_valtype_new_i64(), // offset - wasm_valtype_new_i32(), // whence - wasm_valtype_new_i32(), // retptr0 - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = wasm_func_new(store, fn_type, &ecsactsi_wasi_fd_seek); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_4_1( + wasm_valtype_new_i32(), // fd + wasm_valtype_new_i64(), // offset + wasm_valtype_new_i32(), // whence + wasm_valtype_new_i32(), // retptr0 + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_fd_seek, + }; }, }, { "fd_write", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_4_1( - wasm_valtype_new_i32(), // fd - wasm_valtype_new_i32(), // iovs - wasm_valtype_new_i32(), // iovs_len - wasm_valtype_new_i32(), // retptr0 - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = wasm_func_new(store, fn_type, &ecsactsi_wasi_fd_write); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_4_1( + wasm_valtype_new_i32(), // fd + wasm_valtype_new_i32(), // iovs + wasm_valtype_new_i32(), // iovs_len + wasm_valtype_new_i32(), // retptr0 + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_fd_write, + }; }, }, { "fd_read", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_4_1( - wasm_valtype_new_i32(), // fd - wasm_valtype_new_i32(), // iovs - wasm_valtype_new_i32(), // iovs_len - wasm_valtype_new_i32(), // retptr0 - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = wasm_func_new(store, fn_type, &ecsactsi_wasi_fd_read); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_4_1( + wasm_valtype_new_i32(), // fd + wasm_valtype_new_i32(), // iovs + wasm_valtype_new_i32(), // iovs_len + wasm_valtype_new_i32(), // retptr0 + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_fd_read, + }; }, }, { "fd_close", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_1_1( - wasm_valtype_new_i32(), // fd - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = wasm_func_new(store, fn_type, &ecsactsi_wasi_fd_close); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_1_1( + wasm_valtype_new_i32(), // fd + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_fd_close, + }; }, }, { "environ_sizes_get", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_1( - wasm_valtype_new_i32(), // retptr0 - wasm_valtype_new_i32(), // retptr1 - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = - wasm_func_new(store, fn_type, &ecsactsi_wasi_environ_sizes_get); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_1( + wasm_valtype_new_i32(), // retptr0 + wasm_valtype_new_i32(), // retptr1 + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_environ_sizes_get, + }; }, }, { "environ_get", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_1( - wasm_valtype_new_i32(), // environ - wasm_valtype_new_i32(), // environ_buf - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = - wasm_func_new(store, fn_type, &ecsactsi_wasi_environ_get); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_1( + wasm_valtype_new_i32(), // environ + wasm_valtype_new_i32(), // environ_buf + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_environ_get, + }; }, }, { "fd_fdstat_get", - [](wasm_store_t* store) -> wasm_func_t* { - wasm_functype_t* fn_type = wasm_functype_new_2_1( - wasm_valtype_new_i32(), // fd - wasm_valtype_new_i32(), // retptr0 - wasm_valtype_new_i32() // error code (return) - ); - wasm_func_t* fn = - wasm_func_new(store, fn_type, &ecsactsi_wasi_fd_fdstat_get); - - // wasm_functype_delete(fn_type); - - return fn; + []() -> minst_import_resolve_func { + return { + wasm_functype_new_2_1( + wasm_valtype_new_i32(), // fd + wasm_valtype_new_i32(), // retptr0 + wasm_valtype_new_i32() // error code (return) + ), + &ecsactsi_wasi_fd_fdstat_get, + }; }, }, }; diff --git a/ecsact/wasm/detail/minst/minst.cc b/ecsact/wasm/detail/minst/minst.cc index 0f5cd29..e2bcfb5 100644 --- a/ecsact/wasm/detail/minst/minst.cc +++ b/ecsact/wasm/detail/minst/minst.cc @@ -1,9 +1,10 @@ -#include "minst.hh" +#include "ecsact/wasm/detail/minst/minst.hh" #include #include #include #include +#include "ecsact/wasm/detail/cpp_util.hh" using ecsact::wasm::detail::minst; using ecsact::wasm::detail::minst_export; @@ -12,14 +13,6 @@ using ecsact::wasm::detail::minst_import_resolve_func; using ecsact::wasm::detail::minst_trap; namespace { -// https://en.cppreference.com/w/cpp/utility/variant/visit -template -struct overloaded : Ts... { - using Ts::operator()...; -}; -template -overloaded(Ts...) -> overloaded; - auto get_wasmer_last_error_message() -> std::string { auto msg = std::string{}; msg.resize(wasmer_last_error_length()); @@ -110,7 +103,7 @@ auto minst::create( // wasm_engine_t* engine, std::span wasm_data, import_resolver_t import_resolver -) -> std::expected { +) -> std::variant { auto self = minst{}; auto wasm_bytes = wasm_byte_vec_t{ @@ -123,10 +116,10 @@ auto minst::create( // self._module = wasm_module_new(self._store, &wasm_bytes); if(self._module == nullptr) { - return std::unexpected(minst_error{ + return minst_error{ minst_error_code::compile_fail, get_wasmer_last_error_message(), - }); + }; } auto imports = wasm_importtype_vec_t{}; @@ -149,14 +142,14 @@ auto minst::create( // auto guest_import_resolve = import_resolver(self._imports[i]); if(!guest_import_resolve) { - return std::unexpected(minst_error{ + return minst_error{ minst_error_code::unresolved_guest_import, std::format( "Guest import '{}.{}' is unresolved", self._imports[i].module(), self._imports[i].name() ), - }); + }; } wasm_extern_t* guest_import_extern = std::visit( @@ -180,10 +173,10 @@ auto minst::create( // ); if(self._instance == nullptr) { - return std::unexpected(minst_error{ + return minst_error{ minst_error_code::instantiate_fail, get_wasmer_last_error_message(), - }); + }; } auto inst_exports = wasm_extern_vec_t{}; @@ -262,3 +255,27 @@ auto minst::initialize() -> std::optional { return std::nullopt; } + +auto minst::find_import( // + std::string_view module_name, + std::string_view import_name +) -> std::optional { + for(auto imp : imports()) { + if(imp.module() == module_name && imp.name() == import_name) { + return imp; + } + } + + return std::nullopt; +} +auto minst::find_export( // + std::string_view export_name +) -> std::optional { + for(auto exp : exports()) { + if(exp.name() == export_name) { + return exp; + } + } + + return std::nullopt; +} diff --git a/ecsact/wasm/detail/minst/minst.hh b/ecsact/wasm/detail/minst/minst.hh index b375721..19566ff 100644 --- a/ecsact/wasm/detail/minst/minst.hh +++ b/ecsact/wasm/detail/minst/minst.hh @@ -1,113 +1,128 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ecsact::wasm::detail { - -enum class minst_error_code { - ok, - compile_fail, - unresolved_guest_import, - instantiate_fail, -}; - -struct minst_error { - minst_error_code code = {}; - std::string message = {}; -}; - -class minst_trap { -public: - minst_trap(wasm_trap_t* trap); - minst_trap(minst_trap&& other); - ~minst_trap(); - - auto message() const -> std::string; - -private: - wasm_trap_t* trap; -}; - -struct minst_import_resolve_func { - wasm_functype_t* func_type; - wasm_func_callback_t func_callback; - - auto as_extern(wasm_store_t* store) -> wasm_extern_t*; -}; - -using minst_import_resolve_t = - std::optional>; - -struct minst_import { - wasm_importtype_t* import_type; - - auto name() const -> std::string_view; - auto module() const -> std::string_view; - auto kind() const -> wasm_externkind_enum; -}; - -struct minst_export { - wasm_exporttype_t* export_type; - - union { - wasm_func_t* func; - wasm_global_t* global; - wasm_table_t* table; - wasm_memory_t* memory; - }; - - auto name() const -> std::string_view; - auto kind() const -> wasm_externkind_enum; - - auto func_call() -> std::optional; -}; - -/** - * WebAssembly module and instance (minst) - */ -class minst { -public: - using import_resolver_t = - std::function; - - static auto create( // - wasm_engine_t* engine, - std::span wasm_data, - import_resolver_t import_resolver - ) -> std::expected; - - minst(minst&& other); - ~minst(); - - auto imports() -> std::span; - auto exports() -> std::span; - - /** - * Convenience function for invoking the exported `_initialize` function - * (if present.) - */ - auto initialize() -> std::optional; - -private: - minst(); - - wasm_engine_t* _engine = {}; - wasm_store_t* _store = {}; - wasm_module_t* _module = {}; - wasm_instance_t* _instance = {}; - - std::vector _imports; - std::vector _exports; -}; - -} // namespace ecsact::wasm::detail +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ecsact::wasm::detail { + +enum class minst_error_code { + ok, + compile_fail, + unresolved_guest_import, + instantiate_fail, +}; + +struct minst_error { + minst_error_code code = {}; + std::string message = {}; +}; + +class minst_trap { +public: + minst_trap(wasm_trap_t* trap); + minst_trap(minst_trap&& other); + ~minst_trap(); + + auto message() const -> std::string; + +private: + wasm_trap_t* trap; +}; + +struct minst_import_resolve_func { + wasm_functype_t* func_type; + wasm_func_callback_t func_callback; + + auto as_extern(wasm_store_t* store) -> wasm_extern_t*; +}; + +using minst_import_resolve_t = + std::optional>; + +struct minst_import { + wasm_importtype_t* import_type; + + auto name() const -> std::string_view; + auto module() const -> std::string_view; + auto kind() const -> wasm_externkind_enum; +}; + +struct minst_export { + wasm_exporttype_t* export_type; + + union { + wasm_func_t* func; + wasm_global_t* global; + wasm_table_t* table; + wasm_memory_t* memory; + }; + + auto name() const -> std::string_view; + auto kind() const -> wasm_externkind_enum; + + auto func_call() -> std::optional; +}; + +/** + * WebAssembly module and instance (minst) + */ +class minst { +public: + using import_resolver_t = + std::function; + + static auto create( // + wasm_engine_t* engine, + std::span wasm_data, + import_resolver_t import_resolver + ) -> std::variant; + + /** + * Create a new minst with internal memory copied over. If 'initialize' was + * already called then the memory should be identical, thus 'initialize' + * should not be called again. + */ + static auto copy(minst& other) -> minst; + + minst(minst&& other); + ~minst(); + + auto imports() -> std::span; + auto exports() -> std::span; + + /** + * Convenience function for invoking the exported `_initialize` function + * (if present.) + */ + auto initialize() -> std::optional; + + auto find_import( // + std::string_view module_name, + std::string_view import_name + ) -> std::optional; + + auto find_export( // + std::string_view export_name + ) -> std::optional; + +private: + minst(); + + wasm_engine_t* _engine = {}; + wasm_store_t* _store = {}; + wasm_module_t* _module = {}; + wasm_instance_t* _instance = {}; + + std::vector _imports; + std::vector _exports; +}; + +} // namespace ecsact::wasm::detail diff --git a/ecsact/wasm/detail/wasm.cc b/ecsact/wasm/detail/wasm.cc index b1b7d5b..9284475 100644 --- a/ecsact/wasm/detail/wasm.cc +++ b/ecsact/wasm/detail/wasm.cc @@ -23,11 +23,20 @@ #include "ecsact/wasm/detail/minst/minst.hh" #include "ecsact/wasm/detail/logger.hh" #include "ecsact/wasm/detail/wasi_fs.hh" +#include "ecsact/wasm/detail/globals.hh" +#include "ecsact/wasm/detail/guest_imports/wasi_snapshot_preview1.hh" +#include "ecsact/wasm/detail/guest_imports/env.hh" +#include "ecsact/wasm/detail/cpp_util.hh" using namespace std::string_literals; using ecsact::wasm::detail::clear_log_lines; using ecsact::wasm::detail::consume_stdio_str_as_log_lines; using ecsact::wasm::detail::get_log_lines; +using ecsact::wasm::detail::guest_env_module_imports; +using ecsact::wasm::detail::guest_wasi_module_imports; +using ecsact::wasm::detail::minst_error; +using ecsact::wasm::detail::minst_import; +using ecsact::wasm::detail::minst_import_resolve_t; using ecsact::wasm::detail::start_transaction; namespace { @@ -79,8 +88,80 @@ ecsactsi_wasm_error ecsactsi_wasm_load( ecsact_system_like_id* system_ids, const char** wasm_exports ) { - // auto result = ecsact::wasm::detail::minst::create(); - return ECSACTSI_WASM_ERR_COMPILE_FAIL; + using ecsact::wasm::detail::minst; + using ecsact::wasm::detail::minst_error_code; + using ecsact::wasm::detail::engine; + + auto result = ecsact::wasm::detail::minst::create( + engine(), + std::span{ + reinterpret_cast(wasm_data), + static_cast(wasm_data_size), + }, + [&](const minst_import imp) -> minst_import_resolve_t { + auto module_name = imp.module(); + auto method_name = imp.name(); + + if(imp.module() == "env") { + auto itr = guest_env_module_imports.find(method_name); + if(itr == guest_env_module_imports.end()) { + return std::nullopt; + } + return itr->second(); + } + + if(imp.module() == "wasi_snapshot_preview1") { + auto itr = guest_wasi_module_imports.find(method_name); + if(itr == guest_wasi_module_imports.end()) { + return std::nullopt; + } + return itr->second(); + } + + return std::nullopt; + } + ); + + if(std::holds_alternative(result)) { + auto err = std::get(result); + switch(err.code) { + case minst_error_code::ok: + assert(err.code != minst_error_code::ok); + case minst_error_code::compile_fail: + return ECSACTSI_WASM_ERR_COMPILE_FAIL; + case minst_error_code::unresolved_guest_import: + return ECSACTSI_WASM_ERR_GUEST_IMPORT_INVALID; + case minst_error_code::instantiate_fail: + return ECSACTSI_WASM_ERR_INSTANTIATE_FAIL; + } + } + + auto& inst = std::get(result); + + for(auto i=0; systems_count > i; ++i) { + auto sys_id = system_ids[i]; + auto system_impl_export_name = std::string_view{ + wasm_exports[i], + std::strlen(wasm_exports[i]), + }; + + auto exp = inst.find_export(system_impl_export_name); + + if(!exp) { + return ECSACTSI_WASM_ERR_EXPORT_NOT_FOUND; + } + + if(exp->kind() != WASM_EXTERN_FUNC) { + return ECSACTSI_WASM_ERR_EXPORT_INVALID; + } + } + + auto init_trap = inst.initialize(); + if(init_trap) { + return ECSACTSI_WASM_ERR_INITIALIZE_FAIL; + } + + return ECSACTSI_WASM_OK; } ecsactsi_wasm_error ecsactsi_wasm_load_file( diff --git a/test/WORKSPACE.bazel b/test/WORKSPACE.bazel index 6a6ffb5..50163d0 100644 --- a/test/WORKSPACE.bazel +++ b/test/WORKSPACE.bazel @@ -1,5 +1,38 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "com_grail_bazel_toolchain", + sha256 = "5fb0a6ae0f5bc72a7d80e6de0edad906c9936729734009f1c01dac4b06f966e0", + strip_prefix = "bazel-toolchain-f94335f1f5434256b1793dafbb7dd07773b0e76e", + url = "https://github.com/grailbio/bazel-toolchain/archive/f94335f1f5434256b1793dafbb7dd07773b0e76e.zip", +) + +load("@com_grail_bazel_toolchain//toolchain:deps.bzl", "bazel_toolchain_dependencies") + +bazel_toolchain_dependencies() + +load("@com_grail_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain") + +llvm_toolchain( + name = "llvm_toolchain", + llvm_version = "16.0.4", +) + +load("@llvm_toolchain//:toolchains.bzl", "llvm_register_toolchains") + +llvm_register_toolchains() + +http_archive( + name = "hedron_compile_commands", + sha256 = "9d1ce53443747dd574a309fbe6774d1cfc94d20abdb7e7de1bb337f9437761bb", + strip_prefix = "bazel-compile-commands-extractor-86dbf526c56cebb2c3a060c09fe8c0458a694d23", + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/86dbf526c56cebb2c3a060c09fe8c0458a694d23.tar.gz", +) + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") + +hedron_compile_commands_setup() + http_archive( name = "emsdk", sha256 = "2e9958d05cbb31e4464de23938884db9562c83a3ffc0b0b67ebc29c7f951f384", diff --git a/test/copts.bzl b/test/copts.bzl deleted file mode 100644 index f45eafd..0000000 --- a/test/copts.bzl +++ /dev/null @@ -1,16 +0,0 @@ -load("@bazel_skylib//lib:selects.bzl", "selects") - -copts = selects.with_or({ - ("//:compiler_emscripten"): [ - "-std=c++20", - ], - ("@rules_cc//cc/compiler:clang"): [ - "-std=c++2b", - "-fexperimental-library", - ], - ("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [ - "/std:c++latest", - "/permissive-", - "/Zc:preprocessor", - ], -}) diff --git a/test/example/BUILD.bazel b/test/example/BUILD.bazel index a56e093..0e544be 100644 --- a/test/example/BUILD.bazel +++ b/test/example/BUILD.bazel @@ -65,7 +65,10 @@ cc_binary( "example.cc", ], copts = copts, - defines = ["ECSACTSI_WASM_API="], + defines = [ + "ECSACTSI_WASM_API=", + "ECSACT_CORE_API=", + ], deps = [ ":runtime", "@ecsact_si_wasm//:minst", diff --git a/test/example/example.cc b/test/example/example.cc index 6198d33..cce17b1 100644 --- a/test/example/example.cc +++ b/test/example/example.cc @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "magic_enum.hpp" #include "ecsact/runtime/core.h" #include "ecsact/runtime/core.hh" @@ -57,8 +57,7 @@ const auto test_systems = std::array{ }; void print_load_error(ecsactsi_wasm_error err, const std::string& wasm_path) { - std::print( - stderr, + std::cerr << std::format( "[ERROR] loading wasm file {} failed: {}\n{}\n", wasm_path, magic_enum::enum_name(err), diff --git a/test/minst_test.cc b/test/minst_test.cc index 9144c28..c263238 100644 --- a/test/minst_test.cc +++ b/test/minst_test.cc @@ -14,6 +14,7 @@ namespace fs = std::filesystem; using bazel::tools::cpp::runfiles::Runfiles; using ecsact::wasm::detail::minst; +using ecsact::wasm::detail::minst_error; using ecsact::wasm::detail::minst_export; using ecsact::wasm::detail::minst_import; using ecsact::wasm::detail::minst_import_resolve_func; @@ -82,8 +83,9 @@ auto main(int argc, char* argv[]) -> int { test_guest_import_resolver ); - if(result) { - if(auto trap = result->initialize()) { + if(std::holds_alternative(result)) { + auto& inst = std::get(result); + if(auto trap = inst.initialize()) { std::cerr // << "[INITIALIZE TRAP]: " << trap->message() << std::endl; return 1; @@ -91,7 +93,7 @@ auto main(int argc, char* argv[]) -> int { auto minst_test_export_fn = std::optional{}; - for(auto exp : result->exports()) { + for(auto exp : inst.exports()) { if(exp.name() == "minst_test_export_fn") { minst_test_export_fn = exp; } @@ -117,7 +119,10 @@ auto main(int argc, char* argv[]) -> int { } } else { - std::cerr << "[ERROR]: " << result.error().message << std::endl; + std::cerr << std::format( // + "[ERROR]: {}\n", + std::get(result).message + ); return 1; } }