Skip to content

Commit 130375b

Browse files
committed
Properly handle uninitialized and reinitialized objects
In RGSS, uninitialized disposable objects (and Fonts, sort of) are technically disposed, and other objects (Tone, Color, Rect, and Table) are created with all values set to 0. It's also possible to reinitialize them, although reinitializing disposables leaks memory. This commit causes MiniFFI and FileInt objects to improperly raise disposed errors if used while uninitialized, but that feels acceptable to me.
1 parent 99ad4fa commit 130375b

8 files changed

+164
-46
lines changed

binding/binding-util.h

+53-4
Original file line numberDiff line numberDiff line change
@@ -198,20 +198,50 @@ template <rb_data_type_t *rbType> static VALUE classAllocate(VALUE klass) {
198198
}
199199
#endif
200200

201+
#if RAPI_FULL > 187
202+
#define CLASS_ALLOCATE_PRE_INIT(Name, initializeFunc) \
203+
static VALUE Name##AllocatePreInit(VALUE klass) { \
204+
VALUE ret = classAllocate<& Name##Type>(klass); \
205+
\
206+
initializeFunc(0, 0, ret); \
207+
\
208+
return ret; \
209+
}
210+
#else
211+
#define CLASS_ALLOCATE_PRE_INIT(Name, initializeFunc) \
212+
static VALUE Name##AllocatePreInit(VALUE klass) { \
213+
VALUE ret = Name##Allocate(klass); \
214+
\
215+
initializeFunc(0, 0, ret); \
216+
\
217+
return ret; \
218+
}
219+
#endif
220+
201221
template <class C> static void freeInstance(void *inst) {
202222
delete static_cast<C *>(inst);
203223
}
204224

205225
void raiseDisposedAccess(VALUE self);
206226

207-
template <class C> inline C *getPrivateData(VALUE self) {
227+
template <class C> inline C *getPrivateDataNoRaise(VALUE self) {
208228
#if RAPI_FULL > 187
209-
C *c = static_cast<C *>(RTYPEDDATA_DATA(self));
229+
return static_cast<C *>(RTYPEDDATA_DATA(self));
210230
#else
211-
C *c = static_cast<C *>(DATA_PTR(self));
231+
return static_cast<C *>(DATA_PTR(self));
212232
#endif
233+
}
234+
235+
template <class C> inline C *getPrivateData(VALUE self) {
236+
C *c = getPrivateDataNoRaise<C>(self);
237+
213238
if (!c) {
214-
raiseRbExc(Exception(Exception::MKXPError, "No instance data for variable (missing call to super?)"));
239+
//raiseRbExc(Exception(Exception::MKXPError, "No instance data for variable (missing call to super?)"));
240+
241+
/* FIXME: MiniFFI and FileInt don't have default allocations
242+
* despite not being disposables. Should they be fixed,
243+
* or just left with a misleading error message? */
244+
raiseDisposedAccess(self);
215245
}
216246
return c;
217247
}
@@ -246,9 +276,28 @@ getPrivateDataCheck(VALUE self, const char *type)
246276
}
247277

248278
static inline void setPrivateData(VALUE self, void *p) {
279+
/* RGSS's behavior is to just leak memory if a disposable is reinitialized,
280+
* with the original disposable being left permanently instantiated,
281+
* but that's (1) bad, and (2) would currently cause memory access issues
282+
* when things like a sprite's src_rect inevitably get GC'd, so we're not
283+
* copying that. */
249284
#if RAPI_FULL > 187
285+
// Free the old value if it already exists (initialize called twice?)
286+
if (RTYPEDDATA_DATA(self) && (RTYPEDDATA_DATA(self) != p)) {
287+
/* RUBY_TYPED_NEVER_FREE == 0, and we don't use
288+
* RUBY_TYPED_DEFAULT_FREE for our stuff, so just
289+
* checking if it's truthy should be fine */
290+
if (RTYPEDDATA_TYPE(self)->function.dfree)
291+
(*RTYPEDDATA_TYPE(self)->function.dfree)(RTYPEDDATA_DATA(self));
292+
}
250293
RTYPEDDATA_DATA(self) = p;
251294
#else
295+
// Free the old value if it already exists (initialize called twice?)
296+
if (DATA_PTR(self) && (DATA_PTR(self) != p)) {
297+
/* As above, just check if it's truthy */
298+
if (RDATA(self)->dfree)
299+
(*RDATA(self)->dfree)(DATA_PTR(self));
300+
}
252301
DATA_PTR(self) = p;
253302
#endif
254303
}

