/* * Baltisot * Copyright (C) 1999-2003 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 */ /* $Id: BLua.cc,v 1.30 2005-02-17 08:39:06 pixel Exp $ */ #include #include "BLua.h" #include #ifndef BUFFERSIZE #define BUFFERSIZE 2048 #endif 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 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 *); }; 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; except 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 = 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; } Lua::Lua() : L(lua_open()) { lualist[L] = this; lua_atpanic(L, LuaStatics::luapanic); lua_setcallwrap(L, LuaStatics::callwrap); 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); } Lua::Lua(lua_State * _L) : L(_L), _protected(false) { lualist[L] = this; lua_atpanic(L, LuaStatics::luapanic); } Lua::~Lua() { lua_setgcthreshold(L, 0); lua_close(L); } 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); lua_pop(L, 1); } void Lua::open_table() { luaopen_table(L); lua_pop(L, 1); } void Lua::open_io() { luaopen_io(L); lua_pop(L, 1); } void Lua::open_string() { luaopen_string(L); 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); } void Lua::open_dir() { luaopen_dir(L); lua_pop(L, 1); } 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."); default: throw LuaException("Unknow error while running LUA code."); } } 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(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); } 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::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::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) { if (isnil(i)) return String(); return String(lua_tostring(L, i)); } 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; } 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(); } extern "C" void luacmain(lua_State * L, int stripping, lua_Chunkwriter w, void * uD); void Lua::dump(Handle * h, bool strip) { DumpF lf; lf.f = h; luacmain(L, strip, LuaStatics::putF, &lf); } Lua * Lua::thread() { return new Lua(lua_newthread(L)); } int Lua::yield(int nargs) { return lua_yield(L, nargs); } int Lua::resume(int nresults) { return lua_resume(L, nresults); } 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::showerror() { int n = lua_gettop(L); int i; String t; printm(M_ERROR, "Lua object: Got an LUA error, inspecting stack.\n"); if (n == 0) { printm(M_ERROR, "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"; } printm(M_ERROR, String(i) + ": " + t + "\n"); } } 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 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, 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); } 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(); } 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); Base * b = (Base *) LuaObject::getme(L); delete b; 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 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() { }