blob: fdd2333d7c8a1332e2ae7b5d6667218037c62993 [file] [log] [blame]
Ben Murdochb0fe1622011-05-05 13:52:32 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --allow-natives-syntax
29
30// Test array sort.
31
32// Test counter-intuitive default number sorting.
33function TestNumberSort() {
34 var a = [ 200, 45, 7 ];
35
36 // Default sort converts each element to string and orders
37 // lexicographically.
38 a.sort();
39 assertArrayEquals([ 200, 45, 7 ], a);
40 // Sort numbers by value using a compare functions.
41 a.sort(function(x, y) { return x - y; });
42 assertArrayEquals([ 7, 45, 200 ], a);
43
44 // Default sort on negative numbers.
45 a = [-12345,-123,-1234,-123456];
46 a.sort();
47 assertArrayEquals([-123,-1234,-12345,-123456], a);
48
49 // Default sort on negative and non-negative numbers.
50 a = [123456,0,-12345,-123,123,1234,-1234,0,12345,-123456];
51 a.sort();
52 assertArrayEquals([-123,-1234,-12345,-123456,0,0,123,1234,12345,123456], a);
53
54 // Tricky case avoiding integer overflow in Runtime_SmiLexicographicCompare.
55 a = [9, 1000000000].sort();
56 assertArrayEquals([1000000000, 9], a);
57 a = [1000000000, 1].sort();
58 assertArrayEquals([1, 1000000000], a);
59 a = [1000000000, 0].sort();
60 assertArrayEquals([0, 1000000000], a);
61
62 // One string is a prefix of the other.
63 a = [1230, 123].sort();
64 assertArrayEquals([123, 1230], a);
65 a = [1231, 123].sort();
66 assertArrayEquals([123, 1231], a);
67
68 // Default sort on Smis and non-Smis.
69 a = [1000000000, 10000000000, 1000000001, -1000000000, -10000000000, -1000000001];
70 a.sort();
71 assertArrayEquals([-1000000000, -10000000000, -1000000001, 1000000000, 10000000000, 1000000001], a);
72
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000073 // Other cases are tested implicitly in TestSmiLexicographicCompare.
74}
Steve Blocka7e24c12009-10-30 11:49:00 +000075
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000076TestNumberSort();
77
78function TestSmiLexicographicCompare() {
79
80 assertFalse(%_IsSmi(2147483648), 'Update test for >32 bit Smi');
81
82 // Collect a list of interesting Smis.
83 var seen = {};
84 var smis = [];
85 function add(x) {
86 if (x | 0 == x) {
87 x = x | 0; // Canonicalizes to Smi if 32-bit signed and fits in Smi.
88 }
89 if (%_IsSmi(x) && !seen[x]) {
90 seen[x] = 1;
91 smis.push(x);
92 }
93 }
94 function addSigned(x) {
95 add(x);
96 add(-x);
97 }
98
99 var BIGGER_THAN_ANY_SMI = 10 * 1000 * 1000 * 1000;
100 for (var xb = 1; xb <= BIGGER_THAN_ANY_SMI; xb *= 10) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000101 for (var xf = 0; xf <= 9; xf++) {
102 for (var xo = -1; xo <= 1; xo++) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000103 addSigned(xb * xf + xo);
Steve Blocka7e24c12009-10-30 11:49:00 +0000104 }
105 }
106 }
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000107
108 for (var yb = 1; yb <= BIGGER_THAN_ANY_SMI; yb *= 2) {
109 for (var yo = -2; yo <= 2; yo++) {
110 addSigned(yb + yo);
111 }
112 }
113
114 for (var i = 0; i < smis.length; i++) {
115 for (var j = 0; j < smis.length; j++) {
116 var x = smis[i];
117 var y = smis[j];
118 var lex = %SmiLexicographicCompare(x, y);
119 var expected = (x == y) ? 0 : ((x + "") < (y + "") ? -1 : 1);
120 assertEquals(lex, expected, x + " < " + y);
121 }
122 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000123}
124
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000125TestSmiLexicographicCompare();
Steve Blocka7e24c12009-10-30 11:49:00 +0000126
127
128// Test lexicographical string sorting.
129function TestStringSort() {
130 var a = [ "cc", "c", "aa", "a", "bb", "b", "ab", "ac" ];
131 a.sort();
132 assertArrayEquals([ "a", "aa", "ab", "ac", "b", "bb", "c", "cc" ], a);
133}
134
135TestStringSort();
136
137
138// Test object sorting. Calls toString on each element and sorts
139// lexicographically.
140function TestObjectSort() {
141 var obj0 = { toString: function() { return "a"; } };
142 var obj1 = { toString: function() { return "b"; } };
143 var obj2 = { toString: function() { return "c"; } };
144 var a = [ obj2, obj0, obj1 ];
145 a.sort();
146 assertArrayEquals([ obj0, obj1, obj2 ], a);
147}
148
149TestObjectSort();
150
151// Test array sorting with holes in the array.
152function TestArraySortingWithHoles() {
153 var a = [];
154 a[4] = "18";
155 a[10] = "12";
156 a.sort();
157 assertEquals(11, a.length);
158 assertEquals("12", a[0]);
159 assertEquals("18", a[1]);
160}
161
162TestArraySortingWithHoles();
163
164// Test array sorting with undefined elemeents in the array.
165function TestArraySortingWithUndefined() {
166 var a = [ 3, void 0, 2 ];
167 a.sort();
168 assertArrayEquals([ 2, 3, void 0 ], a);
169}
170
171TestArraySortingWithUndefined();
172
173// Test that sorting using an unsound comparison function still gives a
174// sane result, i.e. it terminates without error and retains the elements
175// in the array.
176function TestArraySortingWithUnsoundComparisonFunction() {
177 var a = [ 3, void 0, 2 ];
178 a.sort(function(x, y) { return 1; });
179 a.sort();
180 assertArrayEquals([ 2, 3, void 0 ], a);
181}
182
183TestArraySortingWithUnsoundComparisonFunction();
184
185
186function TestSparseNonArraySorting(length) {
187 assertTrue(length > 101);
188 var obj = {length: length};
189 obj[0] = 42;
190 obj[10] = 37;
191 obj[100] = undefined;
192 obj[length - 1] = null;
193 Array.prototype.sort.call(obj);
194 assertEquals(length, obj.length, "objsort length unaffected");
195 assertEquals(37, obj[0], "objsort smallest number");
196 assertEquals(42, obj[1], "objsort largest number");
197 assertEquals(null, obj[2], "objsort null");
198 assertEquals(undefined, obj[3], "objsort undefined");
199 assertTrue(3 in obj, "objsort undefined retained");
200 assertFalse(4 in obj, "objsort non-existing retained");
201}
202
203TestSparseNonArraySorting(5000);
204TestSparseNonArraySorting(500000);
205TestSparseNonArraySorting(Math.pow(2, 31) + 1);
206
207
208function TestArrayLongerLength(length) {
209 var x = new Array(4);
210 x[0] = 42;
211 x[2] = 37;
212 x.length = length;
213 Array.prototype.sort.call(x);
214 assertEquals(length, x.length, "longlength length");
215 assertEquals(37, x[0], "longlength first");
216 assertEquals(42, x[1], "longlength second");
217 assertFalse(2 in x,"longlength third");
218}
219
220TestArrayLongerLength(4);
221TestArrayLongerLength(10);
222TestArrayLongerLength(1000);
223TestArrayLongerLength(500000);
224TestArrayLongerLength(Math.pow(2,32) - 1);
225
226
227function TestNonArrayLongerLength(length) {
228 var x = {};
229 x[0] = 42;
230 x[2] = 37;
231 x.length = length;
232 Array.prototype.sort.call(x);
233 assertEquals(length, x.length, "longlength length");
234 assertEquals(37, x[0], "longlength first");
235 assertEquals(42, x[1], "longlength second");
236 assertFalse(2 in x,"longlength third");
237}
238
239TestNonArrayLongerLength(4);
240TestNonArrayLongerLength(10);
241TestNonArrayLongerLength(1000);
242TestNonArrayLongerLength(500000);
243TestNonArrayLongerLength(Math.pow(2,32) - 1);
244
245
246function TestNonArrayWithAccessors() {
247 // Regression test for issue 346, more info at URL
248 // http://code.google.com/p/v8/issues/detail?id=346
249 // Reported by nth10sd, test based on this report.
250 var x = {};
251 x[0] = 42;
252 x.__defineGetter__("1", function(){return this.foo;});
253 x.__defineSetter__("1", function(val){this.foo = val;});
254 x[1] = 49
255 x[3] = 37;
256 x.length = 4;
257 Array.prototype.sort.call(x);
258 // Behavior of sort with accessors is undefined. This accessor is
259 // well-behaved (acts like a normal property), so it should work.
260 assertEquals(4, x.length, "sortaccessors length");
261 assertEquals(37, x[0], "sortaccessors first");
262 assertEquals(42, x[1], "sortaccessors second");
263 assertEquals(49, x[2], "sortaccessors third")
264 assertFalse(3 in x, "sortaccessors fourth");
265}
266
267TestNonArrayWithAccessors();
268
269
270function TestInheritedElementSort(depth) {
271 var length = depth * 2 + 3;
272 var obj = {length: length};
273 obj[depth * 2 + 1] = 0;
274 for (var i = 0; i < depth; i++) {
275 var newObj = {};
276 newObj.__proto__ = obj;
277 obj[i] = undefined;
278 obj[i + depth + 1] = depth - i;
279 obj = newObj;
280 }
281 // expected (inherited) object: [undef1,...undefdepth,hole,1,...,depth,0,hole]
282
283 Array.prototype.sort.call(obj, function(a,b) { return (b < a) - (a < b); });
284 // expected result: [0,1,...,depth,undef1,...,undefdepth,undef,hole]
285 var name = "SortInherit("+depth+")-";
286
287 assertEquals(length, obj.length, name+"length");
288 for (var i = 0; i <= depth; i++) {
289 assertTrue(obj.hasOwnProperty(i), name + "hasvalue" + i);
290 assertEquals(i, obj[i], name + "value" + i);
291 }
292 for (var i = depth + 1; i <= depth * 2 + 1; i++) {
293 assertEquals(undefined, obj[i], name + "undefined" + i);
294 assertTrue(obj.hasOwnProperty(i), name + "hasundefined" + i);
295 }
296 assertTrue(!obj.hasOwnProperty(depth * 2 + 2), name + "hashole");
297}
298
299TestInheritedElementSort(5);
300TestInheritedElementSort(15);
301
302function TestSparseInheritedElementSort(scale) {
303 var length = scale * 10;
304 var x = {length: length};
305 var y = {};
306 y.__proto__ = x;
307
308 for (var i = 0; i < 5; i++) {
309 x[i * 2 * scale] = 2 * (4 - i);
310 y[(i * 2 + 1) * scale] = 2 * (4 - i) + 1;
311 }
312
313 var name = "SparseSortInherit(" + scale + ")-";
314
315 Array.prototype.sort.call(y);
316
317 assertEquals(length, y.length, name +"length");
318
319 for (var i = 0; i < 10; i++) {
320 assertTrue(y.hasOwnProperty(i), name + "hasvalue" + i);
321 assertEquals(i, y[i], name + "value" + i);
322 }
323 for (var i = 10; i < length; i++) {
324 assertEquals(x.hasOwnProperty(i), y.hasOwnProperty(i),
325 name + "hasundef" + i);
326 assertEquals(undefined, y[i], name+"undefined"+i);
327 if (x.hasOwnProperty(i)) {
328 assertTrue(0 == i % (2 * scale), name + "new_x" + i);
329 }
330 }
331}
332
333TestSparseInheritedElementSort(10);
334TestSparseInheritedElementSort(100);
335TestSparseInheritedElementSort(1000);
336
337function TestSpecialCasesInheritedElementSort() {
338
339 var x = {
340 1:"d1",
341 2:"c1",
342 3:"b1",
343 4: undefined,
344 __proto__: {
345 length: 10000,
346 1: "e2",
347 10: "a2",
348 100: "b2",
349 1000: "c2",
350 2000: undefined,
351 8000: "d2",
352 12000: "XX",
353 __proto__: {
354 0: "e3",
355 1: "d3",
356 2: "c3",
357 3: "b3",
358 4: "f3",
359 5: "a3",
360 6: undefined,
361 }
362 }
363 };
364 Array.prototype.sort.call(x);
365
366 var name = "SpecialInherit-";
367
368 assertEquals(10000, x.length, name + "length");
369 var sorted = ["a2", "a3", "b1", "b2", "c1", "c2", "d1", "d2", "e3",
370 undefined, undefined, undefined];
371 for (var i = 0; i < sorted.length; i++) {
372 assertTrue(x.hasOwnProperty(i), name + "has" + i)
373 assertEquals(sorted[i], x[i], name + i);
374 }
375 assertFalse(x.hasOwnProperty(sorted.length), name + "haspost");
376 assertFalse(sorted.length in x, name + "haspost2");
377 assertTrue(x.hasOwnProperty(10), name + "hasundefined10");
378 assertEquals(undefined, x[10], name + "undefined10");
379 assertTrue(x.hasOwnProperty(100), name + "hasundefined100");
380 assertEquals(undefined, x[100], name + "undefined100");
381 assertTrue(x.hasOwnProperty(1000), name + "hasundefined1000");
382 assertEquals(undefined, x[1000], name + "undefined1000");
383 assertTrue(x.hasOwnProperty(2000), name + "hasundefined2000");
384 assertEquals(undefined, x[2000], name + "undefined2000");
385 assertTrue(x.hasOwnProperty(8000), name + "hasundefined8000");
386 assertEquals(undefined, x[8000], name + "undefined8000");
387 assertFalse(x.hasOwnProperty(12000), name + "has12000");
388 assertEquals("XX", x[12000], name + "XX12000");
389}
390
391TestSpecialCasesInheritedElementSort();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100392
393// Test that sort calls compare function with global object as receiver,
394// and with only elements of the array as arguments.
Ben Murdoch589d6972011-11-30 16:04:58 +0000395function o(v) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100396 return {__proto__: o.prototype, val: v};
397}
398var arr = [o(1), o(2), o(4), o(8), o(16), o(32), o(64), o(128), o(256), o(-0)];
399var global = this;
400function cmpTest(a, b) {
401 assertEquals(global, this);
402 assertTrue(a instanceof o);
403 assertTrue(b instanceof o);
404 return a.val - b.val;
405}
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000406arr.sort(cmpTest);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000407
408function TestSortDoesNotDependOnObjectPrototypeHasOwnProperty() {
409 Array.prototype.sort.call({
410 __proto__: { hasOwnProperty: null, 0: 1 },
411 length: 5
412 });
413
414 var arr = new Array(2);
415 Object.defineProperty(arr, 0, { get: function() {}, set: function() {} });
416 arr.hasOwnProperty = null;
417 arr.sort();
418}
419
420TestSortDoesNotDependOnObjectPrototypeHasOwnProperty();
421
422function TestSortDoesNotDependOnArrayPrototypePush() {
423 // InsertionSort is used for arrays which length <= 22
424 var arr = [];
425 for (var i = 0; i < 22; i++) arr[i] = {};
426 Array.prototype.push = function() {
427 fail('Should not call push');
428 };
429 arr.sort();
430
431 // Quicksort is used for arrays which length > 22
432 // Arrays which length > 1000 guarantee GetThirdIndex is executed
433 arr = [];
434 for (var i = 0; i < 2000; ++i) arr[i] = {};
435 arr.sort();
436}
437
438TestSortDoesNotDependOnArrayPrototypePush();
439
440function TestSortDoesNotDependOnArrayPrototypeSort() {
441 var arr = [];
442 for (var i = 0; i < 2000; i++) arr[i] = {};
443 var sortfn = Array.prototype.sort;
444 Array.prototype.sort = function() {
445 fail('Should not call sort');
446 };
447 sortfn.call(arr);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000448 // Restore for the next test
449 Array.prototype.sort = sortfn;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000450}
451
452TestSortDoesNotDependOnArrayPrototypeSort();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000453
454function TestSortToObject() {
455 Number.prototype[0] = 5;
456 Number.prototype[1] = 4;
457 Number.prototype.length = 2;
458 x = new Number(0);
459 assertEquals(0, Number(Array.prototype.sort.call(x)));
460 assertEquals(4, x[0]);
461 assertEquals(5, x[1]);
462 assertArrayEquals(["0", "1"], Object.getOwnPropertyNames(x));
463 // The following would throw if ToObject weren't called.
464 assertEquals(0, Number(Array.prototype.sort.call(0)));
465}
466TestSortToObject();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100467
468function TestSortOnProxy() {
469 {
470 var p = new Proxy([2,1,3], {});
471 assertEquals([1,2,3], p.sort());
472 }
473
474 {
475 function f() { return arguments };
476 var a = f(2,1,3);
477 a.__proto__ = new Proxy({}, {});
478 assertEquals([1,2,3], [...(Array.prototype.sort.apply(a))]);
479 }
480}
481TestSortOnProxy();
Ben Murdoch61f157c2016-09-16 13:49:30 +0100482
483
484// Test special prototypes
485(function testSortSpecialPrototypes() {
486 function test(proto, length, expected) {
487 var result = {
488 length: length,
489 __proto__: proto,
490 };
491 Array.prototype.sort.call(result);
492 assertEquals(expected.length, result.length, "result.length");
493 for (var i = 0; i<expected.length; i++) {
494 assertEquals(expected[i], result[i], "result["+i+"]");
495 }
496 }
497
498 (function fast() {
499 // Fast elements, non-empty
500 test(arguments, 0, []);
501 test(arguments, 1, [2]);
502 test(arguments, 2, [1, 2]);
503 test(arguments, 4, [1, 2, 3, 4]);
504 delete arguments[0]
505 // sort copies down the properties to the receiver, hence result[1]
506 // is read on the arguments through the hole on the receiver.
507 test(arguments, 2, [1, 1]);
508 arguments[0] = undefined;
509 test(arguments, 2, [1, undefined]);
510 })(2, 1, 4, 3);
511
512 (function fastSloppy(a) {
513 // Fast sloppy
514 test(arguments, 0, []);
515 test(arguments, 1, [2]);
516 test(arguments, 2, [1, 2]);
517 delete arguments[0]
518 test(arguments, 2, [1, 1]);
519 arguments[0] = undefined;
520 test(arguments, 2, [1, undefined]);
521 })(2, 1);
522
523 (function fastEmpty() {
524 test(arguments, 0, []);
525 test(arguments, 1, [undefined]);
526 test(arguments, 2, [undefined, undefined]);
527 })();
528
529 (function stringWrapper() {
530 // cannot redefine string wrapper properties
531 assertThrows(() => test(new String('cba'), 3, []), TypeError);
532 })();
533
534 (function typedArrys() {
535 test(new Int32Array(0), 0, []);
536 test(new Int32Array(1), 1, [0]);
537 var array = new Int32Array(3);
538 array[0] = 2;
539 array[1] = 1;
540 array[2] = 3;
541 test(array, 1, [2]);
542 test(array, 2, [1, 2]);
543 test(array, 3, [1, 2, 3]);
544 })()
545
546})();