Initial work on WebAssembly script provider
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
c6775d7089
commit
e32d655d80
|
@ -73,10 +73,15 @@ if (SCRIPT_PROVIDER STREQUAL "angelscript")
|
|||
WORKING_DIRECTORY ${Angelscript_SOURCE_DIR}/angelscript/projects/cmake)
|
||||
endif ()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
include_directories(${Angelscript_SOURCE_DIR}/angelscript/include)
|
||||
include_directories(${Angelscript_SOURCE_DIR}/add_on)
|
||||
elseif (SCRIPT_PROVIDER STREQUAL "wasm")
|
||||
message(STATUS "Installing wasmer")
|
||||
execute_process(COMMAND bash -c "curl https://get.wasmer.io -sSfL | WASMER_INSTALL_LOG=0 sh")
|
||||
include_directories($ENV{WASMER_DIR}/include)
|
||||
endif ()
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
|
||||
|
||||
if (WINDOWS)
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
@ -160,13 +165,19 @@ if (SCRIPT_PROVIDER STREQUAL "angelscript")
|
|||
set(_TESTLINKS ${_TESTLINKS} AngelscriptDebugger)
|
||||
endif ()
|
||||
ADD_DEFINITIONS(-D AS_USE_ACCESSORS=1)
|
||||
elseif(SCRIPT_PROVIDER STREQUAL "wasm")
|
||||
SET(FILE_SOURCE ${FILE_SOURCE}
|
||||
"src/ScriptResolving/WASM/*.cpp"
|
||||
"src/ScriptResolving/WASM/*.hpp")
|
||||
# FIXME: Add C Interface
|
||||
endif ()
|
||||
|
||||
file(GLOB_RECURSE CORE_SRC_FILES ${FILE_SOURCE})
|
||||
add_library(pkmnLib ${LIBTYPE} ${CORE_SRC_FILES})
|
||||
# Enable all warnings, and make them error when occurring.
|
||||
target_compile_options(pkmnLib PRIVATE -Wall -Wextra -Werror -pedantic-errors)
|
||||
target_link_directories(pkmnLib PUBLIC ${Angelscript_BINARY_DIR})
|
||||
|
||||
|
||||
|
||||
# If interprocedural optimization is available, apply it
|
||||
check_ipo_supported(RESULT IPO_SUPPORTED)
|
||||
|
@ -180,12 +191,21 @@ SET(_TESTLINKS ${_TESTLINKS} pkmnLib CreatureLib Arbutils)
|
|||
|
||||
if (SCRIPT_PROVIDER STREQUAL "angelscript")
|
||||
message(STATUS "Using Angelscript as script provider.")
|
||||
target_link_directories(pkmnLib PUBLIC ${Angelscript_BINARY_DIR})
|
||||
|
||||
ADD_DEFINITIONS(-D ANGELSCRIPT=1)
|
||||
SET(_LINKS ${_LINKS} angelscript)
|
||||
SET(_TESTLINKS ${_TESTLINKS} angelscript)
|
||||
if (ANGELSCRIPT_DEBUGGER)
|
||||
ADD_DEFINITIONS(-D ANGELSCRIPT_DEBUGGER=1)
|
||||
endif ()
|
||||
elseif (SCRIPT_PROVIDER STREQUAL "wasm")
|
||||
message(STATUS "Using WebAssembly as script provider.")
|
||||
target_link_directories(pkmnLib PUBLIC $ENV{WASMER_DIR}/lib)
|
||||
message($ENV{WASMER_DIR}/lib)
|
||||
ADD_DEFINITIONS(-D WASM=1)
|
||||
SET(_LINKS ${_LINKS} -Wl,-Bstatic wasmer -Wl,-Bdynamic)
|
||||
SET(_TESTLINKS ${_TESTLINKS} -Wl,-Bstatic wasmer -Wl,-Bdynamic)
|
||||
endif ()
|
||||
|
||||
# If we are building for Windows we need to set some specific variables.
|
||||
|
@ -245,6 +265,8 @@ if (PKMNLIB_TESTS)
|
|||
endif ()
|
||||
endif ()
|
||||
|
||||
if (ANGELSCRIPT_DEBUGGER)
|
||||
include_directories(extern/AngelscriptDebuggerServer/extern/asio-1.18.2/include)
|
||||
endif ()
|
||||
if (SCRIPT_PROVIDER STREQUAL "angelscript")
|
||||
if (ANGELSCRIPT_DEBUGGER)
|
||||
include_directories(extern/AngelscriptDebuggerServer/extern/asio-1.18.2/include)
|
||||
endif ()
|
||||
endif()
|
|
@ -0,0 +1 @@
|
|||
src:*/std_function.h
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef PKMNLIB_SCRIPTRESOLVER_HPP
|
||||
#define PKMNLIB_SCRIPTRESOLVER_HPP
|
||||
|
||||
#include <CreatureLib/Battling/ScriptHandling/ScriptResolver.hpp>
|
||||
#include "../EvolutionScript.hpp"
|
||||
|
||||
namespace PkmnLib::Battling {
|
||||
|
|
|
@ -18,9 +18,19 @@ namespace PkmnLib::Library {
|
|||
return {};
|
||||
return res.value().ForceAs<const MoveData>();
|
||||
}
|
||||
inline std::optional<ArbUt::BorrowedPtr<const MoveData>> TryGet(u32 hash) const {
|
||||
auto res = CreatureLib::Library::AttackLibrary::TryGet(hash);
|
||||
if (!res.has_value())
|
||||
return {};
|
||||
return res.value().ForceAs<const MoveData>();
|
||||
}
|
||||
|
||||
inline ArbUt::BorrowedPtr<const MoveData> Get(const ArbUt::BasicStringView& name) const {
|
||||
return CreatureLib::Library::AttackLibrary::Get(name).As<const MoveData>();
|
||||
}
|
||||
inline ArbUt::BorrowedPtr<const MoveData> Get(u32 hash) const {
|
||||
return CreatureLib::Library::AttackLibrary::Get(hash).As<const MoveData>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef PKMNLIB_WASMEXTERNREF_HPP
|
||||
#define PKMNLIB_WASMEXTERNREF_HPP
|
||||
|
||||
template <class T> class WasmExternRef {
|
||||
public:
|
||||
WasmExternRef(T* ptr) : _ptr(ptr) {}
|
||||
|
||||
[[nodiscard]] inline T* GetPtr() const noexcept { return _ptr; }
|
||||
private:
|
||||
T* _ptr;
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_WASMEXTERNREF_HPP
|
|
@ -0,0 +1,40 @@
|
|||
#include "WebAssemblyBattleScript.hpp"
|
||||
#include "WebAssemblyFunctionCall.hpp"
|
||||
#include "WebAssemblyScriptResolver.hpp"
|
||||
|
||||
WebAssemblyBattleScript::~WebAssemblyBattleScript() {
|
||||
// Ensure the WebAssembly library can clear up its own junk
|
||||
auto funcOpt = _resolver->GetFunction<1, 0>("destroy_script");
|
||||
if (funcOpt.has_value()) {
|
||||
auto& func = funcOpt.value();
|
||||
func.Loadi32(0, _wasmPtr);
|
||||
func.Call();
|
||||
}
|
||||
// Remove the backwards lookup to this script.
|
||||
_resolver->RemoveRegisteredScript(_wasmPtr);
|
||||
}
|
||||
|
||||
#define WASM_CALL(capability, script_name, args_count, return_count, parameter_setup) \
|
||||
if (!HasCapability(WebAssemblyScriptCapabilities::capability)) { \
|
||||
return; \
|
||||
} \
|
||||
auto funcOpt = _resolver->GetFunction<args_count, return_count>(script_name); \
|
||||
if (!funcOpt.has_value()) { \
|
||||
return; \
|
||||
} \
|
||||
auto& func = funcOpt.value(); \
|
||||
parameter_setup; \
|
||||
func.Call();
|
||||
|
||||
void WebAssemblyBattleScript::OnInitialize(const CreatureLib::Battling::BattleLibrary* library,
|
||||
const ArbUt::List<CreatureLib::Library::EffectParameter*>&) {
|
||||
WASM_CALL(Initialize, "script_on_initialize", 2, 0, {
|
||||
func.Loadi32(0, _wasmPtr);
|
||||
func.LoadExternRef(1, library);
|
||||
});
|
||||
}
|
||||
|
||||
CreatureLib::Battling::BattleScript* WebAssemblyBattleScript::Clone(const ArbUt::OptionalBorrowedPtr<void>&) {
|
||||
// FIXME: Implement
|
||||
return nullptr;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef PKMNLIB_WEBASSEMBLYBATTLESCRIPT_H
|
||||
#define PKMNLIB_WEBASSEMBLYBATTLESCRIPT_H
|
||||
|
||||
#include "../../Battling/PkmnScript.hpp"
|
||||
#include "WebAssemblyScriptCapabilities.hpp"
|
||||
|
||||
class WebAssemblyScriptResolver;
|
||||
class WebAssemblyBattleScript : public PkmnLib::Battling::PkmnScript {
|
||||
public:
|
||||
WebAssemblyBattleScript(const ArbUt::OptionalBorrowedPtr<void>& owner, i32 wasm_ptr,
|
||||
const std::unordered_set<WebAssemblyScriptCapabilities>* capabilities,
|
||||
WebAssemblyScriptResolver* resolver, const ArbUt::StringView& scriptName)
|
||||
: PkmnScript(owner), _resolver(resolver), _wasmPtr(wasm_ptr), _capabilities(capabilities),
|
||||
_scriptName(scriptName) {}
|
||||
~WebAssemblyBattleScript();
|
||||
|
||||
BattleScript* Clone(const ArbUt::OptionalBorrowedPtr<void>& owner) non_null override;
|
||||
const ArbUt::StringView& GetName() const noexcept override { return _scriptName; }
|
||||
|
||||
public:
|
||||
void OnInitialize(const CreatureLib::Battling::BattleLibrary* library,
|
||||
const ArbUt::List<CreatureLib::Library::EffectParameter*>& parameters) override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] inline bool HasCapability(WebAssemblyScriptCapabilities capability) const noexcept {
|
||||
return _capabilities->contains(capability);
|
||||
}
|
||||
|
||||
WebAssemblyScriptResolver* _resolver;
|
||||
i32 _wasmPtr;
|
||||
const std::unordered_set<WebAssemblyScriptCapabilities>* _capabilities;
|
||||
ArbUt::StringView _scriptName;
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_WEBASSEMBLYBATTLESCRIPT_H
|
|
@ -0,0 +1,107 @@
|
|||
#ifndef PKMNLIB_WEBASSEMBLYFUNCTIONCALL_H
|
||||
#define PKMNLIB_WEBASSEMBLYFUNCTIONCALL_H
|
||||
|
||||
#include <Arbutils/Collections/List.hpp>
|
||||
#include <Arbutils/Memory/Memory.hpp>
|
||||
#include <wasm.h>
|
||||
#include "wasm.h"
|
||||
|
||||
template <u32 argsCount, u32 returnsCount>
|
||||
class WebAssemblyFunctionCall {
|
||||
public:
|
||||
WebAssemblyFunctionCall(const ArbUt::BorrowedPtr<wasm_func_t>& func) : _func(func) {}
|
||||
|
||||
NO_COPY_OR_MOVE(WebAssemblyFunctionCall)
|
||||
|
||||
void Call() {
|
||||
wasm_val_vec_t args = {argsCount, _arguments.Data};
|
||||
|
||||
wasm_val_vec_t results;
|
||||
if constexpr (returnsCount > 0) {
|
||||
results = {returnsCount, _results.Data};
|
||||
} else {
|
||||
results = WASM_EMPTY_VEC;
|
||||
}
|
||||
|
||||
auto* result = wasm_func_call(_func, &args, &results);
|
||||
if (result != nullptr) {
|
||||
wasm_message_t retrieved_message;
|
||||
wasm_trap_message(result, &retrieved_message);
|
||||
auto msg = std::string(retrieved_message.data, retrieved_message.size);
|
||||
THROW(msg);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Loadi32(size_t index, i32 value) { _arguments.Data[index] = WASM_I32_VAL(value); }
|
||||
inline void Loadi64(size_t index, i64 value) { _arguments.Data[index] = WASM_I64_VAL(value); }
|
||||
inline void Loadf32(size_t index, f32 value) { _arguments.Data[index] = WASM_F32_VAL(value); }
|
||||
inline void Loadf64(size_t index, f64 value) { _arguments.Data[index] = WASM_F64_VAL(value); }
|
||||
|
||||
template <class T> inline void LoadExternRef(size_t index, T* value) {
|
||||
_arguments.Data[index] = WASM_I64_VAL(reinterpret_cast<i64>(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline i32 GetResultAsi32() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
Ensure(_results.Data[0].kind == WASM_I32);
|
||||
return _results.Data[0].of.i32;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] inline i64 GetResultAsi64() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
Ensure(_results.Data[0].kind == WASM_I64);
|
||||
return _results.Data[0].of.i64;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] inline f32 GetResultAsf32() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
Ensure(_results.Data[0].kind == WASM_F32);
|
||||
return _results.Data[0].of.f32;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] inline f64 GetResultAsf64() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
Ensure(_results.Data[0].kind == WASM_F64);
|
||||
return _results.Data[0].of.f64;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> [[nodiscard]] inline T* GetResultAsExternRef() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
Ensure(_results.Data[0].kind == WASM_ANYREF);
|
||||
return reinterpret_cast<T*>(_results.Data[0].of.i64);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline const wasm_val_t* GetRawResults() const {
|
||||
if constexpr (returnsCount > 0) {
|
||||
return _results.Data;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ArbUt::BorrowedPtr<wasm_func_t> _func;
|
||||
|
||||
struct Empty {};
|
||||
template <u32 size> struct NonEmpty { wasm_val_t Data[size]; };
|
||||
|
||||
typedef typename std::conditional<argsCount == 0, Empty, NonEmpty<argsCount>>::type args_t;
|
||||
typedef typename std::conditional<returnsCount == 0, Empty, NonEmpty<returnsCount>>::type result_t;
|
||||
|
||||
args_t _arguments;
|
||||
result_t _results;
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_WEBASSEMBLYFUNCTIONCALL_H
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef PKMNLIB_WEBASSEMBLYSCRIPTCAPABILITIES_H
|
||||
#define PKMNLIB_WEBASSEMBLYSCRIPTCAPABILITIES_H
|
||||
|
||||
enum class WebAssemblyScriptCapabilities : u8 {
|
||||
None = 0,
|
||||
Initialize = 1,
|
||||
OnStack,
|
||||
OnRemove,
|
||||
OnBeforeTurn,
|
||||
ChangeAttack,
|
||||
ModifyNumberOfHits,
|
||||
PreventAttack,
|
||||
FailAttack,
|
||||
StopBeforeAttack,
|
||||
OnBeforeAttack,
|
||||
FailIncomingAttack,
|
||||
IsInvulnerable,
|
||||
OnAttackMiss,
|
||||
ChangeAttackType,
|
||||
ChangeEffectiveness,
|
||||
BlockCritical,
|
||||
OnIncomingHit,
|
||||
OnFaintingOpponent,
|
||||
PreventStatBoostChange,
|
||||
ModifyStatBoostChange,
|
||||
PreventSecondaryEffects,
|
||||
OnSecondaryEffect,
|
||||
OnAfterHits,
|
||||
PreventSelfSwitch,
|
||||
ModifyEffectChance,
|
||||
ModifyIncomingEffectChance,
|
||||
OverrideBasePower,
|
||||
ChangeDamageStatsUser,
|
||||
BypassDefensiveStat,
|
||||
BypassOffensiveStat,
|
||||
ModifyStatModifier,
|
||||
ModifyDamageModifier,
|
||||
OverrideDamage,
|
||||
OverrideIncomingDamage,
|
||||
ChangeSpeed,
|
||||
ChangePriority,
|
||||
OnFail,
|
||||
OnOpponentFail,
|
||||
PreventRunAway,
|
||||
PreventOpponentRunAway,
|
||||
PreventOpponentSwitch,
|
||||
OnEndTurn,
|
||||
OnDamage,
|
||||
OnFaint,
|
||||
OnAfterHeldItemConsume,
|
||||
PreventIncomingCritical,
|
||||
ModifyCriticalStage,
|
||||
OverrideCriticalModifier,
|
||||
OverrideSTABModifier,
|
||||
ModifyExperienceGain,
|
||||
DoesShareExperience,
|
||||
BlockWeather,
|
||||
OnSwitchIn,
|
||||
ModifyOffensiveStatValue,
|
||||
ModifyDefensiveStatValue,
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_WEBASSEMBLYSCRIPTCAPABILITIES_H
|
|
@ -0,0 +1,175 @@
|
|||
#include "WebAssemblyScriptResolver.hpp"
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <wasmer.h>
|
||||
#include "InterfaceMethods/CoreMethods.hpp"
|
||||
#include "InterfaceMethods/Library/LibraryMethods.hpp"
|
||||
#include "WebAssemblyBattleScript.hpp"
|
||||
#include "WebAssemblyFunctionCall.hpp"
|
||||
#include "wasm.h"
|
||||
|
||||
PkmnLib::Battling::ScriptResolver* PkmnLib::Battling::BattleLibrary::CreateScriptResolver() {
|
||||
return new WebAssemblyScriptResolver();
|
||||
}
|
||||
WebAssemblyScriptResolver::WebAssemblyScriptResolver() : _engine(wasm_engine_new()), _store(wasm_store_new(_engine)) {}
|
||||
|
||||
WebAssemblyScriptResolver::~WebAssemblyScriptResolver() {
|
||||
wasm_extern_vec_delete(&_exports);
|
||||
for (auto& import : _imports) {
|
||||
wasm_func_delete(import.second);
|
||||
}
|
||||
if (_instance != nullptr) {
|
||||
wasm_instance_delete(_instance);
|
||||
}
|
||||
if (_module != nullptr) {
|
||||
wasm_module_delete(_module);
|
||||
}
|
||||
wasm_store_delete(_store);
|
||||
wasm_engine_delete(_engine);
|
||||
}
|
||||
|
||||
u8 WebAssemblyScriptResolver::LoadWasmFromFile(const std::string& path) {
|
||||
auto file = fopen(path.c_str(), "rb");
|
||||
if (!file) {
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0L, SEEK_END);
|
||||
size_t file_size = ftell(file);
|
||||
fseek(file, 0L, SEEK_SET);
|
||||
wasm_byte_vec_t wasm_bytes;
|
||||
wasm_byte_vec_new_uninitialized(&wasm_bytes, file_size);
|
||||
if (fread(wasm_bytes.data, file_size, 1, file) != 1) {
|
||||
wasm_byte_vec_delete(&wasm_bytes);
|
||||
return 2;
|
||||
}
|
||||
fclose(file);
|
||||
_module = wasm_module_new(_store, &wasm_bytes);
|
||||
if (_module == nullptr) {
|
||||
wasm_byte_vec_delete(&wasm_bytes);
|
||||
return 3;
|
||||
}
|
||||
wasm_byte_vec_delete(&wasm_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 WebAssemblyScriptResolver::LoadWasmFromBytes(std::vector<u8> wasm_bytes) {
|
||||
wasm_byte_vec_t data = {wasm_bytes.size(), (char*)wasm_bytes.data()};
|
||||
_module = wasm_module_new(_store, &data);
|
||||
if (_module == nullptr) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 WebAssemblyScriptResolver::LoadWatFromString(const std::string& data) {
|
||||
wasm_byte_vec_t wat;
|
||||
wasm_byte_vec_new(&wat, data.size(), data.c_str());
|
||||
wasm_byte_vec_t wasm_bytes;
|
||||
wat2wasm(&wat, &wasm_bytes);
|
||||
wasm_byte_vec_delete(&wat);
|
||||
_module = wasm_module_new(_store, &wasm_bytes);
|
||||
if (_module == nullptr) {
|
||||
return 3;
|
||||
}
|
||||
wasm_byte_vec_delete(&wasm_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WebAssemblyScriptResolver::RegisterFunction() {}
|
||||
|
||||
void WebAssemblyScriptResolver::RegisterDefaultMethods() {
|
||||
WebAssemblyCoreMethods::Register(_store, _imports, this);
|
||||
LibraryMethods::Register(_store, _imports, this);
|
||||
}
|
||||
|
||||
void WebAssemblyScriptResolver::Finalize() {
|
||||
RegisterDefaultMethods();
|
||||
auto imports = ArbUt::List<wasm_extern_t*>();
|
||||
|
||||
wasm_importtype_vec_t import_types;
|
||||
wasm_module_imports(_module, &import_types);
|
||||
for (size_t i = 0; i < import_types.size; ++i) {
|
||||
auto importType = import_types.data[i];
|
||||
auto nameWasm = wasm_importtype_name(importType);
|
||||
auto name = std::string(nameWasm->data, nameWasm->size);
|
||||
auto exportFunc = _imports.TryGet(name);
|
||||
if (!exportFunc.has_value()) {
|
||||
THROW("Missing imported WASM function: ", name);
|
||||
}
|
||||
imports.Append(wasm_func_as_extern(exportFunc.value()));
|
||||
}
|
||||
|
||||
wasm_extern_vec_t import_object = {imports.Count(), const_cast<wasm_extern_t**>(imports.RawData())};
|
||||
|
||||
wasm_trap_t* trap = nullptr;
|
||||
_instance = wasm_instance_new(_store, _module, &import_object, &trap);
|
||||
wasm_importtype_vec_delete(&import_types);
|
||||
if (_instance == nullptr) {
|
||||
char* err = new char[wasmer_last_error_length()];
|
||||
wasmer_last_error_message(err, wasmer_last_error_length());
|
||||
std::cout << err << std::endl;
|
||||
delete[] err;
|
||||
}
|
||||
|
||||
EnsureNotNull(_instance);
|
||||
|
||||
wasm_exporttype_vec_t export_types;
|
||||
wasm_module_exports(_module, &export_types);
|
||||
wasm_instance_exports(_instance, &_exports);
|
||||
for (size_t i = 0; i < export_types.size; ++i) {
|
||||
auto t = wasm_externtype_kind(wasm_exporttype_type(export_types.data[i]));
|
||||
if (t == WASM_EXTERN_FUNC) {
|
||||
const auto* name = wasm_exporttype_name(export_types.data[i]);
|
||||
|
||||
_exportedFunctions.Insert(ArbUt::StringView(name->data, name->size), wasm_extern_as_func(_exports.data[i]));
|
||||
} else if (t == WASM_EXTERN_MEMORY) {
|
||||
_memory = wasm_extern_as_memory(_exports.data[i]);
|
||||
}
|
||||
}
|
||||
wasm_exporttype_vec_delete(&export_types);
|
||||
if (_memory != nullptr) {
|
||||
wasm_memory_grow(_memory, 100);
|
||||
}
|
||||
}
|
||||
|
||||
CreatureLib::Battling::BattleScript*
|
||||
WebAssemblyScriptResolver::LoadScript(const ArbUt::OptionalBorrowedPtr<void>& owner, ScriptCategory category,
|
||||
const ArbUt::StringView& scriptName) {
|
||||
auto loadScriptOpt = GetFunction<2, 1>("load_script"_cnc);
|
||||
if (!loadScriptOpt.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& loadScriptFunc = loadScriptOpt.value();
|
||||
loadScriptFunc.Loadi32(0, static_cast<i32>(category));
|
||||
loadScriptFunc.LoadExternRef(1, &scriptName);
|
||||
loadScriptFunc.Call();
|
||||
auto result = loadScriptFunc.GetResultAsi32();
|
||||
if (result == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
auto key = std::pair<ScriptCategory, ArbUt::StringView>(category, scriptName);
|
||||
auto findCapabilities = _scriptCapabilities.find(key);
|
||||
std::unordered_set<WebAssemblyScriptCapabilities> capabilities;
|
||||
if (findCapabilities != _scriptCapabilities.end()) {
|
||||
capabilities = findCapabilities->second;
|
||||
} else {
|
||||
auto getCapabilitiesOpt = GetFunction<1,2>("get_script_capabilities"_cnc);
|
||||
if (getCapabilitiesOpt.has_value()) {
|
||||
auto& getCapabilitiesFunc = getCapabilitiesOpt.value();
|
||||
getCapabilitiesFunc.Loadi32(0, result);
|
||||
getCapabilitiesFunc.Call();
|
||||
const auto* rawResult = getCapabilitiesFunc.GetRawResults();
|
||||
auto ptr = (WebAssemblyScriptCapabilities*)(wasm_memory_data(_memory) + rawResult[0].of.i32);
|
||||
auto end = (WebAssemblyScriptCapabilities*)(ptr + rawResult[1].of.i32);
|
||||
auto vec = std::vector<WebAssemblyScriptCapabilities>(ptr, end);
|
||||
for (auto capability: vec){
|
||||
capabilities.insert(capability);
|
||||
}
|
||||
}
|
||||
_scriptCapabilities[key] = capabilities;
|
||||
}
|
||||
|
||||
auto script = new WebAssemblyBattleScript(owner, result, &_scriptCapabilities[key], this, scriptName);
|
||||
_loadedScripts.Insert(result, script);
|
||||
return script;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
#ifndef PKMNLIB_WEBASSEMBLYSCRIPTRESOLVER_HPP
|
||||
#define PKMNLIB_WEBASSEMBLYSCRIPTRESOLVER_HPP
|
||||
|
||||
#include <Arbutils/Collections/Dictionary.hpp>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <wasm.h>
|
||||
#include "../../Battling/Library/ScriptResolver.hpp"
|
||||
#include "WebAssemblyBattleScript.hpp"
|
||||
#include "WebAssemblyFunctionCall.hpp"
|
||||
#include "WebAssemblyScriptCapabilities.hpp"
|
||||
|
||||
class WebAssemblyScriptResolver : public PkmnLib::Battling::ScriptResolver {
|
||||
public:
|
||||
WebAssemblyScriptResolver();
|
||||
~WebAssemblyScriptResolver();
|
||||
|
||||
u8 LoadWasmFromFile(const std::string& path);
|
||||
u8 LoadWasmFromBytes(std::vector<u8>);
|
||||
u8 LoadWatFromString(const std::string& data);
|
||||
void RegisterFunction();
|
||||
void Finalize();
|
||||
|
||||
template <u32 argsCount, u32 returnsCount>
|
||||
inline std::optional<WebAssemblyFunctionCall<argsCount, returnsCount>>
|
||||
GetFunction(const ArbUt::StringView& name) const {
|
||||
auto res = _exportedFunctions.TryGet(name);
|
||||
if (!res.has_value()) {
|
||||
return {};
|
||||
}
|
||||
return std::make_optional<WebAssemblyFunctionCall<argsCount, returnsCount>>(
|
||||
ArbUt::BorrowedPtr<wasm_func_t>(res.value()));
|
||||
}
|
||||
|
||||
std::pair<u8*, i32> AllocateMemory(u32 size, u32 align) const {
|
||||
auto funcOpt = GetFunction<2, 1>("allocate_mem");
|
||||
auto& func = funcOpt.value();
|
||||
func.Loadi32(0, size);
|
||||
func.Loadi32(1, align);
|
||||
func.Call();
|
||||
auto memoryOffset = func.GetResultAsi32();
|
||||
return std::make_pair(reinterpret_cast<u8*>(wasm_memory_data(_memory) + memoryOffset), memoryOffset);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline wasm_memory_t* GetMemory() const noexcept { return _memory; }
|
||||
|
||||
CreatureLib::Battling::BattleScript* LoadScript(const ArbUt::OptionalBorrowedPtr<void>& owner,
|
||||
ScriptCategory category,
|
||||
const ArbUt::StringView& scriptName) nullable override;
|
||||
|
||||
[[nodiscard]] inline wasm_store_t* GetStore() const noexcept { return _store; }
|
||||
|
||||
inline void RemoveRegisteredScript(i32 wasmPtr) { _loadedScripts.Remove(wasmPtr); }
|
||||
|
||||
template <typename T>
|
||||
inline void MarkLoadedPointer(T* ptr){
|
||||
_loadedPointers.Set((void*)ptr, typeid(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool ValidateLoadedPointer(void* ptr){
|
||||
const auto& opt = _loadedPointers.TryGet(ptr);
|
||||
return opt.has_value() && opt.value() == typeid(T);
|
||||
}
|
||||
|
||||
private:
|
||||
wasm_engine_t* _engine;
|
||||
wasm_store_t* _store;
|
||||
wasm_module_t* _module = nullptr;
|
||||
wasm_instance_t* _instance = nullptr;
|
||||
wasm_memory_t* _memory = nullptr;
|
||||
ArbUt::Dictionary<std::string, wasm_func_t*> _imports;
|
||||
wasm_extern_vec_t _exports = {0, nullptr};
|
||||
ArbUt::Dictionary<ArbUt::StringView, wasm_func_t*> _exportedFunctions;
|
||||
ArbUt::Dictionary<i32, WebAssemblyBattleScript*> _loadedScripts;
|
||||
|
||||
void RegisterDefaultMethods();
|
||||
|
||||
typedef std::pair<ScriptCategory, ArbUt::StringView> scriptCapabilitiesKey;
|
||||
|
||||
struct pair_hash {
|
||||
template <class T1, class T2> std::size_t operator()(const std::pair<T1, T2>& pair) const {
|
||||
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
|
||||
}
|
||||
};
|
||||
std::unordered_map<scriptCapabilitiesKey, std::unordered_set<WebAssemblyScriptCapabilities>, pair_hash>
|
||||
_scriptCapabilities;
|
||||
|
||||
ArbUt::Dictionary<void*, std::type_info> _loadedPointers;
|
||||
};
|
||||
|
||||
#endif // PKMNLIB_WEBASSEMBLYSCRIPTRESOLVER_HPP
|
|
@ -8,4 +8,9 @@ TEST_CASE("Able to build and destroy empty library") {
|
|||
delete lib;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_EXCEPTION_TRANSLATOR(ArbUt::Exception& ex) {
|
||||
return {(std::string(ex.what()) + "\n" + ex.GetStacktrace()).c_str()};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
|
@ -1,7 +1,7 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#include <Arbutils/StringView.hpp>
|
||||
#include <doctest.h>
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp"
|
||||
#include "../../../src/ScriptResolving/AngelScript/AngelScriptMetadata.hpp"
|
||||
#include "Arbutils/StringView.hpp"
|
||||
#include "doctest.h"
|
||||
|
||||
TEST_CASE("Metadata without parameters") {
|
||||
auto m = AngelscriptMetadata("Foo");
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../src/Battling/Pokemon/CreatePokemon.hpp"
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD && ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/Battling/Battle/Battle.hpp"
|
||||
#include "../../../../src/Battling/Pokemon/CreatePokemon.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/Battling/Pokemon/CreatePokemon.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD && ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -1,4 +1,4 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#if TESTS_BUILD and ANGELSCRIPT
|
||||
#include <doctest.h>
|
||||
#include "../../../../src/ScriptResolving/AngelScript/AngelScriptResolver.hpp"
|
||||
#include "../../../../src/ScriptResolving/AngelScript/ContextPool.hpp"
|
|
@ -0,0 +1,63 @@
|
|||
#if TESTS_BUILD && WASM
|
||||
#include <doctest.h>
|
||||
#include "../../TestLibrary/TestLibrary.hpp"
|
||||
#include "../../src/ScriptResolving/WASM/WebAssemblyFunctionCall.hpp"
|
||||
#include "../../src/ScriptResolving/WASM/WebAssemblyScriptResolver.hpp"
|
||||
|
||||
TEST_CASE("Get a script resolver, initialize it, then delete it") {
|
||||
auto lib = PkmnLib::Battling::BattleLibrary::CreateScriptResolver();
|
||||
lib->Initialize(TestLibrary::GetLibrary());
|
||||
delete lib;
|
||||
}
|
||||
|
||||
TEST_CASE("Get a script resolver, load simple wat, run function") {
|
||||
auto lib = dynamic_cast<WebAssemblyScriptResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||
lib->Initialize(TestLibrary::GetLibrary());
|
||||
auto res = lib->LoadWatFromString(R"(
|
||||
(module
|
||||
(type $add_one_t (func (param i32) (result i32)))
|
||||
(func $add_one_f (type $add_one_t) (param $value i32) (result i32)
|
||||
local.get $value
|
||||
i32.const 1
|
||||
i32.add)
|
||||
(export "add_one" (func $add_one_f))
|
||||
)
|
||||
)");
|
||||
REQUIRE_EQ(0, res);
|
||||
lib->Finalize();
|
||||
auto func_opt = lib->GetFunction<1, 1>("add_one");
|
||||
REQUIRE(func_opt.has_value());
|
||||
auto& func = func_opt.value();
|
||||
func.Loadi32(0, 684);
|
||||
func.Call();
|
||||
REQUIRE_EQ(func.GetResultAsi32(), 685);
|
||||
delete lib;
|
||||
}
|
||||
|
||||
TEST_CASE("Get a script resolver, load a real wasm script") {
|
||||
auto lib = dynamic_cast<WebAssemblyScriptResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||
lib->Initialize(TestLibrary::GetLibrary());
|
||||
std::string file_path = __FILE__;
|
||||
std::string dir_path = file_path.substr(0, file_path.rfind("/"));
|
||||
auto res = lib->LoadWasmFromFile(dir_path + "/gen7_scripts_rs.wasm");
|
||||
REQUIRE_EQ(0, res);
|
||||
lib->Finalize();
|
||||
delete lib;
|
||||
}
|
||||
|
||||
TEST_CASE("Get a script resolver, load a real wasm script, load test script") {
|
||||
ArbUt::ScopedPtr<WebAssemblyScriptResolver> lib =
|
||||
dynamic_cast<WebAssemblyScriptResolver*>(PkmnLib::Battling::BattleLibrary::CreateScriptResolver());
|
||||
lib->Initialize(TestLibrary::GetLibrary());
|
||||
std::string file_path = __FILE__;
|
||||
std::string dir_path = file_path.substr(0, file_path.rfind("/"));
|
||||
auto res = lib->LoadWasmFromFile(dir_path + "/gen7_scripts_rs.wasm");
|
||||
REQUIRE_EQ(0, res);
|
||||
lib->Finalize();
|
||||
auto script = lib->LoadScript(nullptr, ScriptCategory::Attack, "test"_cnc);
|
||||
EnsureNotNull(script);
|
||||
script->OnInitialize(TestLibrary::GetLibrary(), {});
|
||||
delete script;
|
||||
}
|
||||
|
||||
#endif
|
Binary file not shown.
Loading…
Reference in New Issue