binding/bitmap-binding.cpp

+47-8
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,11 @@ RB_METHOD_GUARD(bitmapBlt) {
141141
&opacity RB_ARG_END);
142142

143143
src = getPrivateDataCheck<Bitmap>(srcObj, BitmapType);
144-
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
144+
if (src) {
145+
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
145146

146-
GFX_GUARD_EXC(b->blt(x, y, *src, srcRect->toIntRect(), opacity););
147+
GFX_GUARD_EXC(b->blt(x, y, *src, srcRect->toIntRect(), opacity););
148+
}
147149

148150
return self;
149151
}
@@ -164,11 +166,13 @@ RB_METHOD_GUARD(bitmapStretchBlt) {
164166
&opacity RB_ARG_END);
165167

166168
src = getPrivateDataCheck<Bitmap>(srcObj, BitmapType);
167-
destRect = getPrivateDataCheck<Rect>(destRectObj, RectType);
168-
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
169-
170-
GFX_GUARD_EXC(b->stretchBlt(destRect->toIntRect(), *src, srcRect->toIntRect(),
171-
opacity););
169+
if (src) {
170+
destRect = getPrivateDataCheck<Rect>(destRectObj, RectType);
171+
srcRect = getPrivateDataCheck<Rect>(srcRectObj, RectType);
172+
173+
GFX_GUARD_EXC(b->stretchBlt(destRect->toIntRect(), *src, srcRect->toIntRect(),
174+
opacity););
175+
}
172176

173177
return self;
174178
}
@@ -332,7 +336,40 @@ RB_METHOD_GUARD(bitmapTextSize) {
332336
}
333337
RB_METHOD_GUARD_END
334338

335-
DEF_GFX_PROP_OBJ_VAL(Bitmap, Font, Font, "font")
339+
RB_METHOD(BitmapGetFont) {
340+
RB_UNUSED_PARAM;
341+
checkDisposed<Bitmap>(self);
342+
return rb_iv_get(self, "font");
343+
}
344+
RB_METHOD_GUARD(BitmapSetFont) {
345+
rb_check_argc(argc, 1);
346+
Bitmap *b = getPrivateData<Bitmap>(self);
347+
VALUE propObj = *argv;
348+
349+
Font *prop = getPrivateDataCheck<Font>(propObj, FontType);
350+
if (prop) {
351+
GFX_GUARD_EXC(b->setFont(*prop);)
352+
353+
VALUE f = rb_iv_get(self, "font");
354+
if (f) {
355+
rb_iv_set(f, "name", rb_iv_get(propObj, "name"));
356+
rb_iv_set(f, "size", rb_iv_get(propObj, "size"));
357+
rb_iv_set(f, "bold", rb_iv_get(propObj, "bold"));
358+
rb_iv_set(f, "italic", rb_iv_get(propObj, "italic"));
359+
360+
if (rgssVer >= 2) {
361+
rb_iv_set(f, "shadow", rb_iv_get(propObj, "shadow"));
362+
}
363+
364+
if (rgssVer >= 3) {
365+
rb_iv_set(f, "outline", rb_iv_get(propObj, "outline"));
366+
}
367+
}
368+
}
369+
370+
return propObj;
371+
}
372+
RB_METHOD_GUARD_END
336373

