From 32ec92e71c3f26c17ad80e4049f48811e592ace1 Mon Sep 17 00:00:00 2001 From: elsid Date: Sun, 18 Aug 2024 19:57:21 +0200 Subject: [PATCH] Use normalized path in LuaState --- apps/components_tests/lua/test_lua.cpp | 42 ++++++++++++++++---------- components/lua/luastate.cpp | 33 ++++++++++---------- components/lua/luastate.hpp | 16 +++++----- components/lua/scriptscontainer.cpp | 2 +- 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/apps/components_tests/lua/test_lua.cpp b/apps/components_tests/lua/test_lua.cpp index 6596b509db..a4b78889bb 100644 --- a/apps/components_tests/lua/test_lua.cpp +++ b/apps/components_tests/lua/test_lua.cpp @@ -81,13 +81,14 @@ return { TEST_F(LuaStateTest, Sandbox) { - sol::table script1 = mLua.runInNewSandbox("aaa/counter.lua"); + const VFS::Path::Normalized path("aaa/counter.lua"); + sol::table script1 = mLua.runInNewSandbox(path); EXPECT_EQ(LuaUtil::call(script1["get"]).get(), 42); LuaUtil::call(script1["inc"], 3); EXPECT_EQ(LuaUtil::call(script1["get"]).get(), 45); - sol::table script2 = mLua.runInNewSandbox("aaa/counter.lua"); + sol::table script2 = mLua.runInNewSandbox(path); EXPECT_EQ(LuaUtil::call(script2["get"]).get(), 42); LuaUtil::call(script2["inc"], 1); EXPECT_EQ(LuaUtil::call(script2["get"]).get(), 43); @@ -122,12 +123,14 @@ return { TEST_F(LuaStateTest, ErrorHandling) { - EXPECT_ERROR(mLua.runInNewSandbox("invalid.lua"), "[string \"invalid.lua\"]:1:"); + const VFS::Path::Normalized path("invalid.lua"); + EXPECT_ERROR(mLua.runInNewSandbox(path), "[string \"invalid.lua\"]:1:"); } TEST_F(LuaStateTest, CustomRequire) { - sol::table script = mLua.runInNewSandbox("bbb/tests.lua"); + const VFS::Path::Normalized path("bbb/tests.lua"); + sol::table script = mLua.runInNewSandbox(path); EXPECT_FLOAT_EQ( LuaUtil::call(script["sin"], 1).get(), -LuaUtil::call(script["requireMathSin"], -1).get()); @@ -135,7 +138,7 @@ return { EXPECT_EQ(LuaUtil::call(script["useCounter"]).get(), 43); EXPECT_EQ(LuaUtil::call(script["useCounter"]).get(), 44); { - sol::table script2 = mLua.runInNewSandbox("bbb/tests.lua"); + sol::table script2 = mLua.runInNewSandbox(path); EXPECT_EQ(LuaUtil::call(script2["useCounter"]).get(), 43); } EXPECT_EQ(LuaUtil::call(script["useCounter"]).get(), 45); @@ -145,7 +148,8 @@ return { TEST_F(LuaStateTest, ReadOnly) { - sol::table script = mLua.runInNewSandbox("bbb/tests.lua"); + const VFS::Path::Normalized path("bbb/tests.lua"); + sol::table script = mLua.runInNewSandbox(path); // rawset itself is allowed EXPECT_EQ(LuaUtil::call(script["callRawset"]).get(), 3); @@ -161,25 +165,27 @@ return { TEST_F(LuaStateTest, Print) { + const VFS::Path::Normalized path("bbb/tests.lua"); { - sol::table script = mLua.runInNewSandbox("bbb/tests.lua"); + sol::table script = mLua.runInNewSandbox(path); testing::internal::CaptureStdout(); LuaUtil::call(script["print"], 1, 2, 3); std::string output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output, "[bbb/tests.lua]:\t1\t2\t3\n"); + EXPECT_EQ(output, "unnamed:\t1\t2\t3\n"); } { - sol::table script = mLua.runInNewSandbox("bbb/tests.lua", "prefix"); + sol::table script = mLua.runInNewSandbox(path, "prefix"); testing::internal::CaptureStdout(); LuaUtil::call(script["print"]); // print with no arguments std::string output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output, "prefix[bbb/tests.lua]:\n"); + EXPECT_EQ(output, "prefix:\n"); } } TEST_F(LuaStateTest, UnsafeFunction) { - sol::table script = mLua.runInNewSandbox("bbb/tests.lua"); + const VFS::Path::Normalized path("bbb/tests.lua"); + sol::table script = mLua.runInNewSandbox(path); EXPECT_ERROR(LuaUtil::call(script["callLoadstring"]), "a nil value"); } @@ -190,11 +196,13 @@ return { sol::table api1 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api1")); sol::table api2 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api2")); - sol::table script1 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api1 } }); + const VFS::Path::Normalized path("bbb/tests.lua"); + + sol::table script1 = lua.runInNewSandbox(path, "", { { "test.api", api1 } }); lua.addCommonPackage("sqrlib", view.sol().create_table_with("sqr", [](int x) { return x * x; })); - sol::table script2 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api2 } }); + sol::table script2 = lua.runInNewSandbox(path, "", { { "test.api", api2 } }); EXPECT_ERROR(LuaUtil::call(script1["sqr"], 3), "module not found: sqrlib"); EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get(), 9); @@ -217,12 +225,13 @@ return { return mLua.getTotalMemoryUsage(); }; int64_t memWithScript; + const VFS::Path::Normalized path("requireBig.lua"); { - sol::object s = mLua.runInNewSandbox("requireBig.lua"); + sol::object s = mLua.runInNewSandbox(path); memWithScript = getMem(); } for (int i = 0; i < 100; ++i) // run many times to make small memory leaks visible - mLua.runInNewSandbox("requireBig.lua"); + mLua.runInNewSandbox(path); int64_t memWithoutScript = getMem(); // At this moment all instances of the script should be garbage-collected. EXPECT_LT(memWithoutScript, memWithScript); @@ -230,7 +239,8 @@ return { TEST_F(LuaStateTest, SafeIndexMetamethod) { - sol::table t = mLua.runInNewSandbox("metaIndexError.lua"); + const VFS::Path::Normalized path("metaIndexError.lua"); + sol::table t = mLua.runInNewSandbox(path); // without safe get we crash here EXPECT_ERROR(LuaUtil::safeGet(t, "any key"), "meta index error"); } diff --git a/components/lua/luastate.cpp b/components/lua/luastate.cpp index 1e1e1abae2..dc626b2a1f 100644 --- a/components/lua/luastate.cpp +++ b/components/lua/luastate.cpp @@ -16,16 +16,16 @@ namespace LuaUtil { - - static std::string packageNameToVfsPath(std::string_view packageName, const VFS::Manager* vfs) + static VFS::Path::Normalized packageNameToVfsPath(std::string_view packageName, const VFS::Manager& vfs) { - std::string path(packageName); - std::replace(path.begin(), path.end(), '.', '/'); - std::string pathWithInit = path + "/init.lua"; - path.append(".lua"); - if (vfs->exists(path)) + std::string pathValue(packageName); + std::replace(pathValue.begin(), pathValue.end(), '.', '/'); + VFS::Path::Normalized pathWithInit(pathValue + "/init.lua"); + pathValue.append(".lua"); + VFS::Path::Normalized path(pathValue); + if (vfs.exists(path)) return path; - else if (vfs->exists(pathWithInit)) + else if (vfs.exists(pathWithInit)) return pathWithInit; else throw std::runtime_error("module not found: " + std::string(packageName)); @@ -202,7 +202,7 @@ namespace LuaUtil sol["setEnvironment"] = [](const sol::environment& env, const sol::function& fn) { sol::set_environment(env, fn); }; sol["loadFromVFS"] = [this](std::string_view packageName) { - return loadScriptAndCache(packageNameToVfsPath(packageName, mVFS)); + return loadScriptAndCache(packageNameToVfsPath(packageName, *mVFS)); }; sol["loadInternalLib"] = [this](std::string_view packageName) { return loadInternalLib(packageName); }; @@ -341,15 +341,14 @@ namespace LuaUtil mCommonPackages.insert_or_assign(std::move(packageName), std::move(package)); } - sol::protected_function_result LuaState::runInNewSandbox(const std::string& path, const std::string& namePrefix, - const std::map& packages, const sol::object& hiddenData) + sol::protected_function_result LuaState::runInNewSandbox(const VFS::Path::Normalized& path, + const std::string& envName, const std::map& packages, const sol::object& hiddenData) { // TODO sol::protected_function script = loadScriptAndCache(path); sol::environment env(mSol, sol::create, mSandboxEnv); - std::string envName = namePrefix + "[" + path + "]:"; - env["print"] = mSol["printGen"](envName); + env["print"] = mSol["printGen"](envName + ":"); env["_G"] = env; env[sol::metatable_key]["__metatable"] = false; @@ -395,12 +394,12 @@ namespace LuaUtil return std::move(res); } - sol::function LuaState::loadScriptAndCache(const std::string& path) + sol::function LuaState::loadScriptAndCache(const VFS::Path::Normalized& path) { auto iter = mCompiledScripts.find(path); if (iter != mCompiledScripts.end()) { - sol::load_result res = mSol.load(iter->second.as_string_view(), path, sol::load_mode::binary); + sol::load_result res = mSol.load(iter->second.as_string_view(), path.value(), sol::load_mode::binary); // Unless we have memory corruption issues, the bytecode is valid at this point, but loading might still // fail because we've hit our Lua memory cap if (!res.valid()) @@ -412,10 +411,10 @@ namespace LuaUtil return res; } - sol::function LuaState::loadFromVFS(const std::string& path) + sol::function LuaState::loadFromVFS(const VFS::Path::Normalized& path) { std::string fileContent(std::istreambuf_iterator(*mVFS->get(path)), {}); - sol::load_result res = mSol.load(fileContent, path, sol::load_mode::text); + sol::load_result res = mSol.load(fileContent, path.value(), sol::load_mode::text); if (!res.valid()) throw std::runtime_error(std::string("Lua error: ") += res.get().what()); return res; diff --git a/components/lua/luastate.hpp b/components/lua/luastate.hpp index 36b3f11cb7..08e4e578fc 100644 --- a/components/lua/luastate.hpp +++ b/components/lua/luastate.hpp @@ -7,6 +7,8 @@ #include +#include + #include "configuration.hpp" namespace VFS @@ -141,13 +143,13 @@ namespace LuaUtil // (the result is expected to be an interface of the script). // Args: // path: path to the script in the virtual filesystem; - // namePrefix: sandbox name will be "[]". Sandbox name - // will be added to every `print` output. + // envName: sandbox name. // packages: additional packages that should be available from the sandbox via `require`. Each package // should be either a sol::table or a sol::function. If it is a function, it will be evaluated // (once per sandbox) with the argument 'hiddenData' the first time when requested. - sol::protected_function_result runInNewSandbox(const std::string& path, const std::string& namePrefix = "", - const std::map& packages = {}, const sol::object& hiddenData = sol::nil); + sol::protected_function_result runInNewSandbox(const VFS::Path::Normalized& path, + const std::string& envName = "unnamed", const std::map& packages = {}, + const sol::object& hiddenData = sol::nil); void dropScriptCache() { mCompiledScripts.clear(); } @@ -157,7 +159,7 @@ namespace LuaUtil // directly. void addInternalLibSearchPath(const std::filesystem::path& path) { mLibSearchPaths.push_back(path); } sol::function loadInternalLib(std::string_view libName); - sol::function loadFromVFS(const std::string& path); + sol::function loadFromVFS(const VFS::Path::Normalized& path); sol::environment newInternalLibEnvironment(); uint64_t getTotalMemoryUsage() const { return mSol.memory_used(); } @@ -182,7 +184,7 @@ namespace LuaUtil friend sol::protected_function_result call( ScriptId scriptId, const sol::protected_function& fn, Args&&... args); - sol::function loadScriptAndCache(const std::string& path); + sol::function loadScriptAndCache(const VFS::Path::Normalized& path); static void countHook(lua_State* L, lua_Debug* ar); static void* trackingAllocator(void* ud, void* ptr, size_t osize, size_t nsize); @@ -227,7 +229,7 @@ namespace LuaUtil sol::state_view mSol; const ScriptsConfiguration* mConf; sol::table mSandboxEnv; - std::map mCompiledScripts; + std::map mCompiledScripts; std::map mCommonPackages; const VFS::Manager* mVFS; std::vector mLibSearchPaths; diff --git a/components/lua/scriptscontainer.cpp b/components/lua/scriptscontainer.cpp index 1294271676..ae62d952fa 100644 --- a/components/lua/scriptscontainer.cpp +++ b/components/lua/scriptscontainer.cpp @@ -97,7 +97,7 @@ namespace LuaUtil try { - sol::object scriptOutput = mLua.runInNewSandbox(path, mNamePrefix, mAPI, script.mHiddenData); + sol::object scriptOutput = mLua.runInNewSandbox(path, debugName, mAPI, script.mHiddenData); if (scriptOutput == sol::nil) return true; sol::object engineHandlers = sol::nil, eventHandlers = sol::nil;