[5.2+] Can a function access/modify/replace its caller's _ENV?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

[5.2+] Can a function access/modify/replace its caller's _ENV?

Jonathan Goble
Can a function (Lua or C) in Lua 5.2+ gain access to its caller's _ENV, and if so, can it modify that table and/or replace that table with a new one? I know access and modification are trivial if you require passing the _ENV table as an argument, and replacement is trivial if you simply return a table that documentation specifies must be assigned to _ENV. I'm not interested in those simple "tricks".

What I'm looking for is an approach that allows all three (access, modify, and replace) without writing "_ENV" (or the name of any variable that refers to _ENV) anywhere in the statement that calls the function. I'm open to any solution, including complicated debug library hacks and/or the C API, as long as it would work consistently. Bonus points for a single approach that works on 5.2, 5.3, and the 5.4work git HEAD with minimal or no branching to deal with differences between versions.

Any thoughts on how to do this? Or is it not possible?
Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Jonathan Goble
On Fri, Dec 21, 2018 at 10:47 PM Jonathan Goble <[hidden email]> wrote:
Can a function (Lua or C) in Lua 5.2+ gain access to its caller's _ENV, and if so, can it modify that table and/or replace that table with a new one? I know access and modification are trivial if you require passing the _ENV table as an argument, and replacement is trivial if you simply return a table that documentation specifies must be assigned to _ENV. I'm not interested in those simple "tricks".

What I'm looking for is an approach that allows all three (access, modify, and replace) without writing "_ENV" (or the name of any variable that refers to _ENV) anywhere in the statement that calls the function. I'm open to any solution, including complicated debug library hacks and/or the C API, as long as it would work consistently. Bonus points for a single approach that works on 5.2, 5.3, and the 5.4work git HEAD with minimal or no branching to deal with differences between versions.

Any thoughts on how to do this? Or is it not possible?

I played around in the REPL and think I've figured it out. debug.getupvalue and debug.setupvalue (and their C equivalents) give the ability to access and replace the upvalues of an arbitrary function, but need the function object itself rather than a stack level. To get the calling function, debug.getinfo with "uf" for the "what" parameter and 2 for the stack level gives me the caller and its number of upvalues. Then I pass the function to debug.getupvalue with each index up to the number of upvalues until I find the one I want. Finally, I can directly mutate the table, or replace it with another using debug.setupvalue with the same index. I haven't tried changing _ENV yet, but this has worked with other upvalues, so I assume it will work with _ENV as well.

*puts on mad scientist hat* Might attempt an interesting project during the break between semesters.
Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Sean Conner
It was thus said that the Great Jonathan Goble once stated:

> On Fri, Dec 21, 2018 at 10:47 PM Jonathan Goble <[hidden email]> wrote:
>
> > Can a function (Lua or C) in Lua 5.2+ gain access to its caller's _ENV,
> > and if so, can it modify that table and/or replace that table with a new
> > one? I know access and modification are trivial if you require passing the
> > _ENV table as an argument, and replacement is trivial if you simply return
> > a table that documentation specifies must be assigned to _ENV. I'm not
> > interested in those simple "tricks".
> >
> > What I'm looking for is an approach that allows all three (access, modify,
> > and replace) without writing "_ENV" (or the name of any variable that
> > refers to _ENV) anywhere in the statement that calls the function. I'm open
> > to any solution, including complicated debug library hacks and/or the C
> > API, as long as it would work consistently. Bonus points for a single
> > approach that works on 5.2, 5.3, and the 5.4work git HEAD with minimal or
> > no branching to deal with differences between versions.
> >
> > Any thoughts on how to do this? Or is it not possible?
> >
>
> I played around in the REPL and think I've figured it out. debug.getupvalue
> and debug.setupvalue (and their C equivalents) give the ability to access
> and replace the upvalues of an arbitrary function, but need the function
> object itself rather than a stack level. To get the calling function,
> debug.getinfo with "uf" for the "what" parameter and 2 for the stack level
> gives me the caller and its number of upvalues. Then I pass the function to
> debug.getupvalue with each index up to the number of upvalues until I find
> the one I want. Finally, I can directly mutate the table, or replace it
> with another using debug.setupvalue with the same index. I haven't tried
> changing _ENV yet, but this has worked with other upvalues, so I assume it
> will work with _ENV as well.
>
> *puts on mad scientist hat* Might attempt an interesting project during the
> break between semesters.

  You might want to check out the thread starting here:

        http://lua-users.org/lists/lua-l/2018-11/msg00386.html

  -spc

Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Jonathan Goble
On Sat, Dec 22, 2018 at 1:52 AM Sean Conner <[hidden email]> wrote:
  You might want to check out the thread starting here:

        http://lua-users.org/lists/lua-l/2018-11/msg00386.html

  -spc

