Merge branch 'vfs_normalized_path_10' into 'master'

Use normalized path in LuaState (#8138)

See merge request OpenMW/openmw!4365
pull/3236/head
psi29a 4 months ago
commit b32f68fcd2

@ -81,13 +81,14 @@ return {
TEST_F(LuaStateTest, Sandbox) 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<int>(), 42); EXPECT_EQ(LuaUtil::call(script1["get"]).get<int>(), 42);
LuaUtil::call(script1["inc"], 3); LuaUtil::call(script1["inc"], 3);
EXPECT_EQ(LuaUtil::call(script1["get"]).get<int>(), 45); EXPECT_EQ(LuaUtil::call(script1["get"]).get<int>(), 45);
sol::table script2 = mLua.runInNewSandbox("aaa/counter.lua"); sol::table script2 = mLua.runInNewSandbox(path);
EXPECT_EQ(LuaUtil::call(script2["get"]).get<int>(), 42); EXPECT_EQ(LuaUtil::call(script2["get"]).get<int>(), 42);
LuaUtil::call(script2["inc"], 1); LuaUtil::call(script2["inc"], 1);
EXPECT_EQ(LuaUtil::call(script2["get"]).get<int>(), 43); EXPECT_EQ(LuaUtil::call(script2["get"]).get<int>(), 43);
@ -122,12 +123,14 @@ return {
TEST_F(LuaStateTest, ErrorHandling) 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) 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( EXPECT_FLOAT_EQ(
LuaUtil::call(script["sin"], 1).get<float>(), -LuaUtil::call(script["requireMathSin"], -1).get<float>()); LuaUtil::call(script["sin"], 1).get<float>(), -LuaUtil::call(script["requireMathSin"], -1).get<float>());
@ -135,7 +138,7 @@ return {
EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 43); EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 43);
EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 44); EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 44);
{ {
sol::table script2 = mLua.runInNewSandbox("bbb/tests.lua"); sol::table script2 = mLua.runInNewSandbox(path);
EXPECT_EQ(LuaUtil::call(script2["useCounter"]).get<int>(), 43); EXPECT_EQ(LuaUtil::call(script2["useCounter"]).get<int>(), 43);
} }
EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 45); EXPECT_EQ(LuaUtil::call(script["useCounter"]).get<int>(), 45);
@ -145,7 +148,8 @@ return {
TEST_F(LuaStateTest, ReadOnly) 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 // rawset itself is allowed
EXPECT_EQ(LuaUtil::call(script["callRawset"]).get<int>(), 3); EXPECT_EQ(LuaUtil::call(script["callRawset"]).get<int>(), 3);
@ -161,25 +165,27 @@ return {
TEST_F(LuaStateTest, Print) 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(); testing::internal::CaptureStdout();
LuaUtil::call(script["print"], 1, 2, 3); LuaUtil::call(script["print"], 1, 2, 3);
std::string output = testing::internal::GetCapturedStdout(); 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(); testing::internal::CaptureStdout();
LuaUtil::call(script["print"]); // print with no arguments LuaUtil::call(script["print"]); // print with no arguments
std::string output = testing::internal::GetCapturedStdout(); std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(output, "prefix[bbb/tests.lua]:\n"); EXPECT_EQ(output, "prefix:\n");
} }
} }
TEST_F(LuaStateTest, UnsafeFunction) 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"); 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 api1 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api1"));
sol::table api2 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api2")); 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; })); 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_ERROR(LuaUtil::call(script1["sqr"], 3), "module not found: sqrlib");
EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9); EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9);
@ -217,12 +225,13 @@ return {
return mLua.getTotalMemoryUsage(); return mLua.getTotalMemoryUsage();
}; };
int64_t memWithScript; 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(); memWithScript = getMem();
} }
for (int i = 0; i < 100; ++i) // run many times to make small memory leaks visible 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(); int64_t memWithoutScript = getMem();
// At this moment all instances of the script should be garbage-collected. // At this moment all instances of the script should be garbage-collected.
EXPECT_LT(memWithoutScript, memWithScript); EXPECT_LT(memWithoutScript, memWithScript);
@ -230,7 +239,8 @@ return {
TEST_F(LuaStateTest, SafeIndexMetamethod) 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 // without safe get we crash here
EXPECT_ERROR(LuaUtil::safeGet(t, "any key"), "meta index error"); EXPECT_ERROR(LuaUtil::safeGet(t, "any key"), "meta index error");
} }

