/* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqopcodes.h" #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "squserdata.h" #include "sqclass.h" //SQObjectPtr _null_; //SQObjectPtr _true_(true); //SQObjectPtr _false_(false); //SQObjectPtr _one_((SQInteger)1); //SQObjectPtr _minusone_((SQInteger)-1); SQSharedState::SQSharedState() { _compilererrorhandler = NULL; _printfunc = NULL; _errorfunc = NULL; _debuginfo = false; _notifyallexceptions = false; _foreignptr = NULL; _releasehook = NULL; } #define newsysstring(s) { \ _systemstrings->push_back(SQString::Create(this,s)); \ } #define newmetamethod(s) { \ _metamethods->push_back(SQString::Create(this,s)); \ _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ } bool CompileTypemask(SQIntVec &res,const SQChar *typemask) { SQInteger i = 0; SQInteger mask = 0; while(typemask[i] != 0) { switch(typemask[i]){ case 'o': mask |= _RT_NULL; break; case 'i': mask |= _RT_INTEGER; break; case 'f': mask |= _RT_FLOAT; break; case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; case 's': mask |= _RT_STRING; break; case 't': mask |= _RT_TABLE; break; case 'a': mask |= _RT_ARRAY; break; case 'u': mask |= _RT_USERDATA; break; case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; case 'b': mask |= _RT_BOOL; break; case 'g': mask |= _RT_GENERATOR; break; case 'p': mask |= _RT_USERPOINTER; break; case 'v': mask |= _RT_THREAD; break; case 'x': mask |= _RT_INSTANCE; break; case 'y': mask |= _RT_CLASS; break; case 'r': mask |= _RT_WEAKREF; break; case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; case ' ': i++; continue; //ignores spaces default: return false; } i++; if(typemask[i] == '|') { i++; if(typemask[i] == 0) return false; continue; } res.push_back(mask); mask = 0; } return true; } SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz) { SQInteger i=0; SQTable *t=SQTable::Create(ss,0); while(funcz[i].name!=0){ SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f,0); nc->_nparamscheck = funcz[i].nparamscheck; nc->_name = SQString::Create(ss,funcz[i].name); if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) return NULL; t->NewSlot(SQString::Create(ss,funcz[i].name),nc); i++; } return t; } void SQSharedState::Init() { _scratchpad=NULL; _scratchpadsize=0; #ifndef NO_GARBAGE_COLLECTOR _gc_chain=NULL; #endif _stringtable = (SQStringTable*)SQ_MALLOC(sizeof(SQStringTable)); new (_stringtable) SQStringTable(this); sq_new(_metamethods,SQObjectPtrVec); sq_new(_systemstrings,SQObjectPtrVec); sq_new(_types,SQObjectPtrVec); _metamethodsmap = SQTable::Create(this,MT_LAST-1); //adding type strings to avoid memory trashing //types names newsysstring(_SC("null")); newsysstring(_SC("table")); newsysstring(_SC("array")); newsysstring(_SC("closure")); newsysstring(_SC("string")); newsysstring(_SC("userdata")); newsysstring(_SC("integer")); newsysstring(_SC("float")); newsysstring(_SC("userpointer")); newsysstring(_SC("function")); newsysstring(_SC("generator")); newsysstring(_SC("thread")); newsysstring(_SC("class")); newsysstring(_SC("instance")); newsysstring(_SC("bool")); //meta methods newmetamethod(MM_ADD); newmetamethod(MM_SUB); newmetamethod(MM_MUL); newmetamethod(MM_DIV); newmetamethod(MM_UNM); newmetamethod(MM_MODULO); newmetamethod(MM_SET); newmetamethod(MM_GET); newmetamethod(MM_TYPEOF); newmetamethod(MM_NEXTI); newmetamethod(MM_CMP); newmetamethod(MM_CALL); newmetamethod(MM_CLONED); newmetamethod(MM_NEWSLOT); newmetamethod(MM_DELSLOT); newmetamethod(MM_TOSTRING); newmetamethod(MM_NEWMEMBER); newmetamethod(MM_INHERITED); _constructoridx = SQString::Create(this,_SC("constructor")); _registry = SQTable::Create(this,0); _consts = SQTable::Create(this,0); _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz); _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz); _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz); _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz); _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz); _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz); _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz); _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz); _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz); _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz); } SQSharedState::~SQSharedState() { if(_releasehook) { _releasehook(_foreignptr,0); _releasehook = NULL; } _constructoridx.Null(); _table(_registry)->Finalize(); _table(_consts)->Finalize(); _table(_metamethodsmap)->Finalize(); _registry.Null(); _consts.Null(); _metamethodsmap.Null(); while(!_systemstrings->empty()) { _systemstrings->back().Null(); _systemstrings->pop_back(); } _thread(_root_vm)->Finalize(); _root_vm.Null(); _table_default_delegate.Null(); _array_default_delegate.Null(); _string_default_delegate.Null(); _number_default_delegate.Null(); _closure_default_delegate.Null(); _generator_default_delegate.Null(); _thread_default_delegate.Null(); _class_default_delegate.Null(); _instance_default_delegate.Null(); _weakref_default_delegate.Null(); _refs_table.Finalize(); #ifndef NO_GARBAGE_COLLECTOR SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; } } assert(_gc_chain==NULL); //just to proove a theory while(_gc_chain){ _gc_chain->_uiRef++; _gc_chain->Release(); } #endif sq_delete(_types,SQObjectPtrVec); sq_delete(_systemstrings,SQObjectPtrVec); sq_delete(_metamethods,SQObjectPtrVec); sq_delete(_stringtable,SQStringTable); if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize); } SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) { if(type(name) != OT_STRING) return -1; SQObjectPtr ret; if(_table(_metamethodsmap)->Get(name,ret)) { return _integer(ret); } return -1; } #ifndef NO_GARBAGE_COLLECTOR void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) { switch(type(o)){ case OT_TABLE:_table(o)->Mark(chain);break; case OT_ARRAY:_array(o)->Mark(chain);break; case OT_USERDATA:_userdata(o)->Mark(chain);break; case OT_CLOSURE:_closure(o)->Mark(chain);break; case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; case OT_GENERATOR:_generator(o)->Mark(chain);break; case OT_THREAD:_thread(o)->Mark(chain);break; case OT_CLASS:_class(o)->Mark(chain);break; case OT_INSTANCE:_instance(o)->Mark(chain);break; case OT_OUTER:_outer(o)->Mark(chain);break; case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; default: break; //shutup compiler } } void SQSharedState::RunMark(SQVM *vm,SQCollectable **tchain) { SQVM *vms = _thread(_root_vm); vms->Mark(tchain); _refs_table.Mark(tchain); MarkObject(_registry,tchain); MarkObject(_consts,tchain); MarkObject(_metamethodsmap,tchain); MarkObject(_table_default_delegate,tchain); MarkObject(_array_default_delegate,tchain); MarkObject(_string_default_delegate,tchain); MarkObject(_number_default_delegate,tchain); MarkObject(_generator_default_delegate,tchain); MarkObject(_thread_default_delegate,tchain); MarkObject(_closure_default_delegate,tchain); MarkObject(_class_default_delegate,tchain); MarkObject(_instance_default_delegate,tchain); MarkObject(_weakref_default_delegate,tchain); } SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) { SQInteger n=0; SQCollectable *tchain=NULL; RunMark(vm,&tchain); SQCollectable *resurrected = _gc_chain; SQCollectable *t = resurrected; //SQCollectable *nx = NULL; _gc_chain = tchain; SQArray *ret = NULL; if(resurrected) { ret = SQArray::Create(this,0); SQCollectable *rlast = NULL; while(t) { rlast = t; SQObjectType type = t->GetType(); if(type != OT_FUNCPROTO && type != OT_OUTER) { SQObject sqo; sqo._type = type; sqo._unVal.pRefCounted = t; ret->Append(sqo); } t = t->_next; n++; } assert(rlast->_next == NULL); rlast->_next = _gc_chain; if(_gc_chain) { _gc_chain->_prev = rlast; } _gc_chain = resurrected; } t = _gc_chain; while(t) { t->UnMark(); t = t->_next; } if(ret) { SQObjectPtr temp = ret; vm->Push(temp); } else { vm->PushNull(); } return n; } SQInteger SQSharedState::CollectGarbage(SQVM *vm) { SQInteger n = 0; SQCollectable *tchain = NULL; RunMark(vm,&tchain); SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; n++; } } t = tchain; while(t) { t->UnMark(); t = t->_next; } _gc_chain = tchain; return n; } #endif #ifndef NO_GARBAGE_COLLECTOR void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) { c->_prev = NULL; c->_next = *chain; if(*chain) (*chain)->_prev = c; *chain = c; } void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) { if(c->_prev) c->_prev->_next = c->_next; else *chain = c->_next; if(c->_next) c->_next->_prev = c->_prev; c->_next = NULL; c->_prev = NULL; } #endif SQChar* SQSharedState::GetScratchPad(SQInteger size) { SQInteger newsize; if(size>0) { if(_scratchpadsize < size) { newsize = size + (size>>1); _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); _scratchpadsize = newsize; }else if(_scratchpadsize >= (size<<5)) { newsize = _scratchpadsize >> 1; _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); _scratchpadsize = newsize; } } return _scratchpad; } RefTable::RefTable() { AllocNodes(4); } void RefTable::Finalize() { RefNode *nodes = _nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { nodes->obj.Null(); nodes++; } } RefTable::~RefTable() { SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); } #ifndef NO_GARBAGE_COLLECTOR void RefTable::Mark(SQCollectable **chain) { RefNode *nodes = (RefNode *)_nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { if(type(nodes->obj) != OT_NULL) { SQSharedState::MarkObject(nodes->obj,chain); } nodes++; } } #endif void RefTable::AddRef(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,true); ref->refs++; } SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,true); return ref->refs; } SQBool RefTable::Release(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,false); if(ref) { if(--ref->refs == 0) { SQObjectPtr o = ref->obj; if(prev) { prev->next = ref->next; } else { _buckets[mainpos] = ref->next; } ref->next = _freelist; _freelist = ref; _slotused--; ref->obj.Null(); //<>test for shrink? return SQTrue; } } else { assert(0); } return SQFalse; } void RefTable::Resize(SQUnsignedInteger size) { RefNode **oldbucks = _buckets; RefNode *t = _nodes; SQUnsignedInteger oldnumofslots = _numofslots; AllocNodes(size); //rehash SQUnsignedInteger nfound = 0; for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { if(type(t->obj) != OT_NULL) { //add back; assert(t->refs != 0); RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); nn->refs = t->refs; t->obj.Null(); nfound++; } t++; } assert(nfound == oldnumofslots); SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); } RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) { RefNode *t = _buckets[mainpos]; RefNode *newnode = _freelist; newnode->obj = obj; _buckets[mainpos] = newnode; _freelist = _freelist->next; newnode->next = t; assert(newnode->refs == 0); _slotused++; return newnode; } RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) { RefNode *ref; mainpos = ::HashObj(obj)&(_numofslots-1); *prev = NULL; for (ref = _buckets[mainpos]; ref; ) { if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj)) break; *prev = ref; ref = ref->next; } if(ref == NULL && add) { if(_numofslots == _slotused) { assert(_freelist == 0); Resize(_numofslots*2); mainpos = ::HashObj(obj)&(_numofslots-1); } ref = Add(mainpos,obj); } return ref; } void RefTable::AllocNodes(SQUnsignedInteger size) { RefNode **bucks; RefNode *nodes; bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode))); nodes = (RefNode *)&bucks[size]; RefNode *temp = nodes; SQUnsignedInteger n; for(n = 0; n < size - 1; n++) { bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = temp+1; temp++; } bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = NULL; _freelist = nodes; _nodes = nodes; _buckets = bucks; _slotused = 0; _numofslots = size; } ////////////////////////////////////////////////////////////////////////// //SQStringTable /* * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) * http://www.lua.org/copyright.html#4 * http://www.lua.org/source/4.0.1/src_lstring.c.html */ SQStringTable::SQStringTable(SQSharedState *ss) { _sharedstate = ss; AllocNodes(4); _slotused = 0; } SQStringTable::~SQStringTable() { SQ_FREE(_strings,sizeof(SQString*)*_numofslots); _strings = NULL; } void SQStringTable::AllocNodes(SQInteger size) { _numofslots = size; _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); memset(_strings,0,sizeof(SQString*)*_numofslots); } SQString *SQStringTable::Add(const SQChar *news,SQInteger len) { if(len<0) len = (SQInteger)scstrlen(news); SQHash newhash = ::_hashstr(news,len); SQHash h = newhash&(_numofslots-1); SQString *s; for (s = _strings[h]; s; s = s->_next){ if(s->_len == len && (!memcmp(news,s->_val,rsl(len)))) return s; //found } SQString *t = (SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString)); new (t) SQString; t->_sharedstate = _sharedstate; memcpy(t->_val,news,rsl(len)); t->_val[len] = _SC('\0'); t->_len = len; t->_hash = newhash; t->_next = _strings[h]; _strings[h] = t; _slotused++; if (_slotused > _numofslots) /* too crowded? */ Resize(_numofslots*2); return t; } void SQStringTable::Resize(SQInteger size) { SQInteger oldsize=_numofslots; SQString **oldtable=_strings; AllocNodes(size); for (SQInteger i=0; i_next; SQHash h = p->_hash&(_numofslots-1); p->_next = _strings[h]; _strings[h] = p; p = next; } } SQ_FREE(oldtable,oldsize*sizeof(SQString*)); } void SQStringTable::Remove(SQString *bs) { SQString *s; SQString *prev=NULL; SQHash h = bs->_hash&(_numofslots - 1); for (s = _strings[h]; s; ){ if(s == bs){ if(prev) prev->_next = s->_next; else _strings[h] = s->_next; _slotused--; SQInteger slen = s->_len; s->~SQString(); SQ_FREE(s,sizeof(SQString) + rsl(slen)); return; } prev = s; s = s->_next; } assert(0);//if this fail something is wrong }