summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-08-29 21:36:39 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-08-29 21:36:39 -0400
commit2d5cf36be6e0e367efec2bfa01378146269f33db (patch)
tree082b0c6b35e2d6cb76078aca32da50c3604e620c
parenta098a3d42bf862e0e3789e21f6b8f3f0e71d60d0 (diff)
Support next(), pairs(), ipairs() for LL.setdtor() table proxies.
Replace the global next(), pairs() and ipairs() functions with a C++ function that drills down through layers of setdtor() proxy objects and then forwards the updated arguments to the original global function. Add a Luau __iter() metamethod to setdtor() proxy objects that, like other proxy metamethods, drills down to the underlying _target object. __iter() recognizes the case of a _target table which itself has a __iter() metamethod. Also add __idiv() metamethod to support integer division. Add tests for proxy // division, next(proxy), next(proxy, key), pairs(proxy), ipairs(proxy) and 'for k, v in proxy'. Also test the case where the table wrapped in the proxy has an __iter() metamethod of its own.
-rw-r--r--indra/llcommon/lua_function.cpp62
-rw-r--r--indra/newview/scripts/lua/test_setdtor.lua29
2 files changed, 91 insertions, 0 deletions
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp
index be39a7d095..850dff1a33 100644
--- a/indra/llcommon/lua_function.cpp
+++ b/indra/llcommon/lua_function.cpp
@@ -496,6 +496,9 @@ namespace
using LuaStateMap = std::unordered_map<lua_State*, LuaState*>;
static LuaStateMap sLuaStateMap;
+// replacement next(), pairs(), ipairs() that understand setdtor() proxy args
+int lua_proxydrill(lua_State* L);
+
} // anonymous namespace
LuaState::LuaState(script_finished_fn cb):
@@ -513,6 +516,28 @@ LuaState::LuaState(script_finished_fn cb):
lua_register(mState, "print", LuaFunction::get("print_info"));
// We don't want to have to prefix require().
lua_register(mState, "require", LuaFunction::get("require"));
+
+ // Replace certain key global functions so they understand our
+ // LL.setdtor() proxy objects.
+ // (We could also do this for selected library functions as well,
+ // e.g. the table, string, math libraries... let's see if needed.)
+ for (const auto& func : { "next", "pairs", "ipairs" })
+ {
+ // push the function's name string twice
+ lua_pushstring(mState, func);
+ lua_pushvalue(mState, -1);
+ // stack: name, name
+ // look up the existing global
+ lua_rawget(mState, LUA_GLOBALSINDEX);
+ // stack: name, global function
+ // bind original function as the upvalue for lua_proxydrill()
+ lua_pushcclosure(mState, lua_proxydrill,
+ stringize("lua_proxydrill(", func, ')').c_str(),
+ 1);
+ // stack: name, lua_proxydrill(func)
+ // global name = lua_proxydrill(func)
+ lua_rawset(mState, LUA_GLOBALSINDEX);
+ }
}
LuaState::~LuaState()
@@ -1165,6 +1190,14 @@ void setdtor_refs::push_metatable(lua_State* L)
-- don't fret about arg._target's __tostring metamethod,
-- if any, because built-in tostring() deals with that
return tostring(arg._target)
+ end,
+ __iter = function(arg)
+ local iter = (getmetatable(arg._target) or {}).__iter
+ if iter then
+ return iter(arg._target)
+ else
+ return next, arg._target
+ end
end
}
)-",
@@ -1172,6 +1205,7 @@ void setdtor_refs::push_metatable(lua_State* L)
binop("sub", "-"),
binop("mul", "*"),
binop("div", "/"),
+ binop("idiv", "//"),
binop("mod", "%"),
binop("pow", "^"),
binop("concat", ".."),
@@ -1254,6 +1288,34 @@ int setdtor_refs::meta__index(lua_State* L)
return 1;
}
+// replacement for global next(), pairs(), ipairs():
+// its lua_upvalueindex(1) is the original function it's replacing
+int lua_proxydrill(lua_State* L)
+{
+ // Accept however many arguments the original function normally accepts.
+ // If our first arg is a userdata, check if it's a setdtor_refs proxy.
+ // Drill through as many levels of proxy wrapper as needed.
+ while (const setdtor_refs* ptr = lua_toclass<setdtor_refs>(L, 1))
+ {
+ // push original object
+ lua_getref(L, ptr->objref);
+ // replace first argument with that
+ lua_replace(L, 1);
+ }
+ // We've reached a first argument that's not a setdtor() proxy.
+ // How many arguments were we passed, anyway?
+ int args = lua_gettop(L);
+ // Push the original function, captured as our upvalue.
+ lua_pushvalue(L, lua_upvalueindex(1));
+ // Shift the stack so the original function is first.
+ lua_insert(L, 1);
+ // Call the original function with all original args, no error checking.
+ // Don't truncate however many values that function returns.
+ lua_call(L, args, LUA_MULTRET);
+ // Return as many values as the original function returned.
+ return lua_gettop(L);
+}
+
// When Lua destroys a setdtor_refs userdata object, either from garbage
// collection or from LL.atexit(lua_destroybounduserdata), it's time to keep
// its promise to call the specified Lua destructor function with the
diff --git a/indra/newview/scripts/lua/test_setdtor.lua b/indra/newview/scripts/lua/test_setdtor.lua
index 61ed86dcc8..ec5cd47e93 100644
--- a/indra/newview/scripts/lua/test_setdtor.lua
+++ b/indra/newview/scripts/lua/test_setdtor.lua
@@ -30,6 +30,8 @@ print(`n * {i} =`, n * i)
print(`{i} * n =`, i * n)
print(`n / {i} =`, n / i)
print(`{i} / n =`, i / n)
+print(`n // {i} =`, n // i)
+print(`{i} // n =`, i // n)
print(`n % {i} =`, n % i)
print(`{i} % n =`, i % n)
print(`n ^ {i} =`, n ^ i)
@@ -48,10 +50,37 @@ t = LL.setdtor('table', {'[1]', '[2]', abc='.abc', def='.def'},
print('t =', inspect(t))
print('t._target =', inspect(t._target))
print('#t =', #t)
+print('next(t) =', next(t))
+print('next(t, 1) =', next(t, 1))
print('t[2] =', t[2])
print('t.def =', t.def)
t[1] = 'new [1]'
print('t[1] =', t[1])
+print('for k, v in pairs(t) do')
+for k, v in pairs(t) do
+ print(`{k}: {v}`)
+end
+print('for k, v in ipairs(t) do')
+for k, v in ipairs(t) do
+ print(`{k}: {v}`)
+end
+print('for k, v in t do')
+for k, v in t do
+ print(`{k}: {v}`)
+end
+-- and now for something completely different
+setmetatable(
+ t._target,
+ {
+ __iter = function(arg)
+ return next, {'alternate', '__iter'}
+ end
+ }
+)
+print('for k, v in t with __iter() metamethod do')
+for k, v in t do
+ print(`{k}: {v}`)
+end
print('function')
f = LL.setdtor('function', function(a, b) return (a .. b) end, print)