Indexing a RBXScriptSignal from an Instance does not return the same object

When indexing a property on an Instance that returns an RBXScriptSignal, instead of returning a cached copy of the same object, it returns a new object for every time the property is indexed.

This creates issues when storing them inside tables as keys, as it means it can never be retrieved again using the t[k] syntax. They can be metamethod equated between each other, though this is very expensive. rawequal which compares objects by skipping any __eq magic, they are different.

This code sample demonstrates the issue pretty easily:

local t = {}

for i = 1, 5 do
	t[game.Loaded] = true
end

print(t)

Instead of printing one key in the table, it prints 5 RBXScriptSignal objects at different memory locations, even though its the same signal.

This can also be an entry point to a very easy memory leak to developers who aren’t aware of this weird behaviour.

Expected behavior

When indexing a property that emits RBXScriptSignal, for example, BindableEvent.Event, it should return the same RBXScriptSignal object, instead of making a new one every index.

4 Likes

I’m interested on why this even happens. For separate VMs I can see, but for every time is odd.

Looks like I’m going to have to store my events as locals now.

For various reasons (related to how engine objects are marshalled into the Luau VM), this is a difficult problem to solve. In fact there might be some edge cases that are even more difficult to handle than the general case.

We don’t consider the current behavior a bug. However, I can see why it is surprising and perhaps less than ideal to use.

I am going to close this as “By Design” since we don’t consider the current behavior a defect. However you can make a feature request that we change the behavior (assuming that is practical/possible after more investigation).

When we push a reference to a C++ object into the Luau VM we create a “userdata” object which wraps a reference to the C++ object. Luau tables treat the pointer to the “userdata” object as the key when they are inserted into Luau tables. So each new instance of a userdata object ends up being treated as a unique key. That is why the userdata objects create duplicate entries in a Luau table even if they internally refer to the C++ object.

1 Like