lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Idk if I consider this a proposal or not, but what if environments had stacks?

For example, let's say we add a new keyword "setsenv" (set scope environment), and another one "getsenv" (get scope environment) (keywords that act like functions? yeah I like that :P), and we have some code like this:

print("outside scope", getsenv)
do
setsenv {print=print, tostring=tostring} -- setting tostring would be needed as print would act on the current env
print("inside scope", getsenv)
end
print("outside scope, again", getsenv)

This would output bytecode like this: (pseudobytecode)

GETGLOBAL "print" 0
    LOADK "outside scope" 1
   GETENV 2
     CALL 0 2 0 -- call function at 0 with 2 arguments, put results starting from 0
-- some table boilerplate
  PUSHENV 0

GETGLOBAL "print" 0
    LOADK "inside scope" 1
   GETENV 2
     CALL 0 2 0
   POPENV -- caused by "end" in "do...end"

GETGLOBAL "print" 0
    LOADK "outside scope, again" 1
   GETENV 2
     CALL 0 2 0

Q: Why would this be good?

A:
1. print()'s tostring() calls wouldn't depend on the "global globals" (that is, the "global environment", as stored in the C registry), but rather on the callers' currently active environment.
2. string metatable hacks for sandboxing would be possible like in Lua 5.1.[1]
3. other cool stuff[?]

Q: What about loading bytecode that pops the environment stack multiple times?

A: Environment stacks are per closure, not global.
-- 
Disclaimer: these emails are public and can be accessed from <TODO: get a non-DHCP IP and put it here>. If you do not agree with this, DO NOT REPLY.

[1]: This is just an example and I don't remember the right values so the getfenv offsets might be wrong, but basically this is the code:

local globalstringmetatable = getmetatable''

debug.setmetatable('', {
__index = function(t,k)
  local x = getmetatable(getfenv(2))
  if type(x) == "table" and rawget(x, "__stringmt") then
    local y = rawget(rawget(x, "__stringmt"), "__index")
    if type(y) == "table" then
      return y[k]
    elseif type(y) == "function" then
      return y(t,k)
    elseif y == nil then
      local y = rawget(globalstringmetatable, "__index")
      if type(y) == "table" then
        return y[k]
      elseif type(y) == "function" then
        return y(t,k)
      else
        error("Attempt to index a string value", 2)
      end
    else
      error("Attempt to index a string value", 2)
    end
  end
end
})