/* * Baltisot * Copyright (C) 1999-2008 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include "BLua.h" #include "Buffer.h" #include #ifndef BUFFERSIZE #define BUFFERSIZE 2048 #endif #undef isnumber void LuaPrinter::puts(const char * msg) { fputs(msg, stdout); fputs("\n", stdout); } LuaPrinter default_lua_printer; extern "C" { void do_lua_lock(lua_State * __L) { Lua * L; try { L = Lua::find(__L); } catch (GeneralException e) { return; } L->lock(); } void do_lua_unlock(lua_State * __L) { Lua * L; try { L = Lua::find(__L); } catch (GeneralException e) { return; } L->unlock(); } } class LuaStatics : public Base { public: static const char * getF(lua_State *, void *, size_t *); static int putF(lua_State *, const void *, size_t, void *); static int luapanic(lua_State *); static int trueluapanic(lua_State *) throw(GeneralException); static int luaerror(lua_State *); static int callwrap(lua_State *, lua_CFunction); static void createhook(lua_State *, lua_State *); static void destroyhook(lua_State *, lua_State *); static int collector(lua_State *); static int destructor(lua_State *); static int andB(lua_State *); static int orB(lua_State *); static int xorB(lua_State *); static int notB(lua_State *); static int hex(lua_State *); static int shl(lua_State *); static int shr(lua_State *); static int getglobal(lua_State *); static int dumpvars(lua_State *); static int iconv(lua_State *); static int mkdir(lua_State *); static int time(lua_State *); static int getenv(lua_State *); static int setenv(lua_State *); static int unsetenv(lua_State *); static int sleep(lua_State *); static int globalindex(lua_State *); static int print(lua_State *); }; std::map Lua::lualist; int LuaStatics::luaerror(lua_State * __L) { Lua * L = Lua::find(__L); L->push_lua_context(); L->showerror(); return 0; } int LuaStatics::luapanic(lua_State * L) { return trueluapanic(L); } int LuaStatics::trueluapanic(lua_State * __L) throw (GeneralException) { Lua * L = Lua::find(__L); if (L->is_protected()) return 0; // luaerror will get it for us... L->push_lua_context(); L->showerror(); throw LuaException("Unprotected error running Lua code, bailing out; expect unstable lua environment."); } int LuaStatics::andB(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 a, b; if ((n != 2) || !L->isnumber(1) || !L->isnumber(2)) { L->error("Incorrect arguments to function `andB'"); } a = L->tonumber(1); b = L->tonumber(2); L->push((lua_Number) (a & b)); return 1; } int LuaStatics::orB(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 a, b; if ((n != 2) || !L->isnumber(1) || !L->isnumber(2)) { L->error("Incorrect arguments to function `orB'"); } a = L->tonumber(1); b = L->tonumber(2); L->push((lua_Number) (a | b)); return 1; } int LuaStatics::xorB(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 a, b; if ((n != 2) || !L->isnumber(1) || !L->isnumber(2)) { L->error("Incorrect arguments to function `xorB'"); } a = L->tonumber(1); b = L->tonumber(2); L->push((lua_Number) (a ^ b)); return 1; } int LuaStatics::notB(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 x; if ((n != 1) || !L->isnumber()) { L->error("Incorrect arguments to function `notB'"); } x = ~((Uint32) L->tonumber()); L->push((lua_Number) x); return 1; } int LuaStatics::shl(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 a, b; if ((n > 2) || !L->isnumber(1) || ((n == 2) && !L->isnumber(2))) { L->error("Incorrect arguments to function `shl'"); } a = L->tonumber(1); if (n == 2) b = L->tonumber(2); else b = 1; L->push((lua_Number) (a << b)); return 1; } int LuaStatics::shr(lua_State * __L) { Lua * L = Lua::find(__L); int n = L->gettop(); Uint32 a, b; if ((n > 2) || !L->isnumber(1) || ((n == 2) && !L->isnumber(2))) { L->error("Incorrect arguments to function `shr'"); } a = L->tonumber(1); if (n == 2) b = L->tonumber(2); else b = 1; L->push((lua_Number) (a >> b)); return 1; } int LuaStatics::hex(lua_State * __L) { Lua * L = Lua::find(__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 LuaStatics::getglobal(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); if ((n != 1) || !L->isstring(1)) { L->error("Incorrect arguments to function `getglobal'"); } Buffer b; b << "return " + L->tostring(); L->load(&b, false); L->call(0, 1); return 1; } int LuaStatics::dumpvars(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); Handle * h; 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)); h = L->recast(); L->dumpvars(h, prefix); return 0; } int LuaStatics::iconv(lua_State * _L) { Lua * L = Lua::find(_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 LuaStatics::mkdir(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); String dirname; if (n != 1) { L->error("Incorrect arguments to function `mkdir'"); } dirname = L->tostring(1); ::MKDIR(dirname.to_charp()); return 0; } int LuaStatics::time(lua_State * _L) { Lua * L = Lua::find(_L); L->push((lua_Number) ::time(NULL)); return 1; } int LuaStatics::getenv(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); /* number of arguments */ 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 LuaStatics::setenv(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); /* number of arguments */ 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 LuaStatics::unsetenv(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); /* number of arguments */ 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 LuaStatics::sleep(lua_State * _L) { Lua * L = Lua::find(_L); int t; int n = L->gettop(); /* number of arguments */ if ((n != 1) || !L->isnumber(1)) { L->error("Incorrect arguments to function `sleep'"); } t = L->tonumber(1); #ifdef _WIN32 ::Sleep(t); #else ::usleep(t * 1000); #endif return 0; } int LuaStatics::print(lua_State * _L) { Lua * L = Lua::find(_L); int n = L->gettop(); /* number of arguments */ int i; for (i = 1; i <= n; i++) { const char *s; s = lua_tostring(_L, i); /* get result */ if (s == NULL) L->error("`tostring' must return a string to `print'"); if (i > 1) L->puts("\t"); L->puts(s); L->pop(); /* pop result */ } return 0; } int LuaStatics::callwrap(lua_State * __L, lua_CFunction func) { Lua * L = Lua::find(__L); int n; try { n = func(__L); } catch (LuaException e) { L->error(String("LuaException: ") + e.GetMsg()); } catch (GeneralException e) { L->error(String("GeneralException: ") + e.GetMsg()); } return n; } int LuaStatics::collector(lua_State * __L) { Lua * L = Lua::find(__L); void ** u = (void **) L->touserdata(); bool * obj = (bool *) (u + 1); // printm(M_INFO, "From LUA: collecting object\n"); if (*obj) { // printm(M_INFO, "Is object at %p\n", *u); Base * b = (Base *) *u; delete b; } else { // printm(M_INFO, "Is struct at %p\n", *u); free(*u); } *u = 0; return 0; } int LuaStatics::destructor(lua_State * __L) { Lua * L = Lua::find(__L); L->push("__obj"); L->gettable(-2, true); void ** u = (void **) L->touserdata(); bool * obj = (bool *) (u + 1); if (*obj) { Base * b = (Base *) *u; delete b; } else { free(*u); } *u = 0; L->pop(); return 0; } void LuaStatics::createhook(lua_State * __L, lua_State * L1) { Lua * L = Lua::find(__L); L->spawn_from_thread(L1); } void LuaStatics::destroyhook(lua_State * __L, lua_State * L1) { Lua * L = Lua::find(L1); delete L; } void Lua::setup_state(lua_State *) { lualist[L] = this; lua_atpanic(L, LuaStatics::luapanic); lua_setcallwrap(L, LuaStatics::callwrap); lua_setcreatehook(L, LuaStatics::createhook); lua_setdestroyhook(L, LuaStatics::destroyhook); lprinter = &default_lua_printer; } Lua::Lua() : L(lua_open()), _protected(false), _is_thread(false) { setup_state(L); declarefunc("andB", LuaStatics::andB); declarefunc("orB", LuaStatics::orB); declarefunc("xorB", LuaStatics::xorB); declarefunc("notB", LuaStatics::notB); declarefunc("shl", LuaStatics::shl); declarefunc("shr", LuaStatics::shr); declarefunc("hex", LuaStatics::hex); declarefunc("getglobal", LuaStatics::getglobal); declarefunc("dumpvars", LuaStatics::dumpvars); declarefunc("print", LuaStatics::print); push("BLUA_THREADS"); newtable(); settable(LUA_REGISTRYINDEX); father = 0; } Lua::Lua(lua_State * __L) : L(__L), _protected(false), _is_thread(true) { setup_state(L); // **TODO** // do we need to define global functions as well.. ? // I don't think so. Environment should be duplicated. } void Lua::SetPrinter(LuaPrinter * lp) { lprinter = lp; } void Lua::puts(const char * msg) { lprinter->puts(msg); } Lua * Lua::Father() { return father ? father : this; } Lua * Lua::spawn_from_thread(lua_State * __L) { Lua * L = new Lua(__L); L->father = Father(); return L; } #define IntPoint(p) ((unsigned int)(lu_mem)(p)) typedef size_t lu_mem; void Lua::weaken() { if (_is_thread) { push(); // -1 = nil push("BLUA_THREADS"); // -2 = nil, -1 = "BLUA_THREADS" copy(); // -3 = nil, -2 = "BLUA_THREADS", -1 = "BLUA_THREADS" gettable(LUA_REGISTRYINDEX); // -3 = nil, -2 = "BLUA_THREADS", -1 = BLUA_THREADS push((lua_Number) IntPoint(L)); // -4 = nil, -3 = "BLUA_THREADS", -2 = BLUA_THREADS, -1 = key-Lt copy(-4); // -5 = nil, -4 = "BLUA_THREADS", -3 = BLUA_THREADS, -2 = key-Lt, -1 = thread settable(); // -3 = nil, -2 = "BLUA_THREADS", -1 = BLUA_THREADS settable(LUA_REGISTRYINDEX); // -1 = thread pop(); } } Lua::~Lua() { weaken(); if (!_is_thread) lua_close(L); L = 0; } bool Lua::is_protected() { return _protected; } Lua::Lua(const Lua & l) throw (GeneralException) { throw GeneralException("Error: can't duplicate a Lua object."); } void Lua::open_base() { luaopen_base(L); push("mkdir"); push(LuaStatics::mkdir); settable(); push("time"); push(LuaStatics::time); settable(); lua_pop(L, 1); 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("sleep"); push(LuaStatics::sleep); settable(LUA_GLOBALSINDEX); } void Lua::open_table() { luaopen_table(L); lua_pop(L, 1); } void Lua::open_io(bool safe) { luaopen_io(L); lua_pop(L, 1); } void Lua::open_string() { luaopen_string(L); push("iconv"); push(LuaStatics::iconv); settable(); lua_pop(L, 1); } void Lua::open_math() { luaopen_math(L); lua_pop(L, 1); } void Lua::open_debug() { luaopen_debug(L); lua_pop(L, 1); } extern "C" { LUALIB_API int (luaopen_dir) (lua_State *L); } void Lua::open_dir() { luaopen_dir(L); lua_pop(L, 1); } int Lua::wrap_open(openlualib_t open) { return open(L); } void Lua::declarefunc(const String & name, lua_CFunction f, int i) { lua_pushstring(L, name.to_charp()); lua_pushcfunction(L, f); lua_settable(L, i); } void Lua::call(const String & f, int i, int nargs, int nresults) { lua_pushstring(L, f.to_charp()); lua_gettable(L, i); lua_insert(L, -1 - nargs - nresults); call(nargs, nresults); } void Lua::call(int nargs, int nresults) throw(GeneralException) { int r; lua_pushcfunction(L, LuaStatics::luaerror); lua_insert(L, 1); _protected = true; r = lua_pcall(L, nargs, nresults, 1); _protected = false; lua_remove(L, 1); switch(r) { case 0: return; 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("Unknow error while running LUA code (err code: " + String(r) + ")"); } } void Lua::push() { lua_pushnil(L); } void Lua::push(lua_Number n) { lua_pushnumber(L, n); } void Lua::push(const String & s) { lua_pushlstring(L, s.to_charp(), s.strlen()); } void Lua::push(bool b) { lua_pushboolean(L, b); } void Lua::push(const char * s, int size) { if (size < 0) { lua_pushstring(L, s); } else { lua_pushlstring(L, s, size); } } void Lua::push(void * p) { lua_pushlightuserdata(L, p); } void Lua::push(lua_CFunction f, int n) { lua_pushcclosure(L, f, n); } void Lua::pop(int n) { lua_pop(L, n); } int Lua::next(int t) { return lua_next(L, t); } void Lua::copy(int n) { lua_pushvalue(L, n); } void Lua::remove(int n) { lua_remove(L, n); } void Lua::insert(int n) { lua_insert(L, n); } void Lua::replace(int n) { lua_replace(L, n); } void Lua::newtable() { lua_newtable(L); } void * Lua::newuser(size_t s) { return lua_newuserdata(L, s); } void Lua::settable(int i, bool raw) { if (raw) { lua_rawset(L, i); } else { lua_settable(L, i); } } void Lua::gettable(int i, bool raw) { if (raw) { lua_rawget(L, i); } else { lua_gettable(L, i); } } void Lua::setvar() { lua_settable(L, LUA_GLOBALSINDEX); } int Lua::gettop() { return lua_gettop(L); } void Lua::getglobal(const String & name) throw (GeneralException) { Buffer b; b << "return " + name; try { load(&b, false); call(0, 1); } catch (LuaException &) { throw LuaException("Error finding global variable `" + name + "'"); } } void Lua::push_lua_context() { 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 Lua::error(const String & msg) { push(msg); lua_error(L); } int Lua::status() { return 0; } int Lua::type(int i) { return lua_type(L, i); } bool Lua::isnil(int i) { return lua_isnil(L, i); } bool Lua::isboolean(int i) { return lua_isboolean(L, i); } bool Lua::isnumber(int i) { return lua_isnumber(L, i); } bool Lua::isstring(int i) { return lua_isstring(L, i); } bool Lua::istable(int i) { return lua_istable(L, i); } bool Lua::isfunction(int i) { return lua_isfunction(L, i); } bool Lua::iscfunction(int i) { return lua_iscfunction(L, i); } bool Lua::isuserdata(int i) { return lua_isuserdata(L, i); } bool Lua::islightuserdata(int i) { return lua_islightuserdata(L, i); } bool Lua::isobject(int i) { bool r = false; if (istable(i)) { push("__obj"); gettable(i); r = isuserdata(); pop(); } else { r = isnil(i); } return r; } bool Lua::toboolean(int i) { return lua_toboolean(L, i); } lua_Number Lua::tonumber(int i) { return lua_tonumber(L, i); } String 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 : "", l); } lua_CFunction Lua::tocfunction(int i) { return lua_tocfunction(L, i); } void * Lua::touserdata(int i) { return lua_touserdata(L, i); } Lua * Lua::tothread(int i) { return find(lua_tothread(L, i)); } struct LoadF { Handle * f; char buff[BUFFERSIZE]; }; const char * LuaStatics::getF(lua_State * L, void * ud, size_t * size) { LoadF *lf = (LoadF *)ud; (void)L; *size = lf->f->read(lf->buff, BUFFERSIZE); return (*size > 0) ? lf->buff : NULL; } struct DumpF { Handle * f; }; int LuaStatics::putF(lua_State * L, const void * p, size_t size, void * ud) { DumpF *lf = (DumpF *)ud; (void)L; return (lf->f->write(p, size) != size) && (size != 0); } String Lua::escape_string(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 Lua::load(Handle * h, bool docall) throw (GeneralException) { LoadF lf; int status; lf.f = h; status = lua_load(L, LuaStatics::getF, &lf, h->GetName().to_charp()); if (status) { push_lua_context(); showerror(); throw LuaException("Error loading lua chunk from Handle `" + h->GetName() + "'"); } if (docall) { call(); } } void 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) { push_lua_context(); showerror(); throw LuaException("Error loading lua string `" + s + "'"); } if (docall) { call(); } } //extern "C" void luacmain(lua_State * L, int stripping, lua_Chunkwriter w, void * uD, int listing); void Lua::dump(Handle * h, bool strip, int listing) { // DumpF lf; // // lf.f = h; // // luacmain(L, strip, LuaStatics::putF, &lf, listing); } void Lua::dumpvars(Handle * h, const String & prefix, int i) { Buffer b; b << prefix << " = {\n"; dumpvars_r(&b, i); b << "}\n"; b.copyto(h); } void Lua::dumpvars_r(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++; 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) << " "; } 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 = "[\"" + escape_string(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) << 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) << "-- "; (*h) << 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) << "nil,\n"; case LUA_TNUMBER: t.set("%.14g,\n", lua_tonumber(L, -1)); (*h) << t; break; case LUA_TBOOLEAN: (*h) << (lua_toboolean(L, -1) ? "true" : "false") << ",\n"; break; case LUA_TSTRING: (*h) << "\"" << escape_string(lua_tostring(L, -1)) << "\",\n"; break; case LUA_TTABLE: (*h) << "{\n"; dumpvars_r(h, -1, depth); for (j = 0; j < depth; j++) { (*h) << " "; } (*h) << "},\n"; break; case LUA_TFUNCTION: (*h) << "function() ... end\n"; break; case LUA_TUSERDATA: (*h) << "userdata ...\n"; break; case LUA_TTHREAD: (*h) << "thread ...\n"; break; default: throw LuaException("Internal error: got unknow index for value while dumpvars."); } pop(); } } Lua * Lua::thread(bool saveit) { lua_State * L1 = lua_newthread(L); Lua * Lt = find(L1); if (saveit) { push("BLUA_THREADS"); // -2 = thread, -1 = "BLUA_THREADS" copy(); // -3 = thread, -2 = "BLUA_THREADS", -1 = "BLUA_THREADS" gettable(LUA_REGISTRYINDEX); // -3 = thread, -2 = "BLUA_THREADS", -1 = BLUA_THREADS push((lua_Number) IntPoint(Lt)); // -4 = thread, -3 = "BLUA_THREADS", -2 = BLUA_THREADS, -1 = key-Lt copy(-4); // -5 = thread, -4 = "BLUA_THREADS", -3 = BLUA_THREADS, -2 = key-Lt, -1 = thread settable(); // -3 = thread, -2 = "BLUA_THREADS", -1 = BLUA_THREADS settable(LUA_REGISTRYINDEX); // -1 = thread } return Lt; } Lua * Lua::thread(const String & code, int nargs, bool saveit) { Lua * L1; L1 = thread(saveit); L1->resume(code, nargs); return L1; } Lua * Lua::thread(Handle * h, int nargs, bool saveit) { Lua * L1; L1 = thread(saveit); L1->resume(h, nargs); return L1; } int Lua::yield(int nresults) { return lua_yield(L, nresults); } bool Lua::resume(int nargs) throw (GeneralException) { int r; _protected = true; r = lua_resume(L, nargs); _protected = false; if ((r != 0) && (r != LUA_YIELD)) { push_lua_context(); 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("Unknow error while running LUA code (err code: " + String(r) + ")"); } } bool Lua::resume(Handle * h, int nargs) { bool r; load(h, false); r = resume(nargs); return r; } bool Lua::resume(const String & s, int nargs) { bool r; load(s, false); r = resume(nargs); return r; } Lua * Lua::find(lua_State * __L) throw (GeneralException) { std::map::iterator i; if ((i = lualist.find(__L)) == lualist.end()) { throw GeneralException("Unable to find the Lua object for this context"); } return i->second; } void Lua::showstack(int level) { int n = lua_gettop(L); int i; String t; if (n == 0) { printm(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"; } printr(level, String(i) + ": " + t + "\n"); } } void Lua::showerror() { printm(M_ERROR, "Lua object: Got an LUA error, inspecting stack.\n"); showstack(M_ERROR); } int Lua::getmetatable(int i) { return lua_getmetatable(L, i); } int Lua::setmetatable(int i) { return lua_setmetatable(L, i); } int Lua::sethook(lua_Hook func, int mask, int count) { return lua_sethook(L, func, mask, count); } void Lua::do_break() { // lua_break(L); } void Lua::lock() { } void Lua::unlock() { } void Lua::openlib(const String & libname, const luaL_reg *l, int nup) { luaL_openlib(L, libname.to_charp(), l, nup); } /* ***TODO*** handle the new garbage collection system. */ void Lua::setgcthreshold(int newthreshold) { // lua_setgcthreshold(L, newthreshold); } int Lua::getgcthreshold() { // return lua_getgcthreshold(L); return 0; } int Lua::getgccount() { return lua_getgccount(L); } void LuaObject::push(Lua * L) throw (GeneralException) { if (pushed && wantdestruct) { throw GeneralException("Error: object is owned by the LUA script and can not be pushed."); } L->newtable(); pushmembers(L); pushed = true; } void LuaObject::pushme(Lua * L, void * o, const String & objname, bool obj) { void ** u; bool * b; L->push("__obj"); u = (void **) L->newuser(sizeof(o) + sizeof(bool)); *u = o; b = (bool *) (u + 1); *b = obj; L->settable(-3, true); if (objname != "") { L->push("__objname"); L->push(objname); L->settable(-3, true); } } void * LuaObject::getme(Lua * L, int i) { void ** r = 0; if (L->istable(i)) { L->push("__obj"); L->gettable(i, true); if (!(r = (void **) L->touserdata())) L->error("Table is not an object."); if (!*r) L->error("Object already destroyed."); L->pop(); } else if (L->isnil(i)) { r = 0; } else { L->error("Not an object (not even a table)."); } return r ? *r : 0; } void LuaObject::pushit(Lua * L, const String & s, lua_CFunction f) { L->push(s); L->push(f); L->settable(-3, true); } void LuaObject::pushmeta(Lua * L, const String & s, lua_CFunction f) { if (!L->getmetatable()) { L->newtable(); } L->push(s); L->push(f); L->settable(); L->setmetatable(); } void LuaObject::pushdestruct(Lua * L) throw (GeneralException) { if (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); wantdestruct = true; } LuaException::LuaException(String fn) : GeneralException(fn) { } LuaException::LuaException() { }