From daa0f8250c01b344714906242ab3e689cea52dba Mon Sep 17 00:00:00 2001 From: Moulins Date: Tue, 10 Sep 2024 14:15:54 +0200 Subject: [PATCH 1/2] tests: add (failing) tests for random Array "sorting" These tests fail because we do not implement the exact same algorithm as Flash Player: - in AVM1, the test produces a different result - in AVM2, the test panics because Rust's sort doesn't accomodate non-Ord comparison functions --- .../tests/swfs/avm1/array_sort_random/Test.as | 38 ++ .../swfs/avm1/array_sort_random/output.txt | 443 ++++++++++++++++++ .../swfs/avm1/array_sort_random/test.swf | Bin 0 -> 489 bytes .../swfs/avm1/array_sort_random/test.toml | 2 + .../tests/swfs/avm2/array_sort_random/Test.as | 36 ++ .../swfs/avm2/array_sort_random/output.txt | 210 +++++++++ .../swfs/avm2/array_sort_random/test.swf | Bin 0 -> 1079 bytes .../swfs/avm2/array_sort_random/test.toml | 2 + 8 files changed, 731 insertions(+) create mode 100644 tests/tests/swfs/avm1/array_sort_random/Test.as create mode 100644 tests/tests/swfs/avm1/array_sort_random/output.txt create mode 100644 tests/tests/swfs/avm1/array_sort_random/test.swf create mode 100644 tests/tests/swfs/avm1/array_sort_random/test.toml create mode 100644 tests/tests/swfs/avm2/array_sort_random/Test.as create mode 100644 tests/tests/swfs/avm2/array_sort_random/output.txt create mode 100644 tests/tests/swfs/avm2/array_sort_random/test.swf create mode 100644 tests/tests/swfs/avm2/array_sort_random/test.toml diff --git a/tests/tests/swfs/avm1/array_sort_random/Test.as b/tests/tests/swfs/avm1/array_sort_random/Test.as new file mode 100644 index 000000000000..dd7bb4d8d686 --- /dev/null +++ b/tests/tests/swfs/avm1/array_sort_random/Test.as @@ -0,0 +1,38 @@ +// Compile with: +// mtasc -main -version 8 Test.as -out test.swf +class Test { + + static function main(current) { + var array = []; + for (var i = 0; i < 50; i++) { + array.push(i); + } + + // "sort" the array using randomly-chosen comparison results. + array.sort(function(a, b) { + var r = Test.rng(); + if (r % 8 == 0) { + trace("cmp: " + a + " == " + b); + return 0; + } else if (r > 0) { + trace("cmp: " + a + " > " + b); + return 1; + } else { + trace("cmp: " + a + " < " + b); + return -1; + } + }); + + trace("// contents of array"); + trace(array); + } + + // A simple deterministic PRNG; namely, Xorshift. + static var rngState = 0x12345678; + static function rng() { + rngState ^= rngState << 13; + rngState ^= rngState >>> 17; + rngState ^= rngState << 5; + return rngState; + } +} diff --git a/tests/tests/swfs/avm1/array_sort_random/output.txt b/tests/tests/swfs/avm1/array_sort_random/output.txt new file mode 100644 index 000000000000..a5deafa8f3a4 --- /dev/null +++ b/tests/tests/swfs/avm1/array_sort_random/output.txt @@ -0,0 +1,443 @@ +cmp: 0 < 1 +cmp: 0 > 49 +cmp: 0 > 49 +cmp: 0 == 2 +cmp: 0 == 1 +cmp: 0 > 48 +cmp: 0 < 48 +cmp: 0 < 2 +cmp: 0 < 47 +cmp: 0 > 46 +cmp: 0 == 46 +cmp: 0 == 48 +cmp: 0 == 45 +cmp: 0 > 44 +cmp: 0 < 44 +cmp: 0 < 46 +cmp: 0 < 43 +cmp: 0 < 42 +cmp: 0 < 41 +cmp: 0 > 40 +cmp: 0 > 40 +cmp: 0 < 3 +cmp: 0 > 44 +cmp: 0 < 44 +cmp: 0 < 3 +cmp: 0 < 39 +cmp: 0 == 38 +cmp: 0 < 37 +cmp: 0 < 36 +cmp: 0 > 35 +cmp: 0 > 35 +cmp: 0 < 4 +cmp: 0 < 44 +cmp: 0 < 34 +cmp: 0 < 33 +cmp: 0 < 32 +cmp: 0 < 31 +cmp: 0 < 30 +cmp: 0 < 29 +cmp: 0 > 28 +cmp: 0 > 28 +cmp: 0 < 5 +cmp: 0 > 4 +cmp: 0 > 4 +cmp: 0 > 6 +cmp: 0 > 7 +cmp: 0 > 8 +cmp: 0 > 9 +cmp: 0 > 10 +cmp: 0 == 11 +cmp: 0 < 5 +cmp: 0 == 27 +cmp: 0 > 26 +cmp: 0 > 26 +cmp: 0 < 12 +cmp: 0 == 11 +cmp: 0 > 25 +cmp: 0 < 25 +cmp: 0 > 12 +cmp: 0 > 12 +cmp: 0 == 13 +cmp: 0 < 25 +cmp: 0 == 24 +cmp: 0 > 23 +cmp: 0 < 23 +cmp: 0 > 13 +cmp: 0 > 13 +cmp: 0 < 14 +cmp: 0 > 23 +cmp: 0 < 23 +cmp: 0 < 14 +cmp: 0 < 22 +cmp: 0 < 21 +cmp: 0 > 20 +cmp: 0 < 20 +cmp: 0 < 23 +cmp: 0 > 19 +cmp: 0 < 19 +cmp: 0 < 20 +cmp: 0 > 18 +cmp: 0 < 18 +cmp: 0 < 19 +cmp: 0 > 17 +cmp: 0 < 17 +cmp: 0 > 18 +cmp: 0 > 18 +cmp: 0 == 15 +cmp: 0 > 17 +cmp: 0 > 17 +cmp: 0 < 16 +cmp: 0 > 15 +cmp: 0 == 15 +cmp: 0 > 16 +cmp: 0 < 16 +cmp: 0 > 15 +cmp: 0 < 15 +cmp: 0 < 16 +cmp: 0 == 15 +cmp: 0 > 17 +cmp: 17 == 49 +cmp: 17 < 18 +cmp: 17 > 13 +cmp: 17 > 13 +cmp: 17 < 40 +cmp: 17 == 49 +cmp: 17 < 12 +cmp: 17 > 26 +cmp: 17 > 26 +cmp: 17 == 35 +cmp: 17 > 40 +cmp: 17 > 40 +cmp: 17 < 28 +cmp: 17 < 35 +cmp: 17 < 10 +cmp: 17 < 9 +cmp: 17 < 8 +cmp: 17 > 7 +cmp: 17 > 7 +cmp: 17 < 4 +cmp: 17 < 28 +cmp: 17 < 6 +cmp: 17 < 4 +cmp: 17 > 7 +cmp: 7 < 13 +cmp: 7 < 40 +cmp: 7 > 26 +cmp: 7 < 26 +cmp: 7 < 13 +cmp: 7 > 26 +cmp: 13 < 40 +cmp: 4 < 6 +cmp: 4 < 18 +cmp: 4 == 49 +cmp: 4 < 12 +cmp: 4 < 35 +cmp: 4 < 10 +cmp: 4 > 9 +cmp: 4 > 9 +cmp: 4 == 28 +cmp: 4 < 6 +cmp: 4 == 8 +cmp: 4 == 28 +cmp: 4 > 9 +cmp: 28 < 8 +cmp: 28 > 18 +cmp: 28 > 18 +cmp: 28 < 6 +cmp: 28 < 8 +cmp: 28 < 49 +cmp: 28 > 12 +cmp: 28 < 12 +cmp: 28 < 6 +cmp: 28 < 35 +cmp: 28 > 10 +cmp: 28 > 10 +cmp: 28 < 12 +cmp: 28 == 10 +cmp: 28 > 18 +cmp: 10 < 12 +cmp: 10 < 8 +cmp: 10 < 49 +cmp: 10 < 6 +cmp: 10 == 35 +cmp: 10 < 12 +cmp: 12 > 35 +cmp: 12 < 6 +cmp: 12 < 8 +cmp: 12 < 49 +cmp: 12 > 6 +cmp: 6 < 35 +cmp: 49 > 8 +cmp: 15 > 16 +cmp: 15 < 19 +cmp: 15 > 1 +cmp: 15 < 1 +cmp: 15 == 19 +cmp: 15 > 2 +cmp: 15 > 2 +cmp: 15 == 20 +cmp: 15 > 1 +cmp: 15 > 1 +cmp: 15 > 23 +cmp: 15 > 21 +cmp: 15 > 22 +cmp: 15 < 14 +cmp: 15 > 20 +cmp: 15 < 20 +cmp: 15 < 14 +cmp: 15 < 47 +cmp: 15 < 48 +cmp: 15 == 45 +cmp: 15 > 46 +cmp: 15 < 46 +cmp: 15 < 20 +cmp: 15 == 43 +cmp: 15 < 42 +cmp: 15 > 41 +cmp: 15 > 41 +cmp: 15 < 24 +cmp: 15 == 46 +cmp: 15 < 3 +cmp: 15 < 39 +cmp: 15 > 38 +cmp: 15 > 38 +cmp: 15 < 25 +cmp: 15 > 24 +cmp: 15 > 24 +cmp: 15 < 11 +cmp: 15 > 25 +cmp: 15 > 25 +cmp: 15 > 27 +cmp: 15 < 5 +cmp: 15 < 11 +cmp: 15 > 37 +cmp: 15 == 37 +cmp: 15 > 5 +cmp: 15 > 5 +cmp: 15 < 29 +cmp: 15 == 37 +cmp: 15 < 36 +cmp: 15 > 44 +cmp: 15 < 44 +cmp: 15 > 29 +cmp: 15 < 29 +cmp: 15 > 44 +cmp: 15 < 44 +cmp: 15 < 29 +cmp: 15 > 34 +cmp: 15 > 34 +cmp: 15 > 30 +cmp: 15 < 31 +cmp: 15 > 44 +cmp: 15 < 44 +cmp: 15 < 31 +cmp: 15 < 33 +cmp: 15 < 32 +cmp: 15 > 44 +cmp: 44 < 16 +cmp: 44 < 30 +cmp: 44 > 34 +cmp: 44 > 34 +cmp: 44 > 2 +cmp: 44 < 1 +cmp: 44 > 16 +cmp: 44 < 16 +cmp: 44 < 1 +cmp: 44 == 5 +cmp: 44 == 27 +cmp: 44 > 25 +cmp: 44 > 25 +cmp: 44 > 23 +cmp: 44 > 21 +cmp: 44 < 22 +cmp: 44 < 16 +cmp: 44 > 24 +cmp: 44 > 24 +cmp: 44 > 41 +cmp: 44 == 38 +cmp: 44 > 22 +cmp: 44 == 22 +cmp: 44 < 38 +cmp: 44 > 22 +cmp: 22 < 34 +cmp: 22 == 41 +cmp: 22 < 24 +cmp: 22 > 21 +cmp: 22 < 21 +cmp: 22 > 34 +cmp: 22 == 34 +cmp: 22 < 21 +cmp: 22 < 23 +cmp: 22 > 25 +cmp: 22 == 25 +cmp: 22 < 34 +cmp: 22 > 2 +cmp: 22 > 2 +cmp: 22 < 25 +cmp: 22 > 2 +cmp: 25 < 34 +cmp: 25 < 41 +cmp: 25 > 24 +cmp: 25 < 24 +cmp: 25 == 34 +cmp: 25 > 21 +cmp: 25 > 21 +cmp: 25 < 23 +cmp: 25 < 24 +cmp: 25 > 23 +cmp: 23 > 21 +cmp: 24 > 34 +cmp: 24 > 41 +cmp: 41 > 34 +cmp: 38 > 16 +cmp: 38 > 27 +cmp: 38 > 5 +cmp: 38 < 1 +cmp: 38 > 30 +cmp: 38 > 30 +cmp: 38 < 1 +cmp: 38 > 30 +cmp: 30 > 16 +cmp: 30 < 27 +cmp: 30 > 5 +cmp: 30 > 5 +cmp: 30 == 27 +cmp: 30 < 5 +cmp: 30 == 16 +cmp: 16 > 5 +cmp: 16 > 27 +cmp: 27 < 5 +cmp: 32 < 33 +cmp: 32 < 19 +cmp: 32 > 14 +cmp: 32 > 14 +cmp: 32 == 31 +cmp: 32 > 33 +cmp: 32 < 33 +cmp: 32 < 31 +cmp: 32 < 47 +cmp: 32 < 48 +cmp: 32 < 45 +cmp: 32 < 20 +cmp: 32 > 43 +cmp: 32 < 43 +cmp: 32 > 33 +cmp: 32 == 33 +cmp: 32 > 43 +cmp: 32 < 43 +cmp: 32 < 33 +cmp: 32 < 42 +cmp: 32 < 46 +cmp: 32 > 3 +cmp: 32 < 3 +cmp: 32 < 43 +cmp: 32 < 39 +cmp: 32 > 11 +cmp: 32 < 11 +cmp: 32 < 3 +cmp: 32 < 37 +cmp: 32 > 36 +cmp: 32 < 36 +cmp: 32 < 11 +cmp: 32 < 29 +cmp: 32 < 36 +cmp: 32 < 14 +cmp: 14 < 36 +cmp: 14 < 19 +cmp: 14 < 31 +cmp: 14 > 47 +cmp: 14 < 47 +cmp: 14 < 36 +cmp: 14 > 48 +cmp: 14 < 48 +cmp: 14 > 47 +cmp: 14 < 47 +cmp: 14 > 48 +cmp: 14 > 48 +cmp: 14 < 29 +cmp: 14 > 47 +cmp: 14 == 47 +cmp: 14 < 29 +cmp: 14 > 45 +cmp: 14 < 45 +cmp: 14 < 47 +cmp: 14 > 20 +cmp: 14 > 20 +cmp: 14 > 11 +cmp: 14 > 37 +cmp: 14 > 3 +cmp: 14 > 39 +cmp: 14 == 43 +cmp: 14 == 45 +cmp: 14 < 33 +cmp: 14 > 42 +cmp: 14 == 42 +cmp: 14 < 43 +cmp: 14 < 46 +cmp: 14 > 42 +cmp: 42 > 48 +cmp: 42 > 20 +cmp: 42 > 11 +cmp: 42 > 37 +cmp: 42 < 3 +cmp: 42 > 39 +cmp: 42 == 39 +cmp: 42 > 3 +cmp: 42 > 3 +cmp: 42 < 39 +cmp: 42 > 3 +cmp: 3 > 48 +cmp: 3 < 20 +cmp: 3 > 37 +cmp: 3 > 37 +cmp: 3 == 11 +cmp: 3 > 20 +cmp: 3 > 20 +cmp: 3 > 11 +cmp: 11 < 48 +cmp: 11 > 20 +cmp: 11 > 20 +cmp: 11 > 37 +cmp: 11 == 48 +cmp: 11 > 37 +cmp: 37 > 20 +cmp: 46 > 43 +cmp: 46 > 33 +cmp: 46 < 45 +cmp: 46 == 19 +cmp: 46 < 31 +cmp: 46 < 36 +cmp: 46 > 29 +cmp: 46 > 29 +cmp: 46 < 47 +cmp: 46 > 45 +cmp: 46 > 45 +cmp: 46 < 47 +cmp: 46 < 45 +cmp: 46 < 29 +cmp: 46 < 33 +cmp: 46 > 43 +cmp: 33 == 29 +cmp: 33 < 19 +cmp: 33 < 31 +cmp: 33 < 36 +cmp: 33 < 47 +cmp: 33 < 45 +cmp: 33 > 29 +cmp: 45 == 47 +cmp: 45 < 19 +cmp: 45 < 31 +cmp: 45 < 36 +cmp: 45 == 47 +cmp: 47 > 36 +cmp: 47 < 31 +cmp: 47 > 19 +cmp: 47 > 19 +cmp: 47 > 31 +cmp: 31 > 36 +cmp: 31 < 19 +cmp: 31 == 36 +cmp: 36 < 19 +// contents of array +26,7,13,40,17,9,4,18,28,10,6,35,12,8,49,0,2,22,21,23,25,34,41,24,44,30,27,5,16,38,1,15,32,20,37,11,48,3,42,39,14,43,46,29,33,45,31,36,19,47 diff --git a/tests/tests/swfs/avm1/array_sort_random/test.swf b/tests/tests/swfs/avm1/array_sort_random/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..70e536906a0e6f2964521753247d300b18350086 GIT binary patch literal 489 zcmV{ ztmrhhK*u-83|hNn>E=(Tei(fZlelt^1`GQ-q_93F{Jg0nk zZIl*lW*!mHbk`m^(!CdGay}`2?Uy!P?f3Ni3#WgHvdIA+lQbO}FyZ%V*#d-462hF} zCy1ssscf#)3O2;J=)pSaDXYE#73*wCal&Amtu#-%YfPVV1gOaDC;xMtO^#7>_8vhR zn-G5Se%Zjfk!OL+m}XaFy7eTjYQSP2%lZviaJz15dsN4&JE`svIGT;00;N|${4}Wb zNL7dX>> 17; + rngState ^= rngState << 5; + return rngState; +} + +var array: Array = new Array(); +for (var i: int = 0; i < 50; i++) { + array.push(i); +} + +// "sort" the array using randomly-chosen comparison results. +array.sort(function(a:*, b:*): int { + var r: int = rng(); + if (r % 8 == 0) { + trace("cmp: " + a + " == " + b); + return 0; + } else if (r > 0) { + trace("cmp: " + a + " > " + b); + return 1; + } else { + trace("cmp: " + a + " < " + b); + return -1; + } +}); + +trace("// contents of array"); +trace(array); diff --git a/tests/tests/swfs/avm2/array_sort_random/output.txt b/tests/tests/swfs/avm2/array_sort_random/output.txt new file mode 100644 index 000000000000..0ee68161eaee --- /dev/null +++ b/tests/tests/swfs/avm2/array_sort_random/output.txt @@ -0,0 +1,210 @@ +cmp: 1 < 25 +cmp: 2 > 25 +cmp: 49 > 25 +cmp: 48 == 25 +cmp: 47 == 25 +cmp: 46 > 25 +cmp: 45 < 25 +cmp: 3 < 25 +cmp: 4 < 25 +cmp: 5 > 25 +cmp: 44 == 25 +cmp: 43 == 25 +cmp: 42 == 25 +cmp: 41 > 25 +cmp: 40 < 25 +cmp: 6 < 25 +cmp: 7 < 25 +cmp: 8 < 25 +cmp: 9 < 25 +cmp: 10 > 25 +cmp: 39 > 25 +cmp: 38 < 25 +cmp: 11 > 25 +cmp: 37 < 25 +cmp: 12 < 25 +cmp: 13 < 25 +cmp: 14 == 25 +cmp: 15 < 25 +cmp: 16 < 25 +cmp: 17 > 25 +cmp: 36 > 25 +cmp: 35 < 25 +cmp: 18 < 25 +cmp: 19 < 25 +cmp: 20 < 25 +cmp: 21 < 25 +cmp: 22 < 25 +cmp: 23 < 25 +cmp: 24 < 25 +cmp: 0 > 25 +cmp: 34 > 25 +cmp: 33 < 25 +cmp: 26 > 25 +cmp: 32 > 25 +cmp: 31 > 25 +cmp: 30 > 25 +cmp: 29 > 25 +cmp: 28 > 25 +cmp: 27 > 25 +cmp: 26 == 25 +cmp: 33 < 25 +cmp: 27 == 10 +cmp: 28 > 10 +cmp: 49 > 10 +cmp: 48 < 10 +cmp: 29 == 10 +cmp: 30 > 10 +cmp: 47 < 10 +cmp: 31 > 10 +cmp: 46 > 10 +cmp: 2 == 10 +cmp: 44 < 10 +cmp: 32 == 10 +cmp: 0 > 10 +cmp: 43 < 10 +cmp: 34 > 10 +cmp: 42 > 10 +cmp: 41 < 10 +cmp: 17 > 10 +cmp: 5 < 10 +cmp: 36 < 10 +cmp: 11 < 10 +cmp: 26 < 10 +cmp: 39 > 10 +cmp: 39 < 10 +cmp: 17 < 10 +cmp: 34 > 10 +cmp: 26 < 10 +cmp: 42 < 2 +cmp: 0 > 2 +cmp: 49 < 2 +cmp: 31 < 2 +cmp: 34 > 2 +cmp: 28 < 2 +cmp: 46 > 2 +cmp: 30 > 2 +cmp: 46 == 2 +cmp: 28 > 2 +cmp: 31 > 2 +cmp: 49 < 2 +cmp: 49 > 42 +cmp: 30 == 34 +cmp: 46 > 34 +cmp: 0 < 34 +cmp: 46 > 34 +cmp: 0 < 34 +cmp: 0 < 30 +cmp: 27 == 32 +cmp: 48 > 32 +cmp: 11 == 32 +cmp: 36 < 32 +cmp: 29 > 32 +cmp: 5 > 32 +cmp: 41 < 32 +cmp: 47 == 32 +cmp: 44 < 32 +cmp: 26 > 32 +cmp: 43 > 32 +cmp: 26 == 32 +cmp: 44 > 32 +cmp: 47 > 32 +cmp: 41 < 32 +cmp: 41 < 27 +cmp: 27 < 36 +cmp: 43 < 5 +cmp: 29 < 5 +cmp: 26 > 5 +cmp: 11 > 5 +cmp: 48 < 5 +cmp: 26 < 5 +cmp: 11 < 5 +cmp: 48 < 5 +cmp: 48 > 43 +cmp: 48 < 29 +cmp: 1 < 12 +cmp: 45 > 12 +cmp: 24 < 12 +cmp: 3 < 12 +cmp: 4 > 12 +cmp: 23 < 12 +cmp: 40 < 12 +cmp: 6 < 12 +cmp: 7 == 12 +cmp: 8 < 12 +cmp: 9 < 12 +cmp: 38 < 12 +cmp: 37 > 12 +cmp: 22 > 12 +cmp: 21 == 12 +cmp: 20 < 12 +cmp: 33 == 12 +cmp: 13 == 12 +cmp: 14 > 12 +cmp: 19 < 12 +cmp: 15 > 12 +cmp: 18 > 12 +cmp: 35 < 12 +cmp: 16 < 12 +cmp: 15 < 12 +cmp: 18 > 12 +cmp: 16 < 12 +cmp: 14 < 21 +cmp: 37 < 21 +cmp: 18 > 21 +cmp: 45 > 21 +cmp: 4 < 21 +cmp: 22 == 21 +cmp: 18 > 21 +cmp: 22 < 21 +cmp: 18 < 45 +cmp: 14 < 37 +cmp: 22 < 37 +cmp: 4 == 37 +cmp: 4 < 37 +cmp: 4 > 14 +cmp: 4 < 22 +cmp: 1 < 8 +cmp: 24 < 8 +cmp: 3 > 8 +cmp: 35 < 8 +cmp: 23 > 8 +cmp: 19 > 8 +cmp: 13 < 8 +cmp: 40 > 8 +cmp: 33 < 8 +cmp: 6 == 8 +cmp: 7 > 8 +cmp: 20 > 8 +cmp: 38 == 8 +cmp: 9 > 8 +cmp: 16 > 8 +cmp: 7 > 8 +cmp: 6 > 8 +cmp: 33 > 8 +cmp: 13 < 8 +cmp: 1 > 24 +cmp: 35 < 24 +cmp: 13 < 24 +cmp: 1 < 24 +cmp: 13 < 24 +cmp: 13 == 35 +cmp: 16 > 20 +cmp: 3 < 20 +cmp: 9 < 20 +cmp: 38 == 20 +cmp: 7 < 20 +cmp: 40 > 20 +cmp: 19 > 20 +cmp: 23 < 20 +cmp: 40 == 20 +cmp: 19 < 20 +cmp: 16 < 20 +cmp: 23 > 20 +cmp: 7 > 20 +cmp: 38 < 20 +cmp: 38 > 3 +cmp: 38 > 9 +cmp: 3 < 9 +// contents of array +13,35,24,1,8,33,6,3,9,38,20,7,23,40,19,16,12,15,14,4,22,37,21,18,45,25,41,27,36,32,47,44,43,48,29,5,26,11,10,39,17,42,49,2,31,28,0,30,34,46 diff --git a/tests/tests/swfs/avm2/array_sort_random/test.swf b/tests/tests/swfs/avm2/array_sort_random/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..dda6b20f658817f0bb3b6f0efd05dd44a9ed49c1 GIT binary patch literal 1079 zcmV-71jzeCS5qb+1^@tf0fkdbbK6uDzE=-l*^Xm7Nu8u=s6y3-4zZ=!6dE`2I8$R% z7KEu`V3_fEWa%mvktHEr=dps}AMg*d;4#zbf`0(Rf*E#6JHP@~th%e&b!U!br%6|M ze5LdFzVDuU&(RSQ{y}K^dxRD+YUCFYLSN24!w78#rd55|Xv#-@#|^9CZLRhg^Q)?Q zcz9SktdzW9PhH>G*if~yS}qp>Q4Eh=rXLmE@WyH_7HH7W2yCC(o-0R!dehF`b({tM4-DhG@TnPez05oaJ$v|WcOhMon~PKUime0&-hJr zpBs%vpQU(6C144;{og7uCJ4Rw>pF<_Yy?A^UVcx(x z?&4rY?RkBwGOw>fRr}2I)wqNA?!}an9`b>^mp*rt@`lI)-JnP``hHawcm8BOeVKc@!EzIy<})_|1GU{r#sF_uQr zFcZarGKS_KxQs$2%s!ywNa2!yb?J(OFLN8!*swx{mk2aM(OV%TS`A%og>oBilQpiE3xh1{Jx5}dWg zpvC-p<*w8mxKNnqiZPY1Xd1TpO646%OcW$BTgWeD3s1rU z%n_kLgn1$?5N?r>tAt!5c!h8`2v!KbC34VH=_|}3ei}L}!OF#N;Wd0+hV?qEo3K`~ zASOk`BMyNjG%!rUbRF0j;{=~Yxw?=>n9J8i2=M?EF#h5>#;EWda~K{xH~+6NoNwbM zM&X~sg{O-2t%BwX2n`q8+* zM5oT9vNGC%`Q)LXyRcKrbU64cneQH%Z}z!idgAAQ82eKbU!3GK-~*q-SW5Dz+It9r zQZkh~)lMHE(7Oc@f#b3C2%l=;^AxocRzhnNi)d}hlC*ZxN^0$tmD1XxCB~0W(Q^Fj z3BX8FRL4CGmQ=)XO}TdZ{WJno80MAeE#{UGN)BgCM=%|+IR$r-2jk~IoXwj;rpnVP-$V(ZY^{{WZ Date: Tue, 10 Sep 2024 17:07:31 +0200 Subject: [PATCH 2/2] avm2: replace Rust's sort by avmplus' for `Array` sorts; unblocks #17812 Replace the use of Rust's standard sort in `Array.sort` and `sortOn` by a port of avmplus' QuickSort algorithm. This avoid panics on Rust >=1.81 when `Array.sort` is called with a non-Ord comparison function, and will always produce the same result as Flash Player. --- core/src/avm2/globals/array.rs | 117 +++++++++++++++--- .../swfs/avm2/array_sort_random/test.toml | 1 - 2 files changed, 102 insertions(+), 16 deletions(-) diff --git a/core/src/avm2/globals/array.rs b/core/src/avm2/globals/array.rs index 77ca77666082..43ade131daf1 100644 --- a/core/src/avm2/globals/array.rs +++ b/core/src/avm2/globals/array.rs @@ -866,38 +866,125 @@ where C: FnMut(&mut Activation<'a, 'gc>, Value<'gc>, Value<'gc>) -> Result>, { let mut unique_sort_satisfied = true; - let mut error_signal = Ok(()); - values.sort_unstable_by(|(_a_index, a), (_b_index, b)| { + qsort(values, &mut |(_, a), (_, b)| { let unresolved_a = *a; let unresolved_b = *b; if matches!(unresolved_a, Value::Undefined) && matches!(unresolved_b, Value::Undefined) { unique_sort_satisfied = false; - return Ordering::Equal; + return Ok(Ordering::Equal); } else if matches!(unresolved_a, Value::Undefined) { - return Ordering::Greater; + return Ok(Ordering::Greater); } else if matches!(unresolved_b, Value::Undefined) { - return Ordering::Less; + return Ok(Ordering::Less); } - match sort_func(activation, *a, *b) { - Ok(Ordering::Equal) => { + sort_func(activation, *a, *b).map(|cmp| { + if cmp == Ordering::Equal { unique_sort_satisfied = false; Ordering::Equal + } else if options.contains(SortOptions::DESCENDING) { + cmp.reverse() + } else { + cmp + } + }) + })?; + + Ok(!options.contains(SortOptions::UNIQUE_SORT) || unique_sort_satisfied) +} + +/// A port of the avmplus QuickSort implementation. +/// +/// This differs from Rust's `slice::sort` in the following way: +/// - the comparison function is faillible and can return an error, short-circuiting the sort; +/// - the comparison function isn't required to define a valid total order: in such cases, the sort +/// will permute the slice arbitrarily, but won't return an error. +/// +/// Original code: https://github.com/adobe/avmplus/blob/master/core/ArrayClass.cpp#L637 +fn qsort( + slice: &mut [T], + cmp: &mut impl FnMut(&T, &T) -> Result, +) -> Result<(), E> { + match slice { + // Empty and one-element slices are trivially sorted. + [] | [_] => return Ok(()), + // Fast-path for two elements. + [a, b] => { + if cmp(a, b)?.is_gt() { + swap(a, b); + } + return Ok(()); + } + // Fast-path for three elements. + [a, b, c] => { + if cmp(a, b)?.is_gt() { + swap(a, b); + } + if cmp(b, c)?.is_gt() { + swap(b, c); + if cmp(a, b)?.is_gt() { + swap(a, b); + } + } + return Ok(()); + } + _ => (), + } + + // Select the middle element of the slice as the pivot, and put it at the beginning. + slice.swap(0, slice.len() / 2); + + // Order the elements (excluding the pivot) such that all elements lower + // than the pivot come before all elements greater than the pivot. + // + // This is done by iterating from both ends, swapping greater elements with + // lower ones along the way. + let mut left = 0; + let mut right = slice.len(); + loop { + // Find an element greater than the pivot from the left. + loop { + left += 1; + if left >= slice.len() || cmp(&slice[left], &slice[0])?.is_gt() { + break; } - Ok(v) if options.contains(SortOptions::DESCENDING) => v.reverse(), - Ok(v) => v, - Err(e) => { - error_signal = Err(e); - Ordering::Less + } + + // Find an element lower than the pivot from the right. + loop { + right -= 1; + if right == 0 || cmp(&slice[right], &slice[0])?.is_lt() { + break; } } - }); - error_signal?; + // Nothing left to swap, we are done. + if right < left { + break; + } - Ok(!options.contains(SortOptions::UNIQUE_SORT) || unique_sort_satisfied) + // Otherwise, swap left and right, and keep going. + slice.swap(left, right); + } + + // Put the pivot in its final position. + slice.swap(0, right); + + // The elements are now ordered as follows: + // [..right]: lower partition + // [right..left]: middle partition (equal to pivot) + // [left..]: higher partition + + // Recurse into both higher and lower partitions, with the smallest first. + let (mut fst, mut snd) = slice.split_at_mut(left); + fst = &mut fst[..right]; + if fst.len() >= snd.len() { + swap(&mut fst, &mut snd); + } + qsort(fst, cmp)?; + qsort(snd, cmp) } pub fn compare_string_case_sensitive<'gc>( diff --git a/tests/tests/swfs/avm2/array_sort_random/test.toml b/tests/tests/swfs/avm2/array_sort_random/test.toml index 77f4c0e41c1f..dbee897f5863 100644 --- a/tests/tests/swfs/avm2/array_sort_random/test.toml +++ b/tests/tests/swfs/avm2/array_sort_random/test.toml @@ -1,2 +1 @@ num_frames = 1 -known_failure = true