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