前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >lua5.2引入_ENV的原因

lua5.2引入_ENV的原因

作者头像
用户4766018
发布2022-08-19 10:05:08
3180
发布2022-08-19 10:05:08
举报
文章被收录于专栏:格物致知格物致知

Unlike local variables, which are stored in a special data structure in the interpreter, global variables are just stored in a table. A useful feature in Lua is the ability to change this table per-function, so the function sees a different set of global variables.

The default global table is stored inside itself under the key "_G", this is useful if you want to get an actual reference to it.

The way environments work in Lua 5.2 is very different from 5.1. Both ways will be explained here.

Environments in Lua 5.2

A function's environment is stored in an upvalue, named _ENV. As an example, here's a function that sets its environment to a custom one and uses variables from it:

代码语言:javascript
复制
print(_ENV == _G) -- prints true, since the default _ENV is set to the global table

a = 1

local function f(t)
  local print = print -- since we will change the environment, standard functions will not be visible

  local _ENV = t -- change the environment. without the local, this would change the environment for the entire chunk

  print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env
  
  a = 2 -- create a new entry in t, doesn't touch the original "a" global
  b = 3 -- create a new entry in t
end

local t = {}
f(t)

print(a, b) --> 1 nil
print(t.a, t.b) --> 2 3

When loading a chunk, the top-level function gets a new _ENV upvalue, and any nested functions inside it can see it. You can pretend that when loading works something like this:

代码语言:javascript
复制
local _ENV = _G
return function (...) -- this function is what's returned from load
  -- code you passed to load goes here, with all global variable names replaced with _ENV lookups
  -- so, for example "a = b" becomes "_ENV.a = _ENV.b" if neither a nor b were declared local
end

Now you can see that _ENV is an ordinary local variable, how all the functions have access to the _ENV, and why if one function changes _ENV all other functions in the loaded chunks will see the change. That's why if you want a function to only change its own environment, you need to make a new _ENV local that shadows the original one.

In most cases, you don't need to use environments, unless you want to sandbox a loaded chunk, to give it convenient access to certain functions by making them look global, or to prevent it from seeing unsafe functions for security reasons. This is why 5.2's load function takes a parameter that lets you set the chunk's _ENV to a custom table, instead of _G.

代码语言:javascript
复制
local sandbox_env = {
  print = print,
}

local chunk = load("print('inside sandbox'); os.execute('echo unsafe')", "sandbox string", "bt", sandbox_env)

chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil

If you actually want to make a sandbox to run untrusted code, remember that it's easy to overlook a lot of things that can be exploited, and that you would need some kind of way to limit CPU usage and memory.

Environments in Lua 5.1

In Lua 5.1, environments are their own thing, not related to locals or upvalues. Instead, each function has an environment table associated with it, that can be manipulated with the getfenv/setfenv standard functions.

getfenv and setfenv both take a function or stack level (where 1 is the current function, 2 is the function that called the current function, etc.). setfenv has a second parameter that takes the new env table, and getfenv returns the functions's current env table.

The previous example rewritten for 5.1:

代码语言:javascript
复制
print(getfenv(1) == _G) -- prints true, since the default env is set to the global table

a = 1

local function f(t)
  local print = print -- since we will change the environment, standard functions will not be visible

  setfenv(1, t) -- change the environment

  print(getmetatable) -- prints nil, since global variables (including the standard functions) are not in the new env
  
  a = 2 -- create a new entry in t, doesn't touch the original "a" global
  b = 3 -- create a new entry in t
end

local t = {}
f(t)

print(a, b) --> 1 nil
print(t.a, t.b) --> 2 3

And the sandbox example rewritten for 5.1:

代码语言:javascript
复制
local sandbox_env = {
  print = print,
}

local chunk = loadstring("print('inside sandbox'); os.execute('echo unsafe')")
setfenv(chunk, sandbox_env)

chunk() -- prevents os.execute from being called, instead raises an error saying that os is nil

The 5.1 way is sometimes considered simpler and more versatile, but it also requires special treatment of environments (instead of using the existing local variable system). Also, the 5.2 way is designed with the idea that a function's environment is supposed to be private, instead of being accessible from everywhere without the debug library, so it can be considered safer.

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-01-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Environments in Lua 5.2
  • Environments in Lua 5.1
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档