summaryrefslogtreecommitdiffstats
path: root/squirrel_3_0_1_stable/_OLD_sqplus/SquirrelVM.cpp
blob: 12b84dbe561c908192c1a4fa7e414f2e2e4b4c20 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>

#define _DEBUG_DUMP

#include "sqplus.h"

#include <sqstdio.h>
#include <sqstdmath.h>
#include <sqstdstring.h>
#include <sqstdaux.h>
#include <sqstdblob.h>
#include <sqstdsystem.h>


HSQUIRRELVM     SquirrelVM::_VM;
bool            SquirrelVM::_no_vm_ref;
int             SquirrelVM::_CallState = -1;
SquirrelObject* SquirrelVM::_root;
HSQUIRRELVM     SquirrelVM::_sandboxVM;
SquirrelObject  SquirrelVM::_vm;


// Helper struct to keep track of all SQSharedState:s created by SquirrelVM.
#include "../squirrel/sqpcheader.h"
#include "../squirrel/sqvm.h"
struct SQSharedStateNode { 
    SQSharedStateNode( SQSharedState* ps );
    ~SQSharedStateNode( );
    SQSharedState*     m_ps;
    SQSharedStateNode* m_nxt;
};

// Linked list of shared states
static SQSharedStateNode* g_sqss_fst;

SQSharedStateNode::SQSharedStateNode( SQSharedState* ps ) : m_ps(ps), m_nxt(g_sqss_fst) { 
    g_sqss_fst = this;
}

SQSharedStateNode::~SQSharedStateNode() {
    if(m_ps) sq_delete(m_ps,SQSharedState); 
    delete m_nxt;
}

static struct SquirrelVM_ModConstr { 
    ~SquirrelVM_ModConstr(){ 
        // Delete any shared states we created
        delete g_sqss_fst;
        g_sqss_fst = NULL;
    }
} g_squirrelvm_mod_constr;



SquirrelError::SquirrelError() 
{
    const SQChar *s;
    sq_getlasterror(SquirrelVM::_VM);
    sq_getstring(SquirrelVM::_VM,-1,&s);
    if(s) {
        desc = s;
    }
    else {
        desc = _SC("unknown error");
    }
}


SquirrelVMSys::~SquirrelVMSys() {
    // Must take care to release object with the 'ref' VM
    PushRefVM( _vm.GetObjectHandle()._unVal.pThread );
    _vm.Reset();
    PopRefVM();
}

void SquirrelVMSys::Set( HSQUIRRELVM v ){
    // Must take care to release object with the 'ref' VM
    PushRefVM( v );
    _vm = v;
    PopRefVM( );
}

void SquirrelVMSys::Set( const SquirrelObject& ov ){
    assert( ov.GetObjectHandle()._type==OT_THREAD );
    // Must take care to release object with the 'ref' VM
    PushRefVM( ov.GetObjectHandle()._unVal.pThread );
    _vm = ov;
    PopRefVM( );
}

SquirrelVMSys::operator HSQUIRRELVM () const { 
    // Avoid const madness
    SquirrelObject *pvm = (SquirrelObject*)&_vm;
    assert( pvm->GetObjectHandle()._type==OT_THREAD );
    return pvm->GetObjectHandle()._unVal.pThread; 
}


// When doing a SquirrelObject assignment, a reference using the current
// VM is done. 
HSQUIRRELVM g_VM_pushed;
void SquirrelVMSys::PushRefVM( HSQUIRRELVM v ){
    assert( !g_VM_pushed );
    g_VM_pushed = SquirrelVM::_VM;
    SquirrelVM::_VM = v;
}

void SquirrelVMSys::PopRefVM( ){
    SquirrelVM::_VM = g_VM_pushed;
    g_VM_pushed = NULL;
}


