PkmnLib/src/ScriptResolving/WASM/InterfaceMethods/WasmHelperFile.hpp

198 lines
8.7 KiB
C++

#ifndef PKMNLIB_HELPERFILE_H
#define PKMNLIB_HELPERFILE_H
#include <Arbutils/Memory/Memory.hpp>
#include <memory>
#include <sstream>
#include <type_traits>
#include <wasm.h>
#include "../WebAssemblyScriptResolver.hpp"
#include "wasm.h"
template <typename Test, template <typename...> class Ref> struct is_specialization : std::false_type {};
template <template <typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <template <typename...> class Ref, typename... Args>
struct is_specialization<const Ref<Args...>&, Ref> : std::true_type {};
template <template <typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>&, Ref> : std::true_type {};
struct WasmHelpers {
public:
static wasm_trap_t* CreateTrapFromException(const ArbUt::Exception& e, const WebAssemblyScriptResolver* resolver) {
std::stringstream ss;
ss << e.what() << std::endl;
ss << e.GetStacktrace() << std::endl;
wasm_message_t message;
wasm_name_new_from_string_nt(&message, ss.str().c_str());
wasm_trap_t* trap = wasm_trap_new(resolver->GetStore(), &message);
wasm_name_delete(&message);
return trap;
}
static wasm_trap_t* FromStdException(const std::exception& e, const WebAssemblyScriptResolver* resolver) {
wasm_message_t message;
wasm_name_new_from_string_nt(&message, e.what());
wasm_trap_t* trap = wasm_trap_new(resolver->GetStore(), &message);
wasm_name_delete(&message);
return trap;
}
template <typename T, typename R, R (T::*Method)() const>
static wasm_func_t* RegisterGetter(wasm_store_t* store, WebAssemblyScriptResolver* resolver) {
wasm_functype_t* type = wasm_functype_new_1_1(wasm_valtype_new_i64(), GetValType<R>());
auto* f = wasm_func_new_with_env(
store, type,
[](void* env, const wasm_val_vec_t* args, wasm_val_vec_t* returns) -> wasm_trap_t* {
try {
auto obj = (const T*)args->data[0].of.i64;
returns->data[0] = ToVal<R>((obj->*Method)());
} catch (ArbUt::Exception& e) {
return CreateTrapFromException(e, (WebAssemblyScriptResolver*)env);
} catch (std::exception& e) {
return FromStdException(e, (WebAssemblyScriptResolver*)env);
}
return nullptr;
},
resolver, nullptr);
wasm_functype_delete(type);
return f;
}
template <class R, class... Args>
static wasm_func_t* CreateFunc(wasm_store_t* store, WebAssemblyScriptResolver* resolver,
wasm_func_callback_with_env_t func) {
auto funcType = GetFuncType<R, Args...>();
auto* f = wasm_func_new_with_env(store, funcType, func, resolver, nullptr);
wasm_functype_delete(funcType);
return f;
}
template <typename T>
inline static T ConvertAllArguments(const wasm_val_vec_t* t, std::size_t& index,
WebAssemblyScriptResolver* resolver) {
return FromVal<T>(t->data[index++], resolver);
}
template <class R, class... Args>
static wasm_func_t* CreateFunc2(wasm_store_t* store, WebAssemblyScriptResolver* resolver,
std::function<R(WebAssemblyScriptResolver*, Args...)> func) {
auto funcType = GetFuncType<R, Args...>();
struct Env {
WebAssemblyScriptResolver* Resolver;
std::function<R(WebAssemblyScriptResolver*, Args...)> Func;
__attribute__((no_sanitize("address")))
~Env(){}
};
auto env = new Env{.Resolver = resolver, .Func = func};
auto* f = wasm_func_new_with_env(
store, funcType,
[](void* env, const wasm_val_vec_t* parameters, wasm_val_vec_t* results) -> wasm_trap_t* {
auto e = *(Env*)env;
size_t index = 0;
R result = e.Func(e.Resolver, ConvertAllArguments<Args>(parameters, index, e.Resolver)...);
results->data[0] = ToVal<R>(result);
return nullptr;
},
env,
[](void* env) __attribute__((no_sanitize("address"))) {
delete (Env*)env;
}
);
wasm_functype_delete(funcType);
return f;
}
private:
template <class T> inline static wasm_valtype_t* GetValType() {
if constexpr (std::is_pointer<T>() || is_specialization<T, ArbUt::BorrowedPtr>::value ||
is_specialization<T, ArbUt::OptionalBorrowedPtr>::value ||
is_specialization<T, std::unique_ptr>::value) {
return wasm_valtype_new_i64();
} else if constexpr (std::is_enum<T>() || std::is_integral<T>()) {
if constexpr (sizeof(T) > 4) {
return wasm_valtype_new_i64();
} else {
return wasm_valtype_new_i32();
}
} else if constexpr (std::is_same<T, const ArbUt::StringView&>()) {
return wasm_valtype_new_i64();
}
THROW("Unhandled value type: ", typeid(T).name());
}
template <typename T> inline static wasm_val_t ToVal(const T& val) {
if constexpr (std::is_pointer<T>()) {
return WASM_I64_VAL(reinterpret_cast<i64>(val));
} else if constexpr (is_specialization<T, ArbUt::BorrowedPtr>::value) {
return WASM_I64_VAL(reinterpret_cast<i64>(val.GetRaw()));
} else if constexpr (is_specialization<T, ArbUt::OptionalBorrowedPtr>::value) {
return WASM_I64_VAL(reinterpret_cast<i64>(val.GetRaw()));
} else if constexpr (is_specialization<T, std::unique_ptr>::value) {
return WASM_I64_VAL(reinterpret_cast<i64>(val.get()));
} else if constexpr (std::is_enum<T>() || std::is_integral<T>()) {
if constexpr (sizeof(T) > 4) {
return WASM_I64_VAL((i64)val);
} else {
return WASM_I32_VAL((i32)val);
}
} else if constexpr (std::is_same<T, const ArbUt::StringView&>()) {
return WASM_I64_VAL(reinterpret_cast<i64>(&val));
}
THROW("Unhandled value type: ", typeid(T).name());
}
template <typename T> inline static T FromVal(const wasm_val_t& val, WebAssemblyScriptResolver* resolver) {
if constexpr (std::is_pointer<T>()) {
auto v = reinterpret_cast<void*>(val.of.i64);
Ensure(resolver->ValidateLoadedPointer<std::remove_pointer<T>>(v));
return (T)v;
} else if constexpr (is_specialization<T, ArbUt::BorrowedPtr>::value) {
return dynamic_cast<T>(reinterpret_cast<void*>(val.of.i64));
} else if constexpr (is_specialization<T, ArbUt::OptionalBorrowedPtr>::value) {
return dynamic_cast<T>(reinterpret_cast<void*>(val.of.i64));
} else if constexpr (is_specialization<T, std::unique_ptr>::value) {
return dynamic_cast<T>(reinterpret_cast<void*>(val.of.i64));
} else if constexpr (std::is_enum<T>() || std::is_integral<T>()) {
if constexpr (sizeof(T) > 4) {
return reinterpret_cast<T>(val.of.i64);
} else {
return reinterpret_cast<T>(val.of.i32);
}
}
THROW("Unhandled value type: ", typeid(T).name());
}
template <size_t I, class... Args> inline static void AppendArgument(wasm_valtype_t* array[]) {
using type = typename std::tuple_element<I, std::tuple<Args...>>::type;
array[I] = GetValType<type>();
if constexpr (I + 1 < sizeof...(Args)) {
AppendArgument<I + 1, Args...>(array);
}
}
template <class R, class... Args> static wasm_functype_t* GetFuncType() {
wasm_valtype_t* ps[sizeof...(Args)];
AppendArgument<0, Args...>(ps);
wasm_valtype_vec_t params, results;
if constexpr (std::is_void<R>()) {
results = WASM_EMPTY_VEC;
} else {
wasm_valtype_t* rs[1] = {GetValType<R>()};
wasm_valtype_vec_new(&results, 1, rs);
}
wasm_valtype_vec_new(&params, sizeof...(Args), ps);
return wasm_functype_new(&params, &results);
}
};
#define REGISTER_GETTER(name, cType, cFunc, store, resolver) \
externs.Insert( \
name, \
WasmHelpers::RegisterGetter<cType, std::result_of<decltype (&cType::cFunc)(cType)>::type, &cType::cFunc>( \
store, resolver));
#endif // PKMNLIB_HELPERFILE_H