1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "luainterface.h"
24#include "luaobject.h"
25
26#include <framework/core/resourcemanager.h>
27#include <framework/util/stats.h>
28extern "C" {
29#if defined(_MSC_VER) || defined(ANDROID)
30#include <luajit/lua.h>
31#include <luajit/lualib.h>
32#include <luajit/lauxlib.h>
33#else
34#include <lua.h>
35#include <lualib.h>
36#include <lauxlib.h>
37#endif
38}
39
40#include "lbitlib.h"
41
42LuaInterface g_lua;
43
44LuaInterface::LuaInterface()
45{
46 L = nullptr;
47 m_cppCallbackDepth = 0;
48 m_weakTableRef = 0;
49 m_totalObjRefs = 0;
50 m_totalFuncRefs = 0;
51}
52
53LuaInterface::~LuaInterface()
54{
55}
56
57void LuaInterface::init()
58{
59 createLuaState();
60
61 // store global environment reference
62 pushThread();
63 getEnv();
64 m_globalEnv = ref();
65 pop();
66
67 // check if demangle_class is working as expected
68 VALIDATE(stdext::demangle_class<LuaObject>() == "LuaObject");
69
70 // register LuaObject, the base of all other objects
71 registerClass<LuaObject>();
72 bindClassMemberFunction<LuaObject>("getUseCount", &LuaObject::getUseCount);
73 bindClassMemberFunction<LuaObject>("getClassName", &LuaObject::getClassName);
74
75 registerClassMemberFunction<LuaObject>("getFieldsTable", (LuaCppFunction) ([](LuaInterface* lua) -> int {
76 LuaObjectPtr obj = g_lua.popObject();
77 obj->luaGetFieldsTable();
78 return 1;
79 }));
80}
81
82void LuaInterface::terminate()
83{
84 // close lua state, it will release all objects
85 closeLuaState();
86 VALIDATE(m_totalFuncRefs == 0);
87 VALIDATE(m_totalObjRefs == 0);
88}
89
90void LuaInterface::registerFunctions()
91{
92}
93
94void LuaInterface::registerSingletonClass(const std::string& className)
95{
96 newTable();
97 pushValue();
98 setGlobal(className);
99 pop();
100}
101
102void LuaInterface::registerClass(const std::string& className, const std::string& baseClass)
103{
104 // creates the class table (that it's also the class methods table)
105 newTable();
106 pushValue();
107 setGlobal(className);
108 const int klass = getTop();
109
110 // creates the class fieldmethods table
111 newTable();
112 pushValue();
113 setGlobal(className + "_fieldmethods");
114 int klass_fieldmethods = getTop();
115
116 // creates the class metatable
117 newTable();
118 pushValue();
119 setGlobal(className + "_mt");
120 int klass_mt = getTop();
121
122 // set metatable metamethods
123 pushCppFunction(&LuaInterface::luaObjectGetEvent);
124 setField("__index", klass_mt);
125 pushCppFunction(&LuaInterface::luaObjectSetEvent);
126 setField("__newindex", klass_mt);
127 pushCppFunction(&LuaInterface::luaObjectEqualEvent);
128 setField("__eq", klass_mt);
129 pushCppFunction(&LuaInterface::luaObjectCollectEvent);
130 setField("__gc", klass_mt);
131
132 // set some fields that will be used later in metatable
133 pushValue(klass);
134 setField("methods", klass_mt);
135 pushValue(klass_fieldmethods);
136 setField("fieldmethods", klass_mt);
137
138 // redirect methods and fieldmethods to the base class ones
139 if(!className.empty() && className != "LuaObject") {
140 // the following code is what create classes hierarchy for lua, by reproducing:
141 // DerivedClass = { __index = BaseClass }
142 // DerivedClass_fieldmethods = { __index = BaseClass_methods }
143
144 // redirect the class methods to the base methods
145 pushValue(klass);
146 newTable();
147 getGlobal(baseClass);
148 setField("__index");
149 setMetatable();
150 pop();
151
152 // redirect the class fieldmethods to the base fieldmethods
153 pushValue(klass_fieldmethods);
154 newTable();
155 getGlobal(baseClass + "_fieldmethods");
156 setField("__index");
157 setMetatable();
158 pop();
159 }
160
161 // pops klass, klass_mt, klass_fieldmethods
162 pop(3);
163}
164
165void LuaInterface::registerClassStaticFunction(const std::string& className,
166 const std::string& functionName,
167 const LuaCppFunction& function)
168{
169 registerClassMemberFunction(className, functionName, function);
170}
171
172void LuaInterface::registerClassMemberFunction(const std::string& className,
173 const std::string& functionName,
174 const LuaCppFunction& function)
175{
176 getGlobal(className);
177 pushCppFunction(function);
178 setField(functionName);
179 pop();
180}
181
182void LuaInterface::registerClassMemberField(const std::string& className,
183 const std::string& field,
184 const LuaCppFunction& getFunction,
185 const LuaCppFunction& setFunction)
186{
187 getGlobal(className + "_fieldmethods");
188
189 if(getFunction) {
190 pushCppFunction(getFunction);
191 setField(stdext::format("get_%s", field));
192 }
193
194 if(setFunction) {
195 pushCppFunction(setFunction);
196 setField(stdext::format("set_%s", field));
197 }
198
199 pop();
200}
201
202void LuaInterface::registerGlobalFunction(const std::string& functionName, const LuaCppFunction& function)
203{
204 pushCppFunction(function);
205 setGlobal(functionName);
206}
207
208int LuaInterface::luaObjectGetEvent(LuaInterface* lua)
209{
210 // stack: obj, key
211 LuaObjectPtr obj = lua->toObject(-2);
212 std::string key = lua->toString(-1);
213 VALIDATE(obj);
214
215 lua->remove(-1); // removes key
216
217 // if a get method for this key exists, calls it
218 lua->getMetatable(); // pushes obj metatable
219 lua->getField("fieldmethods"); // push obj fieldmethods
220 lua->remove(-2); // removes obj metatable
221 lua->getField(std::string("get_") + key); // pushes get method
222 lua->remove(-2); // remove obj fieldmethods
223 if(!lua->isNil()) { // is the get method not nil?
224 lua->insert(-2); // moves obj to the top
225 lua->signalCall(1, 1); // calls get method, arguments: obj
226 return 1;
227 }
228 lua->pop(); // pops the nil get method
229
230 // if the field for this key exists, returns it
231 obj->luaGetField(key);
232 if(!lua->isNil()) {
233 lua->remove(-2); // removes the obj
234 // field value is on the stack
235 return 1;
236 }
237 lua->pop(); // pops the nil field
238
239 // pushes the method assigned by this key
240 lua->getMetatable(); // pushes obj metatable
241 lua->getField("methods"); // push obj methods
242 lua->remove(-2); // removes obj metatable
243 lua->getField(key); // pushes obj method
244 lua->remove(-2); // remove obj methods
245 lua->remove(-2); // removes obj
246
247 // the result value is on the stack
248 return 1;
249}
250
251int LuaInterface::luaObjectSetEvent(LuaInterface* lua)
252{
253 // stack: obj, key, value
254 LuaObjectPtr obj = lua->toObject(-3);
255 std::string key = lua->toString(-2);
256 VALIDATE(obj);
257
258 lua->remove(-2); // removes key
259 lua->insert(-2); // moves obj to the top
260
261 // check if a set method for this field exists and call it
262 lua->getMetatable(); // pushes obj metatable
263 lua->getField("fieldmethods"); // push obj fieldmethods
264 lua->remove(-2); // removes obj metatable
265 lua->getField(std::string("set_") + key); // pushes set method
266 lua->remove(-2); // remove obj fieldmethods
267 if(!lua->isNil()) { // is the set method not nil?
268 lua->insert(-3); // moves func to -3
269 lua->insert(-2); // moves obj to -2, and value to -1
270 lua->signalCall(2, 0); // calls set method, arguments: obj, value
271 return 0;
272 }
273 lua->pop(); // pops the nil set method
274
275 // no set method exists, then treats as an field and set it
276 lua->pop(); // pops the object
277 obj->luaSetField(key); // sets the obj field
278 return 0;
279}
280
281int LuaInterface::luaObjectEqualEvent(LuaInterface* lua)
282{
283 // stack: obj1, obj2
284 bool ret = false;
285
286 // check if obj1 == obj2
287 if(lua->isUserdata(-1) && lua->isUserdata(-2)) {
288 LuaObjectPtr* objPtr2 = static_cast<LuaObjectPtr*>(lua->popUserdata());
289 LuaObjectPtr* objPtr1 = static_cast<LuaObjectPtr*>(lua->popUserdata());
290 VALIDATE(objPtr1 && objPtr2);
291 if(*objPtr1 == *objPtr2)
292 ret = true;
293 } else
294 lua->pop(2);
295
296 lua->pushBoolean(ret);
297 return 1;
298}
299
300int LuaInterface::luaObjectCollectEvent(LuaInterface* lua)
301{
302 // gets object pointer
303 auto objPtr = static_cast<LuaObjectPtr*>(lua->popUserdata());
304 VALIDATE(objPtr);
305
306 // resets pointer to decrease object use count
307 objPtr->reset();
308 g_lua.m_totalObjRefs--;
309 return 0;
310}
311
312
313///////////////////////////////////////////////////////////////////////////////
314
315
316bool LuaInterface::safeRunScript(const std::string& fileName)
317{
318 try {
319 runScript(fileName);
320 return true;
321 } catch(stdext::exception& e) {
322 g_logger.error(stdext::format("Failed to load script '%s': %s", fileName, e.what()));
323 return false;
324 }
325}
326
327void LuaInterface::runScript(const std::string& fileName)
328{
329 loadScript(fileName);
330 safeCall(0, 0);
331}
332
333void LuaInterface::runBuffer(const std::string& buffer, const std::string& source)
334{
335 loadBuffer(buffer, source);
336 safeCall(0, 0);
337}
338
339void LuaInterface::loadScript(const std::string& fileName)
340{
341 // resolve file full path
342 std::string filePath = fileName;
343 if(!stdext::starts_with(fileName, "/"))
344 filePath = getCurrentSourcePath() + "/" + filePath;
345
346 filePath = g_resources.guessFilePath(filePath, "lua");
347
348 std::string buffer = g_resources.readFileContents(filePath);
349 std::string source = std::string("@") + filePath;
350 loadBuffer(buffer, source);
351}
352
353void LuaInterface::loadFunction(const std::string& buffer, const std::string& source)
354{
355 if(buffer.empty()) {
356 pushNil();
357 return;
358 }
359
360 std::string buf;
361 if(stdext::starts_with(buffer, "function"))
362 buf = stdext::format("__func = %s", buffer);
363 else
364 buf = stdext::format("__func = function(self)\n%s\nend", buffer);
365
366 loadBuffer(buf, source);
367 safeCall();
368
369 // get the function
370 getGlobal("__func");
371
372 // reset the global __func
373 pushNil();
374 setGlobal("__func");
375}
376
377void LuaInterface::evaluateExpression(const std::string& expression, const std::string& source)
378{
379 // evaluates the expression
380 if(!expression.empty()) {
381 std::string buffer = stdext::format("__exp = (%s)", expression);
382 loadBuffer(buffer, source);
383 safeCall();
384
385 // gets the expression result
386 getGlobal("__exp");
387
388 // resets global __exp
389 pushNil();
390 setGlobal("__exp");
391 } else
392 pushNil();
393}
394
395std::string LuaInterface::traceback(const std::string& errorMessage, int level)
396{
397 // gets debug.traceback
398 getGlobal("debug");
399 getField("traceback");
400 remove(-2); // remove debug
401
402 // calls debug.traceback(errorMessage, level)
403 pushString(errorMessage);
404 pushInteger(level);
405 call(2,1);
406
407 // returns the traceback message
408 return popString();
409}
410
411void LuaInterface::throwError(const std::string& message)
412{
413 if(isInCppCallback()) {
414 pushString(message);
415 error();
416 } else
417 throw stdext::exception(message);
418}
419
420std::string LuaInterface::getCurrentSourcePath(int level)
421{
422 std::string path;
423 if(!L)
424 return path;
425
426 // check all stack functions for script source path
427 while(true) {
428 getStackFunction(level); // pushes stack function
429
430 // only lua functions is wanted, because only them have a working directory
431 if(isLuaFunction()) {
432 path = functionSourcePath();
433 break;
434 } else if(isNil()) {
435 pop();
436 break;
437 } else
438 pop();
439
440 // next level
441 level++;
442 }
443
444 return path;
445}
446
447std::string LuaInterface::getCurrentFunction(int level)
448{
449 std::string path = "unknown";
450 if(!L)
451 return path;
452
453 // check all stack functions for script source path
454 while(true) {
455 getStackFunction(level); // pushes stack function
456
457 // only lua functions is wanted, because only them have a working directory
458 if(isLuaFunction()) {
459 path = functionSource();
460 break;
461 } else if(isNil()) {
462 pop();
463 break;
464 } else
465 pop();
466
467 // next level
468 level++;
469 }
470
471 return path;
472}
473
474int LuaInterface::safeCall(int numArgs, int numRets, const std::shared_ptr<std::string>& error)
475{
476 VALIDATE(hasIndex(-numArgs-1));
477
478 // saves the current stack size for calculating the number of results later
479 int previousStackSize = stackSize();
480
481 // pushes error function
482 int errorFuncIndex = previousStackSize - numArgs;
483 pushCFunction(&LuaInterface::luaErrorHandler);
484 insert(errorFuncIndex);
485
486 // calls the function in protected mode (means errors will be caught)
487 int ret = pcall(numArgs, LUA_MULTRET, errorFuncIndex);
488
489 remove(errorFuncIndex); // remove error func
490
491 // if there was an error throw an exception
492 if (ret != 0) {
493 if (error) {
494 error->assign(popString());
495 clearStack();
496 return 0;
497 } else {
498 std::string errorMsg = popString();
499 g_logger.error(stdext::format("Lua error: %s", errorMsg));
500 throw LuaException(errorMsg);
501 }
502 }
503 int rets = (stackSize() + numArgs + 1) - previousStackSize;
504 while(numRets != -1 && rets != numRets) {
505 if(rets < numRets) {
506 pushNil();
507 rets++;
508 } else {
509 pop();
510 rets--;
511 }
512 }
513
514 // returns the number of results
515 return rets;
516}
517
518int LuaInterface::signalCall(int numArgs, int numRets)
519{
520 int rets = 0;
521 int funcIndex = -numArgs-1;
522
523 try {
524 // must be a function
525 if(isFunction(funcIndex)) {
526 static std::shared_ptr<std::string> error = std::make_shared<std::string>();
527 rets = safeCall(numArgs, -1, error);
528 if (!error->empty()) {
529 g_logger.error(stdext::format("protected lua call failed: %s", *error));
530 error->clear();
531 return rets;
532 }
533
534 if(numRets != -1) {
535 if(rets != numRets)
536 throw LuaException("function call didn't return the expected number of results", 0);
537 }
538 }
539 // can also calls table of functions
540 else if(isTable(funcIndex)) {
541 // loop through table values
542 pushNil();
543 bool done = false;
544 while(next(funcIndex-1)) {
545 if(isFunction()) {
546 // repush arguments
547 for(int i=0;i<numArgs;++i)
548 pushValue(-numArgs-2);
549
550 static std::shared_ptr<std::string> error = std::make_shared<std::string>();
551 int rets = safeCall(numArgs, -1, error);
552 if (!error->empty()) {
553 g_logger.error(stdext::format("protected lua call failed: %s", *error));
554 error->clear();
555 return rets;
556 }
557 if(rets == 1) {
558 done = popBoolean();
559 if(done) {
560 pop();
561 break;
562 }
563 } else if(rets != 0)
564 throw LuaException("function call didn't return the expected number of results", 0);
565 } else {
566 throw LuaException("attempt to call a non function", 0);
567 }
568 }
569 pop(numArgs + 1); // pops the table of function and arguments
570
571 if(numRets == 1 || numRets == -1) {
572 rets = 1;
573 pushBoolean(done);
574 }
575 }
576 // nil values are ignored
577 else if(isNil(funcIndex)) {
578 pop(numArgs + 1); // pops the function and arguments
579 }
580 // if not nil, warn
581 else {
582 throw LuaException("attempt to call a non function value", 0);
583 }
584 } catch(stdext::exception& e) {
585 g_logger.error(stdext::format("protected lua call failed: %s", e.what()));
586 }
587
588 // pushes nil values if needed
589 while(numRets != -1 && rets < numRets) {
590 pushNil();
591 rets++;
592 }
593
594 // returns the number of results on the stack
595 return rets;
596}
597
598int LuaInterface::newSandboxEnv()
599{
600 newTable(); // pushes the new environment table
601 newTable(); // pushes the new environment metatable
602 getRef(getGlobalEnvironment()); // pushes the global environment
603 setField("__index"); // sets metatable __index to the global environment
604 setMetatable(); // assigns environment metatable
605 return ref(); // return a reference to the environment table
606}
607
608///////////////////////////////////////////////////////////////////////////////
609// lua C functions
610
611int LuaInterface::luaScriptLoader(lua_State* L)
612{
613 // loads the script as a function
614 std::string fileName = g_lua.popString();
615
616 try {
617 g_lua.loadScript(fileName);
618 return 1;
619 } catch(stdext::exception& e) {
620 g_lua.pushString(std::string("\n\t") + e.what());
621 return 1;
622 }
623}
624
625int LuaInterface::lua_dofile(lua_State* L)
626{
627 std::string file = g_lua.popString();
628
629 try {
630 g_lua.loadScript(file);
631 g_lua.call(0, LUA_MULTRET);
632 return g_lua.stackSize();
633 } catch(stdext::exception& e) {
634 g_lua.pushString(e.what());
635 g_lua.error();
636 return 0;
637 }
638}
639
640int LuaInterface::lua_dofiles(lua_State* L)
641{
642 std::string contains = "";
643 if(g_lua.getTop() > 2) {
644 contains = g_lua.popString();
645 }
646
647 bool recursive = false;
648 if(g_lua.getTop() > 1) {
649 recursive = g_lua.popBoolean();
650 }
651
652 std::string directory = g_lua.popString();
653 g_lua.loadFiles(directory, recursive, contains);
654
655 return 0;
656}
657
658int LuaInterface::lua_loadfile(lua_State* L)
659{
660 std::string fileName = g_lua.popString();
661
662 try {
663 g_lua.loadScript(fileName);
664 return 1;
665 } catch(stdext::exception& e) {
666 g_lua.pushNil();
667 g_lua.pushString(e.what());
668 g_lua.error();
669 return 2;
670 }
671}
672
673int LuaInterface::luaErrorHandler(lua_State* L)
674{
675 // pops the error message
676 auto error = g_lua.popString();
677
678 // prevents repeated tracebacks
679 if(error.find("stack traceback:") == std::string::npos)
680 error = g_lua.traceback(error, 1);
681
682 // pushes the new error message with traceback information
683 g_lua.pushString(error);
684 return 1;
685}
686
687int LuaInterface::luaCppFunctionCallback(lua_State* L)
688{
689 // retrieves function pointer from userdata
690 auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUpvalueUserdata());
691 VALIDATE(funcPtr);
692
693 int numRets = 0;
694
695 // enable only for tests, it has high cpu usage
696 // AutoStat s(STATS_LUACALLBACK, g_lua.getSource(1));
697
698 // do the call
699 try {
700 g_lua.m_cppCallbackDepth++;
701 numRets = (*(funcPtr->get()))(&g_lua);
702 g_lua.m_cppCallbackDepth--;
703#ifndef NDEBUG
704 if (numRets != g_lua.stackSize()) {
705 throw stdext::exception(stdext::format("LuaInterface::luaCppFunctionCallback, numRets != g_lua.stackSize() (%i != %i)", numRets, g_lua.stackSize()));
706 }
707#endif
708 } catch(stdext::exception& e) {
709 // cleanup stack
710 while(g_lua.stackSize() > 0)
711 g_lua.pop();
712 numRets = 0;
713 g_lua.pushString(stdext::format("C++ call failed: %s", g_lua.traceback(e.what())));
714 g_lua.error();
715 }
716 catch (...) {
717 g_logger.fatal(stdext::format("Critical lua error!\nC++ call failed:\n%s|%s", g_lua.getCurrentFunction(), g_lua.traceback("fatal error")));
718 }
719
720 return numRets;
721}
722
723int LuaInterface::luaCollectCppFunction(lua_State* L)
724{
725 auto funcPtr = static_cast<LuaCppFunctionPtr*>(g_lua.popUserdata());
726 VALIDATE(funcPtr);
727 funcPtr->reset();
728 g_lua.m_totalFuncRefs--;
729 return 0;
730}
731
732
733///////////////////////////////////////////////////////////////////////////////
734// from here all next functions are interfaces for the Lua API
735
736void LuaInterface::createLuaState()
737{
738 // creates lua state
739 L = luaL_newstate();
740 if(!L)
741 g_logger.fatal("Unable to create lua state");
742
743 // load lua standard libraries
744 luaL_openlibs(L);
745
746 // load bit32 lib for bitwise operations
747 luaopen_bit32(L);
748
749 // creates weak table
750 newTable();
751 newTable();
752 pushString("v");
753 setField("__mode");
754 setMetatable();
755 m_weakTableRef = ref();
756
757 // installs script loader
758 getGlobal("package");
759 getField("loaders");
760 pushCFunction(&LuaInterface::luaScriptLoader);
761 rawSeti(5);
762 pop(2);
763
764 // replace dofile
765 pushCFunction(&LuaInterface::lua_dofile);
766 setGlobal("dofile");
767
768 // dofiles
769 pushCFunction(&LuaInterface::lua_dofiles);
770 setGlobal("dofiles");
771
772 // replace loadfile
773 pushCFunction(&LuaInterface::lua_loadfile);
774 setGlobal("loadfile");
775
776#ifdef FREE_VERSION
777 pushCFunction(nullptr);
778 setGlobal("io");
779#endif
780}
781
782void LuaInterface::closeLuaState()
783{
784 if(L) {
785 // close lua, it also collects
786 lua_close(L);
787 L = NULL;
788 }
789}
790
791void LuaInterface::collectGarbage()
792{
793 // prevents recursive collects
794 static bool collecting = false;
795 if(!collecting) {
796 collecting = true;
797
798 // we must collect two times because __gc metamethod
799 // is called on uservalues only the second time
800 for(int i=0;i<2;++i)
801 lua_gc(L, LUA_GCCOLLECT, 0);
802
803 collecting = false;
804 }
805}
806
807void LuaInterface::loadBuffer(const std::string& buffer, const std::string& source)
808{
809#ifdef FREE_VERSION
810 for (auto& c : buffer) {
811 if ((uint8_t)c < 8) {
812 throw LuaException("Compiled lua files are not allowed in free version", 0);
813 }
814 }
815#endif
816 // loads lua buffer
817 int ret = luaL_loadbuffer(L, buffer.c_str(), buffer.length(), source.c_str());
818 if(ret != 0)
819 throw LuaException(popString(), 0);
820}
821
822int dumpWriter(lua_State *L, const void* p, size_t sz, void* ud) {
823 if (!ud)
824 return -1;
825
826 std::string* ret = (std::string*)ud;
827 ret->append((const char*)p, sz);
828 return 0;
829}
830
831std::string LuaInterface::generateByteCode(const std::string& buffer, std::string source)
832{
833 stdext::replace_all(source, "\\", "/");
834 source = std::string("@/") + source;
835
836 std::string ret;
837 int status = luaL_loadbuffer(L, buffer.c_str(), buffer.length(), source.c_str());
838 if (status != 0)
839 return ret;
840
841 lua_dump(L, dumpWriter, &ret);
842 clearStack();
843 return ret;
844}
845
846int LuaInterface::pcall(int numArgs, int numRets, int errorFuncIndex)
847{
848 VALIDATE(hasIndex(-numArgs - 1));
849 return lua_pcall(L, numArgs, numRets, errorFuncIndex);
850}
851
852void LuaInterface::call(int numArgs, int numRets)
853{
854 VALIDATE(hasIndex(-numArgs - 1));
855 lua_call(L, numArgs, numRets);
856}
857
858void LuaInterface::error()
859{
860 VALIDATE(hasIndex(-1));
861 lua_error(L);
862}
863
864int LuaInterface::ref()
865{
866 int ref = luaL_ref(L, LUA_REGISTRYINDEX);
867 VALIDATE(ref != LUA_NOREF);
868 VALIDATE(ref < 2147483647);
869 return ref;
870}
871
872int LuaInterface::weakRef()
873{
874 static int id = 0;
875
876 // generates a new id
877 ++id;
878 if(id == 2147483647)
879 id = 0;
880
881 // gets weak table
882 getRef(m_weakTableRef);
883 insert(-2);
884
885 // sets weak_table[id] = v
886 rawSeti(id);
887
888 // pops weak table
889 pop();
890
891 return id;
892}
893
894void LuaInterface::unref(int ref)
895{
896 if(ref >= 0 && L != NULL)
897 luaL_unref(L, LUA_REGISTRYINDEX, ref);
898}
899
900const char* LuaInterface::typeName(int index)
901{
902 VALIDATE(hasIndex(index));
903 int type = lua_type(L, index);
904 return lua_typename(L, type);
905}
906
907std::string LuaInterface::functionSourcePath()
908{
909 std::string path;
910
911 // gets function source path
912 lua_Debug ar;
913 memset(&ar, 0, sizeof(ar));
914 lua_getinfo(L, ">Sn", &ar);
915 if(ar.source) {
916 // scripts coming from files has source beginning with '@'
917 if(ar.source[0] == '@') {
918 path = ar.source;
919 path = path.substr(1, path.find_last_of("/") - 1);
920 path = path.substr(0, path.find_last_of(":"));
921 }
922 }
923
924 return path;
925}
926
927std::string LuaInterface::functionSource()
928{
929 std::string path;
930
931 // gets function source path
932 lua_Debug ar;
933 memset(&ar, 0, sizeof(ar));
934 lua_getinfo(L, ">Sn", &ar);
935 if(ar.source) {
936 // scripts coming from files has source beginning with '@'
937 //if(ar.source[0] == '@') {
938 path = ar.source;
939 //path = path.substr(1, path.find_last_of("/") - 1);
940 //path = path.substr(0, path.find_last_of(":"));
941 //}
942 }
943
944 return path;
945}
946
947void LuaInterface::insert(int index)
948{
949 VALIDATE(hasIndex(index));
950 lua_insert(L, index);
951}
952
953void LuaInterface::remove(int index)
954{
955 VALIDATE(hasIndex(index));
956 lua_remove(L, index);
957}
958
959bool LuaInterface::next(int index)
960{
961 VALIDATE(hasIndex(index));
962 return lua_next(L, index);
963}
964
965void LuaInterface::getStackFunction(int level)
966{
967 lua_Debug ar;
968 if(lua_getstack(L, level, &ar) == 1)
969 lua_getinfo(L, "f", &ar);
970 else
971 pushNil();
972}
973
974void LuaInterface::getRef(int ref)
975{
976 lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
977}
978
979void LuaInterface::getWeakRef(int weakRef)
980{
981 // pushes weak_table[weakRef]
982 getRef(m_weakTableRef);
983 rawGeti(weakRef);
984 remove(-2);
985}
986
987void LuaInterface::setGlobalEnvironment(int env)
988{
989 pushThread();
990 getRef(env);
991 VALIDATE(isTable());
992 setEnv();
993 pop();
994}
995
996void LuaInterface::setMetatable(int index)
997{
998 VALIDATE(hasIndex(index));
999 lua_setmetatable(L, index);
1000}
1001
1002void LuaInterface::getMetatable(int index)
1003{
1004 VALIDATE(hasIndex(index));
1005 lua_getmetatable(L, index);
1006}
1007
1008void LuaInterface::getField(const char* key, int index)
1009{
1010 VALIDATE(hasIndex(index));
1011 VALIDATE(isUserdata(index) || isTable(index));
1012 lua_getfield(L, index, key);
1013}
1014
1015void LuaInterface::setField(const char* key, int index)
1016{
1017 VALIDATE(hasIndex(index));
1018 VALIDATE(isUserdata(index) || isTable(index));
1019 lua_setfield(L, index, key);
1020}
1021
1022void LuaInterface::getEnv(int index)
1023{
1024 VALIDATE(hasIndex(index));
1025 lua_getfenv(L, index);
1026}
1027
1028void LuaInterface::setEnv(int index)
1029{
1030 VALIDATE(hasIndex(index));
1031 lua_setfenv(L, index);
1032}
1033
1034void LuaInterface::getTable(int index)
1035{
1036 VALIDATE(hasIndex(index));
1037 lua_gettable(L, index);
1038}
1039
1040void LuaInterface::setTable(int index)
1041{
1042 VALIDATE(hasIndex(index));
1043 lua_settable(L, index);
1044}
1045
1046void LuaInterface::clearTable(int index)
1047{
1048 VALIDATE(hasIndex(index) && isTable(index));
1049 pushNil(); // table, nil
1050 while(next(index-1)) { // table, key, value
1051 pop(); // table, key
1052 pushValue(); // table, key, key
1053 if(next(index-2)) { // table, key, nextkey, value
1054 pop(); // table, key, nextkey
1055 insert(-2); // table, nextkey, key
1056 pushNil(); // table, nextkey, key, nil
1057 rawSet(index-3); // table, nextkey
1058 } else { // table, key
1059 pushNil(); // table, key, nil
1060 rawSet(index-2); // table
1061 break;
1062 }
1063 }
1064}
1065
1066void LuaInterface::getGlobal(const std::string& key)
1067{
1068 lua_getglobal(L, key.c_str());
1069}
1070
1071void LuaInterface::getGlobalField(const std::string& globalKey, const std::string& fieldKey)
1072{
1073 getGlobal(globalKey);
1074 if(!isNil()) {
1075 VALIDATE(isTable() || isUserdata());
1076 getField(fieldKey);
1077 remove(-2);
1078 }
1079}
1080
1081void LuaInterface::setGlobal(const std::string& key)
1082{
1083 VALIDATE(hasIndex(-1));
1084 lua_setglobal(L, key.c_str());
1085}
1086
1087void LuaInterface::rawGet(int index)
1088{
1089 VALIDATE(hasIndex(index));
1090 lua_rawget(L, index);
1091}
1092
1093void LuaInterface::rawGeti(int n, int index)
1094{
1095 VALIDATE(hasIndex(index));
1096 lua_rawgeti(L, index, n);
1097}
1098
1099void LuaInterface::rawSet(int index)
1100{
1101 VALIDATE(hasIndex(index));
1102 lua_rawset(L, index);
1103}
1104
1105void LuaInterface::rawSeti(int n, int index)
1106{
1107 VALIDATE(hasIndex(index));
1108 lua_rawseti(L, index, n);
1109}
1110
1111void LuaInterface::newTable()
1112{
1113 lua_newtable(L);
1114}
1115
1116void LuaInterface::createTable(int narr, int nrec)
1117{
1118 lua_createtable(L, narr, nrec);
1119}
1120
1121void* LuaInterface::newUserdata(int size)
1122{
1123 return lua_newuserdata(L, size);
1124}
1125
1126void LuaInterface::pop(int n)
1127{
1128 if(n > 0) {
1129 VALIDATE(hasIndex(-n));
1130 lua_pop(L, n);
1131 }
1132}
1133
1134long LuaInterface::popInteger()
1135{
1136 VALIDATE(hasIndex(-1));
1137 long v = toInteger(-1);
1138 pop();
1139 return v;
1140}
1141
1142double LuaInterface::popNumber()
1143{
1144 VALIDATE(hasIndex(-1));
1145 double v = toNumber(-1);
1146 pop();
1147 return v;
1148}
1149
1150bool LuaInterface::popBoolean()
1151{
1152 VALIDATE(hasIndex(-1));
1153 bool v = toBoolean(-1);
1154 pop();
1155 return v;
1156}
1157
1158std::string LuaInterface::popString()
1159{
1160 VALIDATE(hasIndex(-1));
1161 std::string v = toString(-1);
1162 pop();
1163 return v;
1164}
1165
1166void* LuaInterface::popUserdata()
1167{
1168 VALIDATE(hasIndex(-1));
1169 void* v = toUserdata(-1);
1170 pop();
1171 return v;
1172}
1173
1174LuaObjectPtr LuaInterface::popObject()
1175{
1176 VALIDATE(hasIndex(-1));
1177 LuaObjectPtr v = toObject(-1);
1178 pop();
1179 return v;
1180}
1181
1182void* LuaInterface::popUpvalueUserdata()
1183{
1184 return lua_touserdata(L, lua_upvalueindex(1));
1185}
1186
1187void LuaInterface::pushNil()
1188{
1189 lua_pushnil(L);
1190 checkStack();
1191}
1192
1193void LuaInterface::pushInteger(long v)
1194{
1195 lua_pushinteger(L, v);
1196 checkStack();
1197}
1198
1199void LuaInterface::pushNumber(double v)
1200{
1201 lua_pushnumber(L, v);
1202 checkStack();
1203}
1204
1205void LuaInterface::pushBoolean(bool v)
1206{
1207 lua_pushboolean(L, v);
1208 checkStack();
1209}
1210
1211void LuaInterface::pushCString(const char* v)
1212{
1213 if (!v) {
1214 lua_pushnil(L);
1215 } else {
1216 lua_pushstring(L, v);
1217 }
1218 checkStack();
1219}
1220
1221void LuaInterface::pushString(const std::string& v)
1222{
1223 lua_pushlstring(L, v.c_str(), v.length());
1224 checkStack();
1225}
1226
1227void LuaInterface::pushLightUserdata(void* p)
1228{
1229 lua_pushlightuserdata(L, p);
1230 checkStack();
1231}
1232
1233void LuaInterface::pushThread()
1234{
1235 lua_pushthread(L);
1236 checkStack();
1237}
1238
1239void LuaInterface::pushObject(const LuaObjectPtr& obj)
1240{
1241 // fills a new userdata with a new LuaObjectPtr pointer
1242 new(newUserdata(sizeof(LuaObjectPtr))) LuaObjectPtr(obj);
1243 m_totalObjRefs++;
1244
1245 obj->luaGetMetatable();
1246 if(isNil())
1247 g_logger.fatal(stdext::format("metatable for class '%s' not found, did you bind the C++ class?", obj->getClassName()));
1248 setMetatable();
1249}
1250
1251void LuaInterface::pushCFunction(LuaCFunction func, int n)
1252{
1253 lua_pushcclosure(L, func, n);
1254 checkStack();
1255}
1256
1257void LuaInterface::pushCppFunction(const LuaCppFunction& func)
1258{
1259 // create a pointer to func (this pointer will hold the function existence)
1260 new(newUserdata(sizeof(LuaCppFunctionPtr))) LuaCppFunctionPtr(new LuaCppFunction(func));
1261 m_totalFuncRefs++;
1262
1263 // sets the userdata __gc metamethod, needed to free the function pointer when it gets collected
1264 newTable();
1265 pushCFunction(&LuaInterface::luaCollectCppFunction);
1266 setField("__gc");
1267 setMetatable();
1268
1269 // actually pushes a C function callback that will call the cpp function
1270 pushCFunction(&LuaInterface::luaCppFunctionCallback, 1);
1271}
1272
1273void LuaInterface::pushValue(int index)
1274{
1275 VALIDATE(hasIndex(index));
1276 lua_pushvalue(L, index);
1277 checkStack();
1278}
1279
1280bool LuaInterface::isNil(int index)
1281{
1282 VALIDATE(hasIndex(index));
1283 return lua_isnil(L, index);
1284}
1285
1286bool LuaInterface::isBoolean(int index)
1287{
1288 VALIDATE(hasIndex(index));
1289 return lua_isboolean(L, index);
1290}
1291
1292bool LuaInterface::isNumber(int index)
1293{
1294 VALIDATE(hasIndex(index));
1295 return lua_isnumber(L, index);
1296}
1297
1298bool LuaInterface::isString(int index)
1299{
1300 VALIDATE(hasIndex(index));
1301 return lua_isstring(L, index);
1302}
1303
1304bool LuaInterface::isTable(int index)
1305{
1306 VALIDATE(hasIndex(index));
1307 return lua_istable(L, index);
1308}
1309
1310bool LuaInterface::isFunction(int index)
1311{
1312 VALIDATE(hasIndex(index));
1313 return lua_isfunction(L, index);
1314}
1315
1316bool LuaInterface::isCFunction(int index)
1317{
1318 VALIDATE(hasIndex(index));
1319 return lua_iscfunction(L, index);
1320}
1321
1322bool LuaInterface::isUserdata(int index)
1323{
1324 VALIDATE(hasIndex(index));
1325 return lua_isuserdata(L, index);
1326}
1327
1328bool LuaInterface::toBoolean(int index)
1329{
1330 VALIDATE(hasIndex(index));
1331 return (bool)lua_toboolean(L, index);
1332}
1333
1334int LuaInterface::toInteger(int index)
1335{
1336 VALIDATE(hasIndex(index));
1337 return lua_tointeger(L, index);
1338}
1339
1340double LuaInterface::toNumber(int index)
1341{
1342 VALIDATE(hasIndex(index));
1343 return lua_tonumber(L, index);
1344}
1345
1346const char* LuaInterface::toCString(int index)
1347{
1348 VALIDATE(hasIndex(index));
1349 return lua_tostring(L, index);
1350}
1351
1352std::string LuaInterface::toString(int index)
1353{
1354 VALIDATE(hasIndex(index));
1355 std::string str;
1356 size_t len;
1357 const char *c_str = lua_tolstring(L, index, &len);
1358 if(c_str && len > 0)
1359 str.assign(c_str, len);
1360 return str;
1361}
1362
1363void* LuaInterface::toUserdata(int index)
1364{
1365 VALIDATE(hasIndex(index));
1366 return lua_touserdata(L, index);
1367}
1368
1369LuaObjectPtr LuaInterface::toObject(int index)
1370{
1371 VALIDATE(hasIndex(index));
1372 if(isUserdata(index)) {
1373 LuaObjectPtr* objRef = static_cast<LuaObjectPtr*>(toUserdata(index));
1374 if(objRef && *objRef)
1375 return *objRef;
1376 }
1377 return nullptr;
1378}
1379
1380int LuaInterface::getTop()
1381{
1382 return lua_gettop(L);
1383}
1384
1385std::string LuaInterface::getSource(int level)
1386{
1387 lua_Debug ar;
1388 ar.short_src[0] = 0;
1389 ar.currentline = 0;
1390 if (lua_getstack(L, level, &ar) == 1) {
1391 lua_getinfo(L, "Sl", &ar);
1392 }
1393 return std::string(ar.short_src) + ":" + std::to_string(ar.currentline);
1394}
1395
1396void LuaInterface::loadFiles(std::string directory, bool recursive, std::string contains)
1397{
1398 for(const std::string& fileName : g_resources.listDirectoryFiles(directory)) {
1399 std::string fullPath = directory + "/" + fileName;
1400
1401 if(recursive && g_resources.directoryExists(fullPath)) {
1402 loadFiles(fullPath, true, contains);
1403 continue;
1404 }
1405
1406 if(!g_resources.isFileType(fileName, "lua"))
1407 continue;
1408
1409 if(!contains.empty() && fileName.find(contains) == std::string::npos)
1410 continue;
1411
1412 try {
1413 g_lua.loadScript(fullPath);
1414 g_lua.call(0, 0);
1415 } catch(stdext::exception& e) {
1416 g_lua.pushString(e.what());
1417 g_lua.error();
1418 }
1419 }
1420}
1421