mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-18 13:12:50 +00:00
Merge branch 'lua_pairs' into 'master'
Proper support of `pairs` and `ipairs` in Lua; fix bug in `makeReadOnly`. See merge request OpenMW/openmw!1628
This commit is contained in:
commit
7a7a95407a
@ -49,7 +49,7 @@ namespace MWLua
|
||||
{
|
||||
auto* lua = context.mLua;
|
||||
sol::table api(lua->sol(), sol::create);
|
||||
api["API_REVISION"] = 16;
|
||||
api["API_REVISION"] = 17;
|
||||
api["quit"] = [lua]()
|
||||
{
|
||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||
|
@ -72,7 +72,7 @@ namespace MWLua
|
||||
else
|
||||
throw std::runtime_error("Index out of range");
|
||||
};
|
||||
listT["ipairs"] = [registry](const ListT& list)
|
||||
listT[sol::meta_function::ipairs] = [registry](const ListT& list)
|
||||
{
|
||||
auto iter = [registry](const ListT& l, int64_t i) -> sol::optional<std::tuple<int64_t, ObjectT>>
|
||||
{
|
||||
|
@ -39,6 +39,7 @@ return {
|
||||
-- should throw an error
|
||||
incorrectRequire = function() require('counter') end,
|
||||
modifySystemLib = function() math.sin = 5 end,
|
||||
modifySystemLib2 = function() math.__index.sin = 5 end,
|
||||
rawsetSystemLib = function() rawset(math, 'sin', 5) end,
|
||||
callLoadstring = function() loadstring('print(1)') end,
|
||||
setSqr = function() require('sqrlib').sqr = math.sin end,
|
||||
@ -119,6 +120,7 @@ return {
|
||||
// but read-only object can not be modified even with rawset
|
||||
EXPECT_ERROR(LuaUtil::call(script["rawsetSystemLib"]), "bad argument #1 to 'rawset' (table expected, got userdata)");
|
||||
EXPECT_ERROR(LuaUtil::call(script["modifySystemLib"]), "a userdata value");
|
||||
EXPECT_ERROR(LuaUtil::call(script["modifySystemLib2"]), "a nil value");
|
||||
|
||||
EXPECT_EQ(LuaUtil::getMutableFromReadOnly(LuaUtil::makeReadOnly(script)), script);
|
||||
}
|
||||
|
@ -58,20 +58,41 @@ namespace LuaUtil
|
||||
mLua["math"]["randomseed"] = []{};
|
||||
|
||||
mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
|
||||
mLua.script(R"(printToLog = function(name, ...)
|
||||
local msg = name
|
||||
for _, v in ipairs({...}) do
|
||||
msg = msg .. '\t' .. tostring(v)
|
||||
mLua["cmetatable"] = [](const sol::table& v) -> sol::object { return v[sol::metatable_key]; };
|
||||
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)
|
||||
end
|
||||
return _write(msg)
|
||||
end
|
||||
return writeToLog(msg)
|
||||
end)");
|
||||
mLua.script("printGen = function(name) return function(...) return printToLog(name, ...) end 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
|
||||
)");
|
||||
|
||||
// Some fixes for compatibility between different Lua versions
|
||||
if (mLua["unpack"] == sol::nil)
|
||||
mLua["unpack"] = mLua["table"]["unpack"];
|
||||
else if (mLua["table"]["unpack"] == sol::nil)
|
||||
mLua["table"]["unpack"] = mLua["unpack"];
|
||||
if (LUA_VERSION_NUM <= 501)
|
||||
{
|
||||
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
|
||||
)");
|
||||
}
|
||||
|
||||
mSandboxEnv = sol::table(mLua, sol::create);
|
||||
mSandboxEnv["_VERSION"] = mLua["_VERSION"];
|
||||
@ -106,24 +127,22 @@ namespace LuaUtil
|
||||
if (table.is<sol::userdata>())
|
||||
return table; // it is already userdata, no sense to wrap it again
|
||||
|
||||
lua_State* lua = table.lua_state();
|
||||
table[sol::meta_function::index] = table;
|
||||
lua_newuserdata(lua, 0);
|
||||
sol::stack::push(lua, std::move(table));
|
||||
lua_setmetatable(lua, -2);
|
||||
return sol::stack::pop<sol::table>(lua);
|
||||
lua_State* luaState = table.lua_state();
|
||||
sol::state_view lua(luaState);
|
||||
sol::table meta(lua, sol::create);
|
||||
meta["__index"] = table;
|
||||
meta["__pairs"] = lua["pairsForReadOnly"];
|
||||
meta["__ipairs"] = lua["ipairsForReadOnly"];
|
||||
|
||||
lua_newuserdata(luaState, 0);
|
||||
sol::stack::push(luaState, meta);
|
||||
lua_setmetatable(luaState, -2);
|
||||
return sol::stack::pop<sol::table>(luaState);
|
||||
}
|
||||
|
||||
sol::table getMutableFromReadOnly(const sol::userdata& ro)
|
||||
{
|
||||
lua_State* lua = ro.lua_state();
|
||||
sol::stack::push(lua, ro);
|
||||
int ok = lua_getmetatable(lua, -1);
|
||||
assert(ok);
|
||||
(void)ok;
|
||||
sol::table res = sol::stack::pop<sol::table>(lua);
|
||||
lua_pop(lua, 1);
|
||||
return res;
|
||||
return ro[sol::metatable_key].get<sol::table>()["__index"];
|
||||
}
|
||||
|
||||
void LuaState::addCommonPackage(std::string packageName, sol::object package)
|
||||
|
@ -439,10 +439,7 @@ namespace LuaUtil
|
||||
mEventHandlers.clear();
|
||||
mSimulationTimersQueue.clear();
|
||||
mGameTimersQueue.clear();
|
||||
|
||||
mPublicInterfaces.clear();
|
||||
// Assigned by LuaUtil::makeReadOnly, but `clear` removes it, so we need to assign it again.
|
||||
mPublicInterfaces[sol::meta_function::index] = mPublicInterfaces;
|
||||
}
|
||||
|
||||
ScriptsContainer::Script& ScriptsContainer::getScript(int scriptId)
|
||||
|
@ -4,7 +4,7 @@ Overview of Lua scripting
|
||||
Language and sandboxing
|
||||
=======================
|
||||
|
||||
OpenMW supports scripts written in Lua 5.1.
|
||||
OpenMW supports scripts written in Lua 5.1 with some extensions (see below) from Lua 5.2.
|
||||
There are no plans to switch to any newer version of the language, because newer versions are not supported by LuaJIT.
|
||||
|
||||
Here are starting points for learning Lua:
|
||||
@ -24,11 +24,22 @@ These libraries are loaded automatically and are always available.
|
||||
Allowed `basic functions <https://www.lua.org/manual/5.1/manual.html#5.1>`__:
|
||||
``assert``, ``error``, ``ipairs``, ``next``, ``pairs``, ``pcall``, ``print``, ``select``, ``tonumber``, ``tostring``, ``type``, ``unpack``, ``xpcall``, ``rawequal``, ``rawget``, ``rawset``, ``getmetatable``, ``setmetatable``.
|
||||
|
||||
Supported Lua 5.2 features:
|
||||
|
||||
- ``goto`` and ``::labels::``;
|
||||
- hex escapes ``\x3F`` and ``\*`` escape in strings;
|
||||
- ``math.log(x [,base])``;
|
||||
- ``string.rep(s, n [,sep])``;
|
||||
- in ``string.format()``: ``%q`` is reversible, ``%s`` uses ``__tostring``, ``%a`` and ``%A`` are added;
|
||||
- String matching pattern ``%g``;
|
||||
- ``__pairs`` and ``__ipairs`` metamethods;
|
||||
- Function ``table.unpack`` (alias to Lua 5.1 ``unpack``).
|
||||
|
||||
Loading libraries with ``require('library_name')`` is allowed, but limited. It works this way:
|
||||
|
||||
1. If `library_name` is one of the standard libraries, then return the library.
|
||||
2. If `library_name` is one of the built-in `API packages`_, then return the package.
|
||||
3. Otherwise search for a Lua source file with such name in :ref:`data folders <Multiple data folders>`. For example ``require('my_lua_library.something')`` will try to open the file ``my_lua_library/something.lua``.
|
||||
3. Otherwise search for a Lua source file with such name in :ref:`data folders <Multiple data folders>`. For example ``require('my_lua_library.something')`` will try to open one of the files ``my_lua_library/something.lua`` or ``my_lua_library/something/init.lua``.
|
||||
|
||||
Loading DLLs and precompiled Lua files is intentionally prohibited for compatibility and security reasons.
|
||||
|
||||
|
@ -293,11 +293,6 @@
|
||||
-- @type ObjectList
|
||||
-- @extends #list<#GameObject>
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Create iterator.
|
||||
-- @function [parent=#ObjectList] ipairs
|
||||
-- @param self
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Filter list with a Query.
|
||||
-- @function [parent=#ObjectList] select
|
||||
|
Loading…
x
Reference in New Issue
Block a user