From c1ce1bfcc0767b8d3ca1dab2e74d5c5b7b02023c Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 16 Mar 2022 17:05:20 -0400 Subject: [PATCH] Change how builtin functions are loaded, better error checking --- luprex/core/TODO | 5 +- luprex/core/cpp/luastack.cpp | 3 +- luprex/core/cpp/luastack.hpp | 14 +- luprex/core/cpp/source.cpp | 259 ++++++++++++++++++++++------------- luprex/core/cpp/util.cpp | 2 +- 5 files changed, 184 insertions(+), 99 deletions(-) diff --git a/luprex/core/TODO b/luprex/core/TODO index 50e2b987..066c0363 100644 --- a/luprex/core/TODO +++ b/luprex/core/TODO @@ -1,4 +1,6 @@ -Improve table.findremove to work on tables, not just vectors. +Calling out to external servers. + +Support ANSI escape sequences on output. Finish documenting all builtins. @@ -6,3 +8,4 @@ Get rid of source_install_builtins after documenting all builtins. - but don't forget that source_install_builtins sets the string metatable. Do something about std::cerr && std::cout once and for all. + diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 5a92b279..f319d179 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -7,11 +7,12 @@ LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaNilMarker LuaNil; LuaNewTableMarker LuaNewTable; -LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, lua_CFunction f) { +LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, bool s, lua_CFunction f) { name_ = n; args_ = a; docs_ = d; func_ = f; + sandbox_ = s; next_ = All; All = this; } diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index ace53ac3..051c27d0 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -475,18 +475,20 @@ private: const char *name_; const char *args_; const char *docs_; + bool sandbox_; lua_CFunction func_; LuaFunctionReg *next_; public: static LuaFunctionReg *All; - LuaFunctionReg(const char *name, const char *args, const char *docs, lua_CFunction f); + LuaFunctionReg(const char *name, const char *args, const char *docs, bool sand, lua_CFunction f); static const LuaFunctionReg *lookup(lua_CFunction fn); const char *get_name() const { return name_; } const char *get_args() const { return args_; } const char *get_docs() const { return docs_; } lua_CFunction get_func() const { return func_; } + bool get_sandbox() const { return sandbox_; } LuaFunctionReg *next() const { return next_; } void set_func(lua_CFunction fn) { func_ = fn; } }; @@ -494,13 +496,19 @@ public: #define LuaDefine(name, args, docs) \ int lfn_##name(lua_State *L); \ - LuaFunctionReg reg_##name(#name, args, docs, lfn_##name); \ + LuaFunctionReg reg_##name(#name, args, docs, false, lfn_##name); \ int lfn_##name(lua_State *L) +#define LuaSandbox(name, args, docs) \ + int lfn_##name(lua_State *L); \ + LuaFunctionReg reg_##name(#name, args, docs, true, lfn_##name); \ + int lfn_##name(lua_State *L) #define LuaDefineBuiltin(name, args, docs) \ - LuaFunctionReg reg_##name(#name, args, docs, nullptr); + LuaFunctionReg reg_##name(#name, args, docs, false, nullptr); +#define LuaSandboxBuiltin(name, args, docs) \ + LuaFunctionReg reg_##name(#name, args, docs, true, nullptr); #define LuaStringify(x) #x #define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); } diff --git a/luprex/core/cpp/source.cpp b/luprex/core/cpp/source.cpp index dcc4c882..8763b308 100644 --- a/luprex/core/cpp/source.cpp +++ b/luprex/core/cpp/source.cpp @@ -66,70 +66,6 @@ static void get_reg_name(const LuaFunctionReg *reg, std::string_view &classname, } } - -// the 'luaopen' function creates a new table. -// -// We don't want to create new tables during reload, so we call luaopen, -// then we copy the contents of the new table into the existing 'makeclass' -// table. -// -static void load_builtin_class(lua_State *L, const char *name, lua_CFunction func) { - LuaVar sourcetab, classtab, key, value; - LuaStack LS(L, sourcetab, classtab, key, value); - LS.makeclass(classtab, name); - func(L); - lua_replace(L, sourcetab.index()); - LS.set(key, LuaNil); - while (LS.next(sourcetab, key, value) != 0) { - LS.rawset(classtab, key, value); - } - LS.result(); -} - -static void erase_builtin(LuaStack &LS, LuaSlot globtab, const eng::string &classname, const eng::string &funcname) { - if (classname.empty()) { - LS.rawset(globtab, funcname, LuaNil); - } else { - LuaVar classtab; - LuaStack LSX(LS.state(), classtab); - LS.rawget(classtab, globtab, classname); - if (LS.istable(classtab)) { - LS.rawset(classtab, funcname, LuaNil); - } - } -} - -static void source_install_builtins(lua_State *L) { - LuaVar nullstring, stringclass, globtab; - LuaStack LS(L, nullstring, stringclass, globtab); - luaopen_base(L); - load_builtin_class(L, "table", luaopen_table); - load_builtin_class(L, "string", luaopen_string); - load_builtin_class(L, "math", luaopen_math); - load_builtin_class(L, "debug", luaopen_debug); - load_builtin_class(L, "coroutine", luaopen_coroutine); - - // Nuke a few of the builtin functions for sandboxing reasons. - LS.getglobaltable(globtab); - erase_builtin(LS, globtab, "", "dofile"); - erase_builtin(LS, globtab, "", "collectgarbage"); - erase_builtin(LS, globtab, "", "loadfile"); - erase_builtin(LS, globtab, "", "load"); - erase_builtin(LS, globtab, "", "loadstring"); - erase_builtin(LS, globtab, "", "print"); - erase_builtin(LS, globtab, "", "xpcall"); - erase_builtin(LS, globtab, "string", "dump"); - - // Set the metatable for strings. - // Normally, this would be done by luaopen_string, but we're - // messing with the tables so we have to redo it. - LS.makeclass(stringclass, "string"); - LS.set(nullstring, ""); - LS.setmetatable(nullstring, stringclass); - - LS.result(); -} - static void get_info_table(LuaStack &LS, LuaSlot db, LuaSlot info, const eng::string &fn) { LS.rawget(info, db, fn); if (!LS.istable(info)) { @@ -339,15 +275,17 @@ static void source_load_cfunctions(lua_State *L) { LuaStack LS(L, classobj); for (auto r = LuaFunctionReg::All; r != nullptr; r=r->next()) { lua_CFunction func = r->get_func(); - std::string_view classname; - std::string_view funcname; - get_reg_name(r, classname, funcname); - if (classname.empty()) { - LS.getglobaltable(classobj); - LS.rawset(classobj, funcname, func); - } else { - LS.makeclass(classobj, classname); - LS.rawset(classobj, funcname, func); + if ((func != nullptr) && (!r->get_sandbox())) { + std::string_view classname; + std::string_view funcname; + get_reg_name(r, classname, funcname); + if (classname.empty()) { + LS.getglobaltable(classobj); + LS.rawset(classobj, funcname, func); + } else { + LS.makeclass(classobj, classname); + LS.rawset(classobj, funcname, func); + } } } LS.result(); @@ -401,7 +339,6 @@ static eng::string source_load_lfunctions(lua_State *L) { eng::string SourceDB::rebuild() { lua_State *L = lua_state_; source_clear_globals(L); - // source_install_builtins(L); source_load_cfunctions(L); eng::string errs = source_load_lfunctions(L); return errs; @@ -446,13 +383,18 @@ void SourceDB::run_unittests() { void SourceDB::init(lua_State *L) { lua_state_ = L; - LuaVar globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc; - LuaStack LS(L, globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc); + LuaVar globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc, nullstring; + LuaStack LS(L, globtab, persist, unpersist, classname, classtab, funcname, funcp, rawfunc, nullstring); + + source_clear_globals(L); + source_load_cfunctions(L); + + // Set the metatable for strings. + LS.makeclass(classtab, "string"); + LS.set(nullstring, ""); + LS.setmetatable(nullstring, classtab); // We need to register all C functions with the eris permanents tables. - source_clear_globals(L); - source_install_builtins(L); - source_load_cfunctions(L); LS.getglobaltable(globtab); LS.rawget(persist, LuaRegistry, "persist"); LS.rawget(unpersist, LuaRegistry, "unpersist"); @@ -500,30 +442,55 @@ void SourceDB::deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb) { void SourceDB::register_lua_builtins() { lua_State *L = LuaStack::newstate(nullptr); luaL_openlibs(L); - LuaVar globals,classtab,func; - LuaStack LS(L, globals, classtab, func); + LuaVar globals, lclassname, lfuncname, classtab, func; + LuaStack LS(L, globals, lclassname, lfuncname, classtab, func); LS.getglobaltable(globals); + + // Iterate over the function registry, copying function pointers from + // the prototype lua state into the registry, then remove the closure + // from the prototype. for (auto reg = LuaFunctionReg::All; reg != nullptr; reg=reg->next()) { + std::string_view funcname; + std::string_view classname; + get_reg_name(reg, classname, funcname); + if (classname.empty()) { + LS.getglobaltable(classtab); + } else { + LS.rawget(classtab, globals, classname); + } + lua_CFunction builtin = nullptr; + if (LS.istable(classtab)) { + LS.rawget(func, classtab, funcname); + if (LS.iscfunction(func)) { + builtin = lua_tocfunction(L, func.index()); + LS.rawset(classtab, funcname, LuaNil); + } + } if (reg->get_func() == nullptr) { - std::string_view funcname; - std::string_view classname; - get_reg_name(reg, classname, funcname); - if (classname.empty()) { - LS.rawget(func, globals, funcname); - if (LS.iscfunction(func)) { - reg->set_func(lua_tocfunction(L, func.index())); - } + if (builtin == nullptr) { + std::cerr << "No such builtin function: " << classname << " " << funcname << std::endl; } else { - LS.rawget(classtab, globals, classname); - if (LS.istable(classtab)) { - LS.rawget(func, classtab, funcname); + reg->set_func(builtin); + } + } + } + + // Iterate over the prototype. All cfunctions should have been removed. + LS.set(lclassname, LuaNil); + while (LS.next(globals, lclassname, classtab)) { + if (LS.isstring(lclassname)) { + if (LS.istable(classtab)) { + LS.set(lfuncname, LuaNil); + while (LS.next(classtab, lfuncname, func)) { if (LS.iscfunction(func)) { - reg->set_func(lua_tocfunction(L, func.index())); + std::cerr << "Failed to declare builtin: " << LS.ckstring(lclassname) << "." + << LS.ckstring(lfuncname) << std::endl; } } } } } + lua_close(L); } @@ -717,6 +684,48 @@ LuaDefineBuiltin(string_gmatch, "str, pattern", "iterate over pattern-matched su LuaDefineBuiltin(string_gsub, "str, pattern, replace", "global replace pattern in string"); LuaDefineBuiltin(string_match, "str, pattern", "return start and end of pattern in string"); LuaDefineBuiltin(string_sub, "str, pos1, pos2", "return substring of str from pos1 to pos2"); +LuaSandboxBuiltin(string_dump, "func", "convert a function to a string"); + +LuaDefineBuiltin(bit32_arshift, "n, shift", "shift 32-bit number to the right, keeping high bit unchanged"); +LuaDefineBuiltin(bit32_band, "n, n, n...", "return the bitwise and of all 32-bit numbers"); +LuaDefineBuiltin(bit32_bnot, "n", "return the bitwise negation of n, ie, (-1 - n) % 2^32"); +LuaDefineBuiltin(bit32_bor, "n, n, n...", "return the bitwise or of all 32-bit arguments"); +LuaDefineBuiltin(bit32_bxor, "n, n, n...", "return the bitwise exclusive or of all 32-bit arguments"); +LuaDefineBuiltin(bit32_btest, "n, n, n...", "compute bitwise and of all 32-bit arguments, return true if nonzero"); +LuaDefineBuiltin(bit32_extract, "n, field, width", "return value from extracted bitfield of 32-bit number"); +LuaDefineBuiltin(bit32_lrotate, "n, shift", "rotate 32-bit number to the left"); +LuaDefineBuiltin(bit32_lshift, "n, shift", "shift 32-bit number to the left, padding with zeros"); +LuaDefineBuiltin(bit32_replace, "n, v, field, width", "change value of extracted bitfield in 32-bit number"); +LuaDefineBuiltin(bit32_rrotate, "n, shift", "rotate 32-bit number to the right"); +LuaDefineBuiltin(bit32_rshift, "n, shift", "shift 32-bit number to the right, padding with zeros"); + +LuaDefineBuiltin(math_abs, "x", "return absolute value of x"); +LuaDefineBuiltin(math_acos, "x", "return arc-cosine of x in radians"); +LuaDefineBuiltin(math_asin, "x", "return arc-sine of x in radians"); +LuaDefineBuiltin(math_atan, "x", "return the arc-tangent of x in radians"); +LuaDefineBuiltin(math_atan2, "y, x", "return arc-tangent of y/x, using signs to compute quadrant"); +LuaDefineBuiltin(math_ceil, "x", "return the smallest integer larger than or equal to x"); +LuaDefineBuiltin(math_cos, "x", "return the cosine of x in radians"); +LuaDefineBuiltin(math_cosh, "x", "return the hyperbolic cosine of x in radians"); +LuaDefineBuiltin(math_deg, "rad", "convert radians to degrees"); +LuaDefineBuiltin(math_exp, "x", "returns the value e^x"); +LuaDefineBuiltin(math_floor, "x", "returns the smallest integer less than or equal to x"); +LuaDefineBuiltin(math_fmod, "x, y", "return the remainder of x/y that rounds the quotient towards zero"); +LuaDefineBuiltin(math_frexp, "x", "given x, returns mantissa and exponent"); +LuaDefineBuiltin(math_ldexp, "x", "return unnormalized mantissa and exponent"); +LuaDefineBuiltin(math_log, "x [, base]", "return the log of x in base, default base is e"); +LuaDefineBuiltin(math_max, "x, x, x...", "return the largest argument"); +LuaDefineBuiltin(math_min, "x, x, x...", "return the smallest argument"); +LuaDefineBuiltin(math_modf, "x", "returns the integral and fractional part of x"); +LuaDefineBuiltin(math_pow, "x, y", "returns x ^ y, equivalent to the operator"); +LuaDefineBuiltin(math_rad, "deg", "convert degrees to radians"); +LuaDefineBuiltin(math_random, "[m [, n]]", "return random [0.0-1.0), or [1-m], or [m-n]."); +LuaDefineBuiltin(math_randomseed, "x", "set x as the seed for random numbers"); +LuaDefineBuiltin(math_sin, "x", "return the sine of x in radians"); +LuaDefineBuiltin(math_sinh, "x", "return the hyperbolic sine of x in radians"); +LuaDefineBuiltin(math_sqrt, "x", "return the square root of x"); +LuaDefineBuiltin(math_tan, "x", "return the tangent of x in radians"); +LuaDefineBuiltin(math_tanh, "x", "return the hyperbolic tangent of x in radians"); LuaDefineBuiltin(assert, "flag [,message]", "assert that flag is true, if not, raise error"); LuaDefineBuiltin(error, "message", "raise an error"); @@ -734,4 +743,68 @@ LuaDefineBuiltin(select, "n, arg1, arg2, ...", "return the nth argument"); LuaDefineBuiltin(setmetatable, "table, meta", "set the metatable of the specified table"); LuaDefineBuiltin(tonumber, "str", "convert a string to a number"); LuaDefineBuiltin(type, "obj", "return the type of obj as a string"); +// print is redefined in world.cpp (because it prints into the world model) +// tostring is redefined in pprint.cpp + +LuaSandboxBuiltin(collectgarbage, "", ""); +LuaSandboxBuiltin(dofile, "", ""); +LuaSandboxBuiltin(xpcall, "", ""); +LuaSandboxBuiltin(loadfile, "", ""); +LuaSandboxBuiltin(load, "", ""); +LuaSandboxBuiltin(require, "", ""); + +LuaSandboxBuiltin(debug_debug, "", ""); +LuaSandboxBuiltin(debug_getuservalue, "", ""); +LuaSandboxBuiltin(debug_gethook, "", ""); +LuaSandboxBuiltin(debug_getinfo, "", ""); +LuaSandboxBuiltin(debug_getlocal, "", ""); +LuaSandboxBuiltin(debug_getregistry, "", ""); +LuaSandboxBuiltin(debug_getmetatable, "", ""); +LuaSandboxBuiltin(debug_getupvalue, "", ""); +LuaSandboxBuiltin(debug_upvaluejoin, "", ""); +LuaSandboxBuiltin(debug_upvalueid, "", ""); +LuaSandboxBuiltin(debug_setuservalue, "", ""); +LuaSandboxBuiltin(debug_sethook, "", ""); +LuaSandboxBuiltin(debug_setlocal, "", ""); +LuaSandboxBuiltin(debug_setmetatable, "", ""); +LuaSandboxBuiltin(debug_setupvalue, "", ""); +LuaSandboxBuiltin(debug_traceback, "", ""); + +LuaSandboxBuiltin(eris_persist, "", ""); +LuaSandboxBuiltin(eris_unpersist, "", ""); +LuaSandboxBuiltin(eris_settings, "", ""); + +LuaSandboxBuiltin(package_loadlib, "", ""); +LuaSandboxBuiltin(package_searchpath, "", ""); + +LuaSandboxBuiltin(coroutine_create, "", ""); +LuaSandboxBuiltin(coroutine_resume, "", ""); +LuaSandboxBuiltin(coroutine_running, "", ""); +LuaSandboxBuiltin(coroutine_status, "", ""); +LuaSandboxBuiltin(coroutine_wrap, "", ""); +LuaSandboxBuiltin(coroutine_yield, "", ""); + +LuaSandboxBuiltin(io_close, "", ""); +LuaSandboxBuiltin(io_flush, "", ""); +LuaSandboxBuiltin(io_input, "", ""); +LuaSandboxBuiltin(io_lines, "", ""); +LuaSandboxBuiltin(io_open, "", ""); +LuaSandboxBuiltin(io_output, "", ""); +LuaSandboxBuiltin(io_popen, "", ""); +LuaSandboxBuiltin(io_read, "", ""); +LuaSandboxBuiltin(io_tmpfile, "", ""); +LuaSandboxBuiltin(io_type, "", ""); +LuaSandboxBuiltin(io_write, "", ""); + +LuaSandboxBuiltin(os_clock, "", ""); +LuaSandboxBuiltin(os_date, "", ""); +LuaSandboxBuiltin(os_difftime, "", ""); +LuaSandboxBuiltin(os_execute, "", ""); +LuaSandboxBuiltin(os_exit, "", ""); +LuaSandboxBuiltin(os_getenv, "", ""); +LuaSandboxBuiltin(os_remove, "", ""); +LuaSandboxBuiltin(os_rename, "", ""); +LuaSandboxBuiltin(os_setlocale, "", ""); +LuaSandboxBuiltin(os_time, "", ""); +LuaSandboxBuiltin(os_tmpname, "", ""); diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index 412c7c39..172e4bc9 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -65,7 +65,7 @@ void quote_string(const eng::string &s, std::ostream *os) { case '\t': (*os) << "\\t"; break; case '\r': (*os) << "\\r"; break; default: - (*os) << "\\" << std::setw(3) << int(c); + (*os) << "\\" << std::setfill('0') << std::setw(3) << int(c); break; } }