bool SquirrelVM::Init( HSQUIRRELVM v ){
    if( v && v==_VM )
        return true; 
    
    // Do we have a previous state?
    Release( );
        
    bool created_new = false;
    if( !v ){
        // Create a new VM - a root VM with new SharedState.
        v = sq_open(1024);
        if( !v ) return false;
        // Store the associated shared state in a linked list. The state will only 
        // be destroyed at app shutdown. Often that is fine, but if using many 
        // VM:s briefly, this allocation is not optimal.
        new SQSharedStateNode( _ss(v) );
        created_new = true;
        sq_setprintfunc(v,SquirrelVM::PrintFunc, SquirrelVM::PrintFunc);
        sq_pushroottable(v);
        sqstd_register_iolib(v);
        sqstd_register_bloblib(v);
        sqstd_register_mathlib(v);
        sqstd_register_stringlib(v);
#ifdef SQPLUS_SQUIRRELVM_WITH_SYSTEMLIB        
        sqstd_register_systemlib(v);
#endif        
        sqstd_seterrorhandlers(v);
        //TODO error handler, compiler error handler
        sq_pop(v,1);
    }
    
    // After this we hold a ref
    _no_vm_ref = false;
    _VM = v;
    _vm = v;
   
    // In the case where Squirrel is ref counted we currently
    // hold two references to the VM (since it is created with 
    // a ref count of 1). In the GC case, it is outside of the 
    // chain of valid objects, so it is not referenced. Compensate 
    // in ref counted case.
    if( created_new )
        DropVMRefIfRefCounted( v );
    
    return true;
}

bool SquirrelVM::InitNoRef( HSQUIRRELVM v ){
    if( v && v==_VM )
        return true; 
    
    // Do we have a previous state?
    Release( );
        
    // Set pointer to this VM, without referencing it
    _no_vm_ref = true;
    _VM = v;
    
    return true;
}

/*
void SquirrelVM::Init( HSQUIRRELVM v )
{
    if( v && v==_VM ) {
      return;
    }
    
    // Do we have a previous state?
    Release();
    
    if( !v ){
        // Create a new VM and own it
        _VM = sq_open(1024);
        sq_setprintfunc(_VM,SquirrelVM::PrintFunc);
        sq_pushroottable(_VM);
        sqstd_register_iolib(_VM);
        sqstd_register_bloblib(_VM);
        sqstd_register_mathlib(_VM);
        sqstd_register_stringlib(_VM);
        sqstd_seterrorhandlers(_VM);
        //TODO error handler, compiler error handler
        sq_pop(_VM,1);
    }
    else {
        _VM = v;
    }
    // After this we hold a ref
    _vm = _VM;
}
*/

void SquirrelVM::Release() {
    // Release root table object if we have one
    if( _root ){ 
        delete _root;
        _root = NULL;
    }
    
    // Release our ref on VM - if we should
    if( !_no_vm_ref )
        _vm.Reset();
        
    _VM = NULL;
}

void SquirrelVM::DropVMRefIfRefCounted( HSQUIRRELVM v ){ 
#ifdef NO_GARBAGE_COLLECTOR
    if( v ){
        SQObject t;
        t._unVal.pThread = v;
        t._type = OT_THREAD;
        sq_release( v, &t );
    }
#endif     
}

BOOL SquirrelVM::Update()
{
    //update remote debugger
    return TRUE;
}

void SquirrelVM::PrintFunc(HSQUIRRELVM v,const SQChar* s,...)
{
    static SQChar temp[2048];
    va_list vl;
    va_start(vl, s);
    scvsprintf( temp,s, vl);
    SCPUTS(temp);
    va_end(vl);
}

SquirrelObject SquirrelVM::CompileScript(const SQChar *s)
{
    SquirrelObject ret;
    if(SQ_SUCCEEDED(sqstd_loadfile(_VM,s,1))) {
        ret.AttachToStackObject(-1);
        sq_pop(_VM,1);
        return ret;
    }
    throw SquirrelError();
}

SquirrelObject SquirrelVM::CompileBuffer(const SQChar *s,const SQChar * debugInfo)
{
    SquirrelObject ret;
    if(SQ_SUCCEEDED(sq_compilebuffer(_VM,s,(int)scstrlen(s)*sizeof(SQChar),debugInfo,1))) {
        ret.AttachToStackObject(-1);
        sq_pop(_VM,1);
        return ret;
    }
    throw SquirrelError();
}

