- More work on scripting interface
[supertux.git] / src / squirrel / squirrel / sqvm.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #include <math.h>\r
6 #include <stdlib.h>\r
7 #include "sqopcodes.h"\r
8 #include "sqfuncproto.h"\r
9 #include "sqvm.h"\r
10 #include "sqclosure.h"\r
11 #include "sqstring.h"\r
12 #include "sqtable.h"\r
13 #include "squserdata.h"\r
14 #include "sqarray.h"\r
15 #include "sqclass.h"\r
16 \r
17 #define TOP() (_stack[_top-1])\r
18 \r
19 bool SQVM::BW_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)\r
20 {\r
21         SQInteger res;\r
22         SQInteger i1 = _integer(o1), i2 = _integer(o2);\r
23         if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER))\r
24         {\r
25                 switch(op) {\r
26                         case BW_AND:    res = i1 & i2; break;\r
27                         case BW_OR:             res = i1 | i2; break;\r
28                         case BW_XOR:    res = i1 ^ i2; break;\r
29                         case BW_SHIFTL: res = i1 << i2; break;\r
30                         case BW_SHIFTR: res = i1 >> i2; break;\r
31                         case BW_USHIFTR:res = (SQInteger)(*((unsigned int*)&i1) >> i2); break;\r
32                         default: { Raise_Error(_SC("internal vm error bitwise op failed")); return false; }\r
33                 }\r
34         } \r
35         else { Raise_Error(_SC("bitwise op between '%s' and '%s'"),GetTypeName(o1),GetTypeName(o2)); return false;}\r
36         trg = res;\r
37         return true;\r
38 }\r
39 \r
40 bool SQVM::ARITH_OP(unsigned int op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2)\r
41 {\r
42         if(sq_isnumeric(o1) && sq_isnumeric(o2)) {\r
43                         if((type(o1)==OT_INTEGER) && (type(o2)==OT_INTEGER)) {\r
44                                 switch(op) {\r
45                                 case '+': trg = _integer(o1) + _integer(o2); break;\r
46                                 case '-': trg = _integer(o1) - _integer(o2); break;\r
47                                 case '/': if(_integer(o2) == 0) { Raise_Error(_SC("division by zero")); return false; }\r
48                                         trg = _integer(o1) / _integer(o2); \r
49                                         break;\r
50                                 case '*': trg = _integer(o1) * _integer(o2); break;\r
51                                 case '%': trg = _integer(o1) % _integer(o2); break;\r
52                                 }\r
53                         }else{\r
54                                 switch(op) {\r
55                                 case '+': trg = tofloat(o1) + tofloat(o2); break;\r
56                                 case '-': trg = tofloat(o1) - tofloat(o2); break;\r
57                                 case '/': trg = tofloat(o1) / tofloat(o2); break;\r
58                                 case '*': trg = tofloat(o1) * tofloat(o2); break;\r
59                                 case '%': trg = SQFloat(fmod((double)tofloat(o1),(double)tofloat(o2))); break;\r
60                                 }\r
61                         }       \r
62                 } else {\r
63                         if(op == '+' && (type(o1) == OT_STRING || type(o2) == OT_STRING)){\r
64                                         if(!StringCat(o1, o2, trg)) return false;\r
65                         }\r
66                         else if(!ArithMetaMethod(op,o1,o2,trg)) { \r
67                                 Raise_Error(_SC("arith op %c on between '%s' and '%s'"),op,GetTypeName(o1),GetTypeName(o2)); return false; \r
68                         }\r
69                 }\r
70                 return true;\r
71 }\r
72 \r
73 SQObjectPtr &stack_get(HSQUIRRELVM v,int idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));}\r
74 \r
75 SQVM::SQVM(SQSharedState *ss)\r
76 {\r
77         _sharedstate=ss;\r
78         _suspended=false;\r
79         _suspended_target=-1;\r
80         _suspended_root=false;\r
81         _suspended_traps=-1;\r
82         _foreignptr=NULL;\r
83         _nnativecalls=0;\r
84         _uiRef=0;\r
85         _lasterror = _null_;\r
86         _errorhandler = _null_;\r
87         _debughook = _null_;\r
88         INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this);\r
89 }\r
90 \r
91 void SQVM::Finalize()\r
92 {\r
93         _roottable = _null_;\r
94         _lasterror = _null_;\r
95         _errorhandler = _null_;\r
96         _debughook = _null_;\r
97         temp_reg = _null_;\r
98         int size=_stack.size();\r
99         for(int i=0;i<size;i++)\r
100                 _stack[i]=_null_;\r
101 }\r
102 \r
103 SQVM::~SQVM()\r
104 {\r
105         Finalize();\r
106         REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this);\r
107 }\r
108 \r
109 bool SQVM::ArithMetaMethod(int op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest)\r
110 {\r
111         SQMetaMethod mm;\r
112         switch(op){\r
113                 case _SC('+'): mm=MT_ADD; break;\r
114                 case _SC('-'): mm=MT_SUB; break;\r
115                 case _SC('/'): mm=MT_DIV; break;\r
116                 case _SC('*'): mm=MT_MUL; break;\r
117                 case _SC('%'): mm=MT_MODULO; break;\r
118         }\r
119         if(is_delegable(o1) && _delegable(o1)->_delegate) {\r
120                 Push(o1);Push(o2);\r
121                 return CallMetaMethod(_delegable(o1),mm,2,dest);\r
122         }\r
123         return false;\r
124 }\r
125 \r
126 bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o)\r
127 {\r
128         \r
129         switch(type(o)) {\r
130         case OT_INTEGER:\r
131                 trg = -_integer(o);\r
132                 return true;\r
133         case OT_FLOAT:\r
134                 trg = -_float(o);\r
135                 return true;\r
136         case OT_TABLE:\r
137         case OT_USERDATA:\r
138         case OT_INSTANCE:\r
139                 if(_delegable(o)->_delegate) {\r
140                         Push(o);\r
141                         if(CallMetaMethod(_delegable(o), MT_UNM, 1, temp_reg)) {\r
142                                 trg = temp_reg;\r
143                                 return true;\r
144                         }\r
145                 }\r
146                 return true;\r
147 \r
148         }\r
149         Raise_Error(_SC("attempt to negate a %s"), GetTypeName(o));\r
150         return false;\r
151 }\r
152 \r
153 #define _RET_SUCCEED(exp) { result = (exp); return true; } \r
154 bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,int &result)\r
155 {\r
156         if(type(o1)==type(o2)){\r
157                 if(_userpointer(o1)==_userpointer(o2))_RET_SUCCEED(0);\r
158                 SQObjectPtr res;\r
159                 switch(type(o1)){\r
160                 case OT_STRING:\r
161                         _RET_SUCCEED(scstrcmp(_stringval(o1),_stringval(o2)));\r
162                 case OT_INTEGER:\r
163                         _RET_SUCCEED(_integer(o1)-_integer(o2));\r
164                 case OT_FLOAT:\r
165                         _RET_SUCCEED((_float(o1)<_float(o2))?-1:1);\r
166                 case OT_TABLE:\r
167                 case OT_USERDATA:\r
168                 case OT_INSTANCE:\r
169                         Push(o1);Push(o2);\r
170                         if(_delegable(o1)->_delegate)CallMetaMethod(_delegable(o1),MT_CMP,2,res);\r
171                         break;\r
172                 }\r
173                 if(type(res)!=OT_INTEGER) { Raise_CompareError(o1,o2); return false; }\r
174                 _RET_SUCCEED(_integer(res));\r
175         }\r
176         else{\r
177                 if(sq_isnumeric(o1) && sq_isnumeric(o2)){\r
178                         if((type(o1)==OT_INTEGER) && (type(o2)==OT_FLOAT)) { \r
179                                 if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); }\r
180                                 else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); }\r
181                                 _RET_SUCCEED(1);\r
182                         }\r
183                         else{\r
184                                 if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); }\r
185                                 else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); }\r
186                                 _RET_SUCCEED(1);\r
187                         }\r
188                 }\r
189                 else if(type(o1)==OT_NULL) {_RET_SUCCEED(-1);}\r
190                 else if(type(o2)==OT_NULL) {_RET_SUCCEED(1);}\r
191                 else { Raise_CompareError(o1,o2); return false; }\r
192                 \r
193         }\r
194         assert(0);\r
195         _RET_SUCCEED(0); //cannot happen\r
196 }\r
197 \r
198 bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res)\r
199 {\r
200         int r;\r
201         if(ObjCmp(o1,o2,r)) {\r
202                 switch(op) {\r
203                         case CMP_G: res = (r > 0)?_true_:_false_; return true;\r
204                         case CMP_GE: res = (r >= 0)?_true_:_false_; return true;\r
205                         case CMP_L: res = (r < 0)?_true_:_false_; return true;\r
206                         case CMP_LE: res = (r <= 0)?_true_:_false_; return true;\r
207                         \r
208                 }\r
209                 assert(0);\r
210         }\r
211         return false;\r
212 }\r
213 \r
214 bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest)\r
215 {\r
216         switch(type(obj))\r
217         {\r
218         case OT_STRING:\r
219                 switch(type(str)){\r
220                 case OT_STRING: {\r
221                         int l=_string(str)->_len,ol=_string(obj)->_len;\r
222                         SQChar *s=_sp(rsl(l+ol+1));\r
223                         memcpy(s,_stringval(str),rsl(l));memcpy(s+l,_stringval(obj),rsl(ol));s[l+ol]=_SC('\0');\r
224                         break;\r
225                 }\r
226                 case OT_FLOAT:\r
227                         scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%g%s"),_float(str),_stringval(obj));\r
228                         break;\r
229                 case OT_INTEGER:\r
230                         scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(obj)->_len+1)),_SC("%d%s"),_integer(str),_stringval(obj));\r
231                         break;\r
232                 default:\r
233                         Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));\r
234                         return false;\r
235                 }\r
236                 dest=SQString::Create(_ss(this),_spval);\r
237                 break;\r
238         case OT_FLOAT:\r
239                 scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%g"),_stringval(str),_float(obj));\r
240                 dest=SQString::Create(_ss(this),_spval);\r
241                 break;\r
242         case OT_INTEGER:\r
243                 scsprintf(_sp(rsl(NUMBER_MAX_CHAR+_string(str)->_len+1)),_SC("%s%d"),_stringval(str),_integer(obj));\r
244                 dest=SQString::Create(_ss(this),_spval);\r
245                 break;\r
246         default:\r
247                 Raise_Error(_SC("string concatenation between '%s' and '%s'"),GetTypeName(str),GetTypeName(obj));\r
248                 return false;\r
249         }\r
250         return true;\r
251 }\r
252 \r
253 const SQChar *IdType2Name(SQObjectType type)\r
254 {\r
255         switch(_RAW_TYPE(type))\r
256         {\r
257         case _RT_NULL:return _SC("null");\r
258         case _RT_INTEGER:return _SC("integer");\r
259         case _RT_FLOAT:return _SC("float");\r
260         case _RT_BOOL:return _SC("bool");\r
261         case _RT_STRING:return _SC("string");\r
262         case _RT_TABLE:return _SC("table");\r
263         case _RT_ARRAY:return _SC("array");\r
264         case _RT_GENERATOR:return _SC("generator");\r
265         case _RT_CLOSURE:\r
266         case _RT_NATIVECLOSURE:\r
267                 return _SC("function");\r
268         case _RT_USERDATA:\r
269         case _RT_USERPOINTER:\r
270                 return _SC("userdata");\r
271         case _RT_THREAD: return _SC("thread");\r
272         case _RT_FUNCPROTO: return _SC("function");\r
273         case _RT_CLASS: return _SC("class");\r
274         case _RT_INSTANCE: return _SC("instance");\r
275         default:\r
276                 return NULL;\r
277         }\r
278 }\r
279 \r
280 const SQChar *GetTypeName(const SQObjectPtr &obj1)\r
281 {\r
282         return IdType2Name(type(obj1)); \r
283 }\r
284 \r
285 void SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest)\r
286 {\r
287         if(is_delegable(obj1) && _delegable(obj1)->_delegate) {\r
288                 Push(obj1);\r
289                 if(CallMetaMethod(_delegable(obj1),MT_TYPEOF,1,dest))\r
290                         return;\r
291         }\r
292         dest = SQString::Create(_ss(this),GetTypeName(obj1));\r
293 }\r
294 \r
295 bool SQVM::Init(SQVM *friendvm, int stacksize)\r
296 {\r
297         _stack.resize(stacksize);\r
298         _callsstack.reserve(4);\r
299         _stackbase = 0;\r
300         _top = 0;\r
301         if(!friendvm) \r
302                 _roottable = SQTable::Create(_ss(this), 0);\r
303         else {\r
304                 _roottable = friendvm->_roottable;\r
305                 _errorhandler = friendvm->_errorhandler;\r
306                 _debughook = friendvm->_debughook;\r
307         }\r
308         \r
309         sq_base_register(this);\r
310         return true;\r
311 }\r
312 \r
313 extern SQInstructionDesc g_InstrDesc[];\r
314 \r
315 bool SQVM::StartCall(SQClosure *closure,int target,int nargs,int stackbase,bool tailcall)\r
316 {\r
317         SQFunctionProto *func = _funcproto(closure->_function);\r
318         //const int outerssize = func->_outervalues.size();\r
319 \r
320         const int paramssize = func->_parameters.size();\r
321         const int oldtop = _top;\r
322         const int newtop = stackbase + func->_stacksize;\r
323         \r
324         \r
325         if(func->_varparams)\r
326         {\r
327                 if (nargs < paramssize) {\r
328                         Raise_Error(_SC("wrong number of parameters"));\r
329                         return false;\r
330                 }\r
331                 for(int n = 0; n < nargs - paramssize; n++) {\r
332                         _vargsstack.push_back(_stack[stackbase+paramssize+n]);\r
333                         _stack[stackbase+paramssize+n] = _null_;\r
334                 }\r
335         }\r
336         else {\r
337                 if (paramssize != nargs) {\r
338                         Raise_Error(_SC("wrong number of parameters"));\r
339                         return false;\r
340                 }\r
341                 \r
342         }\r
343         \r
344         if (!tailcall) {\r
345                 PUSH_CALLINFO(this, CallInfo());\r
346                 ci->_etraps = 0;\r
347                 ci->_prevstkbase = stackbase - _stackbase;\r
348                 ci->_target = target;\r
349                 ci->_prevtop = _top - _stackbase;\r
350                 ci->_ncalls = 1;\r
351                 ci->_root = false;\r
352         }\r
353         else {\r
354                 ci->_ncalls++;\r
355         }\r
356         ci->_vargs.size = (nargs - paramssize);\r
357         ci->_vargs.base = _vargsstack.size()-(nargs - paramssize);\r
358         ci->_closure._unVal.pClosure = closure;\r
359         ci->_closure._type = OT_CLOSURE;\r
360         ci->_iv = &func->_instructions;\r
361         ci->_literals = &func->_literals;\r
362         //grows the stack if needed\r
363         if (((unsigned int)newtop + (func->_stacksize<<1)) > _stack.size()) {\r
364                 _stack.resize(_stack.size() + (func->_stacksize<<1));\r
365         }\r
366                 \r
367         _top = newtop;\r
368         _stackbase = stackbase;\r
369         ci->_ip = ci->_iv->_vals;\r
370         return true;\r
371 }\r
372 \r
373 bool SQVM::Return(int _arg0, int _arg1, SQObjectPtr &retval)\r
374 {\r
375         if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
376                 for(int i=0;i<ci->_ncalls;i++)\r
377                         CallDebugHook(_SC('r'));\r
378                                                 \r
379         bool broot = ci->_root;\r
380         int last_top = _top;\r
381         int target = ci->_target;\r
382         int oldstackbase = _stackbase;\r
383         _stackbase -= ci->_prevstkbase;\r
384         _top = _stackbase + ci->_prevtop;\r
385         PopVarArgs(ci->_vargs);\r
386         POP_CALLINFO(this);\r
387         if (broot) {\r
388                 if (_arg0 != MAX_FUNC_STACKSIZE) retval = _stack[oldstackbase+_arg1];\r
389                 else retval = _null_;\r
390         }\r
391         else {\r
392                 if (_arg0 != MAX_FUNC_STACKSIZE)\r
393                         STK(target) = _stack[oldstackbase+_arg1];\r
394                 else\r
395                         STK(target) = _null_;\r
396         }\r
397 \r
398         while (last_top >= _top) _stack[last_top--].Null();\r
399         assert(oldstackbase >= _stackbase); \r
400         return broot;\r
401 }\r
402 \r
403 #define _RET_ON_FAIL(exp) { if(!exp) return false; }\r
404 \r
405 bool SQVM::LOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)\r
406 {\r
407         _RET_ON_FAIL(ARITH_OP( op , target, a, incr));\r
408         a = target;\r
409         return true;\r
410 }\r
411 \r
412 bool SQVM::PLOCAL_INC(int op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr)\r
413 {\r
414         SQObjectPtr trg;\r
415         _RET_ON_FAIL(ARITH_OP( op , trg, a, incr));\r
416         target = a;\r
417         a = trg;\r
418         return true;\r
419 }\r
420 \r
421 bool SQVM::DerefInc(int op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix)\r
422 {\r
423         SQObjectPtr tmp, tself = self, tkey = key;\r
424         if (!Get(tself, tkey, tmp, false, true)) { Raise_IdxError(tkey); return false; }\r
425         _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr))\r
426         Set(tself, tkey, target,true);\r
427         if (postfix) target = tmp;\r
428         return true;\r
429 }\r
430 \r
431 #define arg0 (_i_._arg0)\r
432 #define arg1 (_i_._arg1)\r
433 #define sarg1 (*((int *)&_i_._arg1))\r
434 #define arg2 (_i_._arg2)\r
435 #define arg3 (_i_._arg3)\r
436 #define sarg3 (*((char *)&_i_._arg3))\r
437 \r
438 SQRESULT SQVM::Suspend()\r
439 {\r
440         if (_suspended)\r
441                 return sq_throwerror(this, _SC("cannot suspend an already suspended vm"));\r
442         if (_nnativecalls!=2)\r
443                 return sq_throwerror(this, _SC("cannot suspend through native calls/metamethods"));\r
444         return SQ_SUSPEND_FLAG;\r
445 }\r
446 \r
447 void SQVM::PopVarArgs(VarArgs &vargs)\r
448 {\r
449         for(int n = 0; n< vargs.size; n++)\r
450                 _vargsstack.pop_back();\r
451 }\r
452 \r
453 #define _FINISH(stoploop) {finished = stoploop; return true; }\r
454 bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr \r
455 &o3,SQObjectPtr &o4,int arg_2,bool &finished)\r
456 {\r
457         int nrefidx;\r
458         switch(type(o1)) {\r
459         case OT_TABLE:\r
460                 if((nrefidx = _table(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);\r
461                 o4 = (SQInteger)nrefidx; _FINISH(false);\r
462         case OT_ARRAY:\r
463                 if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(true);\r
464                 o4 = (SQInteger) nrefidx; _FINISH(false);\r
465         case OT_STRING:\r
466                 if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);\r
467                 o4 = (SQInteger)nrefidx; _FINISH(false);\r
468         case OT_CLASS:\r
469                 if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(true);\r
470                 o4 = (SQInteger)nrefidx; _FINISH(false);\r
471         case OT_USERDATA:\r
472         case OT_INSTANCE:\r
473                 if(_delegable(o1)->_delegate) {\r
474                         SQObjectPtr itr;\r
475                         Push(o1);\r
476                         Push(o4);\r
477                         if(CallMetaMethod(_delegable(o1), MT_NEXTI, 2, itr)){\r
478                                 o4 = o2 = itr;\r
479                                 if(type(itr) == OT_NULL) _FINISH(true);\r
480                                 if(!Get(o1, itr, o3, false,false)) {\r
481                                         Raise_Error(_SC("_nexti returned an invalid idx"));\r
482                                         return false;\r
483                                 }\r
484                                 _FINISH(false);\r
485                         }\r
486                         Raise_Error(_SC("_nexti failed"));\r
487                         return false;\r
488                 }\r
489                 break;\r
490         case OT_GENERATOR:\r
491                 if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(true);\r
492                 if(_generator(o1)->_state == SQGenerator::eSuspended) {\r
493                         SQInteger idx = 0;\r
494                         if(type(o4) == OT_INTEGER) {\r
495                                 idx = _integer(o4) + 1;\r
496                         }\r
497                         o2 = idx;\r
498                         o4 = idx;\r
499                         _generator(o1)->Resume(this, arg_2+1);\r
500                         _FINISH(false);\r
501                 }\r
502         }\r
503         Raise_Error(_SC("cannot iterate %s"), GetTypeName(o1));\r
504         return false; //cannot be hit(just to avoid warnings)\r
505 }\r
506 \r
507 bool SQVM::DELEGATE_OP(SQObjectPtr &trg,SQObjectPtr &o1,SQObjectPtr &o2)\r
508 {\r
509         if(type(o1) != OT_TABLE) { Raise_Error(_SC("delegating a '%s'"), GetTypeName(o1)); return false; }\r
510         switch(type(o2)) {\r
511         case OT_TABLE:\r
512                 if(!_table(o1)->SetDelegate(_table(o2))){\r
513                         Raise_Error(_SC("delegate cycle detected"));\r
514                         return false;\r
515                 }\r
516                 break;\r
517         case OT_NULL:\r
518                 _table(o1)->SetDelegate(NULL);\r
519                 break;\r
520         default:\r
521                 Raise_Error(_SC("using '%s' as delegate"), GetTypeName(o2));\r
522                 return false;\r
523                 break;\r
524         }\r
525         trg = o1;\r
526         return true;\r
527 }\r
528 #define COND_LITERAL (arg3!=0?(*ci->_literals)[arg1]:STK(arg1))\r
529 \r
530 #define _GUARD(exp) { if(!exp) { Raise_Error(_lasterror); SQ_THROW();} }\r
531 \r
532 #define SQ_THROW() { goto exception_trap; }\r
533 \r
534 bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func)\r
535 {\r
536         int nouters;\r
537         SQClosure *closure = SQClosure::Create(_ss(this), func);\r
538         if(nouters = func->_outervalues.size()) {\r
539                 closure->_outervalues.reserve(nouters);\r
540                 for(int i = 0; i<nouters; i++) {\r
541                         SQOuterVar &v = func->_outervalues[i];\r
542                         switch(v._type){\r
543                         case otSYMBOL:\r
544                                 closure->_outervalues.push_back(_null_);\r
545                                 if(!Get(_stack._vals[_stackbase]/*STK(0)*/, v._src, closure->_outervalues.top(), false,true))\r
546                                 {Raise_IdxError(v._src); return false; }\r
547                                 break;\r
548                         case otLOCAL:\r
549                                 closure->_outervalues.push_back(_stack._vals[_stackbase+_integer(v._src)]);\r
550                                 break;\r
551                         case otOUTER:\r
552                                 closure->_outervalues.push_back(_closure(ci->_closure)->_outervalues[_integer(v._src)]);\r
553                                 break;\r
554                         }\r
555                 }\r
556         }\r
557         target = closure;\r
558         return true;\r
559 \r
560 }\r
561 \r
562 bool SQVM::GETVARGV_OP(SQObjectPtr &target,SQObjectPtr &index,CallInfo *ci)\r
563 {\r
564         if(ci->_vargs.size == 0) {\r
565                 Raise_Error(_SC("the function doesn't have var args"));\r
566                 return false;\r
567         }\r
568         if(!sq_isnumeric(index)){\r
569                 Raise_Error(_SC("indexing 'vargv' with %s"),GetTypeName(index));\r
570                 return false;\r
571         }\r
572         int idx = tointeger(index);\r
573         if(idx < 0 || idx >= ci->_vargs.size){ Raise_Error(_SC("vargv index out of range")); return false; }\r
574         target = _vargsstack[ci->_vargs.base+idx];\r
575         return true;\r
576 }\r
577 \r
578 bool SQVM::CLASS_OP(SQObjectPtr &target,int baseclass,int attributes)\r
579 {\r
580         SQClass *base = NULL;\r
581         SQObjectPtr attrs;\r
582         if(baseclass != MAX_LITERALS) {\r
583                 if(type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error(_SC("trying to inherit from a %s"),GetTypeName(_stack._vals[_stackbase+baseclass])); return false; }\r
584                 base = _class(_stack._vals[_stackbase + baseclass]);\r
585         }\r
586         if(attributes != MAX_FUNC_STACKSIZE) {\r
587                 attrs = _stack._vals[_stackbase+attributes];\r
588         }\r
589         target = SQClass::Create(_ss(this),base);\r
590         _class(target)->_attributes = attrs;\r
591         return true;\r
592 }\r
593 \r
594 bool SQVM::IsFalse(SQObjectPtr &o)\r
595 {\r
596         SQObjectType t = type(o);\r
597         if((t & SQOBJECT_CANBEFALSE)\r
598                 && ((t == OT_NULL) || ((t == OT_INTEGER || t == OT_BOOL) && _integer(o) == 0)\r
599                 || (t == OT_FLOAT && _float(o) == SQFloat(0.0)))) {\r
600                         return true;\r
601                 }\r
602         return false;\r
603 }\r
604 \r
605 bool SQVM::IsEqual(SQObjectPtr &o1,SQObjectPtr &o2,bool &res)\r
606 {\r
607         if(type(o1) == type(o2)) {\r
608                 res = ((_userpointer(o1) == _userpointer(o2)?true:false));\r
609         }\r
610         else {\r
611                 if(sq_isnumeric(o1) && sq_isnumeric(o2)) {\r
612                         int cmpres;\r
613                         if(!ObjCmp(o1, o2,cmpres)) return false;\r
614                         res = (cmpres == 0);\r
615                 }\r
616                 else {\r
617                         res = false;\r
618                 }\r
619         }\r
620         return true;\r
621 }\r
622 \r
623 bool SQVM::Execute(SQObjectPtr &closure, int target, int nargs, int stackbase,SQObjectPtr &outres, ExecutionType et)\r
624 {\r
625         if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }\r
626         _nnativecalls++;\r
627         AutoDec ad(&_nnativecalls);\r
628         int traps = 0;\r
629         //temp_reg vars for OP_CALL\r
630         int ct_target;\r
631         bool ct_tailcall; \r
632 \r
633         switch(et) {\r
634                 case ET_CALL: \r
635                         if(!StartCall(_closure(closure), _top - nargs, nargs, stackbase, false)) { \r
636                                 //call the handler if there are no calls in the stack, if not relies on the previous node\r
637                                 if(ci == NULL) CallErrorHandler(_lasterror);\r
638                                 return false;\r
639                         }\r
640                         ci->_root = true;\r
641                         break;\r
642                 case ET_RESUME_GENERATOR: _generator(closure)->Resume(this, target); ci->_root = true; traps += ci->_etraps; break;\r
643                 case ET_RESUME_VM:\r
644                         traps = _suspended_traps;\r
645                         ci->_root = _suspended_root;\r
646                         _suspended = false;\r
647                         break;\r
648         }\r
649         \r
650 exception_restore:\r
651         //SQ_TRY \r
652         {\r
653                 for(;;)\r
654                 {\r
655                         const SQInstruction &_i_ = *ci->_ip++;\r
656                         //dumpstack(_stackbase);\r
657                         //scprintf("\n[%d] %s %d %d %d %d\n",ci->_ip-ci->_iv->_vals,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3);\r
658                         switch(_i_.op)\r
659                         {\r
660                         case _OP_LINE:\r
661                                 if(type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
662                                         CallDebugHook(_SC('l'),arg1);\r
663                                 continue;\r
664                         case _OP_LOAD: TARGET = (*ci->_literals)[arg1]; continue;\r
665                         case _OP_TAILCALL:\r
666                                 temp_reg = STK(arg1);\r
667                                 if (type(temp_reg) == OT_CLOSURE){ \r
668                                         ct_tailcall = true;\r
669                                         PopVarArgs(ci->_vargs);\r
670                                         for (int i = 0; i < arg3; i++) STK(i) = STK(arg2 + i);\r
671                                         ct_target = ci->_target;\r
672                                         goto common_call;\r
673                                 }\r
674                         case _OP_CALL: {\r
675                                         ct_tailcall = false;\r
676                                         ct_target = arg0;\r
677                                         temp_reg = STK(arg1);\r
678 common_call:\r
679                                         int last_top = _top;\r
680                                         switch (type(temp_reg)) {\r
681                                         case OT_CLOSURE:{\r
682                                                 StartCall(_closure(temp_reg), ct_target, arg3, ct_tailcall?_stackbase:_stackbase+arg2, ct_tailcall);\r
683                                                 if (_funcproto(_closure(temp_reg)->_function)->_bgenerator) {\r
684                                                         SQGenerator *gen = SQGenerator::Create(_ss(this), _closure(temp_reg));\r
685                                                         _GUARD(gen->Yield(this));\r
686                                                         Return(1, ct_target, temp_reg);\r
687                                                         STK(ct_target) = gen;\r
688                                                         while (last_top >= _top) _stack[last_top--].Null();\r
689                                                         continue;\r
690                                                 }\r
691                                                 if (type(_debughook) != OT_NULL && _rawval(_debughook) != _rawval(ci->_closure))\r
692                                                         CallDebugHook(_SC('c'));\r
693                                                 }\r
694                                                 break;\r
695                                         case OT_NATIVECLOSURE: {\r
696                                                 bool suspend;\r
697                                                 _GUARD(CallNative(_nativeclosure(temp_reg), arg3, _stackbase+arg2, ct_tailcall, temp_reg,suspend));\r
698                                                 if(suspend){\r
699                                                         _suspended = true;\r
700                                                         _suspended_target = ct_target;\r
701                                                         _suspended_root = ci->_root;\r
702                                                         _suspended_traps = traps;\r
703                                                         outres = temp_reg;\r
704                                                         return true;\r
705                                                 }\r
706                                                 STK(ct_target) = temp_reg;\r
707                                                                                    }\r
708                                                 break;\r
709                                         case OT_CLASS:{\r
710                                                 _GUARD(CreateClassInstance(_class(temp_reg),arg3,_stackbase+arg2,STK(ct_target)));\r
711                                                 }\r
712                                                 break;\r
713                                         case OT_TABLE:\r
714                                         case OT_USERDATA:\r
715                                         case OT_INSTANCE:\r
716                                                 {\r
717                                                 Push(temp_reg);\r
718                                                 for (int i = 0; i < arg3; i++) Push(STK(arg2 + i));\r
719                                                 if (_delegable(temp_reg) && CallMetaMethod(_delegable(temp_reg), MT_CALL, arg3+1, temp_reg)){\r
720                                                         STK(ct_target) = temp_reg;\r
721                                                         break;\r
722                                                 }\r
723                                                 Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));\r
724                                                 SQ_THROW();\r
725                                           }\r
726                                         default:\r
727                                                 Raise_Error(_SC("attempt to call '%s'"), GetTypeName(temp_reg));\r
728                                                 SQ_THROW();\r
729                                         }\r
730                                 }\r
731                                   continue;\r
732                         case _OP_PREPCALL:\r
733                                         if (!Get(STK(arg2), STK(arg1), temp_reg, false,true))\r
734                                         { Raise_IdxError(STK(arg1)); SQ_THROW(); }\r
735                                         goto common_prepcall;\r
736                         case _OP_PREPCALLK:\r
737                                         if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg,false,true)) {\r
738                                                 if(type(STK(arg2)) == OT_CLASS) { //hack?\r
739                                                         if(_class_ddel->Get((*ci->_literals)[arg1],temp_reg)) {\r
740                                                                 STK(arg3) = STK(arg2);\r
741                                                                 TARGET = temp_reg;\r
742                                                                 continue;\r
743                                                         }\r
744                                                 }\r
745                                                 { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}\r
746                                         }\r
747 common_prepcall:\r
748                                         if(type(STK(arg2)) == OT_CLASS) {\r
749                                                 STK(arg3) = STK(0); // this\r
750                                         }\r
751                                         else {\r
752                                                 STK(arg3) = STK(arg2);\r
753                                         }\r
754                                         TARGET = temp_reg;\r
755                                 continue;\r
756                         case _OP_GETK:\r
757                                 if (!Get(STK(arg2), (*ci->_literals)[arg1], temp_reg, false,true)) { Raise_IdxError((*ci->_literals)[arg1]); SQ_THROW();}\r
758                                 TARGET = temp_reg;\r
759                                 continue;\r
760                         case _OP_MOVE: TARGET = STK(arg1); continue;\r
761                         case _OP_NEWSLOT:\r
762                                 _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));\r
763                                 if(arg0 != arg3) TARGET = STK(arg3);\r
764                                 continue;\r
765                         case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue;\r
766                         case _OP_SET:\r
767                                 if (!Set(STK(arg1), STK(arg2), STK(arg3),true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }\r
768                                 if (arg0 != arg3) TARGET = STK(arg3);\r
769                                 continue;\r
770                         case _OP_GET:\r
771                                 if (!Get(STK(arg1), STK(arg2), temp_reg, false,true)) { Raise_IdxError(STK(arg2)); SQ_THROW(); }\r
772                                 TARGET = temp_reg;\r
773                                 continue;\r
774                         case _OP_EQ:{\r
775                                 bool res;\r
776                                 if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }\r
777                                 TARGET = res?_true_:_false_;\r
778                                 }continue;\r
779                         case _OP_NE:{ \r
780                                 bool res;\r
781                                 if(!IsEqual(STK(arg2),COND_LITERAL,res)) { SQ_THROW(); }\r
782                                 TARGET = (!res)?_true_:_false_;\r
783                                 } continue;\r
784                         case _OP_ARITH: _GUARD(ARITH_OP( arg3 , temp_reg, STK(arg2), STK(arg1))); TARGET = temp_reg; continue;\r
785                         case _OP_BITW:  _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue;\r
786                         case _OP_RETURN:\r
787                                 if(type((ci)->_generator) == OT_GENERATOR) {\r
788                                         _generator((ci)->_generator)->Kill();\r
789                                 }\r
790                                 if(Return(arg0, arg1, temp_reg)){\r
791                                         assert(traps==0);\r
792                                         outres = temp_reg;\r
793                                         return true;\r
794                                 }\r
795                                 continue;\r
796                         case _OP_LOADNULLS:{ for(unsigned int n=0;n<arg1;n++) STK(arg0+n) = _null_; }continue;\r
797                         case _OP_LOADROOTTABLE: TARGET = _roottable; continue;\r
798                         case _OP_LOADBOOL: TARGET = arg1?_true_:_false_; continue;\r
799                         case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue;\r
800                         case _OP_JMP: ci->_ip += (sarg1); continue;\r
801                         case _OP_JNZ: if(!IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;\r
802                         case _OP_JZ: if(IsFalse(STK(arg0))) ci->_ip+=(sarg1); continue;\r
803                         case _OP_LOADFREEVAR: TARGET = _closure(ci->_closure)->_outervalues[arg1]; continue;\r
804                         case _OP_VARGC: TARGET = SQInteger(ci->_vargs.size); continue;\r
805                         case _OP_GETVARGV: \r
806                                 if(!GETVARGV_OP(TARGET,STK(arg1),ci)) { SQ_THROW(); } \r
807                                 continue;\r
808                         case _OP_NEWTABLE: TARGET = SQTable::Create(_ss(this), arg1); continue;\r
809                         case _OP_NEWARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue;\r
810                         case _OP_APPENDARRAY: _array(STK(arg0))->Append(COND_LITERAL);  continue;\r
811                         case _OP_GETPARENT:\r
812                                 switch(type(STK(arg1))) {\r
813                                 case OT_TABLE: \r
814                         TARGET = _table(STK(arg1))->_delegate?SQObjectPtr(_table(STK(arg1))->_delegate):_null_;\r
815                                         continue;\r
816                                 case OT_CLASS: TARGET = _class(STK(arg1))->_base?_class(STK(arg1))->_base:_null_;\r
817                                         continue;\r
818                                 }\r
819                                 Raise_Error(_SC("the %s type doesn't have a parent slot"), GetTypeName(STK(arg1)));\r
820                                 SQ_THROW();\r
821                                 continue;\r
822                         case _OP_COMPARITH: _GUARD(DerefInc(arg3, TARGET, STK((((unsigned int)arg1&0xFFFF0000)>>16)), STK(arg2), STK(arg1&0x0000FFFF), false)); continue;\r
823                         case _OP_COMPARITHL: _GUARD(LOCAL_INC(arg3, TARGET, STK(arg1), STK(arg2))); continue;\r
824                         case _OP_INC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false));} continue;\r
825                         case _OP_INCL: {SQObjectPtr o(sarg3); _GUARD(LOCAL_INC('+',TARGET, STK(arg1), o));} continue;\r
826                         case _OP_PINC: {SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true));} continue;\r
827                         case _OP_PINCL: {SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o));} continue;\r
828                         case _OP_CMP:   _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET))  continue;\r
829                         case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, true,false)?_true_:_false_;continue;\r
830                         case _OP_INSTANCEOF: \r
831                                 if(type(STK(arg1)) != OT_CLASS || type(STK(arg2)) != OT_INSTANCE)\r
832                                 {Raise_Error(_SC("cannot apply instanceof between a %s and a %s"),GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW();}\r
833                                 TARGET = _instance(STK(arg2))->InstanceOf(_class(STK(arg1)))?_true_:_false_;\r
834                                 continue;\r
835                         case _OP_AND: \r
836                                 if(IsFalse(STK(arg2))) {\r
837                                         TARGET = STK(arg2);\r
838                                         ci->_ip += (sarg1);\r
839                                 }\r
840                                 continue;\r
841                         case _OP_OR:\r
842                                 if(!IsFalse(STK(arg2))) {\r
843                                         TARGET = STK(arg2);\r
844                                         ci->_ip += (sarg1);\r
845                                 }\r
846                                 continue;\r
847                         case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue;\r
848                         case _OP_NOT: TARGET = (IsFalse(STK(arg1))?_true_:_false_); continue;\r
849                         case _OP_BWNOT:\r
850                                 if(type(STK(arg1)) == OT_INTEGER) {\r
851                                         TARGET = SQInteger(~_integer(STK(arg1)));\r
852                                         continue;\r
853                                 }\r
854                                 Raise_Error(_SC("attempt to perform a bitwise op on a %s"), GetTypeName(STK(arg1)));\r
855                                 SQ_THROW();\r
856                         case _OP_CLOSURE: {\r
857                                 SQClosure *c = ci->_closure._unVal.pClosure;\r
858                                 SQFunctionProto *fp = c->_function._unVal.pFunctionProto;\r
859                                 if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); }\r
860                                 continue;\r
861                         }\r
862                         case _OP_YIELD:{\r
863                                 if(type(ci->_generator) == OT_GENERATOR) {\r
864                                         if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1);\r
865                                         _GUARD(_generator(ci->_generator)->Yield(this));\r
866                                         traps -= ci->_etraps;\r
867                                         if(sarg1 != MAX_FUNC_STACKSIZE) STK(arg1) = temp_reg;\r
868                                 }\r
869                                 else { Raise_Error(_SC("trying to yield a '%s',only genenerator can be yielded"), GetTypeName(ci->_generator)); SQ_THROW();}\r
870                                 if(Return(arg0, arg1, temp_reg)){\r
871                                         assert(traps==0);\r
872                                         outres = temp_reg;\r
873                                         return true;\r
874                                 }\r
875                                         \r
876                                 }\r
877                                 continue;\r
878                         case _OP_RESUME:\r
879                                 if(type(STK(arg1)) != OT_GENERATOR){ Raise_Error(_SC("trying to resume a '%s',only genenerator can be resumed"), GetTypeName(STK(arg1))); SQ_THROW();}\r
880                                 _GUARD(_generator(STK(arg1))->Resume(this, arg0));\r
881                                 traps += ci->_etraps;\r
882                 continue;\r
883                         case _OP_FOREACH:{ bool finished;\r
884                                 _GUARD(FOREACH_OP(STK(arg0),STK(arg2),STK(arg2+1),STK(arg2+2),arg2,finished));\r
885                                 if(finished) ci->_ip += sarg1; }\r
886                                 continue;\r
887                         case _OP_DELEGATE: _GUARD(DELEGATE_OP(TARGET,STK(arg1),STK(arg2))); continue;\r
888                         case _OP_CLONE:\r
889                                 if(!Clone(STK(arg1), TARGET))\r
890                                 { Raise_Error(_SC("cloning a %s"), GetTypeName(STK(arg1))); SQ_THROW();}\r
891                                 continue;\r
892                         case _OP_TYPEOF: TypeOf(STK(arg1), TARGET); continue;\r
893                         case _OP_PUSHTRAP:\r
894                                 _etraps.push_back(SQExceptionTrap(_top,_stackbase, &ci->_iv->_vals[(ci->_ip-ci->_iv->_vals)+arg1], arg0)); traps++;\r
895                                 ci->_etraps++;\r
896                                 continue;\r
897                         case _OP_POPTRAP:{\r
898                                 for(int i=0; i<arg0; i++) {\r
899                                         _etraps.pop_back(); traps--;\r
900                                         ci->_etraps--;\r
901                                 }}\r
902                                 continue;\r
903                         case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue;\r
904                         case _OP_CLASS: _GUARD(CLASS_OP(TARGET,arg1,arg2)); continue;\r
905                         case _OP_NEWSLOTA:\r
906                                 _GUARD(NewSlot(STK(arg1), STK(arg2), STK(arg3)));\r
907                                 _class(STK(arg1))->SetAttributes(STK(arg2),STK(arg2-1));\r
908                                 if(arg0 != arg3) TARGET = STK(arg3);\r
909                                 continue;\r
910                         }\r
911                         \r
912                 }\r
913         }\r
914 exception_trap:\r
915         {\r
916                 SQObjectPtr currerror = _lasterror;\r
917 //              dumpstack(_stackbase);\r
918                 int n = 0;\r
919                 int last_top = _top;\r
920                 if(ci) {\r
921                         if(traps) {\r
922                                 do {\r
923                                         if(ci->_etraps > 0) {\r
924                                                 SQExceptionTrap &et = _etraps.top();\r
925                                                 ci->_ip = et._ip;\r
926                                                 _top = et._stacksize;\r
927                                                 _stackbase = et._stackbase;\r
928                                                 _stack[_stackbase+et._extarget] = currerror;\r
929                                                 _etraps.pop_back(); traps--; ci->_etraps--;\r
930                                                 while(last_top >= _top) _stack[last_top--].Null();\r
931                                                 goto exception_restore;\r
932                                         }\r
933                                         //if is a native closure\r
934                                         if(type(ci->_closure) != OT_CLOSURE && n)\r
935                                                 break;\r
936                                         if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();\r
937                                         PopVarArgs(ci->_vargs);\r
938                                         POP_CALLINFO(this);\r
939                                         n++;\r
940                                 }while(_callsstack.size());\r
941                         }\r
942                         //call the hook\r
943                         CallErrorHandler(currerror);\r
944                         //remove call stack until a C function is found or the cstack is empty\r
945                         if(ci) do{\r
946                                 bool exitafterthisone = ci->_root;\r
947                                 if(type(ci->_generator) == OT_GENERATOR) _generator(ci->_generator)->Kill();\r
948                                 _stackbase -= ci->_prevstkbase;\r
949                                 _top = _stackbase + ci->_prevtop;\r
950                                 PopVarArgs(ci->_vargs);\r
951                                 POP_CALLINFO(this);\r
952                                 if( (ci && type(ci->_closure) != OT_CLOSURE) || exitafterthisone) break;\r
953                         }while(_callsstack.size());\r
954 \r
955                         while(last_top >= _top) _stack[last_top--].Null();\r
956                 }\r
957                 _lasterror = currerror;\r
958                 return false;\r
959         }\r
960         assert(0);\r
961 }\r
962 \r
963 bool SQVM::CreateClassInstance(SQClass *theclass, int nargs, int stackbase, SQObjectPtr &retval)\r
964 {\r
965         SQObjectPtr constr;\r
966         SQObjectPtr inst = theclass->CreateInstance();\r
967         _stack[stackbase] = inst;\r
968         if(theclass->Get(_ss(this)->_constructoridx,constr)) {\r
969                 if(!Call(constr,nargs,stackbase,constr))\r
970                         return false;\r
971         }\r
972         retval = inst;\r
973         return true;\r
974 }\r
975 \r
976 void SQVM::CallErrorHandler(SQObjectPtr &error)\r
977 {\r
978         if(type(_errorhandler) != OT_NULL) {\r
979                 SQObjectPtr out;\r
980                 Push(_roottable); Push(error);\r
981                 Call(_errorhandler, 2, _top-2, out);\r
982                 Pop(2);\r
983         }\r
984 }\r
985 \r
986 void SQVM::CallDebugHook(int type,int forcedline)\r
987 {\r
988         SQObjectPtr temp_reg;\r
989         int nparams=5;\r
990         SQFunctionProto *func=_funcproto(_closure(ci->_closure)->_function);\r
991         Push(_roottable); Push(type); Push(func->_sourcename); Push(forcedline?forcedline:func->GetLine(ci->_ip)); Push(func->_name);\r
992         Call(_debughook,nparams,_top-nparams,temp_reg);\r
993         Pop(nparams);\r
994 }\r
995 \r
996 bool SQVM::CallNative(SQNativeClosure *nclosure,int nargs,int stackbase,bool tailcall,SQObjectPtr &retval,bool &suspend)\r
997 {\r
998         if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error(_SC("Native stack overflow")); return false; }\r
999         int nparamscheck = nclosure->_nparamscheck;\r
1000         if(((nparamscheck > 0) && (nparamscheck != nargs))\r
1001                 || ((nparamscheck < 0) && (nargs < (-nparamscheck)))) {\r
1002                 Raise_Error(_SC("wrong number of parameters"));\r
1003                 return false;\r
1004                 }\r
1005 \r
1006         int tcs;\r
1007         if(tcs = nclosure->_typecheck.size()) {\r
1008                 for(int i = 0; i < nargs && i < tcs; i++)\r
1009                         if((nclosure->_typecheck[i] != -1) && !(type(_stack[stackbase+i]) & nclosure->_typecheck[i])) {\r
1010                 Raise_ParamTypeError(i,nclosure->_typecheck[i],type(_stack[stackbase+i]));\r
1011                                 return false;\r
1012                         }\r
1013         }\r
1014         _nnativecalls++;\r
1015         if ((_top + MIN_STACK_OVERHEAD) > (int)_stack.size()) {\r
1016                 _stack.resize(_stack.size() + (MIN_STACK_OVERHEAD<<1));\r
1017         }\r
1018         int oldtop = _top;\r
1019         int oldstackbase = _stackbase;\r
1020         _top = stackbase + nargs;\r
1021         PUSH_CALLINFO(this, CallInfo());\r
1022         ci->_etraps = 0;\r
1023         ci->_closure._unVal.pNativeClosure = nclosure;\r
1024         ci->_closure._type = OT_NATIVECLOSURE;\r
1025         ci->_prevstkbase = stackbase - _stackbase;\r
1026         ci->_ncalls = 1;\r
1027         _stackbase = stackbase;\r
1028         //push free variables\r
1029         int outers = nclosure->_outervalues.size();\r
1030         for (int i = 0; i < outers; i++) {\r
1031                 Push(nclosure->_outervalues[i]);\r
1032         }\r
1033         ci->_prevtop = (oldtop - oldstackbase);\r
1034         int ret = (nclosure->_function)(this);\r
1035         _nnativecalls--;\r
1036         suspend = false;\r
1037         if( ret == SQ_SUSPEND_FLAG) suspend = true;\r
1038         else if (ret < 0) { \r
1039                 _stackbase = oldstackbase;\r
1040                 _top = oldtop;\r
1041                 POP_CALLINFO(this);\r
1042                 Raise_Error(_lasterror);\r
1043                 return false;\r
1044         }\r
1045         \r
1046         if (ret != 0){ retval = TOP(); }\r
1047         else { retval = _null_; }\r
1048         _stackbase = oldstackbase;\r
1049         _top = oldtop;\r
1050         POP_CALLINFO(this);\r
1051         return true;\r
1052 }\r
1053 \r
1054 bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw, bool fetchroot)\r
1055 {\r
1056         switch(type(self)){\r
1057         case OT_TABLE:\r
1058                 if(_table(self)->Get(key,dest))return true;\r
1059                 break;\r
1060         case OT_ARRAY:\r
1061                 if(sq_isnumeric(key)){\r
1062                         return _array(self)->Get(tointeger(key),dest);\r
1063                 }\r
1064                 break;\r
1065         case OT_INSTANCE:\r
1066                 if(_instance(self)->Get(key,dest)) return true;\r
1067                 break;\r
1068         }\r
1069         if(FallBackGet(self,key,dest,raw)) return true;\r
1070 \r
1071         if(fetchroot) {\r
1072                 if(_rawval(STK(0)) == _rawval(self) &&\r
1073                         type(STK(0)) == type(self)) {\r
1074                                 return _table(_roottable)->Get(key,dest);\r
1075                 }\r
1076         }\r
1077         return false;\r
1078 }\r
1079 \r
1080 bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,bool raw)\r
1081 {\r
1082         switch(type(self)){\r
1083         case OT_CLASS: \r
1084                 return _class(self)->Get(key,dest);\r
1085                 break;\r
1086         case OT_TABLE:\r
1087         case OT_USERDATA:\r
1088         //delegation\r
1089                 if(_delegable(self)->_delegate) {\r
1090                         if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,raw,false))\r
1091                                 return true;    \r
1092                         if(raw)return false;\r
1093                         Push(self);Push(key);\r
1094                         if(CallMetaMethod(_delegable(self),MT_GET,2,dest))\r
1095                                 return true;\r
1096                 }\r
1097                 if(type(self) == OT_TABLE) {\r
1098                         if(raw) return false;\r
1099                         return _table_ddel->Get(key,dest);\r
1100                 }\r
1101                 return false;\r
1102                 break;\r
1103         case OT_ARRAY:\r
1104                 if(raw)return false;\r
1105                 return _array_ddel->Get(key,dest);\r
1106         case OT_STRING:\r
1107                 if(sq_isnumeric(key)){\r
1108                         SQInteger n=tointeger(key);\r
1109                         if(abs(n)<_string(self)->_len){\r
1110                                 if(n<0)n=_string(self)->_len-n;\r
1111                                 dest=SQInteger(_stringval(self)[n]);\r
1112                                 return true;\r
1113                         }\r
1114                         return false;\r
1115                 }\r
1116                 else {\r
1117                         if(raw)return false;\r
1118                         return _string_ddel->Get(key,dest);\r
1119                 }\r
1120                 break;\r
1121         case OT_INSTANCE:\r
1122                 if(raw)return false;\r
1123                 Push(self);Push(key);\r
1124                 if(!CallMetaMethod(_delegable(self),MT_GET,2,dest)) {\r
1125                         return _instance_ddel->Get(key,dest);\r
1126                 }\r
1127                 return true;\r
1128         case OT_INTEGER:case OT_FLOAT:case OT_BOOL: \r
1129                 if(raw)return false;\r
1130                 return _number_ddel->Get(key,dest);\r
1131         case OT_GENERATOR: \r
1132                 if(raw)return false;\r
1133                 return _generator_ddel->Get(key,dest);\r
1134         case OT_CLOSURE: case OT_NATIVECLOSURE: \r
1135                 if(raw)return false;\r
1136                 return _closure_ddel->Get(key,dest);\r
1137         case OT_THREAD:\r
1138                 if(raw)return false;\r
1139                 return  _thread_ddel->Get(key,dest);\r
1140         default:return false;\r
1141         }\r
1142         return false;\r
1143 }\r
1144 \r
1145 bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool fetchroot)\r
1146 {\r
1147         switch(type(self)){\r
1148         case OT_TABLE:\r
1149                 if(_table(self)->Set(key,val))\r
1150                         return true;\r
1151                 if(_table(self)->_delegate) {\r
1152                         if(Set(_table(self)->_delegate,key,val,false)) {\r
1153                                 return true;\r
1154                         }\r
1155                 }\r
1156                 //keeps going\r
1157         case OT_USERDATA:\r
1158                 if(_delegable(self)->_delegate) {\r
1159                         SQObjectPtr t;\r
1160                         Push(self);Push(key);Push(val);\r
1161                         if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;\r
1162                 }\r
1163                 break;\r
1164         case OT_INSTANCE:{\r
1165                 if(_instance(self)->Set(key,val))\r
1166                         return true;\r
1167                 SQObjectPtr t;\r
1168                 Push(self);Push(key);Push(val);\r
1169                 if(CallMetaMethod(_delegable(self),MT_SET,3,t)) return true;\r
1170                 }\r
1171                 break;\r
1172         case OT_ARRAY:\r
1173                 if(!sq_isnumeric(key)) {Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key)); return false; }\r
1174                 return _array(self)->Set(tointeger(key),val);\r
1175         default:\r
1176                 Raise_Error(_SC("trying to set '%s'"),GetTypeName(self));\r
1177                 return false;\r
1178         }\r
1179         if(fetchroot) {\r
1180                 if(_rawval(STK(0)) == _rawval(self) &&\r
1181                         type(STK(0)) == type(self)) {\r
1182                                 return _table(_roottable)->Set(key,val);\r
1183                         }\r
1184         }\r
1185         return false;\r
1186 }\r
1187 \r
1188 bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target)\r
1189 {\r
1190         SQObjectPtr temp_reg;\r
1191         switch(type(self)){\r
1192         case OT_TABLE:\r
1193                 target = _table(self)->Clone();\r
1194                 goto cloned_mt;\r
1195         case OT_INSTANCE:\r
1196                 target = _instance(self)->Clone(_ss(this));\r
1197 cloned_mt:\r
1198                 if(_delegable(target)->_delegate){\r
1199                         Push(target);\r
1200                         Push(self);\r
1201                         CallMetaMethod(_delegable(target),MT_CLONED,2,temp_reg);\r
1202                 }\r
1203                 return true;\r
1204         case OT_ARRAY: \r
1205                 target=_array(self)->Clone();\r
1206                 return true;\r
1207         default: return false;\r
1208         }\r
1209 }\r
1210 \r
1211 bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val)\r
1212 {\r
1213         if(type(key) == OT_NULL) { Raise_Error(_SC("null cannot be used as index")); return false; }\r
1214         switch(type(self)) {\r
1215         case OT_TABLE: {\r
1216                 bool rawcall = true;\r
1217                 if(_table(self)->_delegate) {\r
1218                         SQObjectPtr res;\r
1219                         if(!_table(self)->Get(key,res)) {\r
1220                                 Push(self);Push(key);Push(val);\r
1221                                 rawcall = !CallMetaMethod(_table(self),MT_NEWSLOT,3,res);\r
1222                         }\r
1223                 }\r
1224                 if(rawcall) _table(self)->NewSlot(key,val); //cannot fail\r
1225                 \r
1226                 break;}\r
1227         case OT_CLASS: \r
1228                 if(!_class(self)->NewSlot(key,val)) {\r
1229                         if(_class(self)->_locked) {\r
1230                                 Raise_Error(_SC("trying to modify a class that has already been instantiated"));\r
1231                                 return false;\r
1232                         }\r
1233                         else {\r
1234                                 SQObjectPtr oval = PrintObjVal(key);\r
1235                                 Raise_Error(_SC("the property '%s' already exists"),_stringval(oval));\r
1236                                 return false;\r
1237                         }\r
1238                 }\r
1239                 break;\r
1240         default:\r
1241                 Raise_Error(_SC("indexing %s with %s"),GetTypeName(self),GetTypeName(key));\r
1242                 return false;\r
1243                 break;\r
1244         }\r
1245         return true;\r
1246 }\r
1247 \r
1248 bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res)\r
1249 {\r
1250         switch(type(self)) {\r
1251         case OT_TABLE:\r
1252         case OT_INSTANCE:\r
1253         case OT_USERDATA: {\r
1254                 SQObjectPtr t;\r
1255                 bool handled = false;\r
1256                 if(_delegable(self)->_delegate) {\r
1257                         Push(self);Push(key);\r
1258                         handled = CallMetaMethod(_delegable(self),MT_DELSLOT,2,t);\r
1259                 }\r
1260 \r
1261                 if(!handled) {\r
1262                         if(type(self) == OT_TABLE) {\r
1263                                 if(_table(self)->Get(key,t)) {\r
1264                                         _table(self)->Remove(key);\r
1265                                 }\r
1266                                 else {\r
1267                                         Raise_IdxError((SQObject &)key);\r
1268                                         return false;\r
1269                                 }\r
1270                         }\r
1271                         else {\r
1272                                 Raise_Error(_SC("cannot delete a slot from %s"),GetTypeName(self));\r
1273                                 return false;\r
1274                         }\r
1275                 }\r
1276                 res = t;\r
1277                                 }\r
1278                 break;\r
1279         default:\r
1280                 Raise_Error(_SC("attempt to delete a slot from a %s"),GetTypeName(self));\r
1281                 return false;\r
1282         }\r
1283         return true;\r
1284 }\r
1285 \r
1286 bool SQVM::Call(SQObjectPtr &closure,int nparams,int stackbase,SQObjectPtr &outres)\r
1287 {\r
1288 #ifdef _DEBUG\r
1289 int prevstackbase = _stackbase;\r
1290 #endif\r
1291         switch(type(closure)) {\r
1292         case OT_CLOSURE:\r
1293                 return Execute(closure, _top - nparams, nparams, stackbase,outres);\r
1294                 break;\r
1295         case OT_NATIVECLOSURE:{\r
1296                 bool suspend;\r
1297                 return CallNative(_nativeclosure(closure), nparams, stackbase, false, outres,suspend);\r
1298                 \r
1299                                                   }\r
1300                 break;\r
1301         case OT_CLASS:\r
1302                 return CreateClassInstance(_class(closure),nparams,stackbase,outres);\r
1303                 break;\r
1304         default:\r
1305                 return false;\r
1306         }\r
1307 #ifdef _DEBUG\r
1308         if(!_suspended) {\r
1309                 assert(_stackbase == prevstackbase);\r
1310         }\r
1311 #endif\r
1312         return true;\r
1313 }\r
1314 \r
1315 bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,int nparams,SQObjectPtr &outres)\r
1316 {\r
1317         SQObjectPtr closure;\r
1318         if(del->GetMetaMethod(mm, closure)) {\r
1319                 if(Call(closure, nparams, _top - nparams, outres)) {\r
1320                         Pop(nparams);\r
1321                         return true;\r
1322                 }\r
1323         }\r
1324         Pop(nparams);\r
1325         return false;\r
1326 }\r
1327 \r
1328 void SQVM::Pop() {\r
1329         _stack[--_top] = _null_;\r
1330 }\r
1331 void SQVM::Pop(int n) {\r
1332         for(int i = 0; i < n; i++){\r
1333                 _stack[--_top] = _null_;\r
1334         }\r
1335 }\r
1336 \r
1337 void SQVM::Remove(int n) {\r
1338         n = (n >= 0)?n + _stackbase - 1:_top + n;\r
1339         for(int i = n; i < _top; i++){\r
1340                 _stack[i] = _stack[i+1];\r
1341         }\r
1342         _stack[_top] = _null_;\r
1343         _top--;\r
1344 }\r
1345 \r
1346 void SQVM::Push(const SQObjectPtr &o) { _stack[_top++] = o; }\r
1347 SQObjectPtr &SQVM::Top() { return _stack[_top-1]; }\r
1348 SQObjectPtr &SQVM::PopGet() { return _stack[--_top]; }\r
1349 SQObjectPtr &SQVM::GetUp(int n) { return _stack[_top+n]; }\r
1350 SQObjectPtr &SQVM::GetAt(int n) { return _stack[n]; }\r
1351 \r
1352 #ifdef _DEBUG_DUMP\r
1353 void SQVM::dumpstack(int stackbase,bool dumpall)\r
1354 {\r
1355         int size=dumpall?_stack.size():_top;\r
1356         int n=0;\r
1357         scprintf(_SC("\n>>>>stack dump<<<<\n"));\r
1358         CallInfo &ci=_callsstack.back();\r
1359         scprintf(_SC("IP: %d\n"),ci._ip);\r
1360         scprintf(_SC("prev stack base: %d\n"),ci._prevstkbase);\r
1361         scprintf(_SC("prev top: %d\n"),ci._prevtop);\r
1362         for(int i=0;i<size;i++){\r
1363                 SQObjectPtr &obj=_stack[i];     \r
1364                 if(stackbase==i)scprintf(_SC(">"));else scprintf(_SC(" "));\r
1365                 scprintf(_SC("[%d]:"),n);\r
1366                 switch(type(obj)){\r
1367                 case OT_FLOAT:                  scprintf(_SC("FLOAT %.3f"),_float(obj));break;\r
1368                 case OT_INTEGER:                scprintf(_SC("INTEGER %d"),_integer(obj));break;\r
1369                 case OT_BOOL:                   scprintf(_SC("BOOL %s"),_integer(obj)?"true":"false");break;\r
1370                 case OT_STRING:                 scprintf(_SC("STRING %s"),_stringval(obj));break;\r
1371                 case OT_NULL:                   scprintf(_SC("NULL"));  break;\r
1372                 case OT_TABLE:                  scprintf(_SC("TABLE %p[%p]"),_table(obj),_table(obj)->_delegate);break;\r
1373                 case OT_ARRAY:                  scprintf(_SC("ARRAY %p"),_array(obj));break;\r
1374                 case OT_CLOSURE:                scprintf(_SC("CLOSURE [%p]"),_closure(obj));break;\r
1375                 case OT_NATIVECLOSURE:  scprintf(_SC("NATIVECLOSURE"));break;\r
1376                 case OT_USERDATA:               scprintf(_SC("USERDATA %p[%p]"),_userdataval(obj),_userdata(obj)->_delegate);break;\r
1377                 case OT_GENERATOR:              scprintf(_SC("GENERATOR"));break;\r
1378                 case OT_THREAD:                 scprintf(_SC("THREAD [%p]"),_thread(obj));break;\r
1379                 case OT_USERPOINTER:    scprintf(_SC("USERPOINTER %p"),_userpointer(obj));break;\r
1380                 case OT_CLASS:                  scprintf(_SC("CLASS %p"),_class(obj));break;\r
1381                 case OT_INSTANCE:               scprintf(_SC("INSTANCE %p"),_instance(obj));break;\r
1382                 default:\r
1383                         assert(0);\r
1384                         break;\r
1385                 };\r
1386                 scprintf(_SC("\n"));\r
1387                 ++n;\r
1388         }\r
1389 }\r
1390 \r
1391 #endif\r