Initial work on WebAssembly script provider
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
86
src/ScriptResolving/WASM/InterfaceMethods/CoreMethods.cpp
Normal file
86
src/ScriptResolving/WASM/InterfaceMethods/CoreMethods.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "CoreMethods.hpp"
|
||||
#include <cstring>
|
||||
#include "../WebAssemblyScriptResolver.hpp"
|
||||
#include "WasmHelperFile.hpp"
|
||||
#include "wasm.h"
|
||||
|
||||
wasm_func_t* CreateErrorFunc(wasm_store_t* store, WebAssemblyScriptResolver* resolver) {
|
||||
// This is probably the most horrific function we need to expose. As we do not want the scripting library to be
|
||||
// responsible for string formatting for size reasons, we pass a lot of data separately. This includes several
|
||||
// strings.
|
||||
return WasmHelpers::CreateFunc<void, i32, i32, i32, i32, i32, i32>(
|
||||
store, resolver, [](void* env, const wasm_val_vec_t* args, wasm_val_vec_t*) -> wasm_trap_t* {
|
||||
auto msg = args->data[0].of.i32;
|
||||
auto msg_len = args->data[1].of.i32;
|
||||
auto file = args->data[2].of.i32;
|
||||
auto file_len = args->data[3].of.i32;
|
||||
auto line = args->data[4].of.i32;
|
||||
auto position = args->data[5].of.i32;
|
||||
|
||||
auto* resolver = (WebAssemblyScriptResolver*)env;
|
||||
auto* msgPointer = wasm_memory_data(resolver->GetMemory()) + msg;
|
||||
auto* filePointer = wasm_memory_data(resolver->GetMemory()) + file;
|
||||
|
||||
auto msgString = std::string_view(msgPointer, msg_len);
|
||||
auto fileString = std::string_view(filePointer, file_len);
|
||||
|
||||
std::stringstream fullMessage;
|
||||
fullMessage << "WASM Error with message: " << msgString << std::endl
|
||||
<< "in file: " << fileString << ". Line: " << line << ":" << position;
|
||||
|
||||
wasm_message_t message;
|
||||
wasm_name_new_from_string_nt(&message, fullMessage.str().c_str());
|
||||
wasm_trap_t* trap = wasm_trap_new(resolver->GetStore(), &message);
|
||||
wasm_name_delete(&message);
|
||||
return trap;
|
||||
});
|
||||
}
|
||||
|
||||
wasm_func_t* CreatePrintFunc(wasm_store_t* store, WebAssemblyScriptResolver* resolver) {
|
||||
return WasmHelpers::CreateFunc<void, i32, i32>(
|
||||
store, resolver, [](void* env, const wasm_val_vec_t* args, wasm_val_vec_t*) -> wasm_trap_t* {
|
||||
auto msg = args->data[0].of.i32;
|
||||
auto msg_len = args->data[1].of.i32;
|
||||
auto resolver = (WebAssemblyScriptResolver*)env;
|
||||
auto* msgPointer = wasm_memory_data(resolver->GetMemory()) + msg;
|
||||
auto msgString = std::string_view(msgPointer, msg_len);
|
||||
std::cout << msgString << std::endl;
|
||||
return nullptr;
|
||||
});
|
||||
;
|
||||
}
|
||||
|
||||
wasm_func_t* ConstString_GetHash(wasm_store_t* store, WebAssemblyScriptResolver* resolver) {
|
||||
return WasmHelpers::CreateFunc2<u32, const ArbUt::StringView*>(
|
||||
store, resolver,
|
||||
std::function<u32(WebAssemblyScriptResolver*, const ArbUt::StringView*)>(
|
||||
[](WebAssemblyScriptResolver*, const ArbUt::StringView* sv) -> u32 { return sv->GetHash(); }));
|
||||
}
|
||||
|
||||
wasm_func_t* ConstString_GetStr(wasm_store_t* store, WebAssemblyScriptResolver* resolver) {
|
||||
return WasmHelpers::CreateFunc<i32, const ArbUt::StringView*>(
|
||||
store, resolver, [](void* env, const wasm_val_vec_t* args, wasm_val_vec_t* returns) -> wasm_trap_t* {
|
||||
auto resolver = (WebAssemblyScriptResolver*)env;
|
||||
auto& constString = *(ArbUt::StringView*)args->data[0].of.i64;
|
||||
|
||||
// To allow webassembly to access the C String, we need to allocate it inside it's memory.
|
||||
// Length + 1 to make room for the '\0'
|
||||
auto stringPointer = resolver->AllocateMemory(constString.Length() + 1, std::alignment_of<const char>());
|
||||
|
||||
// After we have the pointer to the memory allocated for the string, copy the string with specified length
|
||||
// to it, then suffix it with the null byte to end it.
|
||||
strncpy(reinterpret_cast<char*>(stringPointer.first), constString.c_str(), constString.Length());
|
||||
stringPointer.first[constString.Length()] = '\0';
|
||||
|
||||
returns->data[0] = WASM_I32_VAL(stringPointer.second);
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void WebAssemblyCoreMethods::Register(wasm_store_t* store, ArbUt::Dictionary<std::string, wasm_func_t*>& externs,
|
||||
WebAssemblyScriptResolver* resolver) {
|
||||
externs.Insert("_error", CreateErrorFunc(store, resolver));
|
||||
externs.Insert("_print", CreatePrintFunc(store, resolver));
|
||||
externs.Insert("arbutils_const_string_get_hash", ConstString_GetHash(store, resolver));
|
||||
externs.Insert("arbutils_const_string_get_str", ConstString_GetStr(store, resolver));
|
||||
}
|
||||
14
src/ScriptResolving/WASM/InterfaceMethods/CoreMethods.hpp
Normal file
14
src/ScriptResolving/WASM/InterfaceMethods/CoreMethods.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef PKMNLIB_COREMETHODS_HPP
|
||||
#define PKMNLIB_COREMETHODS_HPP
|
||||
|
||||
#include <Arbutils/Collections/Dictionary.hpp>
|
||||
#include <wasm.h>
|
||||
|
||||
class WebAssemblyScriptResolver;
|
||||
class WebAssemblyCoreMethods {
|
||||
public:
|
||||
static void Register(wasm_store_t* store, ArbUt::Dictionary<std::string, wasm_func_t*>& externs,
|
||||
WebAssemblyScriptResolver* resolver);
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_COREMETHODS_HPP
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "LibraryMethods.hpp"
|
||||
#include <type_traits>
|
||||
#include "../../../../Battling/Library/BattleLibrary.hpp"
|
||||
#include "../../WebAssemblyScriptResolver.hpp"
|
||||
#include "../WasmHelperFile.hpp"
|
||||
#include "wasm.h"
|
||||
|
||||
wasm_func_t* MoveLibrary_GetMoveByHash(wasm_store_t* store) {
|
||||
wasm_functype_t* type =
|
||||
wasm_functype_new_2_1(wasm_valtype_new_i64(), wasm_valtype_new_i32(), wasm_valtype_new_i64());
|
||||
auto* f = wasm_func_new(store, type, [](const wasm_val_vec_t* args, wasm_val_vec_t* returns) -> wasm_trap_t* {
|
||||
auto moveLibrary = (PkmnLib::Library::MoveLibrary*)args->data[0].of.i64;
|
||||
auto hash = (u32)args->data[1].of.i32;
|
||||
auto opt = moveLibrary->TryGet(hash);
|
||||
if (!opt.has_value()) {
|
||||
returns->data[0] = WASM_I64_VAL(0);
|
||||
} else{
|
||||
returns->data[0] = WASM_I64_VAL(reinterpret_cast<i64>(moveLibrary->Get(hash).GetRaw()));
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
wasm_functype_delete(type);
|
||||
return f;
|
||||
}
|
||||
|
||||
void LibraryMethods::Register(wasm_store_t* store, ArbUt::Dictionary<std::string, wasm_func_t*>& externs,
|
||||
WebAssemblyScriptResolver* resolver) {
|
||||
REGISTER_GETTER("battling_battle_library_get_data_library", PkmnLib::Battling::BattleLibrary, GetStaticLib, store,
|
||||
resolver);
|
||||
REGISTER_GETTER("library_data_library_get_move_library", PkmnLib::Library::PokemonLibrary, GetMoveLibrary, store,
|
||||
resolver);
|
||||
externs.Insert("library_move_library_get_move_by_hash", MoveLibrary_GetMoveByHash(store));
|
||||
|
||||
REGISTER_GETTER("library_move_data_get_base_power", CreatureLib::Library::AttackData, GetBasePower, store,
|
||||
resolver);
|
||||
|
||||
REGISTER_GETTER("library_move_data_get_name", CreatureLib::Library::AttackData, GetName, store,
|
||||
resolver);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef PKMNLIB_LIBRARYMETHODS_H
|
||||
#define PKMNLIB_LIBRARYMETHODS_H
|
||||
#include <Arbutils/Collections/Dictionary.hpp>
|
||||
#include <wasm.h>
|
||||
|
||||
class WebAssemblyScriptResolver;
|
||||
class LibraryMethods {
|
||||
public:
|
||||
static void Register(wasm_store_t* store, ArbUt::Dictionary<std::string, wasm_func_t*>& externs,
|
||||
WebAssemblyScriptResolver* resolver);
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_LIBRARYMETHODS_H
|
||||
197
src/ScriptResolving/WASM/InterfaceMethods/WasmHelperFile.hpp
Normal file
197
src/ScriptResolving/WASM/InterfaceMethods/WasmHelperFile.hpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#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(¶ms, sizeof...(Args), ps);
|
||||
return wasm_functype_new(¶ms, &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
|
||||
Reference in New Issue
Block a user