Change how builtin functions are loaded, better error checking

This commit is contained in:
2022-03-16 17:05:20 -04:00
parent 3b1c3e022a
commit c1ce1bfcc0
5 changed files with 184 additions and 99 deletions

View File

@@ -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;
}

View File

@@ -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__); }

View File

@@ -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, "", "");

View File

@@ -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;
}
}