Interesting. So _ENV may not exist as an upvalue, as I have just verified in the REPL. Still, chunks (as opposed to explicit functions) are guaranteed to have an _ENV upvalue, and that guarantee may be all I need for my project.
Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Dirk Laurie-2
In reply to this post by Jonathan Goble
Op Sa. 22 Des. 2018 om 05:48 het Jonathan Goble <[hidden email]> geskryf:
>
> Can a function (Lua or C) in Lua 5.2+ gain access to its caller's _ENV,

Only as an upvalue, and even then, only if your function refers to it,
e.g. via a global name.

> and if so, can it modify that table

Yes.

> and/or replace that table with a new one?

No.

> What I'm looking for is an approach that allows all three (access, modify, and replace) without writing "_ENV" (or the name of any variable that refers to _ENV) anywhere in the statement that calls the function.
> Any thoughts on how to do this? Or is it not possible?

Do you grant me the debug library?

Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Jonathan Goble
On Sat, Dec 22, 2018 at 2:54 AM Dirk Laurie <[hidden email]> wrote:
Op Sa. 22 Des. 2018 om 05:48 het Jonathan Goble <[hidden email]> geskryf:
>
> Can a function (Lua or C) in Lua 5.2+ gain access to its caller's _ENV,

Only as an upvalue, and even then, only if your function refers to it,
e.g. via a global name.

> and if so, can it modify that table

Yes.

> and/or replace that table with a new one?

No.

Even with debug.setupvalue?
 
> What I'm looking for is an approach that allows all three (access, modify, and replace) without writing "_ENV" (or the name of any variable that refers to _ENV) anywhere in the statement that calls the function.
> Any thoughts on how to do this? Or is it not possible?

Do you grant me the debug library?

To quote the sentence immediately following one you just quoted: "I'm open to any solution, including complicated debug library hacks and/or the C API, as long as it would work consistently."
Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Dirk Laurie-2
Op Sa. 22 Des. 2018 om 09:59 het Jonathan Goble <[hidden email]> geskryf:
>
> What I'm looking for is an approach that allows all three (access, modify, and replace) without writing "_ENV" (or the name of any variable that refers to _ENV) anywhere in the statement that calls the function.

Does the following formulation of your question accurately convey what you ask?
~~~
function global_function()
-- ...
end

local function upvalue_function()
-- ...
end

function f1(param_function)
  _ENV = {NAME='f1_env'; global_function = global_function}
  global_function()   -- Can this call change _ENV.NAME? Or even _ENV itself?
  upvalue_function()  -- Can this call change _ENV.NAME? Or even _ENV itself?
  param_function()    -- Can this call change _ENV.NAME? Or even _ENV itself?
end
~

Reply | Threaded
Open this post in threaded view
|

Re: [5.2+] Can a function access/modify/replace its caller's _ENV?

Jonathan Goble
On Sat, Dec 22, 2018 at 8:36 AM Dirk Laurie <[hidden email]> wrote:
Does the following formulation of your question accurately convey what you ask?
~~~
function global_function()
-- ...
end

local function upvalue_function()
-- ...
end

function f1(param_function)
  _ENV = {NAME='f1_env'; global_function = global_function}
  global_function()   -- Can this call change _ENV.NAME? Or even _ENV itself?
  upvalue_function()  -- Can this call change _ENV.NAME? Or even _ENV itself?
  param_function()    -- Can this call change _ENV.NAME? Or even _ENV itself?
end
~

Yes. Within any of global/upvalue/param_function, can they change f1's _ENV contents or f1's _ENV itself? Consider also the case (actually the primary case for what I'm considering) where f1 is not a normal function, but the anonymous function of a compiled chunk, such as a module being loaded by require().