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