/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include #include #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 &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 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(); } //------------------------------------------------------------------------------