SquirrelObject SquirrelVM::RunScript(const SquirrelObject &o,SquirrelObject *_this)
{
    SquirrelObject ret;
    sq_pushobject(_VM,o._o);
    if(_this) {
        sq_pushobject(_VM,_this->_o);
    }
    else {
        sq_pushroottable(_VM);
    }
    if(SQ_SUCCEEDED(sq_call(_VM,1,SQTrue,SQ_CALL_RAISE_ERROR))) {
        ret.AttachToStackObject(-1);
        sq_pop(_VM,2);
        return ret;
    }
    sq_pop(_VM,1);
    throw SquirrelError();
    
}


BOOL SquirrelVM::BeginCall(const SquirrelObject &func)
{
    if(_CallState != -1)
        return FALSE;
    _CallState = 1;
    sq_pushobject(_VM,func._o);
    sq_pushroottable(_VM);
    return TRUE;
}

BOOL SquirrelVM::BeginCall(const SquirrelObject &func,SquirrelObject &_this)
{
    if(_CallState != -1)
        throw SquirrelError(_SC("call already initialized"));
    _CallState = 1;
    sq_pushobject(_VM,func._o);
    sq_pushobject(_VM,_this._o);
    return TRUE;
}

#define _CHECK_CALL_STATE \
    if(_CallState == -1) \
        throw SquirrelError(_SC("call not initialized"));

void SquirrelVM::PushParam(const SquirrelObject &o)
{
    _CHECK_CALL_STATE
    sq_pushobject(_VM,o._o);
    _CallState++;
}

void SquirrelVM::PushParam(const SQChar *s)
{
    _CHECK_CALL_STATE
    sq_pushstring(_VM,s,-1);
    _CallState++;
}

void SquirrelVM::PushParam(SQInteger n)
{
    _CHECK_CALL_STATE
    sq_pushinteger(_VM,n);
    _CallState++;
}

void SquirrelVM::PushParam(SQFloat f)
{
    _CHECK_CALL_STATE
    sq_pushfloat(_VM,f);
    _CallState++;
}

void SquirrelVM::PushParamNull()
{
    _CHECK_CALL_STATE
    sq_pushnull(_VM);
    _CallState++;
}

void SquirrelVM::PushParam(SQUserPointer up)
{
    _CHECK_CALL_STATE
    sq_pushuserpointer(_VM,up);
    _CallState++;
}

SquirrelObject SquirrelVM::EndCall()
{
    SquirrelObject ret;
    if(_CallState >= 0) { 
        int oldtop = sq_gettop(_VM);
        int nparams = _CallState;
        _CallState = -1;
        if(SQ_SUCCEEDED(sq_call(_VM,nparams,SQTrue,SQ_CALL_RAISE_ERROR))) {
            ret.AttachToStackObject(-1);
            sq_pop(_VM,2);
        }else {
            sq_settop(_VM,oldtop-(nparams+1));
            throw SquirrelError();
        }
        
    }
    return ret;
}

SquirrelObject SquirrelVM::CreateInstance(SquirrelObject &oclass)
{
    SquirrelObject ret;
    int oldtop = sq_gettop(_VM);
    sq_pushobject(_VM,oclass._o);
    if(SQ_FAILED(sq_createinstance(_VM,-1))) {
        sq_settop(_VM,oldtop);
        throw SquirrelError();
    }
    ret.AttachToStackObject(-1);
    sq_pop(_VM,2);
    return ret;
}

SquirrelObject SquirrelVM::CreateTable()
{
    SquirrelObject ret;
    sq_newtable(_VM);
    ret.AttachToStackObject(-1);
    sq_pop(_VM,1);
    return ret;
}

SquirrelObject SquirrelVM::CreateString(const SQChar *s)
{
    SquirrelObject ret;
    sq_pushstring(_VM,s,-1);
    ret.AttachToStackObject(-1);
    sq_pop(_VM,1);
    return ret;
}