337374
RB_METHOD_GUARD(bitmapGradientFillRect) {
338375
Bitmap *b = getPrivateData<Bitmap>(self);
@@ -602,6 +639,8 @@ RB_METHOD_GUARD(bitmapAddFrame){
602639
rb_scan_args(argc, argv, "11", &srcBitmap, &position);
603640

604641
Bitmap *src = getPrivateDataCheck<Bitmap>(srcBitmap, BitmapType);
642+
if (!src)
643+
raiseDisposedAccess(srcBitmap);
605644

606645
Bitmap *b = getPrivateData<Bitmap>(self);
607646

binding/disposable-binding.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ template<class C>
3030
RB_METHOD(disposableDispose)
3131
{
3232
RB_UNUSED_PARAM;
33-
34-
C *d = getPrivateData<C>(self);
33+
34+
C *d = getPrivateDataNoRaise<C>(self);
3535

3636
if (!d)
3737
return Qnil;
@@ -46,7 +46,7 @@ RB_METHOD(disposableIsDisposed)
4646
{
4747
RB_UNUSED_PARAM;
4848

49-
C *d = getPrivateData<C>(self);
49+
C *d = getPrivateDataNoRaise<C>(self);
5050

5151
if (!d)
5252
return Qtrue;

binding/etc-binding.cpp

+11-19
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ EQUAL_FUN(Rect)
107107
rb_get_args(argc, argv, param_t_s, &p1, &p2, &p3, &p4 RB_ARG_END); \
108108
k = new Klass(p1, p2, p3, p4); \
109109
} \
110+
Klass *orig = getPrivateDataNoRaise<Klass>(self); \
111+
if (orig) { \
112+
*orig = *k; \
113+
delete k; \
114+
} else { \
110115
setPrivateData(self, k); \
116+
} \
111117
return self; \
112118
}
113119

@@ -208,27 +214,14 @@ INITCOPY_FUN(Tone)
208214
INITCOPY_FUN(Color)
209215
INITCOPY_FUN(Rect)
210216

211-
#if RAPI_FULL > 187
212-
#define INIT_BIND(Klass) \
213-
{ \
214-
klass = rb_define_class(#Klass, rb_cObject); \
215-
rb_define_alloc_func(klass, classAllocate<&Klass##Type>); \
216-
rb_define_class_method(klass, "_load", Klass##Load); \
217-
serializableBindingInit<Klass>(klass); \
218-
_rb_define_method(klass, "initialize", Klass##Initialize); \
219-
_rb_define_method(klass, "initialize_copy", Klass##InitializeCopy); \
220-
_rb_define_method(klass, "set", Klass##Set); \
221-
_rb_define_method(klass, "==", Klass##Equal); \
222-
_rb_define_method(klass, "===", Klass##Equal); \
223-
_rb_define_method(klass, "eql?", Klass##Equal); \
224-
_rb_define_method(klass, "to_s", Klass##Stringify); \
225-
_rb_define_method(klass, "inspect", Klass##Stringify); \
226-
}
227-
#else
217+
CLASS_ALLOCATE_PRE_INIT(Tone, ToneInitialize);
218+
CLASS_ALLOCATE_PRE_INIT(Color, ColorInitialize);
219+
CLASS_ALLOCATE_PRE_INIT(Rect, RectInitialize);
220+
228221
#define INIT_BIND(Klass) \
229222
{ \
230223
klass = rb_define_class(#Klass, rb_cObject); \
231-
rb_define_alloc_func(klass, Klass##Allocate); \
224+
rb_define_alloc_func(klass, Klass##AllocatePreInit); \
232225
rb_define_class_method(klass, "_load", Klass##Load); \
233226
serializableBindingInit<Klass>(klass); \
234227
_rb_define_method(klass, "initialize", Klass##Initialize); \
@@ -240,7 +233,6 @@ INITCOPY_FUN(Rect)
240233
_rb_define_method(klass, "to_s", Klass##Stringify); \
241234
_rb_define_method(klass, "inspect", Klass##Stringify); \
242235
}
243-
#endif
244236

245237
#define MRB_ATTR_R(Class, attr) \
246238
mrb_define_method(mrb, klass, #attr, Class##Get_##attr, MRB_ARGS_NONE())

binding/font-binding.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,15 @@ RB_METHOD(fontInitialize) {
8888
* However the same bug/behavior exists in all RM versions. */
8989
rb_iv_set(self, "name", namesObj);
9090

91-
setPrivateData(self, f);
91+
Font *orig = getPrivateDataNoRaise<Font>(self);
92+
if (orig)
93+
{
94+
*orig = *f;
95+
delete f;
96+
f = orig;
97+
} else {
98+
setPrivateData(self, f);
99+
}
92100

93101
/* Wrap property objects */
94102
f->initDynAttribs();

binding/table-binding.cpp

+19-7
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@ RB_METHOD(tableInitialize) {
5757

5858
parseArgsTableSizes(argc, argv, &x, &y, &z);
5959

60-
Table *t = new Table(x, y, z);
60+
Table *t = getPrivateDataNoRaise<Table>(self);
61+
if (t) {
62+
t->resize(x, y, z);
63+
} else {
64+
t = new Table(x, y, z);
6165

62-
setPrivateData(self, t);
66+
setPrivateData(self, t);
67+
}
6368

6469
return self;
6570
}
@@ -152,13 +157,20 @@ RB_METHOD_GUARD_END
152157
MARSH_LOAD_FUN(Table)
153158
INITCOPY_FUN(Table)
154159

160+
161+
RB_METHOD(tableInitializeDefault) {
162+
Table *t = new Table(0, 0, 0);
163+
164+
setPrivateData(self, t);
165+
166+
return self;
167+
}
168+
169+
CLASS_ALLOCATE_PRE_INIT(Table, tableInitializeDefault);
170+
155171
void tableBindingInit() {
156172
VALUE klass = rb_define_class("Table", rb_cObject);
157-
#if RAPI_FULL > 187
158-
rb_define_alloc_func(klass, classAllocate<&TableType>);
159-
#else
160-
rb_define_alloc_func(klass, TableAllocate);
161-
#endif
173+
rb_define_alloc_func(klass, TableAllocatePreInit);
162174

163175
serializableBindingInit<Table>(klass);
164176

binding/tilemap-binding.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ DEF_TYPE_CUSTOMFREE(TilemapAutotiles, RUBY_TYPED_NEVER_FREE);
3535
#endif
3636

3737
RB_METHOD(tilemapAutotilesSet) {
38-
Tilemap::Autotiles *a = getPrivateData<Tilemap::Autotiles>(self);
38+
Tilemap::Autotiles *a = getPrivateDataNoRaise<Tilemap::Autotiles>(self);
39+
40+
if (!a)
41+
return self;
3942

4043
int i;
4144
VALUE bitmapObj;
@@ -93,12 +96,18 @@ RB_METHOD(tilemapInitialize) {
9396

9497
t->initDynAttribs();
9598

99+
/* Dispose the old autotiles if we're reinitializing.
100+
* See the comment in setPrivateData for more info. */
101+
VALUE autotilesObj = rb_iv_get(self, "autotiles");
102+
if (autotilesObj != Qnil)
103+
setPrivateData(autotilesObj, 0);
104+
96105
wrapProperty(self, &t->getAutotiles(), "autotiles", TilemapAutotilesType);
97106

98107
wrapProperty(self, &t->getColor(), "color", ColorType);
99108
wrapProperty(self, &t->getTone(), "tone", ToneType);
100109

101-
VALUE autotilesObj = rb_iv_get(self, "autotiles");
110+
autotilesObj = rb_iv_get(self, "autotiles");
102111

103112
VALUE ary = rb_ary_new2(7);
104113
for (int i = 0; i < 7; ++i)

binding/tilemapvx-binding.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,16 @@ RB_METHOD(tilemapVXInitialize) {
5858

5959
rb_iv_set(self, "viewport", viewportObj);
6060

61+
/* Dispose the old bitmap array if we're reinitializing.
62+
* See the comment in setPrivateData for more info. */
63+
VALUE autotilesObj = rb_iv_get(self, "bitmap_array");
64+
if (autotilesObj != Qnil)
65+
setPrivateData(autotilesObj, 0);
66+
6167
wrapProperty(self, &t->getBitmapArray(), "bitmap_array", BitmapArrayType,
6268
rb_const_get(rb_cObject, rb_intern("Tilemap")));
6369

64-
VALUE autotilesObj = rb_iv_get(self, "bitmap_array");
70+
autotilesObj = rb_iv_get(self, "bitmap_array");
6571

6672
VALUE ary = rb_ary_new2(9);
6773
for (int i = 0; i < 9; ++i)
@@ -106,7 +112,10 @@ DEF_GFX_PROP_I(TilemapVX, OX)
106112
DEF_GFX_PROP_I(TilemapVX, OY)
107113

108114
RB_METHOD(tilemapVXBitmapsSet) {
109-
TilemapVX::BitmapArray *a = getPrivateData<TilemapVX::BitmapArray>(self);
115+
TilemapVX::BitmapArray *a = getPrivateDataNoRaise<TilemapVX::BitmapArray>(self);
116+
117+
if (!a)
118+
return self;
110119

111120
int i;
112121
VALUE bitmapObj;

0 commit comments

Comments
 (0)