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