You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtqt-perl/PerlTQt/smokeperl.cpp

427 lines
12 KiB

#include "smokeperl.h"
class SmokePerlTQt : public SmokePerl {
public:
SmokePerlTQt();
virtual ~SmokePerlTQt();
void registerSmoke(const char *name, Smoke *smoke);
Smoke *getSmoke(const char *name);
void registerHandlers(TypeHandler *h);
SmokeObject newObject(void *p, const SmokeClass &c);
SmokeObject wrapObject(void *p, const SmokeClass &c);
SmokeObject getObject(void *p);
SmokeObject getObject(SV *sv);
private:
HV *_registered_smoke;
HV *_registered_handlers;
HV *_remembered_pointers;
void rememberPointer(SmokeObject &o, const SmokeClass &c, bool remember, void *lastptr = 0);
void rememberPointer(SmokeObject &o);
void forgetPointer(SmokeObject &o);
SmokeObject createObject(void *p, const SmokeClass &c);
const char *getSmokeName(Smoke *smoke) {
static const char none[] = "";
HE *he;
hv_iterinit(_registered_smoke);
while(he = hv_iternext(_registered_smoke)) {
SV *sv = hv_iterval(_registered_smoke, he);
if((Smoke*)SvIV(sv) == smoke) {
I32 toss;
return hv_iterkey(he, &toss);
}
}
return none;
}
HV *package(const SmokeClass &c) {
// for now, we cheat on the class names by assuming they're all TQt::
if(!strcmp(c.className(), "TQt"))
return gv_stashpv(c.className(), TRUE);
SV *name = newSVpv("TQt::", 0);
sv_catpv(name, c.className() + 1);
HV *stash = gv_stashpv(SvPV_nolen(name), TRUE);
SvREFCNT_dec(name);
return stash;
}
};
Marshall::HandlerFn getMarshallFn(const SmokeType &type);
class VirtualMethodReturnValue : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
Smoke::Stack _stack;
SmokeType _st;
SV *_retval;
public:
const Smoke::Method &method() { return _smoke->methods[_method]; }
SmokeType type() { return _st; }
Marshall::Action action() { return Marshall::FromSV; }
Smoke::StackItem &item() { return _stack[0]; }
SV *var() { return _retval; }
void unsupported() {
croak("Cannot handle '%s' as return-type of virtual method %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void next() {}
bool cleanup() { return false; }
VirtualMethodReturnValue(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, SV *retval) :
_smoke(smoke), _method(meth), _stack(stack), _retval(retval) {
_st.set(_smoke, method().ret);
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
}
};
extern SV *sv_this;
extern void *_current_object;
extern Smoke::Index _current_object_class;
extern int object_count;
extern bool temporary_virtual_function_success;
extern struct mgvtbl vtbl_smoke;
class VirtualMethodCall : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
Smoke::Stack _stack;
GV *_gv;
int _cur;
Smoke::Index *_args;
SV **_sp;
bool _called;
SV *_savethis;
public:
SmokeType type() { return SmokeType(_smoke, _args[_cur]); }
Marshall::Action action() { return Marshall::ToSV; }
Smoke::StackItem &item() { return _stack[_cur + 1]; }
SV *var() { return _sp[_cur]; }
const Smoke::Method &method() { return _smoke->methods[_method]; }
void unsupported() {
croak("Cannot handle '%s' as argument of virtual method %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void callMethod() {
dSP;
if(_called) return;
_called = true;
SP = _sp + method().numArgs - 1;
PUTBACK;
int count = call_sv((SV*)_gv, G_SCALAR);
SPAGAIN;
VirtualMethodReturnValue r(_smoke, _method, _stack, POPs);
PUTBACK;
FREETMPS;
LEAVE;
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < method().numArgs) {
Marshall::HandlerFn fn = getMarshallFn(type());
_sp[_cur] = sv_newmortal();
(*fn)(this);
_cur++;
}
callMethod();
_cur = oldcur;
}
bool cleanup() { return false; } // is this right?
VirtualMethodCall(Smoke *smoke, Smoke::Index meth, Smoke::Stack stack, SV *obj, GV *gv) :
_smoke(smoke), _method(meth), _stack(stack), _gv(gv), _cur(-1), _sp(0), _called(false) {
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, method().numArgs);
_savethis = sv_this;
sv_this = newSVsv(obj);
_sp = SP + 1;
for(int i = 0; i < method().numArgs; i++)
_sp[i] = sv_newmortal();
_args = _smoke->argumentList + method().args;
}
~VirtualMethodCall() {
SvREFCNT_dec(sv_this);
sv_this = _savethis;
}
};
class MethodReturnValue : public Marshall {
Smoke *_smoke;
Smoke::Index _method;
SV *_retval;
Smoke::Stack _stack;
public:
MethodReturnValue(Smoke *smoke, Smoke::Index method, Smoke::Stack stack, SV *retval) :
_smoke(smoke), _method(method), _retval(retval), _stack(stack) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
}
const Smoke::Method &method() { return _smoke->methods[_method]; }
SmokeType type() { return SmokeType(_smoke, method().ret); }
Marshall::Action action() { return Marshall::ToSV; }
Smoke::StackItem &item() { return _stack[0]; }
SV *var() { return _retval; }
void unsupported() {
croak("Cannot handle '%s' as return-type of %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
void next() {}
bool cleanup() { return false; }
};
class MethodCall : public Marshall {
int _cur;
Smoke *_smoke;
Smoke::Stack _stack;
Smoke::Index _method;
Smoke::Index *_args;
SV **_sp;
int _items;
SV *_retval;
bool _called;
public:
MethodCall(Smoke *smoke, Smoke::Index method, SV **sp, int items) :
_smoke(smoke), _method(method), _sp(sp), _items(items), _cur(-1), _called(false) {
_args = _smoke->argumentList + _smoke->methods[_method].args;
_items = _smoke->methods[_method].numArgs;
_stack = new Smoke::StackItem[items + 1];
_retval = newSV(0);
}
~MethodCall() {
delete[] _stack;
SvREFCNT_dec(_retval);
}
SmokeType type() { return SmokeType(_smoke, _args[_cur]); }
Marshall::Action action() { return Marshall::FromSV; }
Smoke::StackItem &item() { return _stack[_cur + 1]; }
SV *var() {
if(_cur < 0) return _retval;
SvGETMAGIC(*(_sp + _cur));
return *(_sp + _cur);
}
inline const Smoke::Method &method() { return _smoke->methods[_method]; }
void unsupported() {
croak("Cannot handle '%s' as argument to %s::%s",
type().name(),
_smoke->className(method().classId),
_smoke->methodNames[method().name]);
}
Smoke *smoke() { return _smoke; }
inline void callMethod() {
if(_called) return;
_called = true;
Smoke::ClassFn fn = _smoke->classes[method().classId].classFn;
void *ptr = _smoke->cast(
_current_object,
_current_object_class,
method().classId
);
_items = -1;
(*fn)(method().method, ptr, _stack);
MethodReturnValue r(_smoke, _method, _stack, _retval);
}
void next() {
int oldcur = _cur;
_cur++;
while(!_called && _cur < _items) {
Marshall::HandlerFn fn = getMarshallFn(type());
(*fn)(this);
_cur++;
}
callMethod();
_cur = oldcur;
}
bool cleanup() { return true; }
};
class SmokeBindingTQt : public SmokeBinding {
SmokePerlTQt *_smokeperl;
public:
SmokeBindingTQt(Smoke *s, SmokePerlTQt *smokeperl) :
SmokeBinding(s), _smokeperl(smokeperl) {}
void deleted(Smoke::Index classId, void *ptr) {
if(do_debug) printf("%p->~%s()\n", ptr, smoke->className(classId));
object_count--;
if(do_debug) printf("Remaining objects: %d\n", object_count);
SV *obj = getPointerObject(ptr);
smokeperl_object *o = sv_obj_info(obj);
if(!o || !o->ptr) {
return;
}
unmapPointer(o, o->classId, 0);
o->ptr = 0;
}
bool callMethod(Smoke::Index method, void *ptr, Smoke::Stack args, bool isAbstract) {
SV *obj = getPointerObject(ptr);
smokeperl_object *o = sv_obj_info(obj);
if(do_debug) printf("virtual %p->%s::%s() called\n", ptr,
smoke->classes[smoke->methods[method].classId].className,
smoke->methodNames[smoke->methods[method].name]
);
if(!o) {
if(!PL_dirty) // if not in global destruction
warn("Cannot find object for virtual method");
return false;
}
HV *stash = SvSTASH(SvRV(obj));
if(*HvNAME(stash) == ' ')
stash = gv_stashpv(HvNAME(stash) + 1, TRUE);
const char *methodName = smoke->methodNames[smoke->methods[method].name];
GV *gv = gv_fetchmethod_autoload(stash, methodName, 0);
if(!gv) return false;
VirtualMethodCall c(smoke, method, args, obj, gv);
// exception variable, just temporary
temporary_virtual_function_success = true;
c.next();
bool ret = temporary_virtual_function_success;
temporary_virtual_function_success = true;
return ret;
}
char *className(Smoke::Index classId) {
const char *className = smoke->className(classId);
char *buf = new char[strlen(className) + 6];
strcpy(buf, " TQt::");
strcat(buf, className + 1);
return buf;
}
};
SmokePerlTQt::SmokePerlTQt() {
_registered_smoke = newHV();
_registered_handlers = newHV();
_remembered_pointers = newHV();
}
SmokePerlTQt::~SmokePerlTQt() {
SvREFCNT_dec((SV*)_registered_smoke);
SvREFCNT_dec((SV*)_registered_handlers);
SvREFCNT_dec((SV*)_remembered_pointers);
}
void SmokePerlTQt::registerSmoke(const char *name, Smoke *smoke) {
hv_store(_registered_smoke, name, strlen(name), newSViv((IV)smoke), 0);
// This will also need to handle the per-class initialization
smoke->binding = new SmokeBindingTQt(smoke, this);
}
Smoke *SmokePerlTQt::getSmoke(const char *name) {
SV **svp = hv_fetch(_registered_smoke, name, strlen(name), 0);
if(svp && SvOK(*svp))
return (Smoke*)SvIV(*svp);
return 0;
}
void SmokePerlTQt::registerHandlers(TypeHandler *h) {
while(h->name) {
hv_store(_registered_handlers, h->name, strlen(h->name), newSViv((IV)h->fn), 0);
h++;
}
}
SmokeObject SmokePerlTQt::createObject(void *p, const SmokeClass &c) {
HV *hv = newHV();
SV *obj = newRV_noinc((SV*)hv);
Smoke_MAGIC m(p, c);
sv_magic((SV*)hv, (SV*)newAV(), '~', (char*)&m, sizeof(m));
MAGIC *mg = mg_find((SV*)hv, '~');
mg->mg_virtual = &vtbl_smoke;
sv_bless(obj, package(c));
SmokeObject o(obj, (Smoke_MAGIC*)mg->mg_ptr);
SvREFCNT_dec(obj);
if(c.hasVirtual())
rememberPointer(o);
return o;
}
SmokeObject SmokePerlTQt::newObject(void *p, const SmokeClass &c) {
SmokeObject o = createObject(p, c);
if(c.isVirtual())
rememberPointer(o);
o.setAllocated(true);
return o;
}
SmokeObject SmokePerlTQt::wrapObject(void *p, const SmokeClass &c) {
SmokeObject o = createObject(p, c);
return o;
}
void SmokePerlTQt::rememberPointer(SmokeObject &o, const SmokeClass &c, bool remember, void *lastptr) {
void *ptr = o.cast(c);
if(ptr != lastptr) {
SV *keysv = newSViv((IV)o.ptr());
STRLEN klen;
char *key = SvPV(keysv, klen);
if(remember)
hv_store(_remembered_pointers, key, klen,
sv_rvweaken(newSVsv(o.var())), 0);
else
hv_delete(_remembered_pointers, key, klen, G_DISCARD);
SvREFCNT_dec(keysv);
}
for(Smoke::Index *i = c.smoke()->inheritanceList + c.c().parents;
*i;
i++)
rememberPointer(o, SmokeClass(c.smoke(), *i), remember, ptr);
}
void SmokePerlTQt::rememberPointer(SmokeObject &o) {
rememberPointer(o, o.c(), true);
}
void SmokePerlTQt::forgetPointer(SmokeObject &o) {
rememberPointer(o, o.c(), false);
}
SmokeObject SmokePerlTQt::getObject(SV *sv) {
MAGIC *mg = mg_find(SvRV(sv), '~');
Smoke_MAGIC *m = (Smoke_MAGIC*)mg->mg_ptr;
return SmokeObject(sv, m);
}
SmokeObject SmokePerlTQt::getObject(void *p) {
SV *keysv = newSViv((IV)p);
STRLEN klen;
char *key = SvPV(keysv, klen);
SV **svp = hv_fetch(_remembered_pointers, key, klen, 0);
if(svp && SvROK(*svp))
return getObject(sv_2mortal(newRV(SvRV(*svp)))); // paranoid copy of a weak ref
return SmokeObject(&PL_sv_undef, 0);
}