2012-06-14 13:06:06 +00:00
/*
see copyright notice in squirrel.h
*/
#include "sqpcheader.h"
#include <math.h>
#include <stdlib.h>
#include "sqopcodes.h"
#include "sqvm.h"
#include "sqfuncproto.h"
#include "sqclosure.h"
#include "sqstring.h"
#include "sqtable.h"
#include "squserdata.h"
#include "sqarray.h"
#include "sqclass.h"
#define TOP() (_stack._vals[_top-1])
bool SQVM :: BW_OP ( SQUnsignedInteger op , SQObjectPtr & trg , const SQObjectPtr & o1 , const SQObjectPtr & o2 )
{
SQInteger res ;
if (( type ( o1 ) | type ( o2 )) == OT_INTEGER )
{
SQInteger i1 = _integer ( o1 ), i2 = _integer ( o2 );
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 )( * (( SQUnsignedInteger * ) & 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 ;
}
#define _ARITH_(op,trg,o1,o2) \
{ \
SQInteger tmask = type(o1)|type(o2); \
switch(tmask) { \
case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \
case (OT_FLOAT|OT_INTEGER): \
case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\
default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\
} \
}
#define _ARITH_NOZERO(op,trg,o1,o2,err) \
{ \
SQInteger tmask = type(o1)|type(o2); \
switch(tmask) { \
case OT_INTEGER: { SQInteger i2 = _integer(o2); if(i2 == 0) { Raise_Error(err); SQ_THROW(); } trg = _integer(o1) op i2; } break;\
case (OT_FLOAT|OT_INTEGER): \
case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break;\
default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\
} \
}
bool SQVM :: ARITH_OP ( SQUnsignedInteger op , SQObjectPtr & trg , const SQObjectPtr & o1 , const SQObjectPtr & o2 )
{
SQInteger tmask = type ( o1 ) | type ( o2 );
switch ( tmask ) {
case OT_INTEGER :{
SQInteger res , i1 = _integer ( o1 ), i2 = _integer ( o2 );
switch ( op ) {
case '+' : res = i1 + i2 ; break ;
case '-' : res = i1 - i2 ; break ;
case '/' : if ( i2 == 0 ) { Raise_Error ( _SC ( "division by zero" )); return false ; }
res = i1 / i2 ;
break ;
case '*' : res = i1 * i2 ; break ;
case '%' : if ( i2 == 0 ) { Raise_Error ( _SC ( "modulo by zero" )); return false ; }
res = i1 % i2 ;
break ;
default : res = 0xDEADBEEF ;
}
trg = res ; }
break ;
case ( OT_FLOAT | OT_INTEGER ) :
case ( OT_FLOAT ) : {
SQFloat res , f1 = tofloat ( o1 ), f2 = tofloat ( o2 );
switch ( op ) {
case '+' : res = f1 + f2 ; break ;
case '-' : res = f1 - f2 ; break ;
case '/' : res = f1 / f2 ; break ;
case '*' : res = f1 * f2 ; break ;
case '%' : res = SQFloat ( fmod (( double ) f1 ,( double ) f2 )); break ;
default : res = 0x0f ;
}
trg = res ; }
break ;
default :
if ( op == '+' && ( tmask & _RT_STRING )){
if ( ! StringCat ( o1 , o2 , trg )) return false ;
}
else if ( ! ArithMetaMethod ( op , o1 , o2 , trg )) {
return false ;
}
}
return true ;
}
SQVM :: SQVM ( SQSharedState * ss )
{
_sharedstate = ss ;
_suspended = SQFalse ;
_suspended_target = - 1 ;
_suspended_root = SQFalse ;
_suspended_traps = - 1 ;
_foreignptr = NULL ;
_nnativecalls = 0 ;
_nmetamethodscall = 0 ;
_lasterror . Null ();
_errorhandler . Null ();
_debughook = false ;
_debughook_native = NULL ;
_debughook_closure . Null ();
_openouters = NULL ;
ci = NULL ;
INIT_CHAIN (); ADD_TO_CHAIN ( & _ss ( this ) -> _gc_chain , this );
}
void SQVM :: Finalize ()
{
if ( _openouters ) CloseOuters ( & _stack . _vals [ 0 ]);
_roottable . Null ();
_lasterror . Null ();
_errorhandler . Null ();
_debughook = false ;
_debughook_native = NULL ;
_debughook_closure . Null ();
temp_reg . Null ();
_callstackdata . resize ( 0 );
SQInteger size = _stack . size ();
for ( SQInteger i = 0 ; i < size ; i ++ )
_stack [ i ]. Null ();
}
SQVM ::~ SQVM ()
{
Finalize ();
REMOVE_FROM_CHAIN ( & _ss ( this ) -> _gc_chain , this );
}
bool SQVM :: ArithMetaMethod ( SQInteger 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 ; //shutup compiler
}
if ( is_delegable ( o1 ) && _delegable ( o1 ) -> _delegate ) {
SQObjectPtr closure ;
if ( _delegable ( o1 ) -> GetMetaMethod ( this , mm , closure )) {
Push ( o1 ); Push ( o2 );
return CallMetaMethod ( closure , mm , 2 , dest );
}
}
Raise_Error ( _SC ( "arith op %c on between '%s' and '%s'" ), op , GetTypeName ( o1 ), GetTypeName ( o2 ));
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 ) {
SQObjectPtr closure ;
if ( _delegable ( o ) -> GetMetaMethod ( this , MT_UNM , closure )) {
Push ( o );
if ( ! CallMetaMethod ( closure , MT_UNM , 1 , temp_reg )) return false ;
_Swap ( trg , temp_reg );
return true ;
}
}
default : break ; //shutup compiler
}
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 , SQInteger & result )
{
SQObjectType t1 = type ( o1 ), t2 = type ( o2 );
if ( t1 == t2 ){
if ( _rawval ( o1 ) == _rawval ( o2 )) _RET_SUCCEED ( 0 );
SQObjectPtr res ;
switch ( t1 ){
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 :
if ( _delegable ( o1 ) -> _delegate ) {
SQObjectPtr closure ;
if ( _delegable ( o1 ) -> GetMetaMethod ( this , MT_CMP , closure )) {
Push ( o1 ); Push ( o2 );
if ( ! CallMetaMethod ( closure , MT_CMP , 2 , res )) return false ;
}
break ;
}
//continues through (no break needed)
default :
_RET_SUCCEED ( _userpointer ( o1 ) < _userpointer ( o2 ) ?- 1 : 1 );
}
if ( type ( res ) != OT_INTEGER ) { Raise_CompareError ( o1 , o2 ); return false ; }
_RET_SUCCEED ( _integer ( res ));
}
else {
if ( sq_isnumeric ( o1 ) && sq_isnumeric ( o2 )){
if (( t1 == OT_INTEGER ) && ( t2 == 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 ( t1 == OT_NULL ) { _RET_SUCCEED ( - 1 );}
else if ( t2 == 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 )
{
SQInteger r ;
if ( ObjCmp ( o1 , o2 , r )) {
switch ( op ) {
case CMP_G : res = ( r > 0 ); return true ;
case CMP_GE : res = ( r >= 0 ); return true ;
case CMP_L : res = ( r < 0 ); return true ;
case CMP_LE : res = ( r <= 0 ); return true ;
case CMP_3W : res = r ; return true ;
}
assert ( 0 );
}
return false ;
}
bool SQVM :: ToString ( const SQObjectPtr & o , SQObjectPtr & res )
{
switch ( type ( o )) {
case OT_STRING :
res = o ;
return true ;
case OT_FLOAT :
scsprintf ( _sp ( rsl ( NUMBER_MAX_CHAR + 1 )), _SC ( "%g" ), _float ( o ));
break ;
case OT_INTEGER :
scsprintf ( _sp ( rsl ( NUMBER_MAX_CHAR + 1 )), _PRINT_INT_FMT , _integer ( o ));
break ;
case OT_BOOL :
scsprintf ( _sp ( rsl ( 6 )), _integer ( o ) ? _SC ( "true" ) : _SC ( "false" ));
break ;
case OT_TABLE :
case OT_USERDATA :
case OT_INSTANCE :
if ( _delegable ( o ) -> _delegate ) {
SQObjectPtr closure ;
if ( _delegable ( o ) -> GetMetaMethod ( this , MT_TOSTRING , closure )) {
Push ( o );
if ( CallMetaMethod ( closure , MT_TOSTRING , 1 , res )) {;
if ( type ( res ) == OT_STRING )
return true ;
}
else {
return false ;
}
}
}
default :
scsprintf ( _sp ( rsl ( sizeof ( void * ) + 20 )), _SC ( "(%s : 0x%p)" ), GetTypeName ( o ),( void * ) _rawval ( o ));
}
res = SQString :: Create ( _ss ( this ), _spval );
return true ;
}
bool SQVM :: StringCat ( const SQObjectPtr & str , const SQObjectPtr & obj , SQObjectPtr & dest )
{
SQObjectPtr a , b ;
if ( ! ToString ( str , a )) return false ;
if ( ! ToString ( obj , b )) return false ;
SQInteger l = _string ( a ) -> _len , ol = _string ( b ) -> _len ;
SQChar * s = _sp ( rsl ( l + ol + 1 ));
memcpy ( s , _stringval ( a ), rsl ( l ));
memcpy ( s + l , _stringval ( b ), rsl ( ol ));
dest = SQString :: Create ( _ss ( this ), _spval , l + ol );
return true ;
}
bool SQVM :: TypeOf ( const SQObjectPtr & obj1 , SQObjectPtr & dest )
{
if ( is_delegable ( obj1 ) && _delegable ( obj1 ) -> _delegate ) {
SQObjectPtr closure ;
if ( _delegable ( obj1 ) -> GetMetaMethod ( this , MT_TYPEOF , closure )) {
Push ( obj1 );
return CallMetaMethod ( closure , MT_TYPEOF , 1 , dest );
}
}
dest = SQString :: Create ( _ss ( this ), GetTypeName ( obj1 ));
return true ;
}
bool SQVM :: Init ( SQVM * friendvm , SQInteger stacksize )
{
_stack . resize ( stacksize );
_alloccallsstacksize = 4 ;
_callstackdata . resize ( _alloccallsstacksize );
_callsstacksize = 0 ;
_callsstack = & _callstackdata [ 0 ];
_stackbase = 0 ;
_top = 0 ;
if ( ! friendvm )
_roottable = SQTable :: Create ( _ss ( this ), 0 );
else {
_roottable = friendvm -> _roottable ;
_errorhandler = friendvm -> _errorhandler ;
_debughook = friendvm -> _debughook ;
_debughook_native = friendvm -> _debughook_native ;
_debughook_closure = friendvm -> _debughook_closure ;
}
sq_base_register ( this );
return true ;
}
bool SQVM :: StartCall ( SQClosure * closure , SQInteger target , SQInteger args , SQInteger stackbase , bool tailcall )
{
SQFunctionProto * func = closure -> _function ;
SQInteger paramssize = func -> _nparameters ;
const SQInteger newtop = stackbase + func -> _stacksize ;
SQInteger nargs = args ;
if ( func -> _varparams )
{
paramssize -- ;
if ( nargs < paramssize ) {
Raise_Error ( _SC ( "wrong number of parameters" ));
return false ;
}
//dumpstack(stackbase);
SQInteger nvargs = nargs - paramssize ;
SQArray * arr = SQArray :: Create ( _ss ( this ), nvargs );
SQInteger pbase = stackbase + paramssize ;
for ( SQInteger n = 0 ; n < nvargs ; n ++ ) {
arr -> _values [ n ] = _stack . _vals [ pbase ];
_stack . _vals [ pbase ]. Null ();
pbase ++ ;
}
_stack . _vals [ stackbase + paramssize ] = arr ;
//dumpstack(stackbase);
}
else if ( paramssize != nargs ) {
SQInteger ndef = func -> _ndefaultparams ;
SQInteger diff ;
if ( ndef && nargs < paramssize && ( diff = paramssize - nargs ) <= ndef ) {
for ( SQInteger n = ndef - diff ; n < ndef ; n ++ ) {
_stack . _vals [ stackbase + ( nargs ++ )] = closure -> _defaultparams [ n ];
}
}
else {
Raise_Error ( _SC ( "wrong number of parameters" ));
return false ;
}
}
if ( closure -> _env ) {
_stack . _vals [ stackbase ] = closure -> _env -> _obj ;
}
if ( ! EnterFrame ( stackbase , newtop , tailcall )) return false ;
ci -> _closure = closure ;
ci -> _literals = func -> _literals ;
ci -> _ip = func -> _instructions ;
ci -> _target = ( SQInt32 ) target ;
if ( _debughook ) {
CallDebugHook ( _SC ( 'c' ));
}
if ( closure -> _function -> _bgenerator ) {
SQFunctionProto * f = closure -> _function ;
SQGenerator * gen = SQGenerator :: Create ( _ss ( this ), closure );
if ( ! gen -> Yield ( this , f -> _stacksize ))
return false ;
SQObjectPtr temp ;
Return ( 1 , target , temp );
STK ( target ) = gen ;
}
return true ;
}
bool SQVM :: Return ( SQInteger _arg0 , SQInteger _arg1 , SQObjectPtr & retval )
{
SQBool _isroot = ci -> _root ;
SQInteger callerbase = _stackbase - ci -> _prevstkbase ;
if ( _debughook ) {
for ( SQInteger i = 0 ; i < ci -> _ncalls ; i ++ ) {
CallDebugHook ( _SC ( 'r' ));
}
}
SQObjectPtr * dest ;
if ( _isroot ) {
dest = & ( retval );
} else if ( ci -> _target == - 1 ) {
dest = NULL ;
} else {
dest = & _stack . _vals [ callerbase + ci -> _target ];
}
if ( dest ) {
if ( _arg0 != 0xFF ) {
* dest = _stack . _vals [ _stackbase + _arg1 ];
}
else {
dest -> Null ();
}
//*dest = (_arg0 != 0xFF) ? _stack._vals[_stackbase+_arg1] : _null_;
}
LeaveFrame ();
return _isroot ? true : false ;
}
#define _RET_ON_FAIL(exp) { if(!exp) return false; }
bool SQVM :: PLOCAL_INC ( SQInteger 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 ( SQInteger op , SQObjectPtr & target , SQObjectPtr & self , SQObjectPtr & key , SQObjectPtr & incr , bool postfix , SQInteger selfidx )
{
SQObjectPtr tmp , tself = self , tkey = key ;
if ( ! Get ( tself , tkey , tmp , false , selfidx )) { return false ; }
_RET_ON_FAIL ( ARITH_OP ( op , target , tmp , incr ))
if ( ! Set ( tself , tkey , target , selfidx )) { return false ; }
if ( postfix ) target = tmp ;
return true ;
}
#define arg0 (_i_._arg0)
#define sarg0 ((SQInteger)*((signed char *)&_i_._arg0))
#define arg1 (_i_._arg1)
#define sarg1 (*((SQInt32 *)&_i_._arg1))
#define arg2 (_i_._arg2)
#define arg3 (_i_._arg3)
#define sarg3 ((SQInteger)*((signed 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 ;
}
#define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; }
bool SQVM :: FOREACH_OP ( SQObjectPtr & o1 , SQObjectPtr & o2 , SQObjectPtr
& o3 , SQObjectPtr & o4 , SQInteger arg_2 , int exitpos , int & jump )
{
SQInteger nrefidx ;
switch ( type ( o1 )) {
case OT_TABLE :
if (( nrefidx = _table ( o1 ) -> Next ( false , o4 , o2 , o3 )) == - 1 ) _FINISH ( exitpos );
o4 = ( SQInteger ) nrefidx ; _FINISH ( 1 );
case OT_ARRAY :
if (( nrefidx = _array ( o1 ) -> Next ( o4 , o2 , o3 )) == - 1 ) _FINISH ( exitpos );
o4 = ( SQInteger ) nrefidx ; _FINISH ( 1 );
case OT_STRING :
if (( nrefidx = _string ( o1 ) -> Next ( o4 , o2 , o3 )) == - 1 ) _FINISH ( exitpos );
o4 = ( SQInteger ) nrefidx ; _FINISH ( 1 );
case OT_CLASS :
if (( nrefidx = _class ( o1 ) -> Next ( o4 , o2 , o3 )) == - 1 ) _FINISH ( exitpos );
o4 = ( SQInteger ) nrefidx ; _FINISH ( 1 );
case OT_USERDATA :
case OT_INSTANCE :
if ( _delegable ( o1 ) -> _delegate ) {
SQObjectPtr itr ;
SQObjectPtr closure ;
if ( _delegable ( o1 ) -> GetMetaMethod ( this , MT_NEXTI , closure )) {
Push ( o1 );
Push ( o4 );
if ( CallMetaMethod ( closure , MT_NEXTI , 2 , itr )) {
o4 = o2 = itr ;
if ( type ( itr ) == OT_NULL ) _FINISH ( exitpos );
if ( ! Get ( o1 , itr , o3 , false , DONT_FALL_BACK )) {
Raise_Error ( _SC ( "_nexti returned an invalid idx" )); // cloud be changed
return false ;
}
_FINISH ( 1 );
}
else {
return false ;
}
}
Raise_Error ( _SC ( "_nexti failed" ));
return false ;
}
break ;
case OT_GENERATOR :
if ( _generator ( o1 ) -> _state == SQGenerator :: eDead ) _FINISH ( exitpos );
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 , o3 );
_FINISH ( 0 );
}
default :
Raise_Error ( _SC ( "cannot iterate %s" ), GetTypeName ( o1 ));
}
return false ; //cannot be hit(just to avoid warnings)
}
#define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1))
#define SQ_THROW() { goto exception_trap; }
#define _GUARD(exp) { if(!exp) { SQ_THROW();} }
bool SQVM :: CLOSURE_OP ( SQObjectPtr & target , SQFunctionProto * func )
{
SQInteger nouters ;
SQClosure * closure = SQClosure :: Create ( _ss ( this ), func );
if (( nouters = func -> _noutervalues )) {
for ( SQInteger i = 0 ; i < nouters ; i ++ ) {
SQOuterVar & v = func -> _outervalues [ i ];
switch ( v . _type ){
case otLOCAL :
FindOuter ( closure -> _outervalues [ i ], & STK ( _integer ( v . _src )));
break ;
case otOUTER :
closure -> _outervalues [ i ] = _closure ( ci -> _closure ) -> _outervalues [ _integer ( v . _src )];
break ;
}
}
}
SQInteger ndefparams ;
if (( ndefparams = func -> _ndefaultparams )) {
for ( SQInteger i = 0 ; i < ndefparams ; i ++ ) {
SQInteger spos = func -> _defaultparams [ i ];
closure -> _defaultparams [ i ] = _stack . _vals [ _stackbase + spos ];
}
}
target = closure ;
return true ;
}
bool SQVM :: CLASS_OP ( SQObjectPtr & target , SQInteger baseclass , SQInteger attributes )
{
SQClass * base = NULL ;
SQObjectPtr attrs ;
if ( baseclass != - 1 ) {
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 );
if ( type ( _class ( target ) -> _metamethods [ MT_INHERITED ]) != OT_NULL ) {
int nparams = 2 ;
SQObjectPtr ret ;
Push ( target ); Push ( attrs );
Call ( _class ( target ) -> _metamethods [ MT_INHERITED ], nparams , _top - nparams , ret , false );
Pop ( nparams );
}
_class ( target ) -> _attributes = attrs ;
return true ;
}
bool SQVM :: IsEqual ( const SQObjectPtr & o1 , const SQObjectPtr & o2 , bool & res )
{
if ( type ( o1 ) == type ( o2 )) {
res = ( _rawval ( o1 ) == _rawval ( o2 ));
}
else {
if ( sq_isnumeric ( o1 ) && sq_isnumeric ( o2 )) {
res = ( tofloat ( o1 ) == tofloat ( o2 ));
}
else {
res = false ;
}
}
return true ;
}
bool SQVM :: IsFalse ( SQObjectPtr & o )
{
if ((( type ( o ) & SQOBJECT_CANBEFALSE )
&& ( (( type ( o ) == OT_FLOAT ) && ( _float ( o ) == SQFloat ( 0.0 ))) ))
#if !defined(SQUSEDOUBLE) || (defined(SQUSEDOUBLE) && defined(_SQ64))
|| ( _integer ( o ) == 0 ) ) //OT_NULL|OT_INTEGER|OT_BOOL
#else
|| ((( type ( o ) != OT_FLOAT ) && ( _integer ( o ) == 0 ))) ) //OT_NULL|OT_INTEGER|OT_BOOL
#endif
{
return true ;
}
return false ;
}
bool SQVM :: Execute ( SQObjectPtr & closure , SQInteger nargs , SQInteger stackbase , SQObjectPtr & outres , SQBool raiseerror , ExecutionType et )
{
if (( _nnativecalls + 1 ) > MAX_NATIVE_CALLS ) { Raise_Error ( _SC ( "Native stack overflow" )); return false ; }
_nnativecalls ++ ;
AutoDec ad ( & _nnativecalls );
SQInteger traps = 0 ;
CallInfo * prevci = ci ;
switch ( et ) {
case ET_CALL : {
temp_reg = closure ;
if ( ! StartCall ( _closure ( temp_reg ), _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 ;
}
if ( ci == prevci ) {
outres = STK ( _top - nargs );
return true ;
}
ci -> _root = SQTrue ;
}
break ;
case ET_RESUME_GENERATOR : _generator ( closure ) -> Resume ( this , outres ); ci -> _root = SQTrue ; traps += ci -> _etraps ; break ;
case ET_RESUME_VM :
case ET_RESUME_THROW_VM :
traps = _suspended_traps ;
ci -> _root = _suspended_root ;
_suspended = SQFalse ;
if ( et == ET_RESUME_THROW_VM ) { SQ_THROW (); }
break ;
}
exception_restore :
//
{
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 ( _debughook ) CallDebugHook ( _SC ( 'l' ), arg1 ); continue ;
case _OP_LOAD : TARGET = ci -> _literals [ arg1 ]; continue ;
case _OP_LOADINT :
#ifndef _SQ64
TARGET = ( SQInteger ) arg1 ; continue ;
#else
TARGET = ( SQInteger )(( SQUnsignedInteger32 ) arg1 ); continue ;
#endif
case _OP_LOADFLOAT : TARGET = * (( SQFloat * ) & arg1 ); continue ;
case _OP_DLOAD : TARGET = ci -> _literals [ arg1 ]; STK ( arg2 ) = ci -> _literals [ arg3 ]; continue ;
case _OP_TAILCALL :{
SQObjectPtr & t = STK ( arg1 );
if ( type ( t ) == OT_CLOSURE
&& ( ! _closure ( t ) -> _function -> _bgenerator )){
SQObjectPtr clo = t ;
if ( _openouters ) CloseOuters ( & ( _stack . _vals [ _stackbase ]));
for ( SQInteger i = 0 ; i < arg3 ; i ++ ) STK ( i ) = STK ( arg2 + i );
_GUARD ( StartCall ( _closure ( clo ), ci -> _target , arg3 , _stackbase , true ));
continue ;
}
}
case _OP_CALL : {
SQObjectPtr clo = STK ( arg1 );
switch ( type ( clo )) {
case OT_CLOSURE :
_GUARD ( StartCall ( _closure ( clo ), sarg0 , arg3 , _stackbase + arg2 , false ));
continue ;
case OT_NATIVECLOSURE : {
bool suspend ;
_GUARD ( CallNative ( _nativeclosure ( clo ), arg3 , _stackbase + arg2 , clo , suspend ));
if ( suspend ){
_suspended = SQTrue ;
_suspended_target = sarg0 ;
_suspended_root = ci -> _root ;
_suspended_traps = traps ;
outres = clo ;
return true ;
}
if ( sarg0 != - 1 ) {
STK ( arg0 ) = clo ;
}
}
continue ;
case OT_CLASS :{
SQObjectPtr inst ;
_GUARD ( CreateClassInstance ( _class ( clo ), inst , clo ));
if ( sarg0 != - 1 ) {
STK ( arg0 ) = inst ;
}
SQInteger stkbase ;
switch ( type ( clo )) {
case OT_CLOSURE :
stkbase = _stackbase + arg2 ;
_stack . _vals [ stkbase ] = inst ;
_GUARD ( StartCall ( _closure ( clo ), - 1 , arg3 , stkbase , false ));
break ;
case OT_NATIVECLOSURE :
bool suspend ;
stkbase = _stackbase + arg2 ;
_stack . _vals [ stkbase ] = inst ;
_GUARD ( CallNative ( _nativeclosure ( clo ), arg3 , stkbase , clo , suspend ));
break ;
default : break ; //shutup GCC 4.x
}
}
break ;
case OT_TABLE :
case OT_USERDATA :
case OT_INSTANCE :{
SQObjectPtr closure ;
if ( _delegable ( clo ) -> _delegate && _delegable ( clo ) -> GetMetaMethod ( this , MT_CALL , closure )) {
Push ( clo );
for ( SQInteger i = 0 ; i < arg3 ; i ++ ) Push ( STK ( arg2 + i ));
if ( ! CallMetaMethod ( closure , MT_CALL , arg3 + 1 , clo )) SQ_THROW ();
if ( sarg0 != - 1 ) {
STK ( arg0 ) = clo ;
}
break ;
}
//Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo));
//SQ_THROW();
}
default :
Raise_Error ( _SC ( "attempt to call '%s'" ), GetTypeName ( clo ));
SQ_THROW ();
}
}
continue ;
case _OP_PREPCALL :
case _OP_PREPCALLK : {
SQObjectPtr & key = _i_ . op == _OP_PREPCALLK ? ( ci -> _literals )[ arg1 ] : STK ( arg1 );
SQObjectPtr & o = STK ( arg2 );
if ( ! Get ( o , key , temp_reg , false , arg2 )) {
SQ_THROW ();
}
STK ( arg3 ) = o ;
_Swap ( TARGET , temp_reg ); //TARGET = temp_reg;
}
continue ;
case _OP_GETK :
if ( ! Get ( STK ( arg2 ), ci -> _literals [ arg1 ], temp_reg , false , arg2 )) { SQ_THROW ();}
_Swap ( TARGET , temp_reg ); //TARGET = temp_reg;
continue ;
case _OP_MOVE : TARGET = STK ( arg1 ); continue ;
case _OP_NEWSLOT :
_GUARD ( NewSlot ( STK ( arg1 ), STK ( arg2 ), STK ( arg3 ), false ));
if ( arg0 != 0xFF ) 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 ), arg1 )) { SQ_THROW (); }
if ( arg0 != 0xFF ) TARGET = STK ( arg3 );
continue ;
case _OP_GET :
if ( ! Get ( STK ( arg1 ), STK ( arg2 ), temp_reg , false , arg1 )) { SQ_THROW (); }
_Swap ( TARGET , temp_reg ); //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_ADD : _ARITH_ ( + , TARGET , STK ( arg2 ), STK ( arg1 )); continue ;
case _OP_SUB : _ARITH_ ( - , TARGET , STK ( arg2 ), STK ( arg1 )); continue ;
case _OP_MUL : _ARITH_ ( * , TARGET , STK ( arg2 ), STK ( arg1 )); continue ;
case _OP_DIV : _ARITH_NOZERO ( / , TARGET , STK ( arg2 ), STK ( arg1 ), _SC ( "division by zero" )); continue ;
case _OP_MOD : ARITH_OP ( '%' , TARGET , STK ( arg2 ), STK ( arg1 )); continue ;
case _OP_BITW : _GUARD ( BW_OP ( arg3 , TARGET , STK ( arg2 ), STK ( arg1 ))); continue ;
case _OP_RETURN :
if (( ci ) -> _generator ) {
( ci ) -> _generator -> Kill ();
}
if ( Return ( arg0 , arg1 , temp_reg )){
assert ( traps == 0 );
//outres = temp_reg;
_Swap ( outres , temp_reg );
return true ;
}
continue ;
case _OP_LOADNULLS :{ for ( SQInt32 n = 0 ; n < arg1 ; n ++ ) STK ( arg0 + n ). Null (); } continue ;
case _OP_LOADROOT : 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_JCMP :
_GUARD ( CMP_OP (( CmpOP ) arg3 , STK ( arg2 ), STK ( arg0 ), temp_reg ));
if ( IsFalse ( temp_reg )) ci -> _ip += ( sarg1 );
continue ;
case _OP_JZ : if ( IsFalse ( STK ( arg0 ))) ci -> _ip += ( sarg1 ); continue ;
case _OP_GETOUTER : {
SQClosure * cur_cls = _closure ( ci -> _closure );
SQOuter * otr = _outer ( cur_cls -> _outervalues [ arg1 ]);
TARGET = * ( otr -> _valptr );
}
continue ;
case _OP_SETOUTER : {
SQClosure * cur_cls = _closure ( ci -> _closure );
SQOuter * otr = _outer ( cur_cls -> _outervalues [ arg1 ]);
* ( otr -> _valptr ) = STK ( arg2 );
if ( arg0 != 0xFF ) {
TARGET = STK ( arg2 );
}
}
continue ;
case _OP_NEWOBJ :
switch ( arg3 ) {
case NOT_TABLE : TARGET = SQTable :: Create ( _ss ( this ), arg1 ); continue ;
case NOT_ARRAY : TARGET = SQArray :: Create ( _ss ( this ), 0 ); _array ( TARGET ) -> Reserve ( arg1 ); continue ;
case NOT_CLASS : _GUARD ( CLASS_OP ( TARGET , arg1 , arg2 )); continue ;
default : assert ( 0 ); continue ;
}
case _OP_APPENDARRAY :
{
SQObject val ;
val . _unVal . raw = 0 ;
switch ( arg2 ) {
case AAT_STACK :
val = STK ( arg1 ); break ;
case AAT_LITERAL :
val = ci -> _literals [ arg1 ]; break ;
case AAT_INT :
val . _type = OT_INTEGER ;
#ifndef _SQ64
val . _unVal . nInteger = ( SQInteger ) arg1 ;
#else
val . _unVal . nInteger = ( SQInteger )(( SQUnsignedInteger32 ) arg1 );
#endif
break ;
case AAT_FLOAT :
val . _type = OT_FLOAT ;
val . _unVal . fFloat = * (( SQFloat * ) & arg1 );
break ;
case AAT_BOOL :
val . _type = OT_BOOL ;
val . _unVal . nInteger = arg1 ;
break ;
default : assert ( 0 ); break ;
}
_array ( STK ( arg0 )) -> Append ( val ); continue ;
}
case _OP_COMPARITH : {
SQInteger selfidx = ((( SQUnsignedInteger ) arg1 & 0xFFFF0000 ) >> 16 );
_GUARD ( DerefInc ( arg3 , TARGET , STK ( selfidx ), STK ( arg2 ), STK ( arg1 & 0x0000FFFF ), false , selfidx ));
}
continue ;
case _OP_INC : { SQObjectPtr o ( sarg3 ); _GUARD ( DerefInc ( '+' , TARGET , STK ( arg1 ), STK ( arg2 ), o , false , arg1 ));} continue ;
case _OP_INCL : {
SQObjectPtr & a = STK ( arg1 );
if ( type ( a ) == OT_INTEGER ) {
a . _unVal . nInteger = _integer ( a ) + sarg3 ;
}
else {
SQObjectPtr o ( sarg3 ); //_GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));
_ARITH_ ( + , a , a , o );
}
} continue ;
case _OP_PINC : { SQObjectPtr o ( sarg3 ); _GUARD ( DerefInc ( '+' , TARGET , STK ( arg1 ), STK ( arg2 ), o , true , arg1 ));} continue ;
case _OP_PINCL : {
SQObjectPtr & a = STK ( arg1 );
if ( type ( a ) == OT_INTEGER ) {
TARGET = a ;
a . _unVal . nInteger = _integer ( a ) + sarg3 ;
}
else {
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 , DONT_FALL_BACK ) ? true : false ; continue ;
case _OP_INSTANCEOF :
if ( type ( STK ( arg1 )) != OT_CLASS )
{ Raise_Error ( _SC ( "cannot apply instanceof between a %s and a %s" ), GetTypeName ( STK ( arg1 )), GetTypeName ( STK ( arg2 ))); SQ_THROW ();}
TARGET = ( type ( STK ( arg2 )) == OT_INSTANCE ) ? ( _instance ( STK ( arg2 )) -> InstanceOf ( _class ( STK ( arg1 ))) ? true : false ) : 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 )); continue ;
case _OP_BWNOT :
if ( type ( STK ( arg1 )) == OT_INTEGER ) {
SQInteger t = _integer ( STK ( arg1 ));
TARGET = SQInteger ( ~ t );
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 ;
if ( ! CLOSURE_OP ( TARGET , fp -> _functions [ arg1 ]. _unVal . pFunctionProto )) { SQ_THROW (); }
continue ;
}
case _OP_YIELD :{
if ( ci -> _generator ) {
if ( sarg1 != MAX_FUNC_STACKSIZE ) temp_reg = STK ( arg1 );
_GUARD ( ci -> _generator -> Yield ( this , arg2 ));
traps -= ci -> _etraps ;
if ( sarg1 != MAX_FUNC_STACKSIZE ) _Swap ( STK ( arg1 ), temp_reg ); //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 , TARGET ));
traps += ci -> _etraps ;
continue ;
case _OP_FOREACH :{ int tojump ;
_GUARD ( FOREACH_OP ( STK ( arg0 ), STK ( arg2 ), STK ( arg2 + 1 ), STK ( arg2 + 2 ), arg2 , sarg1 , tojump ));
ci -> _ip += tojump ; }
continue ;
case _OP_POSTFOREACH :
assert ( type ( STK ( arg0 )) == OT_GENERATOR );
if ( _generator ( STK ( arg0 )) -> _state == SQGenerator :: eDead )
ci -> _ip += ( sarg1 - 1 );
continue ;
case _OP_CLONE : _GUARD ( Clone ( STK ( arg1 ), TARGET )); continue ;
case _OP_TYPEOF : _GUARD ( TypeOf ( STK ( arg1 ), TARGET )) continue ;
case _OP_PUSHTRAP :{
SQInstruction * _iv = _closure ( ci -> _closure ) -> _function -> _instructions ;
_etraps . push_back ( SQExceptionTrap ( _top , _stackbase , & _iv [( ci -> _ip - _iv ) + arg1 ], arg0 )); traps ++ ;
ci -> _etraps ++ ;
}
continue ;
case _OP_POPTRAP : {
for ( SQInteger i = 0 ; i < arg0 ; i ++ ) {
_etraps . pop_back (); traps -- ;
ci -> _etraps -- ;
}
}
continue ;
case _OP_THROW : Raise_Error ( TARGET ); SQ_THROW (); continue ;
case _OP_NEWSLOTA : {
bool bstatic = ( arg0 & NEW_SLOT_STATIC_FLAG ) ? true : false ;
if ( type ( STK ( arg1 )) == OT_CLASS ) {
if ( type ( _class ( STK ( arg1 )) -> _metamethods [ MT_NEWMEMBER ]) != OT_NULL ) {
Push ( STK ( arg1 )); Push ( STK ( arg2 )); Push ( STK ( arg3 ));
Push (( arg0 & NEW_SLOT_ATTRIBUTES_FLAG ) ? STK ( arg2 - 1 ) : SQObjectPtr ());
Push ( bstatic );
int nparams = 5 ;
if ( Call ( _class ( STK ( arg1 )) -> _metamethods [ MT_NEWMEMBER ], nparams , _top - nparams , temp_reg , SQFalse )) {
Pop ( nparams );
continue ;
}
else {
SQ_THROW ();
}
}
}
_GUARD ( NewSlot ( STK ( arg1 ), STK ( arg2 ), STK ( arg3 ), bstatic ));
if (( arg0 & NEW_SLOT_ATTRIBUTES_FLAG )) {
_class ( STK ( arg1 )) -> SetAttributes ( STK ( arg2 ), STK ( arg2 - 1 ));
}
}
continue ;
case _OP_GETBASE :{
SQClosure * clo = _closure ( ci -> _closure );
if ( clo -> _base ) {
TARGET = clo -> _base ;
}
else {
TARGET . Null ();
}
continue ;
}
case _OP_CLOSE :
if ( _openouters ) CloseOuters ( & ( STK ( arg1 )));
continue ;
}
}
}
exception_trap :
{
SQObjectPtr currerror = _lasterror ;
// dumpstack(_stackbase);
// SQInteger n = 0;
SQInteger last_top = _top ;
if ( _ss ( this ) -> _notifyallexceptions || ( ! traps && raiseerror )) CallErrorHandler ( currerror );
while ( ci ) {
if ( ci -> _etraps > 0 ) {
SQExceptionTrap & et = _etraps . top ();
ci -> _ip = et . _ip ;
_top = et . _stacksize ;
_stackbase = et . _stackbase ;
_stack . _vals [ _stackbase + et . _extarget ] = currerror ;
_etraps . pop_back (); traps -- ; ci -> _etraps -- ;
while ( last_top >= _top ) _stack . _vals [ last_top -- ]. Null ();
goto exception_restore ;
}
else if ( _debughook ) {
//notify debugger of a "return"
//even if it really an exception unwinding the stack
for ( SQInteger i = 0 ; i < ci -> _ncalls ; i ++ ) {
CallDebugHook ( _SC ( 'r' ));
}
}
if ( ci -> _generator ) ci -> _generator -> Kill ();
bool mustbreak = ci && ci -> _root ;
LeaveFrame ();
if ( mustbreak ) break ;
}
_lasterror = currerror ;
return false ;
}
assert ( 0 );
}
bool SQVM :: CreateClassInstance ( SQClass * theclass , SQObjectPtr & inst , SQObjectPtr & constructor )
{
inst = theclass -> CreateInstance ();
if ( ! theclass -> GetConstructor ( constructor )) {
constructor . Null ();
}
return true ;
}
void SQVM :: CallErrorHandler ( SQObjectPtr & error )
{
if ( type ( _errorhandler ) != OT_NULL ) {
SQObjectPtr out ;
Push ( _roottable ); Push ( error );
Call ( _errorhandler , 2 , _top - 2 , out , SQFalse );
Pop ( 2 );
}
}
void SQVM :: CallDebugHook ( SQInteger type , SQInteger forcedline )
{
_debughook = false ;
SQFunctionProto * func = _closure ( ci -> _closure ) -> _function ;
if ( _debughook_native ) {
const SQChar * src = type ( func -> _sourcename ) == OT_STRING ? _stringval ( func -> _sourcename ) : NULL ;
const SQChar * fname = type ( func -> _name ) == OT_STRING ? _stringval ( func -> _name ) : NULL ;
SQInteger line = forcedline ? forcedline : func -> GetLine ( ci -> _ip );
_debughook_native ( this , type , src , line , fname );
}
else {
SQObjectPtr temp_reg ;
SQInteger nparams = 5 ;
Push ( _roottable ); Push ( type ); Push ( func -> _sourcename ); Push ( forcedline ? forcedline : func -> GetLine ( ci -> _ip )); Push ( func -> _name );
Call ( _debughook_closure , nparams , _top - nparams , temp_reg , SQFalse );
Pop ( nparams );
}
_debughook = true ;
}
bool SQVM :: CallNative ( SQNativeClosure * nclosure , SQInteger nargs , SQInteger newbase , SQObjectPtr & retval , bool & suspend )
{
SQInteger nparamscheck = nclosure -> _nparamscheck ;
SQInteger newtop = newbase + nargs + nclosure -> _noutervalues ;
if ( _nnativecalls + 1 > MAX_NATIVE_CALLS ) {
Raise_Error ( _SC ( "Native stack overflow" ));
return false ;
}
if ( nparamscheck && ((( nparamscheck > 0 ) && ( nparamscheck != nargs )) ||
(( nparamscheck < 0 ) && ( nargs < ( - nparamscheck )))))
{
Raise_Error ( _SC ( "wrong number of parameters" ));
return false ;
}
SQInteger tcs ;
SQIntVec & tc = nclosure -> _typecheck ;
if (( tcs = tc . size ())) {
for ( SQInteger i = 0 ; i < nargs && i < tcs ; i ++ ) {
if (( tc . _vals [ i ] != - 1 ) && ! ( type ( _stack . _vals [ newbase + i ]) & tc . _vals [ i ])) {
Raise_ParamTypeError ( i , tc . _vals [ i ], type ( _stack . _vals [ newbase + i ]));
return false ;
}
}
}
if ( ! EnterFrame ( newbase , newtop , false )) return false ;
ci -> _closure = nclosure ;
SQInteger outers = nclosure -> _noutervalues ;
for ( SQInteger i = 0 ; i < outers ; i ++ ) {
_stack . _vals [ newbase + nargs + i ] = nclosure -> _outervalues [ i ];
}
if ( nclosure -> _env ) {
_stack . _vals [ newbase ] = nclosure -> _env -> _obj ;
}
_nnativecalls ++ ;
SQInteger ret = ( nclosure -> _function )( this );
_nnativecalls -- ;
suspend = false ;
if ( ret == SQ_SUSPEND_FLAG ) {
suspend = true ;
}
else if ( ret < 0 ) {
LeaveFrame ();
Raise_Error ( _lasterror );
return false ;
}
if ( ret ) {
retval = _stack . _vals [ _top - 1 ];
}
else {
retval . Null ();
}
//retval = ret ? _stack._vals[_top-1] : _null_;
LeaveFrame ();
return true ;
}
#define FALLBACK_OK 0
#define FALLBACK_NO_MATCH 1
#define FALLBACK_ERROR 2
bool SQVM :: Get ( const SQObjectPtr & self , const SQObjectPtr & key , SQObjectPtr & dest , bool raw , SQInteger selfidx )
{
switch ( type ( self )){
case OT_TABLE :
if ( _table ( self ) -> Get ( key , dest )) return true ;
break ;
case OT_ARRAY :
if ( sq_isnumeric ( key )) { if ( _array ( self ) -> Get ( tointeger ( key ), dest )) { return true ; } Raise_IdxError ( key ); return false ; }
break ;
case OT_INSTANCE :
if ( _instance ( self ) -> Get ( key , dest )) return true ;
break ;
case OT_CLASS :
if ( _class ( self ) -> Get ( key , dest )) return true ;
break ;
case OT_STRING :
if ( sq_isnumeric ( key )){
SQInteger n = tointeger ( key );
if ( abs (( int ) n ) < _string ( self ) -> _len ) {
if ( n < 0 ) n = _string ( self ) -> _len - n ;
dest = SQInteger ( _stringval ( self )[ n ]);
return true ;
}
Raise_IdxError ( key );
return false ;
}
break ;
default : break ; //shut up compiler
}
if ( ! raw ) {
switch ( FallBackGet ( self , key , dest )) {
case FALLBACK_OK : return true ; //okie
case FALLBACK_NO_MATCH : break ; //keep falling back
case FALLBACK_ERROR : return false ; // the metamethod failed
}
if ( InvokeDefaultDelegate ( self , key , dest )) {
return true ;
}
}
//#ifdef ROOT_FALLBACK
if ( selfidx == 0 ) {
if ( _table ( _roottable ) -> Get ( key , dest )) return true ;
}
//#endif
Raise_IdxError ( key );
return false ;
}
bool SQVM :: InvokeDefaultDelegate ( const SQObjectPtr & self , const SQObjectPtr & key , SQObjectPtr & dest )
{
SQTable * ddel = NULL ;
switch ( type ( self )) {
case OT_CLASS : ddel = _class_ddel ; break ;
case OT_TABLE : ddel = _table_ddel ; break ;
case OT_ARRAY : ddel = _array_ddel ; break ;
case OT_STRING : ddel = _string_ddel ; break ;
case OT_INSTANCE : ddel = _instance_ddel ; break ;
case OT_INTEGER : case OT_FLOAT : case OT_BOOL : ddel = _number_ddel ; break ;
case OT_GENERATOR : ddel = _generator_ddel ; break ;
case OT_CLOSURE : case OT_NATIVECLOSURE : ddel = _closure_ddel ; break ;
case OT_THREAD : ddel = _thread_ddel ; break ;
case OT_WEAKREF : ddel = _weakref_ddel ; break ;
default : return false ;
}
return ddel -> Get ( key , dest );
}
SQInteger SQVM :: FallBackGet ( const SQObjectPtr & self , const SQObjectPtr & key , SQObjectPtr & dest )
{
switch ( type ( self )){
case OT_TABLE :
case OT_USERDATA :
//delegation
if ( _delegable ( self ) -> _delegate ) {
if ( Get ( SQObjectPtr ( _delegable ( self ) -> _delegate ), key , dest , false , DONT_FALL_BACK )) return FALLBACK_OK ;
}
else {
return FALLBACK_NO_MATCH ;
}
//go through
case OT_INSTANCE : {
SQObjectPtr closure ;
if ( _delegable ( self ) -> GetMetaMethod ( this , MT_GET , closure )) {
Push ( self ); Push ( key );
_nmetamethodscall ++ ;
AutoDec ad ( & _nmetamethodscall );
if ( Call ( closure , 2 , _top - 2 , dest , SQFalse )) {
Pop ( 2 );
return FALLBACK_OK ;
}
else {
if ( type ( _lasterror ) != OT_NULL ) { //NULL means "clean failure" (not found)
//error
Pop ( 2 );
return FALLBACK_ERROR ;
}
}
}
}
break ;
default : break ; //shutup GCC 4.x
}
// no metamethod or no fallback type
return FALLBACK_NO_MATCH ;
}
bool SQVM :: Set ( const SQObjectPtr & self , const SQObjectPtr & key , const SQObjectPtr & val , SQInteger selfidx )
{
switch ( type ( self )){
case OT_TABLE :
if ( _table ( self ) -> Set ( key , val )) return true ;
break ;
case OT_INSTANCE :
if ( _instance ( self ) -> Set ( key , val )) return true ;
break ;
case OT_ARRAY :
if ( ! sq_isnumeric ( key )) { Raise_Error ( _SC ( "indexing %s with %s" ), GetTypeName ( self ), GetTypeName ( key )); return false ; }
if ( ! _array ( self ) -> Set ( tointeger ( key ), val )) {
Raise_IdxError ( key );
return false ;
}
return true ;
default :
Raise_Error ( _SC ( "trying to set '%s'" ), GetTypeName ( self ));
return false ;
}
switch ( FallBackSet ( self , key , val )) {
case FALLBACK_OK : return true ; //okie
case FALLBACK_NO_MATCH : break ; //keep falling back
case FALLBACK_ERROR : return false ; // the metamethod failed
}
if ( selfidx == 0 ) {
if ( _table ( _roottable ) -> Set ( key , val ))
return true ;
}
Raise_IdxError ( key );
return false ;
}
SQInteger SQVM :: FallBackSet ( const SQObjectPtr & self , const SQObjectPtr & key , const SQObjectPtr & val )
{
switch ( type ( self )) {
case OT_TABLE :
if ( _table ( self ) -> _delegate ) {
if ( Set ( _table ( self ) -> _delegate , key , val , DONT_FALL_BACK )) return FALLBACK_OK ;
}
//keps on going
case OT_INSTANCE :
case OT_USERDATA :{
SQObjectPtr closure ;
SQObjectPtr t ;
if ( _delegable ( self ) -> GetMetaMethod ( this , MT_SET , closure )) {
Push ( self ); Push ( key ); Push ( val );
_nmetamethodscall ++ ;
AutoDec ad ( & _nmetamethodscall );
if ( Call ( closure , 3 , _top - 3 , t , SQFalse )) {
Pop ( 3 );
return FALLBACK_OK ;
}
else {
if ( type ( _lasterror ) != OT_NULL ) { //NULL means "clean failure" (not found)
//error
Pop ( 3 );
return FALLBACK_ERROR ;
}
}
}
}
break ;
default : break ; //shutup GCC 4.x
}
// no metamethod or no fallback type
return FALLBACK_NO_MATCH ;
}
bool SQVM :: Clone ( const SQObjectPtr & self , SQObjectPtr & target )
{
SQObjectPtr temp_reg ;
SQObjectPtr newobj ;
switch ( type ( self )){
case OT_TABLE :
newobj = _table ( self ) -> Clone ();
goto cloned_mt ;
case OT_INSTANCE : {
newobj = _instance ( self ) -> Clone ( _ss ( this ));
cloned_mt :
SQObjectPtr closure ;
if ( _delegable ( newobj ) -> _delegate && _delegable ( newobj ) -> GetMetaMethod ( this , MT_CLONED , closure )) {
Push ( newobj );
Push ( self );
if ( ! CallMetaMethod ( closure , MT_CLONED , 2 , temp_reg ))
return false ;
}
}
target = newobj ;
return true ;
case OT_ARRAY :
target = _array ( self ) -> Clone ();
return true ;
default :
Raise_Error ( _SC ( "cloning a %s" ), GetTypeName ( self ));
return false ;
}
}
bool SQVM :: NewSlot ( const SQObjectPtr & self , const SQObjectPtr & key , const SQObjectPtr & val , bool bstatic )
{
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 )) {
SQObjectPtr closure ;
if ( _delegable ( self ) -> _delegate && _delegable ( self ) -> GetMetaMethod ( this , MT_NEWSLOT , closure )) {
Push ( self ); Push ( key ); Push ( val );
if ( ! CallMetaMethod ( closure , MT_NEWSLOT , 3 , res )) {
return false ;
}
rawcall = false ;
}
else {
rawcall = true ;
}
}
}
if ( rawcall ) _table ( self ) -> NewSlot ( key , val ); //cannot fail
break ;}
case OT_INSTANCE : {
SQObjectPtr res ;
SQObjectPtr closure ;
if ( _delegable ( self ) -> _delegate && _delegable ( self ) -> GetMetaMethod ( this , MT_NEWSLOT , closure )) {
Push ( self ); Push ( key ); Push ( val );
if ( ! CallMetaMethod ( closure , MT_NEWSLOT , 3 , res )) {
return false ;
}
break ;
}
Raise_Error ( _SC ( "class instances do not support the new slot operator" ));
return false ;
break ;}
case OT_CLASS :
if ( ! _class ( self ) -> NewSlot ( _ss ( this ), key , val , bstatic )) {
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;
SQObjectPtr closure ;
if ( _delegable ( self ) -> _delegate && _delegable ( self ) -> GetMetaMethod ( this , MT_DELSLOT , closure )) {
Push ( self ); Push ( key );
return CallMetaMethod ( closure , MT_DELSLOT , 2 , res );
}
else {
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 , SQInteger nparams , SQInteger stackbase , SQObjectPtr & outres , SQBool raiseerror )
{
#ifdef _DEBUG
SQInteger prevstackbase = _stackbase ;
#endif
switch ( type ( closure )) {
case OT_CLOSURE :
return Execute ( closure , nparams , stackbase , outres , raiseerror );
break ;
case OT_NATIVECLOSURE :{
bool suspend ;
return CallNative ( _nativeclosure ( closure ), nparams , stackbase , outres , suspend );
}
break ;
case OT_CLASS : {
SQObjectPtr constr ;
SQObjectPtr temp ;
CreateClassInstance ( _class ( closure ), outres , constr );
if ( type ( constr ) != OT_NULL ) {
_stack [ stackbase ] = outres ;
return Call ( constr , nparams , stackbase , temp , raiseerror );
}
return true ;
}
break ;
default :
return false ;
}
#ifdef _DEBUG
if ( ! _suspended ) {
assert ( _stackbase == prevstackbase );
}
#endif
return true ;
}
bool SQVM :: CallMetaMethod ( SQObjectPtr & closure , SQMetaMethod mm , SQInteger nparams , SQObjectPtr & outres )
{
//SQObjectPtr closure;
_nmetamethodscall ++ ;
if ( Call ( closure , nparams , _top - nparams , outres , SQFalse )) {
_nmetamethodscall -- ;
Pop ( nparams );
return true ;
}
_nmetamethodscall -- ;
//}
Pop ( nparams );
return false ;
}
void SQVM :: FindOuter ( SQObjectPtr & target , SQObjectPtr * stackindex )
{
SQOuter ** pp = & _openouters ;
SQOuter * p ;
SQOuter * otr ;
while (( p = * pp ) != NULL && p -> _valptr >= stackindex ) {
if ( p -> _valptr == stackindex ) {
target = SQObjectPtr ( p );
return ;
}
pp = & p -> _next ;
}
otr = SQOuter :: Create ( _ss ( this ), stackindex );
otr -> _next = * pp ;
otr -> _idx = ( stackindex - _stack . _vals );
__ObjAddRef ( otr );
* pp = otr ;
target = SQObjectPtr ( otr );
}
bool SQVM :: EnterFrame ( SQInteger newbase , SQInteger newtop , bool tailcall )
{
if ( ! tailcall ) {
if ( _callsstacksize == _alloccallsstacksize ) {
GrowCallStack ();
}
ci = & _callsstack [ _callsstacksize ++ ];
ci -> _prevstkbase = ( SQInt32 )( newbase - _stackbase );
ci -> _prevtop = ( SQInt32 )( _top - _stackbase );
ci -> _etraps = 0 ;
ci -> _ncalls = 1 ;
ci -> _generator = NULL ;
ci -> _root = SQFalse ;
}
else {
ci -> _ncalls ++ ;
}
_stackbase = newbase ;
_top = newtop ;
if ( newtop + MIN_STACK_OVERHEAD > ( SQInteger ) _stack . size ()) {
if ( _nmetamethodscall ) {
Raise_Error ( _SC ( "stack overflow, cannot resize stack while in a metamethod" ));
return false ;
}
_stack . resize ( _stack . size () + ( MIN_STACK_OVERHEAD << 2 ));
RelocateOuters ();
}
return true ;
}
void SQVM :: LeaveFrame () {
SQInteger last_top = _top ;
SQInteger last_stackbase = _stackbase ;
SQInteger css = -- _callsstacksize ;
/* First clean out the call stack frame */
ci -> _closure . Null ();
_stackbase -= ci -> _prevstkbase ;
_top = _stackbase + ci -> _prevtop ;
ci = ( css ) ? & _callsstack [ css - 1 ] : NULL ;
if ( _openouters ) CloseOuters ( & ( _stack . _vals [ last_stackbase ]));
while ( last_top >= _top ) {
_stack . _vals [ last_top -- ]. Null ();
}
}
void SQVM :: RelocateOuters ()
{
SQOuter * p = _openouters ;
while ( p ) {
p -> _valptr = _stack . _vals + p -> _idx ;
p = p -> _next ;
}
}
void SQVM :: CloseOuters ( SQObjectPtr * stackindex ) {
SQOuter * p ;
while (( p = _openouters ) != NULL && p -> _valptr >= stackindex ) {
p -> _value = * ( p -> _valptr );
p -> _valptr = & p -> _value ;
_openouters = p -> _next ;
__ObjRelease ( p );
}
}
void SQVM :: Remove ( SQInteger n ) {
n = ( n >= 0 ) ? n + _stackbase - 1 : _top + n ;
for ( SQInteger i = n ; i < _top ; i ++ ){
_stack [ i ] = _stack [ i + 1 ];
}
_stack [ _top ]. Null ();
_top -- ;
}
void SQVM :: Pop () {
_stack [ -- _top ]. Null ();
}
void SQVM :: Pop ( SQInteger n ) {
for ( SQInteger i = 0 ; i < n ; i ++ ){
_stack [ -- _top ]. Null ();
}
}
void SQVM :: PushNull () { _stack [ _top ++ ]. Null (); }
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 ( SQInteger n ) { return _stack [ _top + n ]; }
SQObjectPtr & SQVM :: GetAt ( SQInteger n ) { return _stack [ n ]; }
#ifdef _DEBUG_DUMP
void SQVM :: dumpstack ( SQInteger stackbase , bool dumpall )
{
SQInteger size = dumpall ? _stack . size () : _top ;
SQInteger n = 0 ;
scprintf ( _SC ( " \n >>>>stack dump<<<< \n " ));
CallInfo & ci = _callsstack [ _callsstacksize - 1 ];
scprintf ( _SC ( "IP: %p \n " ), ci . _ip );
scprintf ( _SC ( "prev stack base: %d \n " ), ci . _prevstkbase );
scprintf ( _SC ( "prev top: %d \n " ), ci . _prevtop );
for ( SQInteger 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 %p" ), _generator ( obj )); 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 ;
case OT_WEAKREF : scprintf ( _SC ( "WEAKERF %p" ), _weakref ( obj )); break ;
default :
assert ( 0 );
break ;
};
scprintf ( _SC ( " \n " ));
++ n ;
}
}
#endif