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