wipe: reset 5.0's remembered table size, so a refilled list counts from 1
`wipe` cleared elements with a bare `pairs` loop, which leaves Lua 5.0's remembered table size stale (the `getsizes` side store `luaL_setn` writes). TurtleWoW's LFT fills its group list, wipes it on the next `S2C_GROUPS_LIST_UPDATE` start, and refills; the refill's `getn` answered the old length, so `LFT_UpdateGroupsList` read nil entries and `CustomSort` raised on `value.category`. Take `Globals.lua:126`'s body instead: a `tremove` walk down from `getn` resets the size, then clear the non-array keys. The test now pins the return and the size reset.
This commit is contained in:
@@ -311,11 +311,20 @@ do
|
||||
local _gsub = string.gsub
|
||||
|
||||
function wipe(t)
|
||||
-- `t = t or {}` and a returned table: TurtleWoW's `Blizzard_RaidUI.lua:77,116` clears its
|
||||
-- `RAID_SUBGROUP_LISTS` with `t[i] = wipe(t[i])` and then `tinsert`s into it, so `wipe` of
|
||||
-- a never-populated entry must hand back a table to insert into.
|
||||
t = t or {}
|
||||
for k in pairs(t) do t[k] = nil end
|
||||
-- `Globals.lua:126`'s body: the `tremove` walk down from `getn` resets 5.0's remembered
|
||||
-- table size, which a bare `pairs` clear leaves stale — a list refilled after `wipe` would
|
||||
-- then count the old length and read holes (LFT's group list). A nil (or non-table) hands
|
||||
-- back a table, as TurtleWoW's `Blizzard_RaidUI.lua:77,116` clears `RAID_SUBGROUP_LISTS`
|
||||
-- with `t[i] = wipe(t[i])` and then `tinsert`s into it.
|
||||
if type(t) ~= "table" then
|
||||
return {}
|
||||
end
|
||||
for i = getn(t), 1, -1 do
|
||||
tremove(t, i)
|
||||
end
|
||||
for k in pairs(t) do
|
||||
rawset(t, k, nil)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
@@ -71,29 +71,40 @@ fn stdlib_aliases_and_helpers() {
|
||||
}
|
||||
|
||||
/// `wipe` empties the table IN PLACE (the reference's `UIFrameFade_CreateInfo` reuses the same
|
||||
/// `fadeInfo` table across calls) and returns nothing.
|
||||
/// `fadeInfo` table across calls) and hands the same table back, as TurtleWoW's `Globals.lua:126`
|
||||
/// does; the `tremove` walk resets 5.0's remembered size.
|
||||
#[test]
|
||||
fn wipe_empties_in_place_and_returns_nothing() {
|
||||
fn wipe_empties_in_place_and_returns_the_table() {
|
||||
let s = script();
|
||||
assert_eq!(
|
||||
s.eval::<i64>(
|
||||
r#"
|
||||
local t = { "a", 2, [ "k" ] = true }
|
||||
local held = t
|
||||
wipe(t)
|
||||
return getn(t) == 0 and t.k == nil and held == t and 1 or 0
|
||||
local back = wipe(t)
|
||||
return getn(t) == 0 and t.k == nil and held == t and back == t and 1 or 0
|
||||
"#
|
||||
)
|
||||
.unwrap(),
|
||||
1,
|
||||
"wipe must empty the caller's own table, not replace it, and must leave no keys"
|
||||
"wipe must empty the caller's own table, leave no keys, and return it"
|
||||
);
|
||||
// The remembered size resets, so a list refilled after `wipe` counts from 1: a stale size is
|
||||
// the nil-index crash TurtleWoW's LFT group list hit on its second fill.
|
||||
assert_eq!(
|
||||
s.eval::<i64>(
|
||||
r#"
|
||||
local t = {}
|
||||
tinsert(t, "a"); tinsert(t, "b"); tinsert(t, "c")
|
||||
wipe(t)
|
||||
tinsert(t, "x")
|
||||
return getn(t)
|
||||
"#
|
||||
)
|
||||
.unwrap(),
|
||||
1,
|
||||
"a refill after wipe must count from 1, not the wiped length"
|
||||
);
|
||||
// 1.12's VM has no `select` (a 5.1 addition), so the arity is read by packing into locals:
|
||||
// only a no-value return leaves both nil.
|
||||
let returns_nothing: bool = s
|
||||
.eval("local a, b = wipe({1}) return a == nil and b == nil")
|
||||
.unwrap();
|
||||
assert!(returns_nothing, "wipe returns no values");
|
||||
}
|
||||
|
||||
/// `strsplit` splits on PLAIN text, not Lua patterns: a `.` is a full stop, not "any character".
|
||||
|
||||
Reference in New Issue
Block a user