diff --git a/js/src/builtin/Sorting.js b/js/src/builtin/Sorting.js index 4fead92dc178c..c1efcca58ec96 100644 --- a/js/src/builtin/Sorting.js +++ b/js/src/builtin/Sorting.js @@ -165,18 +165,31 @@ function MergeSortTypedArray(array, len, comparefn) { assert(IsPossiblyWrappedTypedArray(array), "MergeSortTypedArray works only with typed arrays."); + // Use the same TypedArray kind for the buffer. + var C = ConstructorForTypedArray(array); + + var lBuffer = new C(len); + + // Copy all elements into a temporary buffer, so that any modifications + // when calling |comparefn| are ignored. + for (var i = 0; i < len; i++) { + lBuffer[i] = array[i]; + } + // Insertion sort for small arrays, where "small" is defined by performance // testing. if (len < 8) { - InsertionSort(array, 0, len - 1, comparefn); + InsertionSort(lBuffer, 0, len - 1, comparefn); + + // Move the sorted elements into the array. + for (var i = 0; i < len; i++) { + array[i] = lBuffer[i]; + } + return array; } - // Use the same TypedArray kind for the buffer. - var C = ConstructorForTypedArray(array); - // We do all of our allocating up front. - var lBuffer = array; var rBuffer = new C(len); // Use insertion sort for the initial ranges. @@ -204,10 +217,8 @@ function MergeSortTypedArray(array, len, comparefn) { } // Move the sorted elements into the array. - if (lBuffer !== array) { - for (var i = 0; i < len; i++) { - array[i] = lBuffer[i]; - } + for (var i = 0; i < len; i++) { + array[i] = lBuffer[i]; } return array; diff --git a/js/src/tests/non262/TypedArray/sort_modifications.js b/js/src/tests/non262/TypedArray/sort_modifications.js new file mode 100644 index 0000000000000..2a6ed49eb79fa --- /dev/null +++ b/js/src/tests/non262/TypedArray/sort_modifications.js @@ -0,0 +1,73 @@ +const TAConstructors = [ + Int8Array, + Uint8Array, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Uint8ClampedArray, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, +]; + +// Use different size classes to catch any implementation-specific +// optimisations. +const sizes = [ + 4, 8, 64, 128, 1024 +]; + +function ToNumeric(TA) { + if (TA === BigInt64Array || TA === BigUint64Array) { + return BigInt; + } + return Number; +} + +function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +function descending(a, b) { + return -ascending(a, b); +} + +for (let TA of TAConstructors) { + let toNumeric = ToNumeric(TA); + for (let size of sizes) { + let sorted = new TA(size); + + // Fill with |1..size| and then sort to account for wrap-arounds. + for (let i = 0; i < size; ++i) { + sorted[i] = toNumeric(i + 1); + } + sorted.sort(); + + // Create a copy in descending order. + let ta = new TA(sorted); + ta.sort(descending); + + // Sort the copy in ascending order and on the first call reset all + // elements to zero. + let called = false; + ta.sort(function(a, b) { + if (!called) { + called = true; + ta.fill(toNumeric(0)); + } + return ascending(a, b); + }); + + // Ensure the comparator function was called. + assertEq(called, true); + + // All elements should be sorted correctly. No elements should be zero. + for (let i = 0; i < size; ++i) { + assertEq(ta[i], sorted[i], `${TA.name} at index ${i} for size ${size}`); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true);