Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/LuaState.cc @ 7072

Last change on this file since 7072 was 6763, checked in by rgrieder, 15 years ago

Using our own error handler (including the debugger) for Lua code executed through CEGUILuaFunctor.

  • Property svn:eol-style set to native
File size: 12.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Benjamin Knecht
24 *      Reto Grieder
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "LuaState.h"
31
32#include <tolua/tolua++.h>
33extern "C" {
34#include <lua.h>
35#include <lualib.h>
36}
37
38#include "util/Debug.h"
39#include "util/Exception.h"
40#include "util/ScopeGuard.h"
41#include "IOConsole.h"
42#include "Resource.h"
43#include "ToluaBindCore.h"
44
45namespace orxonox
46{
47    LuaState::ToluaInterfaceMap LuaState::toluaInterfaces_s;
48    std::vector<LuaState*> LuaState::instances_s;
49
50    const std::string LuaState::ERROR_HANDLER_NAME = "errorHandler";
51
52    // Do this after declaring toluaInterfaces_s and instances_s to avoid larger problems
53    DeclareToluaInterface(Core);
54
55    LuaState::LuaState()
56        : bIsRunning_(false)
57        , includeParseFunction_(NULL)
58    {
59        // Create new lua state and configure it
60        luaState_ = lua_open();
61        Loki::ScopeGuard luaStateGuard = Loki::MakeGuard(&lua_close, luaState_);
62#if LUA_VERSION_NUM == 501
63        luaL_openlibs(luaState_);
64#else
65        luaopen_base(luaState_);
66        luaopen_string(luaState_);
67        luaopen_table(luaState_);
68        luaopen_math(luaState_);
69        luaopen_io(luaState_);
70        luaopen_debug(luaState_);
71#endif
72
73        // Open all available tolua interfaces
74        this->openToluaInterfaces(luaState_);
75
76        // Create dummy file info
77        sourceFileInfo_.reset(new ResourceInfo());
78        sourceFileInfo_->group = "General";
79        sourceFileInfo_->size = 0;
80
81        // Push 'this' pointer
82        tolua_pushusertype(luaState_, static_cast<void*>(this), "orxonox::LuaState");
83        lua_setglobal(luaState_, "luaState");
84
85        // Parse init script
86        if (!this->doFile("LuaStateInit.lua"))
87            ThrowException(InitialisationFailed, "Running LuaStateInit.lua failed");
88
89        luaStateGuard.Dismiss();
90    }
91
92    LuaState::~LuaState()
93    {
94        lua_close(luaState_);
95    }
96
97    shared_ptr<ResourceInfo> LuaState::getFileInfo(const std::string& filename)
98    {
99        // Look in the current directory first
100        shared_ptr<ResourceInfo> sourceInfo = Resource::getInfo(sourceFileInfo_->path + filename);
101        // Continue search in root directories
102        if (sourceInfo == NULL && !sourceFileInfo_->path.empty())
103            sourceInfo = Resource::getInfo(filename);
104        return sourceInfo;
105    }
106
107    bool LuaState::includeFile(const std::string& filename)
108    {
109        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
110        if (sourceInfo != NULL)
111            return this->includeString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
112        else
113        {
114            COUT(2) << "LuaState: Cannot include file '" << filename << "' (not found)." << std::endl;
115            return false;
116        }
117    }
118
119    bool LuaState::includeString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
120    {
121        // Parse string with provided include parser (otherwise don't preparse at all)
122        std::string luaInput;
123        if (includeParseFunction_ != NULL)
124            luaInput = (*includeParseFunction_)(code);
125        else
126            luaInput = code;
127
128        if (sourceFileInfo != NULL)
129        {
130            // Also fill a map with the actual source code. This is just for the include* commands
131            // where the content of sourceFileInfo->filename doesn't match 'code'
132            this->sourceCodeMap_[sourceFileInfo->filename] = code;
133        }
134
135        bool returnValue = this->doString(luaInput, sourceFileInfo);
136
137        if (sourceFileInfo != NULL)
138        {
139            // Delete source code entry
140            if (sourceFileInfo != NULL)
141                this->sourceCodeMap_.erase(sourceFileInfo->filename);
142        }
143
144        return returnValue;
145    }
146
147    bool LuaState::doFile(const std::string& filename)
148    {
149        shared_ptr<ResourceInfo> sourceInfo = this->getFileInfo(filename);
150        if (sourceInfo != NULL)
151            return this->doString(Resource::open(sourceInfo)->getAsString(), sourceInfo);
152        else
153        {
154            COUT(2) << "LuaState: Cannot do file '" << filename << "' (not found)." << std::endl;
155            return false;
156        }
157    }
158
159    bool LuaState::doString(const std::string& code, const shared_ptr<ResourceInfo>& sourceFileInfo)
160    {
161        // Save the old source file info
162        shared_ptr<ResourceInfo> oldSourceFileInfo = sourceFileInfo_;
163        // Only override if sourceFileInfo provides useful information
164        if (sourceFileInfo != NULL)
165            sourceFileInfo_ = sourceFileInfo;
166
167        std::string chunkname;
168        if (sourceFileInfo != NULL)
169        {
170            // Provide lua_load with the filename for debug purposes
171            // The '@' is a Lua convention to identify the chunk name as filename
172            chunkname = '@' + sourceFileInfo->filename;
173        }
174        else
175        {
176            // Use the code string to identify the chunk
177            chunkname = code;
178        }
179
180        // Push custom error handler that uses the debugger
181        lua_getglobal(this->luaState_, ERROR_HANDLER_NAME.c_str());
182        int errorHandler = lua_gettop(luaState_);
183        if (lua_isnil(this->luaState_, -1))
184        {
185            lua_pop(this->luaState_, 1);
186            errorHandler = 0;
187        }
188
189#if LUA_VERSION_NUM != 501
190        LoadS ls;
191        ls.s = code.c_str();
192        ls.size = code.size();
193        int error = lua_load(luaState_, &orxonox::LuaState::lua_Chunkreader, &ls, chunkname.c_str());
194#else
195        int error = luaL_loadbuffer(luaState_, code.c_str(), code.size(), chunkname.c_str());
196#endif
197
198        switch (error)
199        {
200        case LUA_ERRSYNTAX: // Syntax error
201            COUT(1) << "Lua syntax error: " << lua_tostring(luaState_, -1) << std::endl;
202            break;
203        case LUA_ERRMEM:    // Memory allocation error
204            COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
205            break;
206        }
207
208        if (error == 0)
209        {
210            // Execute the chunk in protected mode with an error handler function (stack index)
211            error = lua_pcall(luaState_, 0, 1, errorHandler);
212
213            switch (error)
214            {
215            case LUA_ERRRUN: // Runtime error
216                if (errorHandler)
217                {
218                    // Do nothing (we already display the error in the
219                    // 'errorHandler' Lua function in LuaStateInit.lua)
220                }
221                else
222                {
223                    std::string errorString = lua_tostring(this->luaState_, -1);
224                    if (errorString.find("Error propagation") == std::string::npos)
225                        COUT(1) << "Lua runtime error: " << errorString << std::endl;
226                }
227                break;
228            case LUA_ERRERR: // Error in the error handler
229                COUT(1) << "Lua error in error handler. No message available." << std::endl;
230                break;
231            case LUA_ERRMEM: // Memory allocation error
232                COUT(1) << "Lua memory allocation error: Consult your dentist immediately!" << std::endl;
233                break;
234            }
235        }
236
237        if (error != 0)
238        {
239            lua_pop(luaState_, 1);  // Remove error message
240            lua_pushnil(luaState_); // Push a nil return value
241        }
242
243        if (errorHandler != 0)
244            lua_remove(luaState_, errorHandler); // Remove error handler from stack
245
246        // Set return value to a global variable because we cannot return a table in this function
247        // here. It would work for numbers, pointers and strings, but certainly not for Lua tables.
248        lua_setglobal(luaState_, "LuaStateReturnValue");
249
250        // Load the old info again
251        sourceFileInfo_ = oldSourceFileInfo;
252
253        return (error == 0);
254    }
255
256    void LuaState::luaPrint(const std::string& str)
257    {
258        output_ << str;
259    }
260
261    void LuaState::luaLog(unsigned int level, const std::string& message)
262    {
263        OutputHandler::getOutStream(level) << message << std::endl;
264    }
265
266    bool LuaState::fileExists(const std::string& filename)
267    {
268        shared_ptr<ResourceInfo> info = this->getFileInfo(filename);
269        if (info == NULL)
270            return false;
271        else
272            return true;
273    }
274
275    //! Returns the content of a file
276    std::string LuaState::getSourceCode(const std::string& filename)
277    {
278        // Try the internal map first to get the actual Lua code
279        // and not just some pseudo Lua-XML code when using include* commands
280        std::map<std::string, std::string>::const_iterator it = this->sourceCodeMap_.find(filename);
281        if (it != this->sourceCodeMap_.end())
282            return it->second;
283        shared_ptr<ResourceInfo> info = Resource::getInfo(filename);
284        if (info == NULL)
285            return "";
286        else
287            return Resource::open(info)->getAsString();
288    }
289
290    bool LuaState::usingIOConsole() const
291    {
292        return IOConsole::exists();
293    }
294
295#if LUA_VERSION_NUM != 501
296    const char * LuaState::lua_Chunkreader(lua_State *L, void *data, size_t *size)
297    {
298        LoadS* ls = static_cast<LoadS*>(data);
299        if (ls->size == 0)
300            return NULL;
301        *size = ls->size;
302        ls->size = 0;
303        return ls->s;
304    }
305#endif
306
307    /*static*/ bool LuaState::addToluaInterface(int (*function)(lua_State*), const std::string& name)
308    {
309        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
310        {
311            if (it->first == name || it->second == function)
312            {
313                COUT(2) << "Warning: Trying to add a Tolua interface with the same name or function." << std::endl;
314                return true;
315            }
316        }
317        toluaInterfaces_s[name] = function;
318
319        // Open interface in all LuaStates
320        for (std::vector<LuaState*>::const_iterator it = instances_s.begin(); it != instances_s.end(); ++it)
321            (*function)((*it)->luaState_);
322
323        // Return dummy bool
324        return true;
325    }
326
327    /*static*/ bool LuaState::removeToluaInterface(const std::string& name)
328    {
329        ToluaInterfaceMap::iterator it = toluaInterfaces_s.find(name);
330        if (it == toluaInterfaces_s.end())
331        {
332            COUT(2) << "Warning: Cannot remove Tolua interface '" << name << "': Not found" << std::endl;
333            return true;
334        }
335
336        // Close interface in all LuaStates
337        for (std::vector<LuaState*>::const_iterator itState = instances_s.begin(); itState != instances_s.end(); ++itState)
338        {
339            lua_pushnil((*itState)->luaState_);
340            lua_setglobal((*itState)->luaState_, it->first.c_str());
341        }
342
343        // Remove entry
344        toluaInterfaces_s.erase(it);
345
346        // Return dummy bool
347        return true;
348    }
349
350    /*static*/ void LuaState::openToluaInterfaces(lua_State* state)
351    {
352        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
353            (*it->second)(state);
354    }
355
356    /*static*/ void LuaState::closeToluaInterfaces(lua_State* state)
357    {
358        for (ToluaInterfaceMap::const_iterator it = toluaInterfaces_s.begin(); it != toluaInterfaces_s.end(); ++it)
359        {
360            lua_pushnil(state);
361            lua_setglobal(state, it->first.c_str());
362        }
363    }
364
365
366    LuaFunctor::LuaFunctor(const std::string& code, LuaState* luaState)
367    {
368        this->code_ = code;
369        this->lua_ = luaState;
370    }
371
372    void LuaFunctor::operator()(const MultiType& param1, const MultiType& param2, const MultiType& param3, const MultiType& param4, const MultiType& param5)
373    {
374        lua_->doString(this->code_);
375    }
376}
Note: See TracBrowser for help on using the repository browser.