# sqstate.cpp

    00001 /*
    00002   see copyright notice in squirrel.h
    00003 */
    00004 #include "sqpcheader.h"
    00005 #include "sqopcodes.h"
    00006 #include "sqvm.h"
    00007 #include "sqfuncproto.h"
    00008 #include "sqclosure.h"
    00009 #include "sqstring.h"
    00010 #include "sqtable.h"
    00011 #include "sqarray.h"
    00012 #include "squserdata.h"
    00013 #include "sqclass.h"
    00014
    00015 SQObjectPtr _null_;
    00016 SQObjectPtr _true_(true);
    00017 SQObjectPtr _false_(false);
    00018 SQObjectPtr _one_((SQInteger)1);
    00019 SQObjectPtr _minusone_((SQInteger)-1);
    00020
    00021 SQSharedState::SQSharedState()
    00022 {
    00023   _compilererrorhandler = NULL;
    00024   _printfunc = NULL;
    00025   _debuginfo = false;
    00026   _notifyallexceptions = false;
    00027 }
    00028
    00029 #define newsysstring(s) { \
    00030   _systemstrings->push_back(SQString::Create(this,s));  \
    00031   }
    00032
    00033 #define newmetamethod(s) {  \
    00034   _metamethods->push_back(SQString::Create(this,s));  \
    00035   _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
    00036   }
    00037
    00038 bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
    00039 {
    00040   SQInteger i = 0;
    00041
    00042   SQInteger mask = 0;
    00043   while(typemask[i] != 0) {
    00044
    00045     switch(typemask[i]){
    00046         case 'o': mask |= _RT_NULL; break;
    00047         case 'i': mask |= _RT_INTEGER; break;
    00048         case 'f': mask |= _RT_FLOAT; break;
    00049         case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break;
    00050         case 's': mask |= _RT_STRING; break;
    00051         case 't': mask |= _RT_TABLE; break;
    00052         case 'a': mask |= _RT_ARRAY; break;
    00053         case 'u': mask |= _RT_USERDATA; break;
    00054         case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break;
    00055         case 'b': mask |= _RT_BOOL; break;
    00056         case 'g': mask |= _RT_GENERATOR; break;
    00057         case 'p': mask |= _RT_USERPOINTER; break;
    00058         case 'v': mask |= _RT_THREAD; break;
    00059         case 'x': mask |= _RT_INSTANCE; break;
    00060         case 'y': mask |= _RT_CLASS; break;
    00061         case 'r': mask |= _RT_WEAKREF; break;
    00062         case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
    00063         case ' ': i++; continue; //ignores spaces
    00064         default:
    00065           return false;
    00066     }
    00067     i++;
    00068     if(typemask[i] == '|') {
    00069       i++;
    00070       if(typemask[i] == 0)
    00071         return false;
    00072       continue;
    00073     }
    00074     res.push_back(mask);
    00075     mask = 0;
    00076
    00077   }
    00078   return true;
    00079 }
    00080
    00081 SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz)
    00082 {
    00083   SQInteger i=0;
    00084   SQTable *t=SQTable::Create(ss,0);
    00085   while(funcz[i].name!=0){
    00086     SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
    00087     nc->_nparamscheck = funcz[i].nparamscheck;
    00088     nc->_name = SQString::Create(ss,funcz[i].name);
    00089     if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
    00090       return NULL;
    00091     t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
    00092     i++;
    00093   }
    00094   return t;
    00095 }
    00096
    00097 void SQSharedState::Init()
    00098 {
    00099   _scratchpad=NULL;
    00100   _scratchpadsize=0;
    00101 #ifndef NO_GARBAGE_COLLECTOR
    00102   _gc_chain=NULL;
    00103 #endif
    00104   sq_new(_stringtable,StringTable);
    00105   sq_new(_metamethods,SQObjectPtrVec);
    00106   sq_new(_systemstrings,SQObjectPtrVec);
    00107   sq_new(_types,SQObjectPtrVec);
    00108   _metamethodsmap = SQTable::Create(this,MT_LAST-1);
    00109   //adding type strings to avoid memory trashing
    00110   //types names
    00111   newsysstring(_SC("null"));
    00112   newsysstring(_SC("table"));
    00113   newsysstring(_SC("array"));
    00114   newsysstring(_SC("closure"));
    00115   newsysstring(_SC("string"));
    00116   newsysstring(_SC("userdata"));
    00117   newsysstring(_SC("integer"));
    00118   newsysstring(_SC("float"));
    00119   newsysstring(_SC("userpointer"));
    00120   newsysstring(_SC("function"));
    00121   newsysstring(_SC("generator"));
    00122   newsysstring(_SC("thread"));
    00123   newsysstring(_SC("class"));
    00124   newsysstring(_SC("instance"));
    00125   newsysstring(_SC("bool"));
    00126   //meta methods
    00127   newmetamethod(MM_ADD);
    00128   newmetamethod(MM_SUB);
    00129   newmetamethod(MM_MUL);
    00130   newmetamethod(MM_DIV);
    00131   newmetamethod(MM_UNM);
    00132   newmetamethod(MM_MODULO);
    00133   newmetamethod(MM_SET);
    00134   newmetamethod(MM_GET);
    00135   newmetamethod(MM_TYPEOF);
    00136   newmetamethod(MM_NEXTI);
    00137   newmetamethod(MM_CMP);
    00138   newmetamethod(MM_CALL);
    00139   newmetamethod(MM_CLONED);
    00140   newmetamethod(MM_NEWSLOT);
    00141   newmetamethod(MM_DELSLOT);
    00142   newmetamethod(MM_TOSTRING);
    00143   newmetamethod(MM_NEWMEMBER);
    00144   newmetamethod(MM_INHERITED);
    00145
    00146   _constructoridx = SQString::Create(this,_SC("constructor"));
    00147   _registry = SQTable::Create(this,0);
    00148   _consts = SQTable::Create(this,0);
    00149   _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz);
    00150   _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz);
    00151   _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz);
    00152   _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz);
    00153   _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz);
    00154   _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz);
    00155   _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz);
    00156   _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz);
    00157   _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz);
    00158   _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz);
    00159
    00160 }
    00161
    00162 SQSharedState::~SQSharedState()
    00163 {
    00164   _constructoridx = _null_;
    00165   _table(_registry)->Finalize();
    00166   _table(_consts)->Finalize();
    00167   _table(_metamethodsmap)->Finalize();
    00168   _registry = _null_;
    00169   _consts = _null_;
    00170   _metamethodsmap = _null_;
    00171   while(!_systemstrings->empty()) {
    00172     _systemstrings->back()=_null_;
    00173     _systemstrings->pop_back();
    00174   }
    00175   _thread(_root_vm)->Finalize();
    00176   _root_vm = _null_;
    00177   _table_default_delegate = _null_;
    00178   _array_default_delegate = _null_;
    00179   _string_default_delegate = _null_;
    00180   _number_default_delegate = _null_;
    00181   _closure_default_delegate = _null_;
    00182   _generator_default_delegate = _null_;
    00183   _thread_default_delegate = _null_;
    00184   _class_default_delegate = _null_;
    00185   _instance_default_delegate = _null_;
    00186   _weakref_default_delegate = _null_;
    00187   _refs_table.Finalize();
    00188 #ifndef NO_GARBAGE_COLLECTOR
    00189   SQCollectable *t = _gc_chain;
    00190   SQCollectable *nx = NULL;
    00191   while(t) {
    00192     t->_uiRef++;
    00193     t = t->_next;
    00194   }
    00195   t = _gc_chain;
    00196   while(t) {
    00197     t->UnMark();
    00198     t->Finalize();
    00199     nx = t->_next;
    00200     if(--t->_uiRef == 0)
    00201       t->Release();
    00202     t=nx;
    00203   }
    00204 //  assert(_gc_chain==NULL); //just to proove a theory
    00205   while(_gc_chain){
    00206     _gc_chain->_uiRef--;
    00207     _gc_chain->Release();
    00208   }
    00209 #endif
    00210
    00211   sq_delete(_types,SQObjectPtrVec);
    00212   sq_delete(_systemstrings,SQObjectPtrVec);
    00213   sq_delete(_metamethods,SQObjectPtrVec);
    00214   sq_delete(_stringtable,StringTable);
    00215   if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
    00216 }
    00217
    00218
    00219 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
    00220 {
    00221   if(type(name) != OT_STRING)
    00222     return -1;
    00223   SQObjectPtr ret;
    00224   if(_table(_metamethodsmap)->Get(name,ret)) {
    00225     return _integer(ret);
    00226   }
    00227   return -1;
    00228 }
    00229
    00230 #ifndef NO_GARBAGE_COLLECTOR
    00231
    00232 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
    00233 {
    00234   switch(type(o)){
    00235   case OT_TABLE:_table(o)->Mark(chain);break;
    00236   case OT_ARRAY:_array(o)->Mark(chain);break;
    00237   case OT_USERDATA:_userdata(o)->Mark(chain);break;
    00238   case OT_CLOSURE:_closure(o)->Mark(chain);break;
    00239   case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
    00240   case OT_GENERATOR:_generator(o)->Mark(chain);break;
    00241   case OT_THREAD:_thread(o)->Mark(chain);break;
    00242   case OT_CLASS:_class(o)->Mark(chain);break;
    00243   case OT_INSTANCE:_instance(o)->Mark(chain);break;
    00244   default: break; //shutup compiler
    00245   }
    00246 }
    00247
    00248
    00249 SQInteger SQSharedState::CollectGarbage(SQVM *vm)
    00250 {
    00251   SQInteger n=0;
    00252   SQCollectable *tchain=NULL;
    00253   SQVM *vms = _thread(_root_vm);
    00254
    00255   vms->Mark(&tchain);
    00256   SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
    00257   _refs_table.Mark(&tchain);
    00258   MarkObject(_registry,&tchain);
    00259   MarkObject(_consts,&tchain);
    00260   MarkObject(_metamethodsmap,&tchain);
    00261   MarkObject(_table_default_delegate,&tchain);
    00262   MarkObject(_array_default_delegate,&tchain);
    00263   MarkObject(_string_default_delegate,&tchain);
    00264   MarkObject(_number_default_delegate,&tchain);
    00265   MarkObject(_generator_default_delegate,&tchain);
    00266   MarkObject(_thread_default_delegate,&tchain);
    00267   MarkObject(_closure_default_delegate,&tchain);
    00268   MarkObject(_class_default_delegate,&tchain);
    00269   MarkObject(_instance_default_delegate,&tchain);
    00270   MarkObject(_weakref_default_delegate,&tchain);
    00271
    00272   SQCollectable *t = _gc_chain;
    00273   SQCollectable *nx = NULL;
    00274   while(t) {
    00275     t->_uiRef++;
    00276     t->Finalize();
    00277     nx = t->_next;
    00278     if(--t->_uiRef == 0)
    00279       t->Release();
    00280     t = nx;
    00281     n++;
    00282   }
    00283
    00284   t = tchain;
    00285   while(t) {
    00286     t->UnMark();
    00287     t = t->_next;
    00288   }
    00289   _gc_chain = tchain;
    00290   SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
    00291   assert(z == x);
    00292   return n;
    00293 }
    00294 #endif
    00295
    00296 #ifndef NO_GARBAGE_COLLECTOR
    00297 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
    00298 {
    00299     c->_prev = NULL;
    00300   c->_next = *chain;
    00301   if(*chain) (*chain)->_prev = c;
    00302   *chain = c;
    00303 }
    00304
    00305 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
    00306 {
    00307   if(c->_prev) c->_prev->_next = c->_next;
    00308   else *chain = c->_next;
    00309   if(c->_next)
    00310     c->_next->_prev = c->_prev;
    00311   c->_next = NULL;
    00312   c->_prev = NULL;
    00313 }
    00314 #endif
    00315
    00316 SQChar* SQSharedState::GetScratchPad(SQInteger size)
    00317 {
    00318   SQInteger newsize;
    00319   if(size>0) {
    00320     if(_scratchpadsize < size) {
    00321       newsize = size + (size>>1);
    00322       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
    00323       _scratchpadsize = newsize;
    00324
    00325     }else if(_scratchpadsize >= (size<<5)) {
    00326       newsize = _scratchpadsize >> 1;
    00327       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
    00328       _scratchpadsize = newsize;
    00329     }
    00330   }
    00331   return _scratchpad;
    00332 }
    00333
    00334 RefTable::RefTable()
    00335 {
    00336   AllocNodes(4);
    00337 }
    00338
    00339 void RefTable::Finalize()
    00340 {
    00341   RefNode *nodes = _nodes;
    00342   for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
    00343     nodes->obj = _null_;
    00344     nodes++;
    00345   }
    00346 }
    00347
    00348 RefTable::~RefTable()
    00349 {
    00350   SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));
    00351 }
    00352
    00353 #ifndef NO_GARBAGE_COLLECTOR
    00354 void RefTable::Mark(SQCollectable **chain)
    00355 {
    00356   RefNode *nodes = (RefNode *)_nodes;
    00357   for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
    00358     if(type(nodes->obj) != OT_NULL) {
    00359       SQSharedState::MarkObject(nodes->obj,chain);
    00360     }
    00361     nodes++;
    00362   }
    00363 }
    00364 #endif
    00365
    00366 void RefTable::AddRef(SQObject &obj)
    00367 {
    00368   SQHash mainpos;
    00369   RefNode *prev;
    00370   RefNode *ref = Get(obj,mainpos,&prev,true);
    00371   ref->refs++;
    00372 }
    00373
    00374 SQBool RefTable::Release(SQObject &obj)
    00375 {
    00376   SQHash mainpos;
    00377   RefNode *prev;
    00378   RefNode *ref = Get(obj,mainpos,&prev,false);
    00379   if(ref) {
    00380     if(--ref->refs == 0) {
    00381       SQObjectPtr o = ref->obj;
    00382       if(prev) {
    00383         prev->next = ref->next;
    00384       }
    00385       else {
    00386         _buckets[mainpos] = ref->next;
    00387       }
    00388       ref->next = _freelist;
    00389       _freelist = ref;
    00390       _slotused--;
    00391       ref->obj = _null_;
    00392       //<<FIXME>>test for shrink?
    00393       return SQTrue;
    00394     }
    00395   }
    00396   else {
    00397     assert(0);
    00398   }
    00399   return SQFalse;
    00400 }
    00401
    00402 void RefTable::Resize(SQUnsignedInteger size)
    00403 {
    00404   RefNode **oldbucks = _buckets;
    00405   RefNode *t = _nodes;
    00406   SQUnsignedInteger oldnumofslots = _numofslots;
    00407   AllocNodes(size);
    00408   //rehash
    00409   SQUnsignedInteger nfound = 0;
    00410   for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
    00411     if(type(t->obj) != OT_NULL) {
    00412       //add back;
    00413       assert(t->refs != 0);
    00414       RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);
    00415       nn->refs = t->refs;
    00416       t->obj = _null_;
    00417       nfound++;
    00418     }
    00419     t++;
    00420   }
    00421   assert(nfound == oldnumofslots);
    00422   SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));
    00423 }
    00424
    00425 RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)
    00426 {
    00427   RefNode *t = _buckets[mainpos];
    00428   RefNode *newnode = _freelist;
    00429   newnode->obj = obj;
    00430   _buckets[mainpos] = newnode;
    00431   _freelist = _freelist->next;
    00432   newnode->next = t;
    00433   assert(newnode->refs == 0);
    00434   _slotused++;
    00435   return newnode;
    00436 }
    00437
    00438 RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)
    00439 {
    00440   RefNode *ref;
    00441   mainpos = ::HashObj(obj)&(_numofslots-1);
    00442   *prev = NULL;
    00443   for (ref = _buckets[mainpos]; ref; ) {
    00444     if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))
    00445       break;
    00446     *prev = ref;
    00447     ref = ref->next;
    00448   }
    00449   if(ref == NULL && add) {
    00450     if(_numofslots == _slotused) {
    00451       assert(_freelist == 0);
    00452       Resize(_numofslots*2);
    00453       mainpos = ::HashObj(obj)&(_numofslots-1);
    00454     }
    00455     ref = Add(mainpos,obj);
    00456   }
    00457   return ref;
    00458 }
    00459
    00460 void RefTable::AllocNodes(SQUnsignedInteger size)
    00461 {
    00462   RefNode **bucks;
    00463   RefNode *nodes;
    00464   bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));
    00465   nodes = (RefNode *)&bucks[size];
    00466   RefNode *temp = nodes;
    00467   SQUnsignedInteger n;
    00468   for(n = 0; n < size - 1; n++) {
    00469     bucks[n] = NULL;
    00470     temp->refs = 0;
    00471     new (&temp->obj) SQObjectPtr;
    00472     temp->next = temp+1;
    00473     temp++;
    00474   }
    00475   bucks[n] = NULL;
    00476   temp->refs = 0;
    00477   new (&temp->obj) SQObjectPtr;
    00478   temp->next = NULL;
    00479   _freelist = nodes;
    00480   _nodes = nodes;
    00481   _buckets = bucks;
    00482   _slotused = 0;
    00483   _numofslots = size;
    00484 }
    00486 //StringTable
    00487 /*
    00488 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
    00489 * http://www.lua.org/copyright.html#4
    00490 * http://www.lua.org/source/4.0.1/src_lstring.c.html
    00491 */
    00492
    00493 StringTable::StringTable()
    00494 {
    00495   AllocNodes(4);
    00496   _slotused = 0;
    00497 }
    00498
    00499 StringTable::~StringTable()
    00500 {
    00501   SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
    00502   _strings = NULL;
    00503 }
    00504
    00505 void StringTable::AllocNodes(SQInteger size)
    00506 {
    00507   _numofslots = size;
    00508   _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
    00509   memset(_strings,0,sizeof(SQString*)*_numofslots);
    00510 }
    00511
    00512 SQString *StringTable::Add(const SQChar *news,SQInteger len)
    00513 {
    00514   if(len<0)
    00515     len = (SQInteger)scstrlen(news);
    00516   SQHash h = ::_hashstr(news,len)&(_numofslots-1);
    00517   SQString *s;
    00518   for (s = _strings[h]; s; s = s->_next){
    00519     if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))
    00520       return s; //found
    00521   }
    00522
    00523   SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));
    00524   new (t) SQString;
    00525   memcpy(t->_val,news,rsl(len));
    00526   t->_val[len] = _SC('\0');
    00527   t->_len = len;
    00528   t->_hash = ::_hashstr(news,len);
    00529   t->_next = _strings[h];
    00530   _strings[h] = t;
    00531   _slotused++;
    00532   if (_slotused > _numofslots)  /* too crowded? */
    00533     Resize(_numofslots*2);
    00534   return t;
    00535 }
    00536
    00537 void StringTable::Resize(SQInteger size)
    00538 {
    00539   SQInteger oldsize=_numofslots;
    00540   SQString **oldtable=_strings;
    00541   AllocNodes(size);
    00542   for (SQInteger i=0; i<oldsize; i++){
    00543     SQString *p = oldtable[i];
    00544     while(p){
    00545       SQString *next = p->_next;
    00546       SQHash h = p->_hash&(_numofslots-1);
    00547       p->_next = _strings[h];
    00548       _strings[h] = p;
    00549       p = next;
    00550     }
    00551   }
    00552   SQ_FREE(oldtable,oldsize*sizeof(SQString*));
    00553 }
    00554
    00555 void StringTable::Remove(SQString *bs)
    00556 {
    00557   SQString *s;
    00558   SQString *prev=NULL;
    00559   SQHash h = bs->_hash&(_numofslots - 1);
    00560
    00561   for (s = _strings[h]; s; ){
    00562     if(s == bs){
    00563       if(prev)
    00564         prev->_next = s->_next;
    00565       else
    00566         _strings[h] = s->_next;
    00567       _slotused--;
    00568       SQInteger slen = s->_len;
    00569       s->~SQString();
    00570       SQ_FREE(s,sizeof(SQString) + rsl(slen));
    00571       return;
    00572     }
    00573     prev = s;
    00574     s = s->_next;
    00575   }
    00576   assert(0);//if this fail something is wrong
    00577 }