struct plrb_attr
{
VALUE recv;
ID getter;
ID setter;
};
static int
plrb_mg_attr_set(pTHX_ SV* sv, MAGIC* mg)
{
struct plrb_attr* attr = (struct plrb_attr*)mg->mg_ptr;
VALUE val;
if(!attr->setter){ /* first call */
const char* getter = rb_id2name(attr->getter);
STRLEN len = strlen(getter)+1;
char smallbuf[128];
char* buf;
volatile VALUE strbuf;
if(len < sizeof(smallbuf)){
buf = smallbuf;
}
else{
strbuf = rb_str_buf_new((long)len);
buf = RSTRING_PTR(strbuf);
}
/* "attr" + "=" + "\0" */
memcpy(buf, getter, len-1);
buf[len-1] = '=';
buf[len] = '\0';
attr->setter = rb_intern(buf);
}
val = SV2VALUE(sv);
do_funcall_protect(aTHX_ attr->recv, attr->setter, 1, &val, FALSE);
return 0;
}
MGVTBL plrb_attr_vtbl = {
NULL, /* mg_get */
plrb_mg_attr_set,
NULL, /* mg_len */
NULL, /* mg_clear */
NULL, /* mg_free */
NULL, /* mg_copy */
NULL, /* mg_dup */
NULL /* mg_local */
};
XS(XS_Ruby_method_dispatcher);
XS(XS_Ruby_method_dispatcher)
{
dXSARGS;
VALUE self;
VALUE result;
ID method = (ID)XSANY.any_iv;
if(items == 0) croak("Not enough arguments for %s", rb_id2name(method));
self = ruby_self(ST(0));
result = plrb_funcall_protect(self, method, (items - 1), &ST(1));
if(GIMME_V != G_VOID){
VALUE ret_tab;
ID retval_id = method;
VALUE v;
SV* sv;
struct plrb_attr attr = { self, method, (ID)0 };
/* obj[ret_tab][result] = result */
if(OBJ_FROZEN(self) || items != 1){
sv = VALUE2SV(result);
}
else{
ret_tab = rb_ivar_get_defaultf(self, id_ret_tab, obj_new);
if(NIL_P(v = rb_attr_get(ret_tab, retval_id))){
sv = newSVvalue(result);
rb_ivar_set(ret_tab, retval_id, v = any_new_noinc(sv));
sv_magicext(sv, NULL, PERL_MAGIC_ext, &plrb_attr_vtbl, (char*)&attr, sizeof(attr));
}
else{
sv = valueSV(v);
sv_set_value2sv(sv, result);
}
}
ST(0) = sv;
XSRETURN(1);
}
else{
XSRETURN_EMPTY;
}
}