#include #include #include #define _DEBUG_DUMP #include "sqplus.h" #include #include #include #include #include #include HSQUIRRELVM SquirrelVM::_VM; bool SquirrelVM::_no_vm_ref; int SquirrelVM::_CallState = -1; SquirrelObject* SquirrelVM::_root; HSQUIRRELVM SquirrelVM::_sandboxVM; SquirrelObject SquirrelVM::_vm; // Helper struct to keep track of all SQSharedState:s created by SquirrelVM. #include "../squirrel/sqpcheader.h" #include "../squirrel/sqvm.h" struct SQSharedStateNode { SQSharedStateNode( SQSharedState* ps ); ~SQSharedStateNode( ); SQSharedState* m_ps; SQSharedStateNode* m_nxt; }; // Linked list of shared states static SQSharedStateNode* g_sqss_fst; SQSharedStateNode::SQSharedStateNode( SQSharedState* ps ) : m_ps(ps), m_nxt(g_sqss_fst) { g_sqss_fst = this; } SQSharedStateNode::~SQSharedStateNode() { if(m_ps) sq_delete(m_ps,SQSharedState); delete m_nxt; } static struct SquirrelVM_ModConstr { ~SquirrelVM_ModConstr(){ // Delete any shared states we created delete g_sqss_fst; g_sqss_fst = NULL; } } g_squirrelvm_mod_constr; SquirrelError::SquirrelError() { const SQChar *s; sq_getlasterror(SquirrelVM::_VM); sq_getstring(SquirrelVM::_VM,-1,&s); if(s) { desc = s; } else { desc = _SC("unknown error"); } } SquirrelVMSys::~SquirrelVMSys() { // Must take care to release object with the 'ref' VM PushRefVM( _vm.GetObjectHandle()._unVal.pThread ); _vm.Reset(); PopRefVM(); } void SquirrelVMSys::Set( HSQUIRRELVM v ){ // Must take care to release object with the 'ref' VM PushRefVM( v ); _vm = v; PopRefVM( ); } void SquirrelVMSys::Set( const SquirrelObject& ov ){ assert( ov.GetObjectHandle()._type==OT_THREAD ); // Must take care to release object with the 'ref' VM PushRefVM( ov.GetObjectHandle()._unVal.pThread ); _vm = ov; PopRefVM( ); } SquirrelVMSys::operator HSQUIRRELVM () const { // Avoid const madness SquirrelObject *pvm = (SquirrelObject*)&_vm; assert( pvm->GetObjectHandle()._type==OT_THREAD ); return pvm->GetObjectHandle()._unVal.pThread; } // When doing a SquirrelObject assignment, a reference using the current // VM is done. HSQUIRRELVM g_VM_pushed; void SquirrelVMSys::PushRefVM( HSQUIRRELVM v ){ assert( !g_VM_pushed ); g_VM_pushed = SquirrelVM::_VM; SquirrelVM::_VM = v; } void SquirrelVMSys::PopRefVM( ){ SquirrelVM::_VM = g_VM_pushed; g_VM_pushed = NULL; } bool SquirrelVM::Init( HSQUIRRELVM v ){ if( v && v==_VM ) return true; // Do we have a previous state? Release( ); bool created_new = false; if( !v ){ // Create a new VM - a root VM with new SharedState. v = sq_open(1024); if( !v ) return false; // Store the associated shared state in a linked list. The state will only // be destroyed at app shutdown. Often that is fine, but if using many // VM:s briefly, this allocation is not optimal. new SQSharedStateNode( _ss(v) ); created_new = true; sq_setprintfunc(v,SquirrelVM::PrintFunc, SquirrelVM::PrintFunc); sq_pushroottable(v); sqstd_register_iolib(v); sqstd_register_bloblib(v); sqstd_register_mathlib(v); sqstd_register_stringlib(v); #ifdef SQPLUS_SQUIRRELVM_WITH_SYSTEMLIB sqstd_register_systemlib(v); #endif sqstd_seterrorhandlers(v); //TODO error handler, compiler error handler sq_pop(v,1); } // After this we hold a ref _no_vm_ref = false; _VM = v; _vm = v; // In the case where Squirrel is ref counted we currently // hold two references to the VM (since it is created with // a ref count of 1). In the GC case, it is outside of the // chain of valid objects, so it is not referenced. Compensate // in ref counted case. if( created_new ) DropVMRefIfRefCounted( v ); return true; } bool SquirrelVM::InitNoRef( HSQUIRRELVM v ){ if( v && v==_VM ) return true; // Do we have a previous state? Release( ); // Set pointer to this VM, without referencing it _no_vm_ref = true; _VM = v; return true; } /* void SquirrelVM::Init( HSQUIRRELVM v ) { if( v && v==_VM ) { return; } // Do we have a previous state? Release(); if( !v ){ // Create a new VM and own it _VM = sq_open(1024); sq_setprintfunc(_VM,SquirrelVM::PrintFunc); sq_pushroottable(_VM); sqstd_register_iolib(_VM); sqstd_register_bloblib(_VM); sqstd_register_mathlib(_VM); sqstd_register_stringlib(_VM); sqstd_seterrorhandlers(_VM); //TODO error handler, compiler error handler sq_pop(_VM,1); } else { _VM = v; } // After this we hold a ref _vm = _VM; } */ void SquirrelVM::Release() { // Release root table object if we have one if( _root ){ delete _root; _root = NULL; } // Release our ref on VM - if we should if( !_no_vm_ref ) _vm.Reset(); _VM = NULL; } void SquirrelVM::DropVMRefIfRefCounted( HSQUIRRELVM v ){ #ifdef NO_GARBAGE_COLLECTOR if( v ){ SQObject t; t._unVal.pThread = v; t._type = OT_THREAD; sq_release( v, &t ); } #endif } BOOL SquirrelVM::Update() { //update remote debugger return TRUE; } void SquirrelVM::PrintFunc(HSQUIRRELVM v,const SQChar* s,...) { static SQChar temp[2048]; va_list vl; va_start(vl, s); scvsprintf( temp,s, vl); SCPUTS(temp); va_end(vl); } SquirrelObject SquirrelVM::CompileScript(const SQChar *s) { SquirrelObject ret; if(SQ_SUCCEEDED(sqstd_loadfile(_VM,s,1))) { ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } throw SquirrelError(); } SquirrelObject SquirrelVM::CompileBuffer(const SQChar *s,const SQChar * debugInfo) { SquirrelObject ret; if(SQ_SUCCEEDED(sq_compilebuffer(_VM,s,(int)scstrlen(s)*sizeof(SQChar),debugInfo,1))) { ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } throw SquirrelError(); } SquirrelObject SquirrelVM::RunScript(const SquirrelObject &o,SquirrelObject *_this) { SquirrelObject ret; sq_pushobject(_VM,o._o); if(_this) { sq_pushobject(_VM,_this->_o); } else { sq_pushroottable(_VM); } if(SQ_SUCCEEDED(sq_call(_VM,1,SQTrue,SQ_CALL_RAISE_ERROR))) { ret.AttachToStackObject(-1); sq_pop(_VM,2); return ret; } sq_pop(_VM,1); throw SquirrelError(); } BOOL SquirrelVM::BeginCall(const SquirrelObject &func) { if(_CallState != -1) return FALSE; _CallState = 1; sq_pushobject(_VM,func._o); sq_pushroottable(_VM); return TRUE; } BOOL SquirrelVM::BeginCall(const SquirrelObject &func,SquirrelObject &_this) { if(_CallState != -1) throw SquirrelError(_SC("call already initialized")); _CallState = 1; sq_pushobject(_VM,func._o); sq_pushobject(_VM,_this._o); return TRUE; } #define _CHECK_CALL_STATE \ if(_CallState == -1) \ throw SquirrelError(_SC("call not initialized")); void SquirrelVM::PushParam(const SquirrelObject &o) { _CHECK_CALL_STATE sq_pushobject(_VM,o._o); _CallState++; } void SquirrelVM::PushParam(const SQChar *s) { _CHECK_CALL_STATE sq_pushstring(_VM,s,-1); _CallState++; } void SquirrelVM::PushParam(SQInteger n) { _CHECK_CALL_STATE sq_pushinteger(_VM,n); _CallState++; } void SquirrelVM::PushParam(SQFloat f) { _CHECK_CALL_STATE sq_pushfloat(_VM,f); _CallState++; } void SquirrelVM::PushParamNull() { _CHECK_CALL_STATE sq_pushnull(_VM); _CallState++; } void SquirrelVM::PushParam(SQUserPointer up) { _CHECK_CALL_STATE sq_pushuserpointer(_VM,up); _CallState++; } SquirrelObject SquirrelVM::EndCall() { SquirrelObject ret; if(_CallState >= 0) { int oldtop = sq_gettop(_VM); int nparams = _CallState; _CallState = -1; if(SQ_SUCCEEDED(sq_call(_VM,nparams,SQTrue,SQ_CALL_RAISE_ERROR))) { ret.AttachToStackObject(-1); sq_pop(_VM,2); }else { sq_settop(_VM,oldtop-(nparams+1)); throw SquirrelError(); } } return ret; } SquirrelObject SquirrelVM::CreateInstance(SquirrelObject &oclass) { SquirrelObject ret; int oldtop = sq_gettop(_VM); sq_pushobject(_VM,oclass._o); if(SQ_FAILED(sq_createinstance(_VM,-1))) { sq_settop(_VM,oldtop); throw SquirrelError(); } ret.AttachToStackObject(-1); sq_pop(_VM,2); return ret; } SquirrelObject SquirrelVM::CreateTable() { SquirrelObject ret; sq_newtable(_VM); ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } SquirrelObject SquirrelVM::CreateString(const SQChar *s) { SquirrelObject ret; sq_pushstring(_VM,s,-1); ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } SquirrelObject SquirrelVM::CreateArray(int size) { SquirrelObject ret; sq_newarray(_VM,size); ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } SquirrelObject SquirrelVM::CreateFunction(SQFUNCTION func) { SquirrelObject ret; sq_newclosure(_VM,func,0); ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } SquirrelObject SquirrelVM::CreateUserData(int size) { SquirrelObject ret; sq_newuserdata(_VM,size); ret.AttachToStackObject(-1); sq_pop(_VM,1); return ret; } const SquirrelObject &SquirrelVM::GetRootTable() { if( !_root ){ sq_pushroottable(_VM); _root = new SquirrelObject(); _root->AttachToStackObject(-1); sq_pop(_VM,1); } return *_root; } void SquirrelVM::PushRootTable(void) { sq_pushroottable(_VM); } // SquirrelVM::PushRootTable // Creates a function in the table or class currently on the stack. //void CreateFunction(HSQUIRRELVM v,const SQChar * scriptFuncName,SQFUNCTION func,int numParams=0,const SQChar * typeMask=0) { SquirrelObject SquirrelVM::CreateFunction(SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) { sq_pushstring(_VM,scriptFuncName,-1); sq_newclosure(_VM,func,0); SquirrelObject ret; ret.AttachToStackObject(-1); SQChar tm[64]; SQChar * ptm = tm; int numParams = SQ_MATCHTYPEMASKSTRING; if (typeMask) { if (typeMask[0] == '*') { ptm = 0; // Variable args: don't check parameters. numParams = 0; // Clear SQ_MATCHTYPEMASKSTRING (does not mean match 0 params. See sq_setparamscheck()). } else { if (SCSNPRINTF(tm,sizeof(tm),_SC("t|y|x%s"),typeMask) < 0) { // sq_throwerror(_VM,_SC("CreateFunction: typeMask string too long.")); throw SquirrelError(_SC("CreateFunction: typeMask string too long.")); } // if } // if } else { // Need to check object type on stack: table, class, instance, etc. SCSNPRINTF(tm,sizeof(tm),_SC("%s"),_SC("t|y|x")); // table, class, instance. // tm[0] = 't'; // tm[1] = 0; } // if #if 0 sq_setparamscheck(_VM,numParams+1,ptm); // Parameters are table+args (thus, the +1). #else if (ptm) { sq_setparamscheck(_VM,numParams,ptm); // Determine arg count from type string. } // if #endif #ifdef _DEBUG sq_setnativeclosurename(_VM,-1,scriptFuncName); // For debugging only. #endif sq_createslot(_VM,-3); // Create slot in table or class (assigning function to slot at scriptNameFunc). return ret; } // SquirrelVM::CreateFunction SquirrelObject SquirrelVM::CreateFunction(SquirrelObject & so,SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) { PushObject(so); SquirrelObject ret = CreateFunction(func,scriptFuncName,typeMask); Pop(1); return ret; } // SquirrelVM::CreateFunction // Create a Global function on the root table. //void CreateFunctionGlobal(HSQUIRRELVM v,const SQChar * scriptFuncName,SQFUNCTION func,int numParams=0,const SQChar * typeMask=0) { SquirrelObject SquirrelVM::CreateFunctionGlobal(SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) { PushRootTable(); // Push root table. // CreateFunction(scriptFuncName,func,numParams,typeMask); SquirrelObject ret = CreateFunction(func,scriptFuncName,typeMask); Pop(1); // Pop root table. return ret; } // SquirrelVM::CreateFunctionGlobal