Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Why should you use newproxy(true) over setmetatable({}, mt)?

So I was curious and started to inspect code from Freya which is a framework for Roblox. I was reading the code for Arbiters and saw this code:

--code
Controller.new = Hybrid(function(f)
  local newobs = newproxy(true)
  local omt = getmetatable(newobs)
  for k, v in pairs(ObserverMt) do
    omt[k] = v
  end
  ObserverCallbacks[newobs] = f
  ObserverEmitters[newobs] = Event.new()
  ObserverRules[newobs] = { }
end)
--more code

The part that I'm curious about is

local omt = getmetatable(newobs)
for k, v in pairs(ObserverMt) do
  omt[k] = v
end

I searched up on the wiki about newproxy and all it does is create a blank userdata.


Why not just do local omt = setmetatable({}, ObserverMt)?

What does newproxy(true) really do?

Does it have an effect on metatables? If so, what is it?

2 answers

✓ Accepted answer

newproxy(true) produces a value of type "userdata", which means it is not a table.

Beyond having a different type(obj), this is significant because Lua distinguishes values by their type in a few inconvenient places.

The most noticeable distinction is that the __len metamethod only applies to userdata which means if you want to override the behavior of the # operator you must use newproxy(true) to create the object.

(This was evidently seen as a mistake and is not the case in Lua 5.2)

As another minor example, a userdata object cannot be equal to (==) a table object, since they have different types, even if they have the same __eq metamethod.

Userdata has stronger protections
No, don't touch that

Even though Freya has this whole fundamental concept of open data, a lot of the objects rely on being locked down. Why, you ask? Two reasons:

  • If it comes from Freya it should behave predictably
  • It reduces the surface for attacks which target Freya as a vulnerability.

Overall, the newproxy method can have a slightly larger memory footprint, but proper structure optimizations can nullify this and even present advantages over tables. Userdata also can't have any metamethod invocations bypassed through use of the rawset and rawget functions.


Back to the real question

The metatable must be set using getmetatable because setmetatable only works on tables. It's a small price to pay, given Roblox's modification of newproxy to remove userdata templating.

This discussion is preserved as it was. New answers, comments, and votes are closed.