Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/external/ceguilua/ceguilua-0.6.2/ceguilua/CEGUILua.cpp @ 7163

Last change on this file since 7163 was 6761, checked in by rgrieder, 14 years ago

Fixed "error in error handler" problem that occurred within CEGUILua scripted events (like clicking on a button).
Initialising values is always a good idea…
Also fixed a potential bug: not removing the error handler from the Lua stack can be very dangerous as I had to find out lately..

  • Property svn:eol-style set to native
File size: 23.0 KB
Line 
1/***********************************************************************
2        filename: CEGUILua.cpp
3        created:  16/3/2005
4        author:   Tomas Lindquist Olsen
5
6        purpose:  Implementation for LuaScriptModule class
7*************************************************************************/
8/***************************************************************************
9 *   Copyright (C) 2004 - 2008 Paul D Turner & The CEGUI Development Team
10 *
11 *   Permission is hereby granted, free of charge, to any person obtaining
12 *   a copy of this software and associated documentation files (the
13 *   "Software"), to deal in the Software without restriction, including
14 *   without limitation the rights to use, copy, modify, merge, publish,
15 *   distribute, sublicense, and/or sell copies of the Software, and to
16 *   permit persons to whom the Software is furnished to do so, subject to
17 *   the following conditions:
18 *
19 *   The above copyright notice and this permission notice shall be
20 *   included in all copies or substantial portions of the Software.
21 *
22 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 *   OTHER DEALINGS IN THE SOFTWARE.
29 ***************************************************************************/
30#include "CEGUI.h"
31#include "CEGUIConfig.h"
32#include "CEGUIPropertyHelper.h"
33#include "CEGUILua.h"
34#include "CEGUILuaFunctor.h"
35#include <vector>
36
37// include Lua libs and tolua++
38extern "C" {
39#include "lua.h"
40#include "lualib.h"
41#include "lauxlib.h"
42}
43
44#include "tolua/tolua++.h"
45
46// prototype for bindings initialisation function
47int tolua_CEGUI_open(lua_State* tolua_S);
48
49
50// Start of CEGUI namespace section
51namespace CEGUI
52{
53
54/*************************************************************************
55        Constructor (creates Lua state)
56*************************************************************************/
57LuaScriptModule::LuaScriptModule() :
58    d_errFuncIndex(LUA_NOREF),
59    d_activeErrFuncIndex(LUA_NOREF)
60{
61    #if LUA_VERSION_NUM >= 501
62        static const luaL_Reg lualibs[] = {
63            {"", luaopen_base},
64            {LUA_LOADLIBNAME, luaopen_package},
65            {LUA_TABLIBNAME, luaopen_table},
66            {LUA_IOLIBNAME, luaopen_io},
67            {LUA_OSLIBNAME, luaopen_os},
68            {LUA_STRLIBNAME, luaopen_string},
69            {LUA_MATHLIBNAME, luaopen_math},
70        #if defined(DEBUG) || defined (_DEBUG)
71                {LUA_DBLIBNAME, luaopen_debug},
72        #endif
73            {0, 0}
74        };
75    #endif /* LUA_VERSION_NUM >= 501 */
76
77    // create a lua state
78    d_ownsState = true;
79    d_state = lua_open();
80
81    // init all standard libraries
82    #if LUA_VERSION_NUM >= 501
83            const luaL_Reg *lib = lualibs;
84            for (; lib->func; lib++)
85            {
86                lua_pushcfunction(d_state, lib->func);
87                lua_pushstring(d_state, lib->name);
88                lua_call(d_state, 1, 0);
89            }
90    #else /* LUA_VERSION_NUM >= 501 */
91        luaopen_base(d_state);
92        luaopen_io(d_state);
93        luaopen_string(d_state);
94        luaopen_table(d_state);
95        luaopen_math(d_state);
96        #if defined(DEBUG) || defined (_DEBUG)
97            luaopen_debug(d_state);
98        #endif
99    #endif /* LUA_VERSION_NUM >= 501 */
100
101    setModuleIdentifierString();
102}
103
104
105/*************************************************************************
106        Constructor (uses given Lua state)
107*************************************************************************/
108LuaScriptModule::LuaScriptModule(lua_State* state) :
109    d_errFuncIndex(LUA_NOREF),
110    d_activeErrFuncIndex(LUA_NOREF)
111{
112        // just use the given state
113        d_ownsState = false;
114        d_state = state;
115
116        setModuleIdentifierString();
117}
118
119
120/*************************************************************************
121        Destructor
122*************************************************************************/
123LuaScriptModule::~LuaScriptModule()
124{
125    if (d_state)
126    {
127        unrefErrorFunc();
128
129        if (d_ownsState)
130            lua_close( d_state );
131    }
132}
133
134
135/*************************************************************************
136        Execute script file
137*************************************************************************/
138void LuaScriptModule::executeScriptFile(const String& filename,
139    const String& resourceGroup)
140{
141    int top = lua_gettop(d_state);
142
143    executeScriptFile_impl(filename, resourceGroup,
144                           initErrorHandlerFunc(),
145                           top);
146
147    cleanupErrorHandlerFunc();
148}
149
150
151/*************************************************************************
152        Execute global script function
153*************************************************************************/
154int     LuaScriptModule::executeScriptGlobal(const String& function_name)
155{
156    int top = lua_gettop(d_state);
157    int r = executeScriptGlobal_impl(function_name, initErrorHandlerFunc(), top);
158    cleanupErrorHandlerFunc();
159
160    return r;
161}
162
163
164/*************************************************************************
165        Execute scripted event handler
166*************************************************************************/
167bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name,
168    const EventArgs& e)
169{
170    int top = lua_gettop(d_state);
171    bool r = executeScriptedEventHandler_impl(handler_name, e,
172                                              initErrorHandlerFunc(), top);
173    cleanupErrorHandlerFunc();
174
175    return r;
176}
177
178
179/*************************************************************************
180        Execute script code string
181*************************************************************************/
182void LuaScriptModule::executeString(const String& str)
183{
184    int top = lua_gettop(d_state);
185
186    executeString_impl(str, initErrorHandlerFunc(), top);
187    cleanupErrorHandlerFunc();
188}
189
190
191/*************************************************************************
192        Create Lua bindings
193*************************************************************************/
194void LuaScriptModule::createBindings(void)
195{
196        CEGUI::Logger::getSingleton().logEvent( "---- Creating Lua bindings ----" );
197        // init CEGUI module
198        tolua_CEGUI_open(d_state);
199}
200
201
202/*************************************************************************
203        Destroy Lua bindings
204*************************************************************************/
205void LuaScriptModule::destroyBindings(void)
206{
207        CEGUI::Logger::getSingleton().logEvent( "---- Destroying Lua bindings ----" );
208        // is this ok ?
209        lua_pushnil(d_state);
210        lua_setglobal(d_state,"CEGUI");
211}
212
213
214/*************************************************************************
215        Set the ID string for the module
216*************************************************************************/
217void LuaScriptModule::setModuleIdentifierString()
218{
219    // set ID string
220    d_identifierString = "CEGUI::LuaScriptModule - Official Lua based scripting module for CEGUI";
221        d_language = "Lua";
222}
223
224
225/*************************************************************************
226        Subscribe to a scripted event handler
227*************************************************************************/
228Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
229    const String& event_name, const String& subscriber_name)
230{
231    const String& err_str = getActivePCallErrorHandlerString();
232    const int err_ref     = getActivePCallErrorHandlerReference();
233
234    // do the real subscription
235    LuaFunctor functor((err_ref == LUA_NOREF) ?
236                LuaFunctor(d_state, subscriber_name, LUA_NOREF, err_str) :
237                LuaFunctor(d_state, subscriber_name, LUA_NOREF, err_ref));
238    Event::Connection con =
239        target->subscribeEvent(event_name, Event::Subscriber(functor));
240
241    // make sure we don't release the reference we just made when 'functor' is
242    // destroyed as it goes out of scope.
243    functor.index = LUA_NOREF;
244    functor.d_errFuncIndex = LUA_NOREF;
245
246    // return the event connection
247    return con;
248}
249
250
251/*************************************************************************
252        Subscribe to a scripted event handler
253*************************************************************************/
254Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
255    const String& event_name, Event::Group group, const String& subscriber_name)
256{
257    const String& err_str = getActivePCallErrorHandlerString();
258    const int err_ref     = getActivePCallErrorHandlerReference();
259
260    // do the real subscription
261    LuaFunctor functor((err_ref == LUA_NOREF) ?
262                LuaFunctor(d_state, subscriber_name, LUA_NOREF, err_str) :
263                LuaFunctor(d_state, subscriber_name, LUA_NOREF, err_ref));
264    Event::Connection con =
265        target->subscribeEvent(event_name, group, Event::Subscriber(functor));
266
267    // make sure we don't release the reference we just made when 'functor' is
268    // destroyed as it goes out of scope.
269    functor.index = LUA_NOREF;
270    functor.d_errFuncIndex = LUA_NOREF;
271
272    // return the event connection
273    return con;
274}
275
276//----------------------------------------------------------------------------//
277void LuaScriptModule::setDefaultPCallErrorHandler(
278    const String& error_handler_function)
279{
280    unrefErrorFunc();
281
282    d_errFuncName = error_handler_function;
283    d_errFuncIndex = LUA_NOREF;
284}
285
286//----------------------------------------------------------------------------//
287void LuaScriptModule::setDefaultPCallErrorHandler(int function_reference)
288{
289    unrefErrorFunc();
290
291    d_errFuncIndex = function_reference;
292    d_errFuncName.clear();
293}
294
295//----------------------------------------------------------------------------//
296const String& LuaScriptModule::getActivePCallErrorHandlerString() const
297{
298    if ((d_activeErrFuncIndex == LUA_NOREF) && d_activeErrFuncName.empty())
299        return d_errFuncName;
300    else
301        return d_activeErrFuncName;
302}
303
304//----------------------------------------------------------------------------//
305int LuaScriptModule::getActivePCallErrorHandlerReference() const
306{
307    if ((d_activeErrFuncIndex == LUA_NOREF) && d_activeErrFuncName.empty())
308        return d_errFuncIndex;
309    else
310        return d_activeErrFuncIndex;
311}
312
313//----------------------------------------------------------------------------//
314int LuaScriptModule::initErrorHandlerFunc()
315{
316    d_activeErrFuncName = d_errFuncName;
317
318    // should we create a registry reference for named function
319    if ((d_errFuncIndex == LUA_NOREF) && !d_errFuncName.empty())
320    {
321        int top = lua_gettop(d_state);
322
323        LuaFunctor::pushNamedFunction(d_state, d_errFuncName);
324        // reference function
325        d_errFuncIndex = luaL_ref(d_state, LUA_REGISTRYINDEX);
326
327        lua_settop(d_state, top);
328    }
329
330    // init handler via function index in registry
331    return initErrorHandlerFunc(d_errFuncIndex);
332}
333
334//----------------------------------------------------------------------------//
335int LuaScriptModule::initErrorHandlerFunc(const String func_name)
336{
337    d_activeErrFuncName = func_name;
338
339    // string has some text in it?
340    if (!func_name.empty())
341    {
342        LuaFunctor::pushNamedFunction(d_state, func_name);
343        return lua_gettop(d_state);
344    }
345
346    // use no error handler function.
347    return 0;
348}
349
350//----------------------------------------------------------------------------//
351int LuaScriptModule::initErrorHandlerFunc(int func)
352{
353    d_activeErrFuncIndex = func;
354
355    // check if we have a valid registry index for the error handler function
356    if (func != LUA_NOREF)
357    {
358        lua_rawgeti(d_state, LUA_REGISTRYINDEX, func);
359        return lua_gettop(d_state);
360    }
361
362    // use no error handler function.
363    return 0;
364}
365
366//----------------------------------------------------------------------------//
367void LuaScriptModule::cleanupErrorHandlerFunc()
368{
369    d_activeErrFuncIndex = LUA_NOREF;
370    d_activeErrFuncName.clear();
371}
372
373//----------------------------------------------------------------------------//
374void LuaScriptModule::unrefErrorFunc()
375{
376    if ((d_errFuncIndex != LUA_NOREF) && !d_errFuncName.empty())
377    {
378        luaL_unref(d_state, LUA_REGISTRYINDEX, d_errFuncIndex);
379        d_errFuncIndex = LUA_NOREF;
380    }
381}
382
383//----------------------------------------------------------------------------//
384void LuaScriptModule::executeScriptFile(const String& filename,
385    const String& resourceGroup, const String& error_handler)
386{
387    int top = lua_gettop(d_state);
388
389    executeScriptFile_impl(filename, resourceGroup,
390                           initErrorHandlerFunc(error_handler),
391                           top);
392    cleanupErrorHandlerFunc();
393}
394
395//----------------------------------------------------------------------------//
396void LuaScriptModule::executeScriptFile(const String& filename,
397    const String& resourceGroup, const int error_handler)
398{
399    int top = lua_gettop(d_state);
400
401    executeScriptFile_impl(filename, resourceGroup,
402                           initErrorHandlerFunc(error_handler),
403                           top);
404    cleanupErrorHandlerFunc();
405}
406
407//----------------------------------------------------------------------------//
408int LuaScriptModule::executeScriptGlobal(const String& function_name,
409    const String& error_handler)
410{
411    int top = lua_gettop(d_state);
412    int r = executeScriptGlobal_impl(function_name,
413                                     initErrorHandlerFunc(error_handler),
414                                     top);
415    cleanupErrorHandlerFunc();
416
417    return r;
418}
419
420//----------------------------------------------------------------------------//
421int LuaScriptModule::executeScriptGlobal(const String& function_name,
422    const int error_handler)
423{
424    int top = lua_gettop(d_state);
425    int r = executeScriptGlobal_impl(function_name,
426                                     initErrorHandlerFunc(error_handler),
427                                     top);
428    cleanupErrorHandlerFunc();
429
430    return r;
431}
432
433//----------------------------------------------------------------------------//
434bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name,
435    const EventArgs& e, const String& error_handler)
436{
437    int top = lua_gettop(d_state);
438
439    bool r =executeScriptedEventHandler_impl(handler_name, e,
440                                             initErrorHandlerFunc(error_handler),
441                                             top);
442    cleanupErrorHandlerFunc();
443
444    return r;
445}
446
447//----------------------------------------------------------------------------//
448bool LuaScriptModule::executeScriptedEventHandler(const String& handler_name,
449    const EventArgs& e, const int error_handler)
450{
451    int top = lua_gettop(d_state);
452    bool r = executeScriptedEventHandler_impl(handler_name, e,
453                                              initErrorHandlerFunc(error_handler),
454                                              top);
455    cleanupErrorHandlerFunc();
456
457    return r;
458}
459
460//----------------------------------------------------------------------------//
461void LuaScriptModule::executeString(const String& str,
462    const String& error_handler)
463{
464    int top = lua_gettop(d_state);
465
466    executeString_impl(str, initErrorHandlerFunc(error_handler), top);
467    cleanupErrorHandlerFunc();
468}
469
470//----------------------------------------------------------------------------//
471void LuaScriptModule::executeString(const String& str, const int error_handler)
472{
473    int top = lua_gettop(d_state);
474
475    executeString_impl(str, initErrorHandlerFunc(error_handler), top);
476    cleanupErrorHandlerFunc();
477}
478
479//----------------------------------------------------------------------------//
480Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
481    const String& event_name, const String& subscriber_name,
482    const String& error_handler)
483{
484    // do the real subscription
485    LuaFunctor functor(d_state, subscriber_name, LUA_NOREF, error_handler);
486    Event::Connection con =
487        target->subscribeEvent(event_name, Event::Subscriber(functor));
488
489    // make sure we don't release the references we just made when 'functor' is
490    // destroyed as it goes out of scope.
491    functor.index = LUA_NOREF;
492    functor.d_errFuncIndex = LUA_NOREF;
493
494    // return the event connection
495    return con;
496}
497
498//----------------------------------------------------------------------------//
499Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
500    const String& event_name, const String& subscriber_name,
501    const int error_handler)
502{
503    // do the real subscription
504    LuaFunctor functor(d_state, subscriber_name, LUA_NOREF, error_handler);
505    Event::Connection con =
506        target->subscribeEvent(event_name, Event::Subscriber(functor));
507
508    // make sure we don't release the references we just made when 'functor' is
509    // destroyed as it goes out of scope.
510    functor.index = LUA_NOREF;
511    functor.d_errFuncIndex = LUA_NOREF;
512
513    // return the event connection
514    return con;
515}
516
517
518//----------------------------------------------------------------------------//
519Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
520    const String& event_name, Event::Group group, const String& subscriber_name,
521    const String& error_handler)
522{
523    // do the real subscription
524    LuaFunctor functor(d_state, subscriber_name, LUA_NOREF, error_handler);
525    Event::Connection con =
526        target->subscribeEvent(event_name, group, Event::Subscriber(functor));
527
528    // make sure we don't release the references we just made when 'functor' is
529    // destroyed as it goes out of scope.
530    functor.index = LUA_NOREF;
531    functor.d_errFuncIndex = LUA_NOREF;
532
533    // return the event connection
534    return con;
535}
536
537//----------------------------------------------------------------------------//
538Event::Connection LuaScriptModule::subscribeEvent(EventSet* target,
539    const String& event_name, Event::Group group, const String& subscriber_name,
540    const int error_handler)
541{
542    // do the real subscription
543    LuaFunctor functor(d_state, subscriber_name, LUA_NOREF, error_handler);
544    Event::Connection con =
545        target->subscribeEvent(event_name, group, Event::Subscriber(functor));
546
547    // make sure we don't release the references we just made when 'functor' is
548    // destroyed as it goes out of scope.
549    functor.index = LUA_NOREF;
550    functor.d_errFuncIndex = LUA_NOREF;
551
552    // return the event connection
553    return con;
554}
555
556//----------------------------------------------------------------------------//
557void LuaScriptModule::executeScriptFile_impl(const String& filename,
558    const String& resourceGroup, const int err_idx, const int top)
559{
560    // load file
561    RawDataContainer raw;
562    System::getSingleton().getResourceProvider()->loadRawDataContainer(filename,
563        raw, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup);
564
565    // load code into lua
566    int loaderr = luaL_loadbuffer(d_state,
567                                  reinterpret_cast<char*>(raw.getDataPtr()),
568                                  raw.getSize(), filename.c_str());
569
570    System::getSingleton().getResourceProvider()->unloadRawDataContainer( raw );
571
572    if (loaderr)
573    {
574        String errMsg = lua_tostring(d_state,-1);
575        lua_settop(d_state,top);
576        throw ScriptException("Unable to execute Lua script file: '" +
577                              filename + "'\n\n" + errMsg + "\n");
578    }
579
580    // call it
581    if (lua_pcall(d_state, 0, 0, err_idx))
582    {
583        String errMsg = lua_tostring(d_state,-1);
584        lua_settop(d_state,top);
585        throw ScriptException("Unable to execute Lua script file: '" +
586                              filename + "'\n\n" + errMsg + "\n");
587    }
588
589    lua_settop(d_state,top); // just in case :P
590}
591
592//----------------------------------------------------------------------------//
593int LuaScriptModule::executeScriptGlobal_impl(const String& function_name,
594    const int err_idx, const int top)
595{
596    // get the function from lua
597    lua_getglobal(d_state, function_name.c_str());
598
599    // is it a function
600    if (!lua_isfunction(d_state,-1))
601    {
602        lua_settop(d_state,top);
603        throw ScriptException("Unable to get Lua global: '" + function_name +
604                              "' as name not represent a global Lua function" );
605    }
606
607    // call it
608    int error = lua_pcall(d_state, 0, 1, err_idx);
609
610    // handle errors
611    if (error)
612    {
613        String errMsg = lua_tostring(d_state,-1);
614        lua_settop(d_state,top);
615        throw ScriptException("Unable to evaluate Lua global: '" + 
616                              function_name + "\n\n" + errMsg + "\n");
617    }
618
619    // get return value
620    if (!lua_isnumber(d_state,-1))
621    {
622        // log that return value is invalid. return -1 and move on.
623        lua_settop(d_state,top);
624        ScriptException("Unable to get Lua global : '" + function_name +
625                        "' return value as it's not a number");
626        return -1;
627    }
628
629    int ret = static_cast<int>(lua_tonumber(d_state,-1));
630    lua_settop(d_state,top);
631
632    // return it
633    return ret;
634}
635
636//----------------------------------------------------------------------------//
637bool LuaScriptModule::executeScriptedEventHandler_impl(
638    const String& handler_name, const EventArgs& e, const int err_idx,
639    const int top)
640{
641    LuaFunctor::pushNamedFunction(d_state, handler_name);
642
643    ScriptWindowHelper* helper = 0;
644    // If this is an event that was triggered by a window then make a "this"
645    // pointer to the window for the script.
646    if(e.d_hasWindow)
647    {
648        WindowEventArgs& we = (WindowEventArgs&)e;
649        helper = new ScriptWindowHelper(we.window);
650        lua_pushlightuserdata(d_state,(void*)helper);
651        lua_setglobal(d_state,"this");
652    }
653
654    // push EventArgs as the first parameter
655    tolua_pushusertype(d_state, (void*)&e, "const CEGUI::EventArgs");
656
657    // call it
658    int error = lua_pcall(d_state, 1, 1, err_idx);
659
660    // handle errors
661    if (error)
662    {
663        String errStr(lua_tostring(d_state,-1));
664        lua_settop(d_state,top);
665        //cleanup the helper object if any
666        if(helper)
667        {
668            delete helper;
669            helper = 0;
670        }
671
672        throw ScriptException("Unable to evaluate the Lua event handler: '" +
673                              handler_name + "'\n\n" + errStr + "\n");
674    }
675
676    // retrieve result
677    bool ret = lua_isboolean(d_state, -1) ? lua_toboolean(d_state, -1 ) : true;
678    lua_settop(d_state,top);
679
680    if(helper)
681    {
682        delete helper;
683        helper = 0;
684    }
685
686    return ret;
687}
688
689//----------------------------------------------------------------------------//
690void LuaScriptModule::executeString_impl(const String& str, const int err_idx,
691    const int top)
692{
693    // load code into lua and call it
694    int error = luaL_loadbuffer(d_state, str.c_str(), str.length(), str.c_str()) ||
695                lua_pcall(d_state, 0, 0, err_idx);
696
697    // handle errors
698    if (error)
699    {
700        String errMsg = lua_tostring(d_state,-1);
701        lua_settop(d_state,top);
702        throw ScriptException("Unable to execute Lua script string: '" +
703                              str + "'\n\n" + errMsg + "\n");
704    }
705
706    lua_settop(d_state,top);
707}
708
709//----------------------------------------------------------------------------//
710
711
712} // namespace CEGUI
Note: See TracBrowser for help on using the repository browser.