local oldload = load
function _G.load(str, arg1, arg2, arg3)
local func = oldload(str, arg1, arg2, arg3)
local env = getfenv(func)
if (env == _G) then
env = {}
end
setmetatable(env, {__index = _G, __newindex =
function(table, var, val)
rawset(env, var, val)
end})
return func
end
This is a read-only snapshot of the ComputerCraft forums,
taken in April 2020.
Local variables by default
Started by ViperLordX, 10 February 2016 - 03:44 AMPosted 10 February 2016 - 04:44 AM
Hello, fellow programmers! Have you become tired of the Lua variables being global by default? Well, I present unto you thirteen glorious lines of code which should be run upon the startup of a computer. They will not completely disable global variables, but make all variables local by default in any program run after it. If you must, you can still set global variables by doing _G.varname = "value," but doing varname = "value" will define a local variable as opposed to a global one. I've tested this with multiple complex programs, and it has caused no issues thus far and will hopefully solve quite a few problems with colliding variables. So here are my thirteen lines of code which will save you plenty of time and trouble. Simply run it on startup and it'll work its magic.
Posted 10 February 2016 - 10:21 AM
Hello, fellow programmers! Have you become tired of the Lua variables being global by default? Well, I present unto you thirteen glorious lines of code which should be run upon the startup of a computer. They will not completely disable global variables, but make all variables local by default in any program run after it. If you must, you can still set global variables by doing _G.varname = "value," but doing varname = "value" will define a local variable as opposed to a global one. I've tested this with multiple complex programs, and it has caused no issues thus far and will hopefully solve quite a few problems with colliding variables. So here are my thirteen lines of code which will save you plenty of time and trouble. Simply run it on startup and it'll work its magic.local oldload = load function _G.load(str, arg1, arg2, arg3) local func = oldload(str, arg1, arg2, arg3) local env = getfenv(func) if (env == _G) then env = {} end setmetatable(env, {__index = _G, __newindex = function(table, var, val) rawset(env, var, val) end}) return func end
Isn't env the same as _G for a given function?
Posted 10 February 2016 - 04:10 PM
Isn't env the same as _G for a given function?
Yes, but comparing the two will return false.
local function test() end
getfenv( test ).randomvariable = true
print( randomvariable )
print( getfenv( test ) == _G )
Edited on 10 February 2016 - 03:30 PM
Posted 10 February 2016 - 07:37 PM
So, its not truly local?
Posted 10 February 2016 - 08:00 PM
Few problems.
1. Will behave differently than normal load() if a syntax error is encountered.
2. Doesn't handle loadfile, loadstring, or dofile.
3. No sanity checking.
1. Will behave differently than normal load() if a syntax error is encountered.
2. Doesn't handle loadfile, loadstring, or dofile.
3. No sanity checking.
Posted 10 February 2016 - 08:28 PM
While this is a great idea, i personally feel it should be restricted to a per-program basis instead of being applied globally. Here's how i do it.
For the record, your __newindex entry isn't nessacary as new entries would be assigned to env anyways. Hell most of your code isn't nessacary actually, as load already accepts an environment argument by default.
Do note however that both your implementation, as well as mine, prevent access to the shell API as it is not stored in _G.
To be honest im confused as to how your implementation works in the first place, provided getfenv() returns _G you never set the functions environment to env.
setfenv(
1,
setmetatable(
{},
{
__index = _G
}
)
)
For the record, your __newindex entry isn't nessacary as new entries would be assigned to env anyways. Hell most of your code isn't nessacary actually, as load already accepts an environment argument by default.
local load = _G.load
_G.load = function(str, name, mode, env)
return load(str, name, mode, env or setmetatable({},{__index = _G}))
end
Do note however that both your implementation, as well as mine, prevent access to the shell API as it is not stored in _G.
To be honest im confused as to how your implementation works in the first place, provided getfenv() returns _G you never set the functions environment to env.
Posted 10 February 2016 - 08:44 PM
While this is a great idea, i personally feel it should be restricted to a per-program basis instead of being applied globally. Here's how i do it.setfenv( 1, setmetatable( {}, { __index = _G } ) )
For the record, your __newindex entry isn't nessacary as new entries would be assigned to env anyways. Hell most of your code isn't nessacary actually, as load already accepts an environment argument by default.local load = _G.load _G.load = function(str, name, mode, env) return load(str, name, mode, env or setmetatable({},{__index = _G})) end
Do note however that both your implementation, as well as mine, prevent access to the shell API as it is not stored in _G.
To be honest im confused as to how your implementation works in the first place, provided getfenv() returns _G you never set the functions environment to env.
His code works because he getfenv's the function and then manipulates the table. He's using a weird setup - load from 5.2 and getfenv from 5.1…
Posted 10 February 2016 - 08:47 PM
Thank you all for your help! CometWolf, I tried your code and it doesn't seem to work, but I did update mine.
local oldload = load
function _G.load(str, arg1, arg2, arg3)
local func,err = oldload(str, arg1, arg2, arg3)
if not (func) then
return func, err
end
local env = getfenv(func)
if (env == _G) then
env = {}
end
setmetatable(env, {__index = _G})
setfenv(func, env)
return func,err
end
loadstring = load
Posted 10 February 2016 - 08:59 PM
Thank you all for your help! CometWolf, I tried your code and it doesn't seem to work, but I did update mine....
You still have a weird amalgamation of Lua 5.2 and 5.1 by using load and getfenv. Choose one (preferably 5.2) and stick with it.
Edited on 10 February 2016 - 07:59 PM
Posted 10 February 2016 - 09:06 PM
Well, I need to override both load and loadstring, right? I'll need to mix 5.1 and 5.2 either way.
Posted 10 February 2016 - 09:09 PM
He getfenv's the functions environment table and stores it in the variable env yes, but if it's equal to _G, he sets the variable env to an empty table and then proceeds to change that table's metatable instead of the functions environment table. His recent update correctss that however, using setfenv on the function afterwards.His code works because he getfenv's the function and then manipulates the table. He's using a weird setup - load from 5.2 and getfenv from 5.1…
That's strange, it seems to work fine for me. I assume you're refering to the simplifed overload of _G.load?CometWolf, I tried your code and it doesn't seem to work
Edited on 10 February 2016 - 08:10 PM
Posted 10 February 2016 - 09:40 PM
Yes, I am.
Posted 11 February 2016 - 01:11 AM
Ok, I updated my code again so that shell will appear in the environments.
local oldload = load
function _G.load(str, arg1, arg2, arg3)
local func,err = oldload(str, arg1, arg2, arg3)
if not (func) then
return func, err
end
local env = getfenv(func)
for k, v in pairs(env) do
_G[k] = v
end
if (env == _G) then
env = {}
end
setmetatable(env, {__index = _G})
setfenv(func, env)
return func,err
end
loadstring = load