You cannot 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
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			427 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
| #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);
 | |
| }
 | |
| 
 |