Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Changeset 6660


Ignore:
Timestamp:
Mar 31, 2010, 12:04:30 AM (15 years ago)
Author:
rgrieder
Message:

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.

Location:
code/branches/gamestate
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • code/branches/gamestate/data/lua/Debugger.lua

    r6626 r6660  
    433433
    434434  -- Transform line endings to \n
    435   text :gsub("\r\n", "\n") -- Windows to Unix
     435  text:gsub("\r\n", "\n") -- Windows to Unix
    436436  text:gsub("\r", "\n")   -- Mac to Unix
    437437  if text[-1] ~= "\n" then
  • code/branches/gamestate/data/lua/LuaStateInit.lua

    r6629 r6660  
    1717dofile = function(filename)
    1818  luaState:doFile(filename)
    19   -- Required because the C++ function cannot return whatever might be on the stack
     19  -- Required because if the file returns a table, it cannot be passed through the C++ function
    2020  return LuaStateReturnValue -- C-injected global variable
    2121end
     
    2626include = function(filename)
    2727  luaState:includeFile(filename)
    28   -- Required because the C++ function cannot return whatever might be on the stack
     28  -- Required because if the file returns a table, it cannot be passed through the C++ function
    2929  return LuaStateReturnValue -- C-injected global variable
    3030end
     
    4444    _LOADED = {}
    4545  end
    46   if not _LOADED[moduleName] then
     46  if _LOADED[moduleName] == nil then
    4747    -- save old value
    4848    local _REQUIREDNAME_OLD = _REQUIREDNAME
    4949    _REQUIREDNAME = moduleName
    5050    luaState:doFile(moduleName .. ".lua")
    51     _LOADED[moduleName] = LuaStateReturnValue
     51    -- LuaStateReturnValue is required because if the file returns a table,
     52    -- it cannot be passed through the C++ function
     53    if LuaStateReturnValue == nil then -- C-injected global variable
     54        LuaStateReturnValue = true
     55    end
     56    _LOADED[moduleName] = LuaStateReturnValue -- This entry must never be nil
    5257    -- restore old value
    5358    _REQUIREDNAME = _REQUIREDNAME_OLD
     
    5560  return _LOADED[moduleName]
    5661end
     62
     63
     64-- Include command line debugger for lua 5.1
     65-- Note: It doesn't work if the IOConsole was started. Then we replace pause() with a warning
     66if _VERSION ~= "Lua 5.0"  and not luaState:usingIOConsole() then
     67  require("Debugger")
     68else
     69  -- Fallback pause function
     70  pause = function()
     71    logMessage(2, [["Warning: debug() called in Lua, but Debugger is not active.
     72Do you have the IOConsole disabled and are you using Lua version 5.1?"]])
     73  end
     74end
     75
     76-- General error handler that gets called whenever an error happens at runtime
     77errorHandler = function(err)
     78  -- Display the error message
     79  if type(err) == "string" then
     80    logMessage(1, "Lua runtime error: "..err)
     81  end
     82
     83  -- Start debugger if possible
     84  if _LOADED and _LOADED["Debugger"] ~= nil then
     85    pause()
     86  else
     87    -- Fallback: print stack trace
     88    debug.traceback(nil, "", 2)
     89  end
     90  return err -- Hello Lua debugger user! Please type 'set 2' to get to the
     91             -- actual position in the stack where the error occurred
     92end
     93
    5794
    5895-- Convenience function for console commands
     
    71108  return orxonox.SettingsConfigFile:getInstance():tconfig(section, entry, value)
    72109end
    73 
    74 -- Include command line debugger for lua 5.1
    75 if _VERSION ~= "Lua 5.0" then
    76   require("Debugger")
    77 end
  • code/branches/gamestate/src/libraries/core/LuaState.cc

    r6655 r6660  
    3737
    3838#include "util/Debug.h"
     39#include "util/Exception.h"
     40#include "util/ScopeGuard.h"
     41#include "IOConsole.h"
    3942#include "Resource.h"
    4043#include "ToluaBindCore.h"
     
    5457        // Create new lua state and configure it
    5558        luaState_ = lua_open();
     59        Loki::ScopeGuard luaStateGuard = Loki::MakeGuard(&lua_close, luaState_);
    5660#if LUA_VERSION_NUM == 501
    5761        luaL_openlibs(luaState_);
     
    7882
    7983        // Parse init script
    80         this->doFile("LuaStateInit.lua");
     84        if (!this->doFile("LuaStateInit.lua"))
     85            ThrowException(InitialisationFailed, "Running LuaStateInit.lua failed");
     86
     87        luaStateGuard.Dismiss();
    8188    }
    8289
     
    96103    }
    97104
    98     void LuaState::includeFile(const std::string& filename)
     105    bool LuaState::includeFile(const std::string& filename)
    99106    {
    100107        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
    101108        if (sourceInfo != NULL)
    102             this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
    103         else
    104             COUT(2) << "LuaState: Cannot include file '" << filename << "'." << std::endl;
    105     }
    106 
    107     void LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
     109            return this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
     110        else
     111        {
     112            COUT(2) << "LuaState: Cannot include file '" << filename << "' (not found)." << std::endl;
     113            return false;
     114        }
     115    }
     116
     117    bool LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
    108118    {
    109119        // Parse string with provided include parser (otherwise don't preparse at all)
     
    121131        }
    122132
    123         this->doString(luaInput, sourceFileInfo);
     133        bool returnValue = this->doString(luaInput, sourceFileInfo);
    124134
    125135        if (sourceFileInfo != NULL)
     
    129139                this->sourceCodeMap_.erase(sourceFileInfo->filename);
    130140        }
    131     }
    132 
    133     void LuaState::doFile(const std::string& filename)
     141
     142        return returnValue;
     143    }
     144
     145    bool LuaState::doFile(const std::string& filename)
    134146    {
    135147        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
    136148        if (sourceInfo != NULL)
    137             this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
    138         else
    139             COUT(2) << "LuaState: Cannot do file '" << filename << "'." << std::endl;
    140     }
    141 
    142     void LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
     149            return this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
     150        else
     151        {
     152            COUT(2) << "LuaState: Cannot do file '" << filename << "' (not found)." << std::endl;
     153            return false;
     154        }
     155    }
     156
     157    bool LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
    143158    {
    144159        // Save the old source file info
     
    161176        }
    162177
    163         int error = 0;
     178        // Push custom error handler that uses the debugger
     179        int errorHandler = 1;
     180        lua_getglobal(this->luaState_, "errorHandler");
     181        if (lua_isnil(this->luaState_, -1))
     182        {
     183            lua_pop(this->luaState_, 1);
     184            errorHandler = 0;
     185        }
     186
    164187#if LUA_VERSION_NUM != 501
    165188        LoadS ls;
    166189        ls.s = code.c_str();
    167190        ls.size = code.size();
    168         error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
     191        int error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
    169192#else
    170         error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
     193        int error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
    171194#endif
    172195
    173         // execute the chunk
     196        switch (error)
     197        {
     198        case LUA_ERRSYNTAX: // Syntax error
     199            COUT(1) << "Lua syntax error: " << lua_tostring(luaState_, -1) << std::endl;
     200            break;
     201        case LUA_ERRMEM:    // Memory allocation error
     202            COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
     203            lua_pop(luaState_, 1);
     204            break;
     205        }
     206
    174207        if (error == 0)
    175             error = lua_pcall(luaState_, 0, 1, 0);
     208        {
     209            // Execute the chunk in protected mode with an error handler function (stack index)
     210            error = lua_pcall(luaState_, 0, 1, errorHandler);
     211
     212            switch (error)
     213            {
     214            case LUA_ERRRUN: // Runtime error
     215                // Remove error string from stack (we already display the error in the
     216                // 'errorHandler' Lua function in LuaStateInit.lua)
     217                lua_pop(luaState_, 1);
     218                break;
     219            case LUA_ERRERR: // Error in the error handler
     220                COUT(1) << "Lua error in error handler: " << lua_tostring(luaState_, -1) << std::endl;
     221                break;
     222            case LUA_ERRMEM: // Memory allocation error
     223                COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
     224                lua_pop(luaState_, 1);
     225                break;
     226            }
     227        }
     228
    176229        if (error != 0)
    177230        {
    178             std::string origin;
    179             if (sourceFileInfo != NULL)
    180                 origin = " originating from " + sourceFileInfo_->filename;
    181             COUT(1) << "Error in Lua-script" << origin << ": " << lua_tostring(luaState_, -1) << std::endl;
    182             // return value is true (not nil!)
    183             lua_pushboolean(luaState_, 1);
    184         }
    185         if (lua_isnil(luaState_, -1))
    186         {
    187             // Nil return values cause problems
    188             lua_pop(luaState_, 1);
    189             lua_pushboolean(luaState_, 1);
    190         }
    191         // Push return value because it will get lost since the return value of this function is void
     231            // Push a nil return value
     232            lua_pushnil(luaState_);
     233        }
     234
     235        // Set return value to a global variable because we cannot return a table in this function
     236        // here. It would work for numbers, pointers and strings, but certainly not for Lua tables.
    192237        lua_setglobal(luaState_, "LuaStateReturnValue");
    193238
    194239        // Load the old info again
    195240        sourceFileInfo_ = oldSourceFileInfo;
     241
     242        return (error == 0);
    196243    }
    197244
     
    230277    }
    231278
     279    bool LuaState::usingIOConsole() const
     280    {
     281        return IOConsole::exists();
     282    }
     283
    232284#if LUA_VERSION_NUM != 501
    233285    const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
  • code/branches/gamestate/src/libraries/core/LuaState.h

    r6627 r6660  
    7171        ~LuaState();
    7272
    73         void doFile(const std::string& filename); // tolua_export
    74         void doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
     73        bool doFile(const std::string& filename); // tolua_export
     74        bool doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
    7575
    76         void includeFile(const std::string& filename); // tolua_export
    77         void includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
     76        bool includeFile(const std::string& filename); // tolua_export
     77        bool includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo = shared_ptr<ResourceInfo>());
    7878
    7979        void luaPrint(const std::string& str); // tolua_export
     
    9292
    9393        Functor* createLuaFunctor(const std::string& code) { return new LuaFunctor(code, this); } // tolua_export
     94        //! Tells about whether IOConsole was activated. The Lua debugger only works with a normal console.
     95        bool usingIOConsole() const; // tolua_export
    9496
    9597        static bool addToluaInterface(int (*function)(lua_State*), const std::string& name);
Note: See TracChangeset for help on using the changeset viewer.