I have a problem with the __iter
metamethod’s implementation.
I have a sandboxing tool here which wraps around objects with metamethods. In order to do this though, I need to be able to essentially generate a copy of the original metamethod without touching that metamethod (I use this for values going in AND out of the sandbox, so some values I need to wrap are going to be unmanaged!)
With the __iter
method, there does not exist any function which can return the generator, state, or index produced. pairs
throws because the input is not a table, as I feel it it should.
In the RFC, it is noted that the equivalent of how t[index]
is to rawget(t, index)
is as in t
is to in pairs(t)
. This is true, however, t[index]
can be invoked as an expression, meanwhile getmetatable(t).__iter(t)
cannot be safely invoked as an expression, and I can’t access the generator, state, and index and therefore these values go unsandboxed, providing a way for users to define code in completely unmanaged space which can access unmanaged values, even from my own managed tables.
Additionally, I am even unable to do something smart like the following, wrapping a real iterator inside of a coroutine, and returning a function which advances the state by resuming it repeatedly. The generator can return a variable number of results but I can only capture a finite number of results.
This example which mimicks the structure I require runs as expected, but only if the iterator uses two or less arguments. A vararg is not valid syntax. Very very thankfully, iterators cannot yield, but if they could, this example would be invalid for that reason because it would cause the two iterators that end up processing to lose their synchronization.
local metatable = {}
local proxy = {}
-- Psuedo-code
local function getUnmanaged(value)
return {
abc = 123,
cde = 234
}
end
local function sandboxAllTheResults(...)
return ...
end
metatable.__iter = function(sandboxed)
local real = getUnmanaged(sandboxed)
return coroutine.wrap(function(x)
-- Instead of index, value, if a vararg (...) were placed here it would cause a syntax error
for index, value in x do
coroutine.yield(sandboxAllTheResults(index, value))
end
end), real
end
setmetatable(proxy, metatable)
-- cde 234
-- abc 123
for index, value in proxy do
print(index, value)
end
So, there are two solutions that solve this:
- Allow varargs in for loops
- Provide a way to access the results of the
__iter
metamethod directly (Preferable to me since it allows me to manage the generator itself)
c.c. @zeuxcg