From efebac790898f3e2ab6b350987d53c960f1b37d1 Mon Sep 17 00:00:00 2001 From: Dwachs Date: Wed, 21 Jul 2021 17:06:26 +0000 Subject: [PATCH] modifications of squirrel source code git-svn-id: svn://tron.homeunix.org/simutrans/simutrans/trunk@9958 8aca7d54-2c30-db11-9de9-000461428c89 --- squirrel/squirrel_modifications.diff | 600 +++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 squirrel/squirrel_modifications.diff diff --git a/squirrel/squirrel_modifications.diff b/squirrel/squirrel_modifications.diff new file mode 100644 index 00000000000..9413e73b1a1 --- /dev/null +++ b/squirrel/squirrel_modifications.diff @@ -0,0 +1,600 @@ +diff --git b/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc a/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc +index e569acfcf..0d196d9d7 100644 +--- b/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc ++++ a/simutrans/trunk/squirrel/sqstdlib/sqstdaux.cc +@@ -23,13 +23,14 @@ void sqstd_printcallstack(HSQUIRRELVM v) + const SQChar *src=_SC("unknown"); + if(si.funcname)fn=si.funcname; + if(si.source)src=si.source; +- pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); +- level++; ++ pf(v,_SC("* FUNCTION [%s()]
* %s"), fn,src); ++ if (si.line > 0) { ++ pf(v,_SC("* line [%d]"),si.line); + } +- level=0; +- pf(v,_SC("\nLOCALS\n")); ++ pf(v,_SC("
\n")); ++ ++ pf(v,_SC("- - LOCALS\n")); + +- for(level=0;level<10;level++){ + seq=0; + while((name = sq_getlocal(v,level,seq))) + { +@@ -37,63 +38,83 @@ void sqstd_printcallstack(HSQUIRRELVM v) + switch(sq_gettype(v,-1)) + { + case OT_NULL: +- pf(v,_SC("[%s] NULL\n"),name); ++ pf(v,_SC("- - - [%s] NULL\n"),name); + break; + case OT_INTEGER: + sq_getinteger(v,-1,&i); +- pf(v,_SC("[%s] %d\n"),name,i); ++ pf(v,_SC("- - - [%s] %d\n"),name,i); + break; + case OT_FLOAT: + sq_getfloat(v,-1,&f); +- pf(v,_SC("[%s] %.14g\n"),name,f); ++ pf(v,_SC("- - - [%s] %.14g\n"),name,f); + break; + case OT_USERPOINTER: +- pf(v,_SC("[%s] USERPOINTER\n"),name); ++ pf(v,_SC("- - - [%s] USERPOINTER\n"),name); + break; + case OT_STRING: + sq_getstring(v,-1,&s); +- pf(v,_SC("[%s] \"%s\"\n"),name,s); ++ pf(v,_SC("- - - [%s] \"%s\"\n"),name,s); + break; + case OT_TABLE: +- pf(v,_SC("[%s] TABLE\n"),name); ++ pf(v,_SC("- - - [%s] TABLE (%d entries)\n"),name, sq_getsize(v, -1)); + break; + case OT_ARRAY: +- pf(v,_SC("[%s] ARRAY\n"),name); ++ pf(v,_SC("- - - [%s] ARRAY (%d entries)\n"),name, sq_getsize(v, -1)); + break; + case OT_CLOSURE: +- pf(v,_SC("[%s] CLOSURE\n"),name); ++ pf(v,_SC("- - - [%s] CLOSURE\n"),name); + break; + case OT_NATIVECLOSURE: +- pf(v,_SC("[%s] NATIVECLOSURE\n"),name); ++ pf(v,_SC("- - - [%s] NATIVECLOSURE\n"),name); + break; + case OT_GENERATOR: +- pf(v,_SC("[%s] GENERATOR\n"),name); ++ pf(v,_SC("- - - [%s] GENERATOR\n"),name); + break; + case OT_USERDATA: +- pf(v,_SC("[%s] USERDATA\n"),name); ++ pf(v,_SC("- - - [%s] USERDATA\n"),name); + break; + case OT_THREAD: +- pf(v,_SC("[%s] THREAD\n"),name); ++ pf(v,_SC("- - - [%s] THREAD\n"),name); + break; + case OT_CLASS: +- pf(v,_SC("[%s] CLASS\n"),name); ++ pf(v,_SC("- - - [%s] CLASS\n"),name); + break; + case OT_INSTANCE: +- pf(v,_SC("[%s] INSTANCE\n"),name); ++ { ++ // try to obtain class name ++ sq_getclass(v, -1); ++ sq_pushnull(v); ++ if (SQ_SUCCEEDED(sq_getattributes(v, -2))) { ++ // stack: instance, class, attributes ++ sq_pushstring(v, "classname", -1); ++ if (SQ_SUCCEEDED(sq_get(v, -2))) { ++ const char* cn; ++ if (SQ_SUCCEEDED(sq_getstring(v, -1, &cn))) { ++ pf(v,_SC("- - - [%s] INSTANCE(%s)\n"),name, cn); ++ sq_pop(v, 3); + break; ++ } ++ } ++ sq_poptop(v); ++ } ++ sq_poptop(v); ++ pf(v,_SC("- - - [%s] INSTANCE\n"),name); ++ break; ++ } + case OT_WEAKREF: +- pf(v,_SC("[%s] WEAKREF\n"),name); ++ pf(v,_SC("- - - [%s] WEAKREF\n"),name); + break; + case OT_BOOL:{ + SQBool bval; + sq_getbool(v,-1,&bval); +- pf(v,_SC("[%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); ++ pf(v,_SC("- - - [%s] %s\n"),name,bval == SQTrue ? _SC("true"):_SC("false")); + } + break; + default: assert(0); break; + } + sq_pop(v,1); + } ++ level++; + } + } + } +@@ -102,16 +123,18 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) + { + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { ++ pf(v,_SC("")); + const SQChar *sErr = 0; + if(sq_gettop(v)>=1) { + if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { +- pf(v,_SC("\nAN ERROR HAS OCCURRED [%s]\n"),sErr); ++ pf(v,_SC("\nError: [%s]\n"),sErr); + } + else{ +- pf(v,_SC("\nAN ERROR HAS OCCURRED [unknown]\n")); ++ pf(v,_SC("\nError: [unknown]\n")); + } + sqstd_printcallstack(v); + } ++ pf(v,_SC("")); + } + return 0; + } +@@ -120,7 +143,9 @@ void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSourc + { + SQPRINTFUNCTION pf = sq_geterrorfunc(v); + if(pf) { ++ pf(v,_SC("")); + pf(v,_SC("%s line = (%d) column = (%d) : error %s\n"),sSource,line,column,sErr); ++ pf(v,_SC("")); + } + } + +diff --git b/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc a/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc +index 13d68f0c0..dac94cc62 100644 +--- b/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc ++++ a/simutrans/trunk/squirrel/sqstdlib/sqstdblob.cc +@@ -185,22 +185,6 @@ static const SQRegFunction _blob_methods[] = { + + //GLOBAL FUNCTIONS + +-static SQInteger _g_blob_casti2f(HSQUIRRELVM v) +-{ +- SQInteger i; +- sq_getinteger(v,2,&i); +- sq_pushfloat(v,*((const SQFloat *)&i)); +- return 1; +-} +- +-static SQInteger _g_blob_castf2i(HSQUIRRELVM v) +-{ +- SQFloat f; +- sq_getfloat(v,2,&f); +- sq_pushinteger(v,*((const SQInteger *)&f)); +- return 1; +-} +- + static SQInteger _g_blob_swap2(HSQUIRRELVM v) + { + SQInteger i; +@@ -231,8 +215,6 @@ static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) + + #define _DECL_GLOBALBLOB_FUNC(name,nparams,typecheck) {_SC(#name),_g_blob_##name,nparams,typecheck} + static const SQRegFunction bloblib_funcs[]={ +- _DECL_GLOBALBLOB_FUNC(casti2f,2,_SC(".n")), +- _DECL_GLOBALBLOB_FUNC(castf2i,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap2,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swap4,2,_SC(".n")), + _DECL_GLOBALBLOB_FUNC(swapfloat,2,_SC(".n")), +diff --git b/simutrans/trunk/squirrel/squirrel.h a/simutrans/trunk/squirrel/squirrel.h +index 65df9c5bb..2fb201356 100644 +--- b/simutrans/trunk/squirrel/squirrel.h ++++ a/simutrans/trunk/squirrel/squirrel.h +@@ -60,7 +60,7 @@ struct SQDelegable; + struct SQOuter; + + #ifdef _UNICODE +-#define SQUNICODE ++// #define SQUNICODE + #endif + + #include "sqconfig.h" +@@ -398,10 +399,10 @@ SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); + #define SQ_FAILED(res) (res<0) + #define SQ_SUCCEEDED(res) (res>=0) + +-#ifdef __GNUC__ +-# define SQ_UNUSED_ARG(x) x __attribute__((__unused__)) ++#ifdef XX__GNUC__ ++# define SQ_UNUSED_ARG(x) __attribute__((unused)) x + #else +-# define SQ_UNUSED_ARG(x) x ++# define SQ_UNUSED_ARG(x) + #endif + + #ifdef __cplusplus +diff --git b/simutrans/trunk/squirrel/squirrel/sqapi.cc a/simutrans/trunk/squirrel/squirrel/sqapi.cc +index 4ab6f00ba..a19cd0e39 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqapi.cc ++++ a/simutrans/trunk/squirrel/squirrel/sqapi.cc +@@ -1078,8 +1078,10 @@ SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx) + v->Pop(); + return sq_throwerror(v,_SC("rawget works only on array/table/instance and class")); + } ++ // modified: call Raise_IdxError ++ v->Raise_IdxError(v->GetUp(-1)); + v->Pop(); +- return sq_throwerror(v,_SC("the index doesn't exist")); ++ return SQ_ERROR; + } + + SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) +@@ -1172,20 +1174,33 @@ SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror) + return sq_throwerror(v,_SC("only generators can be resumed")); + } + ++/* modified for our purposes to handle suspended scripts */ + SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) + { + SQObjectPtr res; +- if(!v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false)){ +- v->Pop(params); //pop args +- return SQ_ERROR; ++ ++ if(v->_suspended) { ++ v->Pop(params);//pop closure and args ++ return sq_throwerror(v,_SC("script took to long")); ++ } ++ // we can suspend call only when vm is not already running (thus when we are not called from squirrel) ++ if(v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,raiseerror?true:false, sq_getvmstate(v)==SQ_VMSTATE_IDLE)){ ++ ++ if(!v->_suspended) { ++ v->Pop(params);//pop closure and args ++ } ++ if(retval){ ++ v->Push(res); return SQ_OK; + } +- if(!v->_suspended) +- v->Pop(params); //pop args +- if(retval) +- v->Push(res); // push result + return SQ_OK; + } ++ else { ++ v->Pop(params); ++ return SQ_ERROR; // sq_throwerror(v,_SC("call failed")); ++ } ++} + ++/* TODO check whether this works with our suspend logic; wont be missed + SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) + { + SQObjectPtr &res = v->GetUp(-(nparams + 1)); +@@ -1204,6 +1219,7 @@ SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) + } + return SQ_TAILCALL_FLAG; + } ++*/ + + SQRESULT sq_suspendvm(HSQUIRRELVM v) + { +@@ -1223,7 +1239,7 @@ SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool raiseer + v->Pop(); + } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } + SQObjectPtr dummy; +- if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM)) { ++ if(!v->Execute(dummy,-1,-1,ret,raiseerror,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM, true /*can_suspend*/)) { + return SQ_ERROR; + } + if(retval) +diff --git b/simutrans/trunk/squirrel/squirrel/sqbaselib.cc a/simutrans/trunk/squirrel/squirrel/sqbaselib.cc +index aa9f07d5e..130f2a191 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqbaselib.cc ++++ a/simutrans/trunk/squirrel/squirrel/sqbaselib.cc +@@ -955,9 +955,14 @@ static SQInteger string_find(HSQUIRRELVM v) + return 1; \ + } + ++static char toalnum(char c) ++{ ++ return isalnum(c) && c >=0 ? c : '_'; ++} + + STRING_TOFUNCZ(tolower) + STRING_TOFUNCZ(toupper) ++STRING_TOFUNCZ(toalnum) + + const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ + {_SC("len"),default_delegate_len,1, _SC("s")}, +@@ -968,6 +973,7 @@ const SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ + {_SC("find"),string_find,-2, _SC("s s n")}, + {_SC("tolower"),string_tolower,-1, _SC("s n n")}, + {_SC("toupper"),string_toupper,-1, _SC("s n n")}, ++ {_SC("toalnum"),string_toalnum,1, _SC("s")}, + {_SC("weakref"),obj_delegate_weakref,1, NULL }, + {NULL,(SQFUNCTION)0,0,NULL} + }; +@@ -990,11 +996,13 @@ static SQInteger closure_pcall(HSQUIRRELVM v) + + static SQInteger closure_call(HSQUIRRELVM v) + { ++ /* has to wait until sq_tailcall is enabled + SQObjectPtr &c = stack_get(v, -1); + if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) + { + return sq_tailcall(v, sq_gettop(v) - 1); + } ++ */ + return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; + } + +diff --git b/simutrans/trunk/squirrel/squirrel/sqcompiler.cc a/simutrans/trunk/squirrel/squirrel/sqcompiler.cc +index 3f23b9054..774d9cfbe 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqcompiler.cc ++++ a/simutrans/trunk/squirrel/squirrel/sqcompiler.cc +@@ -940,6 +940,19 @@ public: + SQInteger stackbase = _fs->PopTarget(); + SQInteger closure = _fs->PopTarget(); + _fs->AddInstruction(_OP_CALL, _fs->PushTarget(), closure, stackbase, nargs); ++#if 0 ++ /* ++ * disabled as it breaks existing scripts like ++ * ++ * local a = [] ++ * local b = a.len() ++ * // open some new block ++ * { ++ * local c = 0 ++ * } ++ * ++ * see https://github.com/albertodemichelis/squirrel/issues/177 ++ */ + if (_token == '{') + { + SQInteger retval = _fs->TopTarget(); +@@ -964,6 +977,7 @@ public: + } + Lex(); + } ++#endif + } + void ParseTableOrClass(SQInteger separator,SQInteger terminator) + { +diff --git b/simutrans/trunk/squirrel/squirrel/sqdebug.cc a/simutrans/trunk/squirrel/squirrel/sqdebug.cc +index 06fcf4ddb..dcf35c090 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqdebug.cc ++++ a/simutrans/trunk/squirrel/squirrel/sqdebug.cc +@@ -60,9 +60,14 @@ void SQVM::Raise_Error(const SQChar *s, ...) + { + va_list vl; + va_start(vl, s); ++ Raise_Error_vl(s, vl); ++ va_end(vl); ++} ++ ++void SQVM::Raise_Error_vl(const SQChar *s, va_list vl) ++{ + SQInteger buffersize = (SQInteger)scstrlen(s)+(NUMBER_MAX_CHAR*2); + scvsprintf(_sp(sq_rsl(buffersize)),buffersize, s, vl); +- va_end(vl); + _lasterror = SQString::Create(_ss(this),_spval,-1); + } + +diff --git b/simutrans/trunk/squirrel/squirrel/sqopcodes.h a/simutrans/trunk/squirrel/squirrel/sqopcodes.h +index a74893ede..4cffe1393 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqopcodes.h ++++ a/simutrans/trunk/squirrel/squirrel/sqopcodes.h +@@ -99,7 +99,7 @@ enum SQOpcode + _OP_THROW= 0x39, + _OP_NEWSLOTA= 0x3A, + _OP_GETBASE= 0x3B, +- _OP_CLOSE= 0x3C ++ _OP_CLOSE= 0x3C, + }; + + struct SQInstructionDesc { +@@ -110,7 +110,8 @@ struct SQInstruction + { + SQInstruction(){}; + SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) +- { op = (unsigned char)_op; ++ { ++ op = (unsigned char)_op; + _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; + _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; + } +diff --git b/simutrans/trunk/squirrel/squirrel/sqvm.cc a/simutrans/trunk/squirrel/squirrel/sqvm.cc +index 61a65d877..82085aad2 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqvm.cc ++++ a/simutrans/trunk/squirrel/squirrel/sqvm.cc +@@ -126,6 +126,10 @@ SQVM::SQVM(SQSharedState *ss) + ci = NULL; + _releasehook = NULL; + INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); ++ _ops_remaining = 0; ++ _ops_total = 0; ++ _ops_grace_amount = 500; ++ _throw_if_no_ops = true; + } + + void SQVM::Finalize() +@@ -386,8 +388,9 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege + { + paramssize--; + if (nargs < paramssize) { +- Raise_Error(_SC("wrong number of parameters (%d passed, at least %d required)"), +- (int)nargs, (int)paramssize); ++ const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; ++ const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; ++ Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); + return false; + } + +@@ -413,8 +416,9 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege + } + } + else { +- Raise_Error(_SC("wrong number of parameters (%d passed, %d required)"), +- (int)nargs, (int)paramssize); ++ const SQChar *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; ++ const SQChar *name = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; ++ Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s:%s"), nargs, paramssize, src, name); + return false; + } + } +@@ -679,7 +683,8 @@ bool SQVM::IsFalse(SQObjectPtr &o) + return false; + } + extern SQInstructionDesc g_InstrDesc[]; +-bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et) ++ ++bool SQVM::Execute(SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool raiseerror,ExecutionType et, SQBool can_suspend) + { + if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; } + _nnativecalls++; +@@ -717,6 +722,26 @@ exception_restore: + { + for(;;) + { ++ _ops_total++; ++ _ops_remaining --; ++ if (_ops_remaining < 0) { ++ // suspend vm ++ if (can_suspend && !_throw_if_no_ops) { ++ _suspended = SQTrue; ++ _suspended_root = ci->_root; ++ _suspended_traps = traps; ++ _suspended_target = -1; ++ return true; ++ } ++ else { ++ // throw error (if within unsuspendable function give some grace opcodes) ++ if (_throw_if_no_ops || (_ops_remaining + _ops_grace_amount < 0) ) { ++ Raise_Error(_SC("script took too long: remain = %d grac = %d"), _ops_remaining, _ops_grace_amount); ++ SQ_THROW(); ++ } ++ } ++ } ++ + const SQInstruction &_i_ = *ci->_ip++; + //dumpstack(_stackbase); + //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); +@@ -730,7 +755,11 @@ exception_restore: + #else + TARGET = (SQInteger)((SQInt32)arg1); continue; + #endif +- case _OP_LOADFLOAT: TARGET = *((const SQFloat *)&arg1); continue; ++ case _OP_LOADFLOAT: ++ SQFloat v; ++ memcpy(&v, &arg1, sizeof(SQFloat)); ++ TARGET = v; ++ continue; + case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; + case _OP_TAILCALL:{ + SQObjectPtr &t = STK(arg1); +@@ -932,7 +961,7 @@ exception_restore: + break; + case AAT_FLOAT: + val._type = OT_FLOAT; +- val._unVal.fFloat = *((const SQFloat *)&arg1); ++ memcpy(&val._unVal.fFloat, &arg1, sizeof(SQFloat)); + break; + case AAT_BOOL: + val._type = OT_BOOL; +@@ -1165,7 +1194,8 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb + if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || + ((nparamscheck < 0) && (nargs < (-nparamscheck))))) + { +- Raise_Error(_SC("wrong number of parameters")); ++ const SQChar *src = sq_type(nclosure->_name) == OT_STRING?_stringval(nclosure->_name):NULL; ++ Raise_Error(_SC("wrong number of parameters: %d provided (instead %d) in call to %s"), nargs, nparamscheck, src); + return false; + } + +@@ -1577,14 +1607,14 @@ bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr + return true; + } + +-bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror) ++bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend) + { + #ifdef _DEBUG + SQInteger prevstackbase = _stackbase; + #endif + switch(sq_type(closure)) { + case OT_CLOSURE: +- return Execute(closure, nparams, stackbase, outres, raiseerror); ++ return Execute(closure, nparams, stackbase, outres, raiseerror, ET_CALL, can_suspend); + break; + case OT_NATIVECLOSURE:{ + bool dummy; +diff --git b/simutrans/trunk/squirrel/squirrel/sqvm.h a/simutrans/trunk/squirrel/squirrel/sqvm.h +index c82e44c26..e768220e0 100644 +--- b/simutrans/trunk/squirrel/squirrel/sqvm.h ++++ a/simutrans/trunk/squirrel/squirrel/sqvm.h +@@ -55,7 +55,7 @@ public: + SQVM(SQSharedState *ss); + ~SQVM(); + bool Init(SQVM *friendvm, SQInteger stacksize); +- bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL); ++ bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL, SQBool can_suspend = false); + //starts a native call return when the NATIVE closure returns + bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); + bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); +@@ -63,7 +63,8 @@ public: + bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); + bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); + //call a generic closure pure SQUIRREL or NATIVE +- bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror); ++ // @param can_suspend is by default FALSE, only set it to true if you handle the case that vm is suspended after call. ++ bool Call(SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool raiseerror, SQBool can_suspend = false); + SQRESULT Suspend(); + + void CallDebugHook(SQInteger type,SQInteger forcedline=0); +@@ -85,6 +86,7 @@ public: + + + void Raise_Error(const SQChar *s, ...); ++ void Raise_Error_vl(const SQChar *s, va_list vl); + void Raise_Error(const SQObjectPtr &desc); + void Raise_IdxError(const SQObjectPtr &o); + void Raise_CompareError(const SQObject &o1, const SQObject &o2); +@@ -177,6 +179,11 @@ public: + SQBool _suspended_root; + SQInteger _suspended_target; + SQInteger _suspended_traps; ++ ++ SQInteger _ops_remaining; /// number of ops the vm can do till break ++ SQInteger _ops_grace_amount; /// raise error if _ops_remaining is less than -_ops_grace_amount for pure native calls ++ SQInteger _ops_total; /// total number of ops performed ++ bool _throw_if_no_ops; /// is no-ops an error or can call suspended? default: true + }; + + struct AutoDec{ +diff --git b/simutrans/trunk/squirrel/update_squirrel.sh a/simutrans/trunk/squirrel/update_squirrel.sh +index e3f41794c..a23dc59be 100755 +--- b/simutrans/trunk/squirrel/update_squirrel.sh ++++ a/simutrans/trunk/squirrel/update_squirrel.sh +@@ -41,11 +41,11 @@ for i in *.h; do gawk -f ../update_squirrel.awk "$i" > temp; mv temp "$i"; done + cd .. + + cd .. +-git diff -w --ignore-blank-lines --ignore-space-at-eol -b -R > update_squirrel.diff +-# +-# git checkout -- squirrel/squirrel +-# git checkout -- squirrel/sqstdlib +-# git checkout -- squirrel/sq*.h +-# git checkout -- simutrans/license_squirrel.txt +-# +-# patch --ignore-whitespace -p3 < update_squirrel.diff ++git diff -w --ignore-blank-lines --ignore-space-at-eol -b > update_squirrel.diff ++ ++git checkout -- squirrel/squirrel ++git checkout -- squirrel/sqstdlib ++git checkout -- squirrel/sq*.h ++git checkout -- simutrans/license_squirrel.txt ++ ++patch --ignore-whitespace -p3 < update_squirrel.diff