summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-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)