3#include <sysio/vm/allocator.hpp>
4#include <sysio/vm/execution_interface.hpp>
5#include <sysio/vm/function_traits.hpp>
6#include <sysio/vm/argument_proxy.hpp>
7#include <sysio/vm/span.hpp>
8#include <sysio/vm/utils.hpp>
9#include <sysio/vm/wasm_stack.hpp>
20#include <unordered_map>
24namespace sysio {
namespace vm {
30 template <
typename Host_Type=standalone_function_t,
typename Execution_Interface=execution_
interface>
43 template <
typename T,
typename U>
61#define SYS_VM_FROM_WASM_ADD_TAG(...) (__VA_ARGS__, ::sysio::vm::tag<T> = {})
63#define SYS_VM_FROM_WASM(TYPE, PARAMS) \
64 template <typename T> \
65 auto from_wasm SYS_VM_FROM_WASM_ADD_TAG PARAMS const -> std::enable_if_t<std::is_same_v<T, TYPE>, TYPE>
67 template <
typename Host,
typename Execution_Interface=execution_
interface>
83 -> std::enable_if_t<is_span_type_v<T>,
T> {
85 return {
static_cast<typename T::pointer
>(
p),
len};
90 -> std::enable_if_t< is_argument_proxy_type_v<T> &&
91 is_span_type_v<typename T::proxy_type>,
T> {
98 -> std::enable_if_t< is_argument_proxy_type_v<T> &&
99 std::is_pointer_v<typename T::proxy_type>,
T> {
106 if constexpr (std::is_integral_v<T> &&
sizeof(
T) == 4)
107 return static_cast<T>(val.template get<i32_const_t>().data.ui);
108 else if constexpr (std::is_integral_v<T> &&
sizeof(
T) == 8)
109 return static_cast<T>(val.template get<i64_const_t>().data.ui);
110 else if constexpr (std::is_floating_point_v<T> &&
sizeof(
T) == 4)
111 return static_cast<T>(val.template get<f32_const_t>().data.f);
112 else if constexpr (std::is_floating_point_v<T> &&
sizeof(
T) == 8)
113 return static_cast<T>(val.template get<f64_const_t>().data.f);
114 else if constexpr (std::is_void_v<std::decay_t<std::remove_pointer_t<T>>>)
120 template <
typename T>
122 if constexpr (std::is_integral_v<T> &&
sizeof(
T) == 4)
123 return i32_const_t{
static_cast<uint32_t>(val) };
124 else if constexpr (std::is_integral_v<T> &&
sizeof(
T) == 8)
125 return i64_const_t{
static_cast<uint64_t>(val) };
126 else if constexpr (std::is_floating_point_v<T> &&
sizeof(
T) == 4)
127 return f32_const_t{
static_cast<float>(val) };
128 else if constexpr (std::is_floating_point_v<T> &&
sizeof(
T) == 8)
129 return f64_const_t{
static_cast<double>(val) };
130 else if constexpr (std::is_void_v<std::decay_t<std::remove_pointer_t<T>>>)
144 template<
typename Tuple, std::size_t...
N>
145 std::tuple<std::tuple_element_t<N, Tuple>...>
tuple_select(std::index_sequence<N...>);
148 using strip_tag = std::conditional_t<is_tag_v<std::tuple_element_t<std::tuple_size_v<T> - 1,
T>>,
149 decltype(
tuple_select<T>(std::make_index_sequence<std::tuple_size_v<T> - 1>())),
152 template <
class TC,
typename T>
154 template <
class TC,
typename T>
157 template <std::
size_t N,
typename Type_Converter>
158 inline constexpr const auto&
pop_value(Type_Converter& tc) {
return tc.get_interface().operand_from_back(
N); }
160 template <
typename S,
typename Type_Converter>
162 return !std::is_same_v<no_match_t, std::decay_t<
decltype(
163 std::declval<Type_Converter>().template as_value<S>(
pop_value<0>(std::declval<Type_Converter&>())))>>;
166 template <
typename S,
typename Type_Converter>
168 return !std::is_same_v<no_match_t, std::decay_t<
decltype(
169 std::declval<Type_Converter>().template as_result<S>(std::declval<S&&>()))>>;
172 template <
typename T,
class Type_Converter>
177 return std::tuple_size_v<from_wasm_type_deducer_t<Type_Converter, T>>;
180 template <
typename T,
class Type_Converter>
183 template <
typename Args, std::
size_t I,
class Type_Converter>
185 if constexpr (I >= std::tuple_size_v<Args>)
188 constexpr std::size_t sz = value_operand_size_v<std::tuple_element_t<I, Args>, Type_Converter>;
193 template <
typename Args,
class Type_Converter>
196 template <
typename S,
typename Type_Converter>
199 template <
typename S,
typename Type_Converter>
200 constexpr inline static bool has_to_wasm_v =
201 !std::is_same_v<no_match_t, to_wasm_type_deducer_t<Type_Converter, S>>;
203 template <
typename Args,
typename S, std::size_t At,
class Type_Converter, std::size_t... Is>
204 inline constexpr decltype(
auto)
create_value(Type_Converter& tc, std::index_sequence<Is...>) {
205 constexpr std::size_t offset = total_operands_v<Args, Type_Converter> - 1;
206 if constexpr (has_from_wasm_v<S, Type_Converter>) {
208 return tc.template from_wasm<S>(tc.template as_value<std::tuple_element_t<Is, arg_types>>(
pop_value<offset - (At + Is)>(tc))...);
215 template <
typename S,
typename Type_Converter>
220 return std::tuple_size_v<from_wasm_type_deducer_t<Type_Converter, S>>;
224 template <
typename Args, std::
size_t At, std::
size_t Skip_Amt,
class Type_Converter>
225 inline constexpr auto get_values(Type_Converter& tc) {
226 if constexpr (At >= std::tuple_size_v<Args>)
227 return std::tuple<>{};
229 using source_t = std::tuple_element_t<At, Args>;
238 template <
typename Type_Converter,
typename T>
240 if constexpr (has_to_wasm_v<T, Type_Converter>) {
241 return tc.as_result(tc.to_wasm(
static_cast<T&&
>(val)));
243 return tc.as_result(
static_cast<T&&
>(val));
247 template <
bool Once, std::
size_t Cnt,
typename T,
typename F>
249 if constexpr (Once && Cnt == 0) {
254 template <
bool Once, std::size_t Cnt,
typename T,
typename F,
typename Arg,
typename... Args>
255 inline constexpr void invoke_on_impl(F&& fn,
const Arg& arg,
const Args&... args) {
256 if constexpr (Once) {
257 if constexpr (Cnt == 0)
258 std::invoke(fn, arg, args...);
260 if constexpr (std::is_same_v<T, Arg> || std::is_same_v<T, invoke_on_all_t>)
261 std::invoke(fn, arg, args...);
266 template <
typename Precondition,
typename Type_Converter,
typename Args, std::size_t... Is>
267 inline static void precondition_runner(Type_Converter& ctx,
const Args& args, std::index_sequence<Is...>) {
268 Precondition::condition(ctx, std::get<Is>(args)...);
271 template <std::
size_t I,
typename Preconditions,
typename Type_Converter,
typename Args>
272 inline static void preconditions_runner(Type_Converter& ctx,
const Args& args) {
273 constexpr std::size_t args_size = std::tuple_size_v<Args>;
274 constexpr std::size_t preconds_size = std::tuple_size_v<Preconditions>;
275 if constexpr (I < preconds_size) {
276 precondition_runner<std::tuple_element_t<I, Preconditions>>(ctx, args, std::make_index_sequence<args_size>{});
277 preconditions_runner<I+1, Preconditions>(ctx, args);
282 template <
bool Once,
typename T,
typename F,
typename... Args>
287#define SYS_VM_INVOKE_ON(TYPE, CONDITION) \
288 sysio::vm::invoke_on<false, TYPE>(CONDITION, args...);
290#define SYS_VM_INVOKE_ON_ALL(CONDITION) \
291 sysio::vm::invoke_on<false, sysio::vm::invoke_on_all_t>(CONDITION, args...);
293#define SYS_VM_INVOKE_ONCE(CONDITION) \
294 sysio::vm::invoke_on<true, sysio::vm::invoke_on_all_t>(CONDITION, args...);
296#define SYS_VM_PRECONDITION(NAME, ...) \
298 template <typename Type_Converter, typename... Args> \
299 inline static decltype(auto) condition(Type_Converter& ctx, const Args&... args) { \
304 template <
auto F,
typename Preconditions,
typename Type_Converter,
typename Host,
typename... Args>
305 decltype(
auto)
invoke_impl(Type_Converter& tc, Host* host, Args&&... args) {
306 if constexpr (std::is_same_v<Host, standalone_function_t>)
307 return std::invoke(F,
static_cast<Args&&
>(args)...);
309 return std::invoke(F, host,
static_cast<Args&&
>(args)...);
312 template <
auto F,
typename Preconditions,
typename Host,
typename Args,
typename Type_Converter, std::size_t... Is>
313 decltype(
auto)
invoke_with_host_impl(Type_Converter& tc, Host* host, Args&& args, std::index_sequence<Is...>) {
314 detail::preconditions_runner<0, Preconditions>(tc, args);
318 template <
auto F,
typename Preconditions,
typename Args,
typename Type_Converter,
typename Host, std::size_t... Is>
319 decltype(
auto)
invoke_with_host(Type_Converter& tc, Host* host, std::index_sequence<Is...>) {
320 constexpr std::size_t args_size = std::tuple_size_v<decltype(detail::get_values<Args, 0, 0>(tc))>;
324 template<
typename Type_Converter,
typename T>
326 if constexpr (!std::is_same_v<std::decay_t<T>, maybe_void_t>) {
327 tc.get_interface().trim_operands(trim_amt);
330 tc.get_interface().trim_operands(trim_amt);
334 template <
typename Cls,
auto F,
typename Preconditions,
typename R,
typename Args,
typename Type_Converter,
size_t... Is>
336 return std::function<void(Cls*, Type_Converter& )>{ [](Cls*
self, Type_Converter& tc) {
338 detail::total_operands_v<Args, Type_Converter>);
354 template <
typename TC,
typename T>
356 template <
typename TC>
360 std::vector<value_type>
params;
361 std::vector<value_type>
ret;
370 inline bool operator==(
const func_type& lhs,
const host_function& rhs) {
374 template<
typename TC,
typename Args, std::size_t... Is>
379 template<
typename Type_Converter,
typename T>
381 if constexpr (detail::has_from_wasm_v<T, Type_Converter>) {
389 template <
typename Type_Converter,
typename Ret,
typename Args, std::size_t... Is>
392 hf.
params.resize(detail::total_operands_v<Args, Type_Converter>);
393#if (defined(__GNUC__) && !defined(__clang__))
394#pragma GCC diagnostic push
395#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
398# pragma GCC diagnostic pop
412 template <
class T,
class U>
414 return std::hash<T>()(
p.first) ^ std::hash<U>()(
p.second);
418 template <
typename Cls,
typename Execution_Interface=execution_
interface,
typename Type_Converter=type_converter<Cls, Execution_Interface>>
425 std::unordered_map<host_func_pair, uint32_t, host_func_pair_hash>
named_mapping;
426 std::vector<std::function<void(Cls*, Type_Converter&)>>
functions;
430 template <auto F,
typename R,
typename Args,
typename Preconditions>
435 std::make_index_sequence<std::tuple_size_v<Args>>()));
438 std::make_index_sequence<std::tuple_size_v<Args>>()));
447 template <
auto Func,
typename... Preconditions>
448 static void add(
const std::string& mod,
const std::string&
name) {
451 using preconditions = std::tuple<Preconditions...>;
455 template <
typename Module>
457 auto& imports = mod.import_functions;
459 for (std::size_t i = 0; i < mod.imports.size(); i++) {
460 std::string mod_name =
461 std::string((
char*)mod.imports[i].module_str.raw(), mod.imports[i].module_str.size());
462 std::string fn_name = std::string((
char*)mod.imports[i].field_str.raw(), mod.imports[i].field_str.size());
463 SYS_VM_ASSERT(current_mappings.named_mapping.count({ mod_name, fn_name }), wasm_link_exception,
464 std::string(
"no mapping for imported function ") + fn_name);
465 imports[i] = current_mappings.named_mapping[{ mod_name, fn_name }];
468 SYS_VM_ASSERT(current_mappings.host_functions[imports[i]] == mod.types[entry.
type.
func_t], wasm_link_exception, std::string(
"wrong type for imported function ") + fn_name);
474 auto tc = Type_Converter{
host, std::move(ei)};
475 std::invoke(
_func, host, tc);
std::tuple< std::tuple_element_t< N, Tuple >... > tuple_select(std::index_sequence< N... >)
std::conditional_t< is_tag_v< std::tuple_element_t< std::tuple_size_v< T > - 1, T > >, decltype(tuple_select< T >(std::make_index_sequence< std::tuple_size_v< T > - 1 >())), T > strip_tag
constexpr bool has_as_value()
constexpr const auto & pop_value(Type_Converter &tc)
decltype(std::declval< TC >().to_wasm(std::declval< T >())) to_wasm_type_deducer_t
constexpr std::size_t value_operand_size()
constexpr void invoke_on_impl(F &&fn)
constexpr auto resolve_result(Type_Converter &tc, T &&val)
constexpr std::size_t total_operands()
constexpr std::size_t skip_amount()
constexpr decltype(auto) create_value(Type_Converter &tc, std::index_sequence< Is... >)
constexpr auto get_values(Type_Converter &tc)
strip_tag< flatten_parameters_t<&TC::template from_wasm< T > > > from_wasm_type_deducer_t
constexpr bool has_as_result()
decltype(return_type(AUTO_PARAM_WORKAROUND(FN))) return_type_t
constexpr auto to_wasm_type< i32_const_t >()
host_function function_types_provider(std::index_sequence< Is... >)
constexpr auto to_wasm_type_v< TC, void >
constexpr auto to_wasm_type< i64_const_t >()
void get_args(value_type *&out, std::index_sequence< Is... >)
bool operator==(const host_function &lhs, const func_type &rhs)
decltype(auto) invoke_with_host(Type_Converter &tc, Host *host, std::index_sequence< Is... >)
std::nullptr_t standalone_function_t
decltype(flatten_parameters(AUTO_PARAM_WORKAROUND(FN))) flatten_parameters_t
constexpr auto to_wasm_type< f64_const_t >()
decltype(auto) invoke_impl(Type_Converter &tc, Host *host, Args &&... args)
decltype(auto) invoke_with_host_impl(Type_Converter &tc, Host *host, Args &&args, std::index_sequence< Is... >)
auto create_function(std::index_sequence< Is... >)
std::uint32_t wasm_size_t
constexpr auto to_wasm_type_v
std::pair< std::string, std::string > host_func_pair
constexpr auto to_wasm_type< f32_const_t >()
void maybe_push_result(Type_Converter &tc, T &&res, std::size_t trim_amt)
void invoke_on(F &&func, const Args &... args)
@ self
the connection is to itself
#define S(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
#define T(meth, val, expected)
schedule config_dir_name data_dir_name p2p_port http_port file_size name host(p2p_endpoint)) FC_REFLECT(tn_node_def
_W64 unsigned int uintptr_t
unsigned __int64 uint64_t
Immutable except for fc::from_variant.
guarded_vector< value_type > param_types
std::size_t operator()(const std::pair< T, U > &p) const
std::vector< value_type > params
std::vector< value_type > ret
std::vector< host_function > host_functions
std::vector< std::function< void(Cls *, Type_Converter &)> > functions
std::unordered_map< host_func_pair, uint32_t, host_func_pair_hash > named_mapping
void add_mapping(const std::string &mod, const std::string &name)
void operator()(Cls *host, Execution_Interface ei, uint32_t index)
static void add(const std::string &mod, const std::string &name)
Execution_Interface execution_interface_t
static void resolve(Module &mod)
Type_Converter type_converter_t
Execution_Interface & get_interface()
auto validate_null_terminated_pointer(T ptr) const
running_context(Host_Type *host, Execution_Interface &&ei)
auto validate_pointer(U ptr, wasm_size_t len) const
const Execution_Interface & get_interface() const
decltype(auto) get_host()
running_context(Host_Type *host, const Execution_Interface &ei)
Execution_Interface interface
void * access(wasm_ptr_t addr=0) const
constexpr auto as_result(T &&val) const
auto from_wasm(wasm_ptr_t ptr, wasm_size_t len, tag< T >={}) const -> std::enable_if_t< is_argument_proxy_type_v< T > &&is_span_type_v< typename T::proxy_type >, T >
uint32_t to_wasm(bool &&value)
SYS_VM_FROM_WASM(bool,(uint32_t value))
decltype(auto) as_value(const elem_type &val) const
auto from_wasm(wasm_ptr_t ptr, wasm_size_t len, tag< T >={}) const -> std::enable_if_t< is_span_type_v< T >, T >
auto from_wasm(wasm_ptr_t ptr, tag< T >={}) const -> std::enable_if_t< is_argument_proxy_type_v< T > &&std::is_pointer_v< typename T::proxy_type >, T >
#define SYS_VM_ASSERT(expr, exc_type, msg)
#define SYS_VM_HAS_TEMPLATE_MEMBER_TY(TY, NAME)
#define AUTO_PARAM_WORKAROUND(X)