# 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->Finalize();
    00194     nx = t->_next;
    00195     if(--t->_uiRef == 0)
    00196       t->Release();
    00197     t=nx;
    00198   }
    00199 //  assert(_gc_chain==NULL); //just to proove a theory
    00200   while(_gc_chain){
    00201     _gc_chain->_uiRef++;
    00202     _gc_chain->Release();
    00203   }
    00204 #endif
    00205
    00206   sq_delete(_types,SQObjectPtrVec);
    00207   sq_delete(_systemstrings,SQObjectPtrVec);
    00208   sq_delete(_metamethods,SQObjectPtrVec);
    00209   sq_delete(_stringtable,StringTable);
    00210   if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
    00211 }
    00212
    00213
    00214 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name)
    00215 {
    00216   if(type(name) != OT_STRING)
    00217     return -1;
    00218   SQObjectPtr ret;
    00219   if(_table(_metamethodsmap)->Get(name,ret)) {
    00220     return _integer(ret);
    00221   }
    00222   return -1;
    00223 }
    00224
    00225 #ifndef NO_GARBAGE_COLLECTOR
    00226
    00227 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain)
    00228 {
    00229   switch(type(o)){
    00230   case OT_TABLE:_table(o)->Mark(chain);break;
    00231   case OT_ARRAY:_array(o)->Mark(chain);break;
    00232   case OT_USERDATA:_userdata(o)->Mark(chain);break;
    00233   case OT_CLOSURE:_closure(o)->Mark(chain);break;
    00234   case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break;
    00235   case OT_GENERATOR:_generator(o)->Mark(chain);break;
    00236   case OT_THREAD:_thread(o)->Mark(chain);break;
    00237   case OT_CLASS:_class(o)->Mark(chain);break;
    00238   case OT_INSTANCE:_instance(o)->Mark(chain);break;
    00239   default: break; //shutup compiler
    00240   }
    00241 }
    00242
    00243
    00244 SQInteger SQSharedState::CollectGarbage(SQVM *vm)
    00245 {
    00246   SQInteger n=0;
    00247   SQCollectable *tchain=NULL;
    00248   SQVM *vms = _thread(_root_vm);
    00249
    00250   vms->Mark(&tchain);
    00251   SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
    00252   _refs_table.Mark(&tchain);
    00253   MarkObject(_registry,&tchain);
    00254   MarkObject(_consts,&tchain);
    00255   MarkObject(_metamethodsmap,&tchain);
    00256   MarkObject(_table_default_delegate,&tchain);
    00257   MarkObject(_array_default_delegate,&tchain);
    00258   MarkObject(_string_default_delegate,&tchain);
    00259   MarkObject(_number_default_delegate,&tchain);
    00260   MarkObject(_generator_default_delegate,&tchain);
    00261   MarkObject(_thread_default_delegate,&tchain);
    00262   MarkObject(_closure_default_delegate,&tchain);
    00263   MarkObject(_class_default_delegate,&tchain);
    00264   MarkObject(_instance_default_delegate,&tchain);
    00265   MarkObject(_weakref_default_delegate,&tchain);
    00266
    00267   SQCollectable *t = _gc_chain;
    00268   SQCollectable *nx = NULL;
    00269   while(t) {
    00270     t->_uiRef++;
    00271     t->Finalize();
    00272     nx = t->_next;
    00273     if(--t->_uiRef == 0)
    00274       t->Release();
    00275     t = nx;
    00276     n++;
    00277   }
    00278
    00279   t = tchain;
    00280   while(t) {
    00281     t->UnMark();
    00282     t = t->_next;
    00283   }
    00284   _gc_chain = tchain;
    00285   SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
    00286   assert(z == x);
    00287   return n;
    00288 }
    00289 #endif
    00290
    00291 #ifndef NO_GARBAGE_COLLECTOR
    00292 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c)
    00293 {
    00294     c->_prev = NULL;
    00295   c->_next = *chain;
    00296   if(*chain) (*chain)->_prev = c;
    00297   *chain = c;
    00298 }
    00299
    00300 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
    00301 {
    00302   if(c->_prev) c->_prev->_next = c->_next;
    00303   else *chain = c->_next;
    00304   if(c->_next)
    00305     c->_next->_prev = c->_prev;
    00306   c->_next = NULL;
    00307   c->_prev = NULL;
    00308 }
    00309 #endif
    00310
    00311 SQChar* SQSharedState::GetScratchPad(SQInteger size)
    00312 {
    00313   SQInteger newsize;
    00314   if(size>0) {
    00315     if(_scratchpadsize < size) {
    00316       newsize = size + (size>>1);
    00317       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
    00318       _scratchpadsize = newsize;
    00319
    00320     }else if(_scratchpadsize >= (size<<5)) {
    00321       newsize = _scratchpadsize >> 1;
    00322       _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
    00323       _scratchpadsize = newsize;
    00324     }
    00325   }
    00326   return _scratchpad;
    00327 }
    00328
    00329 RefTable::RefTable()
    00330 {
    00331   AllocNodes(4);
    00332 }
    00333
    00334 void RefTable::Finalize()
    00335 {
    00336   RefNode *nodes = _nodes;
    00337   for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
    00338     nodes->obj = _null_;
    00339     nodes++;
    00340   }
    00341 }
    00342
    00343 RefTable::~RefTable()
    00344 {
    00345   SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode)));
    00346 }
    00347
    00348 #ifndef NO_GARBAGE_COLLECTOR
    00349 void RefTable::Mark(SQCollectable **chain)
    00350 {
    00351   RefNode *nodes = (RefNode *)_nodes;
    00352   for(SQUnsignedInteger n = 0; n < _numofslots; n++) {
    00353     if(type(nodes->obj) != OT_NULL) {
    00354       SQSharedState::MarkObject(nodes->obj,chain);
    00355     }
    00356     nodes++;
    00357   }
    00358 }
    00359 #endif
    00360
    00361 void RefTable::AddRef(SQObject &obj)
    00362 {
    00363   SQHash mainpos;
    00364   RefNode *prev;
    00365   RefNode *ref = Get(obj,mainpos,&prev,true);
    00366   ref->refs++;
    00367 }
    00368
    00369 SQBool RefTable::Release(SQObject &obj)
    00370 {
    00371   SQHash mainpos;
    00372   RefNode *prev;
    00373   RefNode *ref = Get(obj,mainpos,&prev,false);
    00374   if(ref) {
    00375     if(--ref->refs == 0) {
    00376       SQObjectPtr o = ref->obj;
    00377       if(prev) {
    00378         prev->next = ref->next;
    00379       }
    00380       else {
    00381         _buckets[mainpos] = ref->next;
    00382       }
    00383       ref->next = _freelist;
    00384       _freelist = ref;
    00385       _slotused--;
    00386       ref->obj = _null_;
    00387       //<<FIXME>>test for shrink?
    00388       return SQTrue;
    00389     }
    00390   }
    00391   else {
    00392     assert(0);
    00393   }
    00394   return SQFalse;
    00395 }
    00396
    00397 void RefTable::Resize(SQUnsignedInteger size)
    00398 {
    00399   RefNode **oldbucks = _buckets;
    00400   RefNode *t = _nodes;
    00401   SQUnsignedInteger oldnumofslots = _numofslots;
    00402   AllocNodes(size);
    00403   //rehash
    00404   SQUnsignedInteger nfound = 0;
    00405   for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) {
    00406     if(type(t->obj) != OT_NULL) {
    00407       //add back;
    00408       assert(t->refs != 0);
    00409       RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj);
    00410       nn->refs = t->refs;
    00411       t->obj = _null_;
    00412       nfound++;
    00413     }
    00414     t++;
    00415   }
    00416   assert(nfound == oldnumofslots);
    00417   SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode)));
    00418 }
    00419
    00420 RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj)
    00421 {
    00422   RefNode *t = _buckets[mainpos];
    00423   RefNode *newnode = _freelist;
    00424   newnode->obj = obj;
    00425   _buckets[mainpos] = newnode;
    00426   _freelist = _freelist->next;
    00427   newnode->next = t;
    00428   assert(newnode->refs == 0);
    00429   _slotused++;
    00430   return newnode;
    00431 }
    00432
    00433 RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add)
    00434 {
    00435   RefNode *ref;
    00436   mainpos = ::HashObj(obj)&(_numofslots-1);
    00437   *prev = NULL;
    00438   for (ref = _buckets[mainpos]; ref; ) {
    00439     if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj))
    00440       break;
    00441     *prev = ref;
    00442     ref = ref->next;
    00443   }
    00444   if(ref == NULL && add) {
    00445     if(_numofslots == _slotused) {
    00446       assert(_freelist == 0);
    00447       Resize(_numofslots*2);
    00448       mainpos = ::HashObj(obj)&(_numofslots-1);
    00449     }
    00450     ref = Add(mainpos,obj);
    00451   }
    00452   return ref;
    00453 }
    00454
    00455 void RefTable::AllocNodes(SQUnsignedInteger size)
    00456 {
    00457   RefNode **bucks;
    00458   RefNode *nodes;
    00459   bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode)));
    00460   nodes = (RefNode *)&bucks[size];
    00461   RefNode *temp = nodes;
    00462   SQUnsignedInteger n;
    00463   for(n = 0; n < size - 1; n++) {
    00464     bucks[n] = NULL;
    00465     temp->refs = 0;
    00466     new (&temp->obj) SQObjectPtr;
    00467     temp->next = temp+1;
    00468     temp++;
    00469   }
    00470   bucks[n] = NULL;
    00471   temp->refs = 0;
    00472   new (&temp->obj) SQObjectPtr;
    00473   temp->next = NULL;
    00474   _freelist = nodes;
    00475   _nodes = nodes;
    00476   _buckets = bucks;
    00477   _slotused = 0;
    00478   _numofslots = size;
    00479 }
    00481 //StringTable
    00482 /*
    00483 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
    00484 * http://www.lua.org/copyright.html#4
    00485 * http://www.lua.org/source/4.0.1/src_lstring.c.html
    00486 */
    00487
    00488 StringTable::StringTable()
    00489 {
    00490   AllocNodes(4);
    00491   _slotused = 0;
    00492 }
    00493
    00494 StringTable::~StringTable()
    00495 {
    00496   SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
    00497   _strings = NULL;
    00498 }
    00499
    00500 void StringTable::AllocNodes(SQInteger size)
    00501 {
    00502   _numofslots = size;
    00503   _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
    00504   memset(_strings,0,sizeof(SQString*)*_numofslots);
    00505 }
    00506
    00507 SQString *StringTable::Add(const SQChar *news,SQInteger len)
    00508 {
    00509   if(len<0)
    00510     len = (SQInteger)scstrlen(news);
    00511   SQHash h = ::_hashstr(news,len)&(_numofslots-1);
    00512   SQString *s;
    00513   for (s = _strings[h]; s; s = s->_next){
    00514     if(s->_len == len && (!memcmp(news,s->_val,rsl(len))))
    00515       return s; //found
    00516   }
    00517
    00518   SQString *t=(SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString));
    00519   new (t) SQString;
    00520   memcpy(t->_val,news,rsl(len));
    00521   t->_val[len] = _SC('\0');
    00522   t->_len = len;
    00523   t->_hash = ::_hashstr(news,len);
    00524   t->_next = _strings[h];
    00525   _strings[h] = t;
    00526   _slotused++;
    00527   if (_slotused > _numofslots)  /* too crowded? */
    00528     Resize(_numofslots*2);
    00529   return t;
    00530 }
    00531
    00532 void StringTable::Resize(SQInteger size)
    00533 {
    00534   SQInteger oldsize=_numofslots;
    00535   SQString **oldtable=_strings;
    00536   AllocNodes(size);
    00537   for (SQInteger i=0; i<oldsize; i++){
    00538     SQString *p = oldtable[i];
    00539     while(p){
    00540       SQString *next = p->_next;
    00541       SQHash h = p->_hash&(_numofslots-1);
    00542       p->_next = _strings[h];
    00543       _strings[h] = p;
    00544       p = next;
    00545     }
    00546   }
    00547   SQ_FREE(oldtable,oldsize*sizeof(SQString*));
    00548 }
    00549
    00550 void StringTable::Remove(SQString *bs)
    00551 {
    00552   SQString *s;
    00553   SQString *prev=NULL;
    00554   SQHash h = bs->_hash&(_numofslots - 1);
    00555
    00556   for (s = _strings[h]; s; ){
    00557     if(s == bs){
    00558       if(prev)
    00559         prev->_next = s->_next;
    00560       else
    00561         _strings[h] = s->_next;
    00562       _slotused--;
    00563       SQInteger slen = s->_len;
    00564       s->~SQString();
    00565       SQ_FREE(s,sizeof(SQString) + rsl(slen));
    00566       return;
    00567     }
    00568     prev = s;
    00569     s = s->_next;
    00570   }
    00571   assert(0);//if this fail something is wrong
    00572 }