Installed some elevators
[supertux.git] / src / squirrel / squirrel / sqstate.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #include "sqopcodes.h"\r
6 #include "sqvm.h"\r
7 #include "sqfuncproto.h"\r
8 #include "sqclosure.h"\r
9 #include "sqstring.h"\r
10 #include "sqtable.h"\r
11 #include "sqarray.h"\r
12 #include "squserdata.h"\r
13 #include "sqclass.h"\r
14 \r
15 SQObjectPtr _null_;\r
16 SQObjectPtr _true_(true);\r
17 SQObjectPtr _false_(false);\r
18 SQObjectPtr _one_((SQInteger)1);\r
19 SQObjectPtr _minusone_((SQInteger)-1);\r
20 \r
21 SQSharedState::SQSharedState()\r
22 {\r
23         _compilererrorhandler = NULL;\r
24         _printfunc = NULL;\r
25         _debuginfo = false;\r
26         _notifyallexceptions = false;\r
27 }\r
28 \r
29 #define newsysstring(s) {       \\r
30         _systemstrings->push_back(SQString::Create(this,s));    \\r
31         }\r
32 \r
33 #define newmetamethod(s) {      \\r
34         _metamethods->push_back(SQString::Create(this,s));      \\r
35         _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \\r
36         }\r
37 \r
38 bool CompileTypemask(SQIntVec &res,const SQChar *typemask)\r
39 {\r
40         SQInteger i = 0;\r
41         \r
42         SQInteger mask = 0;\r
43         while(typemask[i] != 0) {\r
44                 \r
45                 switch(typemask[i]){\r
46                                 case 'o': mask |= _RT_NULL; break;\r
47                                 case 'i': mask |= _RT_INTEGER; break;\r
48                                 case 'f': mask |= _RT_FLOAT; break;\r
49                                 case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;\r
50                                 case 's': mask |= _RT_STRING; break;\r
51                                 case 't': mask |= _RT_TABLE; break;\r
52                                 case 'a': mask |= _RT_ARRAY; break;\r
53                                 case 'u': mask |= _RT_USERDATA; break;\r
54                                 case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;\r
55                                 case 'b': mask |= _RT_BOOL; break;\r
56                                 case 'g': mask |= _RT_GENERATOR; break;\r
57                                 case 'p': mask |= _RT_USERPOINTER; break;\r
58                                 case 'v': mask |= _RT_THREAD; break;\r
59                                 case 'x': mask |= _RT_INSTANCE; break;\r
60                                 case 'y': mask |= _RT_CLASS; break;\r
61                                 case 'r': mask |= _RT_WEAKREF; break;\r
62                                 case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;\r
63                                 case ' ': i++; continue; //ignores spaces\r
64                                 default:\r
65                                         return false;\r
66                 }\r
67                 i++;\r
68                 if(typemask[i] == '|') { \r
69                         i++; \r
70                         if(typemask[i] == 0)\r
71                                 return false;\r
72                         continue; \r
73                 }\r
74                 res.push_back(mask);\r
75                 mask = 0;\r
76                 \r
77         }\r
78         return true;\r
79 }\r
80 \r
81 SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)\r
82 {\r
83         SQInteger i=0;\r
84         SQTable *t=SQTable::Create(ss,0);\r
85         while(funcz[i].name!=0){\r
86                 SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);\r
87                 nc->_nparamscheck = funcz[i].nparamscheck;\r
88                 nc->_name = SQString::Create(ss,funcz[i].name);\r
89                 if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))\r
90                         return NULL;\r
91                 t->NewSlot(SQString::Create(ss,funcz[i].name),nc);\r
92                 i++;\r
93         }\r
94         return t;\r
95 }\r
96 \r
97 void SQSharedState::Init()\r
98 {       \r
99         _scratchpad=NULL;\r
100         _scratchpadsize=0;\r
101 #ifndef NO_GARBAGE_COLLECTOR\r
102         _gc_chain=NULL;\r
103 #endif\r
104         sq_new(_stringtable,StringTable);\r
105         sq_new(_metamethods,SQObjectPtrVec);\r
106         sq_new(_systemstrings,SQObjectPtrVec);\r
107         sq_new(_types,SQObjectPtrVec);\r
108         _metamethodsmap = SQTable::Create(this,MT_LAST-1);\r
109         //adding type strings to avoid memory trashing\r
110         //types names\r
111         newsysstring(_SC("null"));\r
112         newsysstring(_SC("table"));\r
113         newsysstring(_SC("array"));\r
114         newsysstring(_SC("closure"));\r
115         newsysstring(_SC("string"));\r
116         newsysstring(_SC("userdata"));\r
117         newsysstring(_SC("integer"));\r
118         newsysstring(_SC("float"));\r
119         newsysstring(_SC("userpointer"));\r
120         newsysstring(_SC("function"));\r
121         newsysstring(_SC("generator"));\r
122         newsysstring(_SC("thread"));\r
123         newsysstring(_SC("class"));\r
124         newsysstring(_SC("instance"));\r
125         newsysstring(_SC("bool"));\r
126         //meta methods\r
127         newmetamethod(MM_ADD);\r
128         newmetamethod(MM_SUB);\r
129         newmetamethod(MM_MUL);\r
130         newmetamethod(MM_DIV);\r
131         newmetamethod(MM_UNM);\r
132         newmetamethod(MM_MODULO);\r
133         newmetamethod(MM_SET);\r
134         newmetamethod(MM_GET);\r
135         newmetamethod(MM_TYPEOF);\r
136         newmetamethod(MM_NEXTI);\r
137         newmetamethod(MM_CMP);\r
138         newmetamethod(MM_CALL);\r
139         newmetamethod(MM_CLONED);\r
140         newmetamethod(MM_NEWSLOT);\r
141         newmetamethod(MM_DELSLOT);\r
142         newmetamethod(MM_TOSTRING);\r
143         newmetamethod(MM_NEWMEMBER);\r
144         newmetamethod(MM_INHERITED);\r
145 \r
146         _constructoridx = SQString::Create(this,_SC("constructor"));\r
147         _registry = SQTable::Create(this,0);\r
148         _table_default_delegate=CreateDefaultDelegate(this,_table_default_delegate_funcz);\r
149         _array_default_delegate=CreateDefaultDelegate(this,_array_default_delegate_funcz);\r
150         _string_default_delegate=CreateDefaultDelegate(this,_string_default_delegate_funcz);\r
151         _number_default_delegate=CreateDefaultDelegate(this,_number_default_delegate_funcz);\r
152         _closure_default_delegate=CreateDefaultDelegate(this,_closure_default_delegate_funcz);\r
153         _generator_default_delegate=CreateDefaultDelegate(this,_generator_default_delegate_funcz);\r
154         _thread_default_delegate=CreateDefaultDelegate(this,_thread_default_delegate_funcz);\r
155         _class_default_delegate=CreateDefaultDelegate(this,_class_default_delegate_funcz);\r
156         _instance_default_delegate=CreateDefaultDelegate(this,_instance_default_delegate_funcz);\r
157         _weakref_default_delegate=CreateDefaultDelegate(this,_weakref_default_delegate_funcz);\r
158 \r
159 }\r
160 \r
161 SQSharedState::~SQSharedState()\r
162 {\r
163         _constructoridx = _null_;\r
164         _refs_table.Finalize();\r
165         _table(_registry)->Finalize();\r
166         _table(_metamethodsmap)->Finalize();\r
167 //      _refs_table = _null_;\r
168         _registry = _null_;\r
169         _metamethodsmap = _null_;\r
170         while(!_systemstrings->empty()){\r
171                 _systemstrings->back()=_null_;\r
172                 _systemstrings->pop_back();\r
173         }\r
174         _thread(_root_vm)->Finalize();\r
175         _root_vm = _null_;\r
176         _table_default_delegate=_null_;\r
177         _array_default_delegate=_null_;\r
178         _string_default_delegate=_null_;\r
179         _number_default_delegate=_null_;\r
180         _closure_default_delegate=_null_;\r
181         _generator_default_delegate=_null_;\r
182         _thread_default_delegate=_null_;\r
183         _class_default_delegate=_null_;\r
184         _instance_default_delegate=_null_;\r
185         _weakref_default_delegate=_null_;\r
186         \r
187 #ifndef NO_GARBAGE_COLLECTOR\r
188         \r
189         \r
190         SQCollectable *t=_gc_chain;\r
191         SQCollectable *nx=NULL;\r
192         while(t){\r
193                 t->_uiRef++;\r
194                 t->Finalize();\r
195                 nx=t->_next;\r
196                 if(--t->_uiRef==0)\r
197                         t->Release();\r
198                 t=nx;\r
199         }\r
200         assert(_gc_chain==NULL); //just to proove a theory\r
201         while(_gc_chain){\r
202                 _gc_chain->_uiRef++;\r
203                 _gc_chain->Release();\r
204         }\r
205 #endif\r
206         sq_delete(_types,SQObjectPtrVec);\r
207         sq_delete(_systemstrings,SQObjectPtrVec);\r
208         sq_delete(_metamethods,SQObjectPtrVec);\r
209         sq_delete(_stringtable,StringTable);\r
210         if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);\r
211 }\r
212 \r
213 \r
214 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)\r
215 {\r
216         if(type(name) != OT_STRING)\r
217                 return -1;\r
218         SQObjectPtr ret;\r
219         if(_table(_metamethodsmap)->Get(name,ret)) {\r
220                 return _integer(ret);\r
221         }\r
222         return -1;\r
223 }\r
224 \r
225 #ifndef NO_GARBAGE_COLLECTOR\r
226 \r
227 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)\r
228 {\r
229         switch(type(o)){\r
230         case OT_TABLE:_table(o)->Mark(chain);break;\r
231         case OT_ARRAY:_array(o)->Mark(chain);break;\r
232         case OT_USERDATA:_userdata(o)->Mark(chain);break;\r
233         case OT_CLOSURE:_closure(o)->Mark(chain);break;\r
234         case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;\r
235         case OT_GENERATOR:_generator(o)->Mark(chain);break;\r
236         case OT_THREAD:_thread(o)->Mark(chain);break;\r
237         case OT_CLASS:_class(o)->Mark(chain);break;\r
238         case OT_INSTANCE:_instance(o)->Mark(chain);break;\r
239         default: break; //shutup compiler\r
240         }\r
241 }\r
242 \r
243 \r
244 SQInteger SQSharedState::CollectGarbage(SQVM *vm)\r
245 {\r
246         SQInteger n=0;\r
247         SQCollectable *tchain=NULL;\r
248         SQVM *vms=_thread(_root_vm);\r
249         \r
250         vms->Mark(&tchain);\r
251         SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();\r
252         _refs_table.Mark(&tchain);\r
253         MarkObject(_registry,&tchain);\r
254         MarkObject(_metamethodsmap,&tchain);\r
255         MarkObject(_table_default_delegate,&tchain);\r
256         MarkObject(_array_default_delegate,&tchain);\r
257         MarkObject(_string_default_delegate,&tchain);\r
258         MarkObject(_number_default_delegate,&tchain);\r
259         MarkObject(_generator_default_delegate,&tchain);\r
260         MarkObject(_thread_default_delegate,&tchain);\r
261         MarkObject(_closure_default_delegate,&tchain);\r
262         MarkObject(_class_default_delegate,&tchain);\r
263         MarkObject(_instance_default_delegate,&tchain);\r
264         MarkObject(_weakref_default_delegate,&tchain);\r
265         \r
266         SQCollectable *t=_gc_chain;\r
267         SQCollectable *nx=NULL;\r
268         while(t){\r
269                 t->_uiRef++;\r
270                 t->Finalize();\r
271                 nx=t->_next;\r
272                 if(--t->_uiRef==0)\r
273                         t->Release();\r
274                 t=nx;\r
275                 n++;\r
276         }\r
277 \r
278         t=tchain;\r
279         while(t){\r
280                 t->UnMark();\r
281                 t=t->_next;\r
282         }\r
283         _gc_chain=tchain;\r
284         SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();\r
285         assert(z == x);\r
286         return n;\r
287 }\r
288 #endif\r
289 \r
290 #ifndef NO_GARBAGE_COLLECTOR\r
291 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)\r
292 {\r
293     c->_prev=NULL;\r
294         c->_next=*chain;\r
295         if(*chain) (*chain)->_prev=c;\r
296         *chain=c;\r
297 }\r
298 \r
299 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)\r
300 {\r
301         if(c->_prev) c->_prev->_next=c->_next;\r
302         else *chain=c->_next;\r
303         if(c->_next)\r
304                 c->_next->_prev=c->_prev;\r
305         c->_next=NULL;\r
306         c->_prev=NULL;\r
307 }\r
308 #endif\r
309 \r
310 SQChar* SQSharedState::GetScratchPad(SQInteger size)\r
311 {\r
312         SQInteger newsize;\r
313         if(size>0){\r
314                 if(_scratchpadsize<size){\r
315                         newsize=size+(size>>1);\r
316                         _scratchpad=(SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);\r
317                         _scratchpadsize=newsize;\r
318 \r
319                 }else if(_scratchpadsize>=(size<<5)){\r
320                         newsize=_scratchpadsize>>1;\r
321                         _scratchpad=(SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);\r
322                         _scratchpadsize=newsize;\r
323                 }\r
324         }\r
325         return _scratchpad;\r
326 }\r
327 \r
328 RefTable::RefTable()\r
329 {\r
330         AllocNodes(4);\r
331 }\r
332 \r
333 void RefTable::Finalize()\r
334 {\r
335         RefNode *nodes = (RefNode *)&_buckets[_numofslots];\r
336         for(SQUnsignedInteger n = 0; n < _numofslots; n++) {\r
337                 nodes->obj = _null_;\r
338                 nodes++;\r
339         }\r
340 }\r
341 \r
342 RefTable::~RefTable()\r
343 {\r
344         SQ_FREE(_buckets,_buffersize);\r
345 }\r
346 #ifndef NO_GARBAGE_COLLECTOR\r
347 void RefTable::Mark(SQCollectable **chain)\r
348 {\r
349         RefNode *nodes = (RefNode *)&_buckets[_numofslots];\r
350         for(SQUnsignedInteger n = 0; n < _numofslots; n++) {\r
351                 if(type(nodes->obj) != OT_NULL) {\r
352                         SQSharedState::MarkObject(nodes->obj,chain);\r
353                 }\r
354                 nodes++;\r
355         }\r
356 }\r
357 #endif\r
358 void RefTable::AddRef(SQObject &obj)\r
359 {\r
360         SQHash mainpos;\r
361         RefNode *prev;\r
362         RefNode *ref = Get(obj,mainpos,&prev,true);\r
363         ref->refs++;\r
364 }\r
365 \r
366 SQBool RefTable::Release(SQObject &obj)\r
367 {\r
368         SQHash mainpos;\r
369         RefNode *prev;\r
370         RefNode *ref = Get(obj,mainpos,&prev,false);\r
371         if(ref) {\r
372                 if(--ref->refs == 0) {\r
373                         ref->obj = _null_;\r
374                         if(prev) {\r
375                                 prev->next = ref->next;\r
376                         }\r
377                         else {\r
378                                 _buckets[mainpos] = ref->next;\r
379                         }\r
380                         ref->next = _freelist;\r
381                         _freelist = ref;\r
382                         _slotused--;\r
383                         //<<FIXME>>test for shrink?\r
384                         return SQTrue;\r
385                 }\r
386         }\r
387         return SQFalse;\r
388 }\r
389 \r
390 void RefTable::Resize(SQUnsignedInteger size)\r
391 {\r
392         RefNode **oldbuffer = _buckets;\r
393         RefNode *oldnodes = (RefNode *)&_buckets[_numofslots];\r
394         SQUnsignedInteger oldnumofslots = _numofslots;\r
395         SQUnsignedInteger oldbuffersize = _buffersize;\r
396         AllocNodes(size);\r
397         //rehash\r
398         for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {\r
399                 if(type(oldnodes->obj) != OT_NULL) {\r
400                         //add back;\r
401                         assert(oldnodes->refs != 0);\r
402                         RefNode *nn = Add(::HashObj(oldnodes->obj)&(_numofslots-1),oldnodes->obj);\r
403                         nn->refs = oldnodes->refs; \r
404                         oldnodes->obj = _null_;\r
405                 }\r
406                 oldnodes++;\r
407         }\r
408         SQ_FREE(oldbuffer,oldbuffersize);\r
409 }\r
410 \r
411 RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)\r
412 {\r
413         RefNode *t = _buckets[mainpos];\r
414         RefNode *newnode = _freelist;\r
415         newnode->obj = obj;\r
416         _buckets[mainpos] = newnode;\r
417         _freelist = _freelist->next;\r
418         newnode->next = t;\r
419         assert(newnode->refs == 0);\r
420         _slotused++;\r
421         return newnode;\r
422 }\r
423 \r
424 RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)\r
425 {\r
426         RefNode *ref;\r
427         mainpos = ::HashObj(obj)&(_numofslots-1);\r
428         *prev = NULL;\r
429         for (ref = _buckets[mainpos]; ref; ) {\r
430                 if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))\r
431                         break;\r
432                 *prev = ref;\r
433                 ref = ref->next;\r
434         }\r
435         if(ref == NULL && add) {\r
436                 if(_numofslots == _slotused) {\r
437                         Resize(_numofslots*2);\r
438                 }\r
439                 ref = Add(mainpos,obj);\r
440         }\r
441         return ref;\r
442 }\r
443 \r
444 void RefTable::AllocNodes(SQUnsignedInteger size)\r
445 {\r
446         RefNode **bucks;\r
447         RefNode *firstnode;\r
448         _buffersize = size * sizeof(RefNode *) + size * sizeof(RefNode);\r
449         bucks = (RefNode **)SQ_MALLOC(_buffersize);\r
450         firstnode = (RefNode *)&bucks[size];\r
451         RefNode *temp = firstnode;\r
452         SQUnsignedInteger n;\r
453         for(n = 0; n < size - 1; n++) {\r
454                 bucks[n] = NULL;\r
455                 temp->refs = 0;\r
456                 new (&temp->obj) SQObjectPtr;\r
457                 temp->next = temp+1;\r
458                 temp++;\r
459         }\r
460         bucks[n] = NULL;\r
461         temp->refs = 0;\r
462         new (&temp->obj) SQObjectPtr;\r
463         temp->next = NULL;\r
464         _freelist = firstnode;\r
465         _buckets = bucks;\r
466         _slotused = 0;\r
467         _numofslots = size;\r
468 }\r
469 //////////////////////////////////////////////////////////////////////////\r
470 //StringTable\r
471 /*\r
472 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)\r
473 * http://www.lua.org/copyright.html#4\r
474 * http://www.lua.org/source/4.0.1/src_lstring.c.html\r
475 */\r
476 \r
477 StringTable::StringTable()\r
478 {\r
479         AllocNodes(4);\r
480         _slotused = 0;\r
481 }\r
482 \r
483 StringTable::~StringTable()\r
484 {\r
485         SQ_FREE(_strings,sizeof(SQString*)*_numofslots);\r
486         _strings=NULL;\r
487 }\r
488 \r
489 void StringTable::AllocNodes(SQInteger size)\r
490 {\r
491         _numofslots=size;\r
492         //_slotused=0;\r
493         _strings=(SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);\r
494         memset(_strings,0,sizeof(SQString*)*_numofslots);\r
495 }\r
496 \r
497 SQString *StringTable::Add(const SQChar *news,SQInteger len)\r
498 {\r
499         if(len<0)\r
500                 len = (SQInteger)scstrlen(news);\r
501         SQHash h = ::_hashstr(news,len)&(_numofslots-1);\r
502         SQString *s;\r
503         for (s = _strings[h]; s; s = s->_next){\r
504                 if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))\r
505                         return s; //found\r
506         }\r
507 \r
508         SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));\r
509         new (t) SQString;\r
510         memcpy(t->_val,news,rsl(len));\r
511         t->_val[len] = _SC('\0');\r
512         t->_len = len;\r
513         t->_hash = ::_hashstr(news,len);\r
514         t->_next = _strings[h];\r
515         _strings[h] = t;\r
516         _slotused++;\r
517         if (_slotused > _numofslots)  /* too crowded? */\r
518                 Resize(_numofslots*2);\r
519         return t;\r
520 }\r
521 \r
522 void StringTable::Resize(SQInteger size)\r
523 {\r
524         SQInteger oldsize=_numofslots;\r
525         SQString **oldtable=_strings;\r
526         AllocNodes(size);\r
527         for (SQInteger i=0; i<oldsize; i++){\r
528                 SQString *p = oldtable[i];\r
529                 while(p){\r
530                         SQString *next = p->_next;\r
531                         SQHash h = p->_hash&(_numofslots-1);\r
532                         p->_next = _strings[h];\r
533                         _strings[h] = p;\r
534                         p = next;\r
535                 }\r
536         }\r
537         SQ_FREE(oldtable,oldsize*sizeof(SQString*));\r
538 }\r
539 \r
540 void StringTable::Remove(SQString *bs)\r
541 {\r
542         SQString *s;\r
543         SQString *prev=NULL;\r
544         SQHash h = bs->_hash&(_numofslots - 1);\r
545         \r
546         for (s = _strings[h]; s; ){\r
547                 if(s == bs){\r
548                         if(prev)\r
549                                 prev->_next = s->_next;\r
550                         else\r
551                                 _strings[h] = s->_next;\r
552                         _slotused--;\r
553                         SQInteger slen = s->_len;\r
554                         s->~SQString();\r
555                         SQ_FREE(s,sizeof(SQString) + rsl(slen));\r
556                         return;\r
557                 }\r
558                 prev = s;\r
559                 s = s->_next;\r
560         }\r
561         assert(0);//if this fail something is wrong\r
562 }\r