1 /*
2  see copyright notice in squirrel.h
3 */
4 #include "sqpcheader.h"
5 #include "sqopcodes.h"
6 #include "sqvm.h"
7 #include "sqfuncproto.h"
8 #include "sqclosure.h"
9 #include "sqstring.h"
10 #include "sqtable.h"
11 #include "sqarray.h"
12 #include "squserdata.h"
13 #include "sqclass.h"
14 
15 SQObjectPtr _null_;
16 SQObjectPtr _true_( true );
17 SQObjectPtr _false_( false );
18 SQObjectPtr _one_( 1 );
19 SQObjectPtr _minusone_( -1 );
20 
SQSharedState()21 SQSharedState::SQSharedState() {
22 	_compilererrorhandler = NULL;
23 	_printfunc = NULL;
24 	_debuginfo = false;
25 }
26 
27 #define newsysstring(s) { \
28 		_systemstrings->push_back(SQString::Create(this,s)); \
29 	}
30 
31 #define newmetamethod(s) { \
32 		_metamethods->push_back(SQString::Create(this,s)); \
33 		_table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
34 	}
35 
CompileTypemask(SQIntVec & res,const SQChar * typemask)36 bool CompileTypemask( SQIntVec &res, const SQChar *typemask ) {
37 	SQInteger i = 0;
38 
39 	SQInteger mask = 0;
40 	while ( typemask[i] != 0 ) {
41 
42 		switch ( typemask[i] ) {
43 		case 'o': mask |= _RT_NULL; break;
44 		case 'i': mask |= _RT_INTEGER; break;
45 		case 'f': mask |= _RT_FLOAT; break;
46 		case 'n': mask |= ( _RT_FLOAT | _RT_INTEGER ); break;
47 		case 's': mask |= _RT_STRING; break;
48 		case 't': mask |= _RT_TABLE; break;
49 		case 'a': mask |= _RT_ARRAY; break;
50 		case 'u': mask |= _RT_USERDATA; break;
51 		case 'c': mask |= ( _RT_CLOSURE | _RT_NATIVECLOSURE ); break;
52 		case 'b': mask |= _RT_BOOL; break;
53 		case 'g': mask |= _RT_GENERATOR; break;
54 		case 'p': mask |= _RT_USERPOINTER; break;
55 		case 'v': mask |= _RT_THREAD; break;
56 		case 'x': mask |= _RT_INSTANCE; break;
57 		case 'y': mask |= _RT_CLASS; break;
58 		case 'r': mask |= _RT_WEAKREF; break;
59 		case '.': mask = -1; res.push_back( mask ); i++; mask = 0; continue;
60 		case ' ': i++; continue; //ignores spaces
61 		default:
62 			return false;
63 		}
64 		i++;
65 		if ( typemask[i] == '|' ) {
66 			i++;
67 			if ( typemask[i] == 0 )
68 				return false;
69 			continue;
70 		}
71 		res.push_back( mask );
72 		mask = 0;
73 
74 	}
75 	return true;
76 }
77 
CreateDefaultDelegate(SQSharedState * ss,SQRegFunction * funcz)78 SQTable *CreateDefaultDelegate( SQSharedState *ss, SQRegFunction *funcz ) {
79 	SQInteger i = 0;
80 	SQTable *t = SQTable::Create( ss, 0 );
81 	while ( funcz[i].name != 0 ) {
82 		SQNativeClosure *nc = SQNativeClosure::Create( ss, funcz[i].f );
83 		nc->_nparamscheck = funcz[i].nparamscheck;
84 		nc->_name = SQString::Create( ss, funcz[i].name );
85 		if ( funcz[i].typemask && !CompileTypemask( nc->_typecheck, funcz[i].typemask ) )
86 			return NULL;
87 		t->NewSlot( SQString::Create( ss, funcz[i].name ), nc );
88 		i++;
89 	}
90 	return t;
91 }
92 
Init()93 void SQSharedState::Init() {
94 	_scratchpad = NULL;
95 	_scratchpadsize = 0;
96 #ifndef NO_GARBAGE_COLLECTOR
97 	_gc_chain = NULL;
98 #endif
99 	sq_new( _stringtable, StringTable );
100 	sq_new( _metamethods, SQObjectPtrVec );
101 	sq_new( _systemstrings, SQObjectPtrVec );
102 	sq_new( _types, SQObjectPtrVec );
103 	_metamethodsmap = SQTable::Create( this, MT_LAST - 1 );
104 	//adding type strings to avoid memory trashing
105 	//types names
106 	newsysstring( _SC( "null" ) );
107 	newsysstring( _SC( "table" ) );
108 	newsysstring( _SC( "array" ) );
109 	newsysstring( _SC( "closure" ) );
110 	newsysstring( _SC( "string" ) );
111 	newsysstring( _SC( "userdata" ) );
112 	newsysstring( _SC( "integer" ) );
113 	newsysstring( _SC( "float" ) );
114 	newsysstring( _SC( "userpointer" ) );
115 	newsysstring( _SC( "function" ) );
116 	newsysstring( _SC( "generator" ) );
117 	newsysstring( _SC( "thread" ) );
118 	newsysstring( _SC( "class" ) );
119 	newsysstring( _SC( "instance" ) );
120 	newsysstring( _SC( "bool" ) );
121 	//meta methods
122 	newmetamethod( MM_ADD );
123 	newmetamethod( MM_SUB );
124 	newmetamethod( MM_MUL );
125 	newmetamethod( MM_DIV );
126 	newmetamethod( MM_UNM );
127 	newmetamethod( MM_MODULO );
128 	newmetamethod( MM_SET );
129 	newmetamethod( MM_GET );
130 	newmetamethod( MM_TYPEOF );
131 	newmetamethod( MM_NEXTI );
132 	newmetamethod( MM_CMP );
133 	newmetamethod( MM_CALL );
134 	newmetamethod( MM_CLONED );
135 	newmetamethod( MM_NEWSLOT );
136 	newmetamethod( MM_DELSLOT );
137 	newmetamethod( MM_TOSTRING );
138 
139 	_constructoridx = SQString::Create( this, _SC( "constructor" ) );
140 	_refs_table = SQTable::Create( this, 0 );
141 	_registry = SQTable::Create( this, 0 );
142 	_table_default_delegate = CreateDefaultDelegate( this, _table_default_delegate_funcz );
143 	_array_default_delegate = CreateDefaultDelegate( this, _array_default_delegate_funcz );
144 	_string_default_delegate = CreateDefaultDelegate( this, _string_default_delegate_funcz );
145 	_number_default_delegate = CreateDefaultDelegate( this, _number_default_delegate_funcz );
146 	_closure_default_delegate = CreateDefaultDelegate( this, _closure_default_delegate_funcz );
147 	_generator_default_delegate = CreateDefaultDelegate( this, _generator_default_delegate_funcz );
148 	_thread_default_delegate = CreateDefaultDelegate( this, _thread_default_delegate_funcz );
149 	_class_default_delegate = CreateDefaultDelegate( this, _class_default_delegate_funcz );
150 	_instance_default_delegate = CreateDefaultDelegate( this, _instance_default_delegate_funcz );
151 	_weakref_default_delegate = CreateDefaultDelegate( this, _weakref_default_delegate_funcz );
152 
153 }
154 
~SQSharedState()155 SQSharedState::~SQSharedState() {
156 	_constructoridx = _null_;
157 	_table( _refs_table )->Finalize();
158 	_table( _registry )->Finalize();
159 	_table( _metamethodsmap )->Finalize();
160 	_refs_table = _null_;
161 	_registry = _null_;
162 	_metamethodsmap = _null_;
163 	while ( !_systemstrings->empty() ) {
164 		_systemstrings->back() = _null_;
165 		_systemstrings->pop_back();
166 	}
167 	_thread( _root_vm )->Finalize();
168 	_root_vm = _null_;
169 	_table_default_delegate = _null_;
170 	_array_default_delegate = _null_;
171 	_string_default_delegate = _null_;
172 	_number_default_delegate = _null_;
173 	_closure_default_delegate = _null_;
174 	_generator_default_delegate = _null_;
175 	_thread_default_delegate = _null_;
176 	_class_default_delegate = _null_;
177 	_instance_default_delegate = _null_;
178 	_weakref_default_delegate = _null_;
179 
180 #ifndef NO_GARBAGE_COLLECTOR
181 
182 
183 	SQCollectable *t = _gc_chain;
184 	SQCollectable *nx = NULL;
185 	while ( t ) {
186 		t->_uiRef++;
187 		t->Finalize();
188 		nx = t->_next;
189 		if ( --t->_uiRef == 0 )
190 			t->Release();
191 		t = nx;
192 	}
193 	assert( _gc_chain == NULL ); //just to proove a theory
194 	while ( _gc_chain ) {
195 		_gc_chain->_uiRef++;
196 		_gc_chain->Release();
197 	}
198 #endif
199 	sq_delete( _types, SQObjectPtrVec );
200 	sq_delete( _systemstrings, SQObjectPtrVec );
201 	sq_delete( _metamethods, SQObjectPtrVec );
202 	sq_delete( _stringtable, StringTable );
203 	if ( _scratchpad )SQ_FREE( _scratchpad, _scratchpadsize );
204 }
205 
206 
GetMetaMethodIdxByName(const SQObjectPtr & name)207 SQInteger SQSharedState::GetMetaMethodIdxByName( const SQObjectPtr &name ) {
208 	if ( squirrel_type( name ) != OT_STRING )
209 		return -1;
210 	SQObjectPtr ret;
211 	if ( _table( _metamethodsmap )->Get( name, ret ) ) {
212 		return _integer( ret );
213 	}
214 	return -1;
215 }
216 
217 #ifndef NO_GARBAGE_COLLECTOR
218 
MarkObject(SQObjectPtr & o,SQCollectable ** chain)219 void SQSharedState::MarkObject( SQObjectPtr &o, SQCollectable **chain ) {
220 	switch ( squirrel_type( o ) ) {
221 	case OT_TABLE: _table( o )->Mark( chain );break;
222 	case OT_ARRAY: _array( o )->Mark( chain );break;
223 	case OT_USERDATA: _userdata( o )->Mark( chain );break;
224 	case OT_CLOSURE: _closure( o )->Mark( chain );break;
225 	case OT_NATIVECLOSURE: _nativeclosure( o )->Mark( chain );break;
226 	case OT_GENERATOR: _generator( o )->Mark( chain );break;
227 	case OT_THREAD: _thread( o )->Mark( chain );break;
228 	case OT_CLASS: _class( o )->Mark( chain );break;
229 	case OT_INSTANCE: _instance( o )->Mark( chain );break;
230 	}
231 }
232 
233 
CollectGarbage(SQVM * vm)234 SQInteger SQSharedState::CollectGarbage( SQVM *vm ) {
235 	SQInteger n = 0;
236 	SQCollectable *tchain = NULL;
237 	SQVM *vms = _thread( _root_vm );
238 
239 	vms->Mark( &tchain );
240 	SQInteger x = _table( _thread( _root_vm )->_roottable )->CountUsed();
241 	MarkObject( _refs_table, &tchain );
242 	MarkObject( _registry, &tchain );
243 	MarkObject( _metamethodsmap, &tchain );
244 	MarkObject( _table_default_delegate, &tchain );
245 	MarkObject( _array_default_delegate, &tchain );
246 	MarkObject( _string_default_delegate, &tchain );
247 	MarkObject( _number_default_delegate, &tchain );
248 	MarkObject( _generator_default_delegate, &tchain );
249 	MarkObject( _thread_default_delegate, &tchain );
250 	MarkObject( _closure_default_delegate, &tchain );
251 	MarkObject( _class_default_delegate, &tchain );
252 	MarkObject( _instance_default_delegate, &tchain );
253 	MarkObject( _weakref_default_delegate, &tchain );
254 
255 	SQCollectable *t = _gc_chain;
256 	SQCollectable *nx = NULL;
257 	while ( t ) {
258 		t->_uiRef++;
259 		t->Finalize();
260 		nx = t->_next;
261 		if ( --t->_uiRef == 0 )
262 			t->Release();
263 		t = nx;
264 		n++;
265 	}
266 
267 	t = tchain;
268 	while ( t ) {
269 		t->UnMark();
270 		t = t->_next;
271 	}
272 	_gc_chain = tchain;
273 	SQInteger z = _table( _thread( _root_vm )->_roottable )->CountUsed();
274 	assert( z == x );
275 	return n;
276 }
277 #endif
278 
279 #ifndef NO_GARBAGE_COLLECTOR
AddToChain(SQCollectable ** chain,SQCollectable * c)280 void SQCollectable::AddToChain( SQCollectable **chain, SQCollectable *c ) {
281 	c->_prev = NULL;
282 	c->_next = *chain;
283 	if ( *chain ) ( *chain )->_prev = c;
284 	*chain = c;
285 }
286 
RemoveFromChain(SQCollectable ** chain,SQCollectable * c)287 void SQCollectable::RemoveFromChain( SQCollectable **chain, SQCollectable *c ) {
288 	if ( c->_prev ) c->_prev->_next = c->_next;
289 	else *chain = c->_next;
290 	if ( c->_next )
291 		c->_next->_prev = c->_prev;
292 	c->_next = NULL;
293 	c->_prev = NULL;
294 }
295 #endif
296 
GetScratchPad(SQInteger size)297 SQChar* SQSharedState::GetScratchPad( SQInteger size ) {
298 	SQInteger newsize;
299 	if ( size > 0 ) {
300 		if ( _scratchpadsize < size ) {
301 			newsize = size + ( size >> 1 );
302 			_scratchpad = ( SQChar * )SQ_REALLOC( _scratchpad, _scratchpadsize, newsize );
303 			_scratchpadsize = newsize;
304 
305 		} else if ( _scratchpadsize >= ( size << 5 ) ) {
306 			newsize = _scratchpadsize >> 1;
307 			_scratchpad = ( SQChar * )SQ_REALLOC( _scratchpad, _scratchpadsize, newsize );
308 			_scratchpadsize = newsize;
309 		}
310 	}
311 	return _scratchpad;
312 }
313 
314 //////////////////////////////////////////////////////////////////////////
315 //StringTable
316 /*
317 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.)
318 * http://www.lua.org/copyright.html#4
319 * http://www.lua.org/source/4.0.1/src_lstring.c.html
320 */
321 
Next(const SQObjectPtr & refpos,SQObjectPtr & outkey,SQObjectPtr & outval)322 SQInteger SQString::Next( const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval ) {
323 	SQInteger idx = ( SQInteger )TranslateIndex( refpos );
324 	while ( idx < _len ) {
325 		outkey = ( SQInteger )idx;
326 		outval = SQInteger( _val[idx] );
327 		//return idx for the next iteration
328 		return ++idx;
329 	}
330 	//nothing to iterate anymore
331 	return -1;
332 }
333 
StringTable()334 StringTable::StringTable() {
335 	AllocNodes( 4 );
336 	_slotused = 0;
337 }
338 
~StringTable()339 StringTable::~StringTable() {
340 	SQ_FREE( _strings, sizeof( SQString* )*_numofslots );
341 	_strings = NULL;
342 }
343 
AllocNodes(SQInteger size)344 void StringTable::AllocNodes( SQInteger size ) {
345 	_numofslots = size;
346 	//_slotused=0;
347 	_strings = ( SQString** )SQ_MALLOC( sizeof( SQString* ) * _numofslots );
348 	memset( _strings, 0, sizeof( SQString* )*_numofslots );
349 }
350 
Add(const SQChar * news,SQInteger len)351 SQString *StringTable::Add( const SQChar *news, SQInteger len ) {
352 	if ( len < 0 )
353 		len = scstrlen( news );
354 	SQHash h = ::_hashstr( news, len ) & ( _numofslots - 1 );
355 	SQString *s;
356 	for ( s = _strings[h]; s; s = s->_next ) {
357 		if ( s->_len == len && ( !memcmp( news, s->_val, rsl( len ) ) ) )
358 			return s; //found
359 	}
360 
361 	SQString *t = ( SQString * )SQ_MALLOC( rsl( len ) + sizeof( SQString ) );
362 	new ( t ) SQString;
363 	memcpy( t->_val, news, rsl( len ) );
364 	t->_val[len] = _SC( '\0' );
365 	t->_len = len;
366 	t->_hash = ::_hashstr( news, len );
367 	t->_next = _strings[h];
368 	_strings[h] = t;
369 	_slotused++;
370 	if ( _slotused > _numofslots )  /* too crowded? */
371 		Resize( _numofslots*2 );
372 	return t;
373 }
374 
Resize(SQInteger size)375 void StringTable::Resize( SQInteger size ) {
376 	SQInteger oldsize = _numofslots;
377 	SQString **oldtable = _strings;
378 	AllocNodes( size );
379 	for ( SQInteger i = 0; i < oldsize; i++ ) {
380 		SQString *p = oldtable[i];
381 		while ( p ) {
382 			SQString *next = p->_next;
383 			SQHash h = p->_hash & ( _numofslots - 1 );
384 			p->_next = _strings[h];
385 			_strings[h] = p;
386 			p = next;
387 		}
388 	}
389 	SQ_FREE( oldtable, oldsize*sizeof( SQString* ) );
390 }
391 
Remove(SQString * bs)392 void StringTable::Remove( SQString *bs ) {
393 	SQString *s;
394 	SQString *prev = NULL;
395 	SQHash h = bs->_hash & ( _numofslots - 1 );
396 
397 	for ( s = _strings[h]; s; ) {
398 		if ( s == bs ) {
399 			if ( prev )
400 				prev->_next = s->_next;
401 			else
402 				_strings[h] = s->_next;
403 			_slotused--;
404 			SQInteger slen = s->_len;
405 			s->~SQString();
406 			SQ_FREE( s, sizeof( SQString ) + rsl( slen ) );
407 			return;
408 		}
409 		prev = s;
410 		s = s->_next;
411 	}
412 	assert( 0 );//if this fail something is wrong
413 }
414