From c7516781796f8be8931e798380facf8ef631efaa Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Sat, 5 Dec 2020 18:57:53 -0500 Subject: [PATCH] Changed calling conventions again --- luprex/build.bat | 3 +- luprex/syscpp/globaldb.cpp | 11 +- luprex/syscpp/globaldb.hpp | 11 +- luprex/syscpp/idalloc.cpp | 19 +++ luprex/syscpp/idalloc.hpp | 12 ++ luprex/syscpp/luastack.cpp | 63 ++++------ luprex/syscpp/luastack.hpp | 175 ++++++++++++++++++-------- luprex/syscpp/main.cpp | 22 +--- luprex/syscpp/source.cpp | 245 +++++++++++++++++++++++++------------ luprex/syscpp/source.hpp | 13 +- luprex/syscpp/table.cpp | 9 +- luprex/syscpp/table.hpp | 8 +- luprex/syscpp/util.cpp | 28 ++++- luprex/syscpp/util.hpp | 3 + 14 files changed, 403 insertions(+), 219 deletions(-) create mode 100644 luprex/syscpp/idalloc.cpp create mode 100644 luprex/syscpp/idalloc.hpp diff --git a/luprex/build.bat b/luprex/build.bat index 48836a16..4cc92dd6 100644 --- a/luprex/build.bat +++ b/luprex/build.bat @@ -1 +1,2 @@ -g++ -std=c++17 -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/luastack.cpp syscpp/source.cpp syscpp/table.cpp syscpp/globaldb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp +clear +g++ -std=c++17 -Wall -g -o main syscpp/util.cpp syscpp/main.cpp syscpp/luastack.cpp syscpp/source.cpp syscpp/table.cpp syscpp/idalloc.cpp syscpp/globaldb.cpp -Iinc -Llib lib/libluajit-dbg.a -Isyscpp diff --git a/luprex/syscpp/globaldb.cpp b/luprex/syscpp/globaldb.cpp index ef12b476..e4c7149b 100644 --- a/luprex/syscpp/globaldb.cpp +++ b/luprex/syscpp/globaldb.cpp @@ -1,3 +1,4 @@ +#include "luastack.hpp" #include "globaldb.hpp" #include "table.hpp" @@ -8,7 +9,7 @@ // if globalname is already present, and not a table, error. // if globalname is not present, create and initialize it. // -int lpx_globaldb_global(lua_State *L) { +LuaDefineGlobalFunction(globaldb_global) { LuaArg globalname; LuaRet globaltab; LuaVar globaldb; @@ -17,10 +18,10 @@ int lpx_globaldb_global(lua_State *L) { LS.checktype(globalname, LUA_TSTRING); // Get a pointer to the globaldb. - LS.getfield(globaldb, LUA_REGISTRYINDEX, "globaldb"); + LS.getfield(globaldb, LuaRegistry, "globaldb"); if (!LS.istable(globaldb)) { LS.newtable(globaldb); - LS.setfield(LUA_REGISTRYINDEX, "globaldb", globaldb); + LS.setfield(LuaRegistry, "globaldb", globaldb); } // Get the globaltab from the globaldb, sanity check it. @@ -37,7 +38,3 @@ int lpx_globaldb_global(lua_State *L) { LS.setfield(globaltab, "__global", globalname); return LS.result(); } - -void luaopen_lpx_globaldb(lua_State *L) { - LuaStack::reg(L, 0, "global", LuaArgCheck<1, lpx_globaldb_global>); -} diff --git a/luprex/syscpp/globaldb.hpp b/luprex/syscpp/globaldb.hpp index 6d37f194..ddc1109c 100644 --- a/luprex/syscpp/globaldb.hpp +++ b/luprex/syscpp/globaldb.hpp @@ -1,18 +1,11 @@ -// ClassDB - code to manipulate class databases. -// -// It would have been easier to write this in Lua, but since every -// lua module in the system depends on it, it's safer to have it -// preloaded before we even open any of the lua files. -// + #ifndef GLOBALDB_HPP #define GLOBALDB_HPP #include "luastack.hpp" -int lpx_globaldb_global(lua_State *L); - -void luaopen_lpx_globaldb(lua_State *L); +int globaldb_global(lua_State *L); #endif // GLOBALDB_HPP diff --git a/luprex/syscpp/idalloc.cpp b/luprex/syscpp/idalloc.cpp new file mode 100644 index 00000000..3afd215a --- /dev/null +++ b/luprex/syscpp/idalloc.cpp @@ -0,0 +1,19 @@ +#include "luastack.hpp" +#include "idalloc.hpp" + +LuaDefineGlobalMethod(idalloc_getnextid) { + LuaRet value; + LuaStack LS(L, value); + double id = lua_getnextid(L); + LS.set(value, id); + return LS.result(); +} + +LuaDefineGlobalMethod(idalloc_setnextid) { + LuaArg value; + LuaStack LS(L, value); + double id = LS.tonumber(value); + lua_setnextid(L, int64_t(id)); + return LS.result(); +} + diff --git a/luprex/syscpp/idalloc.hpp b/luprex/syscpp/idalloc.hpp new file mode 100644 index 00000000..34a3ba04 --- /dev/null +++ b/luprex/syscpp/idalloc.hpp @@ -0,0 +1,12 @@ + + +#ifndef IDALLOC_HPP +#define IDALLOC_HPP + +#include "luastack.hpp" + +int idalloc_getnextid(lua_State *L); +int idalloc_setnextid(lua_State *L); + +#endif // IDALLOC_HPP + diff --git a/luprex/syscpp/luastack.cpp b/luprex/syscpp/luastack.cpp index 6126fa4b..595aa9ce 100644 --- a/luprex/syscpp/luastack.cpp +++ b/luprex/syscpp/luastack.cpp @@ -1,7 +1,8 @@ #include "luastack.hpp" -LuaSlot LuaRegistry(LUA_REGISTRYINDEX); -LuaSlot LuaGlobals(LUA_GLOBALSINDEX); +LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); +LuaSpecial LuaGlobals(LUA_GLOBALSINDEX); +LuaNilMarker LuaNil; void LuaStack::count_slots_finalize(int narg, int nvar, int nret) { narg_ = narg; @@ -38,44 +39,22 @@ int LuaStack::result() { return nret_; } -void LuaStack::reg(lua_State *L, const char *classname, const char *funcname, lua_CFunction fn) { - int top = lua_gettop(L); - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_Reg reg; - reg.name = funcname; - reg.func = fn; - luaL_register(L, classname, ®); - lua_settop(L, top); +LuaFunctionReg *LuaFunctionReg::LuaFunctionRegistry; + +LuaFunctionReg::LuaFunctionReg(int m, const char *n, lua_CFunction f) { + mode_ = m; + name_ = n; + func_ = f; + next_ = LuaFunctionRegistry; + LuaFunctionRegistry = this; } -void LuaStack::setnil(LuaSlot target) const { - lua_pushnil(L_); - lua_replace(L_, target); -} - -void LuaStack::setboolean(LuaSlot target, bool b) const { - lua_pushboolean(L_, b ? 1 : 0); - lua_replace(L_, target); -} - -void LuaStack::setboolean(LuaSlot target, int b) const { - lua_pushboolean(L_, b); - lua_replace(L_, target); -} - -void LuaStack::setstring(LuaSlot target, const char *str) const { - lua_pushstring(L_, str); - lua_replace(L_, target); -} - -void LuaStack::setstring(LuaSlot target, const std::string &str) const { - lua_pushlstring(L_, str.c_str(), str.size()); - lua_replace(L_, target); -} - -void LuaStack::setnumber(LuaSlot target, double value) const { - lua_pushnumber(L_, value); - lua_replace(L_, target); +LuaFunctionReg::List LuaFunctionReg::all() { + LuaFunctionReg::List result; + for (const LuaFunctionReg *r = LuaFunctionRegistry; r != 0; r = r->next_) { + result.push_back(r); + } + return result; } std::string LuaStack::tostring(LuaSlot s) const { @@ -123,3 +102,11 @@ void LuaStack::newtable(LuaSlot target) const { lua_newtable(L_); lua_replace(L_, target); } + +void LuaStack::check_nret(int xnret, int otop, int nret) const { + int ntop = lua_gettop(L_); + if ((nret != xnret)||(ntop != otop + xnret)) { + luaL_error(L_, "expected %d return values", xnret); + } +} + \ No newline at end of file diff --git a/luprex/syscpp/luastack.hpp b/luprex/syscpp/luastack.hpp index 1c80aef1..9ceadb8e 100644 --- a/luprex/syscpp/luastack.hpp +++ b/luprex/syscpp/luastack.hpp @@ -11,28 +11,7 @@ extern "C" { } #include - -// LuaArgCheck -// -// This is a template that, when given a lua_CFunction that doesn't -// verify the number of arguments on the stack, returns a new lua_CFunction -// that *does* verify the number of arguments on the stack. -// -// In general, your lua_CFunctions should not verify the number of -// arguments on the stack. Instead, they should assume that the stack -// contains arbitrary stuff underneath the arguments. That makes your -// lua_CFunctions callable from both C and Lua. But it is desirable to -// check arguments (only when calling from lua), so it is useful to use -// LuaArgCheck when turning a lua_CFunction into a lua closure. -// -template -int LuaArgCheck(lua_State *L) { - if (lua_gettop(L) != N) { - lua_pushfstring(L, "expected %d arguments, got %d", N, lua_gettop(L)); - lua_error(L); - } - FN(L); -} +#include // LuaSlot // @@ -53,10 +32,6 @@ private: return index_; } public: - LuaSlot(int n) { - index_ = n; - } - LuaSlot() { index_ = 0; } @@ -72,8 +47,15 @@ class LuaArg : public LuaSlot {}; class LuaRet : public LuaSlot {}; class LuaVar : public LuaSlot {}; -extern LuaSlot LuaRegistry; -extern LuaSlot LuaGlobals; +class LuaSpecial : public LuaSlot { +public: + LuaSpecial(int n) { + index_ = n; + } +}; + +extern LuaSpecial LuaRegistry; +extern LuaSpecial LuaGlobals; class LuaUpvalue : public LuaSlot { public: @@ -82,6 +64,9 @@ class LuaUpvalue : public LuaSlot { } }; +class LuaNilMarker {}; +extern LuaNilMarker LuaNil; + class LuaStack { private: int narg_; @@ -150,6 +135,9 @@ public: LuaStack(lua_State *L, SS & ... stackslots) { L_ = L; count_slots<0, 0, 0>(stackslots...); + if (lua_gettop(L) < narg_) { + luaL_error(L, "not enough arguments on stack"); + } assign_slots(argpos_, varpos_, retpos_, stackslots...); clear_frame(); } @@ -158,15 +146,62 @@ public: int result(); -public: - static void reg(lua_State *L, const char *classname, const char *funcname, lua_CFunction fn); +private: + // Push any value on the stack, by type. + void push_any_value(LuaNilMarker s) const { lua_pushnil(L_); } + void push_any_value(LuaSlot s) const { lua_pushvalue(L_, s); } + void push_any_value(const std::string &s) const { lua_pushlstring(L_, s.c_str(), s.size()); } + void push_any_value(const char *s) const { lua_pushstring(L_, s); } + void push_any_value(double s) const { lua_pushnumber(L_, s); } + void push_any_value(int s) const { lua_pushnumber(L_, s); } + void push_any_value(bool b) const { lua_pushboolean(L_, b ? 1:0); } - void setnil(LuaSlot target) const; - void setboolean(LuaSlot target, bool b) const; - void setboolean(LuaSlot target, int b) const; - void setstring(LuaSlot target, const char *str) const; - void setstring(LuaSlot target, const std::string &str) const; - void setnumber(LuaSlot target, double value) const; + // Push multiple values on the stack, in order, by type. + template + void push_any_values(T0 arg0, T... args) { + push_any_value(arg0); + push_any_values(args...); + } + void push_any_values() { + } + +private: + // Push everything after the CFunction. Used in the implementation + // of the 'call' template function. + template + void push_after_cfunction(LuaSlot s, T... args) { + push_after_cfunction(args...); + } + template + void push_after_cfunction(lua_CFunction f, T... args) { + push_any_values(args...); + } + + // Call the CFunction, verify that the number of return values is + // correct, and pop everything before the CFunction. Used in the + // implementation of the 'call' template function. + template + void call_cfunction_and_pop(int otop, LuaSlot s, T... args) { + call_cfunction_and_pop(otop, args...); + lua_replace(L_, s); + } + template + void call_cfunction_and_pop(int otop, lua_CFunction fn, T... args) { + int nret = fn(L_); + check_nret(NRET, otop, nret); + } + + // Check number of return values: xpected number of return values, + // original stack top, and number of declared return values. + void check_nret(int xnret, int otop, int nret) const; +public: + template + void set(LuaSlot target, T value) const { + push_any_value(value); + lua_replace(L_, target); + } + + int type(LuaSlot s) const { return lua_type(L_, s); } bool isboolean(LuaSlot s) const { return lua_isboolean(L_, s); } bool iscfunction(LuaSlot s) const { return lua_iscfunction(L_, s); } @@ -199,40 +234,72 @@ public: int next(LuaSlot tab, LuaSlot key, LuaSlot value) const; - void rawget(LuaSlot target, LuaSlot tab, LuaSlot key) const { - lua_pushvalue(L_, key); + template + void rawget(LuaSlot target, LuaSlot tab, KT key) const { + push_any_value(key); lua_rawget(L_, tab); lua_replace(L_, target); } - void rawset(LuaSlot tab, LuaSlot key, LuaSlot value) const { - lua_pushvalue(L_, key); - lua_pushvalue(L_, value); + template + void rawset(LuaSlot tab, KT key, VT value) const { + push_any_value(key); + push_any_value(value); lua_rawset(L_, tab); } - void setfield(LuaSlot tab, const char *field, LuaSlot value) const { - lua_pushvalue(L_, value); + template + void setfield(LuaSlot tab, const char *field, VT value) const { + push_any_value(value); lua_setfield(L_, tab, field); } - void setfield(LuaSlot tab, const char *field, const char *value) const { - lua_pushstring(L_, value); - lua_setfield(L_, tab, field); - } - - void setfield(LuaSlot tab, const char *field, const std::string &value) const { - lua_pushlstring(L_, value.c_str(), value.size()); - lua_setfield(L_, tab, field); - } - void getfield(LuaSlot target, LuaSlot tab, const char *field) const { lua_getfield(L_, tab, field); lua_replace(L_, target); } - + // Call invokes any C function. It pushes the arguments on the stack, + // calls the cfunction, verifies that the number of return values is as + // expected, and pops the return values into LuaVars. + template + void call(T... args) { + int otop = lua_gettop(L_); + push_after_cfunction(args...); + call_cfunction_and_pop<0>(otop, args...); + } }; +class LuaFunctionReg { +private: + int mode_; + const char *name_; + lua_CFunction func_; + LuaFunctionReg *next_; + + static LuaFunctionReg *LuaFunctionRegistry; + +public: + using List = std::vector; + + LuaFunctionReg(int m, const char *n, lua_CFunction f); + static List all(); + + int get_mode() const { return mode_; } + const char *get_name() const { return name_; } + lua_CFunction get_func() const { return func_; } +}; + +#define LuaDefineHidden(name) LuaDefineCore(name, 0) +#define LuaDefineGlobalFunction(name) LuaDefineCore(name, 1) +#define LuaDefineGlobalMethod(name) LuaDefineCore(name, 2) +#define LuaDefineClassMethod(name) LuaDefineCore(name, 3) + +#define LuaDefineCore(name, mode) \ + int name(lua_State *L); \ + LuaFunctionReg reg_##name(mode, #name, name); \ + int name(lua_State *L) + + #endif // LUASTACK_HPP diff --git a/luprex/syscpp/main.cpp b/luprex/syscpp/main.cpp index ab119ba6..17c82afc 100644 --- a/luprex/syscpp/main.cpp +++ b/luprex/syscpp/main.cpp @@ -14,8 +14,6 @@ #include #include "luastack.hpp" #include "util.hpp" -#include "table.hpp" -#include "globaldb.hpp" #include "source.hpp" @@ -200,21 +198,11 @@ static int pmain(lua_State *L) LUAJIT_VERSION_SYM(); /* Linker-enforced version check. */ - lua_gc(L, LUA_GCSTOP, 0); - luaopen_base(L); - // luaopen_package(L); // Omitted because we use our own package system. - luaopen_table(L); // Standard table operations. - luaopen_lpx_table(L); // Supplemental table operations. - // luaopen_io(L); // Not safe for the sandbox. - // luaopen_os(L); // Not safe for the sandbox. - luaopen_string(L); - luaopen_math(L); - // luaopen_debug(L); // Not safe for the sandbox. - luaopen_bit(L); - // luaopen_jit(L); // Don't know what it's for. - luaopen_lpx_globaldb(L); - luaopen_lpx_source(L); - lua_gc(L, LUA_GCRESTART, -1); + // Load the lua source. + source_update(L); + + // Rebuild the global environment and class database. + source_rebuild(L); dotty(L); return 0; diff --git a/luprex/syscpp/source.cpp b/luprex/syscpp/source.cpp index 7524bfb7..60ec2e95 100644 --- a/luprex/syscpp/source.cpp +++ b/luprex/syscpp/source.cpp @@ -1,20 +1,16 @@ #include #include #include +#include #include #include #include -#include -#include #include "util.hpp" -#include "table.hpp" -#ifndef WIN32 -#include -#endif + #include "luastack.hpp" -#ifdef WIN32 -#define stat _stat -#endif +#include "table.hpp" +#include "source.hpp" + //////////////////////////////////////////////////////////// @@ -23,7 +19,7 @@ // to file info. A file info is a lua table containing: // // name: filename as a string -// mtime: file modification time as human-readable string +// fingerprint: file modification time as human-readable string // code: the entire contents of the source file as a string // error: a syntax error message, or nil // hash: 128-bit hash of the code @@ -39,29 +35,11 @@ // //////////////////////////////////////////////////////////// -static std::string get_mtime(const std::string &fn) { - struct stat result; - if(stat(fn.c_str(), &result)==0) - { - std::stringstream ss; - ss << result.st_mtime; - return ss.str(); - } - return ""; -} - -static std::string get_code(const std::string &fn) { - std::ifstream fs(fn); - std::stringstream buffer; - buffer << fs.rdbuf(); - return buffer.str(); -} - -int lpx_source_updatefile(lua_State *L) { +LuaDefineHidden(source_updatefile) { LuaArg source, fn; LuaRet info; - LuaVar mtime, null; - LuaStack LS(L, source, fn, info, mtime, null); + LuaVar fingerprint, null; + LuaStack LS(L, source, fn, info, fingerprint, null); // Get the existing info table from the source DB. if (LS.istable(source)) { @@ -74,42 +52,43 @@ int lpx_source_updatefile(lua_State *L) { } // If the file modification is wrong, update - // these fields: code, mtime, closure, error + // these fields: code, fingerprint, closure, error // Otherwise, update nothing. std::string cfn = LS.tostring(fn); - LS.getfield(mtime, info, "mtime"); - std::string old_mtime; - if (LS.isstring(mtime)) { - old_mtime = LS.tostring(mtime); + LS.getfield(fingerprint, info, "fingerprint"); + std::string old_fingerprint; + if (LS.isstring(fingerprint)) { + old_fingerprint = LS.tostring(fingerprint); } std::cerr << "Probing " << cfn << std::endl; - std::string new_mtime = get_mtime("syslua/" + cfn); - LS.setnil(null); - if ((old_mtime == "") || (old_mtime != new_mtime)) { + std::string new_fingerprint = util::get_file_fingerprint("syslua/" + cfn); + LS.set(null, LuaNil); + if ((old_fingerprint == "") || (old_fingerprint != new_fingerprint)) { std::cerr << "Rereading " << cfn << std::endl; - std::string ccode = get_code("syslua/" + cfn); + std::string ccode = util::get_file_contents("syslua/" + cfn); LS.setfield(info, "name", fn); - LS.setfield(info, "mtime", new_mtime); + LS.setfield(info, "fingerprint", new_fingerprint); LS.setfield(info, "code", ccode); - if ((new_mtime == "")||(ccode == "")) { - LS.setfield(info, "error", "cannot read source file"); - LS.setfield(info, "closure", null); - } else if (luaL_loadbuffer(L, ccode.c_str(), ccode.size(), cfn.c_str()) == 0) { - lua_setfield(L, info.index(), "closure"); - LS.setfield(info, "error", null); + if ((new_fingerprint == "")||(ccode == "")) { + LS.setfield(info, "loadresult", "cannot read source file"); } else { - lua_setfield(L, info.index(), "error"); - LS.setfield(info, "closure", null); + std::string chunk = "=" + cfn; + luaL_loadbuffer(L, ccode.c_str(), ccode.size(), chunk.c_str()); + lua_setfield(L, info.index(), "loadresult"); } } return LS.result(); } -int lpx_source_updateall(lua_State *L) { - LuaArg source; - LuaRet newdb; - LuaVar info, fn, seq; - LuaStack LS(L, source, newdb, info, fn, seq); +LuaDefineHidden(source_update) { + LuaVar sourcedb, newdb, info, fn, seq; + LuaStack LS(L, newdb, sourcedb, info, fn, seq); + + // Get the (old) source database. + LS.getfield(sourcedb, LuaRegistry, "sourcedb"); + if (!LS.istable(sourcedb)) { + LS.newtable(sourcedb); + } // Read the list of filenames. std::string ctrl = "syslua/control.lst"; @@ -120,20 +99,20 @@ int lpx_source_updateall(lua_State *L) { // Process the files one by one. LS.newtable(newdb); - for (int i = 0; i < filenames.size(); i++) { - LS.setstring(fn, filenames[i]); + for (int i = 0; i < int(filenames.size()); i++) { + LS.set(fn, filenames[i]); // Call source_updatefile to get the updated info for one file. - lua_pushvalue(L, source.index()); - lua_pushvalue(L, fn.index()); - lpx_source_updatefile(L); - lua_replace(L, info.index()); + LS.call(info, source_updatefile, sourcedb, fn); // Insert the sequence number and put finalized info into the new database. - LS.setnumber(seq, i + 1); + LS.set(seq, i + 1); LS.setfield(info, "sequence", seq); LS.rawset(newdb, fn, info); } + + // Store the new source db. + LS.setfield(LuaRegistry, "sourcedb", newdb); return LS.result(); } @@ -144,7 +123,7 @@ int lpx_source_updateall(lua_State *L) { // if classname is already present, and not a table, error. // if classname is not present, create and initialize it. // -int lpx_source_class(lua_State *L) { +LuaDefineGlobalFunction(source_class) { LuaArg classname; LuaRet classtab; LuaVar classdb, action; @@ -153,10 +132,10 @@ int lpx_source_class(lua_State *L) { LS.checktype(classname, LUA_TSTRING); // Get a pointer to the classdb. - LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb"); + LS.getfield(classdb, LuaRegistry, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); - LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb); + LS.setfield(LuaRegistry, "classdb", classdb); } // Get the classtab from the classdb, sanity check it. @@ -183,31 +162,29 @@ int lpx_source_class(lua_State *L) { // it simply deletes all keys. That way, if somebody has a pointer // to a class, the pointer is not invalidated. // -int lpx_source_resetclasses(lua_State *L) { +LuaDefineHidden(source_reset_classes) { LuaVar classdb, classname, classtab, action, key; LuaStack LS(L, classname, classtab, classdb, action, key); // Get a pointer to the classdb. - LS.getfield(classdb, LUA_REGISTRYINDEX, "classdb"); + LS.getfield(classdb, LuaRegistry, "classdb"); if (!LS.istable(classdb)) { LS.newtable(classdb); - LS.setfield(LUA_REGISTRYINDEX, "classdb", classdb); + LS.setfield(LuaRegistry, "classdb", classdb); } // Iterate over the classdb, clearing it. - LS.setnil(classname); + LS.set(classname, LuaNil); while (LS.next(classdb, classname, classtab) != 0) { if (LS.istable(classtab)) { - LS.setstring(key, "action"); + LS.set(key, "action"); LS.rawget(action, classtab, key); if (LS.istable(action)) { - lua_pushvalue(L, action.index()); - lpx_table_clear(L); + LS.call(table_clear, action); } else { LS.newtable(action); } - lua_pushvalue(L, classtab.index()); - lpx_table_clear(L); + LS.call(table_clear, classtab); LS.setfield(classtab, "__index", classtab); LS.setfield(classtab, "__class", classname); LS.setfield(classtab, "action", action); @@ -216,10 +193,120 @@ int lpx_source_resetclasses(lua_State *L) { return LS.result(); } -void luaopen_lpx_source(lua_State *L) { - LuaStack::reg(L, "source", "updatefile", LuaArgCheck<2, lpx_source_updatefile>); - LuaStack::reg(L, "source", "updateall", LuaArgCheck<1, lpx_source_updateall>); - LuaStack::reg(L, "source", "class", LuaArgCheck<1, lpx_source_class>); - LuaStack::reg(L, "source", "resetclasses", LuaArgCheck<0, lpx_source_resetclasses>); - LuaStack::reg(L, 0 , "class", LuaArgCheck<1, lpx_source_class>); +LuaDefineHidden(source_load_builtins) { + luaopen_base(L); + luaopen_table(L); + luaopen_string(L); + luaopen_math(L); + luaopen_bit(L); + // luaopen_package(L); // Omitted because we use our own package system. + // luaopen_io(L); // Not safe for the sandbox. + // luaopen_os(L); // Not safe for the sandbox. + // luaopen_debug(L); // Not safe for the sandbox. + // luaopen_jit(L); // Don't know what it's for. + return 0; } + +LuaDefineHidden(source_load_cfunctions) { + auto regs = LuaFunctionReg::all(); + for (const LuaFunctionReg *r : regs) { + const std::string &name = r->get_name(); + size_t upos = name.find('_'); + std::string classname; + std::string funcname; + if (upos == std::string::npos) { + continue; + } else { + funcname = name.substr(upos + 1); + classname = name.substr(0, upos); + } + lua_CFunction func = r->get_func(); + int mode = r->get_mode(); + if (mode == 3) { // Class Method + lua_pushlstring(L, classname.c_str(), classname.size()); + source_class(L); + lua_pushcfunction(L, func); + lua_setfield(L, -2, funcname.c_str()); + } + if ((mode == 1) || (mode == 2)) { // Global function or global method + int top = lua_gettop(L); + lua_getglobal(L, classname.c_str()); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setglobal(L, classname.c_str()); + } + lua_pushcfunction(L, func); + lua_setfield(L, -2, funcname.c_str()); + lua_settop(L, top); + } + if (mode == 1) { // Global function + lua_pushcfunction(L, func); + lua_setglobal(L, funcname.c_str()); + } + } + return 0; +} + +// Fetches the source database and runs all the loadresults. +// +// Returns a single string, which is a bunch of concatenated error +// messages. +// +LuaDefineHidden(source_load_lfunctions) { + LuaRet errors; + LuaVar sourcedb, key, info, seq, closure, err; + LuaStack LS(L, sourcedb, errors, key, info, seq, closure, err); + + // Get the source database. + LS.getfield(sourcedb, LuaRegistry, "sourcedb"); + if (LS.type(sourcedb) != LUA_TTABLE) { + LS.newtable(sourcedb); + LS.setfield(LuaRegistry, "sourcedb", sourcedb); + } + + // Sort the keys by sequence number. + std::map indices; + LS.set(key, LuaNil); + while (LS.next(sourcedb, key, info) != 0) { + LS.getfield(seq, info, "sequence"); + indices[LS.tointeger(seq)] = LS.tostring(key); + } + + // Now call the closures in the proper order. + std::stringstream errss; + for (const auto &p : indices) { + LS.rawget(info, sourcedb, p.second); + LS.getfield(closure, info, "loadresult"); + + // If there's already an error in the sourcedb, collect it. + if (!LS.isfunction(closure)) { + errss << LS.tostring(closure); + continue; + } + + // Call the closure. If there's an error, collect it. + lua_pushvalue(L, closure.index()); + if (lua_pcall(L, 0, 0, 0) != 0) { + lua_replace(L, err.index()); + errss << LS.tostring(err); + } + } + LS.set(errors, errss.str()); + return LS.result(); +} + +LuaDefineGlobalMethod(source_rebuild) { + LuaVar errs; + LuaStack LS(L, errs); + source_reset_classes(L); + source_load_builtins(L); + source_load_cfunctions(L); + source_load_lfunctions(L); + lua_replace(L, errs.index()); + std::string errstr = LS.tostring(errs); + std::cerr << errstr; + return LS.result(); +} + diff --git a/luprex/syscpp/source.hpp b/luprex/syscpp/source.hpp index 96108fbf..5b106f21 100644 --- a/luprex/syscpp/source.hpp +++ b/luprex/syscpp/source.hpp @@ -9,12 +9,17 @@ #define SOURCE_HPP #include "luastack.hpp" +#include -int lpx_source_update(lua_State *L); -int lpx_source_class(lua_State *L); -int lpx_source_resetclasses(lua_State *L); -void luaopen_lpx_source(lua_State *L); +// Get a class from the class database. +int source_class(lua_State *L); + +// Update the source database from disk. No parameters, no return values. +int source_update(lua_State *L); + +// Rebuild the class database from the source database. No parameters, no return values. +int source_rebuild(lua_State *L); #endif // SOURCE_HPP diff --git a/luprex/syscpp/table.cpp b/luprex/syscpp/table.cpp index 4edce9f5..46f7a6a1 100644 --- a/luprex/syscpp/table.cpp +++ b/luprex/syscpp/table.cpp @@ -1,7 +1,8 @@ #include "table.hpp" +#include "source.hpp" // Clear the table. Removes metatable and all key-value pairs. -int lpx_table_clear(lua_State *L) { +LuaDefineGlobalMethod(table_clear) { LuaArg tab; LuaStack LS(L, tab); @@ -20,7 +21,7 @@ int lpx_table_clear(lua_State *L) { return LS.result(); } -int lpx_table_coerce(lua_State *L) { +LuaDefineGlobalMethod(table_coerce) { if (!lua_istable(L, -1)) { lua_pop(L, 1); lua_newtable(L); @@ -28,7 +29,3 @@ int lpx_table_coerce(lua_State *L) { return 1; } -void luaopen_lpx_table (lua_State *L) { - LuaStack::reg(L, "table", "clear", LuaArgCheck<1, lpx_table_clear>); - LuaStack::reg(L, "table", "coerce", LuaArgCheck<1, lpx_table_coerce>); -} diff --git a/luprex/syscpp/table.hpp b/luprex/syscpp/table.hpp index 6546f4ec..a8a36bf9 100644 --- a/luprex/syscpp/table.hpp +++ b/luprex/syscpp/table.hpp @@ -3,9 +3,11 @@ #include "luastack.hpp" -int lpx_table_clear(lua_State *L); -int lpx_table_coerce(lua_State *L); +// Clear a table. Takes the table as a parameter. +int table_clear(lua_State *L); -void luaopen_lpx_table (lua_State *L); +// Takes an object O. If O is a table, returns it, otherwise +// returns a new table. +int table_coerce(lua_State *L); #endif // TABLE_HPP diff --git a/luprex/syscpp/util.cpp b/luprex/syscpp/util.cpp index 96a68c9e..5e0ab71f 100644 --- a/luprex/syscpp/util.cpp +++ b/luprex/syscpp/util.cpp @@ -2,6 +2,12 @@ #include #include #include "util.hpp" +#include +#include +#ifndef WIN32 +#include +#define stat _stat +#endif namespace util { @@ -20,10 +26,30 @@ const stringvec read_lines(const std::string &path) { return result; } +// Get a string encoding the modification time and size of the file. +std::string get_file_fingerprint(const std::string &fn) { + struct stat result; + if(stat(fn.c_str(), &result)==0) + { + std::stringstream ss; + ss << result.st_mtime; + return ss.str(); + } + return ""; +} + +std::string get_file_contents(const std::string &fn) { + std::ifstream fs(fn); + std::stringstream buffer; + buffer << fs.rdbuf(); + return buffer.str(); +} + + // Strip leading and trailing whitespace and comments. const stringvec trim_and_uncomment(const stringvec &lines) { stringvec result; - for (int i = 0; i < lines.size(); i++) { + for (int i = 0; i < int(lines.size()); i++) { std::string trimmed = trim(lines[i]); if ((trimmed.size() > 0) && (trimmed[0] != '#')) { result.push_back(trimmed); diff --git a/luprex/syscpp/util.hpp b/luprex/syscpp/util.hpp index b3d0089e..71fef15e 100644 --- a/luprex/syscpp/util.hpp +++ b/luprex/syscpp/util.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace util { @@ -31,6 +32,8 @@ static inline std::string trim(std::string s) { const stringvec read_lines(const std::string &path); const stringvec trim_and_uncomment(const stringvec &lines); +std::string get_file_fingerprint(const std::string &path); +std::string get_file_contents(const std::string &fn); } // namespace util #endif // UTIL_HPP