static void _getCurrentStack(lua_State *L, utStack<stackinfo>& stack) { int top = lua_gettop(L); lua_Debug lstack; int stackFrame = 0; MethodBase *lastMethod = NULL; while (true) { // if we get a null result here, we are out of stack if (!lua_getstack(L, stackFrame++, &lstack)) { lua_settop(L, top); return; } // something bad in denmark if (!lua_getinfo(L, "fSl", &lstack)) { lua_settop(L, top); return; } bool cfunc = false; if (lua_iscfunction(L, -1)) { cfunc = true; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_settop(L, top); continue; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_settop(L, top); // we only want the root call, not the pcall wrapper if (cfunc && (lastMethod == methodBase)) { continue; } lastMethod = methodBase; stackinfo si; si.methodBase = methodBase; si.source = methodBase->isNative() ? "[NATIVE]" : lstack.source; si.linenumber = lstack.currentline == -1 ? 0 : lstack.currentline; stack.push(si); } }
void LSLuaState::triggerRuntimeError(const char *format, ...) { LSLog(LSLogError, "====================="); LSLog(LSLogError, "= RUNTIME ERROR ="); LSLog(LSLogError, "=====================\n"); char buff[2048]; va_list args; va_start(args, format); #ifdef _MSC_VER vsprintf_s(buff, 2046, format, args); #else vsnprintf(buff, 2046, format, args); #endif va_end(args); if (buff) { LSLog(LSLogError, "%s", buff); } if (_tracemessage[0]) { LSLog(LSLogError, "%s\n", _tracemessage); } _tracemessage[0] = 0; // coming from a native assert? if (!_tracestack.size()) { _getCurrentStack(L, _tracestack); } LSLog(LSLogError, "Stacktrace:", buff); for (UTsize i = 0; i < _tracestack.size(); i++) { const stackinfo& s = _tracestack.peek(_tracestack.size() - i - 1); LSLog(LSLogError, "%s : %s : %i", s.methodBase->getFullMemberName(), s.source ? s.source : NULL, s.linenumber); } LSError("\nFatal Runtime Error\n\n"); }
int LSLuaState::traceback(lua_State *L) { _tracestack.clear(); if (lua_isstring(L, 1)) { snprintf(_tracemessage, 2040, "%s", lua_tostring(L, 1)); } else { _tracemessage[0] = 0; } _getCurrentStack(L, _tracestack); return 0; }
namespace LS { void lsr_classinitializestatic(lua_State *L, Type *type); utHashTable<utPointerHashKey, LSLuaState *> LSLuaState::toLuaState; utArray<utString> LSLuaState::commandLine; double LSLuaState::uniqueKey = 1; lua_State *LSLuaState::lastState = NULL; LSLuaState *LSLuaState::lastLSState = NULL; double LSLuaState::constructorKey = 0; utArray<utString> LSLuaState::buildCache; // traceback stack queries struct stackinfo { const char *source; int linenumber; MethodBase *methodBase; }; static utStack<stackinfo> _tracestack; static char _tracemessage[2048]; #include "jemalloc/jemalloc.h" static void *lsLuaAlloc(void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; /* not used */ if (nsize == 0 && ptr) { lmFree(NULL, ptr); return NULL; } else { if(osize == 0) return lmAlloc(NULL, nsize); else return lmRealloc(NULL, ptr, nsize); } } void LSLuaState::open() { assert(!L); L = lua_newstate(lsLuaAlloc, NULL); //L = lua_open(); toLuaState.insert(L, this); luaopen_base(L); luaopen_table(L); luaopen_string(L); luaopen_math(L); luaL_openlibs(L); // open the lua debug library luaopen_debug(L); // open socket library luaopen_socket_core(L); lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXCLASSES); lua_newtable(L); lua_setglobal(L, "__ls_nativeclasses"); lua_pushcfunction(L, traceback); lua_setglobal(L, "__ls_traceback"); _tracemessage[0] = 0; // entry -> version lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXMANAGEDVERSION); // entry -> native user data lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXMANAGEDUSERDATA); // native user data -> script instance lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXMANAGEDNATIVESCRIPT); // native delegate table lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXNATIVEDELEGATES); // interned field name lookup lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXMEMBERINFONAME); // typeid -> type* lua_newtable(L); lua_rawseti(L, LUA_GLOBALSINDEX, LSASSEMBLYLOOKUP); // lua/luacfunction -> MethodBase* lookups lua_newtable(L); // weak key metatable lua_newtable(L); lua_pushstring(L, "k"); lua_setfield(L, -2, "__mode"); lua_setmetatable(L, -2); lua_rawseti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lsr_instanceregister(L); NativeInterface::registerNativeTypes(L); } void LSLuaState::close() { assert(L); if (lastState == L) { lastState = NULL; lastLSState = NULL; } // ensure profiler is down LSProfiler::enable(false, L); for (UTsize i = 0; i < assemblies.size(); i++) { assemblies.at(i)->close(); delete assemblies.at(i); } NativeInterface::shutdownLuaState(L); lua_close(L); toLuaState.remove(L); L = NULL; } Assembly *LSLuaState::loadTypeAssembly(const utString& assemblyString) { beginAssemblyLoad(); Assembly *assembly = Assembly::loadFromString(this, assemblyString); utArray<Type *> types; assembly->getTypes(types); cacheAssemblyTypes(assembly, types); endAssemblyLoad(); return assembly; } void LSLuaState::declareLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { declareClass(types[i]); } // validate/initialize native types for (UTsize i = 0; i < types.size(); i++) { Type *type = types.at(i); if (type->isNative() || type->hasStaticNativeMember()) { NativeTypeBase *ntb = NativeInterface::getNativeType(type); if (!ntb) { LSError("Unable to get NativeTypeBase for type %s", type->getFullName().c_str()); } if (type->isNativeManaged() != ntb->isManaged()) { if (type->isNativeManaged()) { LSError("Managed mismatch for type %s, script declaration specifies native while native bindings are unmanaged", type->getFullName().c_str()); } else { LSError("Managed mismatch for type %s, script declaration specifies unmanaged while native bindings are managed", type->getFullName().c_str()); } } ntb->validate(type); type->setCTypeName(ntb->getCTypeName()); } } } void LSLuaState::initializeLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { types[i]->cache(); } // initialize all classes for (UTsize i = 0; i < types.size(); i++) { initializeClass(types[i]); } // run static initializers now that all classes have been initialized for (UTsize i = 0; i < types.size(); i++) { lsr_classinitializestatic(VM(), types[i]); } } void LSLuaState::cacheAssemblyTypes(Assembly *assembly, utArray<Type *>& types) { // setup assembly type lookup field lua_rawgeti(L, LUA_GLOBALSINDEX, LSASSEMBLYLOOKUP); lua_pushlightuserdata(L, assembly); lua_setfield(L, -2, assembly->getName().c_str()); lua_pop(L, 1); assembly->ordinalTypes = new Type *[types.size() + 1]; for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); assembly->types.insert(type->getName(), type); lmAssert(type->getTypeID() > 0 && type->getTypeID() <= (LSTYPEID)types.size(), "LSLuaState::cacheAssemblyTypes TypeID out of range"); assembly->ordinalTypes[type->getTypeID()] = type; const char *typeName = type->getFullName().c_str(); // fast access cache if (!strcmp(typeName, "system.Object")) { objectType = type; } else if (!strcmp(typeName, "system.Null")) { nullType = type; } else if (!strcmp(typeName, "system.Boolean")) { booleanType = type; } else if (!strcmp(typeName, "system.Number")) { numberType = type; } else if (!strcmp(typeName, "system.String")) { stringType = type; } else if (!strcmp(typeName, "system.Function")) { functionType = type; } else if (!strcmp(typeName, "system.Vector")) { vectorType = type; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMEMBERINFONAME); lua_pushlightuserdata(L, type); lua_gettable(L, -2); // cache all members for fast lookup of memberinfo -> pre-interned // lua string (interning strings is the devil's work) if (lua_isnil(L, -1)) { lua_pop(L, 1); utArray<MemberInfo *> members; MemberTypes types; types.method = true; types.field = true; types.property = true; type->findMembers(types, members, false); // cache the type to member info table lua_pushlightuserdata(L, type); lua_pushstring(L, type->getName()); lua_settable(L, -3); for (UTsize i = 0; i < members.size(); i++) { MemberInfo *mi = members.at(i); lua_pushlightuserdata(L, mi); lua_pushstring(L, mi->getName()); lua_settable(L, -3); } } else { lua_pop(L, 1); } lua_pop(L, 1); // if we weren't cached during assembly load, cache now if (!typeCache.get(type->getFullName())) { typeCache.insert(type->getFullName(), type); } } lmAssert(nullType, "LSLuaState::cacheAssemblyTypes - system.Null not found"); lmAssert(booleanType, "LSLuaState::cacheAssemblyTypes - system.Boolean not found"); lmAssert(numberType, "LSLuaState::cacheAssemblyTypes - system.Number not found"); lmAssert(stringType, "LSLuaState::cacheAssemblyTypes - system.String not found"); lmAssert(functionType, "LSLuaState::cacheAssemblyTypes - system.Function not found"); lmAssert(vectorType, "LSLuaState::cacheAssemblyTypes - system.Vector not found"); } void LSLuaState::finalizeAssemblyLoad(Assembly *assembly, utArray<Type *>& types) { for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); if (type->isNative() || type->hasStaticNativeMember()) { // we're native NativeInterface::resolveScriptType(type); } } declareLuaTypes(types); initializeLuaTypes(types); // we avoid runtime validation on mobile, this works but should be unnecessary // as issues with be caught on OSX/WINDOWS development platforms #if LOOM_PLATFORM == LOOM_PLATFORM_OSX || LOOM_PLATFORM == LOOM_PLATFORM_WIN32 for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); TypeValidatorRT tv(this, type); tv.validate(); } #endif assembly->bootstrap(); } Assembly *LSLuaState::loadAssemblyJSON(const utString& json) { beginAssemblyLoad(); Assembly *assembly = Assembly::loadFromString(this, json); utArray<Type *> types; assembly->getTypes(types); cacheAssemblyTypes(assembly, types); if (!isCompiling()) { finalizeAssemblyLoad(assembly, types); } endAssemblyLoad(); return assembly; } Assembly *LSLuaState::loadAssemblyBinary(utByteArray *bytes) { Assembly *assembly = Assembly::loadBinary(this, bytes); return assembly; } Assembly *LSLuaState::loadExecutableAssembly(const utString& assemblyName, bool absPath) { // executables always in bin utString filePath; if (!absPath) { filePath = "./bin/"; } filePath += assemblyName; if (!strstr(filePath.c_str(), ".loom")) { filePath += ".loom"; } Assembly *assembly = NULL; const char *buffer = NULL; long bufferSize; LSMapFile(filePath.c_str(), (void **)&buffer, &bufferSize); lmAssert(buffer && bufferSize, "Error loading executable: %s, unable to map file", assemblyName.c_str()); utByteArray headerBytes; headerBytes.allocateAndCopy((void *)buffer, sizeof(unsigned int) * 4); // we need to decompress lmAssert(headerBytes.readUnsignedInt() == LOOM_BINARY_ID, "binary id mismatch"); lmAssert(headerBytes.readUnsignedInt() == LOOM_BINARY_VERSION_MAJOR, "major version mismatch"); lmAssert(headerBytes.readUnsignedInt() == LOOM_BINARY_VERSION_MINOR, "minor version mismatch"); unsigned int sz = headerBytes.readUnsignedInt(); utByteArray bytes; bytes.resize(sz); unsigned int readSZ = sz; int ok = uncompress((Bytef *)bytes.getDataPtr(), (uLongf *)&readSZ, (const Bytef *)((unsigned char *)buffer + sizeof(unsigned int) * 4), (uLong)sz); lmAssert(ok == Z_OK, "problem uncompressing executable assembly"); lmAssert(readSZ == sz, "Read size mismatch"); assembly = loadAssemblyBinary(&bytes); LSUnmapFile(filePath.c_str()); lmAssert(assembly, "Error loading executable: %s", assemblyName.c_str()); return assembly; } // get all types loaded for a given package void LSLuaState::getPackageTypes(const utString& packageName, utArray<Type *>& types) { for (UTsize i = 0; i < assemblies.size(); i++) { Assembly *assembly = assemblies.at(i); assembly->getPackageTypes(packageName, types); } } Assembly *LSLuaState::getAssembly(const utString& name) { for (UTsize i = 0; i < assemblies.size(); i++) { Assembly *assembly = assemblies.at(i); if (assembly->getName() == name) { return assembly; } if (assembly->getName() + ".loom" == name) { return assembly; } } return NULL; } void LSLuaState::invokeStaticMethod(const utString& typePath, const char *methodName, int numParameters) { Type *type = getType(typePath.c_str()); lmAssert(type, "LSLuaState::invokeStaticMethod unknown type: %s", typePath.c_str()); MemberInfo *member = type->findMember(methodName); lmAssert(member, "LSLuaState::invokeStaticMethod unknown member: %s:%s", typePath.c_str(), methodName); if (!member->isMethod()) { lmAssert(0, "LSLuaState::invokeStaticMethod member: %s:%s is not a method", typePath.c_str(), methodName); } MethodInfo *method = (MethodInfo *)member; lmAssert(method->isStatic(), "LSLuaState::invokeStaticMethod member: %s:%s is not a static method", typePath.c_str(), methodName); method->invoke(NULL, numParameters); } void LSLuaState::getClassTable(Type *type) { lsr_getclasstable(L, type); } void LSLuaState::declareClass(Type *type) { lsr_declareclass(L, type); } void LSLuaState::initializeClass(Type *type) { lsr_classinitialize(L, type); } void LSLuaState::tick() { invokeStaticMethod("system.VM", "_tick"); } void LSLuaState::initCommandLine(int argc, const char **argv) { for (int i = 0; i < argc; i++) { commandLine.push_back(argv[i]); } } void LSLuaState::dumpManagedNatives() { NativeInterface::dumpManagedNatives(L); } int LSLuaState::getStackSize() { return L->stacksize; } static void _getCurrentStack(lua_State *L, utStack<stackinfo>& stack) { int top = lua_gettop(L); lua_Debug lstack; int stackFrame = 0; MethodBase *lastMethod = NULL; while (true) { // if we get a null result here, we are out of stack if (!lua_getstack(L, stackFrame++, &lstack)) { lua_settop(L, top); return; } // something bad in denmark if (!lua_getinfo(L, "fSl", &lstack)) { lua_settop(L, top); return; } bool cfunc = false; if (lua_iscfunction(L, -1)) { cfunc = true; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_settop(L, top); continue; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_settop(L, top); // we only want the root call, not the pcall wrapper if (cfunc && (lastMethod == methodBase)) { continue; } lastMethod = methodBase; stackinfo si; si.methodBase = methodBase; si.source = methodBase->isNative() ? "[NATIVE]" : lstack.source; si.linenumber = lstack.currentline == -1 ? 0 : lstack.currentline; stack.push(si); } } int LSLuaState::traceback(lua_State *L) { _tracestack.clear(); if (lua_isstring(L, 1)) { snprintf(_tracemessage, 2040, "%s", lua_tostring(L, 1)); } else { _tracemessage[0] = 0; } _getCurrentStack(L, _tracestack); return 0; } void LSLuaState::triggerRuntimeError(const char *format, ...) { LSLog(LSLogError, "====================="); LSLog(LSLogError, "= RUNTIME ERROR ="); LSLog(LSLogError, "=====================\n"); char buff[2048]; va_list args; va_start(args, format); #ifdef _MSC_VER vsprintf_s(buff, 2046, format, args); #else vsnprintf(buff, 2046, format, args); #endif va_end(args); if (buff) { LSLog(LSLogError, "%s", buff); } if (_tracemessage[0]) { LSLog(LSLogError, "%s\n", _tracemessage); } _tracemessage[0] = 0; // coming from a native assert? if (!_tracestack.size()) { _getCurrentStack(L, _tracestack); } LSLog(LSLogError, "Stacktrace:", buff); for (UTsize i = 0; i < _tracestack.size(); i++) { const stackinfo& s = _tracestack.peek(_tracestack.size() - i - 1); LSLog(LSLogError, "%s : %s : %i", s.methodBase->getFullMemberName(), s.source ? s.source : NULL, s.linenumber); } LSError("\nFatal Runtime Error\n\n"); } }
void LSProfiler::getCurrentStack(lua_State *L, utStack<MethodBase *>& stack) { int top = lua_gettop(L); lua_Debug lstack; int stackFrame = 0; MethodBase *lastMethod = NULL; while (true) { // if we get a null result here, we are out of stack if (!lua_getstack(L, stackFrame++, &lstack)) { lua_settop(L, top); return; } // something bad in denmark if (!lua_getinfo(L, "f", &lstack)) { lua_settop(L, top); return; } bool cfunc = false; if (lua_iscfunction(L, -1)) { cfunc = true; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMETHODLOOKUP); lua_pushvalue(L, -2); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_settop(L, top); continue; } MethodBase *methodBase = (MethodBase *)lua_topointer(L, -1); lua_settop(L, top); #ifdef LOOM_ENABLE_JIT // We do not receive line return hooks for native calls under JIT :( // So, don't add to initial stack if (cfunc) { continue; } #endif if (shouldFilterFunction(methodBase->getFullMemberName())) { continue; } // we only want the root call, not the pcall wrapper if (cfunc && (lastMethod == methodBase)) { continue; } lastMethod = methodBase; stack.push(methodBase); } }