Keep track of all C++ objects allocated by bindings in libretro builds

This commit adds `sb()->create_object()`, `sb()->get_object()`,
`sb()->check_object_type()` and `sb()->destroy_object()` in libretro
builds to keep track of all C++ objects allocated by the bindings in
libretro builds. This has some benefits:

* Any C++ objects allocated by the bindings that are still alive when
  the game terminates can now be deallocated instead of being leaked
  like before.
* We now keep track of the types of all objects allocated by the
  bindings, so we will be able to detect when the bindings attempt to
  access objects of mismatching type.
* Keeping track of all allocated objects is required to implement
  libretro save states.
* Objects are now kept track of using numeric keys whose sizes are the
  same on every platform rather than pointers, which helps with making
  save states portable across platforms.
This commit is contained in:
刘皓 2025-05-19 14:44:44 -04:00
parent 01bc2d71fe
commit 031245491f
No known key found for this signature in database
GPG key ID: 7901753DB465B711
18 changed files with 220 additions and 76 deletions

View file

@ -20,6 +20,7 @@
*/
#include "binding-base.h"
#include "mkxp-polyfill.h"
using namespace mkxp_sandbox;
@ -40,7 +41,7 @@ binding_base::stack_frame::~stack_frame() {
}
}
binding_base::binding_base(std::shared_ptr<struct w2c_ruby> m) : _instance(m) {}
binding_base::binding_base(std::shared_ptr<struct w2c_ruby> m) : next_free_objkey(0), _instance(m) {}
binding_base::~binding_base() {
// Destroy all stack frames in order from top to bottom to enforce a portable, compiler-independent ordering of stack frame destruction
@ -196,3 +197,77 @@ void binding_base::strcpy(wasm_ptr_t dst_address, const char *src) const noexcep
void binding_base::strncpy(wasm_ptr_t dst_address, const char *src, wasm_size_t max_size) const noexcept {
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(struct object &&object) noexcept : typenum(std::exchange(object.typenum, 0)), inner(std::exchange(object.inner, {.next = 0})) {}
struct binding_base::object &binding_base::object::operator=(struct object &&object) noexcept {
typenum = std::exchange(object.typenum, 0);
inner = std::exchange(object.inner, {.next = 0});
return *this;
}
binding_base::object::~object() {
if (typenum != 0) {
inner.inner.destructor(inner.inner.ptr);
}
}
wasm_objkey_t binding_base::create_object(wasm_size_t typenum, void *ptr, void (*destructor)(void *)) {
if (typenum == 0 || ptr == nullptr || destructor == nullptr) {
std::abort();
}
if (next_free_objkey == 0) {
objects.emplace_back(typenum, ptr, destructor);
if ((size_t)(wasm_objkey_t)objects.size() < objects.size()) {
MKXPZ_THROW(std::bad_alloc());
}
return objects.size();
} else {
wasm_objkey_t key = next_free_objkey;
struct object &object = objects[next_free_objkey - 1];
assert(object.typenum == 0);
next_free_objkey = object.inner.next;
object.typenum = typenum;
object.inner.inner.ptr = ptr;
object.inner.inner.destructor = destructor;
return key;
}
}
void *binding_base::get_object(wasm_objkey_t key) {
if (key == 0 || key > objects.size()) {
std::abort();
}
struct object &object = objects[key - 1];
if (object.typenum == 0) {
std::abort();
}
return object.inner.inner.ptr;
}
bool binding_base::check_object_type(wasm_objkey_t key, wasm_size_t typenum) {
if (key == 0 || key > objects.size()) {
std::abort();
}
struct object &object = objects[key - 1];
if (object.typenum == 0) {
std::abort();
}
return object.typenum == typenum;
}
void binding_base::destroy_object(wasm_objkey_t key) {
if (key == 0 || key > objects.size()) {
std::abort();
}
struct object &object = objects[key - 1];
if (object.typenum == 0) {
std::abort();
}
object.typenum = 0;
object.inner.inner.destructor(object.inner.inner.ptr);
object.inner.next = next_free_objkey;
next_free_objkey = key;
}

View file

@ -149,7 +149,7 @@ namespace mkxp_sandbox {
// Gets a reference to the value stored at a given address in sandbox memory.
template <typename T> T &sandbox_ref(struct w2c_ruby &instance, wasm_ptr_t address) noexcept {
// TODO: require T to be numeric
static_assert(std::is_integral<T>::value || std::is_floating_point<T>::value, "can only get references to numeric values in the sandbox");
#ifdef MKXPZ_BIG_ENDIAN
return *(T *)((uint8_t *)sandbox_ptr(instance, address) - sizeof(T));
#else
@ -218,8 +218,32 @@ namespace mkxp_sandbox {
size_t stack_index;
};
struct object {
// If this is a free object, this is 0.
// Otherwise, this is a number corresponding to the type of the object.
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.
// Otherwise, `inner.ptr` is a pointer to the actual object and `inner.destructor` is a pointer to its destructor.
union {
struct {
void *ptr;
void (*destructor)(void *);
} inner;
wasm_size_t next;
} inner;
object(wasm_size_t typenum, void *ptr, void (*destructor)(void *));
object(const struct object &object) = delete;
object(struct object &&object) noexcept;
struct object &operator=(const struct object &object) = delete;
struct object &operator=(struct object &&object) noexcept;
~object();
};
std::shared_ptr<struct w2c_ruby> _instance;
std::unordered_map<key_t, struct fiber, boost::hash<key_t>> fibers;
std::vector<struct object> objects;
wasm_objkey_t next_free_objkey;
wasm_ptr_t stack_ptr;
public:
@ -266,6 +290,18 @@ namespace mkxp_sandbox {
return sandbox_arycpy(instance(), dst_address, src, num_elements);
}
// Creates a new object and returns its key.
wasm_objkey_t create_object(wasm_size_t typenum, void *ptr, void (*destructor)(void *));
// Gets the object with the given key.
void *get_object(wasm_objkey_t key);
// Returns true if the typenum of the object with the given key matches the given typenum, otherwise false.
bool check_object_type(wasm_objkey_t key, wasm_size_t typenum);
// Destroys the object with the given key, calling its destructor and freeing its key for use by future objects.
void destroy_object(wasm_objkey_t key);
template <typename T> struct stack_frame_guard {
static_assert(std::is_base_of<boost::asio::coroutine, T>::value, "`T` must be a subclass of `boost::asio::coroutine`");
friend struct binding_base;

View file

@ -20,31 +20,12 @@
*/
#include "binding-util.h"
#include "filesystem.h"
#include "sharedstate.h"
using namespace mkxp_sandbox;
void mkxp_sandbox::set_private_data(VALUE obj, void *ptr) {
/* RGSS's behavior is to just leak memory if a disposable is reinitialized,
* with the original disposable being left permanently instantiated,
* but that's (1) bad, and (2) would currently cause memory access issues
* when things like a sprite's src_rect inevitably get GC'd, so we're not
* copying that. */
wasm_ptr_t data = sb()->rtypeddata_data(obj);
// Free the old value if it already exists (initialize called twice?)
if (sb()->ref<wasm_ptr_t>(data) != 0 && sb()->ref<void *>(sb()->ref<wasm_ptr_t>(data)) != ptr) {
sb()->rtypeddata_dfree(obj, sb()->ref<wasm_ptr_t>(data));
sb()->ref<wasm_ptr_t>(data) = 0;
}
if (sb()->ref<wasm_ptr_t>(data) == 0) {
wasm_ptr_t buf = sb()->sandbox_malloc(sizeof(void *));
sb()->ref<void *>(buf) = ptr;
sb()->ref<wasm_ptr_t>(data) = buf;
}
void mkxp_sandbox::dfree(wasm_objkey_t key) {
sb()->destroy_object(key);
}
wasm_size_t get_length::operator()(VALUE obj) {
@ -67,16 +48,6 @@ wasm_size_t get_bytesize::operator()(VALUE obj) {
return SANDBOX_SLOT(2);
}
VALUE wrap_property::operator()(VALUE self, void *ptr, const char *iv, VALUE klass) {
BOOST_ASIO_CORO_REENTER (this) {
SANDBOX_AWAIT_S(0, rb_obj_alloc, klass);
set_private_data(SANDBOX_SLOT(0), ptr);
SANDBOX_AWAIT(rb_iv_set, self, iv, SANDBOX_SLOT(0));
}
return SANDBOX_SLOT(0);
}
void log_backtrace::operator()(VALUE exception) {
BOOST_ASIO_CORO_REENTER (this) {
SANDBOX_AWAIT(rb_p, exception);

View file

@ -28,6 +28,18 @@
#include "exception.h"
#include "sandbox.h"
#include "bitmap.h"
#include "etc.h"
#include "font.h"
#include "plane.h"
#include "sprite.h"
#include "table.h"
#include "tilemap.h"
#include "tilemapvx.h"
#include "viewport.h"
#include "window.h"
#include "windowvx.h"
#define SANDBOX_SLOT(slot_index) (::mkxp_sandbox::sb()->ref<typename ::mkxp_sandbox::slot_type<(slot_index), slots>::type>(::mkxp_sandbox::sb()->stack_pointer() + ::mkxp_sandbox::slot_offset<(slot_index), slots>::value))
#define SANDBOX_AWAIT(coroutine, ...) \
@ -92,7 +104,7 @@
VALUE operator()(VALUE _klass) { \
BOOST_ASIO_CORO_REENTER (this) { \
SANDBOX_AWAIT_S(0, rb_data_typed_object_wrap, _klass, 0, rbtype); \
set_private_data(SANDBOX_SLOT(0), initializer); /* TODO: free when sandbox is deallocated */ \
set_private_data(SANDBOX_SLOT(0), initializer); \
} \
return SANDBOX_SLOT(0); \
} \
@ -100,13 +112,6 @@
return sb()->bind<struct coro>()()(_klass); \
}
namespace mkxp_sandbox {
template <class T> void dfree(wasm_ptr_t buf) {
delete sb()->ref<T *>(buf);
sb()->sandbox_free(buf);
}
}
#define SANDBOX_DEF_CLASS_PROP_B(S, prop, name) \
static VALUE get_##name(VALUE self) { \
using namespace ::mkxp_sandbox; \
@ -450,7 +455,28 @@ namespace mkxp_sandbox {
#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_DEF_TYPENUM(num, T) \
static_assert(num != 0, "typenum cannot be 0"); \
template <> struct get_typenum<T> { static constexpr wasm_size_t value = num; };
namespace mkxp_sandbox {
template <typename T> struct get_typenum;
_SANDBOX_DEF_TYPENUM(1, Bitmap);
_SANDBOX_DEF_TYPENUM(2, Color);
_SANDBOX_DEF_TYPENUM(3, Font);
_SANDBOX_DEF_TYPENUM(4, Plane);
_SANDBOX_DEF_TYPENUM(5, Rect);
_SANDBOX_DEF_TYPENUM(6, Sprite);
_SANDBOX_DEF_TYPENUM(7, Tilemap);
_SANDBOX_DEF_TYPENUM(8, Tilemap::Autotiles);
_SANDBOX_DEF_TYPENUM(9, TilemapVX);
_SANDBOX_DEF_TYPENUM(10, TilemapVX::BitmapArray);
_SANDBOX_DEF_TYPENUM(11, Table);
_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.
// 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.
template <typename Coroutine, typename... Args> bool _sandbox_await(Args... args) {
@ -469,14 +495,48 @@ namespace mkxp_sandbox {
}
}
// Given Ruby typed data `obj`, stores `ptr` into the private data field of `obj`.
void set_private_data(VALUE obj, void *ptr);
// Given Ruby typed data `obj`, retrieves the private data field of `obj`.
template <typename T> inline T *get_private_data(VALUE obj) {
return sb()->ref<T *>(sb()->ref<wasm_ptr_t>(sb()->rtypeddata_data(obj)));
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`.
// 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.
template <typename T> void set_private_data(VALUE val, T *ptr) {
/* RGSS's behavior is to just leak memory if a disposable is reinitialized,
* with the original disposable being left permanently instantiated,
* but that's (1) bad, and (2) would currently cause memory access issues
* when things like a sprite's src_rect inevitably get GC'd, so we're not
* copying that. */
wasm_objkey_t &key = sb()->ref<wasm_objkey_t>(sb()->rtypeddata_data(val));
// Free the old value if it already exists (initialize called twice?)
if (key != 0 && sb()->get_object(key) != ptr) {
sb()->destroy_object(key);
}
key = ptr == nullptr ? 0 : sb()->create_object(get_typenum<T>::value, ptr, _set_private_data_destructor<T>);
}
// Given a Ruby object `val`, retrieves the C++ object in its private data field.
// Aborts if the private data field hasn't ever been set, has been set to `nullptr` or has been set to an object of a different type,
// so make sure it's been set properly using `set_private_data` beforehand.
template <typename T> inline T *get_private_data(VALUE val) {
wasm_objkey_t key = sb()->ref<wasm_ptr_t>(sb()->rtypeddata_data(val));
if (!sb()->check_object_type(key, get_typenum<T>::value)) {
std::abort();
}
return (T *)sb()->get_object(key);
}
void dfree(wasm_objkey_t key);
// Gets the length of a Ruby object.
struct get_length : boost::asio::coroutine {
typedef decl_slots<ID, VALUE, wasm_size_t> slots;
@ -491,7 +551,16 @@ namespace mkxp_sandbox {
struct wrap_property : boost::asio::coroutine {
typedef decl_slots<VALUE> slots;
VALUE operator()(VALUE self, void *ptr, const char *iv, VALUE klass);
template <typename T> VALUE operator()(VALUE self, T *ptr, const char *iv, VALUE klass) {
BOOST_ASIO_CORO_REENTER (this) {
SANDBOX_AWAIT_S(0, rb_obj_alloc, klass);
set_private_data(SANDBOX_SLOT(0), ptr);
SANDBOX_AWAIT(rb_iv_set, self, iv, SANDBOX_SLOT(0));
}
return SANDBOX_SLOT(0);
}
};
// Prints the backtrace of a Ruby exception to the log.
@ -518,4 +587,6 @@ namespace mkxp_sandbox {
};
}
#undef _SANDBOX_DEF_TYPENUM
#endif // MKXPZ_SANDBOX_BINDING_UTIL_H

View file

@ -821,7 +821,7 @@ static VALUE set_font(VALUE self, VALUE value) {
void bitmap_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
bitmap_type = sb()->rb_data_type("Bitmap", nullptr, dfree<Bitmap>, nullptr, nullptr, 0, 0, 0);
bitmap_type = sb()->rb_data_type("Bitmap", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(bitmap_class, rb_define_class, "Bitmap", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, bitmap_class, alloc);
SANDBOX_AWAIT(rb_define_method, bitmap_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -148,7 +148,7 @@ struct color_binding_init : boost::asio::coroutine {
void operator()() {
BOOST_ASIO_CORO_REENTER (this) {
color_type = sb()->rb_data_type("Color", nullptr, dfree<Color>, nullptr, nullptr, 0, 0, 0);
color_type = sb()->rb_data_type("Color", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(color_class, rb_define_class, "Color", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, color_class, alloc);
SANDBOX_AWAIT(rb_define_method, color_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);
@ -281,7 +281,7 @@ struct tone_binding_init : boost::asio::coroutine {
void operator()() {
BOOST_ASIO_CORO_REENTER (this) {
tone_type = sb()->rb_data_type("Tone", nullptr, dfree<Tone>, nullptr, nullptr, 0, 0, 0);
tone_type = sb()->rb_data_type("Tone", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(tone_class, rb_define_class, "Tone", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, tone_class, alloc);
SANDBOX_AWAIT(rb_define_method, tone_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);
@ -411,7 +411,7 @@ struct rect_binding_init : boost::asio::coroutine {
void operator()() {
BOOST_ASIO_CORO_REENTER (this) {
rect_type = sb()->rb_data_type("Rect", nullptr, dfree<Rect>, nullptr, nullptr, 0, 0, 0);
rect_type = sb()->rb_data_type("Rect", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(rect_class, rb_define_class, "Rect", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, rect_class, alloc);
SANDBOX_AWAIT(rb_define_method, rect_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -246,7 +246,7 @@ static VALUE exist(VALUE self, VALUE value) {
void font_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
font_type = sb()->rb_data_type("Font", nullptr, dfree<Font>, nullptr, nullptr, 0, 0, 0);
font_type = sb()->rb_data_type("Font", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(font_class, rb_define_class, "Font", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, font_class, alloc);

View file

@ -67,7 +67,7 @@ SANDBOX_DEF_GFX_PROP_I(Plane, BlendType, blend_type);
void plane_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
plane_type = sb()->rb_data_type("Plane", nullptr, dfree<Plane>, nullptr, nullptr, 0, 0, 0);
plane_type = sb()->rb_data_type("Plane", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(plane_class, rb_define_class, "Plane", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, plane_class, alloc);
SANDBOX_AWAIT(rb_define_method, plane_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -34,7 +34,7 @@ namespace mkxp_sandbox {
SANDBOX_AWAIT_S(2, rb_obj_alloc, klass);
SANDBOX_AWAIT_S(0, rb_string_value_ptr, &src);
SANDBOX_AWAIT_S(1, get_bytesize, src);
SANDBOX_GUARD(set_private_data(SANDBOX_SLOT(2), C::deserialize(sb().e, (const char *)sb()->ptr(SANDBOX_SLOT(0)), SANDBOX_SLOT(1)))); /* TODO: free when sandbox is deallocated */
SANDBOX_GUARD(set_private_data(SANDBOX_SLOT(2), C::deserialize(sb().e, (const char *)sb()->ptr(SANDBOX_SLOT(0)), SANDBOX_SLOT(1))));
}
return SANDBOX_SLOT(2);

View file

@ -122,7 +122,7 @@ SANDBOX_DEF_GFX_PROP_I(Sprite, WaveSpeed, wave_speed);
void sprite_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
sprite_type = sb()->rb_data_type("Sprite", nullptr, dfree<Sprite>, nullptr, nullptr, 0, 0, 0);
sprite_type = sb()->rb_data_type("Sprite", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(sprite_class, rb_define_class, "Sprite", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, sprite_class, alloc);
SANDBOX_AWAIT(rb_define_method, sprite_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -189,7 +189,7 @@ static VALUE set(int32_t argc, wasm_ptr_t argv, VALUE self) {
void table_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
table_type = sb()->rb_data_type("Table", nullptr, dfree<Table>, nullptr, nullptr, 0, 0, 0);
table_type = sb()->rb_data_type("Table", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(table_class, rb_define_class, "Table", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, table_class, alloc);
SANDBOX_AWAIT(rb_define_method, table_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -84,7 +84,7 @@ struct tilemap_autotiles_binding_init : boost::asio::coroutine {
void operator()() {
BOOST_ASIO_CORO_REENTER (this) {
tilemap_autotiles_type = sb()->rb_data_type("TilemapAutotiles", nullptr, nullptr, nullptr, nullptr, 0, 0, 0);
tilemap_autotiles_type = sb()->rb_data_type("TilemapAutotiles", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(tilemap_autotiles_class, rb_define_class, "TilemapAutotiles", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, tilemap_autotiles_class, alloc);
@ -125,7 +125,7 @@ static VALUE initialize(int32_t argc, wasm_ptr_t argv, VALUE self) {
* See the comment in setPrivateData for more info. */
SANDBOX_AWAIT_S(1, rb_iv_get, self, "autotiles");
if (SANDBOX_SLOT(1) != SANDBOX_NIL) {
set_private_data(SANDBOX_SLOT(1), nullptr);
set_private_data(SANDBOX_SLOT(1), (Tilemap::Autotiles *)nullptr);
}
SANDBOX_GUARD(SANDBOX_AWAIT_S(1, wrap_property, self, &get_private_data<Tilemap>(self)->getAutotiles(sb().e), "autotiles", tilemap_autotiles_class));
@ -195,7 +195,7 @@ void tilemap_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
SANDBOX_AWAIT(tilemap_autotiles_binding_init);
tilemap_type = sb()->rb_data_type("Tilemap", nullptr, dfree<Tilemap>, nullptr, nullptr, 0, 0, 0);
tilemap_type = sb()->rb_data_type("Tilemap", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(tilemap_class, rb_define_class, "Tilemap", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, tilemap_class, alloc);
SANDBOX_AWAIT(rb_define_method, tilemap_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -83,7 +83,7 @@ struct bitmap_array_binding_init : boost::asio::coroutine {
void operator()() {
BOOST_ASIO_CORO_REENTER (this) {
bitmap_array_type = sb()->rb_data_type("BitmapArray", nullptr, nullptr, nullptr, nullptr, 0, 0, 0);
bitmap_array_type = sb()->rb_data_type("BitmapArray", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(bitmap_array_class, rb_define_class, "BitmapArray", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, bitmap_array_class, alloc);
@ -119,7 +119,7 @@ static VALUE initialize(int32_t argc, wasm_ptr_t argv, VALUE self) {
* See the comment in setPrivateData for more info. */
SANDBOX_AWAIT_S(1, rb_iv_get, self, "bitmap_array");
if (SANDBOX_SLOT(1) != SANDBOX_NIL) {
set_private_data(SANDBOX_SLOT(1), nullptr);
set_private_data(SANDBOX_SLOT(1), (TilemapVX::BitmapArray *)nullptr);
}
SANDBOX_GUARD(SANDBOX_AWAIT_S(1, wrap_property, self, &get_private_data<TilemapVX>(self)->getBitmapArray(sb().e), "bitmap_array", bitmap_array_class));
@ -177,7 +177,7 @@ void tilemapvx_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
SANDBOX_AWAIT(bitmap_array_binding_init);
tilemapvx_type = sb()->rb_data_type("Tilemap", nullptr, dfree<TilemapVX>, nullptr, nullptr, 0, 0, 0);
tilemapvx_type = sb()->rb_data_type("Tilemap", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(tilemapvx_class, rb_define_class, "Tilemap", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, tilemapvx_class, alloc);
SANDBOX_AWAIT(rb_define_method, tilemapvx_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -84,7 +84,7 @@ SANDBOX_DEF_GFX_PROP_OBJ_VAL(Viewport, Tone, Tone, tone);
void viewport_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
viewport_type = sb()->rb_data_type("Viewport", nullptr, dfree<Viewport>, nullptr, nullptr, 0, 0, 0);
viewport_type = sb()->rb_data_type("Viewport", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(viewport_class, rb_define_class, "Viewport", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, viewport_class, alloc);
SANDBOX_AWAIT(rb_define_method, viewport_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -35,6 +35,7 @@ namespace mkxp_sandbox {
#define ANYARGS ...
typedef wasm_size_t wasm_ptr_t;
typedef wasm_ptr_t wasm_objkey_t;
typedef wasm_ptr_t VALUE;
typedef wasm_ptr_t ID;
}

View file

@ -85,7 +85,7 @@ SANDBOX_DEF_GFX_PROP_I(Window, ContentsOpacity, contents_opacity);
void window_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
window_type = sb()->rb_data_type("Window", nullptr, dfree<Window>, nullptr, nullptr, 0, 0, 0);
window_type = sb()->rb_data_type("Window", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(window_class, rb_define_class, "Window", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, window_class, alloc);
SANDBOX_AWAIT(rb_define_method, window_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -170,7 +170,7 @@ SANDBOX_DEF_GFX_PROP_OBJ_VAL(WindowVX, Tone, Tone, tone);
void windowvx_binding_init::operator()() {
BOOST_ASIO_CORO_REENTER (this) {
windowvx_type = sb()->rb_data_type("Window", nullptr, dfree<WindowVX>, nullptr, nullptr, 0, 0, 0);
windowvx_type = sb()->rb_data_type("Window", nullptr, dfree, nullptr, nullptr, 0, 0, 0);
SANDBOX_AWAIT_R(windowvx_class, rb_define_class, "Window", sb()->rb_cObject());
SANDBOX_AWAIT(rb_define_alloc_func, windowvx_class, alloc);
SANDBOX_AWAIT(rb_define_method, windowvx_class, "initialize", (VALUE (*)(ANYARGS))initialize, -1);

View file

@ -624,18 +624,8 @@ void Font::initDynAttribs()
void Font::initDefaultDynAttribs()
{
if (FontPrivate::defaultColor != &FontPrivate::defaultColorTmp)
{
delete FontPrivate::defaultColor;
FontPrivate::defaultColor = &FontPrivate::defaultColorTmp;
}
FontPrivate::defaultColor = new Color(FontPrivate::defaultColorTmp);
if (FontPrivate::defaultOutColor != &FontPrivate::defaultOutColorTmp)
{
delete FontPrivate::defaultOutColor;
FontPrivate::defaultOutColor = &FontPrivate::defaultOutColorTmp;
}
if (rgssVer >= 3)
FontPrivate::defaultOutColor = new Color(FontPrivate::defaultOutColorTmp);
}