Store destructors in a global table instead of in the object itself in libretro builds

This commit is contained in:
刘皓 2025-05-19 19:27:30 -04:00
parent 16e701fc7c
commit cd628048ef
No known key found for this signature in database
GPG key ID: 7901753DB465B711
7 changed files with 75 additions and 49 deletions

View file

@ -198,7 +198,7 @@ void binding_base::strncpy(wasm_ptr_t dst_address, const char *src, wasm_size_t
sandbox_strncpy(instance(), dst_address, src, max_size); sandbox_strncpy(instance(), dst_address, src, max_size);
} }
binding_base::object::object(wasm_size_t typenum, void *ptr, void (*destructor)(void *)) : typenum(typenum), inner {.inner = {.ptr = ptr, .destructor = destructor}} {} binding_base::object::object(wasm_size_t typenum, void *ptr) : typenum(typenum), inner {.ptr = ptr} {}
binding_base::object::object(struct object &&object) noexcept : typenum(std::exchange(object.typenum, 0)), inner(std::exchange(object.inner, (union binding_base::object::inner){.next = 0})) {} binding_base::object::object(struct object &&object) noexcept : typenum(std::exchange(object.typenum, 0)), inner(std::exchange(object.inner, (union binding_base::object::inner){.next = 0})) {}
@ -210,16 +210,16 @@ struct binding_base::object &binding_base::object::operator=(struct object &&obj
binding_base::object::~object() { binding_base::object::~object() {
if (typenum != 0) { if (typenum != 0) {
inner.inner.destructor(inner.inner.ptr); typenum_table[typenum - 1].destructor(inner.ptr);
} }
} }
wasm_objkey_t binding_base::create_object(wasm_size_t typenum, void *ptr, void (*destructor)(void *)) { wasm_objkey_t binding_base::create_object(wasm_size_t typenum, void *ptr) {
if (typenum == 0 || ptr == nullptr || destructor == nullptr) { if (ptr == nullptr || typenum == 0 || typenum > typenum_table_size) {
std::abort(); std::abort();
} }
if (next_free_objkey == 0) { if (next_free_objkey == 0) {
objects.emplace_back(typenum, ptr, destructor); objects.emplace_back(typenum, ptr);
if ((size_t)(wasm_objkey_t)objects.size() < objects.size()) { if ((size_t)(wasm_objkey_t)objects.size() < objects.size()) {
MKXPZ_THROW(std::bad_alloc()); MKXPZ_THROW(std::bad_alloc());
} }
@ -230,8 +230,7 @@ wasm_objkey_t binding_base::create_object(wasm_size_t typenum, void *ptr, void (
assert(object.typenum == 0); assert(object.typenum == 0);
next_free_objkey = object.inner.next; next_free_objkey = object.inner.next;
object.typenum = typenum; object.typenum = typenum;
object.inner.inner.ptr = ptr; object.inner.ptr = ptr;
object.inner.inner.destructor = destructor;
return key; return key;
} }
} }
@ -241,10 +240,10 @@ void *binding_base::get_object(wasm_objkey_t key) {
std::abort(); std::abort();
} }
struct object &object = objects[key - 1]; struct object &object = objects[key - 1];
if (object.typenum == 0) { if (object.typenum == 0 || object.typenum > typenum_table_size) {
std::abort(); std::abort();
} }
return object.inner.inner.ptr; return object.inner.ptr;
} }
bool binding_base::check_object_type(wasm_objkey_t key, wasm_size_t typenum) { bool binding_base::check_object_type(wasm_objkey_t key, wasm_size_t typenum) {
@ -252,7 +251,7 @@ bool binding_base::check_object_type(wasm_objkey_t key, wasm_size_t typenum) {
std::abort(); std::abort();
} }
struct object &object = objects[key - 1]; struct object &object = objects[key - 1];
if (object.typenum == 0) { if (object.typenum == 0 || object.typenum > typenum_table_size) {
std::abort(); std::abort();
} }
return object.typenum == typenum; return object.typenum == typenum;
@ -263,11 +262,11 @@ void binding_base::destroy_object(wasm_objkey_t key) {
std::abort(); std::abort();
} }
struct object &object = objects[key - 1]; struct object &object = objects[key - 1];
if (object.typenum == 0) { if (object.typenum == 0 || object.typenum > typenum_table_size) {
std::abort(); std::abort();
} }
typenum_table[object.typenum - 1].destructor(object.inner.ptr);
object.typenum = 0; object.typenum = 0;
object.inner.inner.destructor(object.inner.inner.ptr);
object.inner.next = next_free_objkey; object.inner.next = next_free_objkey;
next_free_objkey = key; next_free_objkey = key;
} }

