diff --git a/luprex/cpp/core/luasnap.cpp b/luprex/cpp/core/luasnap.cpp index 8505b4d5..1dfc3dd5 100644 --- a/luprex/cpp/core/luasnap.cpp +++ b/luprex/cpp/core/luasnap.cpp @@ -33,10 +33,17 @@ void LuaSnap::serialize(StreamBuffer *sb) { // Lua stack should be empty. assert(lua_gettop(state_) == 0); - // lua variables that we'll need. - LuaVar key, value; - LuaRet permstable, regcopy; - LuaDefStack LS(state_, permstable, regcopy, key, value); + // We'll allocate stack slots manually, to ensure + // that the thread serializes as we expect it to. + lua_pushnil(state_); // permstable + lua_pushnil(state_); // regcopy + lua_pushnil(state_); // key + lua_pushnil(state_); // value + LuaSpecial permstable(1); + LuaSpecial regcopy(2); + LuaSpecial key(3); + LuaSpecial value(4); + LuaCoreStack LS(state_); // Construct a copy of the registry table. LS.set(regcopy, LuaNewTable); @@ -62,10 +69,8 @@ void LuaSnap::serialize(StreamBuffer *sb) { LS.rawget(permstable, LuaRegistry, "persist"); assert(LS.istable(permstable)); - // When we call 'LS.result', this should leave the - // permstable and the regcopy on the stack. - LS.result(); - assert(lua_gettop(state_) == 2); + // Remove key and value from stack, leaving permstable and regcopy. + lua_settop(state_, 2); // Write dummy length, use eris to write data, then overwrite length. sb->write_int64(0); @@ -88,13 +93,17 @@ void LuaSnap::deserialize(StreamBuffer *sb) { lua_pushstring(state_, "unpersist"); lua_rawget(state_, LUA_REGISTRYINDEX); eris_undump(state_, sb->lua_reader, ud); + + // Set up a stack frame manually. Eris deserialization + // should have left permstable and regcopy on the stack. assert(lua_gettop(state_) == 2); - - // Set up a stack frame. - LuaArg permstable, regcopy; - LuaVar key, value; - LuaDefStack LS(state_, permstable, regcopy, key, value); - + LuaSpecial permstable(1); + LuaSpecial regcopy(2); + lua_pushnil(state_); + lua_pushnil(state_); + LuaSpecial key(3); + LuaSpecial value(4); + LuaCoreStack LS(state_); assert(LS.istable(regcopy)); // Copy the contents of the snapshot over to the registry. @@ -102,8 +111,7 @@ void LuaSnap::deserialize(StreamBuffer *sb) { while (LS.next(regcopy, key, value) != 0) { LS.rawset(LuaRegistry, key, value); } - LS.result(); - assert(lua_gettop(state_) == 0); + lua_settop(state_, 0); } // Snapshot and rollback can trivially be implemented on top of serialize and diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 6edbc988..1d9dda3a 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -156,16 +156,18 @@ class LuaSlot : public eng::nevernew { protected: int index_; + + constexpr LuaSlot(int n) : index_(n) {} + private: inline operator int() const { return index_; } -public: - LuaSlot() { - index_ = 0; - } - int index() const { +public: + constexpr LuaSlot() : index_(0) {} + + inline int index() const { return index_; } @@ -181,20 +183,30 @@ class LuaVar : public LuaSlot {}; class LuaSpecial : public LuaSlot { public: - LuaSpecial(int n) { - index_ = n; - } + constexpr LuaSpecial(int n) : LuaSlot(n) {} }; extern LuaSpecial LuaRegistry; -class LuaUpvalue : public LuaSlot { - public: - LuaUpvalue(int n) { - index_ = lua_upvalueindex(n); - } +class LuaExtraArgs { +private: + int index_; + int size_; + +public: + LuaExtraArgs() { + index_ = 0; + size_ = 0; + } + + LuaSpecial operator[] (int n) const { return LuaSpecial(index_ + n); } + int size() const { return size_; } + + friend class LuaCoreStack; + friend class LuaDefStack; }; + class LuaNilMarker {}; extern LuaNilMarker LuaNil; @@ -283,27 +295,33 @@ protected: int nret; int narg; int nvar; - constexpr Counts(int r, int a, int v) : nret(r), narg(a), nvar(v) {} + int nextra; + constexpr Counts(int nr, int na, int nv, int ne) : nret(nr), narg(na), nvar(nv), nextra(ne) {} }; - template + template constexpr static Counts count_slots(LuaRet &v, SS & ... stackslots) { - return count_slots(stackslots...); + return count_slots(stackslots...); } - template + template constexpr static Counts count_slots(LuaArg &v, SS & ... stackslots) { - return count_slots(stackslots...); + return count_slots(stackslots...); } - template + template constexpr static Counts count_slots(LuaVar &v, SS & ... stackslots) { - return count_slots(stackslots...); + return count_slots(stackslots...); } - template + template + constexpr static Counts count_slots(LuaExtraArgs &v, SS & ... stackslots) + { + return count_slots(stackslots...); + } + template constexpr static Counts count_slots() { - return Counts(NRET, NARG, NVAR); + return Counts(NRET, NARG, NVAR, NEXTRA); } private: @@ -649,47 +667,76 @@ public: class LuaDefStack : public LuaCoreStack { private: + int top_; int nret_; - template + template void assign_slots(LuaRet &v, SS & ... stackslots) { v.index_ = RETP; - assign_slots(stackslots...); + assign_slots(stackslots...); } - template + template void assign_slots(LuaArg &v, SS & ... stackslots) { v.index_ = ARGP; - assign_slots(stackslots...); + assign_slots(stackslots...); } - template + template void assign_slots(LuaVar &v, SS & ... stackslots) { v.index_ = VARP; - assign_slots(stackslots...); + assign_slots(stackslots...); } - template + template + void assign_slots(LuaExtraArgs &v, SS & ... stackslots) { + v.index_ = EXTRAP; + v.size_ = EXTRAC; + assign_slots(stackslots...); + } + template void assign_slots() {} + template + void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaRet &v, SS & ... stackslots) { + v.index_ = retp; + vassign_slots(retp+1, argp, varp, extrap, extrac, stackslots...); + } + template + void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaArg &v, SS & ... stackslots) { + v.index_ = argp; + vassign_slots(retp, argp+1, varp, extrap, extrac, stackslots...); + } + template + void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaVar &v, SS & ... stackslots) { + v.index_ = varp; + vassign_slots(retp, argp, varp+1, extrap, extrac, stackslots...); + } + template + void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaExtraArgs &v, SS & ... stackslots) { + v.index_ = extrap; + v.size_ = extrac; + vassign_slots(retp, argp, varp, extrap, extrac, stackslots...); + } + void vassign_slots(int retp, int argp, int varp, int extrap, int extrac) {} + + public: template inline LuaDefStack(lua_State *L, SS & ... stackslots) : LuaCoreStack(L) { - constexpr Counts counts = count_slots<0, 0, 0>(stackslots...); - if (lua_gettop(L_) != counts.narg) { - luaL_error(L_, "function expects exactly %d arguments", counts.narg); + constexpr Counts counts = count_slots<0, 0, 0, 0>(stackslots...); + if (lua_gettop(L_) < counts.narg) { + luaL_error(L_, "function expects at least %d arguments", counts.narg); } - lua_checkstack(L, counts.narg + counts.nvar + counts.nret + 20); - for (int i = 0; i < counts.nret; i ++) { - lua_pushnil(L_); - lua_insert(L_, i + 1); - } - for (int i = 0; i < counts.nvar; i++) { + lua_checkstack(L, counts.nvar + counts.nret + 20); + int top = lua_gettop(L); + for (int i = 0; i < counts.nvar + counts.nret; i ++) { lua_pushnil(L_); } - assign_slots<1, 1 + counts.nret, 1 + counts.nret + counts.narg>(stackslots...); + vassign_slots(1 + counts.nvar + top, 1, 1 + top, 1 + counts.narg, top - counts.narg, stackslots...); + top_ = top + counts.nvar + counts.nret; nret_ = counts.nret; } int result() { - lua_settop(L_, nret_); + lua_settop(L_, top_); return nret_; } @@ -730,9 +777,10 @@ private: public: template LuaExtStack(lua_State *L, SS & ... stackslots) : LuaCoreStack(L) { - constexpr Counts counts = count_slots<0, 0, 0>(stackslots...); + constexpr Counts counts = count_slots<0, 0, 0, 0>(stackslots...); static_assert(counts.narg == 0, "LuaExtStack doesn't allow LuaArg parameters"); static_assert(counts.nret == 0, "LuaExtStack doesn't allow LuaRet parameters"); + static_assert(counts.nextra == 0, "LuaExtStack doesn't allow LuaExtraArgs parameters"); lua_checkstack(L_, counts.nvar + 20); oldtop_ = lua_gettop(L_); for (int i = 0; i < counts.nvar; i++) { diff --git a/luprex/cpp/core/pprint.cpp b/luprex/cpp/core/pprint.cpp index 2180633c..a786d766 100644 --- a/luprex/cpp/core/pprint.cpp +++ b/luprex/cpp/core/pprint.cpp @@ -270,19 +270,17 @@ LuaDefine(string_pprint, "obj1, obj2, ...", "|start at indentation level zero, and always expand the" "|top-level table." "|") { - int n = lua_gettop(L); - LuaCoreStack LS(L); + LuaRet result; + LuaExtraArgs extra; + LuaDefStack LS(L, result, extra); + util::ostringstream oss; - for (int i = 1; i <= n; i++) { - LuaSpecial root(i); - pprint(LS, root, PrettyPrintOptions(), &oss); - if (i < n) oss << "\n"; + for (int i = 0; i < extra.size(); i++) { + pprint(LS, extra[i], PrettyPrintOptions(), &oss); + oss << "\n"; } - oss << std::endl; - lua_settop(L, 1); - LuaSpecial result(1); LS.set(result, oss.view()); - return 1; + return LS.result(); } LuaDefine(string_pprintx, "options", diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index aa3a7407..d447b2c9 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -728,14 +728,12 @@ LuaDefine(pprint, "obj1, obj2, ...", "|") { World *w = World::fetch_global_pointer(L); std::ostream *ostream = w->lthread_print_stream(); - int n = lua_gettop(L); - LuaDefStack LS(L); - for (int i = 1; i <= n; i++) { - LuaSpecial root(i); - pprint(LS, root, PrettyPrintOptions(), ostream); - if (i < n) (*ostream) << "\n"; + LuaExtraArgs extra; + LuaDefStack LS(L, extra); + for (int i = 0; i < extra.size(); i++) { + pprint(LS, extra[i], PrettyPrintOptions(), ostream); + (*ostream) << std::endl; } - (*ostream) << std::endl; return LS.result(); }