From 7549496162dd665b6fde48262dcf968c8afccda6 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 14 Dec 2021 00:36:26 +0100 Subject: [PATCH] A few small fixes + expose makeReadOnly to Lua + an option to apply makeReadOnly during deserialize --- apps/openmw/mwlua/cellbindings.cpp | 8 +++++ apps/openmw/mwlua/inputbindings.cpp | 2 +- apps/openmw/mwlua/userdataserializer.cpp | 2 +- .../lua/test_serialization.cpp | 27 ++++++++++++----- .../lua/test_utilpackage.cpp | 3 ++ components/lua/luastate.cpp | 3 +- components/lua/serialization.cpp | 30 +++++++++++-------- components/lua/serialization.hpp | 6 ++-- components/lua/utilpackage.cpp | 1 + files/lua_api/openmw/util.lua | 6 ++++ 10 files changed, 61 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwlua/cellbindings.cpp b/apps/openmw/mwlua/cellbindings.cpp index a23fb47c32..4ca9018cad 100644 --- a/apps/openmw/mwlua/cellbindings.cpp +++ b/apps/openmw/mwlua/cellbindings.cpp @@ -4,6 +4,14 @@ #include "../mwworld/cellstore.hpp" +namespace sol +{ + template <> + struct is_automagical : std::false_type {}; + template <> + struct is_automagical : std::false_type {}; +} + namespace MWLua { diff --git a/apps/openmw/mwlua/inputbindings.cpp b/apps/openmw/mwlua/inputbindings.cpp index 8aecd1269c..9ca2d94770 100644 --- a/apps/openmw/mwlua/inputbindings.cpp +++ b/apps/openmw/mwlua/inputbindings.cpp @@ -54,7 +54,7 @@ namespace MWLua { return input->isControllerButtonPressed(static_cast(button)); }; - api["isMouseButtonPressed"] = [input](int button) -> bool + api["isMouseButtonPressed"] = [](int button) -> bool { return SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(button); }; diff --git a/apps/openmw/mwlua/userdataserializer.cpp b/apps/openmw/mwlua/userdataserializer.cpp index 6946cd5532..b675774c53 100644 --- a/apps/openmw/mwlua/userdataserializer.cpp +++ b/apps/openmw/mwlua/userdataserializer.cpp @@ -33,7 +33,7 @@ namespace MWLua // Deserializes userdata of type "typeName" from binaryData. Should push the result on stack using sol::stack::push. // Returns false if this type is not supported by this serializer. - bool deserialize(std::string_view typeName, std::string_view binaryData, sol::state& lua) const override + bool deserialize(std::string_view typeName, std::string_view binaryData, lua_State* lua) const override { if (typeName == "o") { diff --git a/apps/openmw_test_suite/lua/test_serialization.cpp b/apps/openmw_test_suite/lua/test_serialization.cpp index 1983daa158..9300e4571b 100644 --- a/apps/openmw_test_suite/lua/test_serialization.cpp +++ b/apps/openmw_test_suite/lua/test_serialization.cpp @@ -121,14 +121,25 @@ namespace std::string serialized = LuaUtil::serialize(table); EXPECT_EQ(serialized.size(), 123); sol::table res_table = LuaUtil::deserialize(lua, serialized); + sol::table res_readonly_table = LuaUtil::deserialize(lua, serialized, nullptr, true); - EXPECT_EQ(res_table.get("aa"), 1); - EXPECT_EQ(res_table.get("ab"), true); - EXPECT_EQ(res_table.get("nested").get("aa"), 2); - EXPECT_EQ(res_table.get("nested").get("bb"), "something"); - EXPECT_FLOAT_EQ(res_table.get("nested").get(5), -0.5); - EXPECT_EQ(res_table.get(1), osg::Vec2f(1, 2)); - EXPECT_EQ(res_table.get(2), osg::Vec2f(2, 1)); + for (auto t : {res_table, res_readonly_table}) + { + EXPECT_EQ(t.get("aa"), 1); + EXPECT_EQ(t.get("ab"), true); + EXPECT_EQ(t.get("nested").get("aa"), 2); + EXPECT_EQ(t.get("nested").get("bb"), "something"); + EXPECT_FLOAT_EQ(t.get("nested").get(5), -0.5); + EXPECT_EQ(t.get(1), osg::Vec2f(1, 2)); + EXPECT_EQ(t.get(2), osg::Vec2f(2, 1)); + } + + lua["t"] = res_table; + lua["ro_t"] = res_readonly_table; + EXPECT_NO_THROW(lua.safe_script("t.x = 5")); + EXPECT_NO_THROW(lua.safe_script("t.nested.x = 5")); + EXPECT_ERROR(lua.safe_script("ro_t.x = 5"), "userdata value"); + EXPECT_ERROR(lua.safe_script("ro_t.nested.x = 5"), "userdata value"); } struct TestStruct1 { double a, b; }; @@ -157,7 +168,7 @@ namespace return false; } - bool deserialize(std::string_view typeName, std::string_view binaryData, sol::state& lua) const override + bool deserialize(std::string_view typeName, std::string_view binaryData, lua_State* lua) const override { if (typeName == "ts1") { diff --git a/apps/openmw_test_suite/lua/test_utilpackage.cpp b/apps/openmw_test_suite/lua/test_utilpackage.cpp index 21d2a344d4..14b7021532 100644 --- a/apps/openmw_test_suite/lua/test_utilpackage.cpp +++ b/apps/openmw_test_suite/lua/test_utilpackage.cpp @@ -120,6 +120,9 @@ namespace EXPECT_FLOAT_EQ(get(lua, "util.clamp(0.1, 0, 1.5)"), 0.1); EXPECT_FLOAT_EQ(get(lua, "util.clamp(-0.1, 0, 1.5)"), 0); EXPECT_FLOAT_EQ(get(lua, "util.clamp(2.1, 0, 1.5)"), 1.5); + lua.safe_script("t = util.makeReadOnly({x = 1})"); + EXPECT_FLOAT_EQ(get(lua, "t.x"), 1); + EXPECT_ERROR(lua.safe_script("t.y = 2"), "userdata value"); } } diff --git a/components/lua/luastate.cpp b/components/lua/luastate.cpp index e78f7bed06..61637d7b07 100644 --- a/components/lua/luastate.cpp +++ b/components/lua/luastate.cpp @@ -76,9 +76,8 @@ namespace LuaUtil lua_State* lua = table.lua_state(); table[sol::meta_function::index] = table; - sol::stack::push(lua, std::move(table)); lua_newuserdata(lua, 0); - lua_pushvalue(lua, -2); + sol::stack::push(lua, std::move(table)); lua_setmetatable(lua, -2); return sol::stack::pop(lua); } diff --git a/components/lua/serialization.cpp b/components/lua/serialization.cpp index 2e13cfe29f..f9b8951e4e 100644 --- a/components/lua/serialization.cpp +++ b/components/lua/serialization.cpp @@ -5,6 +5,8 @@ #include +#include "luastate.hpp" + namespace LuaUtil { @@ -147,7 +149,8 @@ namespace LuaUtil throw std::runtime_error("Unknown Lua type."); } - static void deserializeImpl(sol::state& lua, std::string_view& binaryData, const UserdataSerializer* customSerializer) + static void deserializeImpl(lua_State* lua, std::string_view& binaryData, + const UserdataSerializer* customSerializer, bool readOnly) { if (binaryData.empty()) throw std::runtime_error("Unexpected end of serialized data."); @@ -176,22 +179,22 @@ namespace LuaUtil if (type & SHORT_STRING_FLAG) { size_t size = type & 0x1f; - sol::stack::push(lua.lua_state(), binaryData.substr(0, size)); + sol::stack::push(lua, binaryData.substr(0, size)); binaryData = binaryData.substr(size); return; } switch (static_cast(type)) { case SerializedType::NUMBER: - sol::stack::push(lua.lua_state(), getValue(binaryData)); + sol::stack::push(lua, getValue(binaryData)); return; case SerializedType::BOOLEAN: - sol::stack::push(lua.lua_state(), getValue(binaryData) != 0); + sol::stack::push(lua, getValue(binaryData) != 0); return; case SerializedType::LONG_STRING: { uint32_t size = getValue(binaryData); - sol::stack::push(lua.lua_state(), binaryData.substr(0, size)); + sol::stack::push(lua, binaryData.substr(0, size)); binaryData = binaryData.substr(size); return; } @@ -200,13 +203,15 @@ namespace LuaUtil lua_createtable(lua, 0, 0); while (!binaryData.empty() && binaryData[0] != char(SerializedType::TABLE_END)) { - deserializeImpl(lua, binaryData, customSerializer); - deserializeImpl(lua, binaryData, customSerializer); + deserializeImpl(lua, binaryData, customSerializer, readOnly); + deserializeImpl(lua, binaryData, customSerializer, readOnly); lua_settable(lua, -3); } if (binaryData.empty()) throw std::runtime_error("Unexpected end of serialized data."); binaryData = binaryData.substr(1); + if (readOnly) + sol::stack::push(lua, makeReadOnly(sol::stack::pop(lua))); return; } case SerializedType::TABLE_END: @@ -215,7 +220,7 @@ namespace LuaUtil { float x = getValue(binaryData); float y = getValue(binaryData); - sol::stack::push(lua.lua_state(), osg::Vec2f(x, y)); + sol::stack::push(lua, osg::Vec2f(x, y)); return; } case SerializedType::VEC3: @@ -223,7 +228,7 @@ namespace LuaUtil float x = getValue(binaryData); float y = getValue(binaryData); float z = getValue(binaryData); - sol::stack::push(lua.lua_state(), osg::Vec3f(x, y, z)); + sol::stack::push(lua, osg::Vec3f(x, y, z)); return; } } @@ -240,7 +245,8 @@ namespace LuaUtil return res; } - sol::object deserialize(sol::state& lua, std::string_view binaryData, const UserdataSerializer* customSerializer) + sol::object deserialize(lua_State* lua, std::string_view binaryData, + const UserdataSerializer* customSerializer, bool readOnly) { if (binaryData.empty()) return sol::nil; @@ -248,10 +254,10 @@ namespace LuaUtil throw std::runtime_error("Incorrect version of Lua serialization format: " + std::to_string(static_cast(binaryData[0]))); binaryData = binaryData.substr(1); - deserializeImpl(lua, binaryData, customSerializer); + deserializeImpl(lua, binaryData, customSerializer, readOnly); if (!binaryData.empty()) throw std::runtime_error("Unexpected data after serialized object"); - return sol::stack::pop(lua.lua_state()); + return sol::stack::pop(lua); } } diff --git a/components/lua/serialization.hpp b/components/lua/serialization.hpp index fddae2cfb4..d685bb2ad6 100644 --- a/components/lua/serialization.hpp +++ b/components/lua/serialization.hpp @@ -1,7 +1,6 @@ #ifndef COMPONENTS_LUA_SERIALIZATION_H #define COMPONENTS_LUA_SERIALIZATION_H -#include // missing from sol/sol.hpp #include namespace LuaUtil @@ -21,14 +20,15 @@ namespace LuaUtil // Deserializes userdata of type "typeName" from binaryData. Should push the result on stack using sol::stack::push. // Returns false if this type is not supported by this serializer. - virtual bool deserialize(std::string_view typeName, std::string_view binaryData, sol::state&) const = 0; + virtual bool deserialize(std::string_view typeName, std::string_view binaryData, lua_State*) const = 0; protected: static void append(BinaryData&, std::string_view typeName, const void* data, size_t dataSize); }; BinaryData serialize(const sol::object&, const UserdataSerializer* customSerializer = nullptr); - sol::object deserialize(sol::state& lua, std::string_view binaryData, const UserdataSerializer* customSerializer = nullptr); + sol::object deserialize(lua_State* lua, std::string_view binaryData, + const UserdataSerializer* customSerializer = nullptr, bool readOnly = false); } diff --git a/components/lua/utilpackage.cpp b/components/lua/utilpackage.cpp index b68fc7afa4..abb680a6bc 100644 --- a/components/lua/utilpackage.cpp +++ b/components/lua/utilpackage.cpp @@ -175,6 +175,7 @@ namespace LuaUtil util["clamp"] = [](float value, float from, float to) { return std::clamp(value, from, to); }; // NOTE: `util["clamp"] = std::clamp` causes error 'AddressSanitizer: stack-use-after-scope' util["normalizeAngle"] = &Misc::normalizeAngle; + util["makeReadOnly"] = &makeReadOnly; return util; } diff --git a/files/lua_api/openmw/util.lua b/files/lua_api/openmw/util.lua index fdb140ad53..22986f09a6 100644 --- a/files/lua_api/openmw/util.lua +++ b/files/lua_api/openmw/util.lua @@ -19,6 +19,12 @@ -- @param #number angle Angle in radians -- @return #number Angle in range `[-pi, pi]` +------------------------------------------------------------------------------- +-- Makes a table read only. +-- @function [parent=#util] makeReadOnly +-- @param #table table Any table. +-- @return #table The same table wrapped with read only userdata. + ------------------------------------------------------------------------------- -- Immutable 2D vector