/* * error function */ void sq_errorfunc(HSQUIRRELVM v, const SQChar *s,...) { char str[4096]; va_list args; va_start(args, s); vsnprintf(str, 4096, s, args); // check if the error was called from emo::onError // because error inside onError causes infinite loop. bool fromOnError = false; SQInteger level = 1; SQStackInfos si; while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) { const SQChar *fn = _SC("unknown"); if (si.funcname) fn = si.funcname; level++; if (strcmp(fn, "_onError") == 0 || strcmp(fn, "onError") == 0) { fromOnError = true; break; } } if (!fromOnError) { callSqFunction_Bool_String(v, EMO_NAMESPACE, EMO_FUNC_ONERROR, str, SQFalse); } LOGE(str); va_end(args); }
SQInteger SquirrelStd::require(HSQUIRRELVM vm) { SQInteger top = sq_gettop(vm); const SQChar *filename; SQChar *real_filename; sq_getstring(vm, 2, &filename); /* Get the script-name of the current file, so we can work relative from it */ SQStackInfos si; sq_stackinfos(vm, 1, &si); if (si.source == NULL) { DEBUG(misc, 0, "[squirrel] Couldn't detect the script-name of the 'require'-caller; this should never happen!"); return SQ_ERROR; } real_filename = scstrdup(si.source); /* Keep the dir, remove the rest */ SQChar *s = scstrrchr(real_filename, PATHSEPCHAR); if (s != NULL) { /* Keep the PATHSEPCHAR there, remove the rest */ s++; *s = '\0'; } /* And now we concat, so we are relative from the current script * First, we have to make sure we have enough space for the full path */ real_filename = ReallocT(real_filename, scstrlen(real_filename) + scstrlen(filename) + 1); scstrcat(real_filename, filename); /* Tars dislike opening files with '/' on Windows.. so convert it to '\\' ;) */ char *filen = strdup(SQ2OTTD(real_filename)); #if (PATHSEPCHAR != '/') for (char *n = filen; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR; #endif bool ret = Squirrel::LoadScript(vm, filen); /* Reset the top, so the stack stays correct */ sq_settop(vm, top); free(real_filename); free(filen); return ret ? 0 : SQ_ERROR; }
static SQInteger base_getstackinfos(HSQUIRRELVM v) { SQInteger level; SQStackInfos si; SQInteger seq = 0; const SQChar *name = NULL; sq_getinteger(v, -1, &level); if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) { const SQChar *fn = _SC("unknown"); const SQChar *src = _SC("unknown"); if(si.funcname)fn = si.funcname; if(si.source)src = si.source; sq_newtable(v); sq_pushstring(v, _SC("func"), -1); sq_pushstring(v, fn, -1); sq_createslot(v, -3); sq_pushstring(v, _SC("src"), -1); sq_pushstring(v, src, -1); sq_createslot(v, -3); sq_pushstring(v, _SC("line"), -1); sq_pushinteger(v, si.line); sq_createslot(v, -3); sq_pushstring(v, _SC("locals"), -1); sq_newtable(v); seq=0; while ((name = sq_getlocal(v, level, seq))) { sq_pushstring(v, name, -1); sq_push(v, -2); sq_createslot(v, -4); sq_pop(v, 1); seq++; } sq_createslot(v, -3); return 1; } return 0; }
void PrintCallStack(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { SQStackInfos si; SQInteger i; SQFloat f; const SQChar *s; SQInteger level=1; //1 is to skip this function that is level 0 const SQChar *name=0; SQInteger seq=0; pf(v,_SC("\nCALLSTACK\n")); while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) { const SQChar *fn=_SC("unknown"); const SQChar *src=_SC("unknown"); if(si.funcname)fn=si.funcname; if(si.source)src=si.source; pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); level++; } return; level=0; pf(v,_SC("\nLOCALS\n")); for(level=0;level<10;level++){ seq=0; while((name = sq_getlocal(v,level,seq))) { seq++; switch(sq_gettype(v,-1)) { case OT_NULL: pf(v,_SC("[%s] NULL\n"),name); break; case OT_INTEGER: sq_getinteger(v,-1,&i); pf(v,_SC("[%s] %d\n"),name,i); break; case OT_FLOAT: sq_getfloat(v,-1,&f); pf(v,_SC("[%s] %.14g\n"),name,f); break; case OT_USERPOINTER: pf(v,_SC("[%s] USERPOINTER\n"),name); break; case OT_STRING: sq_getstring(v,-1,&s); pf(v,_SC("[%s] \"%s\"\n"),name,s); break; case OT_TABLE: pf(v,_SC("[%s] TABLE\n"),name); break; case OT_ARRAY: pf(v,_SC("[%s] ARRAY\n"),name); break; case OT_CLOSURE: pf(v,_SC("[%s] CLOSURE\n"),name); break; case OT_NATIVECLOSURE: pf(v,_SC("[%s] NATIVECLOSURE\n"),name); break; case OT_GENERATOR: pf(v,_SC("[%s] GENERATOR\n"),name); break; case OT_USERDATA: pf(v,_SC("[%s] USERDATA\n"),name); break; case OT_THREAD: pf(v,_SC("[%s] THREAD\n"),name); break; case OT_CLASS: pf(v,_SC("[%s] CLASS\n"),name); break; case OT_INSTANCE: pf(v,_SC("[%s] INSTANCE\n"),name); break; case OT_WEAKREF: pf(v,_SC("[%s] WEAKREF\n"),name); break; case OT_BOOL:{ sq_getinteger(v,-1,&i); pf(v,_SC("[%s] %s\n"),name,i?_SC("true"):_SC("false")); } break; default: ROCKETSQUIRREL_ASSERT(0); break; } sq_pop(v,1); } } } }
SQInteger CSquirrel::PrintErrorFunction(SQVM * pVM) { if(sq_gettop(pVM) >= 1) { const SQChar * szError = NULL; sq_getstring(pVM, 2, &szError); ErrorInfo info; info.strError = szError; SQStackInfos si; SQInteger level = 1; // 1 is to skip this function that is level 0 const SQChar *name = 0; SQInteger seq = 0; while(SQ_SUCCEEDED(sq_stackinfos(pVM, level, &si))) { const SQChar * fn = _SC("unknown"); const SQChar * src = _SC("unknown"); if(si.funcname) fn = si.funcname; if(si.source) src = si.source; info.callstack.push_back(ErrorCallstackPair(fn, ErrorSourcePair(src, si.line))); level++; } for(level = 0; level < 10; level++) { seq = 0; while((name = sq_getlocal(pVM, level, seq))) { seq++; CSquirrelArgument arg; arg.pushFromStack(pVM, -1); info.locals.push_back(ErrorLocalPair(name, arg)); sq_pop(pVM, 1); } } CSquirrel * pScript = CScriptingManager::GetInstance()->Get(pVM); if(pScript) { CSquirrelArguments arguments; CSquirrelArguments tempArray; CSquirrelArguments callstackTable; CSquirrelArguments localsTable; arguments.push(info.strError); for(ErrorCallstackList::iterator iter = info.callstack.begin(); iter != info.callstack.end(); iter++) { String strFunction = iter->first; String strSource = iter->second.first; int iLine = iter->second.second; callstackTable.push(strFunction); tempArray.reset(); tempArray.push(strSource); tempArray.push(iLine); callstackTable.push(tempArray, true); } arguments.push(callstackTable, false); for(ErrorLocalsList::iterator iter = info.locals.begin(); iter != info.locals.end(); iter++) { String strName = iter->first; CSquirrelArgument arg = iter->second; localsTable.push(strName); localsTable.push(arg); } arguments.push(localsTable, false); if(CEvents::GetInstance()->Call("scriptError", &arguments, pScript).GetInteger() == 1) { CLogFile::Printf("<Error (%s)>", info.strError.Get()); CLogFile::Printf("<Callstack>"); for(ErrorCallstackList::iterator iter = info.callstack.begin(); iter != info.callstack.end(); iter++) { String strFunction = iter->first; String strSource = iter->second.first; int iLine = iter->second.second; CLogFile::Printf("<%s (%s, %d)>", strFunction.Get(), strSource.Get(), iLine); } CLogFile::Printf("</Callstack>"); CLogFile::Printf("<Locals>"); for(ErrorLocalsList::iterator iter = info.locals.begin(); iter != info.locals.end(); iter++) { String strName = iter->first; CSquirrelArgument arg = iter->second; CLogFile::Printf("<%s (%s)>", strName.Get(), arg.GetTypeString().Get()); } CLogFile::Printf("</Locals>"); CLogFile::Printf("</Error>"); } } } return 0; }
static void RaiseScriptError(HSQUIRRELVM v, int errorCode) { SQStackInfos si = {0}; SQInteger level = 1; //1 is to skip this function that is level 0 const SQChar* name = 0; char funcName[15*4] = {0}; char funcName2[15*4] = {0}; char* srcA = (char*)malloc(1001); SQInteger line = 0; SQInteger line2 = 0; char* recordParams = (char*)malloc(1001); memset(recordParams, 0, 1001); memset(srcA, 0, 1001); const SQChar* sErr = 0; if(!SQ_SUCCEEDED(sq_getstring(v, 2, &sErr))) sErr = 0; WTF::String sErrA(sErr); SQRESULT result = sq_stackinfos(v, level, &si); if (SQ_SUCCEEDED(result) || -2 == result) { const SQChar* fn = _SC("unknown"); const SQChar* src = _SC("unknown"); if(si.funcname) fn = si.funcname; if(si.source) src = si.source; SQInteger nLen = (SQInteger)wcslen(fn); for (SQInteger i = 0; i < nLen && i < 15*4 - 1; ++i) funcName[i] = (char)fn[i]; nLen = (SQInteger)wcslen(src); for (SQInteger i = 0; i < nLen && i < 1000; ++i) srcA[i] = (char)src[i]; const int nSaveLen = 20; if (nLen > nSaveLen) { memmove(srcA, srcA + nLen - nSaveLen, nSaveLen); srcA[nSaveLen] = 0; } line = si.line; if (SQ_SUCCEEDED(sq_stackinfos(v, 2, &si))) { fn = _SC("unknown"); src = _SC("unknown"); if(si.funcname) fn = si.funcname; nLen = (SQInteger)wcslen(fn); for (SQInteger i = 0; i < nLen && i < 15*4 - 1; ++i) funcName2[i] = (char)fn[i]; line2 = si.line; } sprintf_s(recordParams, 1000, "%d %s %d %s %d %s %s", errorCode, funcName, line, funcName2, line2, srcA, sErrA.utf8().data()); } OutputDebugStringA(recordParams); ReportError(L"script", recordParams, 0); //MyRaiseException(recordParams, KKdGetCallStackInfo(v)); //::RaiseException(EXCEPTION_BREAKPOINT, EXCEPTION_NONCONTINUABLE, 15, (ULONG_PTR *)recordParams); free(recordParams); free(srcA); if (IsDebuggerPresent()) DebugBreak(); }
static SQInteger squirrel_errorhandler(HSQUIRRELVM v) { /* Detect whether this error is just propagating through, or it originated * here. To do so, we store the last error we saw in the registry and compare * it to the current error. If they are the equal, we've already handled this * error. * * Without this check, we'll call the error handler for every transition * between the Squirrel and Wasm VMs. */ /* TODO(binji): It seems like there should be a better way to handle this */ SQInteger top = sq_gettop(v); WasmBool is_new_error = WASM_TRUE; /* get the last error we saw */ sq_pushregistrytable(v); sq_pushstring(v, WASM_LAST_ERROR_NAME, -1); if (SQ_SUCCEEDED(sq_get(v, -2))) { /* STACK: error registry_table last_error */ sq_push(v, -3); HSQOBJECT last_error; HSQOBJECT error; if (SQ_SUCCEEDED(sq_getstackobj(v, -1, &error)) && SQ_SUCCEEDED(sq_getstackobj(v, -2, &last_error))) { /* we don't want to use cmp here, because it will throw if the values are * unordered, and because it performs value equality */ if (squirrel_objects_are_equal_raw(last_error, error)) is_new_error = WASM_FALSE; } /* set the last_error in the registry to the new error. */ /* STACK: registry_table last_error error */ sq_remove(v, -2); sq_pushstring(v, WASM_LAST_ERROR_NAME, -1); sq_push(v, -2); /* STACK: registry_table error WASM_LAST_ERROR_NAME error */ SQRESULT r = sq_set(v, -4); WASM_USE(r); assert(r == SQ_OK); } sq_settop(v, top); if (is_new_error) { const char* error_msg = "unknown"; if (sq_gettop(v) >= 1) sq_getstring(v, -1, &error_msg); squirrel_error(v, "error: %s\n", error_msg); squirrel_error(v, "callstack:\n"); SQStackInfos stack_info; SQInteger depth; for (depth = 1; SQ_SUCCEEDED(sq_stackinfos(v, depth, &stack_info)); depth++) { const char* source = stack_info.source ? stack_info.source : "unknown"; const char* funcname = stack_info.funcname ? stack_info.funcname : "unknown"; SQInteger line = stack_info.line; squirrel_error(v, " #"_PRINT_INT_FMT ": %s:"_PRINT_INT_FMT ": %s()\n", depth, source, line, funcname); } } return 0; }
void sqstd_printcallstack(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_getprintfunc(v); if(pf) { SQStackInfos si; SQInteger i; SQBool b; SQFloat f; const SQChar *s; SQInteger level=1; //1 is to skip this function that is level 0 const SQChar *name=0; SQInteger seq=0; pf(v,_SC("\nCALLSTACK\n")); while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) { const SQChar *fn=_SC("unknown"); const SQChar *src=_SC("unknown"); if(si.funcname)fn=si.funcname; if(si.source) { /* We don't want to bother users with absolute paths to all AI files. * Since the path only reaches NoAI code in a formatted string we have * to strip it here. Let's hope nobody installs openttd in a subdirectory * of a directory named /ai/. */ src = scstrstr(si.source, _SC("\\ai\\")); if (!src) src = scstrstr(si.source, _SC("/ai/")); if (src) { src += 4; } else { src = si.source; } } pf(v,_SC("*FUNCTION [%s()] %s line [%d]\n"),fn,src,si.line); level++; } level=0; pf(v,_SC("\nLOCALS\n")); for(level=0;level<10;level++){ seq=0; while((name = sq_getlocal(v,level,seq))) { seq++; switch(sq_gettype(v,-1)) { case OT_NULL: pf(v,_SC("[%s] NULL\n"),name); break; case OT_INTEGER: sq_getinteger(v,-1,&i); pf(v,_SC("[%s] %d\n"),name,i); break; case OT_FLOAT: sq_getfloat(v,-1,&f); pf(v,_SC("[%s] %.14g\n"),name,f); break; case OT_USERPOINTER: pf(v,_SC("[%s] USERPOINTER\n"),name); break; case OT_STRING: sq_getstring(v,-1,&s); pf(v,_SC("[%s] \"%s\"\n"),name,s); break; case OT_TABLE: pf(v,_SC("[%s] TABLE\n"),name); break; case OT_ARRAY: pf(v,_SC("[%s] ARRAY\n"),name); break; case OT_CLOSURE: pf(v,_SC("[%s] CLOSURE\n"),name); break; case OT_NATIVECLOSURE: pf(v,_SC("[%s] NATIVECLOSURE\n"),name); break; case OT_GENERATOR: pf(v,_SC("[%s] GENERATOR\n"),name); break; case OT_USERDATA: pf(v,_SC("[%s] USERDATA\n"),name); break; case OT_THREAD: pf(v,_SC("[%s] THREAD\n"),name); break; case OT_CLASS: pf(v,_SC("[%s] CLASS\n"),name); break; case OT_INSTANCE: pf(v,_SC("[%s] INSTANCE\n"),name); break; case OT_WEAKREF: pf(v,_SC("[%s] WEAKREF\n"),name); break; case OT_BOOL:{ sq_getbool(v,-1,&b); pf(v,_SC("[%s] %s\n"),name,b?_SC("true"):_SC("false")); } break; default: assert(0); break; } sq_pop(v,1); } } } }