mkxp/binding-mri/binding-util.h
Jonas Kulla 4ff563725b Make 'rb_get_args()' va_arg passing safer by introducing a termination marker
What can I say. I made a pact with the devil, and paid dearly.
Almost a whole day's worth of debugging, actually. Not again.

If this turns out to be slow we can always optimize the critical
parts (with no variable param count) later, or completely remove it.
2013-09-27 04:49:48 +02:00

301 lines
7 KiB
C++

/*
** binding-util.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BINDING_UTIL_H
#define BINDING_UTIL_H
#include "./ruby/ruby.h"
#define PRIV_IV "priv"
enum RbException
{
RGSS = 0,
PHYSFS,
SDL,
MKXP,
ErrnoENOENT,
IOError,
TypeError,
ArgumentError,
RbExceptionsMax
};
struct RbData
{
VALUE exc[RbExceptionsMax];
RbData();
~RbData();
};
RbData *getRbData();
struct Exception;
void
raiseRbExc(const Exception &exc);
#define DECL_TYPE(Klass) \
extern rb_data_type_struct Klass##Type
#define DEF_TYPE(Klass) \
rb_data_type_struct Klass##Type
void initType(rb_data_type_struct &type,
const char *name,
void (*freeInst)(void*));
template<class C>
static void freeInstance(void *inst)
{
delete static_cast<C*>(inst);
}
#define INIT_TYPE(Klass) initType(Klass##Type, #Klass, freeInstance<Klass>)
template<class C>
static inline C *
getPrivateData(VALUE self)
{
VALUE priv = rb_iv_get(self, PRIV_IV);
return static_cast<C*>(RTYPEDDATA_DATA(priv));
}
template<class C>
static inline C *
getPrivateDataCheck(VALUE self, const rb_data_type_struct &type)
{
VALUE priv = rb_iv_get(self, PRIV_IV);
void *obj = rb_check_typeddata(priv, &type);
return static_cast<C*>(obj);
}
static inline void
setPrivateData(VALUE self, void *p, const rb_data_type_struct &type)
{
VALUE priv = rb_data_typed_object_alloc(rb_cData, p, &type);
rb_iv_set(self, PRIV_IV, priv);
}
inline VALUE
wrapObject(void *p, const rb_data_type_struct &type)
{
VALUE klass = rb_const_get(rb_cObject, rb_intern(type.wrap_struct_name));
VALUE obj = rb_obj_alloc(klass);
setPrivateData(obj, p, type);
return obj;
}
inline VALUE
wrapProperty(VALUE self, void *prop, const char *iv,
const rb_data_type_struct &type)
{
VALUE propObj = wrapObject(prop, type);
rb_iv_set(self, iv, propObj);
return propObj;
}
inline void
wrapNilProperty(VALUE self, const char *iv)
{
rb_iv_set(self, iv, Qnil);
}
/* Implemented: oSszfib| */
int
rb_get_args(int argc, VALUE *argv, const char *format, ...);
/* Always terminate 'rb_get_args' with this */
#define RB_ARG_END ((void*) -1)
typedef VALUE (*RubyMethod)(int argc, VALUE *argv, VALUE self);
static inline void
_rb_define_method(VALUE klass, const char *name, RubyMethod func)
{
rb_define_method(klass, name, RUBY_METHOD_FUNC(func), -1);
}
static inline void
rb_define_class_method(VALUE klass, const char *name, RubyMethod func)
{
rb_define_singleton_method(klass, name, RUBY_METHOD_FUNC(func), -1);
}
static inline void
_rb_define_module_function(VALUE module, const char *name, RubyMethod func)
{
rb_define_module_function(module, name, RUBY_METHOD_FUNC(func), -1);
}
template<class C>
static inline VALUE
objectLoad(int argc, VALUE *argv, VALUE self, rb_data_type_struct &type)
{
const char *data;
int dataLen;
rb_get_args(argc, argv, "s", &data, &dataLen, RB_ARG_END);
VALUE obj = rb_obj_alloc(self);
C *c = C::deserialize(data, dataLen);
setPrivateData(obj, c, type);
return obj;
}
static inline VALUE
rb_bool_new(bool value)
{
return value ? Qtrue : Qfalse;
}
#define RB_METHOD(name) \
static VALUE name(int argc, VALUE *argv, VALUE self)
#define RB_UNUSED_PARAM \
{ (void) argc; (void) argv; (void) self; }
#define MARSH_LOAD_FUN(Typ) \
RB_METHOD(Typ##Load) \
{ \
return objectLoad<Typ>(argc, argv, self, Typ##Type); \
}
#define CLONE_FUNC(Klass) \
static mrb_value \
Klass##Clone(mrb_state *mrb, mrb_value self) \
{ \
Klass *k = getPrivateData<Klass>(mrb, self); \
mrb_value dupObj = mrb_obj_clone(mrb, self); \
Klass *dupK = new Klass(*k); \
setPrivateData(mrb, dupObj, dupK, Klass##Type); \
return dupObj; \
}
#define CLONE_FUN(Klass) \
RB_METHOD(Klass##Clone) \
{ \
RB_UNUSED_PARAM \
Klass *k = getPrivateData<Klass>(self); \
VALUE dupObj = rb_obj_clone(self); \
Klass *dupK = new Klass(*k); \
setPrivateData(dupObj, dupK, Klass##Type); \
return dupObj; \
}
/* If we're not binding a disposable class,
* we want to #undef DEF_PROP_CHK_DISP */
#define DEF_PROP_CHK_DISP \
checkDisposed(k, DISP_CLASS_NAME);
#define DEF_PROP_OBJ(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); (void) k; \
DEF_PROP_CHK_DISP \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj; \
PropKlass *prop; \
rb_get_args(argc, argv, "o", &propObj, RB_ARG_END); \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC( k->set##PropName(prop); ) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
/* Object property with allowed NIL */
#define DEF_PROP_OBJ_NIL(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); (void) k; \
DEF_PROP_CHK_DISP \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj; \
PropKlass *prop; \
rb_get_args(argc, argv, "o", &propObj, RB_ARG_END); \
if (rb_type(propObj) == RUBY_T_NIL) \
prop = 0; \
else \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC( k->set##PropName(prop); ) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
#define DEF_PROP(Klass, type, PropName, param_t_s, value_fun) \
RB_METHOD(Klass##Get##PropName) \
{ \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); \
DEF_PROP_CHK_DISP \
return value_fun(k->get##PropName()); \
} \
RB_METHOD(Klass##Set##PropName) \
{ \
Klass *k = getPrivateData<Klass>(self); \
type value; \
rb_get_args(argc, argv, param_t_s, &value, RB_ARG_END); \
GUARD_EXC( k->set##PropName(value); ) \
return value_fun(value); \
}
#define DEF_PROP_I(Klass, PropName) \
DEF_PROP(Klass, int, PropName, "i", rb_fix_new)
#define DEF_PROP_F(Klass, PropName) \
DEF_PROP(Klass, double, PropName, "f", rb_float_new)
#define DEF_PROP_B(Klass, PropName) \
DEF_PROP(Klass, bool, PropName, "b", rb_bool_new)
#define INIT_PROP_BIND(Klass, PropName, prop_name_s) \
{ \
_rb_define_method(klass, prop_name_s, Klass##Get##PropName); \
_rb_define_method(klass, prop_name_s "=", Klass##Set##PropName); \
}
#define GUARD_EXC(exp) \
{ try { exp } catch (const Exception &exc) { raiseRbExc(exc); } }
#endif // BINDING_UTIL_H