@ -16,16 +16,16 @@
namespace LuaUtil namespace LuaUtil
{ {
static VFS::Path::Normalized packageNameToVfsPath(std::string_view packageName, const VFS::Manager& vfs)
static std::string packageNameToVfsPath(std::string_view packageName, const VFS::Manager* vfs) {
{ std::string pathValue(packageName);
std::string path(packageName); std::replace(pathValue.begin(), pathValue.end(), '.', '/');
std::replace(path.begin(), path.end(), '.', '/'); VFS::Path::Normalized pathWithInit(pathValue + "/init.lua");
std::string pathWithInit = path + "/init.lua"; pathValue.append(".lua");
path.append(".lua"); VFS::Path::Normalized path(pathValue);
if (vfs->exists(path)) if (vfs.exists(path))
return path; return path;
else if (vfs->exists(pathWithInit)) else if (vfs.exists(pathWithInit))
return pathWithInit; return pathWithInit;
else else
throw std::runtime_error("module not found: " + std::string(packageName)); throw std::runtime_error("module not found: " + std::string(packageName));
@ -202,7 +202,7 @@ namespace LuaUtil
sol["setEnvironment"] sol["setEnvironment"]
= [](const sol::environment& env, const sol::function& fn) { sol::set_environment(env, fn); }; = [](const sol::environment& env, const sol::function& fn) { sol::set_environment(env, fn); };
sol["loadFromVFS"] = [this](std::string_view packageName) { 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); }; 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)); mCommonPackages.insert_or_assign(std::move(packageName), std::move(package));
} }
sol::protected_function_result LuaState::runInNewSandbox(const std::string& path, const std::string& namePrefix, sol::protected_function_result LuaState::runInNewSandbox(const VFS::Path::Normalized& path,
const std::map<std::string, sol::object>& packages, const sol::object& hiddenData) const std::string& envName, const std::map<std::string, sol::object>& packages, const sol::object& hiddenData)
{ {
// TODO // TODO
sol::protected_function script = loadScriptAndCache(path); sol::protected_function script = loadScriptAndCache(path);
sol::environment env(mSol, sol::create, mSandboxEnv); 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["_G"] = env;
env[sol::metatable_key]["__metatable"] = false; env[sol::metatable_key]["__metatable"] = false;
@ -395,12 +394,12 @@ namespace LuaUtil
return std::move(res); 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); auto iter = mCompiledScripts.find(path);
if (iter != mCompiledScripts.end()) 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 // 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 // fail because we've hit our Lua memory cap
if (!res.valid()) if (!res.valid())
@ -412,10 +411,10 @@ namespace LuaUtil
return res; 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<char>(*mVFS->get(path)), {}); std::string fileContent(std::istreambuf_iterator<char>(*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()) if (!res.valid())
throw std::runtime_error(std::string("Lua error: ") += res.get<sol::error>().what()); throw std::runtime_error(std::string("Lua error: ") += res.get<sol::error>().what());
return res; return res;

@ -7,6 +7,8 @@
#include <sol/sol.hpp> #include <sol/sol.hpp>
#include <components/vfs/pathutil.hpp>
#include "configuration.hpp" #include "configuration.hpp"
namespace VFS namespace VFS
@ -141,13 +143,13 @@ namespace LuaUtil
// (the result is expected to be an interface of the script). // (the result is expected to be an interface of the script).
// Args: // Args:
// path: path to the script in the virtual filesystem; // path: path to the script in the virtual filesystem;
// namePrefix: sandbox name will be "<namePrefix>[<filePath>]". Sandbox name // envName: sandbox name.
// will be added to every `print` output.
// packages: additional packages that should be available from the sandbox via `require`. Each package // 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 // 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. // (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 = "", sol::protected_function_result runInNewSandbox(const VFS::Path::Normalized& path,
const std::map<std::string, sol::object>& packages = {}, const sol::object& hiddenData = sol::nil); const std::string& envName = "unnamed", const std::map<std::string, sol::object>& packages = {},
const sol::object& hiddenData = sol::nil);
void dropScriptCache() { mCompiledScripts.clear(); } void dropScriptCache() { mCompiledScripts.clear(); }
@ -157,7 +159,7 @@ namespace LuaUtil
// directly. // directly.
void addInternalLibSearchPath(const std::filesystem::path& path) { mLibSearchPaths.push_back(path); } void addInternalLibSearchPath(const std::filesystem::path& path) { mLibSearchPaths.push_back(path); }
sol::function loadInternalLib(std::string_view libName); 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(); sol::environment newInternalLibEnvironment();
uint64_t getTotalMemoryUsage() const { return mSol.memory_used(); } uint64_t getTotalMemoryUsage() const { return mSol.memory_used(); }
@ -182,7 +184,7 @@ namespace LuaUtil
friend sol::protected_function_result call( friend sol::protected_function_result call(
ScriptId scriptId, const sol::protected_function& fn, Args&&... args); 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 countHook(lua_State* L, lua_Debug* ar);
static void* trackingAllocator(void* ud, void* ptr, size_t osize, size_t nsize); static void* trackingAllocator(void* ud, void* ptr, size_t osize, size_t nsize);
@ -227,7 +229,7 @@ namespace LuaUtil
sol::state_view mSol; sol::state_view mSol;
const ScriptsConfiguration* mConf; const ScriptsConfiguration* mConf;
sol::table mSandboxEnv; sol::table mSandboxEnv;
std::map<std::string, sol::bytecode> mCompiledScripts; std::map<VFS::Path::Normalized, sol::bytecode> mCompiledScripts;
std::map<std::string, sol::object> mCommonPackages; std::map<std::string, sol::object> mCommonPackages;
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
std::vector<std::filesystem::path> mLibSearchPaths; std::vector<std::filesystem::path> mLibSearchPaths;

@ -97,7 +97,7 @@ namespace LuaUtil
try 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) if (scriptOutput == sol::nil)
return true; return true;
sol::object engineHandlers = sol::nil, eventHandlers = sol::nil; sol::object engineHandlers = sol::nil, eventHandlers = sol::nil;

Loading…
Cancel
Save