When a :keys custom metamethod is present on a table node, stablepairs
skipped sorting and only used the specified keys to determine order,
adding the extras in as they occurred in pairs, which is unstable in
PUC Lua > 5.2 due to a random nonce added to hash tables to prevent
collission attacks.
As a result, if a macro added extra keys to a table literal, those extra
keys cause non-deterministic order for table literals in the emitted Lua
on affected versions of the Lua VM.
Also addressed an edge case for cases like {:1 :one 1 :also-one} and
{true :TRUE "true" :ALSO-TRUE}, which would not sort reliably with the
old sort comparator that simply used (< (tostring a) (tostring b))
I wasn't sure where the best place to put the tests for this - for now, I
originally created a separate test module running compilation via the CLI so
that it could run while re-instantiating the Lua VM each time so I could add a
loop to re-compile and assert multiple times to check stability.
However (see second commit) since the test is failing reliably on every run from
the main branch, I consolidated it into the core test suit, compiling directly,
to avoid further slowing down tests with more shelling out to the CLI. The test
will already run once for each Lua version (twice for those with the nonce), and
the higher number of table keys means more keys that could potentially show up
out of order, so I decided I was being overly cautious.
I left it in a separate commit for review purposes - we can always go the back
to running via the CLI to ensure we'd hit a random edge case by throwing out the
second commit.
Fixes #449
Description (from commit msg)
Fix Lua 5.3/5.4 edge case for stablepairs
When a
:keys
custom metamethod is present on a table node, stablepairs skipped sorting and only used the specified keys to determine order, adding the extras in as they occurred inpairs
, which is unstable in PUC Lua > 5.2 due to a random nonce added to hash tables to prevent collission attacks.As a result, if a macro added extra keys to a table literal, those extra keys cause non-deterministic order for table literals in the emitted Lua on affected versions of the Lua VM.
Also addressed an edge case for cases like
{:1 :one 1 :also-one}
and{true :TRUE "true" :ALSO-TRUE}
, which would not sort reliably with the old sort comparator that simply used(< (tostring a) (tostring b))
Example:
Test reliability and random nonce
I wasn't sure where the best place to put the tests for this - for now, I originally created a separate test module running compilation via the CLI so that it could run while re-instantiating the Lua VM each time so I could add a loop to re-compile and assert multiple times to check stability.
However (see second commit) since the test is failing reliably on every run from the main branch, I consolidated it into the core test suit, compiling directly, to avoid further slowing down tests with more shelling out to the CLI. The test will already run once for each Lua version (twice for those with the nonce), and the higher number of table keys means more keys that could potentially show up out of order, so I decided I was being overly cautious.
I left it in a separate commit for review purposes - we can always go the back to running via the CLI to ensure we'd hit a random edge case by throwing out the second commit.