574 lines
14 KiB
C++
Executable File
574 lines
14 KiB
C++
Executable File
/*
|
|
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;
|
|
_debuginfo = false;
|
|
_notifyallexceptions = false;
|
|
}
|
|
|
|
#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);
|
|
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
|
|
sq_new(_stringtable,StringTable);
|
|
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);
|
|
_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()
|
|
{
|
|
_constructoridx = _null_;
|
|
_refs_table.Finalize();
|
|
_table(_registry)->Finalize();
|
|
_table(_metamethodsmap)->Finalize();
|
|
// _refs_table = _null_;
|
|
_registry = _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_;
|
|
|
|
#ifndef NO_GARBAGE_COLLECTOR
|
|
|
|
|
|
SQCollectable *t=_gc_chain;
|
|
SQCollectable *nx=NULL;
|
|
while(t){
|
|
t->_uiRef++;
|
|
t->Finalize();
|
|
nx=t->_next;
|
|
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,StringTable);
|
|
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;
|
|
default: break; //shutup compiler
|
|
}
|
|
}
|
|
|
|
|
|
SQInteger SQSharedState::CollectGarbage(SQVM *vm)
|
|
{
|
|
SQInteger n=0;
|
|
SQCollectable *tchain=NULL;
|
|
SQVM *vms=_thread(_root_vm);
|
|
|
|
vms->Mark(&tchain);
|
|
SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed();
|
|
_refs_table.Mark(&tchain);
|
|
MarkObject(_registry,&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);
|
|
|
|
SQCollectable *t=_gc_chain;
|
|
SQCollectable *nx=NULL;
|
|
while(t){
|
|
t->_uiRef++;
|
|
t->Finalize();
|
|
nx=t->_next;
|
|
if(--t->_uiRef==0)
|
|
t->Release();
|
|
t=nx;
|
|
n++;
|
|
}
|
|
|
|
t=tchain;
|
|
while(t){
|
|
t->UnMark();
|
|
t=t->_next;
|
|
}
|
|
_gc_chain=tchain;
|
|
SQInteger z = _table(_thread(_root_vm)->_roottable)->CountUsed();
|
|
assert(z == x);
|
|
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++;
|
|
}
|
|
|
|
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_;
|
|
//<<FIXME>>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;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//StringTable
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
StringTable::StringTable()
|
|
{
|
|
AllocNodes(4);
|
|
_slotused = 0;
|
|
}
|
|
|
|
StringTable::~StringTable()
|
|
{
|
|
SQ_FREE(_strings,sizeof(SQString*)*_numofslots);
|
|
_strings=NULL;
|
|
}
|
|
|
|
void StringTable::AllocNodes(SQInteger size)
|
|
{
|
|
_numofslots=size;
|
|
//_slotused=0;
|
|
_strings=(SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
|
|
memset(_strings,0,sizeof(SQString*)*_numofslots);
|
|
}
|
|
|
|
SQString *StringTable::Add(const SQChar *news,SQInteger len)
|
|
{
|
|
if(len<0)
|
|
len = (SQInteger)scstrlen(news);
|
|
SQHash h = ::_hashstr(news,len)&(_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;
|
|
memcpy(t->_val,news,rsl(len));
|
|
t->_val[len] = _SC('\0');
|
|
t->_len = len;
|
|
t->_hash = ::_hashstr(news,len);
|
|
t->_next = _strings[h];
|
|
_strings[h] = t;
|
|
_slotused++;
|
|
if (_slotused > _numofslots) /* too crowded? */
|
|
Resize(_numofslots*2);
|
|
return t;
|
|
}
|
|
|
|
void StringTable::Resize(SQInteger size)
|
|
{
|
|
SQInteger oldsize=_numofslots;
|
|
SQString **oldtable=_strings;
|
|
AllocNodes(size);
|
|
for (SQInteger i=0; i<oldsize; i++){
|
|
SQString *p = oldtable[i];
|
|
while(p){
|
|
SQString *next = p->_next;
|
|
SQHash h = p->_hash&(_numofslots-1);
|
|
p->_next = _strings[h];
|
|
_strings[h] = p;
|
|
p = next;
|
|
}
|
|
}
|
|
SQ_FREE(oldtable,oldsize*sizeof(SQString*));
|
|
}
|
|
|
|
void StringTable::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
|
|
}
|