fd84b0a24e3fdd3f96e1d26584a09c61c0c7b989
[supertux.git] / external / 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,SQStringTable);\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         _consts = SQTable::Create(this,0);\r
149         _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz);\r
150         _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz);\r
151         _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz);\r
152         _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz);\r
153         _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz);\r
154         _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz);\r
155         _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz);\r
156         _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz);\r
157         _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz);\r
158         _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz);\r
159 \r
160 }\r
161 \r
162 SQSharedState::~SQSharedState()\r
163 {\r
164         _constructoridx = _null_;\r
165         _table(_registry)->Finalize();\r
166         _table(_consts)->Finalize();\r
167         _table(_metamethodsmap)->Finalize();\r
168         _registry = _null_;\r
169         _consts = _null_;\r
170         _metamethodsmap = _null_;\r
171         while(!_systemstrings->empty()) {\r
172                 _systemstrings->back()=_null_;\r
173                 _systemstrings->pop_back();\r
174         }\r
175         _thread(_root_vm)->Finalize();\r
176         _root_vm = _null_;\r
177         _table_default_delegate = _null_;\r
178         _array_default_delegate = _null_;\r
179         _string_default_delegate = _null_;\r
180         _number_default_delegate = _null_;\r
181         _closure_default_delegate = _null_;\r
182         _generator_default_delegate = _null_;\r
183         _thread_default_delegate = _null_;\r
184         _class_default_delegate = _null_;\r
185         _instance_default_delegate = _null_;\r
186         _weakref_default_delegate = _null_;\r
187         _refs_table.Finalize();\r
188 #ifndef NO_GARBAGE_COLLECTOR\r
189         SQCollectable *t = _gc_chain;\r
190         SQCollectable *nx = NULL;\r
191         if(t) {\r
192                 t->_uiRef++;\r
193                 while(t) {\r
194                         t->Finalize();\r
195                         nx = t->_next;\r
196                         if(nx) nx->_uiRef++;\r
197                         if(--t->_uiRef == 0)\r
198                                 t->Release();\r
199                         t = nx;\r
200                 }\r
201         }\r
202         assert(_gc_chain==NULL); //just to proove a theory\r
203         while(_gc_chain){\r
204                 _gc_chain->_uiRef++;\r
205                 _gc_chain->Release();\r
206         }\r
207 #endif\r
208 \r
209         sq_delete(_types,SQObjectPtrVec);\r
210         sq_delete(_systemstrings,SQObjectPtrVec);\r
211         sq_delete(_metamethods,SQObjectPtrVec);\r
212         sq_delete(_stringtable,SQStringTable);\r
213         if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);\r
214 }\r
215 \r
216 \r
217 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)\r
218 {\r
219         if(type(name) != OT_STRING)\r
220                 return -1;\r
221         SQObjectPtr ret;\r
222         if(_table(_metamethodsmap)->Get(name,ret)) {\r
223                 return _integer(ret);\r
224         }\r
225         return -1;\r
226 }\r
227 \r
228 #ifndef NO_GARBAGE_COLLECTOR\r
229 \r
230 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)\r
231 {\r
232         switch(type(o)){\r
233         case OT_TABLE:_table(o)->Mark(chain);break;\r
234         case OT_ARRAY:_array(o)->Mark(chain);break;\r
235         case OT_USERDATA:_userdata(o)->Mark(chain);break;\r
236         case OT_CLOSURE:_closure(o)->Mark(chain);break;\r
237         case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;\r
238         case OT_GENERATOR:_generator(o)->Mark(chain);break;\r
239         case OT_THREAD:_thread(o)->Mark(chain);break;\r
240         case OT_CLASS:_class(o)->Mark(chain);break;\r
241         case OT_INSTANCE:_instance(o)->Mark(chain);break;\r
242         default: break; //shutup compiler\r
243         }\r
244 }\r
245 \r
246 \r
247 SQInteger SQSharedState::CollectGarbage(SQVM *vm)\r
248 {\r
249         SQInteger n=0;\r
250         SQCollectable *tchain=NULL;\r
251         SQVM *vms = _thread(_root_vm);\r
252         \r
253         vms->Mark(&tchain);\r
254         SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();\r
255         _refs_table.Mark(&tchain);\r
256         MarkObject(_registry,&tchain);\r
257         MarkObject(_consts,&tchain);\r
258         MarkObject(_metamethodsmap,&tchain);\r
259         MarkObject(_table_default_delegate,&tchain);\r
260         MarkObject(_array_default_delegate,&tchain);\r
261         MarkObject(_string_default_delegate,&tchain);\r
262         MarkObject(_number_default_delegate,&tchain);\r
263         MarkObject(_generator_default_delegate,&tchain);\r
264         MarkObject(_thread_default_delegate,&tchain);\r
265         MarkObject(_closure_default_delegate,&tchain);\r
266         MarkObject(_class_default_delegate,&tchain);\r
267         MarkObject(_instance_default_delegate,&tchain);\r
268         MarkObject(_weakref_default_delegate,&tchain);\r
269         \r
270         SQCollectable *t = _gc_chain;\r
271         SQCollectable *nx = NULL;\r
272         if(t) {\r
273                 t->_uiRef++;\r
274                 while(t) {\r
275                         t->Finalize();\r
276                         nx = t->_next;\r
277                         if(nx) nx->_uiRef++;\r
278                         if(--t->_uiRef == 0)\r
279                                 t->Release();\r
280                         t = nx;\r
281                         n++;\r
282                 }\r
283         }\r
284 \r
285         t = tchain;\r
286         while(t) {\r
287                 t->UnMark();\r
288                 t = t->_next;\r
289         }\r
290         _gc_chain = tchain;\r
291         SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();\r
292         assert(z == x);\r
293         return n;\r
294 }\r
295 #endif\r
296 \r
297 #ifndef NO_GARBAGE_COLLECTOR\r
298 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)\r
299 {\r
300     c->_prev = NULL;\r
301         c->_next = *chain;\r
302         if(*chain) (*chain)->_prev = c;\r
303         *chain = c;\r
304 }\r
305 \r
306 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)\r
307 {\r
308         if(c->_prev) c->_prev->_next = c->_next;\r
309         else *chain = c->_next;\r
310         if(c->_next)\r
311                 c->_next->_prev = c->_prev;\r
312         c->_next = NULL;\r
313         c->_prev = NULL;\r
314 }\r
315 #endif\r
316 \r
317 SQChar* SQSharedState::GetScratchPad(SQInteger size)\r
318 {\r
319         SQInteger newsize;\r
320         if(size>0) {\r
321                 if(_scratchpadsize < size) {\r
322                         newsize = size + (size>>1);\r
323                         _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);\r
324                         _scratchpadsize = newsize;\r
325 \r
326                 }else if(_scratchpadsize >= (size<<5)) {\r
327                         newsize = _scratchpadsize >> 1;\r
328                         _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);\r
329                         _scratchpadsize = newsize;\r
330                 }\r
331         }\r
332         return _scratchpad;\r
333 }\r
334 \r
335 RefTable::RefTable()\r
336 {\r
337         AllocNodes(4);\r
338 }\r
339 \r
340 void RefTable::Finalize()\r
341 {\r
342         RefNode *nodes = _nodes;\r
343         for(SQUnsignedInteger n = 0; n < _numofslots; n++) {\r
344                 nodes->obj = _null_;\r
345                 nodes++;\r
346         }\r
347 }\r
348 \r
349 RefTable::~RefTable()\r
350 {\r
351         SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));\r
352 }\r
353 \r
354 #ifndef NO_GARBAGE_COLLECTOR\r
355 void RefTable::Mark(SQCollectable **chain)\r
356 {\r
357         RefNode *nodes = (RefNode *)_nodes;\r
358         for(SQUnsignedInteger n = 0; n < _numofslots; n++) {\r
359                 if(type(nodes->obj) != OT_NULL) {\r
360                         SQSharedState::MarkObject(nodes->obj,chain);\r
361                 }\r
362                 nodes++;\r
363         }\r
364 }\r
365 #endif\r
366 \r
367 void RefTable::AddRef(SQObject &obj)\r
368 {\r
369         SQHash mainpos;\r
370         RefNode *prev;\r
371         RefNode *ref = Get(obj,mainpos,&prev,true);\r
372         ref->refs++;\r
373 }\r
374 \r
375 SQBool RefTable::Release(SQObject &obj)\r
376 {\r
377         SQHash mainpos;\r
378         RefNode *prev;\r
379         RefNode *ref = Get(obj,mainpos,&prev,false);\r
380         if(ref) {\r
381                 if(--ref->refs == 0) {\r
382                         SQObjectPtr o = ref->obj;\r
383                         if(prev) {\r
384                                 prev->next = ref->next;\r
385                         }\r
386                         else {\r
387                                 _buckets[mainpos] = ref->next;\r
388                         }\r
389                         ref->next = _freelist;\r
390                         _freelist = ref;\r
391                         _slotused--;\r
392                         ref->obj = _null_;\r
393                         //<<FIXME>>test for shrink?\r
394                         return SQTrue;\r
395                 }\r
396         }\r
397         else {\r
398                 assert(0);\r
399         }\r
400         return SQFalse;\r
401 }\r
402 \r
403 void RefTable::Resize(SQUnsignedInteger size)\r
404 {\r
405         RefNode **oldbucks = _buckets;\r
406         RefNode *t = _nodes;\r
407         SQUnsignedInteger oldnumofslots = _numofslots;\r
408         AllocNodes(size);\r
409         //rehash\r
410         SQUnsignedInteger nfound = 0;\r
411         for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {\r
412                 if(type(t->obj) != OT_NULL) {\r
413                         //add back;\r
414                         assert(t->refs != 0);\r
415                         RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);\r
416                         nn->refs = t->refs; \r
417                         t->obj = _null_;\r
418                         nfound++;\r
419                 }\r
420                 t++;\r
421         }\r
422         assert(nfound == oldnumofslots);\r
423         SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));\r
424 }\r
425 \r
426 RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)\r
427 {\r
428         RefNode *t = _buckets[mainpos];\r
429         RefNode *newnode = _freelist;\r
430         newnode->obj = obj;\r
431         _buckets[mainpos] = newnode;\r
432         _freelist = _freelist->next;\r
433         newnode->next = t;\r
434         assert(newnode->refs == 0);\r
435         _slotused++;\r
436         return newnode;\r
437 }\r
438 \r
439 RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)\r
440 {\r
441         RefNode *ref;\r
442         mainpos = ::HashObj(obj)&(_numofslots-1);\r
443         *prev = NULL;\r
444         for (ref = _buckets[mainpos]; ref; ) {\r
445                 if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))\r
446                         break;\r
447                 *prev = ref;\r
448                 ref = ref->next;\r
449         }\r
450         if(ref == NULL && add) {\r
451                 if(_numofslots == _slotused) {\r
452                         assert(_freelist == 0);\r
453                         Resize(_numofslots*2);\r
454                         mainpos = ::HashObj(obj)&(_numofslots-1);\r
455                 }\r
456                 ref = Add(mainpos,obj);\r
457         }\r
458         return ref;\r
459 }\r
460 \r
461 void RefTable::AllocNodes(SQUnsignedInteger size)\r
462 {\r
463         RefNode **bucks;\r
464         RefNode *nodes;\r
465         bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));\r
466         nodes = (RefNode *)&bucks[size];\r
467         RefNode *temp = nodes;\r
468         SQUnsignedInteger n;\r
469         for(n = 0; n < size - 1; n++) {\r
470                 bucks[n] = NULL;\r
471                 temp->refs = 0;\r
472                 new (&temp->obj) SQObjectPtr;\r
473                 temp->next = temp+1;\r
474                 temp++;\r
475         }\r
476         bucks[n] = NULL;\r
477         temp->refs = 0;\r
478         new (&temp->obj) SQObjectPtr;\r
479         temp->next = NULL;\r
480         _freelist = nodes;\r
481         _nodes = nodes;\r
482         _buckets = bucks;\r
483         _slotused = 0;\r
484         _numofslots = size;\r
485 }\r
486 //////////////////////////////////////////////////////////////////////////\r
487 //SQStringTable\r
488 /*\r
489 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)\r
490 * http://www.lua.org/copyright.html#4\r
491 * http://www.lua.org/source/4.0.1/src_lstring.c.html\r
492 */\r
493 \r
494 SQStringTable::SQStringTable()\r
495 {\r
496         AllocNodes(4);\r
497         _slotused = 0;\r
498 }\r
499 \r
500 SQStringTable::~SQStringTable()\r
501 {\r
502         SQ_FREE(_strings,sizeof(SQString*)*_numofslots);\r
503         _strings = NULL;\r
504 }\r
505 \r
506 void SQStringTable::AllocNodes(SQInteger size)\r
507 {\r
508         _numofslots = size;\r
509         _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);\r
510         memset(_strings,0,sizeof(SQString*)*_numofslots);\r
511 }\r
512 \r
513 SQString *SQStringTable::Add(const SQChar *news,SQInteger len)\r
514 {\r
515         if(len<0)\r
516                 len = (SQInteger)scstrlen(news);\r
517         SQHash h = ::_hashstr(news,len)&(_numofslots-1);\r
518         SQString *s;\r
519         for (s = _strings[h]; s; s = s->_next){\r
520                 if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))\r
521                         return s; //found\r
522         }\r
523 \r
524         SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));\r
525         new (t) SQString;\r
526         memcpy(t->_val,news,rsl(len));\r
527         t->_val[len] = _SC('\0');\r
528         t->_len = len;\r
529         t->_hash = ::_hashstr(news,len);\r
530         t->_next = _strings[h];\r
531         _strings[h] = t;\r
532         _slotused++;\r
533         if (_slotused > _numofslots)  /* too crowded? */\r
534                 Resize(_numofslots*2);\r
535         return t;\r
536 }\r
537 \r
538 void SQStringTable::Resize(SQInteger size)\r
539 {\r
540         SQInteger oldsize=_numofslots;\r
541         SQString **oldtable=_strings;\r
542         AllocNodes(size);\r
543         for (SQInteger i=0; i<oldsize; i++){\r
544                 SQString *p = oldtable[i];\r
545                 while(p){\r
546                         SQString *next = p->_next;\r
547                         SQHash h = p->_hash&(_numofslots-1);\r
548                         p->_next = _strings[h];\r
549                         _strings[h] = p;\r
550                         p = next;\r
551                 }\r
552         }\r
553         SQ_FREE(oldtable,oldsize*sizeof(SQString*));\r
554 }\r
555 \r
556 void SQStringTable::Remove(SQString *bs)\r
557 {\r
558         SQString *s;\r
559         SQString *prev=NULL;\r
560         SQHash h = bs->_hash&(_numofslots - 1);\r
561         \r
562         for (s = _strings[h]; s; ){\r
563                 if(s == bs){\r
564                         if(prev)\r
565                                 prev->_next = s->_next;\r
566                         else\r
567                                 _strings[h] = s->_next;\r
568                         _slotused--;\r
569                         SQInteger slen = s->_len;\r
570                         s->~SQString();\r
571                         SQ_FREE(s,sizeof(SQString) + rsl(slen));\r
572                         return;\r
573                 }\r
574                 prev = s;\r
575                 s = s->_next;\r
576         }\r
577         assert(0);//if this fail something is wrong\r
578 }\r