[Orxonox-commit 1943] r6660 - in code/branches/gamestate: data/lua src/libraries/core
rgrieder at orxonox.net
rgrieder at orxonox.net
Wed Mar 31 00:04:30 CEST 2010
Author: rgrieder
Date: 2010-03-31 00:04:30 +0200 (Wed, 31 Mar 2010)
New Revision: 6660
Modified:
code/branches/gamestate/data/lua/Debugger.lua
code/branches/gamestate/data/lua/LuaStateInit.lua
code/branches/gamestate/src/libraries/core/LuaState.cc
code/branches/gamestate/src/libraries/core/LuaState.h
Log:
Improved Lua error handling: Clearer messages and debugger hook.
So, if the IOConsole is not running and a runtime error occurs, the lua debugger automatically gets started.
Modified: code/branches/gamestate/data/lua/Debugger.lua
===================================================================
--- code/branches/gamestate/data/lua/Debugger.lua 2010-03-30 14:34:01 UTC (rev 6659)
+++ code/branches/gamestate/data/lua/Debugger.lua 2010-03-30 22:04:30 UTC (rev 6660)
@@ -432,7 +432,7 @@
end
-- Transform line endings to \n
- text :gsub("\r\n", "\n") -- Windows to Unix
+ text:gsub("\r\n", "\n") -- Windows to Unix
text:gsub("\r", "\n") -- Mac to Unix
if text[-1] ~= "\n" then
text = text.."\n"
Modified: code/branches/gamestate/data/lua/LuaStateInit.lua
===================================================================
--- code/branches/gamestate/data/lua/LuaStateInit.lua 2010-03-30 14:34:01 UTC (rev 6659)
+++ code/branches/gamestate/data/lua/LuaStateInit.lua 2010-03-30 22:04:30 UTC (rev 6660)
@@ -16,7 +16,7 @@
original_dofile = dofile
dofile = function(filename)
luaState:doFile(filename)
- -- Required because the C++ function cannot return whatever might be on the stack
+ -- Required because if the file returns a table, it cannot be passed through the C++ function
return LuaStateReturnValue -- C-injected global variable
end
doFile = dofile
@@ -25,7 +25,7 @@
-- to a function provided to the LuaState constructor (in C++)
include = function(filename)
luaState:includeFile(filename)
- -- Required because the C++ function cannot return whatever might be on the stack
+ -- Required because if the file returns a table, it cannot be passed through the C++ function
return LuaStateReturnValue -- C-injected global variable
end
@@ -43,18 +43,55 @@
if not _LOADED then
_LOADED = {}
end
- if not _LOADED[moduleName] then
+ if _LOADED[moduleName] == nil then
-- save old value
local _REQUIREDNAME_OLD = _REQUIREDNAME
_REQUIREDNAME = moduleName
luaState:doFile(moduleName .. ".lua")
- _LOADED[moduleName] = LuaStateReturnValue
+ -- LuaStateReturnValue is required because if the file returns a table,
+ -- it cannot be passed through the C++ function
+ if LuaStateReturnValue == nil then -- C-injected global variable
+ LuaStateReturnValue = true
+ end
+ _LOADED[moduleName] = LuaStateReturnValue -- This entry must never be nil
-- restore old value
_REQUIREDNAME = _REQUIREDNAME_OLD
end
return _LOADED[moduleName]
end
+
+-- Include command line debugger for lua 5.1
+-- Note: It doesn't work if the IOConsole was started. Then we replace pause() with a warning
+if _VERSION ~= "Lua 5.0" and not luaState:usingIOConsole() then
+ require("Debugger")
+else
+ -- Fallback pause function
+ pause = function()
+ logMessage(2, [["Warning: debug() called in Lua, but Debugger is not active.
+Do you have the IOConsole disabled and are you using Lua version 5.1?"]])
+ end
+end
+
+-- General error handler that gets called whenever an error happens at runtime
+errorHandler = function(err)
+ -- Display the error message
+ if type(err) == "string" then
+ logMessage(1, "Lua runtime error: "..err)
+ end
+
+ -- Start debugger if possible
+ if _LOADED and _LOADED["Debugger"] ~= nil then
+ pause()
+ else
+ -- Fallback: print stack trace
+ debug.traceback(nil, "", 2)
+ end
+ return err -- Hello Lua debugger user! Please type 'set 2' to get to the
+ -- actual position in the stack where the error occurred
+end
+
+
-- Convenience function for console commands
orxonox.execute = function(command)
orxonox.CommandExecutor:execute(command)
@@ -70,8 +107,3 @@
orxonox.tconfig = function(section, entry, value)
return orxonox.SettingsConfigFile:getInstance():tconfig(section, entry, value)
end
-
--- Include command line debugger for lua 5.1
-if _VERSION ~= "Lua 5.0" then
- require("Debugger")
-end
Modified: code/branches/gamestate/src/libraries/core/LuaState.cc
===================================================================
--- code/branches/gamestate/src/libraries/core/LuaState.cc 2010-03-30 14:34:01 UTC (rev 6659)
+++ code/branches/gamestate/src/libraries/core/LuaState.cc 2010-03-30 22:04:30 UTC (rev 6660)
@@ -36,6 +36,9 @@
}
#include "util/Debug.h"
+#include "util/Exception.h"
+#include "util/ScopeGuard.h"
+#include "IOConsole.h"
#include "Resource.h"
#include "ToluaBindCore.h"
@@ -53,6 +56,7 @@
{
// Create new lua state and configure it
luaState_ = lua_open();
+ Loki::ScopeGuard luaStateGuard = Loki::MakeGuard(&lua_close, luaState_);
#if LUA_VERSION_NUM == 501
luaL_openlibs(luaState_);
#else
@@ -77,7 +81,10 @@
lua_setglobal(luaState_, "luaState");
// Parse init script
- this->doFile("LuaStateInit.lua");
+ if (!this->doFile("LuaStateInit.lua"))
+ ThrowException(InitialisationFailed, "Running LuaStateInit.lua failed");
+
+ luaStateGuard.Dismiss();
}
LuaState::~LuaState()
@@ -95,16 +102,19 @@
return sourceInfo;
}
- void LuaState::includeFile(const std::string& filename)
+ bool LuaState::includeFile(const std::string& filename)
{
shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
if (sourceInfo != NULL)
- this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
+ return this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
else
- COUT(2) << "LuaState: Cannot include file '" << filename << "'." << std::endl;
+ {
+ COUT(2) << "LuaState: Cannot include file '" << filename << "' (not found)." << std::endl;
+ return false;
+ }
}
- void LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
+ bool LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
{
// Parse string with provided include parser (otherwise don't preparse at all)
std::string luaInput;
@@ -120,7 +130,7 @@
this->sourceCodeMap_[sourceFileInfo->filename] = code;
}
- this->doString(luaInput, sourceFileInfo);
+ bool returnValue = this->doString(luaInput, sourceFileInfo);
if (sourceFileInfo != NULL)
{
@@ -128,18 +138,23 @@
if (sourceFileInfo != NULL)
this->sourceCodeMap_.erase(sourceFileInfo->filename);
}
+
+ return returnValue;
}
- void LuaState::doFile(const std::string& filename)
+ bool LuaState::doFile(const std::string& filename)
{
shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
if (sourceInfo != NULL)
- this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
+ return this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
else
- COUT(2) << "LuaState: Cannot do file '" << filename << "'." << std::endl;
+ {
+ COUT(2) << "LuaState: Cannot do file '" << filename << "' (not found)." << std::endl;
+ return false;
+ }
}
- void LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
+ bool LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
{
// Save the old source file info
shared_ptr<ResourceInfo> oldSourceFileInfo = sourceFileInfo_;
@@ -160,39 +175,71 @@
chunkname = code;
}
- int error = 0;
+ // Push custom error handler that uses the debugger
+ int errorHandler = 1;
+ lua_getglobal(this->luaState_, "errorHandler");
+ if (lua_isnil(this->luaState_, -1))
+ {
+ lua_pop(this->luaState_, 1);
+ errorHandler = 0;
+ }
+
#if LUA_VERSION_NUM != 501
LoadS ls;
ls.s = code.c_str();
ls.size = code.size();
- error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
+ int error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
#else
- error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
+ int error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
#endif
- // execute the chunk
+ switch (error)
+ {
+ case LUA_ERRSYNTAX: // Syntax error
+ COUT(1) << "Lua syntax error: " << lua_tostring(luaState_, -1) << std::endl;
+ break;
+ case LUA_ERRMEM: // Memory allocation error
+ COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
+ lua_pop(luaState_, 1);
+ break;
+ }
+
if (error == 0)
- error = lua_pcall(luaState_, 0, 1, 0);
- if (error != 0)
{
- std::string origin;
- if (sourceFileInfo != NULL)
- origin = " originating from " + sourceFileInfo_->filename;
- COUT(1) << "Error in Lua-script" << origin << ": " << lua_tostring(luaState_, -1) << std::endl;
- // return value is true (not nil!)
- lua_pushboolean(luaState_, 1);
+ // Execute the chunk in protected mode with an error handler function (stack index)
+ error = lua_pcall(luaState_, 0, 1, errorHandler);
+
+ switch (error)
+ {
+ case LUA_ERRRUN: // Runtime error
+ // Remove error string from stack (we already display the error in the
+ // 'errorHandler' Lua function in LuaStateInit.lua)
+ lua_pop(luaState_, 1);
+ break;
+ case LUA_ERRERR: // Error in the error handler
+ COUT(1) << "Lua error in error handler: " << lua_tostring(luaState_, -1) << std::endl;
+ break;
+ case LUA_ERRMEM: // Memory allocation error
+ COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
+ lua_pop(luaState_, 1);
+ break;
+ }
}
- if (lua_isnil(luaState_, -1))
+
+ if (error != 0)
{
- // Nil return values cause problems
- lua_pop(luaState_, 1);
- lua_pushboolean(luaState_, 1);
+ // Push a nil return value
+ lua_pushnil(luaState_);
}
- // Push return value because it will get lost since the return value of this function is void
+
+ // Set return value to a global variable because we cannot return a table in this function
+ // here. It would work for numbers, pointers and strings, but certainly not for Lua tables.
lua_setglobal(luaState_, "LuaStateReturnValue");
// Load the old info again
sourceFileInfo_ = oldSourceFileInfo;
+
+ return (error == 0);
}
void LuaState::luaPrint(const std::string& str)
@@ -229,6 +276,11 @@
return Resource::open(info)->getAsString();
}
+ bool LuaState::usingIOConsole() const
+ {
+ return IOConsole::exists();
+ }
+
#if LUA_VERSION_NUM != 501
const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
{
Modified: code/branches/gamestate/src/libraries/core/LuaState.h
===================================================================
--- code/branches/gamestate/src/libraries/core/LuaState.h 2010-03-30 14:34:01 UTC (rev 6659)
+++ code/branches/gamestate/src/libraries/core/LuaState.h 2010-03-30 22:04:30 UTC (rev 6660)
@@ -70,11 +70,11 @@
LuaState();
~LuaState();
- void doFile(const std::string& filename); // tolua_export
- void doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
+ bool doFile(const std::string& filename); // tolua_export
+ bool doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
- void includeFile(const std::string& filename); // tolua_export
- void includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
+ bool includeFile(const std::string& filename); // tolua_export
+ bool includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
void luaPrint(const std::string& str); // tolua_export
void luaLog(unsigned int level, const std::string& message); // tolua_export
@@ -91,6 +91,8 @@
const shared_ptr<ResourceInfo>& getDefaultResourceInfo() { return this->sourceFileInfo_; }
Functor* createLuaFunctor(const std::string& code) { return new LuaFunctor(code, this); } // tolua_export
+ //! Tells about whether IOConsole was activated. The Lua debugger only works with a normal console.
+ bool usingIOConsole() const; // tolua_export
static bool addToluaInterface(int (*function)(lua_State*), const std::string& name);
static bool removeToluaInterface(const std::string& name);
More information about the Orxonox-commit
mailing list