Faster Lua VM: Studio beta

The script works without the limit but the studio editor cannot load the script without crashing

Sorry, I forgot to answer this. We have seen this error on scripts that have a very large recursion depth inside the expressions - something like this:

a+0+0+0+0+0+0+0+0+.........+0

This used to work in vanilla Lua regardless of the number of expressions, but in our compiler due to how it’s implemented we have to clamp the recursion depth (right now the limit is 1000 expressions I believe). We’ve only seen this in code that seems to try to break decompilers, and removing enough expressions for it to work fixed it. Presumably your case is similar?

We found the issue with 0/0 and other similar methods to generate NaN; this only seems to happen in 64-bit Windows Studio builds, 32-bit Windows and 64-bit Mac builds don’t seem to have this problem :sweat_smile: this will be fixed in a future Studio build.

7 Likes

It’s more canonical to vanilla Lua, though if it comes down to it I would prefer LuaJIT and JavaScript’s signed results over bit32’s unsigned ones. Either way, I’m happy to be getting bitwise operations.

2 Likes

I can’t control my excitement, oh my god, this is the best thing ever. THANK YOU ROBLOX!

2 Likes

This is fantastic. Especially for games that work towards a max player count of 100 or even 200. The game will be able to handle itself much more efficiently.

Updated the original post with the list of known differences in behavior, please let us know if you find anything else so that we can correctly classify it as a bug or a feature :slight_smile:

Regarding the desire to have some way to inject globals, we’ll need to figure out if there’s a way to do this that presents a reasonable compromise between performance and usability. One possibility is that we could introduce an injectfenv function which will have an implementation that effectively does this, but using internal mechanisms that don’t penalize execution of existing code:

function injectfenv(f, key, val)
    local env = getfenv(f)
    assert(env[key] == nil, "attempt to inject an existing key")
    env[key] = val
end
13 Likes

Since we’re at the stage of rebuilding the Virtual Machine, would it be feasible to have access to true _G once more? I know in the past, this caused issues due to every security context using the same main environment/thread, but this has since evolved to no longer be the case. There are specific use cases where I’d like to be able to overwrite the main Global environment, but due to how its currently implemented, it’s impossible as it stands.

6 Likes

We don’t have plans to change this; this would introduce behavioral changes that may affect existing games, and also (if I understand your proposal correctly) would defeat some internal optimizations in the same fashion as getfenv/setfenv does.

Could this be an toggle which warns of the loss of optimizations that is opt in? The major use case I have for this is an upcoming project in which I’d like to assist in building the new generation of Scripters. Instead of having to fully re-implement Lua within Lua, it’d work in our favor to have the ability to wrap the currently accessible main thread environment with our sandbox instead of doing a sandbox on top of emulated Lua. While you could argue that I could create a sandboxed environment which prevents this, we want to allow users to be able to require public Modules which are difficult in nature to sandbox due to being a completely third party script source, which having access to the Global environment would allow us to solve since we’d be able to wrap the main environment.

Again, I’d be aware of and accepting to lose performance benefits if this is a toggle opt-in solution.

5 Likes

pcall(Object.Method, Object, Argument1, Argument2, ..., ArgumentN);
Is this affected by the new Lua VM?

Most of the time I use it for wrapping code in a Pcall nice and clean

Are one of these affected by the new Lua VM?
-- 1
while (not pcall(starterGui, "SetCore", "ResetButtonCallback", resetBindable)) do
    wait();
end

-- 2
while (not pcall(starterGui["SetCore"], "ResetButtonCallback", resetBindable)) do
    wait();
end

-- 3
while (not pcall(starterGui.SetCore, starterGui, "ResetButtonCallback", resetBindable)) do
    wait();
end


1 Like

1 works in the old VM but doesn’t work in the new VM. (It also doesn’t work in standard Lua and didn’t work in Roblox several years ago.)
2 doesn’t work in either VM I think?
3 works in both VMs

4 Likes

The first example won’t work because it’s calling the object, which is a behavior based on a glitch and isn’t supposed to be possible. The rest are calling a method. Calling objects won’t work anymore and wasn’t supposed to work in the first place. Calling methods through other means than : still works because that’s something else.

the first 2 examples should also be passing starterGui as the first argument to SetCore since the self argument isn’t being passed implicitly because it isn’t being called through :. they wouldn’t have worked before the new VM. the second and third examples are identical if you fix the second one.

2 Likes

1 and 2 doesn’t work after I tested it in the New VM.
I don’t use those methods anyways so it doesn’t really effect me.

I’m fine with the changes since I’m using the 3rd method

Code
local Success, Message = pcall(DataStoreService.SetAsync, DataStoreService, plr.UserId, PlrsData[plr].SaveData);
--
pcall(workspace.FindFirstChild, workspace, "Part");

So all of the code above shouldn’t be affected by the new VM right?

2 Likes

That code isn’t affected because it doesn’t use __namecall at all. These are all functionally equivalent:
pcall(workspace["FindFirstChild"], workspace, "Part")
pcall(workspace.FindFirstChild, workspace, "Part")
pcall(function() workspace.FindFirstChild(workspace, "Part") end)
pcall(function() workspace:FindFirstChild("Part") end)

7 Likes

I’ve anticipated this update for some time now. I’m glad it’s here.

How does this update affect custom objects? Most of the time my objects are defined like so:

-- Point of interest No. 1: Is creating objects this way altered by the VM? Is it any better to use when compared to the old VM?
function MyClass.new()
    local Object = {}
    -- Point of interest No. 2: Are methods like this any faster in indexing?
    function Object:SomeMethod()

    end

    -- Point of interest No. 3: Are functions like this any faster in indexing?
    function Object.SomeFunction()

    end

    return Object
end

This is perfect timing, some of my new code was starting to get a bit heavy on ol’ processor, this should help a lot!

EDIT: Microprofiler read a speed increase of about 15% in my code, and that’s with the studio debugger disabled for both VMs. Not bad! Might try and get some more accurate results later, as this was just a quick test.

1 Like

Personally, I vote for this NOT to be changed, or at least change how NaN is handled. If you accidentally send NaN, inf, etc. to a BodyMover or constraint, it causes unpredictable bugs to start spilling out into various aspects of the game, which from personal experience, can send you down hours of false leads before finding out why parts of the level are randomly prevented from rendering, why parts are stuck in some weird superposition of existing/not existing, or why vehicles are randomly warping backwards in time to where they were a few seconds ago.

Changing the way the math operations work without extensive notice can be very dangerous. As you can see from @AxisAngle’s code, some people attach a specific semantic to NaN being the result of an operation.

It won’t be possible to tell if you actually got a 0 as a result or NaN, and you wouldn’t be able to tell the difference between NaN and -NaN anymore. If you don’t want NaN spilling into properties of Instances then you should safeguard your code from producing NaN. (i.e. by checking that you aren’t doing .unit on a vector with length 0, or 0/0, etc)

What might be more effective is some way to set a warning message to appear when you set a certain property of an Instance to NaN.

12 Likes

That’s completely fair, and you’re right, the outcome of the equation should stay NaN. I would very much appreciate a warning message, and have created a request for this in the past.

What happened with my code was that a vector that I thought could never be 0,0,0 in very rare circumstances actually could be, which then caused all of the wacky bugs I mentioned earlier. Because they were so weird, and I’d never experienced any NaN errors before, I never thought to check for NaN vectors for a long time resulting in a lot of wasted time. But anyway, wrong thread for this I guess, I might make a new feature request.

6 Likes