New grow and skid sounds from remaxim
[supertux.git] / src / squirrel / squirrel / sqvm.cpp
index 25ebcf9..9b1827a 100644 (file)
 
 #define TOP() (_stack._vals[_top-1])
 
+#define CLEARSTACK(_last_top) { if((_last_top) >= _top) ClearStack(_last_top); }
+void SQVM::ClearStack(SQInteger last_top)
+{
+       SQObjectType tOldType;
+       SQObjectValue unOldVal;
+       while (last_top >= _top) {
+               SQObjectPtr &o = _stack._vals[last_top--];
+               tOldType = o._type;
+               unOldVal = o._unVal;
+               o._type = OT_NULL;
+               o._unVal.pUserPointer = NULL;
+               __Release(tOldType,unOldVal);
+       }
+}
+
 bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)
 {
        SQInteger res;
@@ -49,7 +64,9 @@ bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,
                                        res = i1 / i2; 
                                        break;
                                case '*': 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;
@@ -99,6 +116,7 @@ void SQVM::Finalize()
        _errorhandler = _null_;
        _debughook = _null_;
        temp_reg = _null_;
+       _callstackdata.resize(0);
        SQInteger size=_stack.size();
        for(SQInteger i=0;i<size;i++)
                _stack[i]=_null_;
@@ -107,7 +125,7 @@ void SQVM::Finalize()
 SQVM::~SQVM()
 {
        Finalize();
-       sq_free(_callsstack,_alloccallsstacksize*sizeof(CallInfo));
+       //sq_free(_callsstack,_alloccallsstacksize*sizeof(CallInfo));
        REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);
 }
 
@@ -267,39 +285,6 @@ bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &
        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");
-       case _RT_WEAKREF: return _SC("weakref");
-       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) {
@@ -313,10 +298,10 @@ void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)
 bool SQVM::Init(SQVM *friendvm, SQInteger stacksize)
 {
        _stack.resize(stacksize);
-       //_callsstack.reserve(4);
        _alloccallsstacksize = 4;
+       _callstackdata.resize(_alloccallsstacksize);
        _callsstacksize = 0;
-       _callsstack = (CallInfo*)sq_malloc(_alloccallsstacksize*sizeof(CallInfo));
+       _callsstack = &_callstackdata[0];
        _stackbase = 0;
        _top = 0;
        if(!friendvm) 
@@ -333,16 +318,22 @@ bool SQVM::Init(SQVM *friendvm, SQInteger stacksize)
 
 extern SQInstructionDesc g_InstrDesc[];
 
-bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteger stackbase,bool tailcall)
+bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQInteger stackbase,bool tailcall)
 {
        SQFunctionProto *func = _funcproto(closure->_function);
        
        const SQInteger paramssize = func->_nparameters;
        const SQInteger newtop = stackbase + func->_stacksize;
-       
-       
+       SQInteger nargs = args;
        if (paramssize != nargs) {
-               if(func->_varparams)
+               SQInteger ndef = func->_ndefaultparams;
+               if(ndef && nargs < paramssize) {
+                       SQInteger diff = paramssize - nargs;
+                       for(SQInteger n = ndef - diff; n < ndef; n++) {
+                               _stack._vals[stackbase + (nargs++)] = closure->_defaultparams[n];
+                       }
+               }
+               else if(func->_varparams)
                {
                        if (nargs < paramssize) {
                                Raise_Error(_SC("wrong number of parameters"));
@@ -365,6 +356,7 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteg
 
        if (!tailcall) {
                CallInfo lc;
+               lc._generator = NULL;
                lc._etraps = 0;
                lc._prevstkbase = (SQInt32) ( stackbase - _stackbase );
                lc._target = (SQInt32) target;
@@ -377,9 +369,8 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteg
                ci->_ncalls++;
        }
        ci->_vargs.size = (SQInt32)(nargs - paramssize);
-       ci->_vargs.base = (SQInt32) (_vargsstack.size()-(ci->_vargs.size));
-       ci->_closure._unVal.pClosure = closure;
-       ci->_closure._type = OT_CLOSURE;
+       ci->_vargs.base = (SQInt32)(_vargsstack.size()-(ci->_vargs.size));
+       ci->_closure = closure;
        ci->_literals = func->_literals;
        ci->_ip = func->_instructions;
        //grows the stack if needed
@@ -389,6 +380,8 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger nargs,SQInteg
                
        _top = newtop;
        _stackbase = stackbase;
+       if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
+               CallDebugHook(_SC('c'));
        return true;
 }
 
@@ -419,7 +412,7 @@ bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval)
                }
        }
 
