updated squirrel version
[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_(1);
19 SQObjectPtr _minusone_(-1);
20
21 SQSharedState::SQSharedState()
22 {
23         _compilererrorhandler = NULL;
24         _printfunc = NULL;
25         _debuginfo = false;
26 }
27
28 #define newsysstring(s) {       \
29         _systemstrings->push_back(SQString::Create(this,s));    \
30         }
31
32 #define newmetamethod(s) {      \
33         _metamethods->push_back(SQString::Create(this,s));      \
34         _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
35         }
36
37 bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
38 {
39         int i = 0;
40         
41         int mask = 0;
42         while(typemask[i] != 0) {
43                 
44                 switch(typemask[i]){
45                                 case 'o': mask |= _RT_NULL; break;
46                                 case 'i': mask |= _RT_INTEGER; break;
47                                 case 'f': mask |= _RT_FLOAT; break;
48                                 case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;
49                                 case 's': mask |= _RT_STRING; break;
50                                 case 't': mask |= _RT_TABLE; break;
51                                 case 'a': mask |= _RT_ARRAY; break;
52                                 case 'u': mask |= _RT_USERDATA; break;
53                                 case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;
54                                 case 'b': mask |= _RT_BOOL; break;
55                                 case 'g': mask |= _RT_GENERATOR; break;
56                                 case 'p': mask |= _RT_USERPOINTER; break;
57                                 case 'v': mask |= _RT_THREAD; break;
58                                 case 'x': mask |= _RT_INSTANCE; break;
59                                 case 'y': mask |= _RT_CLASS; break;
60                                 case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
61                                 case ' ': i++; continue; //ignores spaces
62                                 default:
63                                         return false;
64                 }
65                 i++;
66                 if(typemask[i] == '|') { 
67                         i++; 
68                         if(typemask[i] == 0)
69                                 return false;
70                         continue; 
71                 }
72                 res.push_back(mask);
73                 mask = 0;
74                 
75         }
76         return true;
77 }
78
79 SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)
80 {
81         int i=0;
82         SQTable *t=SQTable::Create(ss,0);
83         while(funcz[i].name!=0){
84                 SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
85                 nc->_nparamscheck = funcz[i].nparamscheck;
86                 nc->_name = SQString::Create(ss,funcz[i].name);
87                 if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
88                         return NULL;
89                 t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
90                 i++;
91         }
92         return t;
93 }
94
95 void SQSharedState::Init()
96 {       
97         _scratchpad=NULL;
98         _scratchpadsize=0;
99 #ifndef NO_GARBAGE_COLLECTOR
100         _gc_chain=NULL;
101 #endif
102         sq_new(_stringtable,StringTable);
103         sq_new(_metamethods,SQObjectPtrVec);
104         sq_new(_systemstrings,SQObjectPtrVec);
105         sq_new(_types,SQObjectPtrVec);
106         _metamethodsmap = SQTable::Create(this,MT_LAST-1);
107         //adding type strings to avoid memory trashing
108         //types names
109         newsysstring(_SC("null"));
110         newsysstring(_SC("table"));
111         newsysstring(_SC("array"));
112         newsysstring(_SC("closure"));
113         newsysstring(_SC("string"));
114         newsysstring(_SC("userdata"));
115         newsysstring(_SC("integer"));
116         newsysstring(_SC("float"));
117         newsysstring(_SC("userpointer"));
118         newsysstring(_SC("function"));
119         newsysstring(_SC("generator"));
120         newsysstring(_SC("thread"));
121         newsysstring(_SC("class"));
122         newsysstring(_SC("instance"));
123         newsysstring(_SC("bool"));
124         //meta methods
125         newmetamethod(MM_ADD);
126         newmetamethod(MM_SUB);
127         newmetamethod(MM_MUL);
128         newmetamethod(MM_DIV);
129         newmetamethod(MM_UNM);
130         newmetamethod(MM_MODULO);
131         newmetamethod(MM_SET);
132         newmetamethod(MM_GET);
133         newmetamethod(MM_TYPEOF);
134         newmetamethod(MM_NEXTI);
135         newmetamethod(MM_CMP);
136         newmetamethod(MM_CALL);
137         newmetamethod(MM_CLONED);
138         newmetamethod(MM_NEWSLOT);
139         newmetamethod(MM_DELSLOT);
140
141         _constructoridx = SQString::Create(this,_SC("constructor"));
142         _refs_table = SQTable::Create(this,0);
143         _registry = SQTable::Create(this,0);
144         _table_default_delegate=CreateDefaultDelegate(this,_table_default_delegate_funcz);
145         _array_default_delegate=CreateDefaultDelegate(this,_array_default_delegate_funcz);
146         _string_default_delegate=CreateDefaultDelegate(this,_string_default_delegate_funcz);
147         _number_default_delegate=CreateDefaultDelegate(this,_number_default_delegate_funcz);
148         _closure_default_delegate=CreateDefaultDelegate(this,_closure_default_delegate_funcz);
149         _generator_default_delegate=CreateDefaultDelegate(this,_generator_default_delegate_funcz);
150         _thread_default_delegate=CreateDefaultDelegate(this,_thread_default_delegate_funcz);
151         _class_default_delegate=CreateDefaultDelegate(this,_class_default_delegate_funcz);
152         _instance_default_delegate=CreateDefaultDelegate(this,_instance_default_delegate_funcz);
153
154 }
155
156 SQSharedState::~SQSharedState()
157 {
158         _constructoridx = _null_;
159         _table(_refs_table)->Finalize();
160         _table(_registry)->Finalize();
161         _table(_metamethodsmap)->Finalize();
162         _refs_table = _null_;
163         _registry = _null_;
164         _metamethodsmap = _null_;
165         while(!_systemstrings->empty()){
166                 _systemstrings->back()=_null_;
167                 _systemstrings->pop_back();
168         }
169         _thread(_root_vm)->Finalize();
170         _root_vm = _null_;
171         _table_default_delegate=_null_;
172         _array_default_delegate=_null_;
173         _string_default_delegate=_null_;
174         _number_default_delegate=_null_;
175         _closure_default_delegate=_null_;
176         _generator_default_delegate=_null_;
177         _thread_default_delegate=_null_;
178         _class_default_delegate=_null_;
179         _instance_default_delegate=_null_;
180         
181 #ifndef NO_GARBAGE_COLLECTOR
182         
183         
184         SQCollectable *t=_gc_chain;
185         SQCollectable *nx=NULL;
186         while(t){
187                 t->_uiRef++;
188                 t->Finalize();
189                 nx=t->_next;
190                 if(--t->_uiRef==0)
191                         t->Release();
192                 t=nx;
193         }
194         assert(_gc_chain==NULL); //just to proove a theory
195         while(_gc_chain){
196                 _gc_chain->_uiRef++;
197                 _gc_chain->Release();
198         }
199 #endif
200         sq_delete(_types,SQObjectPtrVec);
201         sq_delete(_systemstrings,SQObjectPtrVec);
202         sq_delete(_metamethods,SQObjectPtrVec);
203         sq_delete(_stringtable,StringTable);
204         if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
205 }
206
207
208 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
209 {
210         if(type(name) != OT_STRING)
211                 return -1;
212         SQObjectPtr ret;
213         if(_table(_metamethodsmap)->Get(name,ret)) {
214                 return _integer(ret);
215         }
216         return -1;
217 }
218
219 #ifndef NO_GARBAGE_COLLECTOR
220
221 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
222 {
223         switch(type(o)){
224         case OT_TABLE:_table(o)->Mark(chain);break;
225         case OT_ARRAY:_array(o)->Mark(chain);break;
226         case OT_USERDATA:_userdata(o)->Mark(chain);break;
227         case OT_CLOSURE:_closure(o)->Mark(chain);break;
228         case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
229         case OT_GENERATOR:_generator(o)->Mark(chain);break;
230         case OT_THREAD:_thread(o)->Mark(chain);break;
231         case OT_CLASS:_class(o)->Mark(chain);break;
232         case OT_INSTANCE:_instance(o)->Mark(chain);break;
233         }
234 }
235
236
237 int SQSharedState::CollectGarbage(SQVM *vm)
238 {
239         int n=0;
240         SQCollectable *tchain=NULL;
241         SQVM *vms=_thread(_root_vm);
242         
243         vms->Mark(&tchain);
244         int x = _table(_thread(_root_vm)->_roottable)->CountUsed();
245         MarkObject(_refs_table,&tchain);
246         MarkObject(_registry,&tchain);
247         MarkObject(_metamethodsmap,&tchain);
248         MarkObject(_table_default_delegate,&tchain);
249         MarkObject(_array_default_delegate,&tchain);
250         MarkObject(_string_default_delegate,&tchain);
251         MarkObject(_number_default_delegate,&tchain);
252         MarkObject(_generator_default_delegate,&tchain);
253         MarkObject(_thread_default_delegate,&tchain);
254         MarkObject(_closure_default_delegate,&tchain);
255         MarkObject(_class_default_delegate,&tchain);
256         MarkObject(_instance_default_delegate,&tchain);
257         
258         SQCollectable *t=_gc_chain;
259         SQCollectable *nx=NULL;
260         while(t){
261                 t->_uiRef++;
262                 t->Finalize();
263                 nx=t->_next;
264                 if(--t->_uiRef==0)
265                         t->Release();
266                 t=nx;
267                 n++;
268         }
269
270         t=tchain;
271         while(t){
272                 t->UnMark();
273                 t=t->_next;
274         }
275         _gc_chain=tchain;
276         int z = _table(_thread(_root_vm)->_roottable)->CountUsed();
277         assert(z == x);
278         return n;
279 }
280 #endif
281
282 #ifndef NO_GARBAGE_COLLECTOR
283 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
284 {
285     c->_prev=NULL;
286         c->_next=*chain;
287         if(*chain) (*chain)->_prev=c;
288         *chain=c;
289 }
290
291 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
292 {
293         if(c->_prev) c->_prev->_next=c->_next;
294         else *chain=c->_next;
295         if(c->_next)
296                 c->_next->_prev=c->_prev;
297         c->_next=NULL;
298         c->_prev=NULL;
299 }
300 #endif
301
302 SQChar* SQSharedState::GetScratchPad(int size)
303 {
304         int newsize;
305         if(size>0){
306                 if(_scratchpadsize<size){
307                         newsize=size+(size>>1);
308                         _scratchpad=(SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
309                         _scratchpadsize=newsize;
310
311                 }else if(_scratchpadsize>=(size<<5)){
312                         newsize=_scratchpadsize>>1;
313                         _scratchpad=(SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
314                         _scratchpadsize=newsize;
315                 }
316         }
317         return _scratchpad;
318 }
319
320 //////////////////////////////////////////////////////////////////////////
321 //StringTable
322 /*
323 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
324 * http://www.lua.org/copyright.html#4
325 * http://www.lua.org/source/4.0.1/src_lstring.c.html
326 */
327
328 int SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
329 {
330         int idx = (int)TranslateIndex(refpos);
331         while(idx < _len){
332                 outkey = (SQInteger)idx;
333                 outval = SQInteger(_val[idx]);
334                 //return idx for the next iteration
335                 return ++idx;
336         }
337         //nothing to iterate anymore
338         return -1;
339 }
340
341 StringTable::StringTable()
342 {
343         AllocNodes(4);
344         _slotused = 0;
345 }
346
347 StringTable::~StringTable()
348 {
349         SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
350         _strings=NULL;
351 }
352
353 void StringTable::AllocNodes(int size)
354 {
355         _numofslots=size;
356         //_slotused=0;
357         _strings=(SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
358         memset(_strings,0,sizeof(SQString*)*_numofslots);
359 }
360
361 SQString *StringTable::Add(const SQChar *news,int len)
362 {
363         if(len<0)
364                 len=scstrlen(news);
365         unsigned int h=::_hashstr(news,len)&(_numofslots-1);
366         SQString *s;
367         for (s = _strings[h]; s; s = s->_next){
368                 if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))
369                         return s; //found
370         }
371
372         SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));
373         new (t) SQString;
374         memcpy(t->_val,news,rsl(len));
375         t->_val[len]=_SC('\0');
376         t->_len=len;
377         t->_hash=::_hashstr(news,len);
378         t->_next=_strings[h];
379         t->_uiRef=0;
380         _strings[h]=t;
381         _slotused++;
382         if (_slotused > _numofslots)  /* too crowded? */
383                 Resize(_numofslots*2);
384         return t;
385 }
386
387 void StringTable::Resize(int size)
388 {
389         int oldsize=_numofslots;
390         SQString **oldtable=_strings;
391         AllocNodes(size);
392         for (int i=0; i<oldsize; i++){
393                 SQString *p = oldtable[i];
394                 while(p){
395                         SQString *next = p->_next;
396                         unsigned int h=p->_hash&(_numofslots-1);
397                         p->_next=_strings[h];
398                         _strings[h] = p;
399                         p=next;
400                 }
401         }
402         SQ_FREE(oldtable,oldsize*sizeof(SQString*));
403 }
404
405 void StringTable::Remove(SQString *bs)
406 {
407         SQString *s;
408         SQString *prev=NULL;
409         unsigned int h=bs->_hash&(_numofslots-1);
410         
411         for (s = _strings[h]; s; ){
412                 if(s == bs){
413                         if(prev)
414                                 prev->_next = s->_next;
415                         else
416                                 _strings[h] = s->_next;
417                         _slotused--;
418                         int slen=s->_len;
419                         s->~SQString();
420                         SQ_FREE(s,sizeof(SQString)+rsl(slen));
421                         return;
422                 }
423                 prev = s;
424                 s = s->_next;
425         }
426         assert(0);//if this fail something is wrong
427 }