在5.2中沙盒嵌入Lua / 为来自lua.file的函数设置环境

假设我至少有两个 Lua 脚本文件。

test1.lua test2.lua

两个文件都定义了一个 init 函数和其他类似名称的函数。

我该如何使用 Lua 5.2 将每个脚本文件加载到一个单独的环境中,以避免相同的函数名称冲突?我找到了一个 5.1 的示例代码,但无法使用(因为 setenv 已被删除,而 lua_setuservalue 似乎不起作用)。

示例在这里 Calling lua functions from .lua's using handles?

基本上,如果我将 setenv 替换为 setuservalue,则会出现访问冲突。

点赞
用户513763
用户513763

非官方 Lua 常见问题解答 中有关于 Lua 沙盒的条目。我的猜想是,你可以很容易地将该逻辑转换到你的 C/C++ 代码上。

另外请参见 lua-users wiki 上的 LuaFiveTo

更正

事实上并不像看上去的那么简单。但是最终的目标很简单:加载代码块,推入 _ENV 表,使用 lua_setupvalue(L,-2,1)。重要的是,该表应该在堆栈的顶部。

作为一个小例子,使用两个默认为 _G 的环境来通过元表读取内容:

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(void){
        lua_State *L = luaL_newstate();
        char *file1 = "file1.lua";
        char *file2 = "file2.lua";

        luaL_openlibs(L);

        luaL_loadfile(L,file2); // S: 1
        luaL_loadfile(L,file1); // S: 2
        lua_newtable(L); // ENV for file 1: S: 321
        lua_newtable(L); // ENV for file 2: S: 4321

        //让每个函数都有它自己的元表,其中已经有的查找会在全局表_G中进行

        lua_newtable(L); // metatable S: 54321
        lua_getglobal(L,"_G"); // pushes _G, which will be the __index metatable entry S: 654321

        lua_setfield(L,-2,"__index"); // metatable on top S: 54321
        lua_pushvalue(L,-1); // copy the metatable S: 554321
        lua_setmetatable(L,-3); // set the last copy for env2 S: 54321
        lua_setmetatable(L,-3); // set the original for env1  S: 4321
        // 这里我们在堆栈上为两个环境有两个表
        lua_setupvalue(L,1,1); // 第一个 upvalue == _ENV,所以设置它。S: 321
        lua_setupvalue(L,2,1); // 设置文件的 _ENV S: 21
        // 堆栈上剩下的是:带有设置环境的2个代码块。
        lua_pcall(L,0,LUA_MULTRET,0);
        lua_pcall(L,0,LUA_MULTRET,0);
        lua_close(L);
        return 0;
}

以及两个 Lua 文件:

-- file1.lua
function init()
        A="foo"
        print("Hello from file1")
        print(A)
end
init()

-- file2.lua
-- 这表明了在 file1 中定义的内容不会污染 file2 的环境。
print("init function is",tostring(init))
function init()
        A="bar"
        print("Hello from file2")
        print(A)
end
init()
2012-06-13 12:31:23
用户734069
用户734069

两者都定义了一个初始化函数和其他类似名称的函数。

首先,为什么这些函数是全局的?它们应该是脚本的局部变量。如果你要在其他文件中require它们,它们应该创建并返回一个包含要公开的函数的表格。

现代的做法是,在需要这些文件的地方做如下操作:

local Library = require 'library'

Library.Func1(...)

这样不会污染全局 Lua 命名空间。你使用局部变量。

但是,如果你坚持像这样使用全局变量,你可以按照文档所说的那样:更改已编译代码块的第一个上值。

如果我将 setenv替换为 setuservalue - 我会得到访问冲突的错误。

当然会,这不是lua_setuservalue的作用。它用于设置与用户数据相关联的值。你需要的是被适当称作lua_setupvalue的函数。

在使用你提到的示例代码时,正确的做法是:

lua_setupvalue(L,-2,1);
2012-06-13 14:31:58