SquirrelObject SquirrelVM::CreateArray(int size)
{
    SquirrelObject ret;
    sq_newarray(_VM,size);
    ret.AttachToStackObject(-1);
    sq_pop(_VM,1);
    return ret;
}

SquirrelObject SquirrelVM::CreateFunction(SQFUNCTION func)
{
    SquirrelObject ret;
    sq_newclosure(_VM,func,0);
    ret.AttachToStackObject(-1);
    sq_pop(_VM,1);
    return ret;
}

SquirrelObject SquirrelVM::CreateUserData(int size) {
  SquirrelObject ret;
  sq_newuserdata(_VM,size);
  ret.AttachToStackObject(-1);
  sq_pop(_VM,1);
  return ret;
}

const SquirrelObject &SquirrelVM::GetRootTable()
{
    if( !_root ){
        sq_pushroottable(_VM);
        _root = new SquirrelObject();
        _root->AttachToStackObject(-1);
        sq_pop(_VM,1);
    }
    return *_root;
}

void SquirrelVM::PushRootTable(void) {
  sq_pushroottable(_VM);
} // SquirrelVM::PushRootTable

// Creates a function in the table or class currently on the stack.
//void CreateFunction(HSQUIRRELVM v,const SQChar * scriptFuncName,SQFUNCTION func,int numParams=0,const SQChar * typeMask=0) {
SquirrelObject SquirrelVM::CreateFunction(SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) {
  sq_pushstring(_VM,scriptFuncName,-1);
  sq_newclosure(_VM,func,0);
  SquirrelObject ret;
  ret.AttachToStackObject(-1);
  SQChar tm[64];
  SQChar * ptm = tm;
  int numParams = SQ_MATCHTYPEMASKSTRING;
  if (typeMask) {
    if (typeMask[0] == '*') {
      ptm       = 0; // Variable args: don't check parameters.
      numParams = 0; // Clear SQ_MATCHTYPEMASKSTRING (does not mean match 0 params. See sq_setparamscheck()).
    } else {
      if (SCSNPRINTF(tm,sizeof(tm),_SC("t|y|x%s"),typeMask) < 0) {
//        sq_throwerror(_VM,_SC("CreateFunction: typeMask string too long."));
        throw SquirrelError(_SC("CreateFunction: typeMask string too long."));
      } // if
    } // if
  } else { // <TODO> Need to check object type on stack: table, class, instance, etc.
    SCSNPRINTF(tm,sizeof(tm),_SC("%s"),_SC("t|y|x")); // table, class, instance.
//    tm[0] = 't';
//    tm[1] = 0;
  } // if
#if 0
  sq_setparamscheck(_VM,numParams+1,ptm); // Parameters are table+args (thus, the +1).
#else
  if (ptm) {
    sq_setparamscheck(_VM,numParams,ptm); // Determine arg count from type string.
  } // if
#endif
#ifdef _DEBUG
  sq_setnativeclosurename(_VM,-1,scriptFuncName); // For debugging only.
#endif
  sq_createslot(_VM,-3); // Create slot in table or class (assigning function to slot at scriptNameFunc).
  return ret;
} // SquirrelVM::CreateFunction

SquirrelObject SquirrelVM::CreateFunction(SquirrelObject & so,SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) {
  PushObject(so);
  SquirrelObject ret = CreateFunction(func,scriptFuncName,typeMask);
  Pop(1);
  return ret;
} // SquirrelVM::CreateFunction

// Create a Global function on the root table.
//void CreateFunctionGlobal(HSQUIRRELVM v,const SQChar * scriptFuncName,SQFUNCTION func,int numParams=0,const SQChar * typeMask=0) {
SquirrelObject SquirrelVM::CreateFunctionGlobal(SQFUNCTION func,const SQChar * scriptFuncName,const SQChar * typeMask) {
  PushRootTable(); // Push root table.
  //  CreateFunction(scriptFuncName,func,numParams,typeMask);
  SquirrelObject ret = CreateFunction(func,scriptFuncName,typeMask);
  Pop(1);         // Pop root table.
  return ret;
} // SquirrelVM::CreateFunctionGlobal