-/*\r
- see copyright notice in squirrel.h\r
-*/\r
-#include "sqpcheader.h"\r
-#include <math.h>\r
-#include <stdlib.h>\r
-#include "sqopcodes.h"\r
-#include "sqfuncproto.h"\r
-#include "sqvm.h"\r
-#include "sqclosure.h"\r
-#include "sqstring.h"\r
-#include "sqtable.h"\r
-#include "squserdata.h"\r
-#include "sqarray.h"\r
-#include "sqclass.h"\r
-\r
-#define TOP() (_stack[_top-1])\r
-\r
-bool SQVM::BW_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)\r
-{\r
- SQInteger res;\r
- SQInteger i1 = _integer(o1), i2 = _integer(o2);\r
- if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER))\r
- {\r
- switch(op) {\r
- case BW_AND: res = i1 & i2; break;\r
- case BW_OR: res = i1 | i2; break;\r
- case BW_XOR: res = i1 ^ i2; break;\r
- case BW_SHIFTL: res = i1 << i2; break;\r
- case BW_SHIFTR: res = i1 >> i2; break;\r
- case BW_USHIFTR:res = (SQInteger)(*((unsigned int*)&i1) >> i2); break;\r
- default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; }\r
- }\r
- } \r
- else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;}\r
- trg = res;\r
- return true;\r
-}\r
-\r
-bool SQVM::ARITH_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)\r
-{\r
- if(sq_isnumeric(o1) && sq_isnumeric(o2)) {\r
- if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER)) {\r
- switch(op) {\r
- case '+': trg = _integer(o1) + _integer(o2); break;\r
- case '-': trg = _integer(o1) - _integer(o2); break;\r
- case '/': if(_integer(o2) == 0) { Raise_Error(_SC("division by zero")); return false; }\r
- trg = _integer(o1) / _integer(o2); \r
- break;\r
- case '*': trg = _integer(o1) * _integer(o2); break;\r
- case '%': trg = _integer(o1) % _integer(o2); break;\r
- }\r
- }else{\r
- switch(op) {\r
- case '+': trg = tofloat(o1) + tofloat(o2); break;\r
- case '-': trg = tofloat(o1) - tofloat(o2); break;\r
- case '/': trg = tofloat(o1) / tofloat(o2); break;\r
- case '*': trg = tofloat(o1) * tofloat(o2); break;\r
- case '%': trg = SQFloat(fmod((double)tofloat(o1),(double)tofloat(o2))); break;\r
- }\r
- } \r
- } else {\r
- if(op == '+' && (type(o1) == OT_STRING || type(o2) == OT_STRING)){\r
- if(!StringCat(o1, o2, trg)) return false;\r
- }\r
- else if(!ArithMetaMethod(op,o1,o2,trg)) { \r
- Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false; \r
- }\r
- }\r
- return true;\r
-}\r
-\r
-SQObjectPtr &stack_get(HSQUIRRELVM v,int idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}\r
-\r
-SQVM::SQVM(SQSharedState *ss)\r
-{\r
- _sharedstate=ss;\r
- _suspended=false;\r
- _suspended_target=-1;\r
- _suspended_root=false;\r
- _suspended_traps=-1;\r
- _foreignptr=NULL;\r
- _nnativecalls=0;\r
- _uiRef=0;\r
- _lasterror = _null_;\r
- _errorhandler = _null_;\r
- _debughook = _null_;\r
- INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);\r
-}\r
-\r
-void SQVM::Finalize()\r
-{\r
- _roottable = _null_;\r
- _lasterror = _null_;\r
- _errorhandler = _null_;\r
- _debughook = _null_;\r
- temp_reg = _null_;\r
- int size=_stack.size();\r
- for(int i=0;i<size;i++)\r
- _stack[i]=_null_;\r
-}\r
-\r
-SQVM::~SQVM()\r
-{\r
- Finalize();\r
- REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);\r
-}\r
-\r
-bool SQVM::ArithMetaMethod(int op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest)\r
-{\r
- SQMetaMethod mm;\r
- switch(op){\r
- case _SC('+'): mm=MT_ADD; break;\r
- case _SC('-'): mm=MT_SUB; break;\r
- case _SC('/'): mm=MT_DIV; break;\r
- case _SC('*'): mm=MT_MUL; break;\r
- case _SC('%'): mm=MT_MODULO; break;\r
- }\r
- if(is_delegable(o1) && _delegable(o1)->_delegate) {\r
- Push(o1);Push(o2);\r
- return CallMetaMethod(_delegable(o1),mm,2,dest);\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o)\r
-{\r
- \r
- switch(type(o)) {\r
- case OT_INTEGER:\r
- trg = -_integer(o);\r
- return true;\r
- case OT_FLOAT:\r
- trg = -_float(o);\r
- return true;\r
- case OT_TABLE:\r
- case OT_USERDATA:\r
- case OT_INSTANCE:\r
- if(_delegable(o)->_delegate) {\r
- Push(o);\r
- if(CallMetaMethod(_delegable(o), MT_UNM, 1, temp_reg)) {\r
- trg = temp_reg;\r
- return true;\r
- }\r
- }\r
- return true;\r
-\r
- }\r
- Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o));\r
- return false;\r
-}\r
-\r
-#define _RET_SUCCEED(exp) { result = (exp); return true; } \r
-bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,int &result)\r
-{\r
- if(type(o1)==type(o2)){\r
- if(_userpointer(o1)==_userpointer(o2))_RET_SUCCEED(0);\r
- SQObjectPtr res;\r
- switch(type(o1)){\r
- case OT_STRING:\r
- _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2)));\r
- case OT_INTEGER:\r
- _RET_SUCCEED(_integer(o1)-_integer(o2));\r
- case OT_FLOAT:\r
- _RET_SUCCEED((_float(o1)<_float(o2))?-1:1);\r
- case OT_TABLE:\r
- case OT_USERDATA:\r
- case OT_INSTANCE:\r
- Push(o1);Push(o2);\r
- if(_delegable(o1)->_delegate)CallMetaMethod(_delegable(o1),MT_CMP,2,res);\r
- break;\r
- }\r
- if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; }\r
- _RET_SUCCEED(_integer(res));\r
- }\r
- else{\r
- if(sq_isnumeric(o1) && sq_isnumeric(o2)){\r
- if((type(o1)==OT_INTEGER) && (type(o2)==OT_FLOAT)) { \r
- if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); }\r
- else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); }\r
- _RET_SUCCEED(1);\r
- }\r
- else{\r
- if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); }\r
- else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); }\r
- _RET_SUCCEED(1);\r
- }\r
- }\r
- else if(type(o1)==OT_NULL) {_RET_SUCCEED(-1);}\r
- else if(type(o2)==OT_NULL) {_RET_SUCCEED(1);}\r
- else { Raise_CompareError(o1,o2); return false; }\r
- \r
- }\r
- assert(0);\r
- _RET_SUCCEED(0); //cannot happen\r
-}\r
-\r
-bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res)\r
-{\r
- int r;\r
- if(ObjCmp(o1,o2,r)) {\r
- switch(op) {\r
- case CMP_G: res = (r > 0)?_true_:_false_; return true;\r
- case CMP_GE: res = (r >= 0)?_true_:_false_; return true;\r
- case CMP_L: res = (r < 0)?_true_:_false_; return true;\r
- case CMP_LE: res = (r <= 0)?_true_:_false_; return true;\r
- \r
- }\r
- assert(0);\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest)\r
-{\r
- switch(type(obj))\r
- {\r
- case OT_STRING:\r
- switch(type(str)){\r
- case OT_STRING: {\r
- int l=_string(str)->_len,ol=_string(obj)->_len;\r
- SQChar *s=_sp(rsl(l+ol+1));\r
- memcpy(s,_stringval(str),rsl(l));memcpy(s+l,_stringval(obj),rsl(ol));s[l+ol]=_SC('\0');\r
- break;\r
- }\r
- case OT_FLOAT:\r
- scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%g%s"),_float(str),_stringval(obj));\r
- break;\r
- case OT_INTEGER:\r
- scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%d%s"),_integer(str),_stringval(obj));\r
- break;\r
- default:\r
- Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));\r
- return false;\r
- }\r
- dest=SQString::Create(_ss(this),_spval);\r
- break;\r
- case OT_FLOAT:\r
- scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%g"),_stringval(str),_float(obj));\r
- dest=SQString::Create(_ss(this),_spval);\r
- break;\r
- case OT_INTEGER:\r
- scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%d"),_stringval(str),_integer(obj));\r
- dest=SQString::Create(_ss(this),_spval);\r
- break;\r
- default:\r
- Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-const SQChar *IdType2Name(SQObjectType type)\r
-{\r
- switch(_RAW_TYPE(type))\r
- {\r
- case _RT_NULL:return _SC("null");\r
- case _RT_INTEGER:return _SC("integer");\r
- case _RT_FLOAT:return _SC("float");\r
- case _RT_BOOL:return _SC("bool");\r
- case _RT_STRING:return _SC("string");\r
- case _RT_TABLE:return _SC("table");\r
- case _RT_ARRAY:return _SC("array");\r
- case _RT_GENERATOR:return _SC("generator");\r
- case _RT_CLOSURE:\r
- case _RT_NATIVECLOSURE:\r
- return _SC("function");\r
- case _RT_USERDATA:\r
- case _RT_USERPOINTER:\r
- return _SC("userdata");\r
- case _RT_THREAD: return _SC("thread");\r
- case _RT_FUNCPROTO: return _SC("function");\r
- case _RT_CLASS: return _SC("class");\r
- case _RT_INSTANCE: return _SC("instance");\r
- default:\r
- return NULL;\r
- }\r
-}\r
-\r
-const SQChar *GetTypeName(const SQObjectPtr &obj1)\r
-{\r
- return IdType2Name(type(obj1)); \r
-}\r
-\r
-void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)\r
-{\r
- if(is_delegable(obj1) && _delegable(obj1)->_delegate) {\r
- Push(obj1);\r
- if(CallMetaMethod(_delegable(obj1),MT_TYPEOF,1,dest))\r
- return;\r
- }\r
- dest = SQString::Create(_ss(this),GetTypeName(obj1));\r
-}\r
-\r
-bool SQVM::Init(SQVM *friendvm, int stacksize)\r
-{\r
- _stack.resize(stacksize);\r
- _callsstack.reserve(4);\r
- _stackbase = 0;\r
- _top = 0;\r
- if(!friendvm) \r
- _roottable = SQTable::Create(_ss(this), 0);\r
- else {\r
- _roottable = friendvm->_roottable;\r
- _errorhandler = friendvm->_errorhandler;\r
- _debughook = friendvm->_debughook;\r
- }\r
- \r
- sq_base_register(this);\r
- return true;\r
-}\r
-\r
-extern SQInstructionDesc g_InstrDesc[];\r
-\r
-bool SQVM::StartCall(SQClosure *closure,int target,int nargs,int stackbase,bool tailcall)\r
-{\r
- SQFunctionProto *func = _funcproto(closure->_function);\r
- //const int outerssize = func->_outervalues.size();\r
-\r
- const int paramssize = func->_parameters.size();\r
- const int oldtop = _top;\r
- const int newtop = stackbase + func->_stacksize;\r
- \r
- \r
- if(func->_varparams)\r
- {\r
- if (nargs < paramssize) {\r
- Raise_Error(_SC("wrong number of parameters"));\r
- return false;\r
- }\r
- for(int n = 0; n < nargs - paramssize; n++) {\r
- _vargsstack.push_back(_stack[stackbase+paramssize+n]);\r
- _stack[stackbase+paramssize+n] = _null_;\r
- }\r
- }\r
- else {\r
- if (paramssize != nargs) {\r
- Raise_Error(_SC("wrong number of parameters"));\r
- return false;\r
- }\r
- \r
- }\r
- \r
- if (!tailcall) {\r
- PUSH_CALLINFO(this, CallInfo());\r
- ci->_etraps = 0;\r
- ci->_prevstkbase = stackbase - _stackbase;\r
- ci->_target = target;\r
- ci->_prevtop = _top - _stackbase;\r
- ci->_ncalls = 1;\r
- ci->_root = false;\r
- }\r
- else {\r
- ci->_ncalls++;\r
- }\r
- ci->_vargs.size = (nargs - paramssize);\r
- ci->_vargs.base = _vargsstack.size()-(nargs - paramssize);\r
- ci->_closure._unVal.pClosure = closure;\r
- ci->_closure._type = OT_CLOSURE;\r
- ci->_iv = &func->_instructions;\r
- ci->_literals = &func->_literals;\r
- //grows the stack if needed\r
- if (((unsigned int)newtop + (func->_stacksize<<1)) > _stack.size()) {\r
- _stack.resize(_stack.size() + (func->_stacksize<<1));\r
- }\r
- \r
- _top = newtop;\r
- _stackbase = stackbase;\r
- ci->_ip = ci->_iv->_vals;\r
- return true;\r
-}\r
-\r
-bool SQVM::Return(int _arg0, int _arg1, SQObjectPtr &retval)\r
-{\r
- if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
- for(int i=0;i<ci->_ncalls;i++)\r
- CallDebugHook(_SC('r'));\r
- \r
- bool broot = ci->_root;\r
- int last_top = _top;\r
- int target = ci->_target;\r
- int oldstackbase = _stackbase;\r
- _stackbase -= ci->_prevstkbase;\r
- _top = _stackbase + ci->_prevtop;\r
- PopVarArgs(ci->_vargs);\r
- POP_CALLINFO(this);\r
- if (broot) {\r
- if (_arg0 != MAX_FUNC_STACKSIZE) retval = _stack[oldstackbase+_arg1];\r
- else retval = _null_;\r
- }\r
- else {\r
- if (_arg0 != MAX_FUNC_STACKSIZE)\r
- STK(target) = _stack[oldstackbase+_arg1];\r
- else\r
- STK(target) = _null_;\r
- }\r
-\r
- while (last_top >= _top) _stack[last_top--].Null();\r
- assert(oldstackbase >= _stackbase); \r
- return broot;\r
-}\r
-\r
-#define _RET_ON_FAIL(exp) { if(!exp) return false; }\r
-\r
-bool SQVM::LOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)\r
-{\r
- _RET_ON_FAIL(ARITH_OP( op , target, a, incr));\r
- a = target;\r
- return true;\r
-}\r
-\r
-bool SQVM::PLOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)\r
-{\r
- SQObjectPtr trg;\r
- _RET_ON_FAIL(ARITH_OP( op , trg, a, incr));\r
- target = a;\r
- a = trg;\r
- return true;\r
-}\r
-\r
-bool SQVM::DerefInc(int op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix)\r
-{\r
- SQObjectPtr tmp, tself = self, tkey = key;\r
- if (!Get(tself, tkey, tmp, false, true)) { Raise_IdxError(tkey); return false; }\r
- _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr))\r
- Set(tself, tkey, target,true);\r
- if (postfix) target = tmp;\r
- return true;\r
-}\r
-\r
-#define arg0 (_i_._arg0)\r
-#define arg1 (_i_._arg1)\r
-#define sarg1 (*((int *)&_i_._arg1))\r
-#define arg2 (_i_._arg2)\r
-#define arg3 (_i_._arg3)\r
-#define sarg3 (*((char *)&_i_._arg3))\r
-\r
-SQRESULT SQVM::Suspend()\r
-{\r
- if (_suspended)\r
- return sq_throwerror(this, _SC("cannot suspend an already suspended vm"));\r
- if (_nnativecalls!=2)\r
- return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods"));\r
- return SQ_SUSPEND_FLAG;\r
-}\r
-\r
-void SQVM::PopVarArgs(VarArgs &vargs)\r
-{\r
- for(int n = 0; n< vargs.size; n++)\r
- _vargsstack.pop_back();\r
-}\r
-\r
-#define _FINISH(stoploop) {finished = stoploop; return true; }\r
-bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr \r
-&o3,SQObjectPtr &o4,int arg_2,bool &finished)\r
-{\r
- int nrefidx;\r
- switch(type(o1)) {\r
- case OT_TABLE:\r
- if((nrefidx = _table(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);\r
- o4 = (SQInteger)nrefidx; _FINISH(false);\r
- case OT_ARRAY:\r
- if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);\r
- o4 = (SQInteger) nrefidx; _FINISH(false);\r
- case OT_STRING:\r
- if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);\r
- o4 = (SQInteger)nrefidx; _FINISH(false);\r
- case OT_CLASS:\r
- if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);\r
- o4 = (SQInteger)nrefidx; _FINISH(false);\r
- case OT_USERDATA:\r
- case OT_INSTANCE:\r
- if(_delegable(o1)->_delegate) {\r
- SQObjectPtr itr;\r
- Push(o1);\r
- Push(o4);\r
- if(CallMetaMethod(_delegable(o1), MT_NEXTI, 2, itr)){\r
- o4 = o2 = itr;\r
- if(type(itr) == OT_NULL) _FINISH(true);\r
- if(!Get(o1, itr, o3, false,false)) {\r
- Raise_Error(_SC("_nexti returned an invalid idx"));\r
- return false;\r
- }\r
- _FINISH(false);\r
- }\r
- Raise_Error(_SC("_nexti failed"));\r
- return false;\r
- }\r
- break;\r
- case OT_GENERATOR:\r
- if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(true);\r
- if(_generator(o1)->_state == SQGenerator::eSuspended) {\r
- SQInteger idx = 0;\r
- if(type(o4) == OT_INTEGER) {\r
- idx = _integer(o4) + 1;\r
- }\r
- o2 = idx;\r
- o4 = idx;\r
- _generator(o1)->Resume(this, arg_2+1);\r
- _FINISH(false);\r
- }\r
- }\r
- Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1));\r
- return false; //cannot be hit(just to avoid warnings)\r
-}\r
-\r
-bool SQVM::DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2)\r
-{\r
- if(type(o1) != OT_TABLE) { Raise_Error(_SC("delegating a '%s'"), GetTypeName(o1)); return false; }\r
- switch(type(o2)) {\r
- case OT_TABLE:\r
- if(!_table(o1)->SetDelegate(_table(o2))){\r
- Raise_Error(_SC("delegate cycle detected"));\r
- return false;\r
- }\r
- break;\r
- case OT_NULL:\r
- _table(o1)->SetDelegate(NULL);\r
- break;\r
- default:\r
- Raise_Error(_SC("using '%s' as delegate"), GetTypeName(o2));\r
- return false;\r
- break;\r
- }\r
- trg = o1;\r
- return true;\r
-}\r
-#define COND_LITERAL (arg3!=0?(*ci->_literals)[arg1]:STK(arg1))\r
-\r
-#define _GUARD(exp) { if(!exp) { Raise_Error(_lasterror); SQ_THROW();} }\r
-\r
-#define SQ_THROW() { goto exception_trap; }\r
-\r
-bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)\r
-{\r
- int nouters;\r
- SQClosure *closure = SQClosure::Create(_ss(this), func);\r
- if(nouters = func->_outervalues.size()) {\r
- closure->_outervalues.reserve(nouters);\r
- for(int i = 0; i<nouters; i++) {\r
- SQOuterVar &v = func->_outervalues[i];\r
- switch(v._type){\r
- case otSYMBOL:\r
- closure->_outervalues.push_back(_null_);\r
- if(!Get(_stack._vals[_stackbase]/*STK(0)*/, v._src, closure->_outervalues.top(), false,true))\r
- {Raise_IdxError(v._src); return false; }\r
- break;\r
- case otLOCAL:\r
- closure->_outervalues.push_back(_stack._vals[_stackbase+_integer(v._src)]);\r
- break;\r
- case otOUTER:\r
- closure->_outervalues.push_back(_closure(ci->_closure)->_outervalues[_integer(v._src)]);\r
- break;\r
- }\r
- }\r
- }\r
- target = closure;\r
- return true;\r
-\r
-}\r
-\r
-bool SQVM::GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &index,CallInfo *ci)\r
-{\r
- if(ci->_vargs.size == 0) {\r
- Raise_Error(_SC("the function doesn't have var args"));\r
- return false;\r
- }\r
- if(!sq_isnumeric(index)){\r
- Raise_Error(_SC("indexing 'vargv' with %s"),GetTypeName(index));\r
- return false;\r
- }\r
- int idx = tointeger(index);\r
- if(idx < 0 || idx >= ci->_vargs.size){ Raise_Error(_SC("vargv index out of range")); return false; }\r
- target = _vargsstack[ci->_vargs.base+idx];\r
- return true;\r
-}\r
-\r
-bool SQVM::CLASS_OP(SQObjectPtr &target,int baseclass,int attributes)\r
-{\r
- SQClass *base = NULL;\r
- SQObjectPtr attrs;\r
- if(baseclass != MAX_LITERALS) {\r
- if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; }\r
- base = _class(_stack._vals[_stackbase + baseclass]);\r
- }\r
- if(attributes != MAX_FUNC_STACKSIZE) {\r
- attrs = _stack._vals[_stackbase+attributes];\r
- }\r
- target = SQClass::Create(_ss(this),base);\r
- _class(target)->_attributes = attrs;\r
- return true;\r
-}\r
-\r
-bool SQVM::IsFalse(SQObjectPtr &o)\r
-{\r
- SQObjectType t = type(o);\r
- if((t & SQOBJECT_CANBEFALSE)\r
- && ((t == OT_NULL) || ((t == OT_INTEGER || t == OT_BOOL) && _integer(o) == 0)\r
- || (t == OT_FLOAT && _float(o) == SQFloat(0.0)))) {\r
- return true;\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res)\r
-{\r
- if(type(o1) == type(o2)) {\r
- res = ((_userpointer(o1) == _userpointer(o2)?true:false));\r
- }\r
- else {\r
- if(sq_isnumeric(o1) && sq_isnumeric(o2)) {\r
- int cmpres;\r
- if(!ObjCmp(o1, o2,cmpres)) return false;\r
- res = (cmpres == 0);\r
- }\r
- else {\r
- res = false;\r
- }\r
- }\r
- return true;\r
-}\r
-\r
-bool SQVM::Execute(SQObjectPtr &closure, int target, int nargs, int stackbase,SQObjectPtr &outres, ExecutionType et)\r
-{\r
- if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }\r
- _nnativecalls++;\r
- AutoDec ad(&_nnativecalls);\r
- int traps = 0;\r
- //temp_reg vars for OP_CALL\r
- int ct_target;\r
- bool ct_tailcall; \r
-\r
- switch(et) {\r
- case ET_CALL: \r
- if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) { \r
- //call the handler if there are no calls in the stack, if not relies on the previous node\r
- if(ci == NULL) CallErrorHandler(_lasterror);\r
- return false;\r
- }\r
- ci->_root = true;\r
- break;\r
- case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = true; traps += ci->_etraps; break;\r
- case ET_RESUME_VM:\r
- traps = _suspended_traps;\r
- ci->_root = _suspended_root;\r
- _suspended = false;\r
- break;\r
- }\r
- \r
-exception_restore:\r
- //SQ_TRY \r
- {\r
- for(;;)\r
- {\r
- const SQInstruction &_i_ = *ci->_ip++;\r
- //dumpstack(_stackbase);\r
- //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3);\r
- switch(_i_.op)\r
- {\r
- case _OP_LINE:\r
- if(type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
- CallDebugHook(_SC('l'),arg1);\r
- continue;\r
- case _OP_LOAD: TARGET = (*ci->_literals)[arg1]; continue;\r
- case _OP_TAILCALL:\r
- temp_reg = STK(arg1);\r
- if (type(temp_reg) == OT_CLOSURE){ \r
- ct_tailcall = true;\r
- PopVarArgs(ci->_vargs);\r
- for (int i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);\r
- ct_target = ci->_target;\r
- goto common_call;\r
- }\r
- case _OP_CALL: {\r
- ct_tailcall = false;\r
- ct_target = arg0;\r
- temp_reg = STK(arg1);\r
-common_call:\r
- int last_top = _top;\r
- switch (type(temp_reg)) {\r
- case OT_CLOSURE:{\r
- StartCall(_closure(temp_reg), ct_target, arg3, ct_tailcall?_stackbase:_stackbase+arg2, ct_tailcall);\r
- if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {\r
- SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));\r
- _GUARD(gen->Yield(this));\r
- Return(1, ct_target, temp_reg);\r
- STK(ct_target) = gen;\r
- while (last_top >= _top) _stack[last_top--].Null();\r
- continue;\r
- }\r
- if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
- CallDebugHook(_SC('c'));\r
- }\r
- break;\r
- case OT_NATIVECLOSURE: {\r
- bool suspend;\r
- _GUARD(CallNative(_nativeclosure(temp_reg), arg3, _stackbase+arg2, ct_tailcall, temp_reg,suspend));\r
- if(suspend){\r
- _suspended = true;\r
- _suspended_target = ct_target;\r
- _suspended_root = ci->_root;\r
- _suspended_traps = traps;\r
- outres = temp_reg;\r
- return true;\r
- }\r
- STK(ct_target) = temp_reg;\r
- }\r
- break;\r
- case OT_CLASS:{\r
- _GUARD(CreateClassInstance(_class(temp_reg),arg3,_stackbase+arg2,STK(ct_target)));\r
- }\r
- break;\r
- case OT_TABLE:\r
- case OT_USERDATA:\r
- case OT_INSTANCE:\r
- {\r
- Push(temp_reg);\r
- for (int i = 0; i < arg3; i++) Push(STK(arg2 + i));\r
- if (_delegable(temp_reg) && CallMetaMethod(_delegable(temp_reg), MT_CALL, arg3+1, temp_reg)){\r
- STK(ct_target) = temp_reg;\r
- break;\r
- }\r
- Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));\r
- SQ_THROW();\r
- }\r
- default:\r
- Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));\r
- SQ_THROW();\r
- }\r
- }\r
- continue;\r
- case _OP_PREPCALL:\r
- if (!Get(STK(arg2), STK(arg1), temp_reg, false,true))\r
- { Raise_IdxError(STK(arg1)); SQ_THROW(); }\r
- goto common_prepcall;\r
- case _OP_PREPCALLK:\r
- if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg,false,true)) {\r
- if(type(STK(arg2)) == OT_CLASS) { //hack?\r
- if(_class_ddel->Get((*ci->_literals)[arg1],temp_reg)) {\r
- STK(arg3) = STK(arg2);\r
- TARGET = temp_reg;\r
- continue;\r
- }\r
- }\r
- { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}\r
- }\r
-common_prepcall:\r
- if(type(STK(arg2)) == OT_CLASS) {\r
- STK(arg3) = STK(0); // this\r
- }\r
- else {\r
- STK(arg3) = STK(arg2);\r
- }\r
- TARGET = temp_reg;\r
- continue;\r
- case _OP_GETK:\r
- if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg, false,true)) { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}\r
- TARGET = temp_reg;\r
- continue;\r
- case _OP_MOVE: TARGET = STK(arg1); continue;\r
- case _OP_NEWSLOT:\r
- _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));\r
- if(arg0 != arg3) TARGET = STK(arg3);\r
- continue;\r
- case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue;\r
- case _OP_SET:\r
- if (!Set(STK(arg1), STK(arg2), STK(arg3),true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }\r
- if (arg0 != arg3) TARGET = STK(arg3);\r
- continue;\r
- case _OP_GET:\r
- if (!Get(STK(arg1), STK(arg2), temp_reg, false,true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }\r
- TARGET = temp_reg;\r
- continue;\r
- case _OP_EQ:{\r
- bool res;\r
- if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }\r
- TARGET = res?_true_:_false_;\r
- }continue;\r
- case _OP_NE:{ \r
- bool res;\r
- if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }\r
- TARGET = (!res)?_true_:_false_;\r
- } continue;\r
- case _OP_ARITH: _GUARD(ARITH_OP( arg3 , temp_reg, STK(arg2), STK(arg1))); TARGET = temp_reg; continue;\r
- case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue;\r
- case _OP_RETURN:\r
- if(type((ci)->_generator) == OT_GENERATOR) {\r
- _generator((ci)->_generator)->Kill();\r
- }\r
- if(Return(arg0, arg1, temp_reg)){\r
- assert(traps==0);\r
- outres = temp_reg;\r
- return true;\r
- }\r
- continue;\r
- case _OP_LOADNULLS:{ for(unsigned int n=0;n<arg1;n++) STK(arg0+n) = _null_; }continue;\r
- case _OP_LOADROOTTABLE: TARGET = _roottable; continue;\r
- case _OP_LOADBOOL: TARGET = arg1?_true_:_false_; continue;\r
- case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue;\r
- case _OP_JMP: ci->_ip += (sarg1); continue;\r
- case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;\r
- case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;\r
- case _OP_LOADFREEVAR: TARGET = _closure(ci->_closure)->_outervalues[arg1]; continue;\r
- case _OP_VARGC: TARGET = SQInteger(ci->_vargs.size); continue;\r
- case _OP_GETVARGV: \r
- if(!GETVARGV_OP(TARGET,STK(arg1),ci)) { SQ_THROW(); } \r
- continue;\r
- case _OP_NEWTABLE: TARGET = SQTable::Create(_ss(this), arg1); continue;\r
- case _OP_NEWARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue;\r
- case _OP_APPENDARRAY: _array(STK(arg0))->Append(COND_LITERAL); continue;\r
- case _OP_GETPARENT:\r
- switch(type(STK(arg1))) {\r
- case OT_TABLE: \r
- TARGET = _table(STK(arg1))->_delegate?SQObjectPtr(_table(STK(arg1))->_delegate):_null_;\r
- continue;\r
- case OT_CLASS: TARGET = _class(STK(arg1))->_base?_class(STK(arg1))->_base:_null_;\r
- continue;\r
- }\r
- Raise_Error(_SC("the %s type doesn't have a parent slot"), GetTypeName(STK(arg1)));\r
- SQ_THROW();\r
- continue;\r
- case _OP_COMPARITH: _GUARD(DerefInc(arg3, TARGET, STK((((unsigned int)arg1&0xFFFF0000)>>16)), STK(arg2), STK(arg1&0x0000FFFF), false)); continue;\r
- case _OP_COMPARITHL: _GUARD(LOCAL_INC(arg3, TARGET, STK(arg1), STK(arg2))); continue;\r
- case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false));} continue;\r
- case _OP_INCL: {SQObjectPtr o(sarg3); _GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));} continue;\r
- case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true));} continue;\r
- case _OP_PINCL: {SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o));} continue;\r
- case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue;\r
- case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, true,false)?_true_:_false_;continue;\r
- case _OP_INSTANCEOF: \r
- if(type(STK(arg1)) != OT_CLASS || type(STK(arg2)) != OT_INSTANCE)\r
- {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();}\r
- TARGET = _instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?_true_:_false_;\r
- continue;\r
- case _OP_AND: \r
- if(IsFalse(STK(arg2))) {\r
- TARGET = STK(arg2);\r
- ci->_ip += (sarg1);\r
- }\r
- continue;\r
- case _OP_OR:\r
- if(!IsFalse(STK(arg2))) {\r
- TARGET = STK(arg2);\r
- ci->_ip += (sarg1);\r
- }\r
- continue;\r
- case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue;\r
- case _OP_NOT: TARGET = (IsFalse(STK(arg1))?_true_:_false_); continue;\r
- case _OP_BWNOT:\r
- if(type(STK(arg1)) == OT_INTEGER) {\r
- TARGET = SQInteger(~_integer(STK(arg1)));\r
- continue;\r
- }\r
- Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1)));\r
- SQ_THROW();\r
- case _OP_CLOSURE: {\r
- SQClosure *c = ci->_closure._unVal.pClosure;\r
- SQFunctionProto *fp = c->_function._unVal.pFunctionProto;\r
- if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); }\r
- continue;\r
- }\r
- case _OP_YIELD:{\r
- if(type(ci->_generator) == OT_GENERATOR) {\r
- if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);\r
- _GUARD(_generator(ci->_generator)->Yield(this));\r
- traps -= ci->_etraps;\r
- if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;\r
- }\r
- else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();}\r
- if(Return(arg0, arg1, temp_reg)){\r
- assert(traps==0);\r
- outres = temp_reg;\r
- return true;\r
- }\r
- \r
- }\r
- continue;\r
- case _OP_RESUME:\r
- if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();}\r
- _GUARD(_generator(STK(arg1))->Resume(this, arg0));\r
- traps += ci->_etraps;\r
- continue;\r
- case _OP_FOREACH:{ bool finished;\r
- _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,finished));\r
- if(finished) ci->_ip += sarg1; }\r
- continue;\r
- case _OP_DELEGATE: _GUARD(DELEGATE_OP(TARGET,STK(arg1),STK(arg2))); continue;\r
- case _OP_CLONE:\r
- if(!Clone(STK(arg1), TARGET))\r
- { Raise_Error(_SC("cloning a %s"), GetTypeName(STK(arg1))); SQ_THROW();}\r
- continue;\r
- case _OP_TYPEOF: TypeOf(STK(arg1), TARGET); continue;\r
- case _OP_PUSHTRAP:\r
- _etraps.push_back(SQExceptionTrap(_top,_stackbase, &ci->_iv->_vals[(ci->_ip-ci->_iv->_vals)+arg1], arg0)); traps++;\r
- ci->_etraps++;\r
- continue;\r
- case _OP_POPTRAP:{\r
- for(int i=0; i<arg0; i++) {\r
- _etraps.pop_back(); traps--;\r
- ci->_etraps--;\r
- }}\r
- continue;\r
- case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue;\r
- case _OP_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue;\r
- case _OP_NEWSLOTA:\r
- _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));\r
- _class(STK(arg1))->SetAttributes(STK(arg2),STK(arg2-1));\r
- if(arg0 != arg3) TARGET = STK(arg3);\r
- continue;\r
- }\r
- \r
- }\r
- }\r
-exception_trap:\r
- {\r
- SQObjectPtr currerror = _lasterror;\r
-// dumpstack(_stackbase);\r
- int n = 0;\r
- int last_top = _top;\r
- if(ci) {\r
- if(traps) {\r
- do {\r
- if(ci->_etraps > 0) {\r
- SQExceptionTrap &et = _etraps.top();\r
- ci->_ip = et._ip;\r
- _top = et._stacksize;\r
- _stackbase = et._stackbase;\r
- _stack[_stackbase+et._extarget] = currerror;\r
- _etraps.pop_back(); traps--; ci->_etraps--;\r
- while(last_top >= _top) _stack[last_top--].Null();\r
- goto exception_restore;\r
- }\r
- //if is a native closure\r
- if(type(ci->_closure) != OT_CLOSURE && n)\r
- break;\r
- if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();\r
- PopVarArgs(ci->_vargs);\r
- POP_CALLINFO(this);\r
- n++;\r
- }while(_callsstack.size());\r
- }\r
- //call the hook\r
- CallErrorHandler(currerror);\r
- //remove call stack until a C function is found or the cstack is empty\r
- if(ci) do{\r
- bool exitafterthisone = ci->_root;\r
- if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();\r
- _stackbase -= ci->_prevstkbase;\r
- _top = _stackbase + ci->_prevtop;\r
- PopVarArgs(ci->_vargs);\r
- POP_CALLINFO(this);\r
- if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;\r
- }while(_callsstack.size());\r
-\r
- while(last_top >= _top) _stack[last_top--].Null();\r
- }\r
- _lasterror = currerror;\r
- return false;\r
- }\r
- assert(0);\r
-}\r
-\r
-bool SQVM::CreateClassInstance(SQClass *theclass, int nargs, int stackbase, SQObjectPtr &retval)\r
-{\r
- SQObjectPtr constr;\r
- SQObjectPtr inst = theclass->CreateInstance();\r
- _stack[stackbase] = inst;\r
- if(theclass->Get(_ss(this)->_constructoridx,constr)) {\r
- if(!Call(constr,nargs,stackbase,constr))\r
- return false;\r
- }\r
- retval = inst;\r
- return true;\r
-}\r
-\r
-void SQVM::CallErrorHandler(SQObjectPtr &error)\r
-{\r
- if(type(_errorhandler) != OT_NULL) {\r
- SQObjectPtr out;\r
- Push(_roottable); Push(error);\r
- Call(_errorhandler, 2, _top-2, out);\r
- Pop(2);\r
- }\r
-}\r
-\r
-void SQVM::CallDebugHook(int type,int forcedline)\r
-{\r
- SQObjectPtr temp_reg;\r
- int nparams=5;\r
- SQFunctionProto *func=_funcproto(_closure(ci->_closure)->_function);\r
- Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name);\r
- Call(_debughook,nparams,_top-nparams,temp_reg);\r
- Pop(nparams);\r
-}\r
-\r
-bool SQVM::CallNative(SQNativeClosure *nclosure,int nargs,int stackbase,bool tailcall,SQObjectPtr &retval,bool &suspend)\r
-{\r
- if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }\r
- int nparamscheck = nclosure->_nparamscheck;\r
- if(((nparamscheck > 0) && (nparamscheck != nargs))\r
- || ((nparamscheck < 0) && (nargs < (-nparamscheck)))) {\r
- Raise_Error(_SC("wrong number of parameters"));\r
- return false;\r
- }\r
-\r
- int tcs;\r
- if(tcs = nclosure->_typecheck.size()) {\r
- for(int i = 0; i < nargs && i < tcs; i++)\r
- if((nclosure->_typecheck[i] != -1) && !(type(_stack[stackbase+i]) & nclosure->_typecheck[i])) {\r
- Raise_ParamTypeError(i,nclosure->_typecheck[i],type(_stack[stackbase+i]));\r
- return false;\r
- }\r
- }\r
- _nnativecalls++;\r
- if ((_top + MIN_STACK_OVERHEAD) > (int)_stack.size()) {\r
- _stack.resize(_stack.size() + (MIN_STACK_OVERHEAD<<1));\r
- }\r
- int oldtop = _top;\r
- int oldstackbase = _stackbase;\r
- _top = stackbase + nargs;\r
- PUSH_CALLINFO(this, CallInfo());\r
- ci->_etraps = 0;\r
- ci->_closure._unVal.pNativeClosure = nclosure;\r
- ci->_closure._type = OT_NATIVECLOSURE;\r
- ci->_prevstkbase = stackbase - _stackbase;\r
- ci->_ncalls = 1;\r
- _stackbase = stackbase;\r
- //push free variables\r
- int outers = nclosure->_outervalues.size();\r
- for (int i = 0; i < outers; i++) {\r
- Push(nclosure->_outervalues[i]);\r
- }\r
- ci->_prevtop = (oldtop - oldstackbase);\r
- int ret = (nclosure->_function)(this);\r
- _nnativecalls--;\r
- suspend = false;\r
- if( ret == SQ_SUSPEND_FLAG) suspend = true;\r
- else if (ret < 0) { \r
- _stackbase = oldstackbase;\r
- _top = oldtop;\r
- POP_CALLINFO(this);\r
- Raise_Error(_lasterror);\r
- return false;\r
- }\r
- \r
- if (ret != 0){ retval = TOP(); }\r
- else { retval = _null_; }\r
- _stackbase = oldstackbase;\r
- _top = oldtop;\r
- POP_CALLINFO(this);\r
- return true;\r
-}\r
-\r
-bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw, bool fetchroot)\r
-{\r
- switch(type(self)){\r
- case OT_TABLE:\r
- if(_table(self)->Get(key,dest))return true;\r
- break;\r
- case OT_ARRAY:\r
- if(sq_isnumeric(key)){\r
- return _array(self)->Get(tointeger(key),dest);\r
- }\r
- break;\r
- case OT_INSTANCE:\r
- if(_instance(self)->Get(key,dest)) return true;\r
- break;\r
- }\r
- if(FallBackGet(self,key,dest,raw)) return true;\r
-\r
- if(fetchroot) {\r
- if(_rawval(STK(0)) == _rawval(self) &&\r
- type(STK(0)) == type(self)) {\r
- return _table(_roottable)->Get(key,dest);\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw)\r
-{\r
- switch(type(self)){\r
- case OT_CLASS: \r
- return _class(self)->Get(key,dest);\r
- break;\r
- case OT_TABLE:\r
- case OT_USERDATA:\r
- //delegation\r
- if(_delegable(self)->_delegate) {\r
- if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,raw,false))\r
- return true; \r
- if(raw)return false;\r
- Push(self);Push(key);\r
- if(CallMetaMethod(_delegable(self),MT_GET,2,dest))\r
- return true;\r
- }\r
- if(type(self) == OT_TABLE) {\r
- if(raw) return false;\r
- return _table_ddel->Get(key,dest);\r
- }\r
- return false;\r
- break;\r
- case OT_ARRAY:\r
- if(raw)return false;\r
- return _array_ddel->Get(key,dest);\r
- case OT_STRING:\r
- if(sq_isnumeric(key)){\r
- SQInteger n=tointeger(key);\r
- if(abs(n)<_string(self)->_len){\r
- if(n<0)n=_string(self)->_len-n;\r
- dest=SQInteger(_stringval(self)[n]);\r
- return true;\r
- }\r
- return false;\r
- }\r
- else {\r
- if(raw)return false;\r
- return _string_ddel->Get(key,dest);\r
- }\r
- break;\r
- case OT_INSTANCE:\r
- if(raw)return false;\r
- Push(self);Push(key);\r
- if(!CallMetaMethod(_delegable(self),MT_GET,2,dest)) {\r
- return _instance_ddel->Get(key,dest);\r
- }\r
- return true;\r
- case OT_INTEGER:case OT_FLOAT:case OT_BOOL: \r
- if(raw)return false;\r
- return _number_ddel->Get(key,dest);\r
- case OT_GENERATOR: \r
- if(raw)return false;\r
- return _generator_ddel->Get(key,dest);\r
- case OT_CLOSURE: case OT_NATIVECLOSURE: \r
- if(raw)return false;\r
- return _closure_ddel->Get(key,dest);\r
- case OT_THREAD:\r
- if(raw)return false;\r
- return _thread_ddel->Get(key,dest);\r
- default:return false;\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool fetchroot)\r
-{\r
- switch(type(self)){\r
- case OT_TABLE:\r
- if(_table(self)->Set(key,val))\r
- return true;\r
- if(_table(self)->_delegate) {\r
- if(Set(_table(self)->_delegate,key,val,false)) {\r
- return true;\r
- }\r
- }\r
- //keeps going\r
- case OT_USERDATA:\r
- if(_delegable(self)->_delegate) {\r
- SQObjectPtr t;\r
- Push(self);Push(key);Push(val);\r
- if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;\r
- }\r
- break;\r
- case OT_INSTANCE:{\r
- if(_instance(self)->Set(key,val))\r
- return true;\r
- SQObjectPtr t;\r
- Push(self);Push(key);Push(val);\r
- if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;\r
- }\r
- break;\r
- case OT_ARRAY:\r
- if(!sq_isnumeric(key)) {Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; }\r
- return _array(self)->Set(tointeger(key),val);\r
- default:\r
- Raise_Error(_SC("trying to set '%s'"),GetTypeName(self));\r
- return false;\r
- }\r
- if(fetchroot) {\r
- if(_rawval(STK(0)) == _rawval(self) &&\r
- type(STK(0)) == type(self)) {\r
- return _table(_roottable)->Set(key,val);\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target)\r
-{\r
- SQObjectPtr temp_reg;\r
- switch(type(self)){\r
- case OT_TABLE:\r
- target = _table(self)->Clone();\r
- goto cloned_mt;\r
- case OT_INSTANCE:\r
- target = _instance(self)->Clone(_ss(this));\r
-cloned_mt:\r
- if(_delegable(target)->_delegate){\r
- Push(target);\r
- Push(self);\r
- CallMetaMethod(_delegable(target),MT_CLONED,2,temp_reg);\r
- }\r
- return true;\r
- case OT_ARRAY: \r
- target=_array(self)->Clone();\r
- return true;\r
- default: return false;\r
- }\r
-}\r
-\r
-bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val)\r
-{\r
- if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; }\r
- switch(type(self)) {\r
- case OT_TABLE: {\r
- bool rawcall = true;\r
- if(_table(self)->_delegate) {\r
- SQObjectPtr res;\r
- if(!_table(self)->Get(key,res)) {\r
- Push(self);Push(key);Push(val);\r
- rawcall = !CallMetaMethod(_table(self),MT_NEWSLOT,3,res);\r
- }\r
- }\r
- if(rawcall) _table(self)->NewSlot(key,val); //cannot fail\r
- \r
- break;}\r
- case OT_CLASS: \r
- if(!_class(self)->NewSlot(key,val)) {\r
- if(_class(self)->_locked) {\r
- Raise_Error(_SC("trying to modify a class that has already been instantiated"));\r
- return false;\r
- }\r
- else {\r
- SQObjectPtr oval = PrintObjVal(key);\r
- Raise_Error(_SC("the property '%s' already exists"),_stringval(oval));\r
- return false;\r
- }\r
- }\r
- break;\r
- default:\r
- Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key));\r
- return false;\r
- break;\r
- }\r
- return true;\r
-}\r
-\r
-bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res)\r
-{\r
- switch(type(self)) {\r
- case OT_TABLE:\r
- case OT_INSTANCE:\r
- case OT_USERDATA: {\r
- SQObjectPtr t;\r
- bool handled = false;\r
- if(_delegable(self)->_delegate) {\r
- Push(self);Push(key);\r
- handled = CallMetaMethod(_delegable(self),MT_DELSLOT,2,t);\r
- }\r
-\r
- if(!handled) {\r
- if(type(self) == OT_TABLE) {\r
- if(_table(self)->Get(key,t)) {\r
- _table(self)->Remove(key);\r
- }\r
- else {\r
- Raise_IdxError((SQObject &)key);\r
- return false;\r
- }\r
- }\r
- else {\r
- Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self));\r
- return false;\r
- }\r
- }\r
- res = t;\r
- }\r
- break;\r
- default:\r
- Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self));\r
- return false;\r
- }\r
- return true;\r
-}\r
-\r
-bool SQVM::Call(SQObjectPtr &closure,int nparams,int stackbase,SQObjectPtr &outres)\r
-{\r
-#ifdef _DEBUG\r
-int prevstackbase = _stackbase;\r
-#endif\r
- switch(type(closure)) {\r
- case OT_CLOSURE:\r
- return Execute(closure, _top - nparams, nparams, stackbase,outres);\r
- break;\r
- case OT_NATIVECLOSURE:{\r
- bool suspend;\r
- return CallNative(_nativeclosure(closure), nparams, stackbase, false, outres,suspend);\r
- \r
- }\r
- break;\r
- case OT_CLASS:\r
- return CreateClassInstance(_class(closure),nparams,stackbase,outres);\r
- break;\r
- default:\r
- return false;\r
- }\r
-#ifdef _DEBUG\r
- if(!_suspended) {\r
- assert(_stackbase == prevstackbase);\r
- }\r
-#endif\r
- return true;\r
-}\r
-\r
-bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,int nparams,SQObjectPtr &outres)\r
-{\r
- SQObjectPtr closure;\r
- if(del->GetMetaMethod(mm, closure)) {\r
- if(Call(closure, nparams, _top - nparams, outres)) {\r
- Pop(nparams);\r
- return true;\r
- }\r
- }\r
- Pop(nparams);\r
- return false;\r
-}\r
-\r
-void SQVM::Pop() {\r
- _stack[--_top] = _null_;\r
-}\r
-void SQVM::Pop(int n) {\r
- for(int i = 0; i < n; i++){\r
- _stack[--_top] = _null_;\r
- }\r
-}\r
-\r
-void SQVM::Remove(int n) {\r
- n = (n >= 0)?n + _stackbase - 1:_top + n;\r
- for(int i = n; i < _top; i++){\r
- _stack[i] = _stack[i+1];\r
- }\r
- _stack[_top] = _null_;\r
- _top--;\r
-}\r
-\r
-void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; }\r
-SQObjectPtr &SQVM::Top() { return _stack[_top-1]; }\r
-SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; }\r
-SQObjectPtr &SQVM::GetUp(int n) { return _stack[_top+n]; }\r
-SQObjectPtr &SQVM::GetAt(int n) { return _stack[n]; }\r
-\r
-#ifdef _DEBUG_DUMP\r
-void SQVM::dumpstack(int stackbase,bool dumpall)\r
-{\r
- int size=dumpall?_stack.size():_top;\r
- int n=0;\r
- scprintf(_SC("\n>>>>stack dump<<<<\n"));\r
- CallInfo &ci=_callsstack.back();\r
- scprintf(_SC("IP: %d\n"),ci._ip);\r
- scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase);\r
- scprintf(_SC("prev top: %d\n"),ci._prevtop);\r
- for(int i=0;i<size;i++){\r
- SQObjectPtr &obj=_stack[i]; \r
- if(stackbase==i)scprintf(_SC(">"));else scprintf(_SC(" "));\r
- scprintf(_SC("[%d]:"),n);\r
- switch(type(obj)){\r
- case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break;\r
- case OT_INTEGER: scprintf(_SC("INTEGER %d"),_integer(obj));break;\r
- case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break;\r
- case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break;\r
- case OT_NULL: scprintf(_SC("NULL")); break;\r
- case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break;\r
- case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break;\r
- case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break;\r
- case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break;\r
- case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break;\r
- case OT_GENERATOR: scprintf(_SC("GENERATOR"));break;\r
- case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break;\r
- case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break;\r
- case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break;\r
- case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break;\r
- default:\r
- assert(0);\r
- break;\r
- };\r
- scprintf(_SC("\n"));\r
- ++n;\r
- }\r
-}\r
-\r
-#endif\r
+/*
+ see copyright notice in squirrel.h
+*/
+#include "sqpcheader.h"
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "sqopcodes.h"
+#include "sqfuncproto.h"
+#include "sqvm.h"
+#include "sqclosure.h"
+#include "sqstring.h"
+#include "sqtable.h"
+#include "squserdata.h"
+#include "sqarray.h"
+#include "sqclass.h"
+
+#define TOP() (_stack[_top-1])
+
+bool SQVM::BW_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+ SQInteger res;
+ SQInteger i1 = _integer(o1), i2 = _integer(o2);
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER))
+ {
+ switch(op) {
+ case BW_AND: res = i1 & i2; break;
+ case BW_OR: res = i1 | i2; break;
+ case BW_XOR: res = i1 ^ i2; break;
+ case BW_SHIFTL: res = i1 << i2; break;
+ case BW_SHIFTR: res = i1 >> i2; break;
+ case BW_USHIFTR:res = (SQInteger)(*((unsigned int*)&i1) >> i2); break;
+ default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; }
+ }
+ }
+ else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;}
+ trg = res;
+ return true;
+}
+
+bool SQVM::ARITH_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
+{
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER)) {
+ switch(op) {
+ case '+': trg = _integer(o1) + _integer(o2); break;
+ case '-': trg = _integer(o1) - _integer(o2); break;
+ case '/': if(_integer(o2) == 0) { Raise_Error(_SC("division by zero")); return false; }
+ trg = _integer(o1) / _integer(o2);
+ break;
+ case '*': trg = _integer(o1) * _integer(o2); break;
+ case '%': trg = _integer(o1) % _integer(o2); break;
+ }
+ }else{
+ switch(op) {
+ case '+': trg = tofloat(o1) + tofloat(o2); break;
+ case '-': trg = tofloat(o1) - tofloat(o2); break;
+ case '/': trg = tofloat(o1) / tofloat(o2); break;
+ case '*': trg = tofloat(o1) * tofloat(o2); break;
+ case '%': trg = SQFloat(fmod((double)tofloat(o1),(double)tofloat(o2))); break;
+ }
+ }
+ } else {
+ if(op == '+' && (type(o1) == OT_STRING || type(o2) == OT_STRING)){
+ if(!StringCat(o1, o2, trg)) return false;
+ }
+ else if(!ArithMetaMethod(op,o1,o2,trg)) {
+ Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false;
+ }
+ }
+ return true;
+}
+
+SQObjectPtr &stack_get(HSQUIRRELVM v,int idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}
+
+SQVM::SQVM(SQSharedState *ss)
+{
+ _sharedstate=ss;
+ _suspended=false;
+ _suspended_target=-1;
+ _suspended_root=false;
+ _suspended_traps=-1;
+ _foreignptr=NULL;
+ _nnativecalls=0;
+ _uiRef=0;
+ _lasterror = _null_;
+ _errorhandler = _null_;
+ _debughook = _null_;
+ INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+void SQVM::Finalize()
+{
+ _roottable = _null_;
+ _lasterror = _null_;
+ _errorhandler = _null_;
+ _debughook = _null_;
+ temp_reg = _null_;
+ int size=_stack.size();
+ for(int i=0;i<size;i++)
+ _stack[i]=_null_;
+}
+
+SQVM::~SQVM()
+{
+ Finalize();
+ REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
+}
+
+bool SQVM::ArithMetaMethod(int op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest)
+{
+ SQMetaMethod mm;
+ switch(op){
+ case _SC('+'): mm=MT_ADD; break;
+ case _SC('-'): mm=MT_SUB; break;
+ case _SC('/'): mm=MT_DIV; break;
+ case _SC('*'): mm=MT_MUL; break;
+ case _SC('%'): mm=MT_MODULO; break;
+ default: mm=MT_ADD; assert(0); break;
+ }
+ if(is_delegable(o1) && _delegable(o1)->_delegate) {
+ Push(o1);Push(o2);
+ return CallMetaMethod(_delegable(o1),mm,2,dest);
+ }
+ return false;
+}
+
+bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o)
+{
+
+ switch(type(o)) {
+ case OT_INTEGER:
+ trg = -_integer(o);
+ return true;
+ case OT_FLOAT:
+ trg = -_float(o);
+ return true;
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o)->_delegate) {
+ Push(o);
+ if(CallMetaMethod(_delegable(o), MT_UNM, 1, temp_reg)) {
+ trg = temp_reg;
+ return true;
+ }
+ }
+ return true;
+ default:
+ break;
+ }
+ Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o));
+ return false;
+}
+
+#define _RET_SUCCEED(exp) { result = (exp); return true; }
+bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,int &result)
+{
+ if(type(o1)==type(o2)){
+ if(_userpointer(o1)==_userpointer(o2))_RET_SUCCEED(0);
+ SQObjectPtr res;
+ switch(type(o1)){
+ case OT_STRING:
+ _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2)));
+ case OT_INTEGER:
+ _RET_SUCCEED(_integer(o1)-_integer(o2));
+ case OT_FLOAT:
+ _RET_SUCCEED((_float(o1)<_float(o2))?-1:1);
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ Push(o1);Push(o2);
+ if(_delegable(o1)->_delegate)CallMetaMethod(_delegable(o1),MT_CMP,2,res);
+ break;
+ default:
+ break;
+ }
+ if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; }
+ _RET_SUCCEED(_integer(res));
+ }
+ else{
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)){
+ if((type(o1)==OT_INTEGER) && (type(o2)==OT_FLOAT)) {
+ if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); }
+ else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); }
+ _RET_SUCCEED(1);
+ }
+ else{
+ if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); }
+ else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); }
+ _RET_SUCCEED(1);
+ }
+ }
+ else if(type(o1)==OT_NULL) {_RET_SUCCEED(-1);}
+ else if(type(o2)==OT_NULL) {_RET_SUCCEED(1);}
+ else { Raise_CompareError(o1,o2); return false; }
+
+ }
+ assert(0);
+ _RET_SUCCEED(0); //cannot happen
+}
+
+bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res)
+{
+ int r;
+ if(ObjCmp(o1,o2,r)) {
+ switch(op) {
+ case CMP_G: res = (r > 0)?_true_:_false_; return true;
+ case CMP_GE: res = (r >= 0)?_true_:_false_; return true;
+ case CMP_L: res = (r < 0)?_true_:_false_; return true;
+ case CMP_LE: res = (r <= 0)?_true_:_false_; return true;
+
+ }
+ assert(0);
+ }
+ return false;
+}
+
+bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest)
+{
+ switch(type(obj))
+ {
+ case OT_STRING:
+ switch(type(str)){
+ case OT_STRING: {
+ int l=_string(str)->_len,ol=_string(obj)->_len;
+ SQChar *s=_sp(rsl(l+ol+1));
+ memcpy(s,_stringval(str),rsl(l));memcpy(s+l,_stringval(obj),rsl(ol));s[l+ol]=_SC('\0');
+ break;
+ }
+ case OT_FLOAT:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%g%s"),_float(str),_stringval(obj));
+ break;
+ case OT_INTEGER:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%d%s"),_integer(str),_stringval(obj));
+ break;
+ default:
+ Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));
+ return false;
+ }
+ dest=SQString::Create(_ss(this),_spval);
+ break;
+ case OT_FLOAT:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%g"),_stringval(str),_float(obj));
+ dest=SQString::Create(_ss(this),_spval);
+ break;
+ case OT_INTEGER:
+ scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%d"),_stringval(str),_integer(obj));
+ dest=SQString::Create(_ss(this),_spval);
+ break;
+ default:
+ Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));
+ return false;
+ }
+ return true;
+}
+
+const SQChar *IdType2Name(SQObjectType type)
+{
+ switch(_RAW_TYPE(type))
+ {
+ case _RT_NULL:return _SC("null");
+ case _RT_INTEGER:return _SC("integer");
+ case _RT_FLOAT:return _SC("float");
+ case _RT_BOOL:return _SC("bool");
+ case _RT_STRING:return _SC("string");
+ case _RT_TABLE:return _SC("table");
+ case _RT_ARRAY:return _SC("array");
+ case _RT_GENERATOR:return _SC("generator");
+ case _RT_CLOSURE:
+ case _RT_NATIVECLOSURE:
+ return _SC("function");
+ case _RT_USERDATA:
+ case _RT_USERPOINTER:
+ return _SC("userdata");
+ case _RT_THREAD: return _SC("thread");
+ case _RT_FUNCPROTO: return _SC("function");
+ case _RT_CLASS: return _SC("class");
+ case _RT_INSTANCE: return _SC("instance");
+ default:
+ return NULL;
+ }
+}
+
+const SQChar *GetTypeName(const SQObjectPtr &obj1)
+{
+ return IdType2Name(type(obj1));
+}
+
+void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)
+{
+ if(is_delegable(obj1) && _delegable(obj1)->_delegate) {
+ Push(obj1);
+ if(CallMetaMethod(_delegable(obj1),MT_TYPEOF,1,dest))
+ return;
+ }
+ dest = SQString::Create(_ss(this),GetTypeName(obj1));
+}
+
+bool SQVM::Init(SQVM *friendvm, int stacksize)
+{
+ _stack.resize(stacksize);
+ _callsstack.reserve(4);
+ _stackbase = 0;
+ _top = 0;
+ if(!friendvm)
+ _roottable = SQTable::Create(_ss(this), 0);
+ else {
+ _roottable = friendvm->_roottable;
+ _errorhandler = friendvm->_errorhandler;
+ _debughook = friendvm->_debughook;
+ }
+
+ sq_base_register(this);
+ return true;
+}
+
+extern SQInstructionDesc g_InstrDesc[];
+
+bool SQVM::StartCall(SQClosure *closure,int target,int nargs,int stackbase,bool tailcall)
+{
+ SQFunctionProto *func = _funcproto(closure->_function);
+ //const int outerssize = func->_outervalues.size();
+
+ const int paramssize = func->_parameters.size();
+ // const int oldtop = _top;
+ const int newtop = stackbase + func->_stacksize;
+
+
+ if(func->_varparams)
+ {
+ if (nargs < paramssize) {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+ for(int n = 0; n < nargs - paramssize; n++) {
+ _vargsstack.push_back(_stack[stackbase+paramssize+n]);
+ _stack[stackbase+paramssize+n] = _null_;
+ }
+ }
+ else {
+ if (paramssize != nargs) {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+
+ }
+
+ if (!tailcall) {
+ PUSH_CALLINFO(this, CallInfo());
+ ci->_etraps = 0;
+ ci->_prevstkbase = stackbase - _stackbase;
+ ci->_target = target;
+ ci->_prevtop = _top - _stackbase;
+ ci->_ncalls = 1;
+ ci->_root = false;
+ }
+ else {
+ ci->_ncalls++;
+ }
+ ci->_vargs.size = (nargs - paramssize);
+ ci->_vargs.base = _vargsstack.size()-(nargs - paramssize);
+ ci->_closure._unVal.pClosure = closure;
+ ci->_closure._type = OT_CLOSURE;
+ ci->_iv = &func->_instructions;
+ ci->_literals = &func->_literals;
+ //grows the stack if needed
+ if (((unsigned int)newtop + (func->_stacksize<<1)) > _stack.size()) {
+ _stack.resize(_stack.size() + (func->_stacksize<<1));
+ }
+
+ _top = newtop;
+ _stackbase = stackbase;
+ ci->_ip = ci->_iv->_vals;
+ return true;
+}
+
+bool SQVM::Return(int _arg0, int _arg1, SQObjectPtr &retval)
+{
+ if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ for(int i=0;i<ci->_ncalls;i++)
+ CallDebugHook(_SC('r'));
+
+ bool broot = ci->_root;
+ int last_top = _top;
+ int target = ci->_target;
+ int oldstackbase = _stackbase;
+ _stackbase -= ci->_prevstkbase;
+ _top = _stackbase + ci->_prevtop;
+ PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ if (broot) {
+ if (_arg0 != MAX_FUNC_STACKSIZE) retval = _stack[oldstackbase+_arg1];
+ else retval = _null_;
+ }
+ else {
+ if (_arg0 != MAX_FUNC_STACKSIZE)
+ STK(target) = _stack[oldstackbase+_arg1];
+ else
+ STK(target) = _null_;
+ }
+
+ while (last_top >= _top) _stack[last_top--].Null();
+ assert(oldstackbase >= _stackbase);
+ return broot;
+}
+
+#define _RET_ON_FAIL(exp) { if(!exp) return false; }
+
+bool SQVM::LOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+ _RET_ON_FAIL(ARITH_OP( op , target, a, incr));
+ a = target;
+ return true;
+}
+
+bool SQVM::PLOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)
+{
+ SQObjectPtr trg;
+ _RET_ON_FAIL(ARITH_OP( op , trg, a, incr));
+ target = a;
+ a = trg;
+ return true;
+}
+
+bool SQVM::DerefInc(int op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix)
+{
+ SQObjectPtr tmp, tself = self, tkey = key;
+ if (!Get(tself, tkey, tmp, false, true)) { Raise_IdxError(tkey); return false; }
+ _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr))
+ Set(tself, tkey, target,true);
+ if (postfix) target = tmp;
+ return true;
+}
+
+#define arg0 (_i_._arg0)
+#define arg1 (_i_._arg1)
+#define sarg1 (*((int *)&_i_._arg1))
+#define arg2 (_i_._arg2)
+#define arg3 (_i_._arg3)
+#define sarg3 (*((char *)&_i_._arg3))
+
+SQRESULT SQVM::Suspend()
+{
+ if (_suspended)
+ return sq_throwerror(this, _SC("cannot suspend an already suspended vm"));
+ if (_nnativecalls!=2)
+ return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods"));
+ return SQ_SUSPEND_FLAG;
+}
+
+void SQVM::PopVarArgs(VarArgs &vargs)
+{
+ for(int n = 0; n< vargs.size; n++)
+ _vargsstack.pop_back();
+}
+
+#define _FINISH(stoploop) {finished = stoploop; return true; }
+bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr
+&o3,SQObjectPtr &o4,int arg_2,bool &finished)
+{
+ int nrefidx;
+ switch(type(o1)) {
+ case OT_TABLE:
+ if((nrefidx = _table(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);
+ o4 = (SQInteger)nrefidx; _FINISH(false);
+ case OT_ARRAY:
+ if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);
+ o4 = (SQInteger) nrefidx; _FINISH(false);
+ case OT_STRING:
+ if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);
+ o4 = (SQInteger)nrefidx; _FINISH(false);
+ case OT_CLASS:
+ if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);
+ o4 = (SQInteger)nrefidx; _FINISH(false);
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ if(_delegable(o1)->_delegate) {
+ SQObjectPtr itr;
+ Push(o1);
+ Push(o4);
+ if(CallMetaMethod(_delegable(o1), MT_NEXTI, 2, itr)){
+ o4 = o2 = itr;
+ if(type(itr) == OT_NULL) _FINISH(true);
+ if(!Get(o1, itr, o3, false,false)) {
+ Raise_Error(_SC("_nexti returned an invalid idx"));
+ return false;
+ }
+ _FINISH(false);
+ }
+ Raise_Error(_SC("_nexti failed"));
+ return false;
+ }
+ break;
+ case OT_GENERATOR:
+ if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(true);
+ if(_generator(o1)->_state == SQGenerator::eSuspended) {
+ SQInteger idx = 0;
+ if(type(o4) == OT_INTEGER) {
+ idx = _integer(o4) + 1;
+ }
+ o2 = idx;
+ o4 = idx;
+ _generator(o1)->Resume(this, arg_2+1);
+ _FINISH(false);
+ }
+ default:
+ break;
+ }
+ Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1));
+ return false; //cannot be hit(just to avoid warnings)
+}
+
+bool SQVM::DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2)
+{
+ if(type(o1) != OT_TABLE) { Raise_Error(_SC("delegating a '%s'"), GetTypeName(o1)); return false; }
+ switch(type(o2)) {
+ case OT_TABLE:
+ if(!_table(o1)->SetDelegate(_table(o2))){
+ Raise_Error(_SC("delegate cycle detected"));
+ return false;
+ }
+ break;
+ case OT_NULL:
+ _table(o1)->SetDelegate(NULL);
+ break;
+ default:
+ Raise_Error(_SC("using '%s' as delegate"), GetTypeName(o2));
+ return false;
+ break;
+ }
+ trg = o1;
+ return true;
+}
+#define COND_LITERAL (arg3!=0?(*ci->_literals)[arg1]:STK(arg1))
+
+#define _GUARD(exp) { if(!exp) { Raise_Error(_lasterror); SQ_THROW();} }
+
+#define SQ_THROW() { goto exception_trap; }
+
+bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)
+{
+ int nouters;
+ SQClosure *closure = SQClosure::Create(_ss(this), func);
+ if( (nouters = func->_outervalues.size()) ) {
+ closure->_outervalues.reserve(nouters);
+ for(int i = 0; i<nouters; i++) {
+ SQOuterVar &v = func->_outervalues[i];
+ switch(v._type){
+ case otSYMBOL:
+ closure->_outervalues.push_back(_null_);
+ if(!Get(_stack._vals[_stackbase]/*STK(0)*/, v._src, closure->_outervalues.top(), false,true))
+ {Raise_IdxError(v._src); return false; }
+ break;
+ case otLOCAL:
+ closure->_outervalues.push_back(_stack._vals[_stackbase+_integer(v._src)]);
+ break;
+ case otOUTER:
+ closure->_outervalues.push_back(_closure(ci->_closure)->_outervalues[_integer(v._src)]);
+ break;
+ }
+ }
+ }
+ target = closure;
+ return true;
+
+}
+
+bool SQVM::GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &index,CallInfo *ci)
+{
+ if(ci->_vargs.size == 0) {
+ Raise_Error(_SC("the function doesn't have var args"));
+ return false;
+ }
+ if(!sq_isnumeric(index)){
+ Raise_Error(_SC("indexing 'vargv' with %s"),GetTypeName(index));
+ return false;
+ }
+ int idx = tointeger(index);
+ if(idx < 0 || idx >= ci->_vargs.size){ Raise_Error(_SC("vargv index out of range")); return false; }
+ target = _vargsstack[ci->_vargs.base+idx];
+ return true;
+}
+
+bool SQVM::CLASS_OP(SQObjectPtr &target,int baseclass,int attributes)
+{
+ SQClass *base = NULL;
+ SQObjectPtr attrs;
+ if(baseclass != MAX_LITERALS) {
+ if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; }
+ base = _class(_stack._vals[_stackbase + baseclass]);
+ }
+ if(attributes != MAX_FUNC_STACKSIZE) {
+ attrs = _stack._vals[_stackbase+attributes];
+ }
+ target = SQClass::Create(_ss(this),base);
+ _class(target)->_attributes = attrs;
+ return true;
+}
+
+bool SQVM::IsFalse(SQObjectPtr &o)
+{
+ SQObjectType t = type(o);
+ if((t & SQOBJECT_CANBEFALSE)
+ && ((t == OT_NULL) || ((t == OT_INTEGER || t == OT_BOOL) && _integer(o) == 0)
+ || (t == OT_FLOAT && _float(o) == SQFloat(0.0)))) {
+ return true;
+ }
+ return false;
+}
+
+bool SQVM::IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res)
+{
+ if(type(o1) == type(o2)) {
+ res = ((_userpointer(o1) == _userpointer(o2)?true:false));
+ }
+ else {
+ if(sq_isnumeric(o1) && sq_isnumeric(o2)) {
+ int cmpres;
+ if(!ObjCmp(o1, o2,cmpres)) return false;
+ res = (cmpres == 0);
+ }
+ else {
+ res = false;
+ }
+ }
+ return true;
+}
+
+bool SQVM::Execute(SQObjectPtr &closure, int target, int nargs, int stackbase,SQObjectPtr &outres, ExecutionType et)
+{
+ if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+ _nnativecalls++;
+ AutoDec ad(&_nnativecalls);
+ int traps = 0;
+ //temp_reg vars for OP_CALL
+ int ct_target;
+ bool ct_tailcall;
+
+ switch(et) {
+ case ET_CALL:
+ if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) {
+ //call the handler if there are no calls in the stack, if not relies on the previous node
+ if(ci == NULL) CallErrorHandler(_lasterror);
+ return false;
+ }
+ ci->_root = true;
+ break;
+ case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = true; traps += ci->_etraps; break;
+ case ET_RESUME_VM:
+ traps = _suspended_traps;
+ ci->_root = _suspended_root;
+ _suspended = false;
+ break;
+ }
+
+exception_restore:
+ //SQ_TRY
+ {
+ for(;;)
+ {
+ const SQInstruction &_i_ = *ci->_ip++;
+ //dumpstack(_stackbase);
+ //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3);
+ switch(_i_.op)
+ {
+ case _OP_LINE:
+ if(type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ CallDebugHook(_SC('l'),arg1);
+ continue;
+ case _OP_LOAD: TARGET = (*ci->_literals)[arg1]; continue;
+ case _OP_TAILCALL:
+ temp_reg = STK(arg1);
+ if (type(temp_reg) == OT_CLOSURE){
+ ct_tailcall = true;
+ PopVarArgs(ci->_vargs);
+ for (int i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);
+ ct_target = ci->_target;
+ goto common_call;
+ }
+ case _OP_CALL: {
+ ct_tailcall = false;
+ ct_target = arg0;
+ temp_reg = STK(arg1);
+common_call:
+ int last_top = _top;
+ switch (type(temp_reg)) {
+ case OT_CLOSURE:{
+ StartCall(_closure(temp_reg), ct_target, arg3, ct_tailcall?_stackbase:_stackbase+arg2, ct_tailcall);
+ if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {
+ SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));
+ _GUARD(gen->Yield(this));
+ Return(1, ct_target, temp_reg);
+ STK(ct_target) = gen;
+ while (last_top >= _top) _stack[last_top--].Null();
+ continue;
+ }
+ if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+ CallDebugHook(_SC('c'));
+ }
+ break;
+ case OT_NATIVECLOSURE: {
+ bool suspend;
+ _GUARD(CallNative(_nativeclosure(temp_reg), arg3, _stackbase+arg2, ct_tailcall, temp_reg,suspend));
+ if(suspend){
+ _suspended = true;
+ _suspended_target = ct_target;
+ _suspended_root = ci->_root;
+ _suspended_traps = traps;
+ outres = temp_reg;
+ return true;
+ }
+ STK(ct_target) = temp_reg;
+ }
+ break;
+ case OT_CLASS:{
+ _GUARD(CreateClassInstance(_class(temp_reg),arg3,_stackbase+arg2,STK(ct_target)));
+ }
+ break;
+ case OT_TABLE:
+ case OT_USERDATA:
+ case OT_INSTANCE:
+ {
+ Push(temp_reg);
+ for (int i = 0; i < arg3; i++) Push(STK(arg2 + i));
+ if (_delegable(temp_reg) && CallMetaMethod(_delegable(temp_reg), MT_CALL, arg3+1, temp_reg)){
+ STK(ct_target) = temp_reg;
+ break;
+ }
+ Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+ SQ_THROW();
+ }
+ default:
+ Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+ SQ_THROW();
+ }
+ }
+ continue;
+ case _OP_PREPCALL:
+ if (!Get(STK(arg2), STK(arg1), temp_reg, false,true))
+ { Raise_IdxError(STK(arg1)); SQ_THROW(); }
+ goto common_prepcall;
+ case _OP_PREPCALLK:
+ if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg,false,true)) {
+ if(type(STK(arg2)) == OT_CLASS) { //hack?
+ if(_class_ddel->Get((*ci->_literals)[arg1],temp_reg)) {
+ STK(arg3) = STK(arg2);
+ TARGET = temp_reg;
+ continue;
+ }
+ }
+ { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}
+ }
+common_prepcall:
+ if(type(STK(arg2)) == OT_CLASS) {
+ STK(arg3) = STK(0); // this
+ }
+ else {
+ STK(arg3) = STK(arg2);
+ }
+ TARGET = temp_reg;
+ continue;
+ case _OP_GETK:
+ if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg, false,true)) { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}
+ TARGET = temp_reg;
+ continue;
+ case _OP_MOVE: TARGET = STK(arg1); continue;
+ case _OP_NEWSLOT:
+ _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));
+ if(arg0 != arg3) TARGET = STK(arg3);
+ continue;
+ case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue;
+ case _OP_SET:
+ if (!Set(STK(arg1), STK(arg2), STK(arg3),true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+ if (arg0 != arg3) TARGET = STK(arg3);
+ continue;
+ case _OP_GET:
+ if (!Get(STK(arg1), STK(arg2), temp_reg, false,true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }
+ TARGET = temp_reg;
+ continue;
+ case _OP_EQ:{
+ bool res;
+ if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+ TARGET = res?_true_:_false_;
+ }continue;
+ case _OP_NE:{
+ bool res;
+ if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }
+ TARGET = (!res)?_true_:_false_;
+ } continue;
+ case _OP_ARITH: _GUARD(ARITH_OP( arg3 , temp_reg, STK(arg2), STK(arg1))); TARGET = temp_reg; continue;
+ case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue;
+ case _OP_RETURN:
+ if(type((ci)->_generator) == OT_GENERATOR) {
+ _generator((ci)->_generator)->Kill();
+ }
+ if(Return(arg0, arg1, temp_reg)){
+ assert(traps==0);
+ outres = temp_reg;
+ return true;
+ }
+ continue;
+ case _OP_LOADNULLS:{ for(unsigned int n=0;n<arg1;n++) STK(arg0+n) = _null_; }continue;
+ case _OP_LOADROOTTABLE: TARGET = _roottable; continue;
+ case _OP_LOADBOOL: TARGET = arg1?_true_:_false_; continue;
+ case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue;
+ case _OP_JMP: ci->_ip += (sarg1); continue;
+ case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+ case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;
+ case _OP_LOADFREEVAR: TARGET = _closure(ci->_closure)->_outervalues[arg1]; continue;
+ case _OP_VARGC: TARGET = SQInteger(ci->_vargs.size); continue;
+ case _OP_GETVARGV:
+ if(!GETVARGV_OP(TARGET,STK(arg1),ci)) { SQ_THROW(); }
+ continue;
+ case _OP_NEWTABLE: TARGET = SQTable::Create(_ss(this), arg1); continue;
+ case _OP_NEWARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue;
+ case _OP_APPENDARRAY: _array(STK(arg0))->Append(COND_LITERAL); continue;
+ case _OP_GETPARENT:
+ switch(type(STK(arg1))) {
+ case OT_TABLE:
+ TARGET = _table(STK(arg1))->_delegate?SQObjectPtr(_table(STK(arg1))->_delegate):_null_;
+ continue;
+ case OT_CLASS: TARGET = _class(STK(arg1))->_base?_class(STK(arg1))->_base:_null_;
+ continue;
+ default:
+ break;
+ }
+ Raise_Error(_SC("the %s type doesn't have a parent slot"), GetTypeName(STK(arg1)));
+ SQ_THROW();
+ continue;
+ case _OP_COMPARITH: _GUARD(DerefInc(arg3, TARGET, STK((((unsigned int)arg1&0xFFFF0000)>>16)), STK(arg2), STK(arg1&0x0000FFFF), false)); continue;
+ case _OP_COMPARITHL: _GUARD(LOCAL_INC(arg3, TARGET, STK(arg1), STK(arg2))); continue;
+ case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false));} continue;
+ case _OP_INCL: {SQObjectPtr o(sarg3); _GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+ case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true));} continue;
+ case _OP_PINCL: {SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o));} continue;
+ case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue;
+ case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, true,false)?_true_:_false_;continue;
+ case _OP_INSTANCEOF:
+ if(type(STK(arg1)) != OT_CLASS || type(STK(arg2)) != OT_INSTANCE)
+ {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();}
+ TARGET = _instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?_true_:_false_;
+ continue;
+ case _OP_AND:
+ if(IsFalse(STK(arg2))) {
+ TARGET = STK(arg2);
+ ci->_ip += (sarg1);
+ }
+ continue;
+ case _OP_OR:
+ if(!IsFalse(STK(arg2))) {
+ TARGET = STK(arg2);
+ ci->_ip += (sarg1);
+ }
+ continue;
+ case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue;
+ case _OP_NOT: TARGET = (IsFalse(STK(arg1))?_true_:_false_); continue;
+ case _OP_BWNOT:
+ if(type(STK(arg1)) == OT_INTEGER) {
+ TARGET = SQInteger(~_integer(STK(arg1)));
+ continue;
+ }
+ Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1)));
+ SQ_THROW();
+ case _OP_CLOSURE: {
+ SQClosure *c = ci->_closure._unVal.pClosure;
+ SQFunctionProto *fp = c->_function._unVal.pFunctionProto;
+ if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); }
+ continue;
+ }
+ case _OP_YIELD:{
+ if(type(ci->_generator) == OT_GENERATOR) {
+ if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);
+ _GUARD(_generator(ci->_generator)->Yield(this));
+ traps -= ci->_etraps;
+ if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;
+ }
+ else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();}
+ if(Return(arg0, arg1, temp_reg)){
+ assert(traps==0);
+ outres = temp_reg;
+ return true;
+ }
+
+ }
+ continue;
+ case _OP_RESUME:
+ if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();}
+ _GUARD(_generator(STK(arg1))->Resume(this, arg0));
+ traps += ci->_etraps;
+ continue;
+ case _OP_FOREACH:{ bool finished;
+ _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,finished));
+ if(finished) ci->_ip += sarg1; }
+ continue;
+ case _OP_DELEGATE: _GUARD(DELEGATE_OP(TARGET,STK(arg1),STK(arg2))); continue;
+ case _OP_CLONE:
+ if(!Clone(STK(arg1), TARGET))
+ { Raise_Error(_SC("cloning a %s"), GetTypeName(STK(arg1))); SQ_THROW();}
+ continue;
+ case _OP_TYPEOF: TypeOf(STK(arg1), TARGET); continue;
+ case _OP_PUSHTRAP:
+ _etraps.push_back(SQExceptionTrap(_top,_stackbase, &ci->_iv->_vals[(ci->_ip-ci->_iv->_vals)+arg1], arg0)); traps++;
+ ci->_etraps++;
+ continue;
+ case _OP_POPTRAP:{
+ for(int i=0; i<arg0; i++) {
+ _etraps.pop_back(); traps--;
+ ci->_etraps--;
+ }}
+ continue;
+ case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue;
+ case _OP_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue;
+ case _OP_NEWSLOTA:
+ _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));
+ _class(STK(arg1))->SetAttributes(STK(arg2),STK(arg2-1));
+ if(arg0 != arg3) TARGET = STK(arg3);
+ continue;
+ }
+
+ }
+ }
+exception_trap:
+ {
+ SQObjectPtr currerror = _lasterror;
+// dumpstack(_stackbase);
+ int n = 0;
+ int last_top = _top;
+ if(ci) {
+ if(traps) {
+ do {
+ if(ci->_etraps > 0) {
+ SQExceptionTrap &et = _etraps.top();
+ ci->_ip = et._ip;
+ _top = et._stacksize;
+ _stackbase = et._stackbase;
+ _stack[_stackbase+et._extarget] = currerror;
+ _etraps.pop_back(); traps--; ci->_etraps--;
+ while(last_top >= _top) _stack[last_top--].Null();
+ goto exception_restore;
+ }
+ //if is a native closure
+ if(type(ci->_closure) != OT_CLOSURE && n)
+ break;
+ if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+ PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ n++;
+ }while(_callsstack.size());
+ }
+ //call the hook
+ CallErrorHandler(currerror);
+ //remove call stack until a C function is found or the cstack is empty
+ if(ci) do{
+ bool exitafterthisone = ci->_root;
+ if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+ _stackbase -= ci->_prevstkbase;
+ _top = _stackbase + ci->_prevtop;
+ PopVarArgs(ci->_vargs);
+ POP_CALLINFO(this);
+ if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;
+ }while(_callsstack.size());
+
+ while(last_top >= _top) _stack[last_top--].Null();
+ }
+ _lasterror = currerror;
+ return false;
+ }
+ assert(0);
+}
+
+bool SQVM::CreateClassInstance(SQClass *theclass, int nargs, int stackbase, SQObjectPtr &retval)
+{
+ SQObjectPtr constr;
+ SQObjectPtr inst = theclass->CreateInstance();
+ _stack[stackbase] = inst;
+ if(theclass->Get(_ss(this)->_constructoridx,constr)) {
+ if(!Call(constr,nargs,stackbase,constr))
+ return false;
+ }
+ retval = inst;
+ return true;
+}
+
+void SQVM::CallErrorHandler(SQObjectPtr &error)
+{
+ if(type(_errorhandler) != OT_NULL) {
+ SQObjectPtr out;
+ Push(_roottable); Push(error);
+ Call(_errorhandler, 2, _top-2, out);
+ Pop(2);
+ }
+}
+
+void SQVM::CallDebugHook(int type,int forcedline)
+{
+ SQObjectPtr temp_reg;
+ int nparams=5;
+ SQFunctionProto *func=_funcproto(_closure(ci->_closure)->_function);
+ Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name);
+ Call(_debughook,nparams,_top-nparams,temp_reg);
+ Pop(nparams);
+}
+
+bool SQVM::CallNative(SQNativeClosure *nclosure,int nargs,int stackbase,bool tailcall,SQObjectPtr &retval,bool &suspend)
+{
+ (void) tailcall;
+ if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }
+ int nparamscheck = nclosure->_nparamscheck;
+ if(((nparamscheck > 0) && (nparamscheck != nargs))
+ || ((nparamscheck < 0) && (nargs < (-nparamscheck)))) {
+ Raise_Error(_SC("wrong number of parameters"));
+ return false;
+ }
+
+ int tcs;
+ if( (tcs = nclosure->_typecheck.size()) ) {
+ for(int i = 0; i < nargs && i < tcs; i++)
+ if((nclosure->_typecheck[i] != -1) && !(type(_stack[stackbase+i]) & nclosure->_typecheck[i])) {
+ Raise_ParamTypeError(i,nclosure->_typecheck[i],type(_stack[stackbase+i]));
+ return false;
+ }
+ }
+ _nnativecalls++;
+ if ((_top + MIN_STACK_OVERHEAD) > (int)_stack.size()) {
+ _stack.resize(_stack.size() + (MIN_STACK_OVERHEAD<<1));
+ }
+ int oldtop = _top;
+ int oldstackbase = _stackbase;
+ _top = stackbase + nargs;
+ PUSH_CALLINFO(this, CallInfo());
+ ci->_etraps = 0;
+ ci->_closure._unVal.pNativeClosure = nclosure;
+ ci->_closure._type = OT_NATIVECLOSURE;
+ ci->_prevstkbase = stackbase - _stackbase;
+ ci->_ncalls = 1;
+ _stackbase = stackbase;
+ //push free variables
+ int outers = nclosure->_outervalues.size();
+ for (int i = 0; i < outers; i++) {
+ Push(nclosure->_outervalues[i]);
+ }
+ ci->_prevtop = (oldtop - oldstackbase);
+ int ret = (nclosure->_function)(this);
+ _nnativecalls--;
+ suspend = false;
+ if( ret == SQ_SUSPEND_FLAG) suspend = true;
+ else if (ret < 0) {
+ _stackbase = oldstackbase;
+ _top = oldtop;
+ POP_CALLINFO(this);
+ Raise_Error(_lasterror);
+ return false;
+ }
+
+ if (ret != 0){ retval = TOP(); }
+ else { retval = _null_; }
+ _stackbase = oldstackbase;
+ _top = oldtop;
+ POP_CALLINFO(this);
+ return true;
+}
+
+bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw, bool fetchroot)
+{
+ switch(type(self)){
+ case OT_TABLE:
+ if(_table(self)->Get(key,dest))return true;
+ break;
+ case OT_ARRAY:
+ if(sq_isnumeric(key)){
+ return _array(self)->Get(tointeger(key),dest);
+ }
+ break;
+ case OT_INSTANCE:
+ if(_instance(self)->Get(key,dest)) return true;
+ break;
+ default:
+ break;
+ }
+ if(FallBackGet(self,key,dest,raw)) return true;
+
+ if(fetchroot) {
+ if(_rawval(STK(0)) == _rawval(self) &&
+ type(STK(0)) == type(self)) {
+ return _table(_roottable)->Get(key,dest);
+ }
+ }
+ return false;
+}
+
+bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw)
+{
+ switch(type(self)){
+ case OT_CLASS:
+ return _class(self)->Get(key,dest);
+ break;
+ case OT_TABLE:
+ case OT_USERDATA:
+ //delegation
+ if(_delegable(self)->_delegate) {
+ if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,raw,false))
+ return true;
+ if(raw)return false;
+ Push(self);Push(key);
+ if(CallMetaMethod(_delegable(self),MT_GET,2,dest))
+ return true;
+ }
+ if(type(self) == OT_TABLE) {
+ if(raw) return false;
+ return _table_ddel->Get(key,dest);
+ }
+ return false;
+ break;
+ case OT_ARRAY:
+ if(raw)return false;
+ return _array_ddel->Get(key,dest);
+ case OT_STRING:
+ if(sq_isnumeric(key)){
+ SQInteger n=tointeger(key);
+ if(abs(n)<_string(self)->_len){
+ if(n<0)n=_string(self)->_len-n;
+ dest=SQInteger(_stringval(self)[n]);
+ return true;
+ }
+ return false;
+ }
+ else {
+ if(raw)return false;
+ return _string_ddel->Get(key,dest);
+ }
+ break;
+ case OT_INSTANCE:
+ if(raw)return false;
+ Push(self);Push(key);
+ if(!CallMetaMethod(_delegable(self),MT_GET,2,dest)) {
+ return _instance_ddel->Get(key,dest);
+ }
+ return true;
+ case OT_INTEGER:case OT_FLOAT:case OT_BOOL:
+ if(raw)return false;
+ return _number_ddel->Get(key,dest);
+ case OT_GENERATOR:
+ if(raw)return false;
+ return _generator_ddel->Get(key,dest);
+ case OT_CLOSURE: case OT_NATIVECLOSURE:
+ if(raw)return false;
+ return _closure_ddel->Get(key,dest);
+ case OT_THREAD:
+ if(raw)return false;
+ return _thread_ddel->Get(key,dest);
+ default:return false;
+ }
+ return false;
+}
+
+bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool fetchroot)
+{
+ switch(type(self)){
+ case OT_TABLE:
+ if(_table(self)->Set(key,val))
+ return true;
+ if(_table(self)->_delegate) {
+ if(Set(_table(self)->_delegate,key,val,false)) {
+ return true;
+ }
+ }
+ //keeps going
+ case OT_USERDATA:
+ if(_delegable(self)->_delegate) {
+ SQObjectPtr t;
+ Push(self);Push(key);Push(val);
+ if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+ }
+ break;
+ case OT_INSTANCE:{
+ if(_instance(self)->Set(key,val))
+ return true;
+ SQObjectPtr t;
+ Push(self);Push(key);Push(val);
+ if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;
+ }
+ break;
+ case OT_ARRAY:
+ if(!sq_isnumeric(key)) {Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; }
+ return _array(self)->Set(tointeger(key),val);
+ default:
+ Raise_Error(_SC("trying to set '%s'"),GetTypeName(self));
+ return false;
+ }
+ if(fetchroot) {
+ if(_rawval(STK(0)) == _rawval(self) &&
+ type(STK(0)) == type(self)) {
+ return _table(_roottable)->Set(key,val);
+ }
+ }
+ return false;
+}
+
+bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target)
+{
+ SQObjectPtr temp_reg;
+ switch(type(self)){
+ case OT_TABLE:
+ target = _table(self)->Clone();
+ goto cloned_mt;
+ case OT_INSTANCE:
+ target = _instance(self)->Clone(_ss(this));
+cloned_mt:
+ if(_delegable(target)->_delegate){
+ Push(target);
+ Push(self);
+ CallMetaMethod(_delegable(target),MT_CLONED,2,temp_reg);
+ }
+ return true;
+ case OT_ARRAY:
+ target=_array(self)->Clone();
+ return true;
+ default: return false;
+ }
+}
+
+bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val)
+{
+ if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; }
+ switch(type(self)) {
+ case OT_TABLE: {
+ bool rawcall = true;
+ if(_table(self)->_delegate) {
+ SQObjectPtr res;
+ if(!_table(self)->Get(key,res)) {
+ Push(self);Push(key);Push(val);
+ rawcall = !CallMetaMethod(_table(self),MT_NEWSLOT,3,res);
+ }
+ }
+ if(rawcall) _table(self)->NewSlot(key,val); //cannot fail
+
+ break;}
+ case OT_CLASS:
+ if(!_class(self)->NewSlot(key,val)) {
+ if(_class(self)->_locked) {
+ Raise_Error(_SC("trying to modify a class that has already been instantiated"));
+ return false;
+ }
+ else {
+ SQObjectPtr oval = PrintObjVal(key);
+ Raise_Error(_SC("the property '%s' already exists"),_stringval(oval));
+ return false;
+ }
+ }
+ break;
+ default:
+ Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key));
+ return false;
+ break;
+ }
+ return true;
+}
+
+bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res)
+{
+ switch(type(self)) {
+ case OT_TABLE:
+ case OT_INSTANCE:
+ case OT_USERDATA: {
+ SQObjectPtr t;
+ bool handled = false;
+ if(_delegable(self)->_delegate) {
+ Push(self);Push(key);
+ handled = CallMetaMethod(_delegable(self),MT_DELSLOT,2,t);
+ }
+
+ if(!handled) {
+ if(type(self) == OT_TABLE) {
+ if(_table(self)->Get(key,t)) {
+ _table(self)->Remove(key);
+ }
+ else {
+ Raise_IdxError((SQObject &)key);
+ return false;
+ }
+ }
+ else {
+ Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self));
+ return false;
+ }
+ }
+ res = t;
+ }
+ break;
+ default:
+ Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self));
+ return false;
+ }
+ return true;
+}
+
+bool SQVM::Call(SQObjectPtr &closure,int nparams,int stackbase,SQObjectPtr &outres)
+{
+#ifdef _DEBUG
+int prevstackbase = _stackbase;
+#endif
+ switch(type(closure)) {
+ case OT_CLOSURE:
+ return Execute(closure, _top - nparams, nparams, stackbase,outres);
+ break;
+ case OT_NATIVECLOSURE:{
+ bool suspend;
+ return CallNative(_nativeclosure(closure), nparams, stackbase, false, outres,suspend);
+
+ }
+ break;
+ case OT_CLASS:
+ return CreateClassInstance(_class(closure),nparams,stackbase,outres);
+ break;
+ default:
+ return false;
+ }
+#ifdef _DEBUG
+ if(!_suspended) {
+ assert(_stackbase == prevstackbase);
+ }
+#endif
+ return true;
+}
+
+bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,int nparams,SQObjectPtr &outres)
+{
+ SQObjectPtr closure;
+ if(del->GetMetaMethod(mm, closure)) {
+ if(Call(closure, nparams, _top - nparams, outres)) {
+ Pop(nparams);
+ return true;
+ }
+ }
+ Pop(nparams);
+ return false;
+}
+
+void SQVM::Pop() {
+ _stack[--_top] = _null_;
+}
+void SQVM::Pop(int n) {
+ for(int i = 0; i < n; i++){
+ _stack[--_top] = _null_;
+ }
+}
+
+void SQVM::Remove(int n) {
+ n = (n >= 0)?n + _stackbase - 1:_top + n;
+ for(int i = n; i < _top; i++){
+ _stack[i] = _stack[i+1];
+ }
+ _stack[_top] = _null_;
+ _top--;
+}
+
+void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; }
+SQObjectPtr &SQVM::Top() { return _stack[_top-1]; }
+SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; }
+SQObjectPtr &SQVM::GetUp(int n) { return _stack[_top+n]; }
+SQObjectPtr &SQVM::GetAt(int n) { return _stack[n]; }
+
+#ifdef _DEBUG_DUMP
+void SQVM::dumpstack(int stackbase,bool dumpall)
+{
+ int size=dumpall?_stack.size():_top;
+ int n=0;
+ scprintf(_SC("\n>>>>stack dump<<<<\n"));
+ CallInfo &ci=_callsstack.back();
+ scprintf(_SC("IP: %d\n"),ci._ip);
+ scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase);
+ scprintf(_SC("prev top: %d\n"),ci._prevtop);
+ for(int i=0;i<size;i++){
+ SQObjectPtr &obj=_stack[i];
+ if(stackbase==i)scprintf(_SC(">"));else scprintf(_SC(" "));
+ scprintf(_SC("[%d]:"),n);
+ switch(type(obj)){
+ case OT_FLOAT: scprintf(_SC("FLOAT %.3f"),_float(obj));break;
+ case OT_INTEGER: scprintf(_SC("INTEGER %d"),_integer(obj));break;
+ case OT_BOOL: scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break;
+ case OT_STRING: scprintf(_SC("STRING %s"),_stringval(obj));break;
+ case OT_NULL: scprintf(_SC("NULL")); break;
+ case OT_TABLE: scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break;
+ case OT_ARRAY: scprintf(_SC("ARRAY %p"),_array(obj));break;
+ case OT_CLOSURE: scprintf(_SC("CLOSURE [%p]"),_closure(obj));break;
+ case OT_NATIVECLOSURE: scprintf(_SC("NATIVECLOSURE"));break;
+ case OT_USERDATA: scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break;
+ case OT_GENERATOR: scprintf(_SC("GENERATOR"));break;
+ case OT_THREAD: scprintf(_SC("THREAD [%p]"),_thread(obj));break;
+ case OT_USERPOINTER: scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break;
+ case OT_CLASS: scprintf(_SC("CLASS %p"),_class(obj));break;
+ case OT_INSTANCE: scprintf(_SC("INSTANCE %p"),_instance(obj));break;
+ default:
+ assert(0);
+ break;
+ };
+ scprintf(_SC("\n"));
+ ++n;
+ }
+}
+
+#endif