523 lines
13 KiB
C++
523 lines
13 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <ctime>
|
|
#include "squirrel.h"
|
|
#include "sqstdio.h"
|
|
#include "sqstdmath.h"
|
|
#include "sqstdstring.h"
|
|
#include "sqstdaux.h"
|
|
#include "sqstdblob.h"
|
|
#include "sqstdsystem.h"
|
|
#include "script_squirrel/squirrel_vm.h"
|
|
#include "script_squirrel/cobject/cobject.h"
|
|
#include "script/script_variant.h"
|
|
#include "filesystem/io_handle.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "viewer_base/viewer_base.h"
|
|
#include "platform.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::Script;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void SquirrelVM::GetCallStack(AutoList <CallStackEntry *> &callstack)
|
|
{
|
|
callstack.Clear();
|
|
|
|
SQStackInfos si;
|
|
for (SQInteger level = 0; SQ_SUCCEEDED(sq_stackinfos(vm, level, &si)); ++level)
|
|
callstack.Add(new CallStackEntry(si.source, si.funcname, si.line));
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void SqPrintFunc(HSQUIRRELVM v, const SQChar *s, ...)
|
|
{
|
|
va_list arglist;
|
|
va_start(arglist, s);
|
|
char lcl[4096];
|
|
|
|
#ifdef __PLATFORM_WINDOWS__
|
|
vsprintf_s(lcl, 4095, s, arglist);
|
|
#else
|
|
vsprintf(lcl, s, arglist);
|
|
#endif
|
|
|
|
__LOG__ << "[S] " << lcl << "\n";
|
|
va_end(arglist);
|
|
}
|
|
static void SqDebugHook(HSQUIRRELVM v, SQInteger type, const SQChar *sourcename, SQInteger line, const SQChar *funcname)
|
|
{
|
|
if (SquirrelVM *vm = (SquirrelVM *)sq_getforeignptr(v))
|
|
if (vm->GetEventHandler())
|
|
vm->GetEventHandler()->OnStep((char)type, sourcename, line, funcname);
|
|
}
|
|
static void SqCompilerError(HSQUIRRELVM v, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
|
|
{
|
|
if (SquirrelVM *vm = (SquirrelVM *)sq_getforeignptr(v))
|
|
{
|
|
String msg("Script runtime Compilation exception:\n");
|
|
msg += String::Format(" - (line %d) in \"%s\"\n", line, source);
|
|
__LOG_E__ << "Squirrel Compiler Error: '" << msg << "'\n\n";
|
|
|
|
if (vm->GetEventHandler())
|
|
vm->GetEventHandler()->OnCompilerError(desc, source, line);
|
|
|
|
time_t now = time(NULL);
|
|
struct tm *timeinfo = localtime(&now);
|
|
char timestamp[64];
|
|
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d_%H-%M-%S", timeinfo);
|
|
|
|
String logFileName = String::Format("g_engine_log_%s.txt", timestamp);
|
|
|
|
// Check if g_engine_log.txt exists and copy it
|
|
if (Platform::Get().io->Exists("g_engine_log.txt"))
|
|
{
|
|
if (Platform::Get().io->FileCopy("g_engine_log.txt", logFileName.c_str()))
|
|
{
|
|
__LOG_E__ << "Engine log copied to: " << logFileName << "\n";
|
|
}
|
|
else
|
|
{
|
|
__LOG_E__ << "Failed to copy engine log to: " << logFileName << "\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
__LOG_E__ << "Engine log file 'g_engine_log.txt' not found for copying\n";
|
|
}
|
|
}
|
|
}
|
|
static SQInteger SqRuntimeError(HSQUIRRELVM v)
|
|
{
|
|
SquirrelVM *vm = (SquirrelVM *)sq_getforeignptr(v);
|
|
|
|
// Get error string.
|
|
const SQChar *error = NULL;
|
|
if (SQ_FAILED(sq_getstring(v, 2, &error)))
|
|
error = "Unspecified runtime error";
|
|
|
|
// Could be null if called from a coroutine.
|
|
if (vm && (vm->GetState() == IVM::StateOk))
|
|
{
|
|
vm->SetState(IVM::StateExceptionThrown);
|
|
|
|
// Redirect to the signal handler.
|
|
if (vm->GetEventHandler())
|
|
{
|
|
vm->GetEventHandler()->OnRuntimeException(error);
|
|
return sq_suspendvm(v); // All debugging hope is lost beyond this point as the Squirrel VM will unwind all exception stack frame.
|
|
}
|
|
else
|
|
{
|
|
String msg = String::Format("Script runtime exception:\n\n%s", error);
|
|
|
|
AutoList <IVM::CallStackEntry *> callstack;
|
|
vm->GetCallStack(callstack);
|
|
|
|
msg += "\n\nCallstack:\n\n";
|
|
ListForeachPtr(IVM::CallStackEntry *, cs, callstack)
|
|
msg += String::Format(" - %s() (line %d) in \"%s\"\n", cs->function.c_str(), cs->line, cs->source.c_str());
|
|
|
|
__LOG_E__ << "Squirrel Compiler Error: '" << msg << "'\n";
|
|
|
|
time_t now = time(NULL);
|
|
struct tm *timeinfo = localtime(&now);
|
|
char timestamp[64];
|
|
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d_%H-%M-%S", timeinfo);
|
|
|
|
String logFileName = String::Format("g_engine_log_%s.txt", timestamp);
|
|
|
|
// Check if g_engine_log.txt exists and copy it
|
|
if (Platform::Get().io->Exists("g_engine_log.txt"))
|
|
{
|
|
if (Platform::Get().io->FileCopy("g_engine_log.txt", logFileName.c_str()))
|
|
{
|
|
__LOG_E__ << "Engine log copied to: " << logFileName << "\n";
|
|
}
|
|
else
|
|
{
|
|
__LOG_E__ << "Failed to copy engine log to: " << logFileName << "\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
__LOG_E__ << "Engine log file 'g_engine_log.txt' not found for copying\n";
|
|
}
|
|
|
|
}
|
|
}
|
|
return sq_suspendvm(v);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void SquirrelVM::SetDebugInterface(IDebug *i, bool debug)
|
|
{
|
|
sq_enabledebuginfo(vm, debug);
|
|
/* if (vm)
|
|
{
|
|
sq_enabledebuginfo(vm, debug);
|
|
if (debug)
|
|
sq_setnativedebughook(vm, SqDebugHook);
|
|
}
|
|
IVM::SetDebugInterface(i, debug);*/
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void SquirrelVM::DumpCallStack(HSQUIRRELVM v, const char *desc, String *msg)
|
|
{
|
|
SQStackInfos si;
|
|
// SQInteger level = 1; // Level 0 is the native closure we are in.
|
|
|
|
__LOG_V__ << "\n";
|
|
String _msg(String::Format("Call stack (%s):\n", desc ? desc : "Requested"));
|
|
__LOG_V__ << _msg.c_str();
|
|
|
|
for (SQInteger level = 1; SQ_SUCCEEDED(sq_stackinfos(v, level, &si)); ++level)
|
|
{
|
|
String _lg;
|
|
|
|
if (si.funcname)
|
|
{
|
|
if (si.line != -1)
|
|
_lg = String::Format(" %d: %s() %s(%d)", level, si.funcname, si.source, si.line);
|
|
else _lg = String::Format(" %d: %s() C/C++", level, si.funcname);
|
|
}
|
|
else
|
|
_lg = String::Format(" %d: NOINFO", level);
|
|
|
|
__LOG_V__ << _lg << "\n";
|
|
_msg += _lg;
|
|
}
|
|
|
|
if (msg)
|
|
*msg = _msg;
|
|
__LOG_V__ << "\n";
|
|
}
|
|
bool SquirrelVM::Compile(const char *source, uint size, const Object *context, const char *sourcename)
|
|
{
|
|
if (!source)
|
|
return false;
|
|
|
|
if (SQ_SUCCEEDED(sq_compilebuffer(vm, source, size, sourcename, true)))
|
|
{
|
|
sq_pushroottable(vm);
|
|
sq_call(vm, 1, SQFalse, SQTrue);
|
|
sq_pop(vm, 1);
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Object *SquirrelVM::GetObjectFromStack(int idx)
|
|
{
|
|
HSQOBJECT o;
|
|
sq_getstackobj(vm, idx, &o);
|
|
return new SquirrelObject(*this, o);
|
|
}
|
|
Object *SquirrelVM::GetObjectFromName(const char *name, const Object *context)
|
|
{
|
|
if (context)
|
|
sq_pushobject(vm, ((const SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
|
|
sq_pushstring(vm, name, -1);
|
|
if (SQ_FAILED(sq_get(vm, -2)))
|
|
{
|
|
sq_pop(vm, 1);
|
|
return NULL;
|
|
}
|
|
|
|
Object *o = GetObjectFromStack(-1);
|
|
sq_pop(vm, 2);
|
|
return o;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool SquirrelVM::SetupFunctionCall(const char *func, const Object *cache, const Object *context)
|
|
{
|
|
if (!vm || (GetState() != StateOk))
|
|
return false;
|
|
|
|
if (cache)
|
|
sq_pushobject(vm, ((SquirrelObject *)cache)->object);
|
|
else
|
|
{
|
|
if (context)
|
|
sq_pushobject(vm, ((SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
|
|
sq_pushstring(vm, func, -1);
|
|
if (SQ_FAILED(sq_get(vm, -2)))
|
|
{
|
|
sq_pop(vm, 1); // Cleanup root table.
|
|
return false;
|
|
}
|
|
sq_remove(vm, -2); // Remove root table.
|
|
}
|
|
|
|
// Push function environment.
|
|
if (context)
|
|
sq_pushobject(vm, ((SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
|
|
call_arg_count = 1;
|
|
return true;
|
|
}
|
|
bool SquirrelVM::SetFunctionCallContext(const Script::Variant &v)
|
|
{
|
|
return PushArgument(v);
|
|
}
|
|
bool SquirrelVM::PushNullArgument()
|
|
{
|
|
call_arg_count++;
|
|
return PushNull();
|
|
}
|
|
bool SquirrelVM::PushArgument(const Script::Variant &v)
|
|
{
|
|
call_arg_count++;
|
|
return PushVariant(v);
|
|
}
|
|
bool SquirrelVM::DoFunctionCall(Script::Variant *v)
|
|
{
|
|
if (GetState() != StateOk)
|
|
return false;
|
|
if (SQ_FAILED(sq_call(vm, call_arg_count, SQTrue, SQTrue)))
|
|
return false;
|
|
|
|
if (v)
|
|
GetVariantFromStack(-1, *v);
|
|
sq_pop(vm, 2);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool SquirrelVM::GetVariantFromStack(int idx, Script::Variant &v)
|
|
{
|
|
switch (sq_gettype(vm, idx))
|
|
{
|
|
case OT_STRING:
|
|
{
|
|
const SQChar *s;
|
|
sq_getstring(vm, idx, &s);
|
|
v.Set((const char *)s);
|
|
}
|
|
break;
|
|
|
|
case OT_BOOL:
|
|
{
|
|
SQBool b;
|
|
sq_getbool(vm, idx, &b);
|
|
v.Set(asbool(b));
|
|
}
|
|
break;
|
|
|
|
case OT_INTEGER:
|
|
{
|
|
SQInteger i;
|
|
sq_getinteger(vm, idx, &i);
|
|
v.Set((int)i);
|
|
}
|
|
break;
|
|
|
|
case OT_FLOAT:
|
|
{
|
|
SQFloat f;
|
|
sq_getfloat(vm, idx, &f);
|
|
v.Set((float)f);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
HSQOBJECT o;
|
|
sq_getstackobj(vm, idx, &o);
|
|
v.Set(new SquirrelObject(*this, o), true);
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
bool SquirrelVM::Get(const char *name, Script::Variant &prop, const Object *context)
|
|
{
|
|
if (context)
|
|
sq_pushobject(vm, ((SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
sq_pushstring(vm, name, -1);
|
|
if (SQ_FAILED(sq_get(vm, -2)))
|
|
{
|
|
sq_pop(vm, 1);
|
|
return false;
|
|
}
|
|
bool r = GetVariantFromStack(-1, prop);
|
|
sq_pop(vm, 2);
|
|
return r;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool SquirrelVM::PushNull()
|
|
{
|
|
sq_pushnull(vm);
|
|
return true;
|
|
}
|
|
bool SquirrelVM::PushVariant(const Script::Variant &v)
|
|
{
|
|
switch (v.type)
|
|
{
|
|
case Variant::Type_Variant:
|
|
switch (v.variant.GetType())
|
|
{
|
|
case GS::Variant::VariantString:
|
|
sq_pushstring(vm, v.variant.s_value.c_str(), -1);
|
|
break;
|
|
case GS::Variant::VariantInteger:
|
|
sq_pushinteger(vm, v.variant.i_value);
|
|
break;
|
|
case GS::Variant::VariantBool:
|
|
sq_pushbool(vm, v.variant.b_value);
|
|
break;
|
|
case GS::Variant::VariantFloat:
|
|
sq_pushfloat(vm, v.variant.f_value);
|
|
break;
|
|
|
|
default:
|
|
__LOG_E__ << "Unsupported variant type: " << v.type << ".\n";
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case Variant::Type_ScriptObject:
|
|
sq_pushobject(VM(), ((SquirrelObject *)v.object)->object);
|
|
break;
|
|
|
|
case Variant::Type_UserObject:
|
|
return CObject::Push(VM(), v.ptr, CObjectType(v.typetag));
|
|
}
|
|
return true;
|
|
}
|
|
bool SquirrelVM::Set(const char *name, const Script::Variant &v, const Object *context)
|
|
{
|
|
if (context)
|
|
sq_pushobject(vm, ((SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
sq_pushstring(vm, name, -1);
|
|
if (!PushVariant(v))
|
|
{
|
|
sq_pop(vm, 2);
|
|
return false;
|
|
}
|
|
SQObjectType type = sq_gettype(vm, -3);
|
|
if ((type == OT_CLASS) || (type == OT_TABLE))
|
|
sq_newslot(vm, -3, false);
|
|
else sq_set(vm, -3);
|
|
sq_pop(vm, 1);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Object *SquirrelVM::CreateArray()
|
|
{
|
|
sq_newarray(vm, 0);
|
|
Object *o = GetObjectFromStack(-1);
|
|
sq_pop(vm, 1);
|
|
return o;
|
|
}
|
|
bool SquirrelVM::Append(const Script::Variant &prop, const Object *context)
|
|
{
|
|
if (context)
|
|
sq_pushobject(vm, ((SquirrelObject *)context)->object);
|
|
else sq_pushroottable(vm);
|
|
if (!PushVariant(prop))
|
|
{
|
|
sq_pop(vm, 1);
|
|
return false;
|
|
}
|
|
sq_arrayappend(vm, -2);
|
|
sq_pop(vm, 1);
|
|
return true;
|
|
}
|
|
Object *SquirrelVM::CreateTable()
|
|
{
|
|
sq_newtable(vm);
|
|
Object *o = GetObjectFromStack(-1);
|
|
sq_pop(vm, 1);
|
|
return o;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool SquirrelVM::Open()
|
|
{
|
|
if (vm)
|
|
return true;
|
|
|
|
vm = sq_open(1024);
|
|
if (!vm)
|
|
return false;
|
|
|
|
sq_setforeignptr(vm, (SQUserPointer)this);
|
|
|
|
sq_setcompilererrorhandler(vm, SqCompilerError);
|
|
sq_setprintfunc(vm, SqPrintFunc, SqPrintFunc);
|
|
sq_newclosure(vm, SqRuntimeError, 0);
|
|
sq_seterrorhandler(vm);
|
|
|
|
sq_pushroottable(vm);
|
|
sqstd_register_bloblib(vm);
|
|
sqstd_register_iolib(vm);
|
|
sqstd_register_systemlib(vm);
|
|
sqstd_register_mathlib(vm);
|
|
sqstd_register_stringlib(vm);
|
|
sq_pop(vm, 1);
|
|
|
|
return true;
|
|
}
|
|
void SquirrelVM::Close()
|
|
{
|
|
//if (vm)
|
|
// sq_close(vm);
|
|
|
|
vm = NULL;
|
|
state = StateOk;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
SquirrelObject::SquirrelObject(SquirrelVM &_vm, HSQOBJECT _object) : Object(_vm)
|
|
{
|
|
object = _object;
|
|
sq_addref(((SquirrelVM &)vm).VM(), &object);
|
|
}
|
|
SquirrelObject::~SquirrelObject()
|
|
{
|
|
sq_release(((SquirrelVM &)vm).VM(), &object);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
SquirrelVM::SquirrelVM()
|
|
{
|
|
vm = NULL;
|
|
call_arg_count = 0;
|
|
}
|
|
SquirrelVM::~SquirrelVM()
|
|
{
|
|
Close();
|
|
}
|
|
//------------------------------------------------------------------------------
|