#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#define _DEBUG_DUMP
#include "sqplus.h"
#include <sqstdio.h>
#include <sqstdmath.h>
#include <sqstdstring.h>
#include <sqstdaux.h>
#include <sqstdblob.h>
#include <sqstdsystem.h>
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 { // <TODO> 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