-       while (last_top >= _top) _stack._vals[last_top--].Null();
+       CLEARSTACK(last_top);
        assert(oldstackbase >= _stackbase); 
        return broot?true:false;
 }
@@ -579,6 +572,14 @@ bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)
                        }
                }
        }
+       SQInteger ndefparams;
+       if((ndefparams = func->_ndefaultparams)) {
+               closure->_defaultparams.reserve(ndefparams);
+               for(SQInteger i = 0; i < ndefparams; i++) {
+                       SQInteger spos = func->_defaultparams[i];
+                       closure->_defaultparams.push_back(_stack._vals[_stackbase + spos]);
+               }
+       }
        target = closure;
        return true;
 
@@ -678,20 +679,34 @@ bool SQVM::Execute(SQObjectPtr &closure, SQInteger target, SQInteger nargs, SQIn
        bool ct_tailcall; 
 
        switch(et) {
-               case ET_CALL: 
-                       if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) { 
+               case ET_CALL: {
+                       SQInteger last_top = _top;
+                       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 (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {
+                               SQFunctionProto *f = _funcproto(_closure(temp_reg)->_function);
+                               SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));
+                               _GUARD(gen->Yield(this));
+                               Return(1, ci->_target, temp_reg);
+                               outres = gen;
+                               CLEARSTACK(last_top);
+                               return true;
+                       }
                        ci->_root = SQTrue;
+                                         }
                        break;
                case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = SQTrue; traps += ci->_etraps; break;
                case ET_RESUME_VM:
+               case ET_RESUME_THROW_VM:
                        traps = _suspended_traps;
                        ci->_root = _suspended_root;
                        ci->_vargs = _suspend_varargs;
                        _suspended = SQFalse;
+                       if(et  == ET_RESUME_THROW_VM) { SQ_THROW(); }
                        break;
        }
        
@@ -715,7 +730,7 @@ exception_restore:
                        case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue;
                        case _OP_TAILCALL:
                                temp_reg = STK(arg1);
-                               if (type(temp_reg) == OT_CLOSURE){ 
+                               if (type(temp_reg) == OT_CLOSURE && !_funcproto(_closure(temp_reg)->_function)->_bgenerator){ 
                                        ct_tailcall = true;
                                        if(ci->_vargs.size) PopVarArgs(ci->_vargs);
                                        for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);
@@ -730,46 +745,41 @@ exception_restore:
                                        ct_stackbase = _stackbase+arg2;
 
 common_call:
+                                       SQObjectPtr clo = temp_reg;
                                        SQInteger last_top = _top;
-                                       switch (type(temp_reg)) {
+                                       switch (type(clo)) {
                                        case OT_CLOSURE:{
-                                               _GUARD(StartCall(_closure(temp_reg), ct_target, arg3, ct_stackbase, ct_tailcall));
-                                               if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {
-                                                       SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));
+                                               _GUARD(StartCall(_closure(clo), ct_target, arg3, ct_stackbase, ct_tailcall));
+                                               if (_funcproto(_closure(clo)->_function)->_bgenerator) {
+                                                       SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(clo));
                                                        _GUARD(gen->Yield(this));
-                                                       Return(1, ct_target, temp_reg);
-
-
-
-
+                                                       Return(1, ct_target, clo);
                                                        STK(ct_target) = gen;
-                                                       while (last_top >= _top) _stack._vals[last_top--].Null();
+                                                       CLEARSTACK(last_top);
                                                        continue;
                                                }
-                                               if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))
-                                                       CallDebugHook(_SC('c'));
                                                }
                                                continue;
                                        case OT_NATIVECLOSURE: {
                                                bool suspend;
-                                               _GUARD(CallNative(_nativeclosure(temp_reg), arg3, ct_stackbase, temp_reg,suspend));
+                                               _GUARD(CallNative(_nativeclosure(clo), arg3, ct_stackbase, clo,suspend));
                                                if(suspend){
                                                        _suspended = SQTrue;
                                                        _suspended_target = ct_target;
                                                        _suspended_root = ci->_root;
                                                        _suspended_traps = traps;
                                                        _suspend_varargs = ci->_vargs;
-                                                       outres = temp_reg;
+                                                       outres = clo;
                                                        return true;
                                                }
                                                if(ct_target != -1) { //skip return value for constructors
-                                                       STK(ct_target) = temp_reg;
+                                                       STK(ct_target) = clo;
                                                }
                                                                                   }
                                                continue;
                                        case OT_CLASS:{
                                                SQObjectPtr inst;
-                                               _GUARD(CreateClassInstance(_class(temp_reg),inst,temp_reg));
+                                               _GUARD(CreateClassInstance(_class(clo),inst,temp_reg));
                                                STK(ct_target) = inst;
                                                ct_target = -1; //fakes return value target so that is not overwritten by the constructor
                                                if(type(temp_reg) != OT_NULL) {
@@ -782,17 +792,17 @@ common_call:
                                        case OT_USERDATA:
                                        case OT_INSTANCE:
                                                {
-                                               Push(temp_reg);
+                                               Push(clo);
                                                for (SQInteger 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;
+                                               if (_delegable(clo) && CallMetaMethod(_delegable(clo), MT_CALL, arg3+1, clo)){
+                                                       STK(ct_target) = clo;
                                                        break;
                                                }
-                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo));
                                                SQ_THROW();
                                          }
                                        default:
-                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));
+                                               Raise_Error(_SC("attempt to call '%s'"), GetTypeName(clo));
                                                SQ_THROW();
                                        }
                                }
