diff options
| author | ruki <[email protected]> | 2026-04-15 14:16:16 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-15 14:16:16 +0800 |
| commit | fabb25749492d6b26887da0217486bfbfd104bdd (patch) | |
| tree | dd7456ec5a1a24044f32a74f7fcce3d107d9a1c6 /xmake | |
| parent | 346f929f7819e92e2752c7b3335894297fb02630 (diff) | |
| parent | bf5af4c886923bc2ce7cad3816d5f3cb71e770e3 (diff) | |
Merge pull request #7485 from xmake-io/pairs
improve pairs for lua5.5
Diffstat (limited to 'xmake')
| -rw-r--r-- | xmake/core/base/hashset.lua | 92 | ||||
| -rw-r--r-- | xmake/core/base/list.lua | 21 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/ipairs.lua | 14 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/irpairs.lua | 16 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/pairs.lua | 26 |
5 files changed, 117 insertions, 52 deletions
diff --git a/xmake/core/base/hashset.lua b/xmake/core/base/hashset.lua index c052d36ee..405a23474 100644 --- a/xmake/core/base/hashset.lua +++ b/xmake/core/base/hashset.lua @@ -119,15 +119,27 @@ end -- end -- @endcode -- +-- Stateful closure so the loop body can safely reassign the first loop +-- variable under lua 5.4+ (paired with the RDKCONST->VDKREG compile-time +-- patch in core/src/lua/xmake.lua). function hashset:items() - return function (t, item) - local k, _ = next(t._DATA, item) - if k == hashset._NIL then - return nil - else - return k - end - end, self, nil + -- keep `next`'s key in an upvalue so the loop body can safely reassign + -- the first loop variable. In lua 5.4+ the for-in control slot is + -- merged with the first user variable; threading the key through the + -- loop would otherwise corrupt `next` on the following iteration. + -- + -- nil-as-a-member is stored under the `_NIL` sentinel. For-loop + -- semantics don't let us yield nil (it would end the loop), so we + -- skip the sentinel and continue to the next real key; the nil + -- member is omitted but entries after it are still visited. + local data = self._DATA + local k = nil + return function () + repeat + k = next(data, k) + until k ~= hashset._NIL + return k + end end -- iterate order items @@ -154,16 +166,18 @@ function hashset:orderitems() end return a < b end) - local i = 1 - return function (t, k) - k = orderkeys[i] - i = i + 1 - if k == hashset._NIL then - return nil - else - return k - end - end, self, nil + -- see hashset:items() for the `_NIL` handling rationale + local n = #orderkeys + local i = 0 + return function () + local k + repeat + i = i + 1 + if i > n then return nil end + k = orderkeys[i] + until k ~= hashset._NIL + return k + end end -- iterate keys (deprecated, please use items()) @@ -175,14 +189,19 @@ end -- @endcode -- function hashset:keys() - return function (t, key) - local k, _ = next(t._DATA, key) - if k == hashset._NIL then - return k, nil - else - return k, k + -- see hashset:items() for the stateful-closure rationale and the + -- `_NIL` skipping behavior. + local data = self._DATA + local k = nil + return function () + repeat + k = next(data, k) + until k ~= hashset._NIL + if k == nil then + return nil end - end, self, nil + return k, k + end end -- iterate order keys (deprecated, please use orderitems()) @@ -194,6 +213,8 @@ end -- @endcode -- function hashset:orderkeys() + -- see hashset:items() for the stateful-closure rationale and the + -- `_NIL` skipping behavior. local orderkeys = table.keys(self._DATA) table.sort(orderkeys, function (a, b) if a == hashset._NIL then @@ -210,16 +231,17 @@ function hashset:orderkeys() end return a < b end) - local i = 1 - return function (t, k) - k = orderkeys[i] - i = i + 1 - if k == hashset._NIL then - return k, nil - else - return k, k - end - end, self, nil + local n = #orderkeys + local i = 0 + return function () + local k + repeat + i = i + 1 + if i > n then return nil end + k = orderkeys[i] + until k ~= hashset._NIL + return k, k + end end -- get size of hashset diff --git a/xmake/core/base/list.lua b/xmake/core/base/list.lua index 4caed81bc..222a6e85d 100644 --- a/xmake/core/base/list.lua +++ b/xmake/core/base/list.lua @@ -238,22 +238,31 @@ end -- -- @return the iterator function -- +-- Stateful closure so the loop body can safely reassign the first loop +-- variable under lua 5.4+ (paired with the RDKCONST->VDKREG compile-time +-- patch in core/src/lua/xmake.lua). function list:items() - local iter = function (list, item) - return list:next(item) + -- stateful closure: keep the cursor in an upvalue so the loop body + -- can safely reassign the first loop variable (lua 5.4+ merges the + -- for-in control slot with the first user variable). + local item = nil + return function () + item = self:next(item) + return item end - return iter, self, nil end -- iterate elements from back to front -- -- @return the reverse iterator function -- +-- Stateful closure; see `list:items()` for the rationale. function list:ritems() - local iter = function (list, item) - return list:prev(item) + local item = nil + return function () + item = self:prev(item) + return item end - return iter, self, nil end -- create a new doubly-linked list diff --git a/xmake/core/sandbox/modules/ipairs.lua b/xmake/core/sandbox/modules/ipairs.lua index 19591fce5..d910eb2b7 100644 --- a/xmake/core/sandbox/modules/ipairs.lua +++ b/xmake/core/sandbox/modules/ipairs.lua @@ -22,6 +22,10 @@ local table = require("base/table") -- improve ipairs, wrap nil and single value +-- +-- Like sandbox `pairs`, this is a stateful closure so the loop body can +-- safely reassign the first loop variable under lua 5.4+ (paired with +-- the RDKCONST->VDKREG compile-time patch in core/src/lua/xmake.lua). function sandbox_ipairs(t) -- exists the custom ipairs? @@ -34,15 +38,21 @@ function sandbox_ipairs(t) if not is_table then t = t ~= nil and {t} or {} end - return function (t, i) + -- keep the index in an upvalue so the loop body can safely reassign + -- the first loop variable. In lua 5.4+ the for-in control slot is + -- merged with the first user variable; writes to it would otherwise + -- silently skip or repeat entries on the next iteration. + local i = 0 + return function () i = i + 1 local v = t[i] if v ~= nil then return i, v end - end, t, 0 + end end + -- load module return sandbox_ipairs diff --git a/xmake/core/sandbox/modules/irpairs.lua b/xmake/core/sandbox/modules/irpairs.lua index 486f89506..c2224b2e1 100644 --- a/xmake/core/sandbox/modules/irpairs.lua +++ b/xmake/core/sandbox/modules/irpairs.lua @@ -40,14 +40,22 @@ local table = require("base/table") -- end -- -- @endcode +-- +-- Implemented as a stateful closure so the loop body can safely +-- reassign the first loop variable under lua 5.4+ (paired with the +-- RDKCONST->VDKREG compile-time patch in core/src/lua/xmake.lua). function sandbox_irpairs(t, filter, ...) -- has filter? local has_filter = type(filter) == "function" - -- init iterator + -- stateful closure: keep the index in an upvalue so the loop body + -- can safely reassign the first loop variable (lua 5.4+ merges the + -- for-in control slot with the first user variable). local args = table.pack(...) - local iter = function (t, i) + t = table.wrap(t) + local i = table.getn(t) + 1 + return function () i = i - 1 local v = t[i] if v ~= nil then @@ -57,10 +65,6 @@ function sandbox_irpairs(t, filter, ...) return i, v end end - - -- return iterator and initialized state - t = table.wrap(t) - return iter, t, table.getn(t) + 1 end -- load module diff --git a/xmake/core/sandbox/modules/pairs.lua b/xmake/core/sandbox/modules/pairs.lua index 50fa4d10e..dc162bc47 100644 --- a/xmake/core/sandbox/modules/pairs.lua +++ b/xmake/core/sandbox/modules/pairs.lua @@ -22,6 +22,19 @@ local table = require("base/table") -- improve pairs, wrap nil/single value +-- +-- Unlike the stock lua `pairs`, this sandbox version tolerates the loop +-- body reassigning the first loop variable, e.g.: +-- +-- for k, v in pairs(t) do +-- k = k:gsub("_", "-") -- safe here +-- ... +-- end +-- +-- This works together with the `RDKCONST -> VDKREG` compile-time patch +-- applied to lparser.c in core/src/lua/xmake.lua (and xmake.sh): that +-- patch lifts lua 5.4+'s ban on writing to the for-in control variable, +-- and this stateful closure makes such writes harmless at runtime. function sandbox_pairs(t) -- exists the custom ipairs? @@ -34,9 +47,16 @@ function sandbox_pairs(t) if not is_table then t = t ~= nil and {t} or {} end - return function (t, i) - return next(t, i) - end, t, nil + -- keep `next`'s key in an upvalue so the loop body can safely reassign + -- the first loop variable. In lua 5.4+ the for-in control slot is + -- merged with the first user variable; if we threaded the key through + -- the loop it would corrupt `next` on the following iteration. + local k = nil + return function () + local nk, nv = next(t, k) + k = nk + return nk, nv + end end -- load module |
