Reputation: 3440
Lets assume I have a module:
-- env.lua
local env = {}
function env.resolve(str)
print("mod", _ENV)
if _resolve_path ~= nil then
return _resolve_path(str)
else
error("bad env")
end
end
return env
and some code using it:
-- sandbox demo
-- run as: lua env-test.lua
env = require('env')
function _resolve_path(path)
return "/" .. path
end
print("before main()")
print("", _ENV)
print("", env.resolve("test"))
local sandbox
do
local _ENV = {
print = print,
env = env,
_resolve_path = function (path)
return "/chroot/" .. path
end
}
function sandbox()
print("from sandbox()")
print("", _ENV)
print("", env.resolve("test"))
end
end
sandbox()
print("after main()")
print("", _ENV)
print("", env.resolve("test"))
What I'd like to achieve is that env.resolve()
from sandbox() would use the custom _resolve_path function from the environment. It see that the environment is not applied to code called from the sandboxed function though. The goal is to augument how some modules behave depending on where they are called from. E.g. having sandbox{1,2,3}() with different local _resolve_path() functions.
Upvotes: 4
Views: 746
Reputation: 3103
When you load your module with require
it is bound to the global environment. Once a function is created in an environment it has that environment for its entire lifetime.
Prior to Lua 5.2 you could use set/getfenv to alter the environment, but environments are now lexical. The environment can only be changed with the debug library by changing the _ENV
upvalue.
So, how can you run the same function within different environments? You can pass in the environment as a parameter:
function env.resolve(str, _ENV)
print("mod", _ENV)
if _resolve_path ~= nil then
return _resolve_path(str)
else
error("bad env")
end
end
Where you then call resolve
like:
env.resolve('test', _ENV)
Or, if you would prefer the environment didn't have to be specified for every resolve
call you can bind a resolve
function to each new environment:
-- env.lua
local print = print
local error = error
local env = {}
-- this is the actual resolve function that takes the environment as a parameter
local function resolve_env(str, _ENV)
print("mod", _ENV)
if _resolve_path ~= nil then
return _resolve_path(str)
else
error("bad env")
end
end
-- this is the module (ie. global) resolve
function env.resolve(str)
return resolve_env(str, _ENV)
end
-- this function binds a resolve function to a sandbox environment
function env.bind(_ENV)
_ENV.env = {
resolve = function(str)
return resolve_env(str, _ENV)
end
}
return _ENV
end
return env
The sandbox can now setup a bound resolve:
-- sandbox.lua
env = require 'env'
function _resolve_path(path)
return "/" .. path
end
print("before main()")
print("", _ENV)
print("", env.resolve("test"))
local sandbox; do
local _ENV = env.bind{
print = print,
_resolve_path = function (path)
return "/chroot/" .. path
end
}
function sandbox()
print("from sandbox()")
print("", _ENV)
print("", env.resolve("test"))
end
end
sandbox()
print("after main()")
print("", _ENV)
print("", env.resolve("test"))
Which will yield the result:
$ lua sandbox.lua
before main()
table: 00612f40
mod table: 00612f40
/test
from sandbox()
table: 0061c7a8
mod table: 0061c7a8
/chroot/test
after main()
table: 00612f40
mod table: 00612f40
/test
Upvotes: 3