diff options
author | RĂ©mi Verschelde <remi@verschelde.fr> | 2020-12-17 09:58:24 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-17 09:58:24 +0100 |
commit | b07a3f503b75831414f92d2cc6886d045eeb96fb (patch) | |
tree | 3c6d4d82b392da7d4950359982fd623e7f32dfc7 /modules/mono/mono_gd | |
parent | f6820306e0463d305bba1bff51b354c4139ea327 (diff) | |
parent | 2af8a7266370b7e641c79bef9425031e6fe68a27 (diff) |
Merge pull request #44105 from neikeq/mono-wasm-m2n-hook
Mono: Make Godot provide its own WASM m2n trampolines
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.h | 19 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_wasm_m2n.cpp | 79 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_wasm_m2n.h | 263 | ||||
-rw-r--r-- | modules/mono/mono_gd/support/android_support.cpp | 4 |
4 files changed, 363 insertions, 2 deletions
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index faa3d24eeb..5370b1c6fc 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -36,6 +36,9 @@ #include "../mono_gc_handle.h" #include "../utils/macros.h" #include "gd_mono_header.h" +#ifdef JAVASCRIPT_ENABLED +#include "gd_mono_wasm_m2n.h" +#endif #include "core/object/class_db.h" #include "core/object/reference.h" @@ -154,6 +157,22 @@ private: }; StringName get_native_godot_class_name(GDMonoClass *p_class); + +template <typename... P> +void add_internal_call(const char *p_name, void (*p_func)(P...)) { +#ifdef JAVASCRIPT_ENABLED + GDMonoWasmM2n::ICallTrampolines<P...>::add(); +#endif + mono_add_internal_call(p_name, (void *)p_func); +} + +template <typename R, typename... P> +void add_internal_call(const char *p_name, R (*p_func)(P...)) { +#ifdef JAVASCRIPT_ENABLED + GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add(); +#endif + mono_add_internal_call(p_name, (void *)p_func); +} } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class)) diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp new file mode 100644 index 0000000000..f4c964c6eb --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* gd_mono_wasm_m2n.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gd_mono_wasm_m2n.h" + +#ifdef JAVASCRIPT_ENABLED + +#include "core/templates/oa_hash_map.h" + +typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs); + +// This extern function is implemented in our patched version of Mono +MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook); + +namespace GDMonoWasmM2n { + +struct HashMapCookieComparator { + static bool compare(const char *p_lhs, const char *p_rhs) { + return strcmp(p_lhs, p_rhs) == 0; + } +}; + +// The default hasher supports 'const char *' C Strings, but we need a custom comparator +OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines; + +void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) { + trampolines.set(cookies, trampoline_func); +} + +mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) { + TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie); + + if (!trampoline_func) { + return false; + } + + (*trampoline_func)(target_func, margs); + return true; +} + +bool initialized = false; + +void lazy_initialize() { + // Doesn't need to be thread safe + if (!initialized) { + initialized = true; + godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook); + } +} +} // namespace GDMonoWasmM2n + +#endif diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h new file mode 100644 index 0000000000..68a14f15f4 --- /dev/null +++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h @@ -0,0 +1,263 @@ +/*************************************************************************/ +/* gd_mono_wasm_m2n.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GD_MONO_WASM_M2N_H +#define GD_MONO_WASM_M2N_H + +#ifdef JAVASCRIPT_ENABLED + +#include "core/string/ustring.h" +#include "core/typedefs.h" + +#include <mono/metadata/loader.h> +#include <mono/utils/mono-publib.h> +#include <stdexcept> +#include <type_traits> + +extern "C" { + +struct Mono_InterpMethodArguments { + size_t ilen; + void **iargs; + size_t flen; + double *fargs; + void **retval; + size_t is_float_ret; + //#ifdef TARGET_WASM + void *sig; + //#endif +}; +} // extern "C" + +namespace GDMonoWasmM2n { + +template <typename T, size_t Size> +struct array { + T elems[Size]; +}; + +template <typename T> +constexpr char get_m2n_cookie_impl() { +#define M2N_REG_COOKIE(m_type, m_cookie) \ + if constexpr (std::is_same_v<m_type, T>) { \ + return m_cookie; \ + } + + M2N_REG_COOKIE(MonoBoolean, 'I'); + M2N_REG_COOKIE(int8_t, 'I'); + M2N_REG_COOKIE(uint8_t, 'I'); + M2N_REG_COOKIE(int16_t, 'I'); + M2N_REG_COOKIE(uint16_t, 'I'); + M2N_REG_COOKIE(int32_t, 'I'); + M2N_REG_COOKIE(uint32_t, 'I'); + M2N_REG_COOKIE(int64_t, 'L'); + M2N_REG_COOKIE(uint64_t, 'L'); + M2N_REG_COOKIE(float, 'F'); + M2N_REG_COOKIE(double, 'D'); + + if constexpr (std::is_pointer_v<T>) { + if constexpr (sizeof(void *) == 4) { + return 'I'; + } else { + return 'L'; + } + } + + if constexpr (std::is_void_v<T>) { + return 'V'; + } + + return 'X'; + +#undef M2N_REG_COOKIE +} + +template <typename T> +constexpr char get_m2n_cookie() { + constexpr char cookie = get_m2n_cookie_impl<T>(); + static_assert(cookie != 'X', "Type not supported in internal call signature."); + return cookie; +} + +template <typename... T> +constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() { + return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' }; +} + +template <typename R, typename... T> +constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() { + return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' }; +} + +template <typename T> +constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) { + constexpr char cookie = get_m2n_cookie<T>(); + + static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); + + if constexpr (cookie == 'I' || cookie == 'L') { + size_t ret = r_int_idx; + r_int_idx += cookie == 'I' ? 1 : 2; + return ret; + } else { + size_t ret = r_float_idx; + r_float_idx += cookie == 'F' ? 1 : 2; + return ret; + } +} + +template <typename... P> +constexpr array<size_t, sizeof...(P)> get_indices_for_type() { + size_t int_idx = 0; + size_t float_idx = 0; + return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... }; +} + +constexpr size_t fidx(size_t p_x) { + if constexpr (sizeof(void *) == 4) { + return p_x * 2; + } else { + return p_x; + } +} + +template <typename T> +T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) { + constexpr char cookie = get_m2n_cookie<T>(); + + static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D'); + + if constexpr (cookie == 'I') { + return (T)(size_t)p_margs->iargs[p_idx]; + } else if constexpr (cookie == 'L') { + static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || + (sizeof(void *) == 8 && std::is_pointer_v<T>), + "Invalid type for cookie 'L'."); + + union { + T l; + struct { + int32_t lo; + int32_t hi; + } pair; + } p; + + p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx]; + p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1]; + + return p.l; + } else if constexpr (cookie == 'F') { + return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]); + } else if constexpr (cookie == 'D') { + return (T)(size_t)p_margs->fargs[p_idx]; + } +} + +template <typename... P, size_t... Is> +void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { + constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); + typedef void (*Func)(P...); + Func func = (Func)p_target_func; + func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); +} + +template <typename R, typename... P, size_t... Is> +void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) { + constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>(); + typedef R (*Func)(P...); + Func func = (Func)p_target_func; + R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...); + *reinterpret_cast<R *>(p_margs->retval) = res; +} + +inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + typedef void (*Func)(); + Func func = (Func)p_target_func; + func(); +} + +template <typename R> +void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + typedef R (*Func)(); + Func func = (Func)p_target_func; + R res = func(); + *reinterpret_cast<R *>(p_margs->retval) = res; +} + +template <typename... P> +void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + if constexpr (sizeof...(P) == 0) { + m2n_trampoline_with_idx_seq_0(p_target_func, p_margs); + } else { + m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); + } +} + +template <typename R, typename... P> +void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) { + if constexpr (sizeof...(P) == 0) { + m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs); + } else { + m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{}); + } +} + +typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs); + +void set_trampoline(const char *cookies, TrampolineFunc trampoline_func); + +void lazy_initialize(); + +template <typename... P> +struct ICallTrampolines { + static constexpr auto cookies = get_m2n_cookies<P...>(); + + static void add() { + lazy_initialize(); + set_trampoline(cookies.elems, &m2n_trampoline<P...>); + } +}; + +template <typename R, typename... P> +struct ICallTrampolinesR { + static constexpr auto cookies = get_m2n_cookies_r<R, P...>(); + + static void add() { + lazy_initialize(); + set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>); + } +}; + +void initialize(); +} // namespace GDMonoWasmM2n + +#endif + +#endif // GD_MONO_WASM_M2N_H diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp index 18daf859b5..bc2ae03299 100644 --- a/modules/mono/mono_gd/support/android_support.cpp +++ b/modules/mono/mono_gd/support/android_support.cpp @@ -355,8 +355,8 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) { } void register_internal_calls() { - mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store); - mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup); + GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store); + GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup); } void initialize() { |