Getfenv & Setfenv replacements?

Hello, I am making a module that uses getfenv a lot to import services. It came to my attention that getfenv and setfenv disable optimizations.

The reason I use this value is so I can do something like this:

module:ImportServices(getfenv())

this will replace all the services with enhanced versions (like table, string, getmetatable, setmetatable, rawget, rawset, pairs and ipairs)

Is there an alternative way to use getfenv/setfenv reliably?

I know I have this workaround:

local getmetatable = module.getmetatable;
local setmetatable = module.setmetatable;
local ipairs = module.ipairs;
local pairs = module.pairs;
local rawget = module.rawget;
local rawset = module.rawset;
...

It just seems too much work to do.

You don’t need to fetch the function environment to achieve this, you can just tabularize each of the standard libraries and iterate over it.

local Global = {}
local Libraries = {bit32, coroutine, debug, math, os, string, table, utf8}
for _, Library in ipairs(Libraries) do
	for Key, Value in pairs(Library) do
		Global[Key] = Value
	end
end

print(Global.pi) --3.14...

Well yes, but what about libraries like pairs()? It is a function and so can’t be indexed.

Also, I have thought about it and can’t I use loadstring? The custom loadstring module allows it to run code anywhere and so I can do like this, right?:

module.loadstring(module:GetStringOrSomething())

This will abstract it away into 1 line

@Forummer I don’t think that’s what he is asking for.

To answer the question, there is no alternative way to call getfenv, setfenv or loadstring without disabling Luau specific optimizations (Yes, loadstring enters this category, I’m sorry.). You just kind of have to live with the normal speed that vanilla Lua 5.1.4 works at.

Even the custom VM module?
image

Wait I am dumb! Reading the code itself I found a mention of getfenv()
image

Is there no variable “global” where the variables are stored? Like other languages such as JS

Unfortunately since Luau derived from Lua 5.1.4, it doesn’t have a global environment table like Lua 5.2 and onwards does with _ENV and it’s not going to be easy to add it to Luau when getfenv/setfenv still exist as part of backwards compatibility.

2 Likes

Those belong to the ‘basic’ library which unfortunately does not come packaged inside a global table of the same name.

local Global = {}
local Globals = {["_G"] = _G,  ["_VERSION"] = _VERSION,  ["assert"] = assert,  ["collectgarbage"] = collectgarbage,  ["DebuggerManager"] = DebuggerManager,  ["delay"] = delay,  ["elapsedTime"] = elapsedTime,  ["Enum"] = Enum,  ["error"] = error,  ["game"] = game,  ["getfenv"] = getfenv,  ["getmetatable"] = getmetatable,  ["ipairs"] = ipairs,  ["loadstring"] = loadstring,  ["newproxy"] = newproxy,  ["next"] = next,  ["pairs"] = pairs,  ["pcall"] = pcall,  ["PluginManager"] = PluginManager,  ["print"] = print,  ["printidentity"] = printidentity,  ["rawequal"] = rawequal,  ["rawget"] = rawget,  ["rawset"] = rawset,  ["require"] = require,  ["select"] = select,  ["setfenv"] = setfenv,  ["setmetatable"] = setmetatable,  ["settings"] = settings,  ["shared"] = shared,  ["spawn"] = spawn,  ["stats"] = stats,  ["tick"] = tick,  ["time"] = time,  ["tonumber"] = tonumber,  ["tostring"] = tostring,  ["typeof"] = typeof,  ["unpack"] = unpack,  ["UserSettings"] = UserSettings,  ["version"] = version,  ["wait"] = wait,  ["warn"] = warn,  ["workspace"] = workspace,  ["xpcall"] = xpcall}
local Libraries = {bit32, coroutine, debug, math, os, string, table, utf8}

for Key, Value in pairs(Globals) do
	Global[Key] = Value
end

for _, Library in ipairs(Libraries) do
	for Key, Value in pairs(Library) do
		Global[Key] = Value
	end
end

print(Global._VERSION) --Luau

Bare in mind that collisions (keys that share the same name) may occur, for example the ‘unpack’ global and the table library function of the same name, fortunately these two functions have the same behavior so the collision isn’t cause for concern.

I believe they’re just looking to localize all of the current globals (so the global environment doesn’t need to be accessed).

There is also no way of doing this right?

myTable[k] = true

[k] = true

Like set a variable using a string

That should be possible with setfenv/getfenv, though I don’t think the linter will be very happy with you.

local ENV = getfenv()

ENV["MyStringValue"] = 1

setfenv(0, ENV)

MyStringValue = 2

Yes, after doing a bit of testing you can get some warnings (that won’t cause errors), but the code above doesn’t generate one because MyStringValue is interpreted as a global variable (by MyStringValue = 2).

Not really, without using getfenv.

I was searching of why they wouldn’t add _ENV but I still don’t get why not. I found this

Why can’t they just add it and keep getfenv/setfenv?

I didn’t say it’s not impossible, it very much is but it’s not going to be a walk in the park and _ENV doesn’t really cover all the cases that setfenv does as stated in the quoted reply that you posted, thus lowering it in the priority list for the time being.

1 Like

Last question, how much does this impact performance? Is going to impact to the point of lagging my game? I believe that I do not have any other way to do this, so I will just do this instead:

local services = {};
module:ImportServices(services);

Not too many people might use the environment function, but for the few of us it would be a life savior to have something like _ENV. Thanks everyone for helping! @Forummer, @BenMactavsin and @Judgy_Oreo

I doubt it, unless you are doing a performance intensive task. One of the optimazations you will lose is global access chains, if you want to read what that is, you can go to this page to read about it with other optimizations included: