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