- More work on scripting interface
[supertux.git] / src / squirrel / squirrel / sqobject.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #include "sqvm.h"\r
6 #include "sqstring.h"\r
7 #include "sqarray.h"\r
8 #include "sqtable.h"\r
9 #include "squserdata.h"\r
10 #include "sqfuncproto.h"\r
11 #include "sqclass.h"\r
12 #include "sqclosure.h"\r
13 \r
14 SQString *SQString::Create(SQSharedState *ss,const SQChar *s,int len)\r
15 {\r
16         SQString *str=ADD_STRING(ss,s,len);\r
17         str->_sharedstate=ss;\r
18         return str;\r
19 }\r
20 \r
21 void SQString::Release()\r
22 {\r
23         REMOVE_STRING(_sharedstate,this);\r
24 }\r
25 \r
26 unsigned int TranslateIndex(const SQObjectPtr &idx)\r
27 {\r
28         switch(type(idx)){\r
29                 case OT_NULL:\r
30                         return 0;\r
31                 case OT_INTEGER:\r
32                         return (unsigned int)_integer(idx);\r
33         }\r
34         assert(0);\r
35         return 0;\r
36 }\r
37 \r
38 bool SQDelegable::GetMetaMethod(SQMetaMethod mm,SQObjectPtr &res) {\r
39         if(_delegate) {\r
40                 return _delegate->Get((*_ss(this)->_metamethods)[mm],res);\r
41         }\r
42         return false;\r
43 }\r
44 \r
45 bool SQGenerator::Yield(SQVM *v)\r
46 {\r
47         if(_state==eSuspended) { v->Raise_Error(_SC("internal vm error, yielding dead generator"));  return false;}\r
48         if(_state==eDead) { v->Raise_Error(_SC("internal vm error, yielding a dead generator")); return false; }\r
49         int size = v->_top-v->_stackbase;\r
50         _ci=*v->ci;\r
51         _stack.resize(size);\r
52         for(int n =0; n<size; n++) {\r
53                 _stack._vals[n] = v->_stack[v->_stackbase+n];\r
54                 v->_stack[v->_stackbase+n] = _null_;\r
55         }\r
56         int nvargs = v->ci->_vargs.size;\r
57         int vargsbase = v->ci->_vargs.base;\r
58         for(int j = nvargs - 1; j >= 0; j--) {\r
59                 _vargsstack.push_back(v->_vargsstack[vargsbase+j]);\r
60         }\r
61         _ci._generator=_null_;\r
62         for(int i=0;i<_ci._etraps;i++) {\r
63                 _etraps.push_back(v->_etraps.top());\r
64                 v->_etraps.pop_back();\r
65         }\r
66         _state=eSuspended;\r
67         return true;\r
68 }\r
69 \r
70 bool SQGenerator::Resume(SQVM *v,int target)\r
71 {\r
72         int size=_stack.size();\r
73         if(_state==eDead){ v->Raise_Error(_SC("resuming dead generator")); return false; }\r
74         if(_state==eRunning){ v->Raise_Error(_SC("resuming active generator")); return false; }\r
75         int prevtop=v->_top-v->_stackbase;\r
76         PUSH_CALLINFO(v,_ci);\r
77         int oldstackbase=v->_stackbase;\r
78         v->_stackbase=v->_top;\r
79         v->ci->_target=target;\r
80         v->ci->_generator=SQObjectPtr(this);\r
81         v->ci->_vargs.size = _vargsstack.size();\r
82         \r
83         for(int i=0;i<_ci._etraps;i++) {\r
84                 v->_etraps.push_back(_etraps.top());\r
85                 _etraps.pop_back();\r
86         }\r
87         for(int n =0; n<size; n++) {\r
88                 v->_stack[v->_stackbase+n] = _stack._vals[n];\r
89                 _stack._vals[0] = _null_;\r
90         }\r
91         while(_vargsstack.size()) {\r
92                 v->_vargsstack.push_back(_vargsstack.back());\r
93                 _vargsstack.pop_back();\r
94         }\r
95         v->ci->_vargs.base = v->_vargsstack.size() - v->ci->_vargs.size;\r
96         v->_top=v->_stackbase+size;\r
97         v->ci->_prevtop=prevtop;\r
98         v->ci->_prevstkbase=v->_stackbase-oldstackbase;\r
99         _state=eRunning;\r
100         return true;\r
101 }\r
102 \r
103 void SQArray::Extend(const SQArray *a){\r
104         int xlen;\r
105         if((xlen=a->Size()))\r
106                 for(int i=0;i<xlen;i++)\r
107                         Append(a->_values[i]);\r
108 }\r
109 \r
110 const SQChar* SQFunctionProto::GetLocal(SQVM *vm,unsigned int stackbase,unsigned int nseq,unsigned int nop)\r
111 {\r
112         unsigned int nvars=_localvarinfos.size();\r
113         const SQChar *res=NULL; \r
114         if(nvars>=nseq){\r
115                 for(unsigned int i=0;i<nvars;i++){\r
116                         if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop)\r
117                         {\r
118                                 if(nseq==0){\r
119                                         vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);\r
120                                         res=_stringval(_localvarinfos[i]._name);\r
121                                         break;\r
122                                 }\r
123                                 nseq--;\r
124                         }\r
125                 }\r
126         }\r
127         return res;\r
128 }\r
129 \r
130 int SQFunctionProto::GetLine(SQInstruction *curr)\r
131 {\r
132         int op=(curr-_instructions._vals);\r
133         int line=_lineinfos[0]._line;\r
134         for(unsigned int i=1;i<_lineinfos.size();i++){\r
135                 if(_lineinfos[i]._op>=op)\r
136                         return line;\r
137                 line=_lineinfos[i]._line;\r
138         }\r
139         return line;\r
140 }\r
141 \r
142 //#define _ERROR_TRAP() error_trap:\r
143 #define _CHECK_IO(exp)  { if(!exp)return false; }\r
144 bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,int size)\r
145 {\r
146         if(write(up,dest,size) != size) {\r
147                 v->Raise_Error(_SC("io error (write function failure)"));\r
148                 return false;\r
149         }\r
150         return true;\r
151 }\r
152 \r
153 bool SafeRead(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,SQUserPointer dest,int size)\r
154 {\r
155         if(size && read(up,dest,size) != size) {\r
156                 v->Raise_Error(_SC("io error, read function failure, the origin stream could be corrupted/trucated"));\r
157                 return false;\r
158         }\r
159         return true;\r
160 }\r
161 \r
162 bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,int tag)\r
163 {\r
164         return SafeWrite(v,write,up,&tag,sizeof(tag));\r
165 }\r
166 \r
167 bool CheckTag(HSQUIRRELVM v,SQWRITEFUNC read,SQUserPointer up,int tag)\r
168 {\r
169         int t;\r
170         _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t)));\r
171         if(t != tag){\r
172                 v->Raise_Error(_SC("invalid or corrupted closure stream"));\r
173                 return false;\r
174         }\r
175         return true;\r
176 }\r
177 \r
178 bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o)\r
179 {\r
180         _CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType)));\r
181         switch(type(o)){\r
182         case OT_STRING:\r
183                 _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger)));\r
184                 _CHECK_IO(SafeWrite(v,write,up,_stringval(o),rsl(_string(o)->_len)));\r
185                 break;\r
186         case OT_INTEGER:\r
187                 _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;\r
188         case OT_FLOAT:\r
189                 _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break;\r
190         case OT_NULL:\r
191                 break;\r
192         default:\r
193                 v->Raise_Error(_SC("cannot serialize a %s"),GetTypeName(o));\r
194                 return false;\r
195         }\r
196         return true;\r
197 }\r
198 \r
199 bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)\r
200 {\r
201         SQObjectType t;\r
202         _CHECK_IO(SafeRead(v,read,up,&t,sizeof(SQObjectType)));\r
203         switch(t){\r
204         case OT_STRING:{\r
205                 int len;\r
206                 _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger)));\r
207                 _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(rsl(len)),rsl(len)));\r
208                 o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len);\r
209                                    }\r
210                 break;\r
211         case OT_INTEGER:{\r
212                 SQInteger i;\r
213                 _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break;\r
214                                         }\r
215         case OT_FLOAT:{\r
216                 SQFloat f;\r
217                 _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break;\r
218                                   }\r
219         case OT_NULL:\r
220                 o=_null_;\r
221                 break;\r
222         default:\r
223                 v->Raise_Error(_SC("cannot serialize a %s"),IdType2Name(t));\r
224                 return false;\r
225         }\r
226         return true;\r
227 }\r
228 \r
229 bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)\r
230 {\r
231         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD));\r
232         _CHECK_IO(WriteTag(v,write,up,sizeof(SQChar)));\r
233         _CHECK_IO(_funcproto(_function)->Save(v,up,write));\r
234         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));\r
235         return true;\r
236 }\r
237 \r
238 bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read)\r
239 {\r
240         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD));\r
241         _CHECK_IO(CheckTag(v,read,up,sizeof(SQChar)));\r
242         _CHECK_IO(_funcproto(_function)->Load(v,up,read));\r
243         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));\r
244         return true;\r
245 }\r
246 \r
247 bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)\r
248 {\r
249         int i,nsize=_literals.size();\r
250         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
251         _CHECK_IO(WriteObject(v,up,write,_sourcename));\r
252         _CHECK_IO(WriteObject(v,up,write,_name));\r
253         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
254         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
255         for(i=0;i<nsize;i++){\r
256                 _CHECK_IO(WriteObject(v,up,write,_literals[i]));\r
257         }\r
258         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
259         nsize=_parameters.size();\r
260         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
261         for(i=0;i<nsize;i++){\r
262                 _CHECK_IO(WriteObject(v,up,write,_parameters[i]));\r
263         }\r
264         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
265         nsize=_outervalues.size();\r
266         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
267         for(i=0;i<nsize;i++){\r
268                 _CHECK_IO(SafeWrite(v,write,up,&_outervalues[i]._type,sizeof(unsigned int)));\r
269                 _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._src));\r
270                 _CHECK_IO(WriteObject(v,up,write,_outervalues[i]._name));\r
271         }\r
272         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
273         nsize=_localvarinfos.size();\r
274         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
275         for(i=0;i<nsize;i++){\r
276                 SQLocalVarInfo &lvi=_localvarinfos[i];\r
277                 _CHECK_IO(WriteObject(v,up,write,lvi._name));\r
278                 _CHECK_IO(SafeWrite(v,write,up,&lvi._pos,sizeof(unsigned int)));\r
279                 _CHECK_IO(SafeWrite(v,write,up,&lvi._start_op,sizeof(unsigned int)));\r
280                 _CHECK_IO(SafeWrite(v,write,up,&lvi._end_op,sizeof(unsigned int)));\r
281         }\r
282         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
283         nsize=_lineinfos.size();\r
284         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
285         _CHECK_IO(SafeWrite(v,write,up,&_lineinfos[0],sizeof(SQLineInfo)*nsize));\r
286         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
287         nsize=_instructions.size();\r
288         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
289         _CHECK_IO(SafeWrite(v,write,up,&_instructions[0],sizeof(SQInstruction)*nsize));\r
290         _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART));\r
291         nsize=_functions.size();\r
292         _CHECK_IO(SafeWrite(v,write,up,&nsize,sizeof(nsize)));\r
293         for(i=0;i<nsize;i++){\r
294                 _CHECK_IO(_funcproto(_functions[i])->Save(v,up,write));\r
295         }\r
296         _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize)));\r
297         _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator)));\r
298         _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams)));\r
299         return true;\r
300 }\r
301 \r
302 bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read)\r
303 {\r
304         int i, nsize = _literals.size();\r
305         SQObjectPtr o;\r
306         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
307         _CHECK_IO(ReadObject(v, up, read, _sourcename));\r
308         _CHECK_IO(ReadObject(v, up, read, _name));\r
309         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
310         _CHECK_IO(SafeRead(v,read,up, &nsize, sizeof(nsize)));\r
311         for(i = 0;i < nsize; i++){\r
312                 _CHECK_IO(ReadObject(v, up, read, o));\r
313                 _literals.push_back(o);\r
314         }\r
315         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
316         _CHECK_IO(SafeRead(v,read,up, &nsize, sizeof(nsize)));\r
317         for(i = 0; i < nsize; i++){\r
318                 _CHECK_IO(ReadObject(v, up, read, o));\r
319                 _parameters.push_back(o);\r
320         }\r
321         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
322         _CHECK_IO(SafeRead(v,read,up,&nsize,sizeof(nsize)));\r
323         for(i = 0; i < nsize; i++){\r
324                 unsigned int type;\r
325                 SQObjectPtr name;\r
326                 _CHECK_IO(SafeRead(v,read,up, &type, sizeof(unsigned int)));\r
327                 _CHECK_IO(ReadObject(v, up, read, o));\r
328                 _CHECK_IO(ReadObject(v, up, read, name));\r
329                 _outervalues.push_back(SQOuterVar(name,o, (SQOuterType)type));\r
330         }\r
331         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
332         _CHECK_IO(SafeRead(v,read,up,&nsize, sizeof(nsize)));\r
333         for(i = 0; i < nsize; i++){\r
334                 SQLocalVarInfo lvi;\r
335                 _CHECK_IO(ReadObject(v, up, read, lvi._name));\r
336                 _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(unsigned int)));\r
337                 _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(unsigned int)));\r
338                 _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(unsigned int)));\r
339                 _localvarinfos.push_back(lvi);\r
340         }\r
341         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
342         _CHECK_IO(SafeRead(v,read,up, &nsize,sizeof(nsize)));\r
343         _lineinfos.resize(nsize);\r
344         _CHECK_IO(SafeRead(v,read,up, &_lineinfos[0], sizeof(SQLineInfo)*nsize));\r
345         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
346         _CHECK_IO(SafeRead(v,read,up, &nsize, sizeof(nsize)));\r
347         _instructions.resize(nsize);\r
348         _CHECK_IO(SafeRead(v,read,up, &_instructions[0], sizeof(SQInstruction)*nsize));\r
349         _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART));\r
350         _CHECK_IO(SafeRead(v,read,up, &nsize, sizeof(nsize)));\r
351         for(i = 0; i < nsize; i++){\r
352                 o = SQFunctionProto::Create();\r
353                 _CHECK_IO(_funcproto(o)->Load(v, up, read));\r
354                 _functions.push_back(o);\r
355         }\r
356         _CHECK_IO(SafeRead(v,read,up, &_stacksize, sizeof(_stacksize)));\r
357         _CHECK_IO(SafeRead(v,read,up, &_bgenerator, sizeof(_bgenerator)));\r
358         _CHECK_IO(SafeRead(v,read,up, &_varparams, sizeof(_varparams)));\r
359         return true;\r
360 }\r
361 \r
362 #ifndef NO_GARBAGE_COLLECTOR\r
363 \r
364 #define START_MARK()    if(!(_uiRef&MARK_FLAG)){ \\r
365                 _uiRef|=MARK_FLAG;\r
366 \r
367 #define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \\r
368                 AddToChain(chain, this); }\r
369 \r
370 void SQVM::Mark(SQCollectable **chain)\r
371 {\r
372         START_MARK()\r
373                 SQSharedState::MarkObject(_lasterror,chain);\r
374                 SQSharedState::MarkObject(_errorhandler,chain);\r
375                 SQSharedState::MarkObject(_debughook,chain);\r
376                 SQSharedState::MarkObject(_roottable, chain);\r
377                 SQSharedState::MarkObject(temp_reg, chain);\r
378                 for(unsigned int i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);\r
379                 for(unsigned int j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);\r
380         END_MARK()\r
381 }\r
382 \r
383 void SQArray::Mark(SQCollectable **chain)\r
384 {\r
385         START_MARK()\r
386                 int len = _values.size();\r
387                 for(int i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain);\r
388         END_MARK()\r
389 }\r
390 void SQTable::Mark(SQCollectable **chain)\r
391 {\r
392         START_MARK()\r
393                 if(_delegate) _delegate->Mark(chain);\r
394                 int len = _numofnodes;\r
395                 for(int i = 0; i < len; i++){\r
396                         SQSharedState::MarkObject(_nodes[i].key, chain);\r
397                         SQSharedState::MarkObject(_nodes[i].val, chain);\r
398                 }\r
399         END_MARK()\r
400 }\r
401 \r
402 void SQClass::Mark(SQCollectable **chain)\r
403 {\r
404         START_MARK()\r
405                 _members->Mark(chain);\r
406                 if(_base) _base->Mark(chain);\r
407                 SQSharedState::MarkObject(_attributes, chain);\r
408                 for(unsigned int i =0; i< _defaultvalues.size(); i++) {\r
409                         SQSharedState::MarkObject(_defaultvalues[i].val, chain);\r
410                         SQSharedState::MarkObject(_defaultvalues[i].attrs, chain);\r
411                 }\r
412                 for(unsigned int j =0; j< _methods.size(); j++) {\r
413                         SQSharedState::MarkObject(_methods[j].val, chain);\r
414                         SQSharedState::MarkObject(_methods[j].attrs, chain);\r
415                 }\r
416                 for(unsigned int k =0; k< _metamethods.size(); k++) {\r
417                         SQSharedState::MarkObject(_metamethods[k], chain);\r
418                 }\r
419         END_MARK()\r
420 }\r
421 \r
422 void SQInstance::Mark(SQCollectable **chain)\r
423 {\r
424         START_MARK()\r
425                 _class->Mark(chain);\r
426                 for(unsigned int i =0; i< _values.size(); i++) {\r
427                         SQSharedState::MarkObject(_values[i], chain);\r
428                 }\r
429         END_MARK()\r
430 }\r
431 \r
432 void SQGenerator::Mark(SQCollectable **chain)\r
433 {\r
434         START_MARK()\r
435                 for(unsigned int i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain);\r
436                 for(unsigned int j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain);\r
437                 SQSharedState::MarkObject(_closure, chain);\r
438         END_MARK()\r
439 }\r
440 \r
441 void SQClosure::Mark(SQCollectable **chain)\r
442 {\r
443         START_MARK()\r
444                 for(unsigned int i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);\r
445         END_MARK()\r
446 }\r
447 \r
448 void SQNativeClosure::Mark(SQCollectable **chain)\r
449 {\r
450         START_MARK()\r
451                 for(unsigned int i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain);\r
452         END_MARK()\r
453 }\r
454 \r
455 void SQUserData::Mark(SQCollectable **chain){\r
456         START_MARK()\r
457                 if(_delegate) _delegate->Mark(chain);\r
458         END_MARK()\r
459 }\r
460 \r
461 void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; }\r
462 \r
463 #endif\r
464 \r