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