From 45c9635e94dcd20c5924060b3c43c5536060fac7 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Sat, 9 Apr 2022 20:52:05 +0200 Subject: [PATCH] Fix getmetatable, fix pairsForReadonly, add util.loadCode, _G --- components/lua/luastate.cpp | 42 ++++++++++++++++++++-------------- components/lua/utilpackage.cpp | 13 +++++++++++ files/lua_api/openmw/util.lua | 7 ++++++ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/components/lua/luastate.cpp b/components/lua/luastate.cpp index 4ede81cc02..38f3ae889e 100644 --- a/components/lua/luastate.cpp +++ b/components/lua/luastate.cpp @@ -46,7 +46,7 @@ namespace LuaUtil static const std::string safeFunctions[] = { "assert", "error", "ipairs", "next", "pairs", "pcall", "select", "tonumber", "tostring", - "type", "unpack", "xpcall", "rawequal", "rawget", "rawset", "getmetatable", "setmetatable"}; + "type", "unpack", "xpcall", "rawequal", "rawget", "rawset", "setmetatable"}; static const std::string safePackages[] = {"coroutine", "math", "string", "table"}; LuaState::LuaState(const VFS::Manager* vfs, const ScriptsConfiguration* conf) : mConf(conf), mVFS(vfs) @@ -58,7 +58,6 @@ namespace LuaUtil mLua["math"]["randomseed"] = []{}; mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; }; - mLua["cmetatable"] = [](const sol::table& v) -> sol::object { return v[sol::metatable_key]; }; // Some fixes for compatibility between different Lua versions if (mLua["unpack"] == sol::nil) @@ -70,29 +69,35 @@ namespace LuaUtil mLua.script(R"( local _pairs = pairs local _ipairs = ipairs - local _cmeta = cmetatable - pairs = function(v) return ((_cmeta(v) or v).__pairs or _pairs)(v) end - ipairs = function(v) return ((_cmeta(v) or v).__ipairs or _ipairs)(v) end + pairs = function(v) return (rawget(getmetatable(v) or {}, '__pairs') or _pairs)(v) end + ipairs = function(v) return (rawget(getmetatable(v) or {}, '__ipairs') or _ipairs)(v) end )"); } mLua.script(R"( - local _pairs = pairs - local _ipairs = ipairs - local _tostring = tostring - local _write = writeToLog - local printToLog = function(name, ...) - local msg = name - for _, v in _ipairs({...}) do - msg = msg .. '\t' .. _tostring(v) + local printToLog = function(...) + local strs = {} + for i = 1, select('#', ...) do + strs[i] = tostring(select(i, ...)) end - return _write(msg) + return writeToLog(table.concat(strs, '\t')) end printGen = function(name) return function(...) return printToLog(name, ...) end end - local _cmeta = cmetatable - function pairsForReadOnly(v) return _pairs(_cmeta(v).__index) end - function ipairsForReadOnly(v) return _ipairs(_cmeta(v).__index) end + function pairsForReadOnly(v) + local nextFn, t, firstKey = pairs(getmetatable(v).__index) + return function(_, k) return nextFn(t, k) end, v, firstKey + end + function ipairsForReadOnly(v) + local nextFn, t, firstKey = ipairs(getmetatable(v).__index) + return function(_, k) return nextFn(t, k) end, v, firstKey + end + + getmetatable('').__metatable = false + getSafeMetatable = function(v) + if type(v) ~= 'table' then error('getmetatable is allowed only for tables', 2) end + return getmetatable(v) + end )"); mSandboxEnv = sol::table(mLua, sol::create); @@ -107,6 +112,7 @@ namespace LuaUtil if (mLua[s] == sol::nil) throw std::logic_error("Lua package not found: " + s); mCommonPackages[s] = mSandboxEnv[s] = makeReadOnly(mLua[s]); } + mSandboxEnv["getmetatable"] = mLua["getSafeMetatable"]; mCommonPackages["os"] = mSandboxEnv["os"] = makeReadOnly(tableFromPairs({ {"date", mLua["os"]["date"]}, {"difftime", mLua["os"]["difftime"]}, @@ -162,6 +168,8 @@ namespace LuaUtil sol::environment env(mLua, sol::create, mSandboxEnv); std::string envName = namePrefix + "[" + path + "]:"; env["print"] = mLua["printGen"](envName); + env["_G"] = env; + env[sol::metatable_key]["__metatable"] = false; auto maybeRunLoader = [&hiddenData](const sol::object& package) -> sol::object { diff --git a/components/lua/utilpackage.cpp b/components/lua/utilpackage.cpp index 2eda03dc52..7f52a770a1 100644 --- a/components/lua/utilpackage.cpp +++ b/components/lua/utilpackage.cpp @@ -247,6 +247,19 @@ namespace LuaUtil util["bitNot"] = [](unsigned a) { return ~a; }; } + util["loadCode"] = [](const std::string& code, const sol::table& env, sol::this_state s) + { + sol::state_view lua(s); + sol::load_result res = lua.load(code, "", sol::load_mode::text); + if (!res.valid()) + throw std::runtime_error("Lua error: " + res.get()); + sol::function fn = res; + sol::environment newEnv(lua, sol::create, env); + newEnv[sol::metatable_key][sol::meta_function::new_index] = env; + sol::set_environment(newEnv, fn); + return fn; + }; + return util; } diff --git a/files/lua_api/openmw/util.lua b/files/lua_api/openmw/util.lua index dbb1cbd8ae..3dd959b4a5 100644 --- a/files/lua_api/openmw/util.lua +++ b/files/lua_api/openmw/util.lua @@ -25,6 +25,13 @@ -- @param #table table Any table. -- @return #table The same table wrapped with read only userdata. +--- +-- Parses Lua code from string and returns as a function. +-- @function [parent=#util] loadCode +-- @param #string code Lua code. +-- @param #table table Environment to run the code in. +-- @return #function The loaded code. + --- -- Bitwise And (supports any number of arguments). -- @function [parent=#util] bitAnd