Skip to content

Commit 586d578

Browse files
committed
make %TypedArray%.prototype.sort stricter
1 parent 07ca565 commit 586d578

6 files changed

Lines changed: 141 additions & 102 deletions

File tree

Lines changed: 3 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,6 @@
11
'use strict';
2-
var aFunction = require('../internals/a-function');
3-
var toObject = require('../internals/to-object');
4-
var toLength = require('../internals/to-length');
5-
var fails = require('../internals/fails');
6-
var arrayMethodIsStrict = require('../internals/array-method-is-strict');
7-
var FF = require('../internals/engine-ff-version');
8-
var IE_OR_EDGE = require('../internals/engine-is-ie-or-edge');
9-
var V8 = require('../internals/engine-v8-version');
10-
var WEBKIT = require('../internals/engine-webkit-version');
11-
12-
var test = [];
13-
var nativeSort = test.sort;
142
var floor = Math.floor;
153

16-
// IE8-
17-
var FAILS_ON_UNDEFINED = fails(function () {
18-
test.sort(undefined);
19-
});
20-
// V8 bug
21-
var FAILS_ON_NULL = fails(function () {
22-
test.sort(null);
23-
});
24-
// Old WebKit
25-
var STRICT_METHOD = arrayMethodIsStrict('sort');
26-
27-
var STABLE_SORT = !fails(function () {
28-
// feature detection can be too slow, so check engines versions
29-
if (V8) return V8 < 70;
30-
if (FF && FF > 3) return;
31-
if (IE_OR_EDGE) return true;
32-
if (WEBKIT) return WEBKIT < 603;
33-
34-
var result = '';
35-
var code, chr, value, index;
36-
37-
// generate an array with more 512 elements (Chakra and old V8 fails only in this case)
38-
for (code = 65; code < 76; code++) {
39-
chr = String.fromCharCode(code);
40-
switch (code) {
41-
case 66: case 69: case 70: case 72: value = 3; break;
42-
case 68: case 71: value = 4; break;
43-
default: value = 2;
44-
}
45-
46-
for (index = 0; index < 47; index++) {
47-
test.push({ k: chr + index, v: value });
48-
}
49-
}
50-
51-
test.sort(function (a, b) { return b.v - a.v; });
52-
53-
for (index = 0; index < test.length; index++) {
54-
chr = test[index].k.charAt(0);
55-
if (result.charAt(result.length - 1) !== chr) result += chr;
56-
}
57-
58-
return result !== 'DGBEFHACIJK';
59-
});
60-
61-
var FORCED = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD || !STABLE_SORT;
62-
634
var mergeSort = function (array, comparefn) {
645
var length = array.length;
656
var middle = floor(length / 2);
@@ -78,7 +19,7 @@ var insertionSort = function (array, comparefn) {
7819
while (i < length) {
7920
j = i;
8021
element = array[i];
81-
while (j && sortCompare(array[j - 1], element, comparefn) > 0) {
22+
while (j && comparefn(array[j - 1], element) > 0) {
8223
array[j] = array[--j];
8324
}
8425
if (j !== i++) array[j] = element;
@@ -94,45 +35,11 @@ var merge = function (left, right, comparefn) {
9435

9536
while (lindex < llength || rindex < rlength) {
9637
if (lindex < llength && rindex < rlength) {
97-
result.push(sortCompare(left[lindex], right[rindex], comparefn) <= 0 ? left[lindex++] : right[rindex++]);
38+
result.push(comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]);
9839
} else {
9940
result.push(lindex < llength ? left[lindex++] : right[rindex++]);
10041
}
10142
} return result;
10243
};
10344

104-
var sortCompare = function (x, y, comparefn) {
105-
if (y === undefined) return -1;
106-
if (x === undefined) return 1;
107-
if (comparefn !== undefined) {
108-
return +comparefn(x, y) || 0;
109-
} return String(x) > String(y) ? 1 : -1;
110-
};
111-
112-
// `Array.prototype.sort` method
113-
// https://tc39.es/ecma262/#sec-array.prototype.sort
114-
module.exports = FORCED ? function sort(comparefn) {
115-
if (comparefn !== undefined) aFunction(comparefn);
116-
117-
var array = toObject(this);
118-
119-
if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn);
120-
121-
var items = [];
122-
var arrayLength = toLength(array.length);
123-
var itemsLength, index;
124-
125-
for (index = 0; index < arrayLength; index++) {
126-
if (index in array) items.push(array[index]);
127-
}
128-
129-
// TODO: use something more complex like timsort?
130-
items = mergeSort(items, comparefn);
131-
itemsLength = items.length;
132-
index = 0;
133-
134-
while (index < itemsLength) array[index] = items[index++];
135-
while (index < arrayLength) delete array[index++];
136-
137-
return array;
138-
} : nativeSort;
45+
module.exports = mergeSort;
Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,102 @@
11
'use strict';
22
var $ = require('../internals/export');
3-
var sort = require('../internals/array-sort');
3+
var aFunction = require('../internals/a-function');
4+
var toObject = require('../internals/to-object');
5+
var toLength = require('../internals/to-length');
6+
var fails = require('../internals/fails');
7+
var internalSort = require('../internals/array-sort');
8+
var arrayMethodIsStrict = require('../internals/array-method-is-strict');
9+
var FF = require('../internals/engine-ff-version');
10+
var IE_OR_EDGE = require('../internals/engine-is-ie-or-edge');
11+
var V8 = require('../internals/engine-v8-version');
12+
var WEBKIT = require('../internals/engine-webkit-version');
13+
14+
var test = [];
15+
var nativeSort = test.sort;
16+
17+
// IE8-
18+
var FAILS_ON_UNDEFINED = fails(function () {
19+
test.sort(undefined);
20+
});
21+
// V8 bug
22+
var FAILS_ON_NULL = fails(function () {
23+
test.sort(null);
24+
});
25+
// Old WebKit
26+
var STRICT_METHOD = arrayMethodIsStrict('sort');
27+
28+
var STABLE_SORT = !fails(function () {
29+
// feature detection can be too slow, so check engines versions
30+
if (V8) return V8 < 70;
31+
if (FF && FF > 3) return;
32+
if (IE_OR_EDGE) return true;
33+
if (WEBKIT) return WEBKIT < 603;
34+
35+
var result = '';
36+
var code, chr, value, index;
37+
38+
// generate an array with more 512 elements (Chakra and old V8 fails only in this case)
39+
for (code = 65; code < 76; code++) {
40+
chr = String.fromCharCode(code);
41+
42+
switch (code) {
43+
case 66: case 69: case 70: case 72: value = 3; break;
44+
case 68: case 71: value = 4; break;
45+
default: value = 2;
46+
}
47+
48+
for (index = 0; index < 47; index++) {
49+
test.push({ k: chr + index, v: value });
50+
}
51+
}
52+
53+
test.sort(function (a, b) { return b.v - a.v; });
54+
55+
for (index = 0; index < test.length; index++) {
56+
chr = test[index].k.charAt(0);
57+
if (result.charAt(result.length - 1) !== chr) result += chr;
58+
}
59+
60+
return result !== 'DGBEFHACIJK';
61+
});
62+
63+
var FORCED = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD || !STABLE_SORT;
64+
65+
var getSortCompare = function (comparefn) {
66+
return function (x, y) {
67+
if (y === undefined) return -1;
68+
if (x === undefined) return 1;
69+
if (comparefn !== undefined) return +comparefn(x, y) || 0;
70+
return String(x) > String(y) ? 1 : -1;
71+
};
72+
};
473

574
// `Array.prototype.sort` method
675
// https://tc39.es/ecma262/#sec-array.prototype.sort
7-
$({ target: 'Array', proto: true, forced: [].sort !== sort }, {
8-
sort: sort
76+
$({ target: 'Array', proto: true, forced: FORCED }, {
77+
sort: function sort(comparefn) {
78+
if (comparefn !== undefined) aFunction(comparefn);
79+
80+
var array = toObject(this);
81+
82+
if (STABLE_SORT) return comparefn === undefined ? nativeSort.call(array) : nativeSort.call(array, comparefn);
83+
84+
var items = [];
85+
var arrayLength = toLength(array.length);
86+
var itemsLength, index;
87+
88+
for (index = 0; index < arrayLength; index++) {
89+
if (index in array) items.push(array[index]);
90+
}
91+
92+
// TODO: use something more complex like timsort?
93+
items = internalSort(items, getSortCompare(comparefn));
94+
itemsLength = items.length;
95+
index = 0;
96+
97+
while (index < itemsLength) array[index] = items[index++];
98+
while (index < arrayLength) delete array[index++];
99+
100+
return array;
101+
}
9102
});

packages/core-js/modules/es.typed-array.sort.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ var ArrayBufferViewCore = require('../internals/array-buffer-view-core');
33
var global = require('../internals/global');
44
var fails = require('../internals/fails');
55
var aFunction = require('../internals/a-function');
6-
var arraySort = require('../internals/array-sort');
6+
var toLength = require('../internals/to-length');
7+
var internalSort = require('../internals/array-sort');
78
var FF = require('../internals/engine-ff-version');
89
var IE_OR_EDGE = require('../internals/engine-is-ie-or-edge');
910
var V8 = require('../internals/engine-v8-version');
@@ -47,10 +48,39 @@ var STABLE_SORT = !!nativeSort && !fails(function () {
4748
}
4849
});
4950

51+
var getSortCompare = function (comparefn) {
52+
return function (x, y) {
53+
if (comparefn !== undefined) return +comparefn(x, y) || 0;
54+
// eslint-disable-next-line no-self-compare -- NaN check
55+
if (y !== y) return -1;
56+
// eslint-disable-next-line no-self-compare -- NaN check
57+
if (x !== x) return 1;
58+
if (x === 0 && y === 0) return 1 / x > 0 && 1 / y < 0 ? 1 : -1;
59+
return x > y;
60+
};
61+
};
62+
5063
// `%TypedArray%.prototype.sort` method
5164
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort
5265
exportTypedArrayMethod('sort', function sort(comparefn) {
53-
if (!STABLE_SORT) return arraySort.call(aTypedArray(this), comparefn);
66+
var array = this;
5467
if (comparefn !== undefined) aFunction(comparefn);
55-
return nativeSort.call(this, comparefn);
68+
if (STABLE_SORT) return nativeSort.call(array, comparefn);
69+
70+
aTypedArray(array);
71+
var arrayLength = toLength(array.length);
72+
var items = Array(arrayLength);
73+
var index;
74+
75+
for (index = 0; index < arrayLength; index++) {
76+
items[index] = array[index];
77+
}
78+
79+
items = internalSort(array, getSortCompare(comparefn));
80+
81+
for (index = 0; index < arrayLength; index++) {
82+
array[index] = items[index];
83+
}
84+
85+
return array;
5686
}, !STABLE_SORT || ACCEPT_INCORRECT_ARGUMENTS);

tests/pure/es.array.sort.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ QUnit.test('Array#sort', assert => {
8080

8181
sort(array, (a, b) => (a / 4 | 0) - (b / 4 | 0));
8282

83+
assert.ok(1 / sort([0, -0])[0] > 0, '-0');
84+
8385
assert.same(String(array), String(expected), 'stable #1');
8486

8587
let result = '';

tests/tests/es.array.sort.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ QUnit.test('Array#sort', assert => {
8080

8181
array.sort((a, b) => (a / 4 | 0) - (b / 4 | 0));
8282

83+
assert.ok(1 / [0, -0].sort()[0] > 0, '-0');
84+
8385
assert.same(String(array), String(expected), 'stable #1');
8486

8587
let result = '';

tests/tests/es.typed-array.sort.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ if (DESCRIPTORS) QUnit.test('%TypedArrayPrototype%.sort', assert => {
1010
assert.name(sort, 'sort', `${ name }::sort name is 'sort'`);
1111
assert.looksNative(sort, `${ name }::sort looks native`);
1212

13+
if (name.indexOf('Float') === 0) {
14+
assert.ok(1 / new TypedArray([0, -0]).sort()[0] < 0, '-0');
15+
assert.deepEqual(new TypedArray([NaN, 1, NaN]).sort(), new TypedArray([1, NaN, NaN]), 'NaN');
16+
}
17+
1318
if (name.indexOf('8') === -1) {
1419
const expected = Array(516);
1520
let array = new TypedArray(516);

0 commit comments

Comments
 (0)