View file

@ -196,6 +196,13 @@ namespace mkxp_sandbox {
#endif #endif
} }
struct typenum_table_entry {
void (*destructor)(void *);
};
extern const struct typenum_table_entry typenum_table[];
extern const wasm_size_t typenum_table_size;
struct binding_base { struct binding_base {
private: private:
typedef std::tuple<wasm_ptr_t, wasm_ptr_t, wasm_ptr_t> key_t; typedef std::tuple<wasm_ptr_t, wasm_ptr_t, wasm_ptr_t> key_t;
@ -223,16 +230,13 @@ namespace mkxp_sandbox {
// Otherwise, this is a number corresponding to the type of the object. // Otherwise, this is a number corresponding to the type of the object.
wasm_size_t typenum; wasm_size_t typenum;
// If this is a free object, the `next` field is the key of the next free object, or 0 if this is the last free object. // If this is a free object, the `next` field is the key of the next free object, or 0 if this is the last free object.
// Otherwise, `inner.ptr` is a pointer to the actual object and `inner.destructor` is a pointer to its destructor. // Otherwise, the `ptr` field is a pointer to the actual object.
union inner { union inner {
struct {
void *ptr;
void (*destructor)(void *);
} inner;
wasm_size_t next; wasm_size_t next;
void *ptr;
} inner; } inner;
object(wasm_size_t typenum, void *ptr, void (*destructor)(void *)); object(wasm_size_t typenum, void *ptr);
object(const struct object &object) = delete; object(const struct object &object) = delete;
object(struct object &&object) noexcept; object(struct object &&object) noexcept;
struct object &operator=(const struct object &object) = delete; struct object &operator=(const struct object &object) = delete;
@ -291,7 +295,7 @@ namespace mkxp_sandbox {
} }
// Creates a new object and returns its key. // Creates a new object and returns its key.
wasm_objkey_t create_object(wasm_size_t typenum, void *ptr, void (*destructor)(void *)); wasm_objkey_t create_object(wasm_size_t typenum, void *ptr);
// Gets the object with the given key. // Gets the object with the given key.
void *get_object(wasm_objkey_t key); void *get_object(wasm_objkey_t key);

View file

@ -24,6 +24,19 @@
using namespace mkxp_sandbox; using namespace mkxp_sandbox;
template <typename T> static typename std::enable_if<std::is_destructible<T>::value>::type destructor(void *ptr) {
static_assert(!(std::is_same<T, Tilemap::Autotiles>::value || std::is_same<T, TilemapVX::BitmapArray>::value), "this type should not have a public destructor");
delete (T *)ptr;
}
template <typename T> static typename std::enable_if<!std::is_destructible<T>::value>::type destructor(void *ptr) {
static_assert(std::is_same<T, Tilemap::Autotiles>::value || std::is_same<T, TilemapVX::BitmapArray>::value, "this type should have a public destructor");
}
#define _SANDBOX_DEF_TYPENUM_TABLE_ENTRY(_r, _data, T) {.destructor = destructor<T>},
extern const struct typenum_table_entry mkxp_sandbox::typenum_table[SANDBOX_NUM_TYPENUMS] = {BOOST_PP_SEQ_FOR_EACH(_SANDBOX_DEF_TYPENUM_TABLE_ENTRY, _, SANDBOX_TYPENUM_TYPES)};
extern const wasm_size_t mkxp_sandbox::typenum_table_size = SANDBOX_NUM_TYPENUMS;
void mkxp_sandbox::dfree(wasm_objkey_t key) { void mkxp_sandbox::dfree(wasm_objkey_t key) {
sb()->destroy_object(key); sb()->destroy_object(key);
} }

View file

@ -24,6 +24,8 @@
#include <type_traits> #include <type_traits>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include "core.h" #include "core.h"
#include "exception.h" #include "exception.h"
#include "sandbox.h" #include "sandbox.h"
@ -455,27 +457,36 @@
#define SANDBOX_GUARD_LF(finalizer, ...) do { GFX_LOCK; SANDBOX_GUARD_F(finalizer; GFX_UNLOCK, __VA_ARGS__); GFX_UNLOCK; } while (0) #define SANDBOX_GUARD_LF(finalizer, ...) do { GFX_LOCK; SANDBOX_GUARD_F(finalizer; GFX_UNLOCK, __VA_ARGS__); GFX_UNLOCK; } while (0)
#define SANDBOX_GUARD_L(...) SANDBOX_GUARD_LF(, __VA_ARGS__) #define SANDBOX_GUARD_L(...) SANDBOX_GUARD_LF(, __VA_ARGS__)
#define _SANDBOX_DEF_TYPENUM(num, T) \ #define SANDBOX_TYPENUM_TYPES \
static_assert(num != 0, "typenum cannot be 0"); \ (Bitmap) \
template <> struct get_typenum<T> { static constexpr wasm_size_t value = num; }; (Color) \
(Font) \
(Plane) \
(Rect) \
(Sprite) \
(Table) \
(Tilemap) \
(Tilemap::Autotiles) \
(TilemapVX) \
(TilemapVX::BitmapArray) \
(Tone) \
(Viewport) \
(Window) \
(WindowVX) \
#define SANDBOX_NUM_TYPENUMS BOOST_PP_SEQ_SIZE(SANDBOX_TYPENUM_TYPES)
#define _SANDBOX_DEF_GET_TYPENUM_DETAIL(T, num) template <> struct get_typenum<T> { \
static_assert(num != 0, "typenum should not be 0"); \
static_assert(num <= SANDBOX_NUM_TYPENUMS, "typenum should not be greater than the number of typenums"); \
static constexpr wasm_size_t value = num; \
};
#define _SANDBOX_DEF_GET_TYPENUM(_r, _data, T) _SANDBOX_DEF_GET_TYPENUM_DETAIL(T, __COUNTER__ - _get_typenum_counter_start)
namespace mkxp_sandbox { namespace mkxp_sandbox {
template <typename T> struct get_typenum; template <typename T> struct get_typenum;
_SANDBOX_DEF_TYPENUM(1, Bitmap); static constexpr size_t _get_typenum_counter_start = __COUNTER__;
_SANDBOX_DEF_TYPENUM(2, Color); BOOST_PP_SEQ_FOR_EACH(_SANDBOX_DEF_GET_TYPENUM, _, SANDBOX_TYPENUM_TYPES);
_SANDBOX_DEF_TYPENUM(3, Font);
_SANDBOX_DEF_TYPENUM(4, Plane);
_SANDBOX_DEF_TYPENUM(5, Rect);
_SANDBOX_DEF_TYPENUM(6, Sprite);
_SANDBOX_DEF_TYPENUM(7, Table);
_SANDBOX_DEF_TYPENUM(8, Tilemap);
_SANDBOX_DEF_TYPENUM(9, Tilemap::Autotiles);
_SANDBOX_DEF_TYPENUM(10, TilemapVX);
_SANDBOX_DEF_TYPENUM(11, TilemapVX::BitmapArray);
_SANDBOX_DEF_TYPENUM(12, Tone);
_SANDBOX_DEF_TYPENUM(13, Viewport);
_SANDBOX_DEF_TYPENUM(14, Window);
_SANDBOX_DEF_TYPENUM(15, WindowVX);
// We need these helper functions so that the arguments to `SANDBOX_AWAIT`/`SANDBOX_AWAIT_R`/`SANDBOX_AWAIT_S` are evaluated before `sb()->bind` is called instead of after. // We need these helper functions so that the arguments to `SANDBOX_AWAIT`/`SANDBOX_AWAIT_R`/`SANDBOX_AWAIT_S` are evaluated before `sb()->bind` is called instead of after.
// The reverse happening can lead to incorrect behaviour if one or more of the arguments is using `SANDBOX_SLOT` or other macros that need the state of the sandbox. // The reverse happening can lead to incorrect behaviour if one or more of the arguments is using `SANDBOX_SLOT` or other macros that need the state of the sandbox.
@ -495,15 +506,6 @@ namespace mkxp_sandbox {
} }
} }
template <typename T> typename std::enable_if<std::is_destructible<T>::value>::type _set_private_data_destructor(void *ptr) {
static_assert(!(std::is_same<T, Tilemap::Autotiles>::value || std::is_same<T, TilemapVX::BitmapArray>::value), "this type should not have a public destructor");
delete (T *)ptr;
}
template <typename T> typename std::enable_if<!std::is_destructible<T>::value>::type _set_private_data_destructor(void *ptr) {
static_assert(std::is_same<T, Tilemap::Autotiles>::value || std::is_same<T, TilemapVX::BitmapArray>::value, "this type should have a public destructor");
}
// Given a Ruby object `val`, stores the C++ object `ptr` into the private data field of `val`. // Given a Ruby object `val`, stores the C++ object `ptr` into the private data field of `val`.
// You can set `ptr` to `nullptr` if you just want to destroy the current object in the private data field, // You can set `ptr` to `nullptr` if you just want to destroy the current object in the private data field,
// but note that calling `get_private_data` while the private data field is set to `nullptr` will trigger an abort. // but note that calling `get_private_data` while the private data field is set to `nullptr` will trigger an abort.
@ -521,7 +523,7 @@ namespace mkxp_sandbox {
sb()->destroy_object(key); sb()->destroy_object(key);
} }
key = ptr == nullptr ? 0 : sb()->create_object(get_typenum<T>::value, ptr, _set_private_data_destructor<T>); key = ptr == nullptr ? 0 : sb()->create_object(get_typenum<T>::value, ptr);
} }
// Given a Ruby object `val`, retrieves the C++ object in its private data field. // Given a Ruby object `val`, retrieves the C++ object in its private data field.
@ -587,6 +589,4 @@ namespace mkxp_sandbox {
}; };
} }
#undef _SANDBOX_DEF_TYPENUM
#endif // MKXPZ_SANDBOX_BINDING_UTIL_H #endif // MKXPZ_SANDBOX_BINDING_UTIL_H

View file

@ -33,6 +33,7 @@ if is_libretro
subproject('boost_asio').get_variable('boost_asio'), subproject('boost_asio').get_variable('boost_asio'),
subproject('boost_container_hash').get_variable('boost_container_hash'), subproject('boost_container_hash').get_variable('boost_container_hash'),
subproject('boost_optional').get_variable('boost_optional'), subproject('boost_optional').get_variable('boost_optional'),
subproject('boost_preprocessor').get_variable('boost_preprocessor'),
subproject('boost_type_traits').get_variable('boost_type_traits'), subproject('boost_type_traits').get_variable('boost_type_traits'),
] ]

View file

@ -0,0 +1,5 @@
[wrap-git]
url = https://github.com/boostorg/preprocessor
revision = boost-1.87.0
depth = 1
patch_directory = boost_preprocessor

View file

@ -0,0 +1,4 @@
project('boost_preprocessor', 'cpp', meson_version: '>=1.3.0')
boost_preprocessor = declare_dependency(
include_directories: 'include',
)