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.
427 lines
12 KiB
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);
|
|
}
|
|
|