mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +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;
|
auto* lua = context.mLua;
|
||||||
sol::table api(lua->sol(), sol::create);
|
sol::table api(lua->sol(), sol::create);
|
||||||
api["API_REVISION"] = 16;
|
api["API_REVISION"] = 17;
|
||||||
api["quit"] = [lua]()
|
api["quit"] = [lua]()
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
|
||||||
|
@ -72,7 +72,7 @@ namespace MWLua
|
|||||||
else
|
else
|
||||||
throw std::runtime_error("Index out of range");
|
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>>
|
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
|
-- should throw an error
|
||||||
incorrectRequire = function() require('counter') end,
|
incorrectRequire = function() require('counter') end,
|
||||||
modifySystemLib = function() math.sin = 5 end,
|
modifySystemLib = function() math.sin = 5 end,
|
||||||
|
modifySystemLib2 = function() math.__index.sin = 5 end,
|
||||||
rawsetSystemLib = function() rawset(math, 'sin', 5) end,
|
rawsetSystemLib = function() rawset(math, 'sin', 5) end,
|
||||||
callLoadstring = function() loadstring('print(1)') end,
|
callLoadstring = function() loadstring('print(1)') end,
|
||||||
setSqr = function() require('sqrlib').sqr = math.sin 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
|
// 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["rawsetSystemLib"]), "bad argument #1 to 'rawset' (table expected, got userdata)");
|
||||||
EXPECT_ERROR(LuaUtil::call(script["modifySystemLib"]), "a userdata value");
|
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);
|
EXPECT_EQ(LuaUtil::getMutableFromReadOnly(LuaUtil::makeReadOnly(script)), script);
|
||||||
}
|
}
|
||||||
|
@ -58,20 +58,41 @@ namespace LuaUtil
|
|||||||
mLua["math"]["randomseed"] = []{};
|
mLua["math"]["randomseed"] = []{};
|
||||||
|
|
||||||
mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
|
mLua["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
|
||||||
mLua.script(R"(printToLog = function(name, ...)
|
mLua["cmetatable"] = [](const sol::table& v) -> sol::object { return v[sol::metatable_key]; };
|
||||||
local msg = name
|
mLua.script(R"(
|
||||||
for _, v in ipairs({...}) do
|
local _pairs = pairs
|
||||||
msg = msg .. '\t' .. tostring(v)
|
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
|
end
|
||||||
return writeToLog(msg)
|
printGen = function(name) return function(...) return printToLog(name, ...) end end
|
||||||
end)");
|
|
||||||
mLua.script("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
|
// Some fixes for compatibility between different Lua versions
|
||||||
if (mLua["unpack"] == sol::nil)
|
if (mLua["unpack"] == sol::nil)
|
||||||
mLua["unpack"] = mLua["table"]["unpack"];
|
mLua["unpack"] = mLua["table"]["unpack"];
|
||||||
else if (mLua["table"]["unpack"] == sol::nil)
|
else if (mLua["table"]["unpack"] == sol::nil)
|
||||||
mLua["table"]["unpack"] = mLua["unpack"];
|
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 = sol::table(mLua, sol::create);
|
||||||
mSandboxEnv["_VERSION"] = mLua["_VERSION"];
|
mSandboxEnv["_VERSION"] = mLua["_VERSION"];
|
||||||
@ -106,24 +127,22 @@ namespace LuaUtil
|
|||||||
if (table.is<sol::userdata>())
|
if (table.is<sol::userdata>())
|
||||||
return table; // it is already userdata, no sense to wrap it again
|
return table; // it is already userdata, no sense to wrap it again
|
||||||
|
|
||||||
lua_State* lua = table.lua_state();
|
lua_State* luaState = table.lua_state();
|
||||||
table[sol::meta_function::index] = table;
|
sol::state_view lua(luaState);
|
||||||
lua_newuserdata(lua, 0);
|
sol::table meta(lua, sol::create);
|
||||||
sol::stack::push(lua, std::move(table));
|
meta["__index"] = table;
|
||||||
lua_setmetatable(lua, -2);
|
meta["__pairs"] = lua["pairsForReadOnly"];
|
||||||
return sol::stack::pop<sol::table>(lua);
|
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)
|
sol::table getMutableFromReadOnly(const sol::userdata& ro)
|
||||||
{
|
{
|
||||||
lua_State* lua = ro.lua_state();
|
return ro[sol::metatable_key].get<sol::table>()["__index"];
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaState::addCommonPackage(std::string packageName, sol::object package)
|
void LuaState::addCommonPackage(std::string packageName, sol::object package)
|
||||||
|
@ -439,10 +439,7 @@ namespace LuaUtil
|
|||||||
mEventHandlers.clear();
|
mEventHandlers.clear();
|
||||||
mSimulationTimersQueue.clear();
|
mSimulationTimersQueue.clear();
|
||||||
mGameTimersQueue.clear();
|
mGameTimersQueue.clear();
|
||||||
|
|
||||||
mPublicInterfaces.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)
|
ScriptsContainer::Script& ScriptsContainer::getScript(int scriptId)
|
||||||
|
@ -4,7 +4,7 @@ Overview of Lua scripting
|
|||||||
Language and sandboxing
|
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.
|
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:
|
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>`__:
|
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``.
|
``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:
|
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.
|
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.
|
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.
|
Loading DLLs and precompiled Lua files is intentionally prohibited for compatibility and security reasons.
|
||||||
|
|
||||||
|
@ -293,11 +293,6 @@
|
|||||||
-- @type ObjectList
|
-- @type ObjectList
|
||||||
-- @extends #list<#GameObject>
|
-- @extends #list<#GameObject>
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Create iterator.
|
|
||||||
-- @function [parent=#ObjectList] ipairs
|
|
||||||
-- @param self
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- Filter list with a Query.
|
-- Filter list with a Query.
|
||||||
-- @function [parent=#ObjectList] select
|
-- @function [parent=#ObjectList] select
|
||||||
|
Loading…
x
Reference in New Issue
Block a user