@@ -848,8 +858,8 @@ common_call:
                        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(ci->_generator) {
+                                       ci->_generator->Kill();
                                }
                                if(Return(arg0, arg1, temp_reg)){
                                        assert(traps==0);
@@ -915,9 +925,9 @@ common_call:
                                continue;
                        }
                        case _OP_YIELD:{
-                               if(type(ci->_generator) == OT_GENERATOR) {
+                               if(ci->_generator) {
                                        if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);
-                                       _GUARD(_generator(ci->_generator)->Yield(this));
+                                       _GUARD(ci->_generator->Yield(this));
                                        traps -= ci->_etraps;
                                        if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;
                                }
@@ -1005,13 +1015,13 @@ exception_trap:
                                                _stackbase = et._stackbase;
                                                _stack._vals[_stackbase+et._extarget] = currerror;
                                                _etraps.pop_back(); traps--; ci->_etraps--;
-                                               while(last_top >= _top) _stack._vals[last_top--].Null();
+                                               CLEARSTACK(last_top);
                                                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();
+                                       if(ci->_generator) ci->_generator->Kill();
                                        PopVarArgs(ci->_vargs);
                                        POP_CALLINFO(this);
                                        n++;
@@ -1025,7 +1035,7 @@ exception_trap:
                        //remove call stack until a C function is found or the cstack is empty
                        if(ci) do {
                                SQBool exitafterthisone = ci->_root;
-                               if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();
+                               if(ci->_generator) ci->_generator->Kill();
                                _stackbase -= ci->_prevstkbase;
                                _top = _stackbase + ci->_prevtop;
                                PopVarArgs(ci->_vargs);
@@ -1033,7 +1043,7 @@ exception_trap:
                                if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;
                        } while(_callsstacksize);
 
-                       while(last_top >= _top) _stack._vals[last_top--].Null();
+                       CLEARSTACK(last_top);
                }
                _lasterror = currerror;
                return false;
@@ -1045,8 +1055,6 @@ bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr
 {
        inst = theclass->CreateInstance();
        if(!theclass->Get(_ss(this)->_constructoridx,constructor)) {
-               //if(!Call(constr,nargs,stackbase,constr,false))
-               //      return false;
                constructor = _null_;
        }
        return true;
@@ -1098,9 +1106,9 @@ bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackb
        SQInteger oldstackbase = _stackbase;
        _top = stackbase + nargs;
        CallInfo lci;
+       lci._closure = nclosure;
+       lci._generator = NULL;
        lci._etraps = 0;
-       lci._closure._unVal.pNativeClosure = nclosure;
-       lci._closure._type = OT_NATIVECLOSURE;
        lci._prevstkbase = (SQInt32) (stackbase - _stackbase);
        lci._ncalls = 1;
        lci._prevtop = (SQInt32) (oldtop - oldstackbase);
@@ -1129,7 +1137,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackb
                return false;
        }
        
-       if (ret != 0){ retval = TOP(); }
+       if (ret != 0){ retval = TOP(); TOP().Null(); }
        else { retval = _null_; }
        _stackbase = oldstackbase;
        _top = oldtop;
@@ -1316,6 +1324,14 @@ bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObject
                if(rawcall) _table(self)->NewSlot(key,val); //cannot fail
                
                break;}
+       case OT_INSTANCE: {
+               SQObjectPtr res;
+               Push(self);Push(key);Push(val);
+               if(!CallMetaMethod(_instance(self),MT_NEWSLOT,3,res)) {
+                       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) {
@@ -1475,7 +1491,7 @@ void SQVM::dumpstack(SQInteger stackbase,bool dumpall)
                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_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;