summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------LuaJIT0
-rw-r--r--Makefile16
-rw-r--r--includes/BLua.h335
-rw-r--r--src/BLua.cc854
-rw-r--r--tests/test-Lua.cc33
5 files changed, 1233 insertions, 5 deletions
diff --git a/LuaJIT b/LuaJIT
-Subproject e80478c44b7e4bf32a509c480edb39bd39ede51
+Subproject 1d190c99a2547b44deb8f5e483452d9f51925fb
diff --git a/Makefile b/Makefile
index 85dbab5..0cc7064 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ CPPFLAGS += -g -DDEBUG
LDFLAGS += -g
endif
-INCLUDES = includes libcoro libeio libev
+INCLUDES = includes libcoro libeio libev LuaJIT/src
LIBS =
ifeq ($(SYSTEM),Darwin)
@@ -91,7 +91,7 @@ endif
ifeq ($(SYSTEM),Linux)
CPPFLAGS += -fPIC
LDFLAGS += -fPIC
- LIBS += pthread
+ LIBS += pthread dl
CONFIG_H = linux-config.h
ARCH_FLAGS = -march=i686 -m32
ASFLAGS = -march=i686 --32
@@ -123,6 +123,8 @@ Socket.cc \
\
Task.cc \
TaskMan.cc \
+\
+BLua.cc \
ifeq ($(SYSTEM),MINGW32)
WIN32_SOURCES = \
@@ -159,6 +161,7 @@ test-Tasks.cc \
test-Threads.cc \
test-Handles.cc \
test-Sockets.cc \
+test-Lua.cc \
LIB = libBalau.a
@@ -186,11 +189,14 @@ strip: $(TESTS)
lib: $(LIB)
-libBalau.a: $(BALAU_OBJECTS)
+LuaJIT:
+ $(MAKE) -C LuaJIT CC="$(CC) $(ARCH_FLAGS)" BUILDMODE=static
+
+libBalau.a: LuaJIT $(BALAU_OBJECTS)
$(AR) libBalau.a $(BALAU_OBJECTS)
%.$(BINEXT) : %.o $(LIB)
- $(LD) $(LDFLAGS) -o $@ $< ./$(LIB) $(LDLIBS)
+ $(LD) $(LDFLAGS) -o $@ $< ./$(LIB) ./LuaJIT/src/libluajit.a $(LDLIBS)
dep: $(ALL_DEPS)
@@ -205,4 +211,4 @@ dep: $(ALL_DEPS)
clean:
rm -f $(ALL_OBJECTS) $(TESTS) $(LIB) $(ALL_DEPS)
-.PHONY: lib tests clean strip
+.PHONY: lib tests clean strip LuaJIT
diff --git a/includes/BLua.h b/includes/BLua.h
new file mode 100644
index 0000000..addd081
--- /dev/null
+++ b/includes/BLua.h
@@ -0,0 +1,335 @@
+#pragma once
+
+#include <typeinfo>
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+}
+
+#include <Exceptions.h>
+#include <Handle.h>
+
+namespace Balau {
+
+class Lua;
+
+class LuaExport {
+ public:
+ virtual ~LuaExport() { }
+};
+
+class LuaObject {
+ public:
+ LuaObject() : m_wantsDestruct(false), m_pushed(false) { }
+ virtual void push(Lua & L) throw (GeneralException);
+ void pushDestruct(Lua & L) throw (GeneralException);
+ template<class T>
+ static T * getMe(Lua & L, int idx = 1);
+ protected:
+ virtual void pushMembers(Lua & L) = 0;
+ void pushMe(Lua & L, void * obj = 0, const char * name = NULL, bool isObj = true);
+ static void pushIt(Lua & L, const char * name, lua_CFunction func);
+ static void pushMeta(Lua & L, const char * name, lua_CFunction func);
+ static void * getMeInternal(Lua & L, int idx);
+ friend class Lua;
+ private:
+ bool m_wantsDestruct, m_pushed;
+};
+
+typedef int (*openlualib_t)(lua_State * L);
+
+class Lua {
+ public:
+ Lua();
+ Lua(lua_State * __L) : L(__L) { }
+ Lua(const Lua &) throw (GeneralException) { throw GeneralException("Error: can't duplicate a Lua object."); }
+
+ typedef int (*lua_CallWrapper)(lua_State *, lua_CFunction);
+
+ int ref(int t = -2) { luaL_ref(L, t); }
+ void unref(int ref, int t = -1) { luaL_unref(L, t, ref); }
+
+ void open_base();
+ void open_table();
+ void open_string();
+ void open_math();
+ void open_debug();
+ void open_bit();
+ void open_jit();
+ int wrap_open(openlualib_t open) { int n = gettop(); int r = open(L); while (n < gettop()) remove(n); }
+ void openlib(const String & libname, const struct luaL_reg *l, int nup) { luaL_openlib(L, libname.to_charp(), l, nup); }
+
+ void setCallWrap(lua_CallWrapper wrapper);
+ void declareFunc(const char * funcName, lua_CFunction f, int tableIdx = LUA_GLOBALSINDEX);
+
+ void call(const char * funcName, int tableIdx = LUA_GLOBALSINDEX, int nArgs = 0);
+ void call(int nArgs = 0) { resume(nArgs); }
+
+ void push() { checkstack(); lua_pushnil(L); }
+ void push(lua_Number n) { checkstack(); lua_pushnumber(L, n); }
+ void push(const String & s) { checkstack(); lua_pushlstring(L, s.to_charp(), s.strlen()); }
+ void push(bool b) { checkstack(); lua_pushboolean(L, b); }
+ void push(const char * str, int size = -1) { if (size < 0) size = strlen(str); checkstack(); lua_pushlstring(L, str, size); }
+ void push(void * p) { checkstack(); lua_pushlightuserdata(L, p); }
+ void push(lua_CFunction f, int n = 0) { checkstack(); lua_pushcclosure(L, f, n); }
+ void pop(int idx = 1) { lua_pop(L, idx); }
+ int checkstack(int extra = 1) { return lua_checkstack(L, extra); }
+
+ int next(int t = -2) { return lua_next(L, t); }
+ void copy(int n = -1) { checkstack(); lua_pushvalue(L, n); }
+ void remove(int n = 1) { lua_remove(L, n); }
+ void insert(int n = 1) { checkstack(); lua_insert(L, n); }
+ void replace(int n = 1) { lua_replace(L, n); }
+ void newtable() { checkstack(); lua_newtable(L); }
+ void * newuser(size_t s) { checkstack(); return lua_newuserdata(L, s); }
+ void settable(int tableIdx = -3, bool raw = false);
+ void gettable(int tableIdx = -2, bool raw = false);
+ void rawseti(int idx, int tableIdx = -2) { lua_rawseti(L, tableIdx, idx); }
+ void rawgeti(int idx, int tableIdx = -1) { lua_rawgeti(L, tableIdx, idx); }
+ void setvar() { lua_settable(L, LUA_GLOBALSINDEX); }
+ int gettop() { return lua_gettop(L); }
+ void getglobal(const char * name) throw (GeneralException);
+ void pushLuaContext();
+ void error(const char * msg);
+ void error(const String & msg) { error(msg.to_charp()); }
+
+ int type(int i = -1) { return lua_type(L, i); }
+ bool isnil(int i = -1) { return lua_isnil(L, i); }
+ bool isboolean(int i = -1) { return lua_isboolean(L, i); }
+ bool isnumber(int i = -1) { return lua_isnumber(L, i); }
+ bool isstring(int i = -1) { return lua_isstring(L, i); }
+ bool istable(int i = -1) { return lua_istable(L, i); }
+ bool isfunction(int i = -1) { return lua_isfunction(L, i); }
+ bool iscfunction(int i = -1) { return lua_iscfunction(L, i); }
+ bool isuserdata(int i = -1) { return lua_isuserdata(L, i); }
+ bool islightuserdata(int i = -1) { return lua_islightuserdata(L, i); }
+ bool isobject(int i = -1);
+
+ bool toboolean(int i = -1) { return lua_toboolean(L, i); }
+ lua_Number tonumber(int i = -1) { return lua_tonumber(L, i); }
+ String tostring(int i = -1);
+ lua_CFunction tocfunction(int i = -1) { return lua_tocfunction(L, i); }
+ void * touserdata(int i = -1) { return lua_touserdata(L, i); }
+
+ String escapeString(const String &);
+ void load(IO<Handle> in, bool docall = true) throw (GeneralException);
+ void load(const String &, bool docall = true) throw (GeneralException);
+ void dumpvars(IO<Handle> out, const String & prefix, int idx = -1);
+ Lua thread(bool saveit = true);
+ Lua thread(const String &, int nargs = 0, bool saveit = true);
+ Lua thread(IO<Handle> in, int nargs = 0, bool saveit = true);
+ int yield(int nresults = 0) { return lua_yield(L, nresults); }
+ bool resume(int nargs = 0) throw (GeneralException);
+ bool resume(const String &, int nargs = 0);
+ bool resume(IO<Handle> in, int nargs = 0);
+ void showstack(int level = M_INFO);
+ void showerror();
+ int getmetatable(int i = -1) { checkstack(); return lua_getmetatable(L, i); }
+ int setmetatable(int i = -2) { return lua_setmetatable(L, i); }
+ int sethook(lua_Hook func, int mask, int count) { return lua_sethook(L, func, mask, count); }
+ void weaken();
+
+ template<class T>
+ T * recast(int n = 1) {
+ LuaExport * b;
+ T * r;
+
+ b = (LuaExport *) LuaObject::getMeInternal(*this, n);
+ if (!b)
+ error("LuaExport base object required; got null.");
+
+ r = dynamic_cast<T *>(b);
+
+ if (!r)
+ error(String("Object not compatible; expecting ") + typeid(r).name() + " but got *" + typeid(*b).name() + " instead.");
+
+ return r;
+ }
+
+ lua_State * getState() { return L; }
+ protected:
+
+ private:
+ void dumpvars_r(IO<Handle> out, int idx, int depth = 0) throw (GeneralException);
+
+ lua_State * L;
+
+ friend class LuaStatics;
+};
+
+class LuaException : public GeneralException {
+ public:
+ LuaException(String fn) : GeneralException(fn) { }
+ protected:
+ LuaException() { }
+};
+
+enum Lua_types_t {
+ BLUA_OBJECT = 0x01,
+ BLUA_TABLE = 0x02,
+ BLUA_BOOLEAN = 0x04,
+ BLUA_NUMBER = 0x08,
+ BLUA_STRING = 0x10,
+ BLUA_FUNCTION = 0x20,
+ BLUA_NIL = 0x40,
+ BLUA_USERDATA = 0x80,
+ BLUA_ANY = 0xff,
+};
+
+#define MAX_TYPE 8
+
+#define MAXARGS 32
+
+struct lua_functypes_t {
+ int number;
+ const char * name;
+ int minargs, maxargs;
+ int argtypes[MAXARGS];
+};
+
+#define DECLARE_METHOD(classname, enumvar) static int method_##enumvar(lua_State * L) { \
+ return LuaHelpers<classname>::method_multiplex( \
+ enumvar, \
+ L, \
+ sLua_##classname::classname##_proceed, \
+ 0, \
+ classname##_methods, \
+ true); \
+ }
+
+#define DECLARE_FUNCTION(classname, enumvar) static int function_##enumvar(lua_State * L) { \
+ return LuaHelpers<classname>::method_multiplex( \
+ enumvar, \
+ L, \
+ 0, \
+ sLua_##classname::classname##_proceed_statics, \
+ classname##_functions, \
+ false); \
+ }
+
+#define PUSH_METHOD(classname, enumvar) pushit( \
+ L, \
+ classname##_methods[enumvar].name, \
+ sLua_##classname::method_##enumvar)
+
+#define PUSH_METAMETHOD(classname, enumvar) pushmeta( \
+ L, \
+ String("__") + classname##_methods[enumvar].name, \
+ sLua_##classname::method_##enumvar)
+
+#define PUSH_FUNCTION(classname, enumvar) L->declarefunc( \
+ classname##_functions[enumvar].name, \
+ sLua_##classname::function_##enumvar)
+
+#define PUSH_SUBFUNCTION(classname, enumvar, array) L->declarefunc( \
+ classname##_functions[enumvar].name, \
+ sLua_##classname::function_##enumvar, \
+ array)
+
+
+#define CHECK_METHODS(classname) { \
+ int i = 0; \
+ while (classname##_methods[i].number != -1) { \
+ if (i != classname##_methods[i].number) { \
+ throw GeneralException("Data of " #classname "_methods inconsistants!"); \
+ } \
+ i++; \
+ } \
+}
+
+#define CHECK_FUNCTIONS(classname) { \
+ int i = 0; \
+ while (classname##_functions[i].number != -1) { \
+ if (i != classname##_functions[i].number) { \
+ throw GeneralException("Data of " #classname "_functions inconsistants!"); \
+ } \
+ i++; \
+ } \
+}
+
+template <class T>
+T * LuaObject::getMe(Lua & L, int idx) { return L.recast<T>(idx); }
+
+template <class T>
+class LuaHelpers {
+ public:
+ static int method_multiplex(int caller, lua_State * __L, int (*proceed)(Lua & L, int n, T * obj, int caller), int (*proceed_static)(Lua & L, int n, int caller), lua_functypes_t * tab, bool method) {
+ Lua L(__L);
+ int add = method ? 1 : 0;
+ int n = L.gettop() - add;
+ T * obj = 0;
+ int i, j, mask;
+ bool invalid = false, arg_valid;
+
+ if (method)
+ obj = LuaObject::getMe<T>(L);
+
+ if ((n < tab[caller].minargs) || (n > tab[caller].maxargs)) {
+ invalid = true;
+ } else {
+ for (i = 0; i < tab[caller].maxargs && !invalid; i++) {
+ if (n >= (i + 1)) {
+ arg_valid = false;
+ for (j = 0; j < MAX_TYPE && !arg_valid; j++) {
+ mask = 1 << j;
+ if (tab[caller].argtypes[i] & mask) {
+ switch(mask) {
+ case BLUA_OBJECT:
+ if (L.istable(i + 1 + add)) {
+ L.push("__obj");
+ L.gettable(i + 1 + add);
+ arg_valid = L.isuserdata();
+ L.pop();
+ } else {
+ arg_valid = L.isnil(i + 1 + add);
+ }
+ break;
+ case BLUA_TABLE:
+ arg_valid = L.istable(i + 1 + add);
+ break;
+ case BLUA_BOOLEAN:
+ arg_valid = L.isboolean(i + 1 + add);
+ break;
+ case BLUA_NUMBER:
+ arg_valid = L.isnumber(i + 1 + add);
+ break;
+ case BLUA_STRING:
+ arg_valid = L.isstring(i + 1 + add);
+ break;
+ case BLUA_FUNCTION:
+ arg_valid = L.isfunction(i + 1 + add);
+ break;
+ case BLUA_NIL:
+ arg_valid = L.isnil(i + 1 + add);
+ break;
+ case BLUA_USERDATA:
+ arg_valid = L.isuserdata(i + 1 + add) || L.islightuserdata(i + 1 + add);
+ break;
+ }
+ }
+ }
+ invalid = !arg_valid;
+ }
+ }
+ }
+
+ if (invalid) {
+ if (method) {
+ L.error(String("Invalid arguments to method `") + typeid(T).name() + "::" + tab[caller].name + "'");
+ } else {
+ L.error(String("Invalid arguments to function `") + typeid(T).name() + " " + tab[caller].name + "'");
+ }
+ }
+
+ if (method) {
+ return proceed(L, n, obj, caller);
+ } else {
+ return proceed_static(L, n, caller);
+ }
+ }
+};
+
+template<class T> T * lua_recast(Lua & L, int n = 1) { return L.recast<T>(n); }
+
+};
diff --git a/src/BLua.cc b/src/BLua.cc
new file mode 100644
index 0000000..9cfccf1
--- /dev/null
+++ b/src/BLua.cc
@@ -0,0 +1,854 @@
+#include <stdlib.h>
+#include "BLua.h"
+#include "Printer.h"
+#include "Input.h"
+
+extern "C" {
+#include <lualib.h>
+#include <luajit.h>
+}
+
+#ifndef BUFFERSIZE
+#define BUFFERSIZE 2048
+#endif
+
+namespace Balau {
+
+class LuaStatics {
+ public:
+ static const char * getF(lua_State * L, void *, size_t *);
+
+ static int callwrap(lua_State * L, lua_CFunction);
+ static int collector(lua_State * L);
+ static int destructor(lua_State * L);
+
+ static int dumpvars(lua_State * L);
+
+ static int iconv(lua_State * L);
+ static int mkdir(lua_State * L);
+ static int time(lua_State * L);
+ static int getenv(lua_State * L);
+ static int setenv(lua_State * L);
+ static int unsetenv(lua_State * L);
+
+ static int hex(lua_State * L);
+
+ static int globalindex(lua_State * L);
+
+ static int print(lua_State * L);
+};
+
+};
+
+int Balau::LuaStatics::hex(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+ int x;
+ String r;
+
+ if (((n != 1) && (n != 2)) || !L.isnumber(1) || ((n == 2) && !L.isstring(2)))
+ L.error("Incorrect arguments to function `hex'");
+
+ x = L.tonumber(1);
+ String fmt = n == 2 ? L.tostring() : "%02x";
+ r.set(fmt.to_charp(), x);
+
+ L.push(r);
+
+ return 1;
+}
+
+int Balau::LuaStatics::dumpvars(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+ String prefix;
+ String varname;
+
+ if ((n > 3) || (n < 2) || !L.isobject(1) ||
+ ((n == 2) && !L.isstring(2)) ||
+ ((n == 3) && !L.isstring(3) && !L.istable(2) && !L.isstring(2)))
+ L.error("Incorrect arguments to function `dumpvars'");
+
+ prefix = L.tostring(n);
+
+ if (n == 3)
+ L.pop();
+
+ if (L.isstring(2))
+ L.getglobal(L.tostring(2).to_charp());
+
+ IO<Handle> h(L.recast<Balau::Handle>());
+
+ L.dumpvars(h, prefix);
+
+ return 0;
+}
+
+int Balau::LuaStatics::iconv(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+ String str, from, to;
+
+ if ((n != 3) || !L.isstring(1) || !L.isstring(2) || !L.isstring(3))
+ L.error("Incorrect arguments to function `string.iconv'");
+
+ str = L.tostring(1);
+ from = L.tostring(2);
+ to = L.tostring(3);
+
+ str.iconv(from, to);
+
+ L.push(str);
+
+ return 1;
+}
+
+int Balau::LuaStatics::mkdir(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+ String dirname;
+
+ if (n != 1)
+ L.error("Incorrect arguments to function `mkdir'");
+
+ dirname = L.tostring(1);
+
+ Balau::FileSystem::mkdir(dirname.to_charp());
+
+ return 0;
+}
+
+int Balau::LuaStatics::time(lua_State * __L) {
+ Lua L(__L);
+
+ L.push((lua_Number) ::time(NULL));
+
+ return 1;
+}
+
+int Balau::LuaStatics::getenv(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+
+ if (n != 1)
+ L.error("Incorrect arguments to function `getenv'");
+
+#ifdef _WIN32
+ char buffer[BUFSIZ + 1];
+ if (GetEnvironmentVariable(L.tostring(1).to_charp(), buffer, BUFSIZ)) {
+ L.push(buffer);
+ } else {
+ L.push();
+ }
+#else
+ char * var = ::getenv(L.tostring(1).to_charp());
+ if (var) {
+ L.push(var);
+ } else {
+ L.push();
+ }
+#endif
+
+ return 1;
+}
+
+int Balau::LuaStatics::setenv(lua_State * __L) {
+ Lua L(__L);
+ int n = L.gettop();
+
+ if (n != 2) {
+ L.error("Incorrect arguments to function `setenv'");
+ }
+
+#ifdef _WIN32
+ SetEnvironmentVariable(L.tostring(1).to_charp(), L.tostring(2).to_charp());
+#else
+ ::setenv(L.tostring(1).to_charp(), L.tostring(2).to_charp(), 1);
+#endif
+
+ return 0;
+}
+
+int Balau::LuaStatics::unsetenv(lua_State * __L) {
+ Lua L(__L);
+
+ int n = L.gettop();
+ if (n != 1)
+ L.error("Incorrect arguments to function `unsetenv'");
+
+#ifdef _WIN32
+ SetEnvironmentVariable(L.tostring(1).to_charp(), NULL);
+#else
+ ::unsetenv(L.tostring(1).to_charp());
+#endif
+
+ return 0;
+}
+
+int Balau::LuaStatics::print(lua_State * __L) {
+ Lua L(__L);
+
+ int n = L.gettop();
+ int i;
+ for (i = 1; i <= n; i++) {
+ const char *s;
+ s = lua_tostring(__L, i);
+ if (s == NULL)
+ L.error("`tostring' must return a string to `print'");
+ if (i > 1)
+ Printer::print("\t");
+ Printer::print(s);
+ L.pop();
+ }
+ Printer::print("\n");
+ return 0;
+}
+
+int Balau::LuaStatics::callwrap(lua_State * __L, lua_CFunction func) {
+ Lua L(__L);
+
+ try {
+ return func(__L);
+ }
+ catch (LuaException e) {
+ L.error(String("LuaException: ") + e.getMsg());
+ }
+ catch (Balau::GeneralException e) {
+ L.error(String("GeneralException: ") + e.getMsg());
+ }
+ catch (...) {
+ L.error("Unknown C++ exception");
+ }
+
+ return 0;
+}
+
+struct ObjData {
+ void * ptr;
+ bool isObj;
+};
+
+int Balau::LuaStatics::collector(lua_State * __L) {
+ Lua L(__L);
+ ObjData * u = (ObjData *) L.touserdata();
+ if (u->isObj) {
+ LuaExport * obj = (LuaExport *) u->ptr;
+ delete obj;
+ } else {
+ free(u->ptr);
+ }
+ u->ptr = NULL;
+ return 0;
+}
+
+int Balau::LuaStatics::destructor(lua_State * __L) {
+ Lua L(__L);
+ L.push("__obj");
+ L.gettable(-2, true);
+ ObjData * u = (ObjData *) L.touserdata();
+ if (u->isObj) {
+ LuaExport * obj = (LuaExport *) u->ptr;
+ delete obj;
+ } else {
+ free(u->ptr);
+ }
+ u->ptr = NULL;
+ L.pop();
+ return 0;
+}
+
+void Balau::Lua::setCallWrap(lua_CallWrapper wrapper) {
+ push((void *) wrapper);
+ luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
+ pop();
+}
+
+Balau::Lua::Lua() : L(lua_open()) {
+ setCallWrap(LuaStatics::callwrap);
+ declareFunc("hex", LuaStatics::hex);
+ declareFunc("dumpvars", LuaStatics::dumpvars);
+ declareFunc("print", LuaStatics::print);
+ push("BLUA_THREADS");
+ newtable();
+ settable(LUA_REGISTRYINDEX);
+}
+
+#define IntPoint(p) ((unsigned int)(lu_mem)(p))
+typedef size_t lu_mem;
+
+void Balau::Lua::weaken() {
+ push("BLUA_THREADS"); // -1 = "BLUA_THREADS"
+ gettable(LUA_REGISTRYINDEX); // -1 = BLUA_THREADS
+ push((lua_Number) IntPoint(L)); // -2 = BLUA_THREADS, -1 = key-Lt
+ push(); // -3 = BLUA_THREADS, -2 = key-Lt, -1 = nil
+ settable(); // -1 = BLUA_THREADS
+ pop();
+}
+
+void Balau::Lua::open_base() {
+ int n = gettop();
+ luaopen_base(L);
+ push("mkdir");
+ push(LuaStatics::mkdir);
+ settable();
+ push("time");
+ push(LuaStatics::time);
+ settable();
+ push("getenv");
+ push(LuaStatics::getenv);
+ settable();
+ push("setenv");
+ push(LuaStatics::setenv);
+ settable();
+ push("unsetenv");
+ push(LuaStatics::unsetenv);
+ settable();
+ push("print");
+ push(LuaStatics::print);
+ settable();
+ while (n < gettop()) remove(n);
+ push("mkdir");
+ push(LuaStatics::mkdir);
+ settable(LUA_GLOBALSINDEX);
+ push("time");
+ push(LuaStatics::time);
+ settable(LUA_GLOBALSINDEX);
+ push("getenv");
+ push(LuaStatics::getenv);
+ settable(LUA_GLOBALSINDEX);
+ push("setenv");
+ push(LuaStatics::setenv);
+ settable(LUA_GLOBALSINDEX);
+ push("unsetenv");
+ push(LuaStatics::unsetenv);
+ settable(LUA_GLOBALSINDEX);
+ push("print");
+ push(LuaStatics::print);
+ settable(LUA_GLOBALSINDEX);
+}
+
+void Balau::Lua::open_table() {
+ int n = gettop();
+ luaopen_table(L);
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::open_string() {
+ int n = gettop();
+ luaopen_string(L);
+ push("iconv");
+ push(LuaStatics::iconv);
+ settable();
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::open_math() {
+ int n = gettop();
+ luaopen_math(L);
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::open_debug() {
+ int n = gettop();
+ luaopen_debug(L);
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::open_jit() {
+ int n = gettop();
+ luaopen_jit(L);
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::open_bit() {
+ int n = gettop();
+ luaopen_bit(L);
+ while (n < gettop()) remove(n);
+}
+
+void Balau::Lua::declareFunc(const char * name, lua_CFunction f, int i) {
+ checkstack(2);
+ lua_pushstring(L, name);
+ lua_pushcfunction(L, f);
+ lua_settable(L, i);
+}
+
+void Balau::Lua::call(const char * f, int i, int nargs) {
+ checkstack(1);
+ lua_pushstring(L, f);
+ lua_gettable(L, i);
+ lua_insert(L, -1 - nargs);
+ call(nargs);
+}
+
+void Balau::Lua::settable(int i, bool raw) {
+ if (raw) {
+ lua_rawset(L, i);
+ } else {
+ lua_settable(L, i);
+ }
+}
+
+void Balau::Lua::gettable(int i, bool raw) {
+ if (raw) {
+ lua_rawget(L, i);
+ } else {
+ lua_gettable(L, i);
+ }
+}
+
+void Balau::Lua::getglobal(const char * name) throw (GeneralException) {
+ push(name);
+ gettable(LUA_GLOBALSINDEX);
+}
+
+void Balau::Lua::pushLuaContext() {
+ String whole_msg;
+ struct lua_Debug ar;
+ bool got_error = false;
+ int level = 0;
+
+ do {
+ if (lua_getstack(L, level, &ar) == 1) {
+ if (lua_getinfo(L, "nSl", &ar) != 0) {
+ push(String("at ") + ar.source + ":" + ar.currentline + " (" + (ar.name ? ar.name : "[top]") + ")");
+ } else {
+ got_error = true;
+ }
+ } else {
+ got_error = true;
+ }
+ level++;
+ } while (!got_error);
+}
+
+void Balau::Lua::error(const char * msg) {
+ push(msg);
+
+ lua_error(L);
+}
+
+bool Balau::Lua::isobject(int i) {
+ bool r = false;
+ if (istable(i)) {
+ push("__obj");
+ gettable(i);
+ r = isuserdata();
+ pop();
+ } else {
+ r = isnil(i);
+ }
+ return r;
+}
+
+Balau::String Balau::Lua::tostring(int i) {
+ const char * r = 0;
+ size_t l = -1;
+ switch (type(i)) {
+ case LUA_TNIL:
+ r = "(nil)";
+ break;
+ case LUA_TBOOLEAN:
+ r = toboolean(i) ? "true" : "false";
+ break;
+ case LUA_TNUMBER:
+ return String(tonumber(i));
+ break;
+ default:
+ r = lua_tolstring(L, i, &l);
+ }
+ return String(r ? r : "<lua-NULL>", l);
+}
+
+struct LoadF {
+ Balau::IO<Balau::Handle> f;
+ char buff[BUFFERSIZE];
+};
+
+const char * Balau::LuaStatics::getF(lua_State * L, void * ud, size_t * size) {
+ LoadF * lf = (LoadF *)ud;
+
+ *size = lf->f->read(lf->buff, BUFFERSIZE);
+ return (*size > 0) ? lf->buff : NULL;
+}
+
+Balau::String Balau::Lua::escapeString(const String & s) {
+ String r = "";
+ int i;
+ for (i = 0; i < s.strlen(); i++) {
+ switch(s[i]) {
+ case '"': case '\\':
+ r += '\\';
+ r += s[i];
+ break;
+ case '\n':
+ r += "\\n";
+ break;
+ case '\r':
+ r += "\\r";
+ break;
+ case '\0':
+ r += "\\000";
+ break;
+ default:
+ r += s[i];
+ }
+ }
+ return r;
+}
+
+void Balau::Lua::load(IO<Handle> h, bool docall) throw (GeneralException) {
+ LoadF lf;
+ int status;
+
+ lf.f = h;
+
+ checkstack();
+ String name = h->getName();
+ IO<Input> i = h;
+ if (!i.isNull())
+ name = String("@") + i->getFName();
+ status = lua_load(L, LuaStatics::getF, &lf, name.to_charp());
+
+ if (status) {
+ pushLuaContext();
+ showerror();
+ throw LuaException(String("Error loading lua chunk from Handle `") + h->getName() + "'");
+ }
+
+ if (docall)
+ call();
+}
+
+void Balau::Lua::load(const String & s, bool docall) throw (GeneralException) {
+ const char * buf = s.to_charp();
+ int status;
+
+ status = luaL_loadbuffer(L, buf, s.strlen(), buf);
+
+ if (status) {
+ pushLuaContext();
+ showerror();
+ throw LuaException(String("Error loading lua string `") + s + "'");
+ }
+
+ if (docall)
+ call();
+}
+
+void Balau::Lua::dumpvars(IO<Handle> h, const String & prefix, int i) {
+ h->writeString(prefix);
+ h->writeString(" = {\n");
+ dumpvars_r(h, i);
+ h->writeString("}\n");
+}
+
+void Balau::Lua::dumpvars_r(IO<Handle> h, int i, int depth) throw (GeneralException) {
+ int j;
+ String t;
+ bool dump_value;
+
+ if (lua_type(L, i) != LUA_TTABLE)
+ throw LuaException("Error dumping variables: variable isn't a table.");
+
+ push();
+
+ if (i < 0)
+ i--;
+
+ depth++;
+
+ checkstack();
+ while(lua_next(L, i) != 0) {
+ if (lua_type(L, -2) == LUA_TSTRING)
+ if (String(lua_tostring(L, -2)) == String("_G"))
+ continue;
+ for (j = 0; j < depth; j++)
+ h->writeString(" ");
+
+ dump_value = true;
+
+ // first, let's dump the key.
+ switch(lua_type(L, -2)) {
+ case LUA_TNONE:
+ throw LuaException("Internal error: got invalid index for key while dumpvars.");
+ case LUA_TNIL:
+ throw LuaException("Internal error: got nil index for key while dumpvars.");
+ case LUA_TNUMBER:
+ t.set("[%.14g] = ", lua_tonumber(L, -2));
+ break;
+ case LUA_TBOOLEAN:
+ t = String("[") + (lua_toboolean(L, -2) ? "true" : "false") + "] = ";
+ break;
+ case LUA_TSTRING:
+ t = String("[\"") + escapeString(lua_tostring(L, -2)) + String("\"] = ");
+ break;
+ case LUA_TTABLE:
+ t = "-- [a table]\n";
+ dump_value = false;
+ break;
+ case LUA_TFUNCTION:
+ t = "-- [function() ... end]\n";
+ dump_value = false;
+ break;
+ case LUA_TUSERDATA:
+ t = "-- [userdata]\n";
+ dump_value = false;
+ break;
+ case LUA_TTHREAD:
+ t = "-- [thread]\n";
+ dump_value = false;
+ break;
+ default:
+ throw LuaException("Internal error: got unknow index for key while dumpvars.");
+ }
+
+ // Seems that we can't dump that key.
+ if (!dump_value) {
+ pop();
+ h->writeString(t);
+ continue;
+ }
+
+ // let's look at the value: if it's a function, a userdata or a thread, we can't dump it.
+ if ((lua_type(L, -1) == LUA_TFUNCTION) ||
+ (lua_type(L, -1) == LUA_TUSERDATA) ||
+ (lua_type(L, -1) == LUA_TTHREAD))
+ h->writeString("-- ");
+
+ h->writeString(t);
+
+ // Finally, let's dump the value.
+ switch(lua_type(L, -1)) {
+ case LUA_TNONE:
+ throw LuaException("Internal error: got invalid index for value while dumpvars.");
+ case LUA_TNIL:
+ h->writeString("nil,\n");
+ case LUA_TNUMBER:
+ t.set("%.14g,\n", lua_tonumber(L, -1));
+ h->writeString(t);
+ break;
+ case LUA_TBOOLEAN:
+ h->writeString(lua_toboolean(L, -1) ? "true" : "false");
+ h->writeString(",\n");
+ break;
+ case LUA_TSTRING:
+ h->writeString("\"");
+ h->writeString(escapeString(lua_tostring(L, -1)));
+ h->writeString("\",\n");
+ break;
+ case LUA_TTABLE:
+ h->writeString("{\n");
+ dumpvars_r(h, -1, depth);
+ for (j = 0; j < depth; j++)
+ h->writeString(" ");
+ h->writeString("},\n");
+ break;
+ case LUA_TFUNCTION:
+ h->writeString("function() ... end\n");
+ break;
+ case LUA_TUSERDATA:
+ h->writeString("userdata ...\n");
+ break;
+ case LUA_TTHREAD:
+ h->writeString("thread ...\n");
+ break;
+ default:
+ throw LuaException("Internal error: got unknow index for value while dumpvars.");
+ }
+
+ pop();
+ }
+}
+
+Balau::Lua Balau::Lua::thread(bool saveit) {
+ checkstack();
+ lua_State * L1 = lua_newthread(L);
+ if (saveit) { // -1 = thread
+ push("BLUA_THREADS"); // -2 = thread, -1 = "BLUA_THREADS"
+ gettable(LUA_REGISTRYINDEX); // -2 = thread, -1 = BLUA_THREADS
+ push((lua_Number) IntPoint(L1)); // -3 = thread, -2 = BLUA_THREADS, -1 = key-Lt
+ copy(-3); // -4 = thread, -3 = BLUA_THREADS, -2 = key-Lt, -1 = thread
+ settable(); // -2 = thread, -1 = BLUA_THREADS
+ pop(); // -1 = thread
+ }
+ return Lua(L1);
+}
+
+Balau::Lua Balau::Lua::thread(const String & code, int nargs, bool saveit) {
+ Lua L1;
+ L1 = thread(saveit);
+ L1.resume(code, nargs);
+ return L1;
+}
+
+Balau::Lua Balau::Lua::thread(IO<Handle> h, int nargs, bool saveit) {
+ Lua L1;
+ L1 = thread(saveit);
+ L1.resume(h, nargs);
+ return L1;
+}
+
+bool Balau::Lua::resume(int nargs) throw (GeneralException) {
+ int r;
+
+ r = lua_resume(L, nargs);
+
+ if ((r == 0) || (r == LUA_YIELD))
+ return 0;
+
+ pushLuaContext();
+ showerror();
+
+ switch(r) {
+ case 0:
+ return false;
+ case LUA_YIELD:
+ return true;
+ case LUA_ERRRUN:
+ throw LuaException("Runtime error while running LUA code.");
+ case LUA_ERRMEM:
+ throw LuaException("Memory allocation error while running LUA code.");
+ case LUA_ERRERR:
+ throw LuaException("Error in Error function.");
+ case LUA_ERRSYNTAX:
+ throw LuaException("Syntax error in Lua code.");
+ default:
+ throw LuaException(String("Unknow error while running LUA code (err code: ") + String(r) + ")");
+ }
+}
+
+bool Balau::Lua::resume(IO<Handle> h, int nargs) {
+ bool r;
+
+ load(h, false);
+ r = resume(nargs);
+
+ return r;
+}
+
+bool Balau::Lua::resume(const String & s, int nargs) {
+ bool r;
+
+ load(s, false);
+ r = resume(nargs);
+
+ return r;
+}
+
+void Balau::Lua::showstack(int level) {
+ int n = lua_gettop(L);
+ int i;
+ String t;
+
+ if (n == 0) {
+ Printer::log(level, "Stack empty\n");
+ return;
+ }
+
+ for (i = 1; i <= n; i++) {
+ switch(lua_type(L, i)) {
+ case LUA_TNONE:
+ t = "Invalid";
+ break;
+ case LUA_TNIL:
+ t = "(Nil)";
+ break;
+ case LUA_TNUMBER:
+ t.set("(Number) %f", lua_tonumber(L, i));
+ break;
+ case LUA_TBOOLEAN:
+ t = String("(Bool) ") + (lua_toboolean(L, i) ? "true" : "false");
+ break;
+ case LUA_TSTRING:
+ t = String("(String) ") + lua_tostring(L, i);
+ break;
+ case LUA_TTABLE:
+ t = "(Table)";
+ break;
+ case LUA_TFUNCTION:
+ t = "(Function)";
+ break;
+ default:
+ t = "Unknown";
+ }
+
+ Printer::log(level, String(i) + ": " + t + "\n");
+ }
+}
+
+void Balau::Lua::showerror() {
+ Printer::log(M_ERROR, "Lua object: Got an LUA error, inspecting stack.\n");
+
+ showstack(M_ERROR);
+}
+
+void Balau::LuaObject::push(Lua & L) throw (GeneralException) {
+ if (m_pushed && m_wantsDestruct)
+ throw GeneralException("Error: object is owned by the LUA script and can not be pushed.");
+ L.newtable();
+ pushMembers(L);
+ m_pushed = true;
+}
+
+void Balau::LuaObject::pushMe(Lua & L, void * o, const char * objname, bool obj) {
+ ObjData * u;
+ L.push("__obj");
+ u = (ObjData *) L.newuser(sizeof(ObjData));
+ u->ptr = o;
+ u->isObj = obj;
+ L.settable(-3, true);
+ if (objname && *objname) {
+ L.push("__objname");
+ L.push(objname);
+ L.settable(-3, true);
+ }
+}
+
+void * Balau::LuaObject::getMeInternal(Lua & L, int i) {
+ ObjData * u = NULL;
+
+ if (L.istable(i)) {
+ L.push("__obj");
+ L.gettable(i, true);
+ if (!(u = (ObjData *) L.touserdata()))
+ L.error("Table is not an object.");
+ if (!u->ptr)
+ L.error("Object already destroyed.");
+ L.pop();
+ } else if (L.isnil(i)) {
+ u = NULL;
+ } else {
+ L.error("Not an object (not even a table).");
+ }
+
+ return u ? u->ptr : NULL;
+}
+
+void Balau::LuaObject::pushIt(Lua & L, const char * s, lua_CFunction f) {
+ L.push(s);
+ L.push(f);
+ L.settable(-3, true);
+}
+
+void Balau::LuaObject::pushMeta(Lua & L, const char * s, lua_CFunction f) {
+ if (!L.getmetatable())
+ L.newtable();
+ L.push(s);
+ L.push(f);
+ L.settable();
+ L.setmetatable();
+}
+
+void Balau::LuaObject::pushDestruct(Lua & L) throw (GeneralException) {
+ if (m_pushed)
+ throw GeneralException("Error: can't push destructor, object already pushed");
+ push(L);
+ L.push("__obj");
+ L.gettable(-2, true);
+ pushMeta(L, "__gc", LuaStatics::collector);
+ L.pop();
+ pushIt(L, "destroy", LuaStatics::destructor);
+
+ m_wantsDestruct = true;
+}
diff --git a/tests/test-Lua.cc b/tests/test-Lua.cc
new file mode 100644
index 0000000..86baffb
--- /dev/null
+++ b/tests/test-Lua.cc
@@ -0,0 +1,33 @@
+#include <Main.h>
+#include <BLua.h>
+
+BALAU_STARTUP;
+
+using namespace Balau;
+
+void MainTask::Do() {
+ Printer::log(M_STATUS, "Test::Lua running.");
+
+ Lua L;
+
+ // yeah, they really should be the same thing.
+ Assert(sizeof(L) == sizeof(lua_State *));
+
+ L.open_base();
+ L.open_table();
+ L.open_string();
+ L.open_math();
+ L.open_debug();
+ L.open_bit();
+ L.open_jit();
+
+ Assert(L.gettop() == 0);
+ L.load("return 42");
+ Assert(L.gettop() == 1);
+ int r = L.tonumber();
+ Assert(r == 42);
+ L.pop();
+ Assert(L.gettop() == 0);
+
+ Printer::log(M_STATUS, "Test::Lua passed.");
+}