diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/non262/Array | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
160 files changed, 9768 insertions, 0 deletions
diff --git a/js/src/tests/non262/Array/11.1.4.js b/js/src/tests/non262/Array/11.1.4.js new file mode 100644 index 0000000000..7f39c9305e --- /dev/null +++ b/js/src/tests/non262/Array/11.1.4.js @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 260106; +var summary = 'Elisons in Array literals should not be enumed'; +var actual = ''; +var expect = ''; +var status; +var prop; +var array; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +status = summary + ' ' + inSection(1) + ' [,1] '; +array = [,1]; +actual = ''; +expect = '1'; +for (prop in array) +{ + if (prop != 'length') + { + actual += prop; + } +} +reportCompare(expect, actual, status); + +status = summary + ' ' + inSection(2) + ' [,,1] '; +array = [,,1]; +actual = ''; +expect = '2'; +for (prop in array) +{ + if (prop != 'length') + { + actual += prop; + } +} +reportCompare(expect, actual, status); + +status = summary + ' ' + inSection(3) + ' [1,] '; +array = [1,]; +actual = ''; +expect = '0'; +for (prop in array) +{ + if (prop != 'length') + { + actual += prop; + } +} +reportCompare(expect, actual, status); + +status = summary + ' ' + inSection(4) + ' [1,,] '; +array = [1,,]; +actual = ''; +expect = '0'; +for (prop in array) +{ + if (prop != 'length') + { + actual += prop; + } +} +reportCompare(expect, actual, status); diff --git a/js/src/tests/non262/Array/15.4.4.5-1.js b/js/src/tests/non262/Array/15.4.4.5-1.js new file mode 100644 index 0000000000..a5f9c9883c --- /dev/null +++ b/js/src/tests/non262/Array/15.4.4.5-1.js @@ -0,0 +1,187 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/** + File Name: 15.4.4.5.js + ECMA Section: Array.prototype.sort(comparefn) + Description: + + This test file tests cases in which the compare function is not supplied. + + The elements of this array are sorted. The sort is not necessarily stable. + If comparefn is provided, it should be a function that accepts two arguments + x and y and returns a negative value if x < y, zero if x = y, or a positive + value if x > y. + + 1. Call the [[Get]] method of this object with argument "length". + 2. Call ToUint32(Result(1)). + 1. Perform an implementation-dependent sequence of calls to the + [[Get]] , [[Put]], and [[Delete]] methods of this object and + toSortCompare (described below), where the first argument for each call + to [[Get]], [[Put]] , or [[Delete]] is a nonnegative integer less + than Result(2) and where the arguments for calls to SortCompare are + results of previous calls to the [[Get]] method. After this sequence + is complete, this object must have the following two properties. + (1) There must be some mathematical permutation of the nonnegative + integers less than Result(2), such that for every nonnegative integer + j less than Result(2), if property old[j] existed, then new[(j)] is + exactly the same value as old[j],. but if property old[j] did not exist, + then new[(j)] either does not exist or exists with value undefined. + (2) If comparefn is not supplied or is a consistent comparison + function for the elements of this array, then for all nonnegative + integers j and k, each less than Result(2), if old[j] compares less + than old[k] (see SortCompare below), then (j) < (k). Here we use the + notation old[j] to refer to the hypothetical result of calling the [ + [Get]] method of this object with argument j before this step is + executed, and the notation new[j] to refer to the hypothetical result + of calling the [[Get]] method of this object with argument j after this + step has been completely executed. A function is a consistent + comparison function for a set of values if (a) for any two of those + values (possibly the same value) considered as an ordered pair, it + always returns the same value when given that pair of values as its + two arguments, and the result of applying ToNumber to this value is + not NaN; (b) when considered as a relation, where the pair (x, y) is + considered to be in the relation if and only if applying the function + to x and y and then applying ToNumber to the result produces a + negative value, this relation is a partial order; and (c) when + considered as a different relation, where the pair (x, y) is considered + to be in the relation if and only if applying the function to x and y + and then applying ToNumber to the result produces a zero value (of either + sign), this relation is an equivalence relation. In this context, the + phrase "x compares less than y" means applying Result(2) to x and y and + then applying ToNumber to the result produces a negative value. + 3.Return this object. + + When the SortCompare operator is called with two arguments x and y, the following steps are taken: + 1.If x and y are both undefined, return +0. + 2.If x is undefined, return 1. + 3.If y is undefined, return 1. + 4.If the argument comparefn was not provided in the call to sort, go to step 7. + 5.Call comparefn with arguments x and y. + 6.Return Result(5). + 7.Call ToString(x). + 8.Call ToString(y). + 9.If Result(7) < Result(8), return 1. + 10.If Result(7) > Result(8), return 1. + 11.Return +0. + + Note that, because undefined always compared greater than any other value, undefined and nonexistent + property values always sort to the end of the result. It is implementation-dependent whether or not such + properties will exist or not at the end of the array when the sort is concluded. + + Note that the sort function is intentionally generic; it does not require that its this value be an Array object. + Therefore it can be transferred to other kinds of objects for use as a method. Whether the sort function can be + applied successfully to a host object is implementation dependent . + + Author: christine@netscape.com + Date: 12 november 1997 +*/ + + +var SECTION = "15.4.4.5-1"; +var TITLE = "Array.prototype.sort(comparefn)"; + +writeHeaderToLog( SECTION + " "+ TITLE); +var S = new Array(); +var item = 0; + +// array is empty. +S[item++] = "var A = new Array()"; + +// array contains one item +S[item++] = "var A = new Array( true )"; + +// length of array is 2 +S[item++] = "var A = new Array( true, false, new Boolean(true), new Boolean(false), 'true', 'false' )"; + +S[item++] = "var A = new Array(); A[3] = 'undefined'; A[6] = null; A[8] = 'null'; A[0] = void 0"; + +S[item] = "var A = new Array( "; + +var limit = 0x0061; +for ( var i = 0x007A; i >= limit; i-- ) { + S[item] += "\'"+ String.fromCharCode(i) +"\'" ; + if ( i > limit ) { + S[item] += ","; + } +} + +S[item] += ")"; + +item++; + +for ( var i = 0; i < S.length; i++ ) { + CheckItems( S[i] ); +} + +test(); + +function CheckItems( S ) { + eval( S ); + var E = Sort( A ); + + new TestCase( + S +"; A.sort(); A.length", + E.length, + eval( S + "; A.sort(); A.length") ); + + for ( var i = 0; i < E.length; i++ ) { + new TestCase( + "A["+i+ "].toString()", + E[i] +"", + A[i] +""); + + if ( A[i] == void 0 && typeof A[i] == "undefined" ) { + new TestCase( + "typeof A["+i+ "]", + typeof E[i], + typeof A[i] ); + } + } +} +function Object_1( value ) { + this.array = value.split(","); + this.length = this.array.length; + for ( var i = 0; i < this.length; i++ ) { + this[i] = eval(this.array[i]); + } + this.sort = Array.prototype.sort; + this.getClass = Object.prototype.toString; +} +function Sort( a ) { + for ( i = 0; i < a.length; i++ ) { + for ( j = i+1; j < a.length; j++ ) { + var lo = a[i]; + var hi = a[j]; + var c = Compare( lo, hi ); + if ( c == 1 ) { + a[i] = hi; + a[j] = lo; + } + } + } + return a; +} +function Compare( x, y ) { + if ( x == void 0 && y == void 0 && typeof x == "undefined" && typeof y == "undefined" ) { + return +0; + } + if ( x == void 0 && typeof x == "undefined" ) { + return 1; + } + if ( y == void 0 && typeof y == "undefined" ) { + return -1; + } + x = String(x); + y = String(y); + if ( x < y ) { + return -1; + } + if ( x > y ) { + return 1; + } + return 0; +} diff --git a/js/src/tests/non262/Array/15.4.4.5-2.js b/js/src/tests/non262/Array/15.4.4.5-2.js new file mode 100644 index 0000000000..5198297f5f --- /dev/null +++ b/js/src/tests/non262/Array/15.4.4.5-2.js @@ -0,0 +1,189 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/** + File Name: 15.4.4.5-2.js + ECMA Section: Array.prototype.sort(comparefn) + Description: + + This test file tests cases in which the compare function is supplied. + In this cases, the sort creates a reverse sort. + + The elements of this array are sorted. The sort is not necessarily stable. + If comparefn is provided, it should be a function that accepts two arguments + x and y and returns a negative value if x < y, zero if x = y, or a positive + value if x > y. + + 1. Call the [[Get]] method of this object with argument "length". + 2. Call ToUint32(Result(1)). + 1. Perform an implementation-dependent sequence of calls to the + [[Get]] , [[Put]], and [[Delete]] methods of this object and + toSortCompare (described below), where the first argument for each call + to [[Get]], [[Put]] , or [[Delete]] is a nonnegative integer less + than Result(2) and where the arguments for calls to SortCompare are + results of previous calls to the [[Get]] method. After this sequence + is complete, this object must have the following two properties. + (1) There must be some mathematical permutation of the nonnegative + integers less than Result(2), such that for every nonnegative integer + j less than Result(2), if property old[j] existed, then new[(j)] is + exactly the same value as old[j],. but if property old[j] did not exist, + then new[(j)] either does not exist or exists with value undefined. + (2) If comparefn is not supplied or is a consistent comparison + function for the elements of this array, then for all nonnegative + integers j and k, each less than Result(2), if old[j] compares less + than old[k] (see SortCompare below), then (j) < (k). Here we use the + notation old[j] to refer to the hypothetical result of calling the [ + [Get]] method of this object with argument j before this step is + executed, and the notation new[j] to refer to the hypothetical result + of calling the [[Get]] method of this object with argument j after this + step has been completely executed. A function is a consistent + comparison function for a set of values if (a) for any two of those + values (possibly the same value) considered as an ordered pair, it + always returns the same value when given that pair of values as its + two arguments, and the result of applying ToNumber to this value is + not NaN; (b) when considered as a relation, where the pair (x, y) is + considered to be in the relation if and only if applying the function + to x and y and then applying ToNumber to the result produces a + negative value, this relation is a partial order; and (c) when + considered as a different relation, where the pair (x, y) is considered + to be in the relation if and only if applying the function to x and y + and then applying ToNumber to the result produces a zero value (of either + sign), this relation is an equivalence relation. In this context, the + phrase "x compares less than y" means applying Result(2) to x and y and + then applying ToNumber to the result produces a negative value. + 3.Return this object. + + When the SortCompare operator is called with two arguments x and y, the following steps are taken: + 1.If x and y are both undefined, return +0. + 2.If x is undefined, return 1. + 3.If y is undefined, return 1. + 4.If the argument comparefn was not provided in the call to sort, go to step 7. + 5.Call comparefn with arguments x and y. + 6.Return Result(5). + 7.Call ToString(x). + 8.Call ToString(y). + 9.If Result(7) < Result(8), return 1. + 10.If Result(7) > Result(8), return 1. + 11.Return +0. + + Note that, because undefined always compared greater than any other value, undefined and nonexistent + property values always sort to the end of the result. It is implementation-dependent whether or not such + properties will exist or not at the end of the array when the sort is concluded. + + Note that the sort function is intentionally generic; it does not require that its this value be an Array object. + Therefore it can be transferred to other kinds of objects for use as a method. Whether the sort function can be + applied successfully to a host object is implementation dependent . + + Author: christine@netscape.com + Date: 12 november 1997 +*/ + + +var SECTION = "15.4.4.5-2"; +var TITLE = "Array.prototype.sort(comparefn)"; + +writeHeaderToLog( SECTION + " "+ TITLE); + + +var S = new Array(); +var item = 0; + +// array is empty. +S[item++] = "var A = new Array()"; + +// array contains one item +S[item++] = "var A = new Array( true )"; + +// length of array is 2 +S[item++] = "var A = new Array( true, false, new Boolean(true), new Boolean(false), 'true', 'false' )"; + +S[item++] = "var A = new Array(); A[3] = 'undefined'; A[6] = null; A[8] = 'null'; A[0] = void 0"; + +S[item] = "var A = new Array( "; + +var limit = 0x0061; +for ( var i = 0x007A; i >= limit; i-- ) { + S[item] += "\'"+ String.fromCharCode(i) +"\'" ; + if ( i > limit ) { + S[item] += ","; + } +} + +S[item] += ")"; + +for ( var i = 0; i < S.length; i++ ) { + CheckItems( S[i] ); +} + +test(); + +function CheckItems( S ) { + eval( S ); + var E = Sort( A ); + + new TestCase( + S +"; A.sort(Compare); A.length", + E.length, + eval( S + "; A.sort(Compare); A.length") ); + + for ( var i = 0; i < E.length; i++ ) { + new TestCase( + "A["+i+ "].toString()", + E[i] +"", + A[i] +""); + + if ( A[i] == void 0 && typeof A[i] == "undefined" ) { + new TestCase( + "typeof A["+i+ "]", + typeof E[i], + typeof A[i] ); + } + } +} +function Object_1( value ) { + this.array = value.split(","); + this.length = this.array.length; + for ( var i = 0; i < this.length; i++ ) { + this[i] = eval(this.array[i]); + } + this.sort = Array.prototype.sort; + this.getClass = Object.prototype.toString; +} +function Sort( a ) { + var r1 = a.length; + for ( i = 0; i < a.length; i++ ) { + for ( j = i+1; j < a.length; j++ ) { + var lo = a[i]; + var hi = a[j]; + var c = Compare( lo, hi ); + if ( c == 1 ) { + a[i] = hi; + a[j] = lo; + } + } + } + return a; +} +function Compare( x, y ) { + if ( x == void 0 && y == void 0 && typeof x == "undefined" && typeof y == "undefined" ) { + return +0; + } + if ( x == void 0 && typeof x == "undefined" ) { + return 1; + } + if ( y == void 0 && typeof y == "undefined" ) { + return -1; + } + x = String(x); + y = String(y); + if ( x < y ) { + return 1; + } + if ( x > y ) { + return -1; + } + return 0; +} diff --git a/js/src/tests/non262/Array/15.4.4.5-3.js b/js/src/tests/non262/Array/15.4.4.5-3.js new file mode 100644 index 0000000000..523a8a2a92 --- /dev/null +++ b/js/src/tests/non262/Array/15.4.4.5-3.js @@ -0,0 +1,145 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/** + File Name: 15.4.4.5-3.js + ECMA Section: Array.prototype.sort(comparefn) + Description: + + This is a regression test for + http://scopus/bugsplat/show_bug.cgi?id=117144 + + Verify that sort is successfull, even if the sort compare function returns + a very large negative or positive value. + + Author: christine@netscape.com + Date: 12 november 1997 +*/ + + +var SECTION = "15.4.4.5-3"; +var TITLE = "Array.prototype.sort(comparefn)"; + +writeHeaderToLog( SECTION + " "+ TITLE); + +var array = new Array(); + +var TIME_2000 = 946684800000; +var TIME_1900 = -2208988800000; + +array[array.length] = new Date( TIME_2000 * Math.PI ); +array[array.length] = new Date( TIME_2000 * 10 ); +array[array.length] = new Date( TIME_1900 + TIME_1900 ); +array[array.length] = new Date(0); +array[array.length] = new Date( TIME_2000 ); +array[array.length] = new Date( TIME_1900 + TIME_1900 +TIME_1900 ); +array[array.length] = new Date( TIME_1900 * Math.PI ); +array[array.length] = new Date( TIME_1900 * 10 ); +array[array.length] = new Date( TIME_1900 ); +array[array.length] = new Date( TIME_2000 + TIME_2000 ); +array[array.length] = new Date( 1899, 0, 1 ); +array[array.length] = new Date( 2000, 1, 29 ); +array[array.length] = new Date( 2000, 0, 1 ); +array[array.length] = new Date( 1999, 11, 31 ); + +var testarr1 = new Array(); +clone( array, testarr1 ); +testarr1.sort( comparefn1 ); + +var testarr2 = new Array(); +clone( array, testarr2 ); +testarr2.sort( comparefn2 ); + +testarr3 = new Array(); +clone( array, testarr3 ); +testarr3.sort( comparefn3 ); + +// when there's no sort function, sort sorts by the toString value of Date. + +var testarr4 = new Array(); +clone( array, testarr4 ); +testarr4.sort(); + +var realarr = new Array(); +clone( array, realarr ); +realarr.sort( realsort ); + +var stringarr = new Array(); +clone( array, stringarr ); +stringarr.sort( stringsort ); + +for ( var i = 0; i < array.length; i++) { + new TestCase( + "testarr1["+i+"]", + realarr[i], + testarr1[i] ); +} + +for ( var i=0; i < array.length; i++) { + new TestCase( + "testarr2["+i+"]", + realarr[i], + testarr2[i] ); +} + +for ( var i=0; i < array.length; i++) { + new TestCase( + "testarr3["+i+"]", + realarr[i], + testarr3[i] ); +} + +for ( var i=0; i < array.length; i++) { + new TestCase( + "testarr4["+i+"]", + stringarr[i].toString(), + testarr4[i].toString() ); +} + +test(); + +function comparefn1( x, y ) { + return x - y; +} +function comparefn2( x, y ) { + return x.valueOf() - y.valueOf(); +} +function realsort( x, y ) { + return ( x.valueOf() == y.valueOf() ? 0 : ( x.valueOf() > y.valueOf() ? 1 : -1 ) ); +} +function comparefn3( x, y ) { + return ( x == y ? 0 : ( x > y ? 1: -1 ) ); +} +function clone( source, target ) { + for (i = 0; i < source.length; i++ ) { + target[i] = source[i]; + } +} +function stringsort( x, y ) { + for ( var i = 0; i < x.toString().length; i++ ) { + var d = (x.toString()).charCodeAt(i) - (y.toString()).charCodeAt(i); + if ( d > 0 ) { + return 1; + } else { + if ( d < 0 ) { + return -1; + } else { + continue; + } + } + + var d = x.length - y.length; + + if ( d > 0 ) { + return 1; + } else { + if ( d < 0 ) { + return -1; + } + } + } + return 0; +} diff --git a/js/src/tests/non262/Array/array-001.js b/js/src/tests/non262/Array/array-001.js new file mode 100644 index 0000000000..4ddf15e604 --- /dev/null +++ b/js/src/tests/non262/Array/array-001.js @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Date: 24 September 2001 + * + * SUMMARY: Truncating arrays that have decimal property names. + * From correspondence with Igor Bukanov <igor@icesoft.no>: + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = '(none)'; +var summary = 'Truncating arrays that have decimal property names'; +var BIG_INDEX = 4294967290; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +var arr = Array(BIG_INDEX); +arr[BIG_INDEX - 1] = 'a'; +arr[BIG_INDEX - 10000] = 'b'; +arr[BIG_INDEX - 0.5] = 'c'; // not an array index - but a valid property name +// Truncate the array - +arr.length = BIG_INDEX - 5000; + + +// Enumerate its properties with for..in +var s = ''; +for (var i in arr) +{ + s += arr[i]; +} + + +/* + * We expect s == 'cb' or 'bc' (EcmaScript does not fix the order). + * Note 'c' is included: for..in includes ALL enumerable properties, + * not just array-index properties. The bug was: Rhino gave s == ''. + */ +status = inSection(1); +actual = sortThis(s); +expect = 'bc'; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function sortThis(str) +{ + var chars = str.split(''); + chars = chars.sort(); + return chars.join(''); +} + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/array-length-set-during-for-in.js b/js/src/tests/non262/Array/array-length-set-during-for-in.js new file mode 100644 index 0000000000..83d476f7f9 --- /dev/null +++ b/js/src/tests/non262/Array/array-length-set-during-for-in.js @@ -0,0 +1,10 @@ +var a = [0, 1]; +var iterations = 0; +for (var k in a) { + iterations++; + a.length = 1; +} +assertEq(iterations, 1); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/array-length-set-on-nonarray.js b/js/src/tests/non262/Array/array-length-set-on-nonarray.js new file mode 100644 index 0000000000..bc47c74777 --- /dev/null +++ b/js/src/tests/non262/Array/array-length-set-on-nonarray.js @@ -0,0 +1,26 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 548671; +var summary = + "Don't use a shared-permanent inherited property to implement " + + "[].length or (function(){}).length"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var a = []; +a.p = 1; +var x = Object.create(a); +assertEq(x.length, 0); +assertEq(x.p, 1); +assertEq(a.length, 0); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/at.js b/js/src/tests/non262/Array/at.js new file mode 100644 index 0000000000..e3227a3ec2 --- /dev/null +++ b/js/src/tests/non262/Array/at.js @@ -0,0 +1,39 @@ +function basic() { + assertEq([0].at(0), 0); + assertEq([0].at(-1), 0); + + assertEq([].at(0), undefined); + assertEq([].at(-1), undefined); + assertEq([].at(1), undefined); + + assertEq([0, 1].at(0), 0); + assertEq([0, 1].at(1), 1); + assertEq([0, 1].at(-2), 0); + assertEq([0, 1].at(-1), 1); + + assertEq([0, 1].at(2), undefined); + assertEq([0, 1].at(-3), undefined); + assertEq([0, 1].at(-4), undefined); + assertEq([0, 1].at(Infinity), undefined); + assertEq([0, 1].at(-Infinity), undefined); + assertEq([0, 1].at(NaN), 0); // ToInteger(NaN) = 0 +} + +function obj() { + var o = {length: 0, [0]: "a", at: Array.prototype.at}; + + assertEq(o.at(0), undefined); + assertEq(o.at(-1), undefined); + + o.length = 1; + assertEq(o.at(0), "a"); + assertEq(o.at(-1), "a"); + assertEq(o.at(1), undefined); + assertEq(o.at(-2), undefined); +} + +basic(); +obj(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/browser.js b/js/src/tests/non262/Array/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Array/browser.js diff --git a/js/src/tests/non262/Array/change-array-by-copy-cross-compartment-create.js b/js/src/tests/non262/Array/change-array-by-copy-cross-compartment-create.js new file mode 100644 index 0000000000..bd6e6d0a9f --- /dev/null +++ b/js/src/tests/non262/Array/change-array-by-copy-cross-compartment-create.js @@ -0,0 +1,26 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.with) + +function test(otherGlobal) { + let arrays = [ + ["with", otherGlobal.Array.prototype.with.call([1,2,3], 1, 3)], + ["toSpliced", otherGlobal.Array.prototype.toSpliced.call([1, 2, 3], 0, 1, 4, 5)], + ["toReversed", otherGlobal.Array.prototype.toReversed.call([1, 2, 3])], + ["toSorted", otherGlobal.Array.prototype.toSorted.call([1, 2, 3], (x, y) => y > x)] + ] + + // Test that calling each method in a different compartment returns an array, and that + // the returned array's prototype matches the other compartment's Array prototype, + // not this one. + for (const [name, arr] of arrays) { + assertEq(arr instanceof Array, false, name + " returned an instance of Array"); + assertEq(arr instanceof otherGlobal.Array, true, name + " did not return an instance of new global's Array"); + assertEq(Object.getPrototypeOf(arr) !== Object.getPrototypeOf([1, 2, 3]), true, + name + " returned an object with a prototype from the wrong realm"); + } +} + +test(newGlobal()); +test(newGlobal({newCompartment: true})); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/change-array-by-copy-errors-from-correct-realm.js b/js/src/tests/non262/Array/change-array-by-copy-errors-from-correct-realm.js new file mode 100644 index 0000000000..4e99cbe0ff --- /dev/null +++ b/js/src/tests/non262/Array/change-array-by-copy-errors-from-correct-realm.js @@ -0,0 +1,77 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.with) + +function test(otherGlobal) { + assertEq(TypeError !== otherGlobal.TypeError, true); + assertEq(Object.getPrototypeOf(TypeError) !== Object.getPrototypeOf(otherGlobal.TypeError), true); + assertEq(RangeError !== otherGlobal.RangeError, true); + assertEq(Object.getPrototypeOf(RangeError) !== Object.getPrototypeOf(otherGlobal.RangeError), true); + + + var arrayLike = { + get "0"() { + throw new Error("Get 0"); + }, + get "4294967295" () { // 2 ** 32 - 1 + throw new Error("Get 2147483648"); + }, + get "4294967296" () { // 2 ** 32 + throw new Error("Get 2147483648"); + }, + length: 2 ** 32 + }; + + let gToSorted = otherGlobal.Array.prototype.toSorted; + let gToSpliced = otherGlobal.Array.prototype.toSpliced; + let gToReversed = otherGlobal.Array.prototype.toReversed; + let gWith = otherGlobal.Array.prototype.with; + + let typeErrorCalls = [ + ["toSorted - bad comparator", () => gToSorted.call([], 5)], + ["toSorted - this is null", () => gToSorted.call(null)], + ["toSpliced - array too long", () => { + var oldLen = arrayLike.length; + arrayLike.length = 2**53 - 1; + gToSpliced.call(arrayLike, 0, 0, 1); + arrayLike.length = oldLen; + }] + ] + + let rangeErrorCalls = [ + ["toSorted - array too long", () => gToSorted.call(arrayLike)], + ["toReversed - array too long", () => gToReversed.call(arrayLike)], + ["toSpliced - adding elements would exceed max array length", () => gToSpliced.call(arrayLike, 0, 0)], + ["with - index out of range", () => gWith.call([0, 1, 2], 3, 7)], + ["with - negative index", () => gWith.call([0, 1, 2], -4, 7)], + ["with - array too long", () => gWith.call(arrayLike, 0, 0)] + ] + + // For each erroneous case, make sure the error comes from + // the other realm (not this realm) + for (const [message, f] of typeErrorCalls) { + try { + f(); + } catch (exc) { + assertEq(exc instanceof TypeError, false, message + " threw TypeError from wrong realm"); + assertEq(exc instanceof otherGlobal.TypeError, true, message + " didn't throw TypeError from other realm"); + assertEq(Object.getPrototypeOf(exc) !== Object.getPrototypeOf(TypeError), true, + message + " TypeError has wrong prototype"); + } + } + + for (const [message, f] of rangeErrorCalls) { + try { + f(); + } catch (exc) { + assertEq(exc instanceof RangeError, false, message + " threw RangeError from wrong realm"); + assertEq(exc instanceof otherGlobal.RangeError, true, message + " didn't throw RangeError from other realm"); + assertEq(Object.getPrototypeOf(exc) !== Object.getPrototypeOf(RangeError), true, + message + " TypeError has wrong prototype"); + } + } +} + +test(newGlobal()); +test(newGlobal({newCompartment: true})); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/concat-proxy.js b/js/src/tests/non262/Array/concat-proxy.js new file mode 100644 index 0000000000..ab733e2f10 --- /dev/null +++ b/js/src/tests/non262/Array/concat-proxy.js @@ -0,0 +1,25 @@ +var BUGNUMBER = 1287520; +var summary = 'Array.prototype.concat should check HasProperty everytime for non-dense array'; + +print(BUGNUMBER + ": " + summary); + +var a = [1, 2, 3]; +a.constructor = { + [Symbol.species]: function(...args) { + var p = new Proxy(new Array(...args), { + defineProperty(target, propertyKey, receiver) { + if (propertyKey === "0") delete a[1]; + return Reflect.defineProperty(target, propertyKey, receiver); + } + }); + return p; + } +}; + +var p = a.concat(); +assertEq(0 in p, true); +assertEq(1 in p, false); +assertEq(2 in p, true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/concat-spreadable-basic.js b/js/src/tests/non262/Array/concat-spreadable-basic.js new file mode 100644 index 0000000000..c13f8f5cd3 --- /dev/null +++ b/js/src/tests/non262/Array/concat-spreadable-basic.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ +"use strict"; + +const x = Object.freeze([1, 2, 3]); + +let fakeArray = { + [Symbol.isConcatSpreadable]: true, + length: 2, + 0: "hello", + 1: "world" +} +assertDeepEq(x.concat(fakeArray), [1, 2, 3, "hello", "world"]); +assertDeepEq(x.concat(fakeArray, fakeArray), [1, 2, 3, "hello", "world", "hello", "world"]); + +for (let truthy of [true, 3.41, "abc", Symbol(), {}]) { + let obj = {[Symbol.isConcatSpreadable]: truthy, length: 1, 0: "hey"} + assertDeepEq(x.concat(obj), [1, 2, 3, "hey"]); +} + +for (let notTruthy of [null, undefined, false, 0, NaN, ""]) { + let obj = {[Symbol.isConcatSpreadable]: notTruthy, length: 1, 0: "hey"} + assertDeepEq(x.concat(obj), [1, 2, 3, obj]); +} + +let array = [5, 4]; +assertDeepEq(x.concat(array), [1, 2, 3, 5, 4]); + +// Can make arrays non-spreadable +array[Symbol.isConcatSpreadable] = false; +assertDeepEq(x.concat(array), [1, 2, 3, [5, 4]]); + +// Explicitly spreadable +array[Symbol.isConcatSpreadable] = true; +assertDeepEq(x.concat(array), [1, 2, 3, 5, 4]); + +reportCompare(true, true); diff --git a/js/src/tests/non262/Array/concat-spreadable-primitive.js b/js/src/tests/non262/Array/concat-spreadable-primitive.js new file mode 100644 index 0000000000..d2264bfa2a --- /dev/null +++ b/js/src/tests/non262/Array/concat-spreadable-primitive.js @@ -0,0 +1,34 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ +"use strict"; + +// Primitive values should never be tried to spread +let primitives = [ + 10, + false, + Symbol() + // Can't change String.prototype.length +]; + +for (let value of primitives) { + let prototype = Object.getPrototypeOf(value); + prototype[Symbol.isConcatSpreadable] = true; + + Object.defineProperty(prototype, "length", { + configurable: true, + get() { + // Should never invoke length getter + assertEq(true, false); + }, + }); + + let x = [1, 2].concat(value); + assertDeepEq(x, [1, 2, value]); + + delete prototype[Symbol.isConcatSpreadable]; + delete prototype.length; + + prototype.length; +} + +reportCompare(true, true); diff --git a/js/src/tests/non262/Array/fill.js b/js/src/tests/non262/Array/fill.js new file mode 100644 index 0000000000..70f1e0b522 --- /dev/null +++ b/js/src/tests/non262/Array/fill.js @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 911147; +var summary = 'Array.prototype.fill'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +assertEq(typeof [].fill, 'function'); +assertEq([].fill.length, 1); + +// Default values for arguments and absolute values for negative start and end +// arguments are resolved correctly. +assertDeepEq([].fill(1), []); +assertDeepEq([1,1,1].fill(2), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 1), [1,2,2]); +assertDeepEq([1,1,1].fill(2, 1, 2), [1,2,1]); +assertDeepEq([1,1,1].fill(2, -2), [1,2,2]); +assertDeepEq([1,1,1].fill(2, -2, -1), [1,2,1]); +assertDeepEq([1,1,1].fill(2, undefined), [2,2,2]); +assertDeepEq([1,1,1].fill(2, undefined, undefined), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 1, undefined), [1,2,2]); +assertDeepEq([1,1,1].fill(2, undefined, 1), [2,1,1]); +assertDeepEq([1,1,1].fill(2, 2, 1), [1,1,1]); +assertDeepEq([1,1,1].fill(2, -1, 1), [1,1,1]); +assertDeepEq([1,1,1].fill(2, -2, 1), [1,1,1]); +assertDeepEq([1,1,1].fill(2, 1, -2), [1,1,1]); +assertDeepEq([1,1,1].fill(2, 0.1), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 0.9), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 1.1), [1,2,2]); +assertDeepEq([1,1,1].fill(2, 0.1, 0.9), [1,1,1]); +assertDeepEq([1,1,1].fill(2, 0.1, 1.9), [2,1,1]); +assertDeepEq([1,1,1].fill(2, 0.1, 1.9), [2,1,1]); +assertDeepEq([1,1,1].fill(2, -0), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 0, -0), [1,1,1]); +assertDeepEq([1,1,1].fill(2, NaN), [2,2,2]); +assertDeepEq([1,1,1].fill(2, 0, NaN), [1,1,1]); +assertDeepEq([1,1,1].fill(2, false), [2,2,2]); +assertDeepEq([1,1,1].fill(2, true), [1,2,2]); +assertDeepEq([1,1,1].fill(2, "0"), [2,2,2]); +assertDeepEq([1,1,1].fill(2, "1"), [1,2,2]); +assertDeepEq([1,1,1].fill(2, "-2"), [1,2,2]); +assertDeepEq([1,1,1].fill(2, "-2", "-1"), [1,2,1]); +assertDeepEq([1,1,1].fill(2, {valueOf: ()=>1}), [1,2,2]); +assertDeepEq([1,1,1].fill(2, 0, {valueOf: ()=>1}), [2,1,1]); + +// fill works generically for objects, too. +assertDeepEq([].fill.call({length: 2}, 2), {0: 2, 1: 2, length: 2}); + +var setterCalled = false; +var objWithSetter = {set "0"(val) { setterCalled = true}, length: 1}; +[].fill.call(objWithSetter, 2); +assertEq(setterCalled, true); + +var setHandlerCallCount = 0; +var proxy = new Proxy({length: 3}, {set(t, i, v, r) { setHandlerCallCount++; return true; }}); +[].fill.call(proxy, 2); +assertEq(setHandlerCallCount, 3); + +var valueOfCallCount = 0; +var typedArray = new Uint8ClampedArray(3); +[].fill.call(typedArray, {valueOf: function() {valueOfCallCount++; return 2000;}}); +assertEq(valueOfCallCount, 3); +assertEq(typedArray[0], 0xff); + +// All remaining cases should throw. +var objWithGetterOnly = {get "0"() {return 1;}, length: 1}; + +var objWithReadOnlyProp = {length: 1}; +Object.defineProperty(objWithReadOnlyProp, 0, {value: 1, writable: false}); + +var objWithNonconfigurableProp = {length: 1}; +Object.defineProperty(objWithNonconfigurableProp, 0, {value: 1, configurable: false}); + +var frozenObj = {length: 1}; +Object.freeze(frozenObj); + +var frozenArray = [1, 1, 1]; +Object.freeze(frozenArray); + +assertThrowsInstanceOf(() => [].fill.call(objWithGetterOnly, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(objWithReadOnlyProp, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(objWithNonconfigurableProp, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(frozenObj, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(frozenArray, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call("111", 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(null, 2), TypeError); +assertThrowsInstanceOf(() => [].fill.call(undefined, 2), TypeError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/filter.js b/js/src/tests/non262/Array/filter.js new file mode 100644 index 0000000000..6cb9290147 --- /dev/null +++ b/js/src/tests/non262/Array/filter.js @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = "364603"; +var summary = "The value placed in a filtered array for an element is the " + + " element's value before the callback is run, not after"; +var actual, expect; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +var failed = false; + +function mutate(val, index, arr) +{ + arr[index] = "mutated"; + return true; +} + +function assertEqual(v1, v2, msg) +{ + if (v1 !== v2) + throw msg; +} + +try +{ + var a = [1, 2]; + var m = a.filter(mutate); + + assertEqual(a[0], "mutated", "Array a not mutated!"); + assertEqual(a[1], "mutated", "Array a not mutated!"); + + assertEqual(m[0], 1, "Filtered value is value before callback is run"); + assertEqual(m[1], 2, "Filtered value is value before callback is run"); +} +catch (e) +{ + failed = e; +} + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/findLast_findLastIndex.js b/js/src/tests/non262/Array/findLast_findLastIndex.js new file mode 100644 index 0000000000..00f5640308 --- /dev/null +++ b/js/src/tests/non262/Array/findLast_findLastIndex.js @@ -0,0 +1,285 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1704385; +var summary = 'Array.prototype.findLast and Array.prototype.findLastIndex'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function isString(v, index, array) +{ + assertEq(array[index], v); + return typeof v == 'string'; +} + +function dumpError(e) +{ + var s = e.name + ': ' + e.message + + ' File: ' + e.fileName + + ', Line: ' + e.lineNumber + + ', Stack: ' + e.stack; + return s; +} + +var expect; +var actual; +var obj; + +var strings = ['hello', 'Array', 'WORLD']; +var mixed = [0, '1', 2]; +var sparsestrings = new Array(); +sparsestrings[2] = 'sparse'; +var arraylike = {0:0, 1:'string', 2:2, length:3}; +// array for which JSObject::isIndexed() holds. +var indexedArray = []; +Object.defineProperty(indexedArray, 42, { get: function() { return 42; } }); +Object.defineProperty(indexedArray, 142, { get: function() { return 'string'; } }); + +// findLast and findLastIndex have 1 required argument + +expect = 1; +actual = Array.prototype.findLast.length; +reportCompare(expect, actual, 'Array.prototype.findLast.length == 1'); +actual = Array.prototype.findLastIndex.length; +reportCompare(expect, actual, 'Array.prototype.findLastIndex.length == 1'); + +// throw TypeError if no predicate specified +expect = 'TypeError'; +try +{ + strings.findLast(); + actual = 'no error'; +} +catch(e) +{ + actual = e.name; +} +reportCompare(expect, actual, 'Array.findLast(undefined) throws TypeError'); +try +{ + strings.findLastIndex(); + actual = 'no error'; +} +catch(e) +{ + actual = e.name; +} +reportCompare(expect, actual, 'Array.findLastIndex(undefined) throws TypeError'); + +// Length gets treated as integer, not uint32 +obj = { length: -4294967295, 0: 42 }; +expected = undefined; +actual = Array.prototype.findLast.call(obj, () => true); +reportCompare(expected, actual, 'findLast correctly treats "length" as an integer'); +expected = -1 +actual = Array.prototype.findLastIndex.call(obj, () => true); +reportCompare(expected, actual, 'findLastIndex correctly treats "length" as an integer'); + +// test findLast and findLastIndex results +try +{ + expect = 'WORLD'; + actual = strings.findLast(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'strings: findLast finds last string element'); + +try +{ + expect = 2; + actual = strings.findLastIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'strings: findLastIndex finds last string element'); + +try +{ + expect = '1'; + actual = mixed.findLast(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'mixed: findLast finds last string element'); + +try +{ + expect = 1; + actual = mixed.findLastIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'mixed: findLastIndex finds last string element'); + +try +{ + expect = 'sparse'; + actual = sparsestrings.findLast(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'sparsestrings: findLast finds last string element'); + +try +{ + expect = 2; + actual = sparsestrings.findLastIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'sparsestrings: findLastIndex finds first string element'); + +try +{ + expect = 'string'; + actual = [].findLast.call(arraylike, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike: findLast finds last string element'); + +try +{ + expect = 1; + actual = [].findLastIndex.call(arraylike, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike: findLastIndex finds last string element'); + +try +{ + expect = 1; + actual = 0; + Array.prototype.findLast.call({get 0(){ actual++ }, length: 1}, ()=>true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike with getter: getter only called once'); + +try +{ + expect = 'string'; + actual = [].findLast.call(indexedArray, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'indexedArray: findLast finds last string element'); + +try +{ + expect = 142; + actual = [].findLastIndex.call(indexedArray, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'indexedArray: findLastIndex finds last string element'); + +// Bug 1058394 - Array#findLast and Array#findLastIndex no longer skip holes too. +var sparseArray = [1,,]; +var sparseArrayWithInheritedDataProperty = Object.setPrototypeOf([1,,,], { + __proto__: [].__proto__, + 2 : 0 +}); +var sparseArrayWithInheritedAccessorProperty = Object.setPrototypeOf([1,,,], { + __proto__: [].__proto__, + get 2(){ + throw "get 2"; + } +}); + +try +{ + expect = undefined; + actual = sparseArray.findLast(() => true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Don't skip holes in Array#findLast."); + +try +{ + expect = 1; + actual = sparseArray.findLastIndex(() => true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Don't skip holes in Array#findLastIndex."); + +try +{ + expect = 0; + actual = sparseArrayWithInheritedDataProperty.findLast(v => v === 0); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Array#findLast can find inherited data property."); + +try +{ + expect = 2; + actual = sparseArrayWithInheritedDataProperty.findLastIndex(v => v === 0); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Array#findLastIndex can find inherited data property."); + +try +{ + expect = "get 2"; + actual = sparseArrayWithInheritedAccessorProperty.findLast(() => true); +} +catch(e) +{ + actual = e; +} +reportCompare(expect, actual, "Array#findLast can find inherited accessor property."); + +try +{ + expect = "get 2"; + actual = sparseArrayWithInheritedAccessorProperty.findLastIndex(() => true); +} +catch(e) +{ + actual = e; +} +reportCompare(expect, actual, "Array#findLastIndex can find inherited accessor property."); diff --git a/js/src/tests/non262/Array/find_findindex.js b/js/src/tests/non262/Array/find_findindex.js new file mode 100644 index 0000000000..6e32ea6b05 --- /dev/null +++ b/js/src/tests/non262/Array/find_findindex.js @@ -0,0 +1,285 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 885553; +var summary = 'Array.prototype.find and Array.prototype.findIndex'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function isString(v, index, array) +{ + assertEq(array[index], v); + return typeof v == 'string'; +} + +function dumpError(e) +{ + var s = e.name + ': ' + e.message + + ' File: ' + e.fileName + + ', Line: ' + e.lineNumber + + ', Stack: ' + e.stack; + return s; +} + +var expect; +var actual; +var obj; + +var strings = ['hello', 'Array', 'WORLD']; +var mixed = [0, '1', 2]; +var sparsestrings = new Array(); +sparsestrings[2] = 'sparse'; +var arraylike = {0:0, 1:'string', 2:2, length:3}; +// array for which JSObject::isIndexed() holds. +var indexedArray = []; +Object.defineProperty(indexedArray, 42, { get: function() { return 42; } }); +Object.defineProperty(indexedArray, 142, { get: function() { return 'string'; } }); + +// find and findIndex have 1 required argument + +expect = 1; +actual = Array.prototype.find.length; +reportCompare(expect, actual, 'Array.prototype.find.length == 1'); +actual = Array.prototype.findIndex.length; +reportCompare(expect, actual, 'Array.prototype.findIndex.length == 1'); + +// throw TypeError if no predicate specified +expect = 'TypeError'; +try +{ + strings.find(); + actual = 'no error'; +} +catch(e) +{ + actual = e.name; +} +reportCompare(expect, actual, 'Array.find(undefined) throws TypeError'); +try +{ + strings.findIndex(); + actual = 'no error'; +} +catch(e) +{ + actual = e.name; +} +reportCompare(expect, actual, 'Array.findIndex(undefined) throws TypeError'); + +// Length gets treated as integer, not uint32 +obj = { length: -4294967295, 0: 42 }; +expected = undefined; +actual = Array.prototype.find.call(obj, () => true); +reportCompare(expected, actual, 'find correctly treats "length" as an integer'); +expected = -1 +actual = Array.prototype.findIndex.call(obj, () => true); +reportCompare(expected, actual, 'findIndex correctly treats "length" as an integer'); + +// test find and findIndex results +try +{ + expect = 'hello'; + actual = strings.find(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'strings: find finds first string element'); + +try +{ + expect = 0; + actual = strings.findIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'strings: findIndex finds first string element'); + +try +{ + expect = '1'; + actual = mixed.find(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'mixed: find finds first string element'); + +try +{ + expect = 1; + actual = mixed.findIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'mixed: findIndex finds first string element'); + +try +{ + expect = 'sparse'; + actual = sparsestrings.find(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'sparsestrings: find finds first string element'); + +try +{ + expect = 2; + actual = sparsestrings.findIndex(isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'sparsestrings: findIndex finds first string element'); + +try +{ + expect = 'string'; + actual = [].find.call(arraylike, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike: find finds first string element'); + +try +{ + expect = 1; + actual = [].findIndex.call(arraylike, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike: findIndex finds first string element'); + +try +{ + expect = 1; + actual = 0; + Array.prototype.find.call({get 0(){ actual++ }, length: 1}, ()=>true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'arraylike with getter: getter only called once'); + +try +{ + expect = 'string'; + actual = [].find.call(indexedArray, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'indexedArray: find finds first string element'); + +try +{ + expect = 142; + actual = [].findIndex.call(indexedArray, isString); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, 'indexedArray: findIndex finds first string element'); + +// Bug 1058394 - Array#find and Array#findIndex no longer skip holes +var sparseArray = [,,1]; +var sparseArrayWithInheritedDataProperty = Object.setPrototypeOf([,,1], { + __proto__: [].__proto__, + 0 : 0 +}); +var sparseArrayWithInheritedAccessorProperty = Object.setPrototypeOf([,,1], { + __proto__: [].__proto__, + get 0(){ + throw "get 0"; + } +}); + +try +{ + expect = undefined; + actual = sparseArray.find(() => true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Don't skip holes in Array#find."); + +try +{ + expect = 0; + actual = sparseArray.findIndex(() => true); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Don't skip holes in Array#findIndex."); + +try +{ + expect = 0; + actual = sparseArrayWithInheritedDataProperty.find(v => v === 0); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Array#find can find inherited data property."); + +try +{ + expect = 0; + actual = sparseArrayWithInheritedDataProperty.findIndex(v => v === 0); +} +catch(e) +{ + actual = dumpError(e); +} +reportCompare(expect, actual, "Array#findIndex can find inherited data property."); + +try +{ + expect = "get 0"; + actual = sparseArrayWithInheritedAccessorProperty.find(() => true); +} +catch(e) +{ + actual = e; +} +reportCompare(expect, actual, "Array#find can find inherited accessor property."); + +try +{ + expect = "get 0"; + actual = sparseArrayWithInheritedAccessorProperty.findIndex(() => true); +} +catch(e) +{ + actual = e; +} +reportCompare(expect, actual, "Array#findIndex can find inherited accessor property."); diff --git a/js/src/tests/non262/Array/for_of_1.js b/js/src/tests/non262/Array/for_of_1.js new file mode 100644 index 0000000000..0233c1d278 --- /dev/null +++ b/js/src/tests/non262/Array/for_of_1.js @@ -0,0 +1,138 @@ +// Test corner cases of for-of iteration over Arrays. +// The current SetObject::construct method uses a ForOfIterator to extract +// values from the array, so we use that mechanism to test ForOfIterator here. + +// Test the properties and prototype of a generator object. +function TestManySmallArrays() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + + var TRUE_SUM = 0; + var N = 100; + var M = 3; + var sum = 0; + for (var i = 0; i < N; i++) { + var arr = new Array(M); + for (var j = 0; j < M; j++) { + arr[j] = j; + TRUE_SUM += j; + } + sum += doIter(fun, arr); + } + assertEq(sum, TRUE_SUM); +} +TestManySmallArrays(); + +// Test the properties and prototype of a generator object. +function TestSingleSmallArray() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + + var TRUE_SUM = 0; + var N = 100; + var M = 3; + var arr = new Array(M); + for (var j = 0; j < M; j++) { + arr[j] = j; + TRUE_SUM += j; + } + TRUE_SUM *= N; + + var sum = 0; + for (var i = 0; i < N; i++) { + sum += doIter(fun, arr); + } + assertEq(sum, TRUE_SUM); +} +TestSingleSmallArray(); + + +function TestChangeArrayPrototype() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + var Proto1 = Object.create(Array.prototype); + + var TRUE_SUM = 0; + var N = 100; + var MID = N/2; + var M = 3; + var arr = new Array(M); + var ARR_SUM = 0; + for (var j = 0; j < M; j++) { + arr[j] = j; + ARR_SUM += j; + } + + var sum = 0; + for (var i = 0; i < N; i++) { + sum += doIter(fun, arr); + if (i == MID) + arr.__proto__ = Proto1; + TRUE_SUM += ARR_SUM; + } + assertEq(sum, TRUE_SUM); +} +TestChangeArrayPrototype(); + + +function TestChangeManyArrayShape() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + var TRUE_SUM = 0; + var N = 100; + var MID = N/2; + var M = 3; + var sum = 0; + for (var i = 0; i < N; i++) { + var arr = new Array(M); + var ARR_SUM = 0; + for (var j = 0; j < M; j++) { + arr[j] = j; + ARR_SUM += j; + } + arr['v_' + i] = i; + sum += doIter(fun, arr); + TRUE_SUM += ARR_SUM; + } + assertEq(sum, TRUE_SUM); +} +TestChangeManyArrayShape(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/for_of_2.js b/js/src/tests/non262/Array/for_of_2.js new file mode 100644 index 0000000000..1d5dd81fc6 --- /dev/null +++ b/js/src/tests/non262/Array/for_of_2.js @@ -0,0 +1,58 @@ +// Test corner cases of for-of iteration over Arrays. +// The current SetObject::construct method uses a ForOfIterator to extract +// values from the array, so we use that mechanism to test ForOfIterator here. + +// +// Check case where ArrayIterator.prototype.next changes in the middle of iteration. +// +function TestChangeArrayIteratorNext() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + var GET_COUNT = 0; + function getter() { + GET_COUNT++; + if (GET_COUNT == MID) + iterProto.next = NewNext; + return M2; + } + + var iter = ([])[Symbol.iterator](); + var iterProto = Object.getPrototypeOf(iter); + var OldNext = iterProto.next; + var NewNext = function () { + return OldNext.apply(this, arguments); + }; + + var TRUE_SUM = 0; + var N = 100; + var MID = N/2; + var M = 3; + var arr = new Array(M); + var ARR_SUM = 0; + for (var j = 0; j < M; j++) { + arr[j] = j; + ARR_SUM += j; + } + var M2 = (M/2)|0; + Object.defineProperty(arr, M2, {'get':getter}); + + var sum = 0; + for (var i = 0; i < N; i++) { + sum += doIter(fun, arr); + TRUE_SUM += ARR_SUM; + } + assertEq(sum, TRUE_SUM); +} +TestChangeArrayIteratorNext(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/for_of_3.js b/js/src/tests/non262/Array/for_of_3.js new file mode 100644 index 0000000000..c90eb1f000 --- /dev/null +++ b/js/src/tests/non262/Array/for_of_3.js @@ -0,0 +1,60 @@ +// Test corner cases of for-of iteration over Arrays. +// The current SetObject::construct method uses a ForOfIterator to extract +// values from the array, so we use that mechanism to test ForOfIterator here. + +// +// Check array length increases changes during iteration. +// +function TestIncreaseArrayLength() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + var GET_COUNT = 0; + function getter() { + GET_COUNT++; + if (GET_COUNT == MID) { + ARR_SUM += arr.length; + arr.push(arr.length); + } + return M2; + } + + var iter = ([])[Symbol.iterator](); + var iterProto = Object.getPrototypeOf(iter); + var OldNext = iterProto.next; + var NewNext = function () { + return OldNext.apply(this, arguments); + }; + + var TRUE_SUM = 0; + var N = 100; + var MID = N/2; + var M = 3; + var arr = new Array(M); + var ARR_SUM = 0; + for (var j = 0; j < M; j++) { + arr[j] = j; + ARR_SUM += j; + } + var M2 = (M/2)|0; + Object.defineProperty(arr, M2, {'get':getter}); + + var sum = 0; + for (var i = 0; i < N; i++) { + sum += doIter(fun, arr); + TRUE_SUM += ARR_SUM; + } + assertEq(sum, TRUE_SUM); +} +TestIncreaseArrayLength(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/for_of_4.js b/js/src/tests/non262/Array/for_of_4.js new file mode 100644 index 0000000000..4572727a1f --- /dev/null +++ b/js/src/tests/non262/Array/for_of_4.js @@ -0,0 +1,64 @@ +// Test corner cases of for-of iteration over Arrays. +// The current SetObject::construct method uses a ForOfIterator to extract +// values from the array, so we use that mechanism to test ForOfIterator here. + +// +// Check array length decreases changes during iteration. +// +function TestDecreaseArrayLength() { + function doIter(f, arr) { + return f(...new Set(arr)); + } + + function fun(a, b, c) { + var result = 0; + for (var i = 0; i < arguments.length; i++) + result += arguments[i]; + return result; + } + + var GET_COUNT = 0; + function getter() { + GET_COUNT++; + if (GET_COUNT == MID) { + arr.length = 0; + } + return M2; + } + + var iter = ([])[Symbol.iterator](); + var iterProto = Object.getPrototypeOf(iter); + var OldNext = iterProto.next; + var NewNext = function () { + return OldNext.apply(this, arguments); + }; + + var TRUE_SUM = 0; + var N = 100; + var MID = N/2; + var M = 3; + var arr = new Array(M); + var ARR_SUM = 0; + for (var j = 0; j < M; j++) { + arr[j] = j; + ARR_SUM += j; + } + var M2 = (M/2)|0; + Object.defineProperty(arr, M2, {'get':getter}); + + var sum = 0; + for (var i = 0; i < N; i++) { + var oldLen = arr.length; + sum += doIter(fun, arr); + var newLen = arr.length; + if (oldLen == newLen) + TRUE_SUM += arr.length > 0 ? ARR_SUM : 0; + else + TRUE_SUM += 1 + } + assertEq(sum, TRUE_SUM); +} +TestDecreaseArrayLength(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/from-iterator-close.js b/js/src/tests/non262/Array/from-iterator-close.js new file mode 100644 index 0000000000..553c44e467 --- /dev/null +++ b/js/src/tests/non262/Array/from-iterator-close.js @@ -0,0 +1,183 @@ +var BUGNUMBER = 1180306; +var summary = 'Array.from should close iterator on error'; + +print(BUGNUMBER + ": " + summary); + +function test(ctor, { mapVal=undefined, + nextVal=undefined, + nextThrowVal=undefined, + modifier=undefined, + exceptionVal=undefined, + exceptionType=undefined, + closed=true }) { + let iterable = { + closed: false, + [Symbol.iterator]() { + let iterator = { + first: true, + next() { + if (this.first) { + this.first = false; + if (nextThrowVal) + throw nextThrowVal; + return nextVal; + } + return { value: undefined, done: true }; + }, + return() { + iterable.closed = true; + return {}; + } + }; + if (modifier) + modifier(iterator, iterable); + + return iterator; + } + }; + if (exceptionVal) { + let caught = false; + try { + ctor.from(iterable, mapVal); + } catch (e) { + assertEq(e, exceptionVal); + caught = true; + } + assertEq(caught, true); + } else if (exceptionType) { + assertThrowsInstanceOf(() => ctor.from(iterable, mapVal), exceptionType); + } else { + ctor.from(iterable, mapVal); + } + assertEq(iterable.closed, closed); +} + +// == Error cases with close == + +// ES 2017 draft 22.1.2.1 step 5.e.i.1. +// Cannot test. + +// ES 2017 draft 22.1.2.1 step 5.e.vi.2. +test(Array, { + mapVal: () => { throw "map throws"; }, + nextVal: { value: 1, done: false }, + exceptionVal: "map throws", + closed: true, +}); + +// ES 2017 draft 22.1.2.1 step 5.e.ix. +class MyArray extends Array { + constructor() { + return new Proxy({}, { + defineProperty() { + throw "defineProperty throws"; + } + }); + } +} +test(MyArray, { + nextVal: { value: 1, done: false }, + exceptionVal: "defineProperty throws", + closed: true, +}); + +// ES 2021 draft 7.4.6 step 5. +// if GetMethod fails, the thrown value should be ignored. +test(MyArray, { + nextVal: { value: 1, done: false }, + modifier: (iterator, iterable) => { + Object.defineProperty(iterator, "return", { + get: function() { + iterable.closed = true; + throw "return getter throws"; + } + }); + }, + exceptionVal: "defineProperty throws", + closed: true, +}); +test(MyArray, { + nextVal: { value: 1, done: false }, + modifier: (iterator, iterable) => { + Object.defineProperty(iterator, "return", { + get: function() { + iterable.closed = true; + return "non object"; + } + }); + }, + exceptionVal: "defineProperty throws", + closed: true, +}); +test(MyArray, { + nextVal: { value: 1, done: false }, + modifier: (iterator, iterable) => { + Object.defineProperty(iterator, "return", { + get: function() { + iterable.closed = true; + // Non callable. + return {}; + } + }); + }, + exceptionVal: "defineProperty throws", + closed: true, +}); + +// ES 2017 draft 7.4.6 steps 6. +// if return method throws, the thrown value should be ignored. +test(MyArray, { + nextVal: { value: 1, done: false }, + modifier: (iterator, iterable) => { + iterator.return = function() { + iterable.closed = true; + throw "return throws"; + }; + }, + exceptionVal: "defineProperty throws", + closed: true, +}); + +test(MyArray, { + nextVal: { value: 1, done: false }, + modifier: (iterator, iterable) => { + iterator.return = function() { + iterable.closed = true; + return "non object"; + }; + }, + exceptionVal: "defineProperty throws", + closed: true, +}); + +// == Error cases without close == + +// ES 2017 draft 22.1.2.1 step 5.e.iii. +test(Array, { + nextThrowVal: "next throws", + exceptionVal: "next throws", + closed: false, +}); + +test(Array, { + nextVal: { value: {}, get done() { throw "done getter throws"; } }, + exceptionVal: "done getter throws", + closed: false, +}); + +// ES 2017 draft 22.1.2.1 step 5.e.v. +test(Array, { + nextVal: { get value() { throw "value getter throws"; }, done: false }, + exceptionVal: "value getter throws", + closed: false, +}); + +// == Successful cases == + +test(Array, { + nextVal: { value: 1, done: false }, + closed: false, +}); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/from_async.js b/js/src/tests/non262/Array/from_async.js new file mode 100644 index 0000000000..c104c26f7d --- /dev/null +++ b/js/src/tests/non262/Array/from_async.js @@ -0,0 +1,302 @@ +// |reftest| shell-option(--enable-array-from-async) skip-if(!xulRuntime.shell||!Array.fromAsync) -- needs drainJobQueue + +// Basic Smoke Test +async function* asyncGen(n) { + for (let i = 0; i < n; i++) { + yield i * 2; + } +} + +let done = false; +Array.fromAsync(asyncGen(4)).then((x) => { + assertEq(Array.isArray(x), true); + assertEq(x.length, 4); + assertEq(x[0], 0); + assertEq(x[1], 2); + assertEq(x[2], 4); + assertEq(x[3], 6); + done = true; +} +); + +drainJobQueue(); +assertEq(done, true); + +(async function () { + class InterruptableAsyncIterator { + count = 0 + closed = false + throwAfter = NaN + constructor(n, throwAfter = NaN) { + this.count = n; + this.throwAfter = throwAfter; + } + [Symbol.asyncIterator] = function () { + return { + iter: this, + i: 0, + async next() { + if (this.i > this.iter.throwAfter) { + throw "Exception" + } + if (this.i++ < this.iter.count) { + return Promise.resolve({ done: false, value: this.i - 1 }); + } + return Promise.resolve({ done: true, value: undefined }); + }, + async return(x) { + this.iter.closed = true; + return { value: x, done: true }; + } + } + } + } + + var one = await Array.fromAsync(new InterruptableAsyncIterator(2)); + assertEq(one.length, 2) + assertEq(one[0], 0); + assertEq(one[1], 1); + + var two = new InterruptableAsyncIterator(10, 2); + var threw = false; + try { + var res = await Array.fromAsync(two); + } catch (e) { + threw = true; + assertEq(e, "Exception"); + } + assertEq(threw, true); + // The iterator is not closed unless we have an abrupt completion while mapping. + assertEq(two.closed, false); + + // Test throwing while mapping: Iterator should be closed. + var three = new InterruptableAsyncIterator(10, 9); + threw = false; + try { + var res = await Array.fromAsync(three, (x) => { + if (x > 3) { + throw "Range" + } + return x; + }); + } catch (e) { + assertEq(e, "Range"); + threw = true; + } + assertEq(threw, true); + assertEq(three.closed, true); + + var sync = await Array.fromAsync([1, 2, 3]); + assertEq(sync.length, 3); + assertEq(sync[0], 1) + assertEq(sync[1], 2) + assertEq(sync[2], 3) + + let closed_frozen = false; + class Frozen { + constructor(x) { + this.count = x; + Object.freeze(this); + } + [Symbol.asyncIterator] = function () { + return { + iter: this, + i: 0, + async next() { + if (this.i++ < this.iter.count) { + return Promise.resolve({ done: false, value: this.i - 1 }); + } + return Promise.resolve({ done: true, value: undefined }); + }, + async return(x) { + // Can't use Frozen instance, becuse frozen is frozen. + closed_frozen = true; + return { value: x, done: true }; + } + } + } + } + + // We should close the iterator when define property throws. + // Test by defining into a frozen object. + var frozen = new Frozen(10); + threw = false; + try { + var result = await Array.fromAsync.call(Frozen, frozen); + } catch (e) { + threw = true; + } + + assertEq(threw, true); + assertEq(closed_frozen, true); + +})(); + +drainJobQueue(); + +(async function () { + var badSyncIterator = { + [Symbol.iterator]() { + return null; + } + }; + + var badAsyncIterator = { + [Symbol.asyncIterator]() { + return null; + } + }; + + async function errorMessage(fn) { + try { + await fn(); + } catch (e) { + return e.message; + } + throw new Error("missing error"); + } + + // Ensure Array.from and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => Array.from(badSyncIterator)); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); + assertEq(actual, expected); +})(); + +drainJobQueue(); + +(async function () { + function* gen() { + for (let i = 0; i < 4; ++i) { + yield Promise.resolve(i); + } + }; + + var array = await Array.fromAsync(gen()); + + // Promise values are unwrapped via AsyncFromSyncIterator. + assertEqArray(array, [0, 1, 2, 3]); +})(); + +drainJobQueue(); + +(async function () { + var badSyncIterator = { + [Symbol.iterator]: 123, + }; + + var badAsyncIterator = { + [Symbol.asyncIterator]: 123, + }; + + async function errorMessage(fn) { + try { + await fn(); + } catch (e) { + return e.message; + } + throw new Error("missing error"); + } + + // Ensure Array.from and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => Array.from(badSyncIterator)); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); + + // Ensure for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); + + // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); + assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable")); +})(); + +drainJobQueue(); + + +var g = newGlobal(); +g.asyncGen = asyncGen; +var p = g.evaluate(` +Array.fromAsync(asyncGen(4)) +`) + +p.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g.Array, true); +}) + +drainJobQueue(); + + +var g2 = newGlobal({ newCompartment: true }); +g2.asyncGen = asyncGen; +var p = g2.evaluate(` +Array.fromAsync(asyncGen(4)) +`) + +p.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g2.Array, true); + nukeCCW(x); // this will throw if x happens to not be a CCW (it should be!) +}) +drainJobQueue(); + +// Test having a CCW 'this' value. +g2.obj = {}; +var p2 = g2.evaluate(` +Array.fromAsync.call(obj, asyncGen(4)) +`) + +p2.then((x) => { + assertEq(x instanceof Array, false); // Should use the other global's Array. + assertEq(x instanceof g2.Array, true); + nukeCCW(x); +}) + +drainJobQueue(); + +// Verify user promise resolution behaviour. +var myThenCalled = false; +var obj = { then: () => { myThenCalled = true; } } +function* genO() { + yield obj; + return; +} + +var res = Array.fromAsync(genO()); +res.then((x) => { + assertEq(x[0], obj); + assertEq(myThenCalled, true); +}); + +drainJobQueue(); + +function* thrower() { + throw new Error(); +} + +g2.thrower = thrower; +var p = g2.evaluate(`Array.fromAsync(thrower())`) +p.catch((e) => { + assertEq(e instanceof Error, true, "Should throw an error from the current global"); +}) +drainJobQueue(); + +p = g2.evaluate(`Array.fromAsync(thrower, 1)`); +p.catch((e) => assertEq(e instanceof g2.Error, true, "Should throw error from g2")) +drainJobQueue(); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/from_basics.js b/js/src/tests/non262/Array/from_basics.js new file mode 100644 index 0000000000..623207a41a --- /dev/null +++ b/js/src/tests/non262/Array/from_basics.js @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from copies arrays. +var src = [1, 2, 3], copy = Array.from(src); +assertEq(copy === src, false); +assertEq(Array.isArray(copy), true); +assertDeepEq(copy, src); + +// Non-element properties are not copied. +var a = [0, 1]; +a.name = "lisa"; +assertDeepEq(Array.from(a), [0, 1]); + +// It's a shallow copy. +src = [[0], [1]]; +copy = Array.from(src); +assertEq(copy[0], src[0]); +assertEq(copy[1], src[1]); + +// Array.from can copy non-iterable objects, if they're array-like. +src = {0: "zero", 1: "one", length: 2}; +copy = Array.from(src); +assertEq(Array.isArray(copy), true); +assertDeepEq(copy, ["zero", "one"]); + +// Properties past the .length are not copied. +src = {0: "zero", 1: "one", 2: "two", 9: "nine", name: "lisa", length: 2}; +assertDeepEq(Array.from(src), ["zero", "one"]); + +// If an object has neither an @@iterator method nor .length, +// then it's treated as zero-length. +assertDeepEq(Array.from({}), []); + +// Source object property order doesn't matter. +src = {length: 2, 1: "last", 0: "first"}; +assertDeepEq(Array.from(src), ["first", "last"]); + +// Array.from does not preserve holes. +assertDeepEq(Array.from(Array(3)), [undefined, undefined, undefined]); +assertDeepEq(Array.from([, , 2, 3]), [undefined, undefined, 2, 3]); +assertDeepEq(Array.from([0, , , ,]), [0, undefined, undefined, undefined]); + +// Even on non-iterable objects. +assertDeepEq(Array.from({length: 4}), [undefined, undefined, undefined, undefined]); + +// Array.from should coerce negative lengths to zero. +assertDeepEq(Array.from({length: -1}), []); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_constructor.js b/js/src/tests/non262/Array/from_constructor.js new file mode 100644 index 0000000000..6846ef09aa --- /dev/null +++ b/js/src/tests/non262/Array/from_constructor.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from can be applied to any constructor. +// For example, the Date builtin constructor. +var d = Array.from.call(Date, ["A", "B"]); +assertEq(Array.isArray(d), false); +assertEq(Object.prototype.toString.call(d), "[object Date]"); +assertEq(Object.getPrototypeOf(d), Date.prototype); +assertEq(d.length, 2); +assertEq(d[0], "A"); +assertEq(d[1], "B"); + +// Or Object. +var obj = Array.from.call(Object, []); +assertEq(Array.isArray(obj), false); +assertEq(Object.getPrototypeOf(obj), Object.prototype); +assertEq(Object.getOwnPropertyNames(obj).join(","), "length"); +assertEq(obj.length, 0); + +// Or any JS function. +function C(arg) { + this.args = arguments; +} +var c = Array.from.call(C, {length: 1, 0: "zero"}); +assertEq(c instanceof C, true); +assertEq(c.args.length, 1); +assertEq(c.args[0], 1); +assertEq(c.length, 1); +assertEq(c[0], "zero"); + +// If the 'this' value passed to Array.from is not a constructor, +// a plain Array is created. +var arr = [3, 4, 5]; +var nonconstructors = [ + {}, Math, Object.getPrototypeOf, undefined, 17, + () => ({}) // arrow functions are not constructors +]; +for (var v of nonconstructors) { + obj = Array.from.call(v, arr); + assertEq(Array.isArray(obj), true); + assertDeepEq(obj, arr); +} + +// Array.from does not get confused if global.Array is replaced with another +// constructor. +function NotArray() { +} +var RealArray = Array; +NotArray.from = Array.from; +Array = NotArray; +assertEq(RealArray.from([1]) instanceof RealArray, true); +assertEq(NotArray.from([1]) instanceof NotArray, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_errors.js b/js/src/tests/non262/Array/from_errors.js new file mode 100644 index 0000000000..cbf0eb195a --- /dev/null +++ b/js/src/tests/non262/Array/from_errors.js @@ -0,0 +1,152 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from throws if the argument is undefined or null. +assertThrowsInstanceOf(() => Array.from(), TypeError); +assertThrowsInstanceOf(() => Array.from(undefined), TypeError); +assertThrowsInstanceOf(() => Array.from(null), TypeError); + +// Array.from throws if an element can't be defined on the new object. +function ObjectWithReadOnlyElement() { + Object.defineProperty(this, "0", {value: null}); + this.length = 0; +} +ObjectWithReadOnlyElement.from = Array.from; +assertDeepEq(ObjectWithReadOnlyElement.from([]), new ObjectWithReadOnlyElement); +assertThrowsInstanceOf(() => ObjectWithReadOnlyElement.from([1]), TypeError); + +// The same, but via preventExtensions. +function InextensibleObject() { + Object.preventExtensions(this); +} +InextensibleObject.from = Array.from; +assertThrowsInstanceOf(() => InextensibleObject.from([1]), TypeError); + +// We will now test this property, that Array.from throws if the .length can't +// be assigned, using several different kinds of object. +var obj; +function init(self) { + obj = self; + self[0] = self[1] = self[2] = self[3] = 0; +} + +function testUnsettableLength(C, Exc) { + if (Exc === undefined) + Exc = TypeError; // the usual expected exception type + C.from = Array.from; + + obj = null; + assertThrowsInstanceOf(() => C.from([]), Exc); + assertEq(obj instanceof C, true); + for (var i = 0; i < 4; i++) + assertEq(obj[0], 0); + + obj = null; + assertThrowsInstanceOf(() => C.from([0, 10, 20, 30]), Exc); + assertEq(obj instanceof C, true); + for (var i = 0; i < 4; i++) + assertEq(obj[i], i * 10); +} + +// Array.from throws if the new object's .length can't be assigned because +// there is no .length and the object is inextensible. +function InextensibleObject4() { + init(this); + Object.preventExtensions(this); +} +testUnsettableLength(InextensibleObject4); + +// Array.from throws if the new object's .length can't be assigned because it's +// read-only. +function ObjectWithReadOnlyLength() { + init(this); + Object.defineProperty(this, "length", {configurable: true, writable: false, value: 4}); +} +testUnsettableLength(ObjectWithReadOnlyLength); + +// The same, but using a builtin type. +Uint8Array.from = Array.from; +assertThrowsInstanceOf(() => Uint8Array.from([]), TypeError); + +// Array.from throws if the new object's .length can't be assigned because it +// inherits a readonly .length along the prototype chain. +function ObjectWithInheritedReadOnlyLength() { + init(this); +} +Object.defineProperty(ObjectWithInheritedReadOnlyLength.prototype, + "length", + {configurable: true, writable: false, value: 4}); +testUnsettableLength(ObjectWithInheritedReadOnlyLength); + +// The same, but using an object with a .length getter but no setter. +function ObjectWithGetterOnlyLength() { + init(this); + Object.defineProperty(this, "length", {configurable: true, get: () => 4}); +} +testUnsettableLength(ObjectWithGetterOnlyLength); + +// The same, but with a setter that throws. +function ObjectWithThrowingLengthSetter() { + init(this); + Object.defineProperty(this, "length", { + configurable: true, + get: () => 4, + set: () => { throw new RangeError("surprise!"); } + }); +} +testUnsettableLength(ObjectWithThrowingLengthSetter, RangeError); + +// Array.from throws if mapfn is neither callable nor undefined. +assertThrowsInstanceOf(() => Array.from([3, 4, 5], {}), TypeError); +assertThrowsInstanceOf(() => Array.from([3, 4, 5], "also not a function"), TypeError); +assertThrowsInstanceOf(() => Array.from([3, 4, 5], null), TypeError); + +// Even if the function would not have been called. +assertThrowsInstanceOf(() => Array.from([], JSON), TypeError); + +// If mapfn is not undefined and not callable, the error happens before anything else. +// Before calling the constructor, before touching the arrayLike. +var log = ""; +function C() { + log += "C"; + obj = this; +} +var p = new Proxy({}, { + has: function () { log += "1"; }, + get: function () { log += "2"; }, + getOwnPropertyDescriptor: function () { log += "3"; } +}); +assertThrowsInstanceOf(() => Array.from.call(C, p, {}), TypeError); +assertEq(log, ""); + +// If mapfn throws, the new object has already been created. +var arrayish = { + get length() { log += "l"; return 1; }, + get 0() { log += "0"; return "q"; } +}; +log = ""; +var exc = {surprise: "ponies"}; +assertThrowsValue(() => Array.from.call(C, arrayish, () => { throw exc; }), exc); +assertEq(log, "lC0"); +assertEq(obj instanceof C, true); + +// It's a TypeError if the @@iterator property is a primitive (except null and undefined). +for (var primitive of ["foo", 17, Symbol(), true]) { + assertThrowsInstanceOf(() => Array.from({[Symbol.iterator] : primitive}), TypeError); +} +assertDeepEq(Array.from({[Symbol.iterator]: null}), []); +assertDeepEq(Array.from({[Symbol.iterator]: undefined}), []); + +// It's a TypeError if the iterator's .next() method returns a primitive. +for (var primitive of [undefined, null, 17]) { + assertThrowsInstanceOf( + () => Array.from({ + [Symbol.iterator]() { + return {next() { return primitive; }}; + } + }), + TypeError); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_iterable.js b/js/src/tests/non262/Array/from_iterable.js new file mode 100644 index 0000000000..5681819612 --- /dev/null +++ b/js/src/tests/non262/Array/from_iterable.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from works on arguments objects. +(function () { + assertDeepEq(Array.from(arguments), ["arg0", "arg1", undefined]); +})("arg0", "arg1", undefined); + +// If an object has both .length and [@@iterator] properties, [@@iterator] is used. +var a = ['a', 'e', 'i', 'o', 'u']; +a[Symbol.iterator] = function* () { + for (var i = 5; i--; ) + yield this[i]; +}; + +var log = ''; +function f(x) { + log += x; + return x + x; +} + +var b = Array.from(a, f); +assertDeepEq(b, ['uu', 'oo', 'ii', 'ee', 'aa']); +assertEq(log, 'uoiea'); + +// In fact, if [@@iterator] is present, .length isn't queried at all. +var pa = new Proxy(a, { + has: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (has)"); + return id in target; + }, + get: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (get)"); + return target[id]; + }, + getOwnPropertyDescriptor: function (target, id) { + if (id === "length") + throw new Error(".length should not be queried (getOwnPropertyDescriptor)"); + return Object.getOwnPropertyDescriptor(target, id) + } +}); +log = ""; +b = Array.from(pa, f); +assertDeepEq(b, ['uu', 'oo', 'ii', 'ee', 'aa']); +assertEq(log, 'uoiea'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_length_setter.js b/js/src/tests/non262/Array/from_length_setter.js new file mode 100644 index 0000000000..43f35f73e0 --- /dev/null +++ b/js/src/tests/non262/Array/from_length_setter.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from calls a length setter if present. +var hits = 0; +function C() {} +C.prototype = {set length(v) { hits++; }}; +C.from = Array.from; +var copy = C.from(["A", "B"]); +assertEq(hits, 1); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_mapping.js b/js/src/tests/non262/Array/from_mapping.js new file mode 100644 index 0000000000..568b9d6d7a --- /dev/null +++ b/js/src/tests/non262/Array/from_mapping.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// If the mapfn argument to Array.from is undefined, don't map. +assertDeepEq(Array.from([3, 4, 5], undefined), [3, 4, 5]); +assertDeepEq(Array.from([4, 5, 6], undefined, Math), [4, 5, 6]); + +// mapfn is called with two arguments: value and index. +var log = []; +function f() { + log.push(Array.from(arguments)); + return log.length; +} +assertDeepEq(Array.from(['a', 'e', 'i', 'o', 'u'], f), [1, 2, 3, 4, 5]); +assertDeepEq(log, [['a', 0], ['e', 1], ['i', 2], ['o', 3], ['u', 4]]); + +// If the object to be copied is non-iterable, mapfn is still called with two +// arguments. +log = []; +assertDeepEq(Array.from({0: "zero", 1: "one", length: 2}, f), [1, 2]); +assertDeepEq(log, [["zero", 0], ["one", 1]]); + +// If the object to be copied is iterable and the constructor is not Array, +// mapfn is still called with two arguments. +log = []; +function C() {} +C.from = Array.from; +var c = new C; +c[0] = 1; +c[1] = 2; +c.length = 2; +assertDeepEq(C.from(["zero", "one"], f), c); +assertDeepEq(log, [["zero", 0], ["one", 1]]); + +// The mapfn is called even if the value to be mapped is undefined. +assertDeepEq(Array.from([0, 1, , 3], String), ["0", "1", "undefined", "3"]); +var arraylike = {length: 4, "0": 0, "1": 1, "3": 3}; +assertDeepEq(Array.from(arraylike, String), ["0", "1", "undefined", "3"]); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_primitive.js b/js/src/tests/non262/Array/from_primitive.js new file mode 100644 index 0000000000..e3f68d8bc2 --- /dev/null +++ b/js/src/tests/non262/Array/from_primitive.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +for (let primitive of [true, 3.14, "hello", Symbol()]) { + let prototype = Object.getPrototypeOf(primitive); + + Object.defineProperty(prototype, Symbol.iterator, { + configurable: true, + get() { + "use strict"; + assertEq(this, primitive); + return () => [this][Symbol.iterator](); + }, + }); + assertEq(Array.from(primitive)[0], primitive); + + delete prototype[Symbol.iterator]; +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_proxy.js b/js/src/tests/non262/Array/from_proxy.js new file mode 100644 index 0000000000..c3c82d0b7e --- /dev/null +++ b/js/src/tests/non262/Array/from_proxy.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Two tests involving Array.from and a Proxy. +var log = []; +function LoggingProxy(target) { + var h = { + defineProperty: function (t, id) { + log.push("define", id); + return true; + }, + has: function (t, id) { + log.push("has", id); + return id in t; + }, + get: function (t, id) { + log.push("get", id); + return t[id]; + }, + set: function (t, id, v) { + log.push("set", id); + t[id] = v; + return true; + } + }; + return new Proxy(target || [], h); +} + +// When the new object created by Array.from is a Proxy, +// Array.from calls handler.defineProperty to create new elements +// but handler.set to set the length. +LoggingProxy.from = Array.from; +LoggingProxy.from([3, 4, 5]); +assertDeepEq(log, ["define", "0", "define", "1", "define", "2", "set", "length"]); + +// When the argument passed to Array.from is a Proxy, Array.from +// calls handler.get on it. +log = []; +assertDeepEq(Array.from(new LoggingProxy([3, 4, 5])), [3, 4, 5]); +assertDeepEq(log, ["get", Symbol.iterator, + "get", "length", "get", "0", + "get", "length", "get", "1", + "get", "length", "get", "2", + "get", "length"]); + +// Array-like iteration only gets the length once. +log = []; +var arr = [5, 6, 7]; +arr[Symbol.iterator] = undefined; +assertDeepEq(Array.from(new LoggingProxy(arr)), [5, 6, 7]); +assertDeepEq(log, ["get", Symbol.iterator, + "get", "length", "get", "0", "get", "1", "get", "2"]); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_realms.js b/js/src/tests/non262/Array/from_realms.js new file mode 100644 index 0000000000..e3a8636063 --- /dev/null +++ b/js/src/tests/non262/Array/from_realms.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +if (typeof newGlobal === 'function') { + // G.Array.from, where G is any global, produces an array whose prototype + // is G.Array.prototype. + var g = newGlobal(); + var ga = g.Array.from([1, 2, 3]); + assertEq(ga instanceof g.Array, true); + + // Even if G.Array is not passed in as the 'this' value to the call. + var from = g.Array.from + var ga2 = from([1, 2, 3]); + assertEq(ga2 instanceof g.Array, true); + + // Array.from can be applied to a constructor from another realm. + var p = Array.from.call(g.Array, [1, 2, 3]); + assertEq(p instanceof g.Array, true); + var q = g.Array.from.call(Array, [3, 4, 5]); + assertEq(q instanceof Array, true); + + // The default 'this' value received by a non-strict mapping function is + // that function's global, not Array.from's global or the caller's global. + var h = newGlobal(), result = undefined; + h.mainGlobal = this; + h.eval("function f() { mainGlobal.result = this; }"); + g.Array.from.call(Array, [5, 6, 7], h.f); + // (Give each global in the test a name, for better error messages. But use + // globalName, because window.name is complicated.) + this.globalName = "main"; + g.globalName = "g"; + h.globalName = "h"; + assertEq(result.globalName, "h"); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_string.js b/js/src/tests/non262/Array/from_string.js new file mode 100644 index 0000000000..3ad19e253a --- /dev/null +++ b/js/src/tests/non262/Array/from_string.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Array.from on a string iterates over the string. +assertDeepEq(Array.from("test string"), + ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g']); + +// Array.from on a string handles surrogate pairs correctly. +var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF +assertDeepEq(Array.from(gclef), [gclef]); +assertDeepEq(Array.from(gclef + " G"), [gclef, " ", "G"]); + +// Array.from on a string calls the @@iterator method. +String.prototype[Symbol.iterator] = function* () { yield 1; yield 2; }; +assertDeepEq(Array.from("anything"), [1, 2]); + +// If the iterator method is deleted, Strings are still arraylike. +delete String.prototype[Symbol.iterator]; +assertDeepEq(Array.from("works"), ['w', 'o', 'r', 'k', 's']); +assertDeepEq(Array.from(gclef), ['\uD834', '\uDD1E']); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_surfaces.js b/js/src/tests/non262/Array/from_surfaces.js new file mode 100644 index 0000000000..680b64817c --- /dev/null +++ b/js/src/tests/non262/Array/from_surfaces.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Check superficial features of Array.from. +var desc = Object.getOwnPropertyDescriptor(Array, "from"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, true); +assertEq(Array.from.length, 1); +assertThrowsInstanceOf(() => new Array.from(), TypeError); // not a constructor + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/from_this.js b/js/src/tests/non262/Array/from_this.js new file mode 100644 index 0000000000..978c90b871 --- /dev/null +++ b/js/src/tests/non262/Array/from_this.js @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// The third argument to Array.from is passed as the 'this' value to the +// mapping function. +var hits = 0, obj = {}; +function f(x) { + assertEq(this, obj); + hits++; +} +Array.from(["a", "b", "c"], f, obj); +assertEq(hits, 3); + +// Without an argument, undefined is passed... +hits = 0; +function gs(x) { + "use strict"; + assertEq(this, undefined); + hits++; +} +Array.from("def", gs); +assertEq(hits, 3); + +// ...and if the mapping function is non-strict, that means the global is +// passed. +var global = this; +hits = 0; +function g(x) { + assertEq(this, global); + hits++; +} +Array.from("ghi", g); +assertEq(hits, 3); + +// A primitive value can be passed. +for (var v of [0, "str", undefined]) { + hits = 0; + var mapfn = function h(x) { + "use strict"; + assertEq(this, v); + hits++; + }; + Array.from("pq", mapfn, v); + assertEq(hits, 2); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/frozen-dense-array.js b/js/src/tests/non262/Array/frozen-dense-array.js new file mode 100644 index 0000000000..cdb86daa3b --- /dev/null +++ b/js/src/tests/non262/Array/frozen-dense-array.js @@ -0,0 +1,42 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Author: Emilio Cobos Álvarez <ecoal95@gmail.com> + */ +var BUGNUMBER = 1310744; +var summary = "Dense array properties shouldn't be modified when they're frozen"; + +print(BUGNUMBER + ": " + summary); + +var a = Object.freeze([4, 5, 1]); + +function assertArrayIsExpected() { + assertEq(a.length, 3); + assertEq(a[0], 4); + assertEq(a[1], 5); + assertEq(a[2], 1); +} + +assertThrowsInstanceOf(() => a.reverse(), TypeError); +assertThrowsInstanceOf(() => a.shift(), TypeError); +assertThrowsInstanceOf(() => a.unshift(0), TypeError); +assertThrowsInstanceOf(() => a.sort(function() {}), TypeError); +assertThrowsInstanceOf(() => a.pop(), TypeError); +assertThrowsInstanceOf(() => a.fill(0), TypeError); +assertThrowsInstanceOf(() => a.splice(0, 1, 1), TypeError); +assertThrowsInstanceOf(() => a.push("foo"), TypeError); +assertThrowsInstanceOf(() => { "use strict"; a.length = 5; }, TypeError); +assertThrowsInstanceOf(() => { "use strict"; a[2] = "foo"; }, TypeError); +assertThrowsInstanceOf(() => { "use strict"; delete a[0]; }, TypeError); +assertThrowsInstanceOf(() => a.splice(Math.a), TypeError); + +// Shouldn't throw, since this is not strict mode, but shouldn't change the +// value of the property. +a.length = 5; +a[2] = "foo"; +assertEq(delete a[0], false); + +assertArrayIsExpected(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/frozen-dict-mode-length.js b/js/src/tests/non262/Array/frozen-dict-mode-length.js new file mode 100644 index 0000000000..e519b58920 --- /dev/null +++ b/js/src/tests/non262/Array/frozen-dict-mode-length.js @@ -0,0 +1,18 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Author: Emilio Cobos Álvarez <ecoal95@gmail.com> + */ +var BUGNUMBER = 1312948; +var summary = "Freezing a dictionary mode object with a length property should make Object.isFrozen report true"; + +print(BUGNUMBER + ": " + summary); + +/* Convert to dictionary mode */ +delete Array.prototype.slice; + +Object.freeze(Array.prototype); +assertEq(Object.isFrozen(Array.prototype), true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/getter-name.js b/js/src/tests/non262/Array/getter-name.js new file mode 100644 index 0000000000..3829c729ba --- /dev/null +++ b/js/src/tests/non262/Array/getter-name.js @@ -0,0 +1,9 @@ +var BUGNUMBER = 1180290; +var summary = 'Array getters should have get prefix'; + +print(BUGNUMBER + ": " + summary); + +assertEq(Object.getOwnPropertyDescriptor(Array, Symbol.species).get.name, "get [Symbol.species]"); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/group-callback-evaluation.js b/js/src/tests/non262/Array/group-callback-evaluation.js new file mode 100644 index 0000000000..0a917df33d --- /dev/null +++ b/js/src/tests/non262/Array/group-callback-evaluation.js @@ -0,0 +1,20 @@ +// |reftest| shell-option(--enable-array-grouping) skip-if(!Array.prototype.group) + +var array = [1, 2, 3]; + +var calls = 0; + +var grouped = array.group(() => { + calls++; + + return { + toString() { + return "a"; + } + } +}); + +assertEq(calls, 3); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/group-propertkey-is-length.js b/js/src/tests/non262/Array/group-propertkey-is-length.js new file mode 100644 index 0000000000..79cb06b6a2 --- /dev/null +++ b/js/src/tests/non262/Array/group-propertkey-is-length.js @@ -0,0 +1,17 @@ +// |reftest| shell-option(--enable-array-grouping) skip-if(!Array.prototype.group) + +var array = [0]; + +var grouped = array.group(() => "length"); + +assertDeepEq(grouped, Object.create(null, { + length: { + value: [0], + writable: true, + enumerable: true, + configurable: true, + }, +})); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/group.js b/js/src/tests/non262/Array/group.js new file mode 100644 index 0000000000..4df43d1445 --- /dev/null +++ b/js/src/tests/non262/Array/group.js @@ -0,0 +1,95 @@ +// |reftest| shell-option(--enable-array-grouping) skip-if(!Array.prototype.group) + +var BUGNUMBER = 1739648; +var summary = "Implement Array.prototype.groupToMap || group"; + +print(BUGNUMBER + ": " + summary); + +function isNeg(x) { + if (Object.is(x, -0) || x < 0) { + return true; + } + return false; +} + +{ + const a1 = [-Infinity, -2, -1, -0, 0, 1, 2, Infinity]; + const expectedObj = { neg: [-Infinity, -2, -1, -0], pos: [0, 1, 2, Infinity] }; + Object.setPrototypeOf(expectedObj, null); + + const groupedArray = a1.group(x => isNeg(x) ? 'neg' : 'pos'); + const mappedArray = a1.groupToMap(x => isNeg(x) ? 'neg' : 'pos'); + + assertEq(Object.getPrototypeOf(groupedArray), null) + assertDeepEq(groupedArray, expectedObj); + assertDeepEq(mappedArray.get("neg"), expectedObj["neg"]); + assertDeepEq(mappedArray.get("pos"), expectedObj["pos"]); + + + const expectedObj2 = {"undefined": [1,2,3]} + Object.setPrototypeOf(expectedObj2, null); + assertDeepEq([1,2,3].group(() => {}), expectedObj2); + assertDeepEq([].group(() => {}), Object.create(null)); + assertDeepEq(([1,2,3].groupToMap(() => {})).get(undefined), [1,2,3]); + assertEq(([1,2,3].groupToMap(() => {})).size, 1); + + const negMappedArray = a1.groupToMap(x => isNeg(x) ? -0 : 0); + assertDeepEq(negMappedArray.get(0), a1); + assertDeepEq(negMappedArray.size, 1); + + assertThrowsInstanceOf(() => [].group(undefined), TypeError); + assertThrowsInstanceOf(() => [].group(null), TypeError); + assertThrowsInstanceOf(() => [].group(0), TypeError); + assertThrowsInstanceOf(() => [].group(""), TypeError); + assertThrowsInstanceOf(() => [].groupToMap(undefined), TypeError); + assertThrowsInstanceOf(() => [].groupToMap(null), TypeError); + assertThrowsInstanceOf(() => [].groupToMap(0), TypeError); + assertThrowsInstanceOf(() => [].groupToMap(""), TypeError); +} + +const array = [ 'test' ]; +Object.defineProperty(Map.prototype, 4, { + get() { + throw new Error('monkey-patched Map get call'); + }, + set(v) { + throw new Error('monkey-patched Map set call'); + }, + has(v) { + throw new Error('monkey-patched Map has call'); + } +}); + +const map1 = array.groupToMap(key => key.length); + +assertEq('test', map1.get(4)[0]) + +Object.defineProperty(Array.prototype, '4', { + set(v) { + throw new Error('user observable array set'); + }, + get() { + throw new Error('user observable array get'); + } +}); + +const map2 = array.groupToMap(key => key.length); +const arr = array.group(key => key.length); + +assertEq('test', map2.get(4)[0]) +assertEq('test', arr[4][0]) + +Object.defineProperty(Object.prototype, "foo", { + get() { throw new Error("user observable object get"); }, + set(v) { throw new Error("user observable object set"); } +}); +[1, 2, 3].group(() => 'foo'); + +// Ensure property key is correctly accessed +count = 0; +p = [1].group(() => ({ toString() { count++; return 10 } })); +assertEq(count, 1); +[1].groupToMap(() => ({ toString() { count++; return 10 } })); +assertEq(count, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/includes-trailing-holes.js b/js/src/tests/non262/Array/includes-trailing-holes.js new file mode 100644 index 0000000000..b783cc36d8 --- /dev/null +++ b/js/src/tests/non262/Array/includes-trailing-holes.js @@ -0,0 +1,16 @@ +// Array with trailing hole as explicit "magic elements hole". +assertEq([,].includes(), true); +assertEq([,].includes(undefined), true); +assertEq([,].includes(undefined, 0), true); +assertEq([,].includes(null), false); +assertEq([,].includes(null, 0), false); + +// Array with trailing hole with no explicit "magic elements hole". +assertEq(Array(1).includes(), true); +assertEq(Array(1).includes(undefined), true); +assertEq(Array(1).includes(undefined, 0), true); +assertEq(Array(1).includes(null), false); +assertEq(Array(1).includes(null, 0), false); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/includes.js b/js/src/tests/non262/Array/includes.js new file mode 100644 index 0000000000..aa439571ce --- /dev/null +++ b/js/src/tests/non262/Array/includes.js @@ -0,0 +1,59 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var BUGNUMBER = 1069063; +var summary = "Implement Array.prototype.includes"; + +print(BUGNUMBER + ": " + summary); + +assertEq(typeof [].includes, "function"); +assertEq([].includes.length, 1); + +assertTrue([1, 2, 3].includes(2)); +assertTrue([1,,2].includes(2)); +assertTrue([1, 2, 3].includes(2, 1)); +assertTrue([1, 2, 3].includes(2, -2)); +assertTrue([1, 2, 3].includes(2, -100)); +assertTrue([Object, Function, Array].includes(Function)); +assertTrue([-0].includes(0)); +assertTrue([NaN].includes(NaN)); +assertTrue([,].includes()); +assertTrue(staticIncludes("123", "2")); +assertTrue(staticIncludes({length: 3, 1: 2}, 2)); +assertTrue(staticIncludes({length: 3, 1: 2, get 3(){throw ""}}, 2)); +assertTrue(staticIncludes({length: 3, get 1() {return 2}}, 2)); +assertTrue(staticIncludes({__proto__: {1: 2}, length: 3}, 2)); +assertTrue(staticIncludes(new Proxy([1], {get(){return 2}}), 2)); + +assertFalse([1, 2, 3].includes("2")); +assertFalse([1, 2, 3].includes(2, 2)); +assertFalse([1, 2, 3].includes(2, -1)); +assertFalse([undefined].includes(NaN)); +assertFalse([{}].includes({})); +assertFalse(staticIncludes({length: 3, 1: 2}, 2, 2)); +assertFalse(staticIncludes({length: 3, get 0(){delete this[1]}, 1: 2}, 2)); +assertFalse(staticIncludes({length: -100, 0: 1}, 1)); + +assertThrowsInstanceOf(() => staticIncludes(), TypeError); +assertThrowsInstanceOf(() => staticIncludes(null), TypeError); +assertThrowsInstanceOf(() => staticIncludes({get length(){throw TypeError()}}), TypeError); +assertThrowsInstanceOf(() => staticIncludes({length: 3, get 1() {throw TypeError()}}, 2), TypeError); +assertThrowsInstanceOf(() => staticIncludes({__proto__: {get 1() {throw TypeError()}}, length: 3}, 2), TypeError); +assertThrowsInstanceOf(() => staticIncludes(new Proxy([1], {get(){throw TypeError()}})), TypeError); + +function assertTrue(v) { + assertEq(v, true); +} + +function assertFalse(v) { + assertEq(v, false); +} + +function staticIncludes(o, v, f) { + return [].includes.call(o, v, f); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/index-with-null-character.js b/js/src/tests/non262/Array/index-with-null-character.js new file mode 100644 index 0000000000..51e71bc8a9 --- /dev/null +++ b/js/src/tests/non262/Array/index-with-null-character.js @@ -0,0 +1,18 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var testArray = [1, 2, 3] +assertEq(testArray['0' + '\0'], undefined); +assertEq(testArray['1' + '\0' + 'aaaa'], undefined) +assertEq(testArray['\0' + '2'], undefined); +assertEq(testArray['\0' + ' 2'], undefined); + +testArray['\0'] = 'hello'; +testArray[' \0'] = 'world'; +assertEq(testArray['\0'], 'hello'); +assertEq(testArray[' \0'], 'world'); + +if (typeof reportCompare == 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/indexOf-never-returns-negative-zero.js b/js/src/tests/non262/Array/indexOf-never-returns-negative-zero.js new file mode 100644 index 0000000000..0b39bf0a16 --- /dev/null +++ b/js/src/tests/non262/Array/indexOf-never-returns-negative-zero.js @@ -0,0 +1,4 @@ +assertEq([17].indexOf(17, -0), +0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/indexOf-packed-array.js b/js/src/tests/non262/Array/indexOf-packed-array.js new file mode 100644 index 0000000000..97c2da8855 --- /dev/null +++ b/js/src/tests/non262/Array/indexOf-packed-array.js @@ -0,0 +1,40 @@ +function makeArray(array) { + var log = []; + Object.setPrototypeOf(array, new Proxy(Array.prototype, new Proxy({ + has(t, pk) { + log.push(`Has:${String(pk)}`); + return Reflect.has(t, pk); + }, + }, { + get(t, pk, r) { + if (pk in t) + return Reflect.get(t, pk, r); + throw new Error(`Unexpected trap "${pk}" called`); + } + }))); + return {array, log}; +} + + +var {array, log} = makeArray([1, null, 3]); +Array.prototype.indexOf.call(array, 100, { + valueOf() { + array.length = 0; + return 0; + } +}); +assertEqArray(log, ["Has:0", "Has:1", "Has:2"]); + + +var {array, log} = makeArray([5, undefined, 7]); +Array.prototype.lastIndexOf.call(array, 100, { + valueOf() { + array.length = 0; + return 2; + } +}); +assertEqArray(log, ["Has:2", "Has:1", "Has:0"]); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/isArray.js b/js/src/tests/non262/Array/isArray.js new file mode 100644 index 0000000000..b752ad54e4 --- /dev/null +++ b/js/src/tests/non262/Array/isArray.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +var global = this; +var otherGlobal = newGlobal(); + +var thisGlobal = () => global; +var alternateGlobals = (function(i) { + return () => (i++ % 2) === 0 ? global : otherGlobal; +})(0); + +function performTests(pickGlobal) +{ + // Base case. + assertEq(Array.isArray([]), true); + + // Simple case: proxy to an array. + var proxy = new (pickGlobal()).Proxy([], {}); + assertEq(Array.isArray(proxy), true); + + // Recursive proxy ultimately terminating in an array. + for (var i = 0; i < 10; i++) { + proxy = new (pickGlobal()).Proxy(proxy, {}); + assertEq(Array.isArray(proxy), true); + } + + // Revocable proxy to an array. + var revocable = (pickGlobal()).Proxy.revocable([], {}); + proxy = revocable.proxy; + assertEq(Array.isArray(proxy), true); + + // Recursive proxy ultimately terminating in a revocable proxy to an array. + for (var i = 0; i < 10; i++) { + proxy = new (pickGlobal()).Proxy(proxy, {}); + assertEq(Array.isArray(proxy), true); + } + + // Revoked proxy to (formerly) an array. + revocable.revoke(); + assertThrowsInstanceOf(() => Array.isArray(revocable.proxy), TypeError); + + // Recursive proxy ultimately terminating in a revoked proxy to an array. + assertThrowsInstanceOf(() => Array.isArray(proxy), TypeError); + +} + +performTests(thisGlobal); +performTests(alternateGlobals); + +function crossGlobalTest() +{ + var array = new otherGlobal.Array(); + + // Array from another global. + assertEq(Array.isArray(array), true); + + // Proxy to an array from another global. + assertEq(Array.isArray(new Proxy(array, {})), true); + + // Other-global proxy to an array from that selfsame global. + assertEq(Array.isArray(new otherGlobal.Proxy(array, {})), true); +} + +crossGlobalTest(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/iterator_edge_cases.js b/js/src/tests/non262/Array/iterator_edge_cases.js new file mode 100644 index 0000000000..156aaba5f1 --- /dev/null +++ b/js/src/tests/non262/Array/iterator_edge_cases.js @@ -0,0 +1,50 @@ +// Test that we can't confuse %ArrayIteratorPrototype% for an +// ArrayIterator object. +function TestArrayIteratorPrototypeConfusion() { + var iter = [][Symbol.iterator](); + try { + iter.next.call(Object.getPrototypeOf(iter)) + throw new Error("Call did not throw"); + } catch (e) { + assertEq(e instanceof TypeError, true); + assertEq(e.message, "next method called on incompatible Array Iterator"); + } +} +TestArrayIteratorPrototypeConfusion(); + +// Tests that we can use %ArrayIteratorPrototype%.next on a +// cross-compartment iterator. +function TestArrayIteratorWrappers() { + var iter = [][Symbol.iterator](); + assertDeepEq(iter.next.call(newGlobal().eval('[5][Symbol.iterator]()')), + { value: 5, done: false }) +} +if (typeof newGlobal === "function") { + TestArrayIteratorWrappers(); +} + +// Tests that calling |next| on an array iterator after iteration has finished +// doesn't get the array's |length| property. +function TestIteratorNextGetLength() { + var lengthCalledTimes = 0; + var array = { + __proto__: Array.prototype, + get length() { + lengthCalledTimes += 1; + return { + valueOf() { + return 0; + } + }; + } + }; + var it = array[Symbol.iterator](); + it.next(); + it.next(); + assertEq(1, lengthCalledTimes); +} +TestIteratorNextGetLength(); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/join-01.js b/js/src/tests/non262/Array/join-01.js new file mode 100644 index 0000000000..3cc6cccd0d --- /dev/null +++ b/js/src/tests/non262/Array/join-01.js @@ -0,0 +1,83 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +print("ES5: Array.prototype.join"); + +/************** + * BEGIN TEST * + **************/ + +var count; +var stringifyCounter = { toString: function() { count++; return "obj"; } }; + +var arr = [1, 2, 3, 4, 5]; +assertEq(arr.join(), "1,2,3,4,5"); +assertEq(arr.join(","), "1,2,3,4,5"); +assertEq(arr.join(undefined), "1,2,3,4,5"); +assertEq(arr.join(4), "142434445"); +assertEq(arr.join(""), "12345"); + +count = 0; +assertEq(arr.join(stringifyCounter), "1obj2obj3obj4obj5"); +assertEq(count, 1); + +var holey = [1, 2, , 4, 5]; +assertEq(holey.join(), "1,2,,4,5"); +assertEq(holey.join(","), "1,2,,4,5"); +assertEq(holey.join(undefined), "1,2,,4,5"); +assertEq(holey.join(4), "14244445"); + +count = 0; +assertEq(holey.join(stringifyCounter), "1obj2objobj4obj5"); +assertEq(count, 1); + +var nully = [1, 2, 3, null, 5]; +assertEq(nully.join(), "1,2,3,,5"); +assertEq(nully.join(","), "1,2,3,,5"); +assertEq(nully.join(undefined), "1,2,3,,5"); +assertEq(nully.join(4), "14243445"); + +count = 0; +assertEq(nully.join(stringifyCounter), "1obj2obj3objobj5"); +assertEq(count, 1); + +var undefiney = [1, undefined, 3, 4, 5]; +assertEq(undefiney.join(), "1,,3,4,5"); +assertEq(undefiney.join(","), "1,,3,4,5"); +assertEq(undefiney.join(undefined), "1,,3,4,5"); +assertEq(undefiney.join(4), "14434445"); + +count = 0; +assertEq(undefiney.join(stringifyCounter), "1objobj3obj4obj5"); +assertEq(count, 1); + +var log = ''; +arr = {length: {valueOf: function () { log += "L"; return 2; }}, + 0: "x", 1: "z"}; +var sep = {toString: function () { log += "S"; return "y"; }}; +assertEq(Array.prototype.join.call(arr, sep), "xyz"); +assertEq(log, "LS"); + +var funky = + { + toString: function() + { + Array.prototype[1] = "chorp"; + Object.prototype[3] = "fnord"; + return "funky"; + } + }; +var trailingHoles = [0, funky, /* 2 */, /* 3 */,]; +assertEq(trailingHoles.join(""), "0funkyfnord"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/join-no-has-trap.js b/js/src/tests/non262/Array/join-no-has-trap.js new file mode 100644 index 0000000000..16b8cbedb1 --- /dev/null +++ b/js/src/tests/non262/Array/join-no-has-trap.js @@ -0,0 +1,36 @@ +// Test that Array.prototype.join doesn't call the [[HasProperty]] internal +// method of objects. + +var log = []; +var array = []; +var proxy = new Proxy(array, new Proxy({}, { + get(t, trap, r) { + return (t, pk, ...more) => { + log.push(`${trap}:${String(pk)}`); + return Reflect[trap](t, pk, ...more); + }; + } +})); + +var result; + +result = Array.prototype.join.call(proxy); +assertEqArray(log, [ "get:length" ]); +assertEq(result, ""); + +log.length = 0; +array.push(1); + +result = Array.prototype.join.call(proxy); +assertEqArray(log, [ "get:length", "get:0" ]); +assertEq(result, "1"); + +log.length = 0; +array.push(2); + +result = Array.prototype.join.call(proxy); +assertEqArray(log, [ "get:length", "get:0", "get:1" ]); +assertEq(result, "1,2"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/lastIndexOf-never-returns-negative-zero.js b/js/src/tests/non262/Array/lastIndexOf-never-returns-negative-zero.js new file mode 100644 index 0000000000..296f87ec1f --- /dev/null +++ b/js/src/tests/non262/Array/lastIndexOf-never-returns-negative-zero.js @@ -0,0 +1,4 @@ +assertEq([17].lastIndexOf(17, -0), +0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/length-01.js b/js/src/tests/non262/Array/length-01.js new file mode 100644 index 0000000000..4fe81e2b6e --- /dev/null +++ b/js/src/tests/non262/Array/length-01.js @@ -0,0 +1,71 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 600392; +var summary = + 'Object.preventExtensions([]).length = 0 should do nothing, not throw'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + + +function testEmpty() +{ + var a = []; + assertEq(a.length, 0); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 0); + a.length = 0; + assertEq(a.length, 0); +} +testEmpty(); + +function testEmptyStrict() +{ + "use strict"; + var a = []; + assertEq(a.length, 0); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 0); + a.length = 0; + assertEq(a.length, 0); +} +testEmptyStrict(); + +function testNonEmpty() +{ + var a = [1, 2, 3]; + assertEq(a.length, 3); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 3); + a.length = 0; + assertEq(a.length, 0); +} +testNonEmpty(); + +function testNonEmptyStrict() +{ + "use strict"; + var a = [1, 2, 3]; + assertEq(a.length, 3); + assertEq(Object.preventExtensions(a), a); + assertEq(a.length, 3); + a.length = 0; + assertEq(a.length, 0); +} +testNonEmptyStrict(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/length-nonwritable-redefine-nop.js b/js/src/tests/non262/Array/length-nonwritable-redefine-nop.js new file mode 100644 index 0000000000..1d7fe32c8e --- /dev/null +++ b/js/src/tests/non262/Array/length-nonwritable-redefine-nop.js @@ -0,0 +1,70 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858381; +var summary = "No-op array length redefinition"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr; + +// initializedLength == capacity == length +// 6 == 6 == 6 +arr = Object.defineProperty([0, 1, 2, 3, 4, 5], "length", { writable: false }); +Object.defineProperty(arr, "length", { value: 6 }); +Object.defineProperty(arr, "length", { writable: false }); +Object.defineProperty(arr, "length", { configurable: false }); +Object.defineProperty(arr, "length", { writable: false, configurable: false }); +Object.defineProperty(arr, "length", { writable: false, value: 6 }); +Object.defineProperty(arr, "length", { configurable: false, value: 6 }); +Object.defineProperty(arr, "length", { writable: false, configurable: false, value: 6 }); + +// initializedLength == capacity < length +// 6 == 6 < 8 +arr = Object.defineProperty([0, 1, 2, 3, 4, 5], "length", { value: 8, writable: false }); +Object.defineProperty(arr, "length", { value: 8 }); +Object.defineProperty(arr, "length", { writable: false }); +Object.defineProperty(arr, "length", { configurable: false }); +Object.defineProperty(arr, "length", { writable: false, configurable: false }); +Object.defineProperty(arr, "length", { writable: false, value: 8 }); +Object.defineProperty(arr, "length", { configurable: false, value: 8 }); +Object.defineProperty(arr, "length", { writable: false, configurable: false, value: 8 }); + +// initializedLength < capacity == length +// 7 < 8 == 8 +arr = Object.defineProperty([0, 1, 2, 3, 4, 5, 6, /* hole */, ], "length", + { value: 8, writable: false }); +Object.defineProperty(arr, "length", { value: 8 }); +Object.defineProperty(arr, "length", { writable: false }); +Object.defineProperty(arr, "length", { configurable: false }); +Object.defineProperty(arr, "length", { writable: false, configurable: false }); +Object.defineProperty(arr, "length", { writable: false, value: 8 }); +Object.defineProperty(arr, "length", { configurable: false, value: 8 }); +Object.defineProperty(arr, "length", { writable: false, configurable: false, value: 8 }); + +// initializedLength < capacity < length +// 3 < 6 < 8 +arr = Object.defineProperty([0, 1, 2], "length", { value: 8, writable: false }); +Object.defineProperty(arr, "length", { value: 8 }); +Object.defineProperty(arr, "length", { writable: false }); +Object.defineProperty(arr, "length", { configurable: false }); +Object.defineProperty(arr, "length", { writable: false, configurable: false }); +Object.defineProperty(arr, "length", { writable: false, value: 8 }); +Object.defineProperty(arr, "length", { configurable: false, value: 8 }); +Object.defineProperty(arr, "length", { writable: false, configurable: false, value: 8 }); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/length-set-object.js b/js/src/tests/non262/Array/length-set-object.js new file mode 100644 index 0000000000..bf80b592ea --- /dev/null +++ b/js/src/tests/non262/Array/length-set-object.js @@ -0,0 +1,68 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 657298; +var summary = 'Various quirks of setting array length properties to objects'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function invokeConversionTwice1() +{ + var count = 0; + [].length = { valueOf: function() { count++; return 1; } }; + assertEq(count, 2); +} +invokeConversionTwice1(); + +function invokeConversionTwice2() +{ + var count = 0; + [].length = { toString: function() { count++; return 1; }, valueOf: null }; + assertEq(count, 2); +} +invokeConversionTwice2(); + +function dontOverwriteError1() +{ + try + { + [].length = { valueOf: {}, toString: {} }; + throw new Error("didn't throw a TypeError"); + } + catch (e) + { + assertEq(e instanceof TypeError, true, + "expected a TypeError running out of conversion options, got " + e); + } +} +dontOverwriteError1(); + +function dontOverwriteError2() +{ + try + { + [].length = { valueOf: function() { throw "error"; } }; + throw new Error("didn't throw a TypeError"); + } + catch (e) + { + assertEq(e, "error", "expected 'error' from failed conversion, got " + e); + } +} +dontOverwriteError2(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/length-truncate-nonconfigurable-sparse.js b/js/src/tests/non262/Array/length-truncate-nonconfigurable-sparse.js new file mode 100644 index 0000000000..a51fd40898 --- /dev/null +++ b/js/src/tests/non262/Array/length-truncate-nonconfigurable-sparse.js @@ -0,0 +1,110 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858381; +var summary = + "Array length redefinition behavior with non-configurable elements"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function addDataProperty(obj, prop, value, enumerable, configurable, writable) +{ + var desc = + { enumerable: enumerable, + configurable: configurable, + writable: writable, + value: value }; + Object.defineProperty(obj, prop, desc); +} + +function nonstrict() +{ + var arr = [0, , 2, , , 5]; + + addDataProperty(arr, 31415926, "foo", true, true, true); + addDataProperty(arr, 123456789, "bar", true, true, false); + addDataProperty(arr, 8675309, "qux", false, true, true); + addDataProperty(arr, 1735039, "eit", false, true, false); + addDataProperty(arr, 987654321, "fun", false, true, false); + + // non-array indexes to spice things up + addDataProperty(arr, "foopy", "sdfsd", false, false, false); + addDataProperty(arr, 4294967296, "psych", true, false, false); + addDataProperty(arr, 4294967295, "psych", true, false, false); + + addDataProperty(arr, 27182818, "eep", false, false, false); + + // Truncate...but only as far as possible. + arr.length = 1; + + assertEq(arr.length, 27182819); + + var props = Object.getOwnPropertyNames(arr).sort(); + var expected = + ["0", "2", "5", "1735039", "8675309", "27182818", + "foopy", "4294967296", "4294967295", "length"].sort(); + + assertEq(props.length, expected.length); + for (var i = 0; i < props.length; i++) + assertEq(props[i], expected[i], "unexpected property: " + props[i]); +} +nonstrict(); + +function strict() +{ + "use strict"; + + var arr = [0, , 2, , , 5]; + + addDataProperty(arr, 31415926, "foo", true, true, true); + addDataProperty(arr, 123456789, "bar", true, true, false); + addDataProperty(arr, 8675309, "qux", false, true, true); + addDataProperty(arr, 1735039, "eit", false, true, false); + addDataProperty(arr, 987654321, "fun", false, true, false); + + // non-array indexes to spice things up + addDataProperty(arr, "foopy", "sdfsd", false, false, false); + addDataProperty(arr, 4294967296, "psych", true, false, false); + addDataProperty(arr, 4294967295, "psych", true, false, false); + + addDataProperty(arr, 27182818, "eep", false, false, false); + + try + { + arr.length = 1; + throw new Error("didn't throw?!"); + } + catch (e) + { + assertEq(e instanceof TypeError, true, + "non-configurable property should trigger TypeError, got " + e); + } + + assertEq(arr.length, 27182819); + + var props = Object.getOwnPropertyNames(arr).sort(); + var expected = + ["0", "2", "5", "1735039", "8675309", "27182818", + "foopy", "4294967296", "4294967295", "length"].sort(); + + assertEq(props.length, expected.length); + for (var i = 0; i < props.length; i++) + assertEq(props[i], expected[i], "unexpected property: " + props[i]); +} +strict(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/length-truncate-nonconfigurable.js b/js/src/tests/non262/Array/length-truncate-nonconfigurable.js new file mode 100644 index 0000000000..e9a66fe0ac --- /dev/null +++ b/js/src/tests/non262/Array/length-truncate-nonconfigurable.js @@ -0,0 +1,48 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858381; +var summary = + "Array length redefinition behavior with non-configurable elements"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr = [0, 1, 2]; +Object.defineProperty(arr, 1, { configurable: false }); + +try +{ + Object.defineProperty(arr, "length", { value: 0, writable: false }); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "must throw TypeError when array truncation would have to remove " + + "non-configurable elements"); +} + +assertEq(arr.length, 2, "length is highest remaining index plus one"); + +var desc = Object.getOwnPropertyDescriptor(arr, "length"); +assertEq(desc !== undefined, true); + +assertEq(desc.value, 2); +assertEq(desc.writable, false); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, false); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/length-truncate-with-indexed.js b/js/src/tests/non262/Array/length-truncate-with-indexed.js new file mode 100644 index 0000000000..9e4da812aa --- /dev/null +++ b/js/src/tests/non262/Array/length-truncate-with-indexed.js @@ -0,0 +1,101 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858381; +var summary = + "Array length setting/truncating with non-dense, indexed elements"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function testTruncateDenseAndSparse() +{ + var arr; + + // initialized length 16, capacity same + arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + + // plus a sparse element + arr[987654321] = 987654321; + + // lop off the sparse element and half the dense elements, shrink capacity + arr.length = 8; + + assertEq(987654321 in arr, false); + assertEq(arr[987654321], undefined); + assertEq(arr.length, 8); +} +testTruncateDenseAndSparse(); + +function testTruncateSparse() +{ + // initialized length 8, capacity same + var arr = [0, 1, 2, 3, 4, 5, 6, 7]; + + // plus a sparse element + arr[987654321] = 987654321; + + // lop off the sparse element, leave initialized length/capacity unchanged + arr.length = 8; + + assertEq(987654321 in arr, false); + assertEq(arr[987654321], undefined); + assertEq(arr.length, 8); +} +testTruncateSparse(); + +function testTruncateDenseAndSparseShrinkCapacity() +{ + // initialized length 11, capacity...somewhat larger, likely 16 + var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + // plus a sparse element + arr[987654321] = 987654321; + + // lop off the sparse element, reduce initialized length, reduce capacity + arr.length = 8; + + assertEq(987654321 in arr, false); + assertEq(arr[987654321], undefined); + assertEq(arr.length, 8); +} +testTruncateDenseAndSparseShrinkCapacity(); + +function testTruncateSparseShrinkCapacity() +{ + // initialized length 8, capacity same + var arr = [0, 1, 2, 3, 4, 5, 6, 7]; + + // capacity expands to accommodate, initialized length remains same (not equal + // to capacity or length) + arr[15] = 15; + + // now no elements past initialized length + delete arr[15]; + + // ...except a sparse element + arr[987654321] = 987654321; + + // trims sparse element, doesn't change initialized length, shrinks capacity + arr.length = 8; + + assertEq(987654321 in arr, false); + assertEq(arr[987654321], undefined); + assertEq(arr.length, 8); +} +testTruncateSparseShrinkCapacity(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/pop-empty-nonwritable.js b/js/src/tests/non262/Array/pop-empty-nonwritable.js new file mode 100644 index 0000000000..f42ca98f5c --- /dev/null +++ b/js/src/tests/non262/Array/pop-empty-nonwritable.js @@ -0,0 +1,32 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858381; +var summary = 'Object.freeze([]).pop() must throw a TypeError'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +try +{ + Object.freeze([]).pop(); + throw new Error("didn't throw"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, + "should have thrown TypeError, instead got: " + e); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/pop-no-has-trap.js b/js/src/tests/non262/Array/pop-no-has-trap.js new file mode 100644 index 0000000000..11bb93ddd0 --- /dev/null +++ b/js/src/tests/non262/Array/pop-no-has-trap.js @@ -0,0 +1,58 @@ +// Test that Array.prototype.pop doesn't call the [[HasProperty]] internal +// method of objects when retrieving the element at the last index. + +var log = []; +var array = []; +var proxy = new Proxy(array, new Proxy({}, { + get(t, trap, r) { + return (t, pk, ...more) => { + log.push(`${trap}:${String(pk)}`); + return Reflect[trap](t, pk, ...more); + }; + } +})); + +var result; + +result = Array.prototype.pop.call(proxy); +assertEqArray(log, [ + "get:length", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, undefined); + +log.length = 0; +array.push(1); + +result = Array.prototype.pop.call(proxy); +assertEqArray(log, [ + "get:length", + "get:0", "deleteProperty:0", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 1); + +log.length = 0; +array.push(2, 3); + +result = Array.prototype.pop.call(proxy); +assertEqArray(log, [ + "get:length", + "get:1", "deleteProperty:1", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 3); + +log.length = 0; +array.push(4, 5); + +result = Array.prototype.pop.call(proxy); +assertEqArray(log, [ + "get:length", + "get:2", "deleteProperty:2", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 5); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/pop-nonarray-higher-elements.js b/js/src/tests/non262/Array/pop-nonarray-higher-elements.js new file mode 100644 index 0000000000..052eb4230f --- /dev/null +++ b/js/src/tests/non262/Array/pop-nonarray-higher-elements.js @@ -0,0 +1,91 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 909602; +var summary = + "Array.prototype.pop shouldn't touch elements greater than length on " + + "non-arrays"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function doTest(obj, index) +{ + // print("testing " + JSON.stringify(obj) + " with index " + index); + assertEq(Array.prototype.pop.call(obj), undefined); + assertEq(index in obj, true); + assertEq(obj[index], 42); +} + +// not-super-much-later element + +// non-zero length +function testPop1() +{ + var obj = { length: 2, 3: 42 }; + doTest(obj, 3); +} +for (var i = 0; i < 50; i++) + testPop1(); + +// zero length +function testPop2() +{ + var obj = { length: 0, 3: 42 }; + doTest(obj, 3); +} +for (var i = 0; i < 50; i++) + testPop2(); + +// much-later (but dense) element + +// non-zero length +function testPop3() +{ + var obj = { length: 2, 55: 42 }; + doTest(obj, 55); +} +for (var i = 0; i < 50; i++) + testPop3(); + +// zero length +function testPop4() +{ + var obj = { length: 0, 55: 42 }; + doTest(obj, 55); +} +for (var i = 0; i < 50; i++) + testPop4(); + +// much much much later (sparse) element + +// non-zero length +function testPop5() +{ + var obj = { length: 2, 65530: 42 }; + doTest(obj, 65530); +} +for (var i = 0; i < 50; i++) + testPop5(); + +// zero length +function testPop6() +{ + var obj = { length: 0, 65530: 42 }; + doTest(obj, 65530); +} +for (var i = 0; i < 50; i++) + testPop6(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/redefine-length-accessor.js b/js/src/tests/non262/Array/redefine-length-accessor.js new file mode 100644 index 0000000000..339a3dd0fa --- /dev/null +++ b/js/src/tests/non262/Array/redefine-length-accessor.js @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var absent = {}; + +var getterValues = [absent, undefined, function(){}]; +var setterValues = [absent, undefined, function(){}]; +var configurableValues = [absent, true, false]; +var enumerableValues = [absent, true, false]; + +function CreateDescriptor(getter, setter, enumerable, configurable) { + var descriptor = {}; + if (getter !== absent) + descriptor.get = getter; + if (setter !== absent) + descriptor.set = setter; + if (configurable !== absent) + descriptor.configurable = configurable; + if (enumerable !== absent) + descriptor.enumerable = enumerable; + return descriptor; +} + +getterValues.forEach(function(getter) { + setterValues.forEach(function(setter) { + enumerableValues.forEach(function(enumerable) { + configurableValues.forEach(function(configurable) { + var descriptor = CreateDescriptor(getter, setter, enumerable, configurable); + if (!("get" in descriptor || "set" in descriptor)) + return; + + assertThrowsInstanceOf(function() { + Object.defineProperty([], "length", descriptor); + }, TypeError); + }); + }); + }); +}); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/redefine-length-frozen-array.js b/js/src/tests/non262/Array/redefine-length-frozen-array.js new file mode 100644 index 0000000000..ca3b43018b --- /dev/null +++ b/js/src/tests/non262/Array/redefine-length-frozen-array.js @@ -0,0 +1,26 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 866580; +var summary = "Assertion redefining length property of a frozen array"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr = Object.freeze([]); +Object.defineProperty(arr, "length", {}); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/redefine-length-frozen-dictionarymode-array.js b/js/src/tests/non262/Array/redefine-length-frozen-dictionarymode-array.js new file mode 100644 index 0000000000..bca6e0cd72 --- /dev/null +++ b/js/src/tests/non262/Array/redefine-length-frozen-dictionarymode-array.js @@ -0,0 +1,36 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 880591; +var summary = + "Assertion redefining length property of a frozen dictionary-mode array"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function convertToDictionaryMode(arr) +{ + Object.defineProperty(arr, 0, { configurable: true }); + Object.defineProperty(arr, 1, { configurable: true }); + delete arr[0]; +} + +var arr = []; +convertToDictionaryMode(arr); +Object.freeze(arr); +Object.defineProperty(arr, "length", {}); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-call-counts.js b/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-call-counts.js new file mode 100644 index 0000000000..b77f396751 --- /dev/null +++ b/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-call-counts.js @@ -0,0 +1,45 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 866700; +var summary = "Assertion redefining non-writable length to a non-numeric value"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var count = 0; + +var convertible = + { + valueOf: function() + { + count++; + return 0; + } + }; + +var arr = []; +Object.defineProperty(arr, "length", { value: 0, writable: false }); + +Object.defineProperty(arr, "length", { value: convertible }); +assertEq(count, 2); + +Object.defineProperty(arr, "length", { value: convertible }); +assertEq(count, 4); + +assertEq(arr.length, 0); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-throw.js b/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-throw.js new file mode 100644 index 0000000000..f3003a6bdb --- /dev/null +++ b/js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-throw.js @@ -0,0 +1,58 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 866700; +var summary = "Assertion redefining non-writable length to a non-numeric value"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var count = 0; + +var convertible = + { + valueOf: function() + { + count++; + if (count > 2) + return 0; + throw new SyntaxError("fnord"); + } + }; + +var arr = []; +Object.defineProperty(arr, "length", { value: 0, writable: false }); + +try +{ + Object.defineProperty(arr, "length", + { + value: convertible, + writable: true, + configurable: true, + enumerable: true + }); + throw new Error("didn't throw"); +} +catch (e) +{ + assertEq(e instanceof SyntaxError, true, "expected SyntaxError, got " + e); +} + +assertEq(count, 1); +assertEq(arr.length, 0); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/redefine-nonwritable-length-nonnumeric.js b/js/src/tests/non262/Array/redefine-nonwritable-length-nonnumeric.js new file mode 100644 index 0000000000..aab9c1d5d2 --- /dev/null +++ b/js/src/tests/non262/Array/redefine-nonwritable-length-nonnumeric.js @@ -0,0 +1,32 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 866700; +var summary = "Assertion redefining non-writable length to a non-numeric value"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr = []; +Object.defineProperty(arr, "length", { value: 0, writable: false }); + +// Per Array's magical behavior, the value in the descriptor gets canonicalized +// *before* SameValue comparisons occur, so this shouldn't throw. +Object.defineProperty(arr, "length", { value: '' }); + +assertEq(arr.length, 0); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/regress-101488.js b/js/src/tests/non262/Array/regress-101488.js new file mode 100644 index 0000000000..f8372e7d92 --- /dev/null +++ b/js/src/tests/non262/Array/regress-101488.js @@ -0,0 +1,135 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Date: 24 September 2001 + * + * SUMMARY: Try assigning arr.length = new Number(n) + * From correspondence with Igor Bukanov <igor@icesoft.no> + * See http://bugzilla.mozilla.org/show_bug.cgi?id=101488 + * + * Without the "new" keyword, assigning arr.length = Number(n) worked. + * But with it, Rhino was giving an error "Inappropriate array length" + * and SpiderMonkey was exiting without giving any error or return value - + * + * Comments on the Rhino code by igor@icesoft.no: + * + * jsSet_length requires that the new length value should be an instance + * of Number. But according to Ecma 15.4.5.1, item 12-13, an error should + * be thrown only if ToUint32(length_value) != ToNumber(length_value) + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 101488; +var summary = 'Try assigning arr.length = new Number(n)'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; +var arr = []; + + +status = inSection(1); +arr = Array(); +tryThis('arr.length = new Number(1);'); +actual = arr.length; +expect = 1; +addThis(); + +status = inSection(2); +arr = Array(5); +tryThis('arr.length = new Number(1);'); +actual = arr.length; +expect = 1; +addThis(); + +status = inSection(3); +arr = Array(); +tryThis('arr.length = new Number(17);'); +actual = arr.length; +expect = 17; +addThis(); + +status = inSection(4); +arr = Array(5); +tryThis('arr.length = new Number(17);'); +actual = arr.length; +expect = 17; +addThis(); + + +/* + * Also try the above with the "new" keyword before Array(). + * Array() and new Array() should be equivalent, by ECMA 15.4.1.1 + */ +status = inSection(5); +arr = new Array(); +tryThis('arr.length = new Number(1);'); +actual = arr.length; +expect = 1; +addThis(); + +status = inSection(6); +arr = new Array(5); +tryThis('arr.length = new Number(1);'); +actual = arr.length; +expect = 1; +addThis(); + +arr = new Array(); +tryThis('arr.length = new Number(17);'); +actual = arr.length; +expect = 17; +addThis(); + +status = inSection(7); +arr = new Array(5); +tryThis('arr.length = new Number(17);'); +actual = arr.length; +expect = 17; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function tryThis(s) +{ + try + { + eval(s); + } + catch(e) + { + // keep going + } +} + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-101964.js b/js/src/tests/non262/Array/regress-101964.js new file mode 100644 index 0000000000..4e82863b1c --- /dev/null +++ b/js/src/tests/non262/Array/regress-101964.js @@ -0,0 +1,83 @@ +// |reftest| random -- bogus perf test (bug 467263) +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/* + * Date: 27 September 2001 + * + * SUMMARY: Performance: truncating even very large arrays should be fast! + * See http://bugzilla.mozilla.org/show_bug.cgi?id=101964 + * + * Adjust this testcase if necessary. The FAST constant defines + * an upper bound in milliseconds for any truncation to take. + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 101964; +var summary = 'Performance: truncating even very large arrays should be fast!'; +var BIG = 10000000; +var LITTLE = 10; +var FAST = 50; // array truncation should be 50 ms or less to pass the test +var MSG_FAST = 'Truncation took less than ' + FAST + ' ms'; +var MSG_SLOW = 'Truncation took '; +var MSG_MS = ' ms'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + + +status = inSection(1); +var arr = Array(BIG); +var start = new Date(); +arr.length = LITTLE; +actual = elapsedTime(start); +expect = FAST; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function elapsedTime(startTime) +{ + return new Date() - startTime; +} + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = isThisFast(actual); + expectedvalues[UBound] = isThisFast(expect); + UBound++; +} + + +function isThisFast(ms) +{ + if (ms <= FAST) + return MSG_FAST; + return MSG_SLOW + ms + MSG_MS; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-107138.js b/js/src/tests/non262/Array/regress-107138.js new file mode 100644 index 0000000000..26d311fb5f --- /dev/null +++ b/js/src/tests/non262/Array/regress-107138.js @@ -0,0 +1,174 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Date: 29 October 2001 + * + * SUMMARY: Regression test for bug 107138 + * See http://bugzilla.mozilla.org/show_bug.cgi?id=107138 + * + * The bug: arr['1'] == undefined instead of arr['1'] == 'one'. + * The bug was intermittent and did not always occur... + * + * The cnSTRESS constant defines how many times to repeat this test. + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var cnSTRESS = 10; +var cnDASH = '-'; +var BUGNUMBER = 107138; +var summary = 'Regression test for bug 107138'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +var arr = ['zero', 'one', 'two', 'three', 'four', 'five', + 'six', 'seven', 'eight', 'nine', 'ten']; + + +// This bug was intermittent. Stress-test it. +for (var j=0; j<cnSTRESS; j++) +{ + status = inSection(j + cnDASH + 1); + actual = arr[0]; + expect = 'zero'; + addThis(); + + status = inSection(j + cnDASH + 2); + actual = arr['0']; + expect = 'zero'; + addThis(); + + status = inSection(j + cnDASH + 3); + actual = arr[1]; + expect = 'one'; + addThis(); + + status = inSection(j + cnDASH + 4); + actual = arr['1']; + expect = 'one'; + addThis(); + + status = inSection(j + cnDASH + 5); + actual = arr[2]; + expect = 'two'; + addThis(); + + status = inSection(j + cnDASH + 6); + actual = arr['2']; + expect = 'two'; + addThis(); + + status = inSection(j + cnDASH + 7); + actual = arr[3]; + expect = 'three'; + addThis(); + + status = inSection(j + cnDASH + 8); + actual = arr['3']; + expect = 'three'; + addThis(); + + status = inSection(j + cnDASH + 9); + actual = arr[4]; + expect = 'four'; + addThis(); + + status = inSection(j + cnDASH + 10); + actual = arr['4']; + expect = 'four'; + addThis(); + + status = inSection(j + cnDASH + 11); + actual = arr[5]; + expect = 'five'; + addThis(); + + status = inSection(j + cnDASH + 12); + actual = arr['5']; + expect = 'five'; + addThis(); + + status = inSection(j + cnDASH + 13); + actual = arr[6]; + expect = 'six'; + addThis(); + + status = inSection(j + cnDASH + 14); + actual = arr['6']; + expect = 'six'; + addThis(); + + status = inSection(j + cnDASH + 15); + actual = arr[7]; + expect = 'seven'; + addThis(); + + status = inSection(j + cnDASH + 16); + actual = arr['7']; + expect = 'seven'; + addThis(); + + status = inSection(j + cnDASH + 17); + actual = arr[8]; + expect = 'eight'; + addThis(); + + status = inSection(j + cnDASH + 18); + actual = arr['8']; + expect = 'eight'; + addThis(); + + status = inSection(j + cnDASH + 19); + actual = arr[9]; + expect = 'nine'; + addThis(); + + status = inSection(j + cnDASH + 20); + actual = arr['9']; + expect = 'nine'; + addThis(); + + status = inSection(j + cnDASH + 21); + actual = arr[10]; + expect = 'ten'; + addThis(); + + status = inSection(j + cnDASH + 22); + actual = arr['10']; + expect = 'ten'; + addThis(); +} + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-108440.js b/js/src/tests/non262/Array/regress-108440.js new file mode 100644 index 0000000000..d83c4164ac --- /dev/null +++ b/js/src/tests/non262/Array/regress-108440.js @@ -0,0 +1,77 @@ +// |reftest| skip-if(Android) -- bug - nsIDOMWindow.crypto throws NS_ERROR_NOT_IMPLEMENTED on Android +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 30 October 2001 + * SUMMARY: Regression test for bug 108440 + * See http://bugzilla.mozilla.org/show_bug.cgi?id=108440 + * + * We shouldn't crash trying to add an array as an element of itself (!) + * + * Brendan: "...it appears that Array.prototype.toString is unsafe, + * and what's more, ECMA-262 Edition 3 has no helpful words about + * avoiding recursive death on a cycle." + */ +//----------------------------------------------------------------------------- +var BUGNUMBER = 108440; +var summary = "Shouldn't crash trying to add an array as an element of itself"; +var self = this; +var temp = ''; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/* + * Explicit test: + */ +var a=[]; +temp = (a[a.length]=a); + +/* + * Implicit test (one of the properties of |self| is |a|) + */ +a=[]; +for(var prop in self) +{ + temp = prop; + temp = (a[a.length] = self[prop]); +} + +/* + * Stressful explicit test + */ +a=[]; +for (var i=0; i<10; i++) +{ + a[a.length] = a; +} + +/* + * Test toString() + */ +a=[]; +for (var i=0; i<10; i++) +{ + a[a.length] = a.toString(); +} + +/* + * Test toSource() - but Rhino doesn't have this, so try...catch it + */ +a=[]; +try +{ + for (var i=0; i<10; i++) + { + a[a.length] = a.toSource(); + } +} +catch(e) +{ +} + +reportCompare('No Crash', 'No Crash', ''); diff --git a/js/src/tests/non262/Array/regress-130451.js b/js/src/tests/non262/Array/regress-130451.js new file mode 100644 index 0000000000..3ccf3d9dff --- /dev/null +++ b/js/src/tests/non262/Array/regress-130451.js @@ -0,0 +1,182 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 25 Mar 2002 + * SUMMARY: Array.prototype.sort() should not (re-)define .length + * See http://bugzilla.mozilla.org/show_bug.cgi?id=130451 + * + * From the ECMA-262 Edition 3 Final spec: + * + * NOTE: The sort function is intentionally generic; it does not require that + * its |this| value be an Array object. Therefore, it can be transferred to + * other kinds of objects for use as a method. Whether the sort function can + * be applied successfully to a host object is implementation-dependent. + * + * The interesting parts of this testcase are the contrasting expectations for + * Brendan's test below, when applied to Array objects vs. non-Array objects. + * + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 130451; +var summary = 'Array.prototype.sort() should not (re-)define .length'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; +var arr = []; +var cmp = new Function(); + + +/* + * First: test Array.prototype.sort() on Array objects + */ +status = inSection(1); +arr = [0,1,2,3]; +cmp = function(x,y) {return x-y;}; +actual = arr.sort(cmp).length; +expect = 4; +addThis(); + +status = inSection(2); +arr = [0,1,2,3]; +cmp = function(x,y) {return y-x;}; +actual = arr.sort(cmp).length; +expect = 4; +addThis(); + +status = inSection(3); +arr = [0,1,2,3]; +cmp = function(x,y) {return x-y;}; +arr.length = 1; +actual = arr.sort(cmp).length; +expect = 1; +addThis(); + +/* + * This test is by Brendan. Setting arr.length to + * 2 and then 4 should cause elements to be deleted. + */ +arr = [0,1,2,3]; +cmp = function(x,y) {return x-y;}; +arr.sort(cmp); + +status = inSection(4); +actual = arr.join(); +expect = '0,1,2,3'; +addThis(); + +status = inSection(5); +actual = arr.length; +expect = 4; +addThis(); + +status = inSection(6); +arr.length = 2; +actual = arr.join(); +expect = '0,1'; +addThis(); + +status = inSection(7); +arr.length = 4; +actual = arr.join(); +expect = '0,1,,'; //<---- see how 2,3 have been lost +addThis(); + + + +/* + * Now test Array.prototype.sort() on non-Array objects + */ +status = inSection(8); +var obj = new Object(); +obj.sort = Array.prototype.sort; +obj.length = 4; +obj[0] = 0; +obj[1] = 1; +obj[2] = 2; +obj[3] = 3; +cmp = function(x,y) {return x-y;}; +actual = obj.sort(cmp).length; +expect = 4; +addThis(); + + +/* + * Here again is Brendan's test. Unlike the array case + * above, the setting of obj.length to 2 and then 4 + * should NOT cause elements to be deleted + */ +obj = new Object(); +obj.sort = Array.prototype.sort; +obj.length = 4; +obj[0] = 3; +obj[1] = 2; +obj[2] = 1; +obj[3] = 0; +cmp = function(x,y) {return x-y;}; +obj.sort(cmp); //<---- this is what triggered the buggy behavior below +obj.join = Array.prototype.join; + +status = inSection(9); +actual = obj.join(); +expect = '0,1,2,3'; +addThis(); + +status = inSection(10); +actual = obj.length; +expect = 4; +addThis(); + +status = inSection(11); +obj.length = 2; +actual = obj.join(); +expect = '0,1'; +addThis(); + +/* + * Before this bug was fixed, |actual| held the value '0,1,,' + * as in the Array-object case at top. This bug only occurred + * if Array.prototype.sort() had been applied to |obj|, + * as we have done higher up. + */ +status = inSection(12); +obj.length = 4; +actual = obj.join(); +expect = '0,1,2,3'; +addThis(); + + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-154338.js b/js/src/tests/non262/Array/regress-154338.js new file mode 100644 index 0000000000..e24610ea7b --- /dev/null +++ b/js/src/tests/non262/Array/regress-154338.js @@ -0,0 +1,87 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 26 June 2002 + * SUMMARY: Testing array.join() when separator is a variable, not a literal + * See http://bugzilla.mozilla.org/show_bug.cgi?id=154338 + * + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 154338; +var summary = 'Test array.join() when separator is a variable, not a literal'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +/* + * Note that x === 'H' and y === 'ome'. + * + * Yet for some reason, using |x| or |y| as the separator + * in arr.join() was causing out-of-memory errors, whereas + * using the literals 'H', 'ome' was not - + * + */ +var x = 'Home'[0]; +var y = ('Home'.split('H'))[1]; + + +status = inSection(1); +var arr = Array('a', 'b'); +actual = arr.join('H'); +expect = 'aHb'; +addThis(); + +status = inSection(2); +arr = Array('a', 'b'); +actual = arr.join(x); +expect = 'aHb'; +addThis(); + +status = inSection(3); +arr = Array('a', 'b'); +actual = arr.join('ome'); +expect = 'aomeb'; +addThis(); + +status = inSection(4); +arr = Array('a', 'b'); +actual = arr.join(y); +expect = 'aomeb'; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-157652.js b/js/src/tests/non262/Array/regress-157652.js new file mode 100644 index 0000000000..32996ca629 --- /dev/null +++ b/js/src/tests/non262/Array/regress-157652.js @@ -0,0 +1,117 @@ +// |reftest| skip-if(xulRuntime.XPCOMABI.match(/x86_64|aarch64|ppc64|ppc64le|s390x|mips64|loongarch64|riscv64/)||Android) -- No test results +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 16 July 2002 + * SUMMARY: Testing that Array.sort() doesn't crash on very large arrays + * See http://bugzilla.mozilla.org/show_bug.cgi?id=157652 + * + * How large can a JavaScript array be? + * ECMA-262 Ed.3 Final, Section 15.4.2.2 : new Array(len) + * + * This states that |len| must be a a uint32_t (unsigned 32-bit integer). + * Note the UBound for uint32's is 2^32 -1 = 0xFFFFFFFF = 4,294,967,295. + * + * Check: + * js> var arr = new Array(0xFFFFFFFF) + * js> arr.length + * 4294967295 + * + * js> var arr = new Array(0x100000000) + * RangeError: invalid array length + * + * + * We'll try the largest possible array first, then a couple others. + * We're just testing that we don't crash on Array.sort(). + * + * Try to be good about memory by nulling each array variable after it is + * used. This will tell the garbage collector the memory is no longer needed. + * + * As of 2002-08-13, the JS shell runs out of memory no matter what we do, + * when trying to sort such large arrays. + * + * We only want to test that we don't CRASH on the sort. So it will be OK + * if we get the JS "out of memory" error. Note this terminates the test + * with exit code 3. Therefore we put + * + * |expectExitCode(3);| + * + * The only problem will arise if the JS shell ever DOES have enough memory + * to do the sort. Then this test will terminate with the normal exit code 0 + * and fail. + * + * Right now, I can't see any other way to do this, because "out of memory" + * is not a catchable error: it cannot be trapped with try...catch. + * + * + * FURTHER HEADACHE: Rhino can't seem to handle the largest array: it hangs. + * So we skip this case in Rhino. Here is correspondence with Igor Bukanov. + * He explains that Rhino isn't actually hanging; it's doing the huge sort: + * + * Philip Schwartau wrote: + * + * > Hi, + * > + * > I'm getting a graceful OOM message on trying to sort certain large + * > arrays. But if the array is too big, Rhino simply hangs. Note that ECMA + * > allows array lengths to be anything less than Math.pow(2,32), so the + * > arrays I'm sorting are legal. + * > + * > Note below, I'm getting an instantaneous OOM error on arr.sort() for LEN + * > = Math.pow(2, 30). So shouldn't I also get one for every LEN between + * > that and Math.pow(2, 32)? For some reason, I start to hang with 100% CPU + * > as LEN hits, say, Math.pow(2, 31) and higher. SpiderMonkey gives OOM + * > messages for all of these. Should I file a bug on this? + * + * Igor Bukanov wrote: + * + * This is due to different sorting algorithm Rhino uses when sorting + * arrays with length > Integer.MAX_VALUE. If length can fit Java int, + * Rhino first copies internal spare array to a temporary buffer, and then + * sorts it, otherwise it sorts array directly. In case of very spare + * arrays, that Array(big_number) generates, it is rather inefficient and + * generates OutOfMemory if length fits int. It may be worth in your case + * to optimize sorting to take into account array spareness, but then it + * would be a good idea to file a bug about ineficient sorting of spare + * arrays both in case of Rhino and SpiderMonkey as SM always uses a + * temporary buffer. + * + */ +//----------------------------------------------------------------------------- +var BUGNUMBER = 157652; +var summary = "Testing that Array.sort() doesn't crash on very large arrays"; +var expect = 'No Crash'; +var actual = 'No Crash'; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +expectExitCode(0); +expectExitCode(5); + +try +{ + var a1=Array(0xFFFFFFFF); + a1.sort(); + a1 = null; + + var a2 = Array(0x40000000); + a2.sort(); + a2=null; + + var a3=Array(0x10000000/4); + a3.sort(); + a3=null; +} +catch(ex) +{ + // handle changed 1.9 branch behavior. see bug 422348 + expect = 'InternalError: allocation size overflow'; + actual = ex + ''; +} + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-178722.js b/js/src/tests/non262/Array/regress-178722.js new file mode 100644 index 0000000000..49c63bcbfe --- /dev/null +++ b/js/src/tests/non262/Array/regress-178722.js @@ -0,0 +1,127 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 06 November 2002 + * SUMMARY: arr.sort() should not output |undefined| when |arr| is empty + * See http://bugzilla.mozilla.org/show_bug.cgi?id=178722 + * + * ECMA-262 Ed.3: 15.4.4.11 Array.prototype.sort (comparefn) + * + * 1. Call the [[Get]] method of this object with argument "length". + * 2. Call ToUint32(Result(1)). + * 3. Perform an implementation-dependent sequence of calls to the [[Get]], + * [[Put]], and [[Delete]] methods of this object, etc. etc. + * 4. Return this object. + * + * + * Note that sort() is done in-place on |arr|. In other words, sort() is a + * "destructive" method rather than a "functional" method. The return value + * of |arr.sort()| and |arr| are the same object. + * + * If |arr| is an empty array, the return value of |arr.sort()| should be + * an empty array, not the value |undefined| as was occurring in bug 178722. + * + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 178722; +var summary = 'arr.sort() should not output |undefined| when |arr| is empty'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; +var arr; + + +// create empty array or pseudo-array objects in various ways +var arr1 = Array(); +var arr2 = new Array(); +var arr3 = []; +var arr4 = [1]; +arr4.pop(); + + +status = inSection(1); +arr = arr1.sort(); +actual = arr instanceof Array && arr.length === 0 && arr === arr1; +expect = true; +addThis(); + +status = inSection(2); +arr = arr2.sort(); +actual = arr instanceof Array && arr.length === 0 && arr === arr2; +expect = true; +addThis(); + +status = inSection(3); +arr = arr3.sort(); +actual = arr instanceof Array && arr.length === 0 && arr === arr3; +expect = true; +addThis(); + +status = inSection(4); +arr = arr4.sort(); +actual = arr instanceof Array && arr.length === 0 && arr === arr4; +expect = true; +addThis(); + +// now do the same thing, with non-default sorting: +function g() {return 1;} + +status = inSection('1a'); +arr = arr1.sort(g); +actual = arr instanceof Array && arr.length === 0 && arr === arr1; +expect = true; +addThis(); + +status = inSection('2a'); +arr = arr2.sort(g); +actual = arr instanceof Array && arr.length === 0 && arr === arr2; +expect = true; +addThis(); + +status = inSection('3a'); +arr = arr3.sort(g); +actual = arr instanceof Array && arr.length === 0 && arr === arr3; +expect = true; +addThis(); + +status = inSection('4a'); +arr = arr4.sort(g); +actual = arr instanceof Array && arr.length === 0 && arr === arr4; +expect = true; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/regress-255555.js b/js/src/tests/non262/Array/regress-255555.js new file mode 100644 index 0000000000..e4b2814b21 --- /dev/null +++ b/js/src/tests/non262/Array/regress-255555.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 255555; +var summary = 'Array.prototype.sort(comparefn) never passes undefined to comparefn'; +var actual = 'not undefined'; +var expect = 'not undefined'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +function comparefn(a,b) +{ + if (typeof a == 'undefined') + { + actual = 'undefined'; + return 1; + } + if (typeof b == 'undefined') + { + actual = 'undefined'; + return -1; + } + return a - b; +} + +var arry = [ 1, 2, undefined ].sort(comparefn) + + reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-290592.js b/js/src/tests/non262/Array/regress-290592.js new file mode 100644 index 0000000000..788459a4ef --- /dev/null +++ b/js/src/tests/non262/Array/regress-290592.js @@ -0,0 +1,661 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 290592; +var summary = 'Array extras: forEach, indexOf, filter, map'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +// Utility functions + +function identity(v, index, array) +{ + reportCompare(v, array[index], 'identity: check callback argument consistency'); + return v; +} + +function mutate(v, index, array) +{ + reportCompare(v, array[index], 'mutate: check callback argument consistency'); + if (index == 0) + { + array[1] = 'mutated'; + delete array[2]; + array.push('not visited'); + } + return v; +} + +function mutateForEach(v, index, array) +{ + reportCompare(v, array[index], 'mutateForEach: check callback argument consistency'); + if (index == 0) + { + array[1] = 'mutated'; + delete array[2]; + array.push('not visited'); + } + actual += v + ','; +} + +function makeUpperCase(v, index, array) +{ + reportCompare(v, array[index], 'makeUpperCase: check callback argument consistency'); + try + { + return v.toUpperCase(); + } + catch(e) + { + } + return v; +} + + +function concat(v, index, array) +{ + reportCompare(v, array[index], 'concat: check callback argument consistency'); + actual += v + ','; +} + + +function isUpperCase(v, index, array) +{ + reportCompare(v, array[index], 'isUpperCase: check callback argument consistency'); + try + { + return v == v.toUpperCase(); + } + catch(e) + { + } + return false; +} + +function isString(v, index, array) +{ + reportCompare(v, array[index], 'isString: check callback argument consistency'); + return typeof v == 'string'; +} + + +// callback object. +function ArrayCallback(state) +{ + this.state = state; +} + +ArrayCallback.prototype.makeUpperCase = function (v, index, array) +{ + reportCompare(v, array[index], 'ArrayCallback.prototype.makeUpperCase: check callback argument consistency'); + try + { + return this.state ? v.toUpperCase() : v.toLowerCase(); + } + catch(e) + { + } + return v; +}; + +ArrayCallback.prototype.concat = function(v, index, array) +{ + reportCompare(v, array[index], 'ArrayCallback.prototype.concat: check callback argument consistency'); + actual += v + ','; +}; + +ArrayCallback.prototype.isUpperCase = function(v, index, array) +{ + reportCompare(v, array[index], 'ArrayCallback.prototype.isUpperCase: check callback argument consistency'); + try + { + return this.state ? true : (v == v.toUpperCase()); + } + catch(e) + { + } + return false; +}; + +ArrayCallback.prototype.isString = function(v, index, array) +{ + reportCompare(v, array[index], 'ArrayCallback.prototype.isString: check callback argument consistency'); + return this.state ? true : (typeof v == 'string'); +}; + +function dumpError(e) +{ + var s = e.name + ': ' + e.message + + ' File: ' + e.fileName + + ', Line: ' + e.lineNumber + + ', Stack: ' + e.stack; + return s; +} + +var obj; +var strings = ['hello', 'Array', 'WORLD']; +var mixed = [0, '0', 0]; +var sparsestrings = new Array(); +sparsestrings[2] = 'sparse'; + +if ('map' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:map + + // test Array.map + + // map has 1 required argument + expect = 1; + actual = Array.prototype.map.length; + reportCompare(expect, actual, 'Array.prototype.map.length == 1'); + + // throw TypeError if no callback function specified + expect = 'TypeError'; + try + { + strings.map(); + actual = 'no error'; + } + catch(e) + { + actual = e.name; + } + reportCompare(expect, actual, 'Array.map(undefined) throws TypeError'); + + try + { + // identity map + expect = 'hello,Array,WORLD'; + actual = strings.map(identity).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: identity'); + + + try + { + expect = 'hello,mutated,'; + actual = strings.map(mutate).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: mutate'); + + strings = ['hello', 'Array', 'WORLD']; + + try + { + // general map + expect = 'HELLO,ARRAY,WORLD'; + actual = strings.map(makeUpperCase).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: uppercase'); + + try + { + // pass object method as map callback + expect = 'HELLO,ARRAY,WORLD'; + var obj = new ArrayCallback(true); + actual = strings.map(obj.makeUpperCase, obj).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: uppercase with object callback'); + + try + { + expect = 'hello,array,world'; + obj = new ArrayCallback(false); + actual = strings.map(obj.makeUpperCase, obj).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: lowercase with object callback'); + + try + { + // map on sparse arrays + expect = ',,SPARSE'; + actual = sparsestrings.map(makeUpperCase).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.map: uppercase on sparse array'); +} + +if ('forEach' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach + + // test Array.forEach + + // forEach has 1 required argument + expect = 1; + actual = Array.prototype.forEach.length; + reportCompare(expect, actual, 'Array.prototype.forEach.length == 1'); + + // throw TypeError if no callback function specified + expect = 'TypeError'; + try + { + strings.forEach(); + actual = 'no error'; + } + catch(e) + { + actual = e.name; + } + reportCompare(expect, actual, 'Array.forEach(undefined) throws TypeError'); + + try + { + // general forEach + expect = 'hello,Array,WORLD,'; + actual = ''; + strings.forEach(concat); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.forEach'); + + try + { + expect = 'hello,mutated,'; + actual = ''; + strings.forEach(mutateForEach); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.forEach: mutate'); + + strings = ['hello', 'Array', 'WORLD']; + + + + try + { + // pass object method as forEach callback + expect = 'hello,Array,WORLD,'; + actual = ''; + obj = new ArrayCallback(true); + strings.forEach(obj.concat, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.forEach with object callback 1'); + + try + { + expect = 'hello,Array,WORLD,'; + actual = ''; + obj = new ArrayCallback(false); + strings.forEach(obj.concat, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.forEach with object callback 2'); + + try + { + // test forEach on sparse arrays + // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 + expect = 'sparse,'; + actual = ''; + sparsestrings.forEach(concat); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.forEach on sparse array'); +} + +if ('filter' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter + + // test Array.filter + + // filter has 1 required argument + expect = 1; + actual = Array.prototype.filter.length; + reportCompare(expect, actual, 'Array.prototype.filter.length == 1'); + + // throw TypeError if no callback function specified + expect = 'TypeError'; + try + { + strings.filter(); + actual = 'no error'; + } + catch(e) + { + actual = e.name; + } + reportCompare(expect, actual, 'Array.filter(undefined) throws TypeError'); + + try + { + // test general filter + expect = 'WORLD'; + actual = strings.filter(isUpperCase).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.filter'); + + try + { + expect = 'WORLD'; + obj = new ArrayCallback(false); + actual = strings.filter(obj.isUpperCase, obj).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.filter object callback 1'); + + try + { + expect = 'hello,Array,WORLD'; + obj = new ArrayCallback(true); + actual = strings.filter(obj.isUpperCase, obj).toString(); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'Array.filter object callback 2'); +} + +if ('every' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:every + + // test Array.every + + // every has 1 required argument + + expect = 1; + actual = Array.prototype.every.length; + reportCompare(expect, actual, 'Array.prototype.every.length == 1'); + + // throw TypeError if no every callback function specified + expect = 'TypeError'; + try + { + strings.every(); + actual = 'no error'; + } + catch(e) + { + actual = e.name; + } + reportCompare(expect, actual, 'Array.every(undefined) throws TypeError'); + + // test general every + + try + { + expect = true; + actual = strings.every(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'strings: every element is a string'); + + try + { + expect = false; + actual = mixed.every(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'mixed: every element is a string'); + + try + { + // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 + expect = true; + actual = sparsestrings.every(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'sparsestrings: every element is a string'); + + // pass object method as map callback + + obj = new ArrayCallback(false); + + try + { + expect = true; + actual = strings.every(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'strings: every element is a string, via object callback'); + + try + { + expect = false; + actual = mixed.every(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e) ; + } + reportCompare(expect, actual, 'mixed: every element is a string, via object callback'); + + try + { + // see https://bugzilla.mozilla.org/show_bug.cgi?id=311082 + expect = true; + actual = sparsestrings.every(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'sparsestrings: every element is a string, via object callback'); + +} + +if ('some' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:some + + // test Array.some + + // some has 1 required argument + + expect = 1; + actual = Array.prototype.some.length; + reportCompare(expect, actual, 'Array.prototype.some.length == 1'); + + // throw TypeError if no some callback function specified + expect = 'TypeError'; + try + { + strings.some(); + actual = 'no error'; + } + catch(e) + { + actual = e.name; + } + reportCompare(expect, actual, 'Array.some(undefined) throws TypeError'); + + // test general some + + try + { + expect = true; + actual = strings.some(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'strings: some element is a string'); + + try + { + expect = true; + actual = mixed.some(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'mixed: some element is a string'); + + try + { + expect = true; + actual = sparsestrings.some(isString); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'sparsestrings: some element is a string'); + + // pass object method as map callback + + obj = new ArrayCallback(false); + + try + { + expect = true; + actual = strings.some(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'strings: some element is a string, via object callback'); + + try + { + expect = true; + actual = mixed.some(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'mixed: some element is a string, via object callback'); + + try + { + expect = true; + actual = sparsestrings.some(obj.isString, obj); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'sparsestrings: some element is a string, via object callback'); + +} + +if ('indexOf' in Array.prototype) +{ +// see http://developer-test.mozilla.org/docs/Core_JavaScript_1.5_Reference:Objects:Array:indexOf + + // test Array.indexOf + + // indexOf has 1 required argument + + expect = 1; + actual = Array.prototype.indexOf.length; + reportCompare(expect, actual, 'Array.prototype.indexOf.length == 1'); + + // test general indexOf + + try + { + expect = -1; + actual = mixed.indexOf('not found'); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'indexOf returns -1 if value not found'); + + try + { + expect = 0; + actual = mixed.indexOf(0); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'indexOf matches using strict equality'); + + try + { + expect = 1; + actual = mixed.indexOf('0'); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'indexOf matches using strict equality'); + + try + { + expect = 2; + actual = mixed.indexOf(0, 1); + } + catch(e) + { + actual = dumpError(e); + } + reportCompare(expect, actual, 'indexOf begins searching at fromIndex'); +} + diff --git a/js/src/tests/non262/Array/regress-299644.js b/js/src/tests/non262/Array/regress-299644.js new file mode 100644 index 0000000000..6a19cf54b9 --- /dev/null +++ b/js/src/tests/non262/Array/regress-299644.js @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 299644; +var summary = 'Arrays with holes'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +actual = (new Array(10).concat()).length; +expect = 10; +reportCompare(expect, actual, '(new Array(10).concat()).length == 10'); + +var a = new Array(10); +actual = true; +expect = true; +for (var p in a) +{ + actual = false; + break; +} +reportCompare(expect, actual, 'Array holes are not enumerable'); diff --git a/js/src/tests/non262/Array/regress-300858.js b/js/src/tests/non262/Array/regress-300858.js new file mode 100644 index 0000000000..2789375571 --- /dev/null +++ b/js/src/tests/non262/Array/regress-300858.js @@ -0,0 +1,21 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 300858; +var summary = 'Do not crash when sorting array with holes'; +var actual = 'No Crash'; +var expect = 'No Crash'; + +var arry = []; +arry[6] = 'six'; +arry[8] = 'eight'; +arry[9] = 'nine'; +arry[13] = 'thirteen'; +arry[14] = 'fourteen'; +arry[21] = 'twentyone'; +arry.sort(); + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-304828.js b/js/src/tests/non262/Array/regress-304828.js new file mode 100644 index 0000000000..0b15e21d28 --- /dev/null +++ b/js/src/tests/non262/Array/regress-304828.js @@ -0,0 +1,256 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 304828; +var summary = 'Array Generic Methods'; +var actual = ''; +var expect = ''; +printBugNumber(BUGNUMBER); +printStatus (summary); + +var value; + +// use Array methods on a String +// join +value = '123'; +expect = '1,2,3'; +try +{ + actual = Array.prototype.join.call(value); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': join'); + +// reverse +value = '123'; +expect = 'TypeError: 0 is read-only'; +try +{ + actual = Array.prototype.reverse.call(value) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': reverse'); + +// sort +value = 'cba'; +expect = 'TypeError: 0 is read-only'; +try +{ + actual = Array.prototype.sort.call(value) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': sort'); + +// push +value = 'abc'; +expect = 6; +try +{ + Array.prototype.push.call(value, 'd', 'e', 'f'); + throw new Error("didn't throw"); +} +catch(e) +{ + reportCompare(true, e instanceof TypeError, + "push on a string primitive should throw TypeError"); +} +reportCompare('abc', value, summary + ': push string primitive'); + +value = new String("abc"); +expect = 6; +try +{ + Array.prototype.push.call(value, 'd', 'e', 'f'); + throw new Error("didn't throw"); +} +catch(e) +{ + reportCompare(true, e instanceof TypeError, + "push on a String object should throw TypeError"); +} +reportCompare("d", value[3], summary + ': push String object index 3'); +reportCompare("e", value[4], summary + ': push String object index 4'); +reportCompare("f", value[5], summary + ': push String object index 5'); + +// pop +value = 'abc'; +expect = "TypeError: property 2 is non-configurable and can't be deleted"; +try +{ + actual = Array.prototype.pop.call(value); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': pop'); +reportCompare('abc', value, summary + ': pop'); + +// unshift +value = 'def'; +expect = 'TypeError: 0 is read-only'; +try +{ + actual = Array.prototype.unshift.call(value, 'a', 'b', 'c'); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': unshift'); +reportCompare('def', value, summary + ': unshift'); + +// shift +value = 'abc'; +expect = 'TypeError: 0 is read-only'; +try +{ + actual = Array.prototype.shift.call(value); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': shift'); +reportCompare('abc', value, summary + ': shift'); + +// splice +value = 'abc'; +expect = 'TypeError: 1 is read-only'; +try +{ + actual = Array.prototype.splice.call(value, 1, 1) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': splice'); + +// concat +value = 'abc'; +expect = 'abc,d,e,f'; +try +{ + actual = Array.prototype.concat.call(value, 'd', 'e', 'f') + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': concat'); + +// slice +value = 'abc'; +expect = 'b'; +try +{ + actual = Array.prototype.slice.call(value, 1, 2) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': slice'); + +// indexOf +value = 'abc'; +expect = 1; +try +{ + actual = Array.prototype.indexOf.call(value, 'b'); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': indexOf'); + +// lastIndexOf +value = 'abcabc'; +expect = 4; +try +{ + actual = Array.prototype.lastIndexOf.call(value, 'b'); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': lastIndexOf'); + +// forEach +value = 'abc'; +expect = 'ABC'; +actual = ''; +try +{ + Array.prototype.forEach.call(value, + function (v, index, array) + {actual += array[index].toUpperCase();}); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': forEach'); + +// map +value = 'abc'; +expect = 'A,B,C'; +try +{ + actual = Array.prototype.map.call(value, + function (v, index, array) + {return v.toUpperCase();}) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': map'); + +// filter +value = '1234567890'; +expect = '2,4,6,8,0'; +try +{ + actual = Array.prototype.filter.call(value, + function (v, index, array) + {return array[index] % 2 == 0;}) + ''; +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': filter'); + +// every +value = '1234567890'; +expect = false; +try +{ + actual = Array.prototype.every.call(value, + function (v, index, array) + {return array[index] % 2 == 0;}); +} +catch(e) +{ + actual = e + ''; +} +reportCompare(expect, actual, summary + ': every'); + + + diff --git a/js/src/tests/non262/Array/regress-305002.js b/js/src/tests/non262/Array/regress-305002.js new file mode 100644 index 0000000000..e59668e7f8 --- /dev/null +++ b/js/src/tests/non262/Array/regress-305002.js @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 305002; +var summary = '[].every(f) == true'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var notcalled = true; + +function callback() +{ + notcalled = false; +} + +expect = true; +actual = [].every(callback) && notcalled; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-310351.js b/js/src/tests/non262/Array/regress-310351.js new file mode 100644 index 0000000000..40401c5369 --- /dev/null +++ b/js/src/tests/non262/Array/regress-310351.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 310351; +var summary = 'Convert host "list" objects to arrays'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var nodeList = []; +if (typeof document != 'undefined') +{ + nodeList = document.getElementsByTagName('*'); +} +else +{ + printStatus('test using dummy array since no document available'); +} + +var array = Array.prototype.slice.call(nodeList, 0); + +expect = 'Array'; +actual = array.constructor.name; + +// nodeList is live and may change +var saveLength = nodeList.length; + +reportCompare(expect, actual, summary + ': constructor test'); + +expect = saveLength; +actual = array.length; + +reportCompare(expect, actual, summary + ': length test'); +expect = true; +actual = true; + +for (var i = 0; i < saveLength; i++) +{ + if (array[i] != nodeList[i]) + { + actual = false; + summary += ' Comparison failed: array[' + i + ']=' + array[i] + + ', nodeList[' + i + ']=' + nodeList[i]; + break; + } +} + +reportCompare(expect, actual, summary + ': identical elements test'); + diff --git a/js/src/tests/non262/Array/regress-310425-01.js b/js/src/tests/non262/Array/regress-310425-01.js new file mode 100644 index 0000000000..a60d6660a4 --- /dev/null +++ b/js/src/tests/non262/Array/regress-310425-01.js @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 310425; +var summary = 'Array.indexOf/lastIndexOf edge cases'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +expect = -1; +actual = [].lastIndexOf(undefined, -1); +reportCompare(expect, actual, summary); + +expect = -1; +actual = [].indexOf(undefined, -1); +reportCompare(expect, actual, summary); + +var x = []; +x[1 << 30] = 1; +expect = (1 << 30); +actual = x.lastIndexOf(1); +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-310425-02.js b/js/src/tests/non262/Array/regress-310425-02.js new file mode 100644 index 0000000000..ff9e88ae4e --- /dev/null +++ b/js/src/tests/non262/Array/regress-310425-02.js @@ -0,0 +1,17 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 310425; +var summary = 'Array.indexOf/lastIndexOf edge cases'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +expect = -1; +actual = Array(1).indexOf(1); +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-311515.js b/js/src/tests/non262/Array/regress-311515.js new file mode 100644 index 0000000000..c6d4928ce7 --- /dev/null +++ b/js/src/tests/non262/Array/regress-311515.js @@ -0,0 +1,20 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 311515; +var summary = 'Array.sort should skip holes and undefined during sort'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var a = [, 1, , 2, undefined]; + +actual = a.sort().toString(); +expect = '1,2,,,'; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-315509-01.js b/js/src/tests/non262/Array/regress-315509-01.js new file mode 100644 index 0000000000..7d29c5946f --- /dev/null +++ b/js/src/tests/non262/Array/regress-315509-01.js @@ -0,0 +1,28 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 315509; +var summary = 'Array.prototype.unshift on Arrays with holes'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var a = [0,1,2,3,4]; +delete a[1]; + +expect = '0,,2,3,4'; +actual = a.toString(); + +reportCompare(expect, actual, summary); + +a.unshift('a','b'); + +expect = 'a,b,0,,2,3,4'; +actual = a.toString(); + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-322135-01.js b/js/src/tests/non262/Array/regress-322135-01.js new file mode 100644 index 0000000000..2f65724ebc --- /dev/null +++ b/js/src/tests/non262/Array/regress-322135-01.js @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 322135; +var summary = 'Array.prototype.push on Array with length 2^32-1'; +var actual = 'Completed'; +var expect = 'Completed'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +printStatus('This bug passes if it does not cause an out of memory error'); +printStatus('Other issues related to array length are not tested.'); + +var length = 4294967295; +var array = new Array(length); + +printStatus('before array.length = ' + array.length); + +try +{ + array.push('Kibo'); +} +catch(ex) +{ + printStatus(ex.name + ': ' + ex.message); +} +reportCompare(expect, actual, summary); + +//expect = 'Kibo'; +//actual = array[length]; +//reportCompare(expect, actual, summary + ': element appended'); + +//expect = length; +//actual = array.length; +//reportCompare(expect, actual, summary + ': array length unchanged'); diff --git a/js/src/tests/non262/Array/regress-322135-02.js b/js/src/tests/non262/Array/regress-322135-02.js new file mode 100644 index 0000000000..9bd55bed4a --- /dev/null +++ b/js/src/tests/non262/Array/regress-322135-02.js @@ -0,0 +1,32 @@ +// |reftest| slow -- (bug 1234947) +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 322135; +var summary = 'Array.prototype.concat on Array with length 2^32-1'; +var actual = 'Completed'; +var expect = 'Completed'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +printStatus('This bug passes if it does not cause an out of memory error'); +printStatus('Other issues related to array length are not tested.'); + +var length = 4294967295; +var array1 = new Array(length); +var array2 = ['Kibo']; +var array; + +try +{ + array = array1.concat(array2); +} +catch(ex) +{ + printStatus(ex.name + ': ' + ex.message); +} +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-322135-03.js b/js/src/tests/non262/Array/regress-322135-03.js new file mode 100644 index 0000000000..bc181ccd07 --- /dev/null +++ b/js/src/tests/non262/Array/regress-322135-03.js @@ -0,0 +1,40 @@ +// |reftest| slow +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 322135; +var summary = 'Array.prototype.splice on Array with length 2^32-1'; +var actual = 'Completed'; +var expect = 'Completed'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +printStatus('This bug passes if it does not cause an out of memory error'); +printStatus('Other issues related to array length are not tested.'); + +var length = 4294967295; +var array = new Array(length); +var array1 = ['Kibo']; +var array; + +try +{ + array.splice(0, 0, array1); +} +catch(ex) +{ + printStatus(ex.name + ': ' + ex.message); +} +reportCompare(expect, actual, summary + ': RangeError'); + +//expect = 'Kibo'; +//actual = array[0]; +//reportCompare(expect, actual, summary + ': element prepended'); + +//expect = length; +//actual = array.length; +//reportCompare(expect, actual, summary + ': array length unchanged'); diff --git a/js/src/tests/non262/Array/regress-322135-04.js b/js/src/tests/non262/Array/regress-322135-04.js new file mode 100644 index 0000000000..555d0bb294 --- /dev/null +++ b/js/src/tests/non262/Array/regress-322135-04.js @@ -0,0 +1,38 @@ +// |reftest| slow +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 322135; +var summary = 'Array.prototype.unshift on Array with length 2^32-1'; +var actual = 'Completed'; +var expect = 'Completed'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +printStatus('This bug passes if it does not cause an out of memory error'); +printStatus('Other issues related to array length are not tested.'); + +var length = 4294967295; +var array = new Array(length); + +try +{ + array.unshift('Kibo'); +} +catch(ex) +{ + printStatus(ex.name + ': ' + ex.message); +} +reportCompare(expect, actual, summary); + +//expect = 'Kibo'; +//actual = array[0]; +//reportCompare(expect, actual, summary + ': first prepended'); + +//expect = length; +//actual = array.length; +//reportCompare(expect, actual, summary + ': array length unchanged'); diff --git a/js/src/tests/non262/Array/regress-330812.js b/js/src/tests/non262/Array/regress-330812.js new file mode 100644 index 0000000000..be700714c2 --- /dev/null +++ b/js/src/tests/non262/Array/regress-330812.js @@ -0,0 +1,33 @@ +// |reftest| slow-if(xulRuntime.XPCOMABI.match(/x86_64|aarch64|ppc64|ppc64le|s390x|mips64|loongarch64|riscv64/)||Android) -- No test results +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 330812; +var summary = 'Making Array(1<<29).sort() less problematic'; +var actual = 'No Crash'; +var expect = 'No Crash'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +expectExitCode(0); +expectExitCode(3); + +printStatus('This test passes if the browser does not hang or crash'); +printStatus('This test expects exit code 0 or 3 to indicate out of memory'); + +try +{ + var result = Array(1 << 29).sort(); +} +catch(ex) +{ + // handle changed 1.9 branch behavior. see bug 422348 + expect = 'InternalError: allocation size overflow'; + actual = ex + ''; +} + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-345961.js b/js/src/tests/non262/Array/regress-345961.js new file mode 100644 index 0000000000..2002301a85 --- /dev/null +++ b/js/src/tests/non262/Array/regress-345961.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 345961; +var summary = 'Array.prototype.shift should preserve holes'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = false; + + var array = new Array(2); + array.shift(); + actual = array.hasOwnProperty(0); + reportCompare(expect, actual, summary); + + array=Array(1); + array.shift(1); + actual = array.hasOwnProperty(1); + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-348810.js b/js/src/tests/non262/Array/regress-348810.js new file mode 100644 index 0000000000..d6cc2efaf8 --- /dev/null +++ b/js/src/tests/non262/Array/regress-348810.js @@ -0,0 +1,25 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 348810; +var summary = 'Do not crash when sorting an array of holes'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var a = Array(1); + a.sort(); + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-350256-01.js b/js/src/tests/non262/Array/regress-350256-01.js new file mode 100644 index 0000000000..6f090e1097 --- /dev/null +++ b/js/src/tests/non262/Array/regress-350256-01.js @@ -0,0 +1,42 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 350256; +var summary = 'Array.apply maximum arguments'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(Math.pow(2, 16)); +//----------------------------------------------------------------------------- + +function test(length) +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + + var a = new Array(); + a[length - 2] = 'length-2'; + a[length - 1] = 'length-1'; + + var b = Array.apply(null, a); + + expect = length + ',length-2,length-1'; + actual = b.length + "," + b[length - 2] + "," + b[length - 1]; + reportCompare(expect, actual, summary); + + function f() { + return arguments.length + "," + arguments[length - 2] + "," + + arguments[length - 1]; + } + + expect = length + ',length-2,length-1'; + actual = f.apply(null, a); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-350256-02.js b/js/src/tests/non262/Array/regress-350256-02.js new file mode 100644 index 0000000000..3281e2f6b7 --- /dev/null +++ b/js/src/tests/non262/Array/regress-350256-02.js @@ -0,0 +1,43 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 350256; +var summary = 'Array.apply maximum arguments'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); +reportCompare(true, true, ""); + +//----------------------------------------------------------------------------- +if (this.getMaxArgs) + test(getMaxArgs()); + +//----------------------------------------------------------------------------- + +function test(length) +{ + var a = new Array(); + a[length - 2] = 'length-2'; + a[length - 1] = 'length-1'; + + var b = Array.apply(null, a); + + expect = length + ',length-2,length-1'; + actual = b.length + "," + b[length - 2] + "," + b[length - 1]; + reportCompare(expect, actual, summary); + + function f() { + return arguments.length + "," + arguments[length - 2] + "," + + arguments[length - 1]; + } + + expect = length + ',length-2,length-1'; + actual = f.apply(null, a); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-352742-01.js b/js/src/tests/non262/Array/regress-352742-01.js new file mode 100644 index 0000000000..8048cb8f1d --- /dev/null +++ b/js/src/tests/non262/Array/regress-352742-01.js @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 352742; +var summary = 'Array filter on {valueOf: Function}'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + print('If the test harness fails, this test fails.'); + expect = 4; + z = {valueOf: Function}; + actual = 2; + try { + [11].filter(z); + } + catch(e) + { + actual = 3; + } + actual = 4; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-352742-02.js b/js/src/tests/non262/Array/regress-352742-02.js new file mode 100644 index 0000000000..946e8ba907 --- /dev/null +++ b/js/src/tests/non262/Array/regress-352742-02.js @@ -0,0 +1,29 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 352742; +var summary = 'eval("return") in toString'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 3; + var j = ({toString: function() { eval("return"); }}); + actual = 2; + try { "" + j; } catch(e){} + actual = 3; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-360681-01.js b/js/src/tests/non262/Array/regress-360681-01.js new file mode 100644 index 0000000000..c842967137 --- /dev/null +++ b/js/src/tests/non262/Array/regress-360681-01.js @@ -0,0 +1,30 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 360681; +var summary = 'Regression from bug 224128'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = actual = 'No Crash'; + + var a = Array(3); + a[0] = 1; + a[1] = 2; + a.sort(function () { gc(); return 1; }); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-360681-02.js b/js/src/tests/non262/Array/regress-360681-02.js new file mode 100644 index 0000000000..ab230e1d4b --- /dev/null +++ b/js/src/tests/non262/Array/regress-360681-02.js @@ -0,0 +1,55 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 360681; +var summary = 'Regression from bug 224128'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = actual = 'No Crash'; + + var N = 1000; + +// Make an array with a hole at the end + var a = Array(N); + for (i = 0; i < N - 1; ++i) + a[i] = 1; + +// array_sort due for array with N elements with allocates a temporary vector +// with 2*N. Lets create strings that on 32 and 64 bit CPU cause allocation +// of the same amount of memory + 1 word for their char arrays. After we GC +// strings with a reasonable malloc implementation that memory will be most +// likely reused in array_sort for the temporary vector. Then the bug causes +// accessing the one-beyond-the-aloocation word and re-interpretation of +// 0xFFF0FFF0 as GC thing. + + var str1 = Array(2*(2*N + 1) + 1).join(String.fromCharCode(0xFFF0)); + var str2 = Array(4*(2*N + 1) + 1).join(String.fromCharCode(0xFFF0)); + gc(); + str1 = str2 = null; + gc(); + + var firstCall = true; + a.sort(function (a, b) { + if (firstCall) { + firstCall = false; + gc(); + } + return a - b; + }); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-364104.js b/js/src/tests/non262/Array/regress-364104.js new file mode 100644 index 0000000000..05d8953b6e --- /dev/null +++ b/js/src/tests/non262/Array/regress-364104.js @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = "364104"; +var summary = "Array.prototype.indexOf, Array.prototype.lastIndexOf issues " + + "with the optional second fromIndex argument"; +var actual, expect; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/************** + * BEGIN TEST * + **************/ + +var failed = false; + +try +{ + // indexOf + if ([2].indexOf(2) != 0) + throw "indexOf: not finding 2!?"; + if ([2].indexOf(2, 0) != 0) + throw "indexOf: not interpreting explicit second argument 0!"; + if ([2].indexOf(2, 1) != -1) + throw "indexOf: ignoring second argument with value equal to array length!"; + if ([2].indexOf(2, 2) != -1) + throw "indexOf: ignoring second argument greater than array length!"; + if ([2].indexOf(2, 17) != -1) + throw "indexOf: ignoring large second argument!"; + if ([2].indexOf(2, -5) != 0) + throw "indexOf: calculated fromIndex < 0, should search entire array!"; + if ([2, 3].indexOf(2, -1) != -1) + throw "indexOf: not handling index == (-1 + 2), element 2 correctly!"; + if ([2, 3].indexOf(3, -1) != 1) + throw "indexOf: not handling index == (-1 + 2), element 3 correctly!"; + + // lastIndexOf + if ([2].lastIndexOf(2) != 0) + throw "lastIndexOf: not finding 2!?"; + if ([2].lastIndexOf(2, 1) != 0) + throw "lastIndexOf: not interpreting explicit second argument 1!?"; + if ([2].lastIndexOf(2, 17) != 0) + throw "lastIndexOf: should have searched entire array!"; + if ([2].lastIndexOf(2, -5) != -1) + throw "lastIndexOf: -5 + 1 < 0, so array shouldn't be searched!"; + if ([2].lastIndexOf(2, -2) != -1) + throw "lastIndexOf: -2 + 1 < 0, so array shouldn't be searched!"; + if ([2, 3].lastIndexOf(2, -1) != 0) + throw "lastIndexOf: not handling index == (-1 + 2), element 2 correctly!"; + if ([2, 3].lastIndexOf(3, -1) != 1) + throw "lastIndexOf: not handling index == (-1 + 2), element 3 correctly!"; + if ([2, 3].lastIndexOf(2, -2) != 0) + throw "lastIndexOf: not handling index == (-2 + 2), element 2 correctly!"; + if ([2, 3].lastIndexOf(3, -2) != -1) + throw "lastIndexOf: not handling index == (-2 + 2), element 3 correctly!"; + if ([2, 3].lastIndexOf(2, -3) != -1) + throw "lastIndexOf: calculated fromIndex < 0, shouldn't search array for 2!"; + if ([2, 3].lastIndexOf(3, -3) != -1) + throw "lastIndexOf: calculated fromIndex < 0, shouldn't search array for 3!"; +} +catch (e) +{ + failed = e; +} + + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-386030.js b/js/src/tests/non262/Array/regress-386030.js new file mode 100644 index 0000000000..239e7e11ad --- /dev/null +++ b/js/src/tests/non262/Array/regress-386030.js @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Blake Kaplan + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 386030; +var summary = 'Array.reduce should ignore holes'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + function add(a, b) { return a + b; } + function testreduce(v) { return v == 3 ? "PASS" : "FAIL"; } + + expect = 'PASS'; + + try { + a = new Array(2); + a[1] = 3; + actual = testreduce(a.reduce(add)); + } catch (e) { + actual = "FAIL, reduce"; + } + reportCompare(expect, actual, summary + ': 1'); + + try { + a = new Array(2); + a[0] = 3; + actual = testreduce(a.reduceRight(add)); + } catch (e) { + actual = "FAIL, reduceRight"; + } + reportCompare(expect, actual, summary + ': 2'); + + try { + a = new Array(2); + a.reduce(add); + actual = "FAIL, empty reduce"; + } catch (e) { + actual = "PASS"; + } + reportCompare(expect, actual, summary + ': 3'); + + try { + a = new Array(2); + print(a.reduceRight(add)); + actual = "FAIL, empty reduceRight"; + } catch (e) { + actual = "PASS"; + } + reportCompare(expect, actual, summary + ': 4'); +} diff --git a/js/src/tests/non262/Array/regress-387501.js b/js/src/tests/non262/Array/regress-387501.js new file mode 100644 index 0000000000..3f800c14b2 --- /dev/null +++ b/js/src/tests/non262/Array/regress-387501.js @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 387501; +var summary = + 'Array.prototype.toString|toLocaleString|toSource are generic'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = '[object String]'; + actual = Array.prototype.toString.call((new String('foo'))); + assertEq(actual, expect, summary); + + expect = 'f,o,o'; + actual = Array.prototype.toLocaleString.call((new String('foo'))); + assertEq(actual, expect, summary); + + if (typeof Array.prototype.toSource != 'undefined') + { + assertEq('["f", "o", "o"]', Array.prototype.toSource.call(new String('foo'))); + + try + { + Array.prototype.toSource.call('foo'); + throw new Error("didn't throw"); + } + catch(ex) + { + assertEq(ex instanceof TypeError, true, + "wrong error thrown: expected TypeError, got " + ex); + } + } + + reportCompare(true, true, "Tests complete"); +} diff --git a/js/src/tests/non262/Array/regress-390598.js b/js/src/tests/non262/Array/regress-390598.js new file mode 100644 index 0000000000..ec34a5b121 --- /dev/null +++ b/js/src/tests/non262/Array/regress-390598.js @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +//----------------------------------------------------------------------------- +var BUGNUMBER = 390598; +var summary = 'Override inherited length of Array-like object'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + + function F() {} + F.prototype = []; + + // override inherited length from the prototype. + expect = 10; + var x = new F(); + + print('x = new F(); x instanceof Array: ' + (x instanceof Array)); + + x.length = expect; + actual = x.length; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-415451.js b/js/src/tests/non262/Array/regress-415451.js new file mode 100644 index 0000000000..b4674f5df8 --- /dev/null +++ b/js/src/tests/non262/Array/regress-415451.js @@ -0,0 +1,24 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 415451; +var summary = 'indexOf/lastIndexOf behavior'; + +var expected = "3,0,3,3,3,-1,-1"; +results = []; +var a = [1,2,3,1]; +for (var i=-1; i < a.length+2; i++) + results.push(a.indexOf(1,i)); +var actual = String(results); +reportCompare(expected, actual, "indexOf"); + +results = []; +var expected = "3,0,0,0,3,3,3"; +for (var i=-1; i < a.length+2; i++) + results.push(a.lastIndexOf(1,i)); +var actual = String(results); +reportCompare(expected, actual, "lastIndexOf"); + diff --git a/js/src/tests/non262/Array/regress-421325.js b/js/src/tests/non262/Array/regress-421325.js new file mode 100644 index 0000000000..fece43a64f --- /dev/null +++ b/js/src/tests/non262/Array/regress-421325.js @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 421325; +var summary = 'Dense Arrays and holes'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + Array.prototype[1] = 'bar'; + + var a = []; + a[0]='foo'; + a[2] = 'baz'; + expect = 'foo,bar,baz'; + actual = a + ''; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-422286.js b/js/src/tests/non262/Array/regress-422286.js new file mode 100644 index 0000000000..e00d40d752 --- /dev/null +++ b/js/src/tests/non262/Array/regress-422286.js @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 422286; +var summary = 'Array slice when array\'s length is assigned'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + Array(10000).slice(1); + a = Array(1); + a.length = 10000; + a.slice(1); + a = Array(1); + a.length = 10000; + a.slice(-1); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-424954.js b/js/src/tests/non262/Array/regress-424954.js new file mode 100644 index 0000000000..3b2f78936b --- /dev/null +++ b/js/src/tests/non262/Array/regress-424954.js @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Bob Clary + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 424954; +var summary = 'Do not crash with [].concat(null)'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + [].concat(null); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-430717.js b/js/src/tests/non262/Array/regress-430717.js new file mode 100644 index 0000000000..cf5f302a63 --- /dev/null +++ b/js/src/tests/non262/Array/regress-430717.js @@ -0,0 +1,29 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 430717; +var summary = 'Dense Arrays should inherit deleted elements from Array.prototype'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + Array.prototype[2] = "two"; + var a = [0,1,2,3]; + delete a[2]; + + expect = 'two'; + actual = a[2]; + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-451483.js b/js/src/tests/non262/Array/regress-451483.js new file mode 100644 index 0000000000..49bb66e7db --- /dev/null +++ b/js/src/tests/non262/Array/regress-451483.js @@ -0,0 +1,28 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 451483; +var summary = '[].splice.call(0) == []'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = true; + var result = [].splice.call(0); + print('[].splice.call(0) = ' + result); + actual = result instanceof Array && result.length == 0; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-451906.js b/js/src/tests/non262/Array/regress-451906.js new file mode 100644 index 0000000000..c80962e435 --- /dev/null +++ b/js/src/tests/non262/Array/regress-451906.js @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 451906; +var summary = 'Index array by numeric string'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 1; + var s=[1,2,3]; + actual = s['0']; + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-456845.js b/js/src/tests/non262/Array/regress-456845.js new file mode 100644 index 0000000000..7531ba17fd --- /dev/null +++ b/js/src/tests/non262/Array/regress-456845.js @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 456845; +var summary = 'JIT: popArrs[a].pop is not a function'; +var actual = 'No Error'; +var expect = 'No Error'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + + try + { + var chars = '0123456789abcdef'; + var size = 1000; + var mult = 100; + + var arr = []; + var lsize = size; + while (lsize--) { arr.push(chars); } + + var popArrs = []; + for (var i=0; i<mult; i++) { popArrs.push(arr.slice()); } + + + for(var a=0;a<mult;a++) { + var x; while (x = popArrs[a].pop()) { } + } + + } + catch(ex) + { + actual = ex + ''; + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-465980-01.js b/js/src/tests/non262/Array/regress-465980-01.js new file mode 100644 index 0000000000..c7d6dee29a --- /dev/null +++ b/js/src/tests/non262/Array/regress-465980-01.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 465980; +var summary = 'Do not crash @ InitArrayElements'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + var a = new Array(4294967294); + a.push("foo", "bar"); + } + catch(ex) + { + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-465980-02.js b/js/src/tests/non262/Array/regress-465980-02.js new file mode 100644 index 0000000000..866ad02d8e --- /dev/null +++ b/js/src/tests/non262/Array/regress-465980-02.js @@ -0,0 +1,167 @@ +// |reftest| slow +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 465980; +var summary = 'Do not crash @ InitArrayElements'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + function describe(name, startLength, pushArgs, expectThrow, expectLength) + { + return name + "(" + startLength + ", " + + "[" + pushArgs.join(", ") + "], " + + expectThrow + ", " + + expectLength + ")"; + } + + var push = Array.prototype.push; + var unshift = Array.prototype.unshift; + + function testArrayPush(startLength, pushArgs, expectThrow, expectLength) + { + print("running testArrayPush(" + + startLength + ", " + + "[" + pushArgs.join(", ") + "], " + + expectThrow + ", " + + expectLength + ")..."); + var a = new Array(startLength); + try + { + push.apply(a, pushArgs); + if (expectThrow) + { + throw "expected to throw for " + + describe("testArrayPush", startLength, pushArgs, expectThrow, + expectLength); + } + } + catch (e) + { + if (!(e instanceof RangeError)) + { + throw "unexpected exception type thrown: " + e + " for " + + describe("testArrayPush", startLength, pushArgs, expectThrow, + expectLength); + } + if (!expectThrow) + { + throw "unexpected exception " + e + " for " + + describe("testArrayPush", startLength, pushArgs, expectThrow, + expectLength); + } + } + + if (a.length !== expectLength) + { + throw "unexpected modified-array length for " + + describe("testArrayPush", startLength, pushArgs, expectThrow, + expectLength); + } + + for (var i = 0, sz = pushArgs.length; i < sz; i++) + { + var index = i + startLength; + if (a[index] !== pushArgs[i]) + { + throw "unexpected value " + a[index] + + " at index " + index + " (" + i + ") during " + + describe("testArrayPush", startLength, pushArgs, expectThrow, + expectLength) + ", expected " + pushArgs[i]; + } + } + } + + function testArrayUnshift(startLength, unshiftArgs, expectThrow, expectLength) + { + print("running testArrayUnshift(" + + startLength + ", " + + "[" + unshiftArgs.join(", ") + "], " + + expectThrow + ", " + + expectLength + ")..."); + var a = new Array(startLength); + try + { + unshift.apply(a, unshiftArgs); + if (expectThrow) + { + throw "expected to throw for " + + describe("testArrayUnshift", startLength, unshiftArgs, expectThrow, + expectLength); + } + } + catch (e) + { + if (!(e instanceof RangeError)) + { + throw "unexpected exception type thrown: " + e + " for " + + describe("testArrayUnshift", startLength, unshiftArgs, expectThrow, + expectLength); + } + if (!expectThrow) + { + throw "unexpected exception " + e + " for " + + describe("testArrayUnshift", startLength, unshiftArgs, expectThrow, + expectLength); + } + } + + if (a.length !== expectLength) + { + throw "unexpected modified-array length for " + + describe("testArrayUnshift", startLength, unshiftArgs, expectThrow, + expectLength); + } + + for (var i = 0, sz = unshiftArgs.length; i < sz; i++) + { + if (a[i] !== unshiftArgs[i]) + { + throw "unexpected value at index " + i + " during " + + describe("testArrayUnshift", startLength, unshiftArgs, expectThrow, + expectLength); + } + } + } + + var failed = true; + + try + { + var foo = "foo", bar = "bar", baz = "baz"; + + testArrayPush(4294967294, [foo], false, 4294967295); + testArrayPush(4294967294, [foo, bar], true, 4294967295); + testArrayPush(4294967294, [foo, bar, baz], true, 4294967295); + testArrayPush(4294967295, [foo], true, 4294967295); + testArrayPush(4294967295, [foo, bar], true, 4294967295); + testArrayPush(4294967295, [foo, bar, baz], true, 4294967295); + + testArrayUnshift(4294967294, [foo], false, 4294967295); + testArrayUnshift(4294967294, [foo, bar], true, 4294967294); + testArrayUnshift(4294967294, [foo, bar, baz], true, 4294967294); + testArrayUnshift(4294967295, [foo], true, 4294967295); + testArrayUnshift(4294967295, [foo, bar], true, 4294967295); + testArrayUnshift(4294967295, [foo, bar, baz], true, 4294967295); + + } + catch (e) + { + actual = e + ''; + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-474529.js b/js/src/tests/non262/Array/regress-474529.js new file mode 100644 index 0000000000..d377a9a939 --- /dev/null +++ b/js/src/tests/non262/Array/regress-474529.js @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 474529; +var summary = 'Do not assert: _buf->_nextPage'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + function main() { + + function timeit(N, buildArrayString) { + return Function("N", + "var d1 = +new Date;" + + "while (N--) var x = " + buildArrayString + + "; return +new Date - d1")(N); + } + + function reportResults(size, N, literalMs, newArrayMs, arrayMs) { + print(Array.prototype.join.call(arguments, "\t")); + } + + var repetitions = [ 9000, 7000, 4000, 2000, 2000, 2000, 800, 800, 800, 300, 100, 100 ] + for (var zeros = "0, ", i = 0; i < repetitions.length; ++i) { + var N = repetitions[i]; + reportResults((1 << i) + 1, N, + timeit(N, "[" + zeros + " 0 ]"), + timeit(N, "new Array(" + zeros + " 0)"), + timeit(N, "Array(" + zeros + " 0)")); + zeros += zeros; + } + } + + gc(); + print("Size\t\Rep.\t\Literal\tnew Arr\tArray()"); + print("====\t=====\t=======\t=======\t======="); + main(); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-488989.js b/js/src/tests/non262/Array/regress-488989.js new file mode 100644 index 0000000000..2df0f72b83 --- /dev/null +++ b/js/src/tests/non262/Array/regress-488989.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 488989; +var summary = 'Array.prototype.push for non-arrays near max-array-index limit'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var stack = { push: [].push }; stack.length = Math.pow(2, 37); + stack.push(-2, -1, 0); + + var stack = { push: [].push }; stack.length = Math.pow(2, 5); + stack.push(-2, -1, 0); + + var stack = { push: [].push }; stack.length = Math.pow(2, 32) -2; + stack.push(-2, -1, 0); + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/Array/regress-566651.js b/js/src/tests/non262/Array/regress-566651.js new file mode 100644 index 0000000000..a05d0310a5 --- /dev/null +++ b/js/src/tests/non262/Array/regress-566651.js @@ -0,0 +1,20 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Blake Kaplan + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 566651; +var summary = 'setting array.length to null should not throw an uncatchable exception'; +var actual = 0; +var expect = 0; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var a = []; +a.length = null; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/Array/regress-599159.js b/js/src/tests/non262/Array/regress-599159.js new file mode 100644 index 0000000000..bd8225b529 --- /dev/null +++ b/js/src/tests/non262/Array/regress-599159.js @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var b = Object.create(Array.prototype); +b.length = 12; +assertEq(b.length, 12); + +reportCompare(true,true); diff --git a/js/src/tests/non262/Array/regress-619970.js b/js/src/tests/non262/Array/regress-619970.js new file mode 100644 index 0000000000..6ec94fc3bb --- /dev/null +++ b/js/src/tests/non262/Array/regress-619970.js @@ -0,0 +1,8 @@ +function test() { + delete arguments[1]; + return Array.prototype.join.call(arguments); +} +assertEq(test(1,2,3), "1,,3"); +Object.prototype[1] = "ponies!!!1"; +assertEq(test(1,2,3), "1,ponies!!!1,3"); +reportCompare(true,true); diff --git a/js/src/tests/non262/Array/regress-94257.js b/js/src/tests/non262/Array/regress-94257.js new file mode 100644 index 0000000000..23fff7bcf0 --- /dev/null +++ b/js/src/tests/non262/Array/regress-94257.js @@ -0,0 +1,83 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Date: 30 October 2001 + * + * SUMMARY: Regression test for bug 94257 + * See http://bugzilla.mozilla.org/show_bug.cgi?id=94257 + * + * Rhino used to crash on this code; specifically, on the line + * + * arr[1+1] += 2; + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 94257; +var summary = "Making sure we don't crash on this code -"; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +var arr = new Array(6); +arr[1+1] = 1; +arr[1+1] += 2; + + +status = inSection(1); +actual = arr[1+1]; +expect = 3; +addThis(); + +status = inSection(2); +actual = arr[1+1+1]; +expect = undefined; +addThis(); + +status = inSection(3); +actual = arr[1]; +expect = undefined; +addThis(); + + +arr[1+2] = 'Hello'; + + +status = inSection(4); +actual = arr[1+1+1]; +expect = 'Hello'; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/Array/reverse-order-of-low-high-accesses.js b/js/src/tests/non262/Array/reverse-order-of-low-high-accesses.js new file mode 100644 index 0000000000..a9b1e80deb --- /dev/null +++ b/js/src/tests/non262/Array/reverse-order-of-low-high-accesses.js @@ -0,0 +1,88 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 858677; +var summary = + "[].reverse should swap elements low to high using accesses to low " + + "elements, then accesses to high elements"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var observed = []; + +// (0, 7) hits the lowerExists/upperExists case. +// (1, 6) hits the !lowerExists/upperExists case. +// (2, 5) hits the lowerExists/!upperExists case. +// (3, 4) hits the !lowerExists/!upperExists case. +// +// It'd be a good idea to have a second version of this test at some point +// where the "array" being reversed is a proxy, to detect proper ordering of +// getproperty, hasproperty, setproperty into a hole, and deleteproperty from a +// non-configurable element. But at present our Array.prototype.reverse +// implementation probably doesn't conform fully to all this (because our +// internal MOP is still slightly off), so punt for now. +var props = + { + 0: { + configurable: true, + get: function() { observed.push("index 0 get"); return "index 0 get"; }, + set: function(v) { observed.push("index 0 set: " + v); } + }, + /* 1: hole */ + 2: { + configurable: true, + get: function() { observed.push("index 2 get"); return "index 2 get"; }, + set: function(v) { observed.push("index 2 set: " + v); } + }, + /* 3: hole */ + /* 4: hole */ + /* 5: hole */ + 6: { + configurable: true, + get: function() { observed.push("index 6 get"); return "index 6 get"; }, + set: function(v) { observed.push("index 6 set: " + v); } + }, + 7: { + configurable: true, + get: function() { observed.push("index 7 get"); return "index 7 get"; }, + set: function(v) { observed.push("index 7 set: " + v); } + }, + }; + +var arr = Object.defineProperties(new Array(8), props); + +arr.reverse(); + +var expectedObserved = + ["index 0 get", "index 7 get", "index 0 set: index 7 get", "index 7 set: index 0 get", + "index 6 get", + "index 2 get" + /* nothing for 3/4 */]; +print(observed); +// Do this before the assertions below futz even more with |observed|. +assertEq(observed.length, expectedObserved.length); +for (var i = 0; i < expectedObserved.length; i++) + assertEq(observed[i], expectedObserved[i]); + +assertEq(arr[0], "index 0 get"); // no deletion, setting doesn't overwrite +assertEq(arr[1], "index 6 get"); // copies result of getter +assertEq(2 in arr, false); // deleted +assertEq(3 in arr, false); // never there +assertEq(4 in arr, false); // never there +assertEq(arr[5], "index 2 get"); // copies result of getter +assertEq(6 in arr, false); // deleted +assertEq(arr[7], "index 7 get"); // no deletion, setter doesn't overwrite + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/set-with-indexed-property-on-prototype-chain.js b/js/src/tests/non262/Array/set-with-indexed-property-on-prototype-chain.js new file mode 100644 index 0000000000..1507e5ce00 --- /dev/null +++ b/js/src/tests/non262/Array/set-with-indexed-property-on-prototype-chain.js @@ -0,0 +1,60 @@ +function ensureSetterCalledOnce(fn, value, index) { + var setterCalled = false; + Object.defineProperty(Array.prototype, index, { + configurable: true, + set: function(v) { + assertEq(setterCalled, false); + setterCalled = true; + assertEq(v, value); + } + }); + + assertEq(setterCalled, false); + fn(); + assertEq(setterCalled, true); + + delete Array.prototype[index]; +} + +ensureSetterCalledOnce(function() { + [].push("push"); +}, "push", 0); + +ensureSetterCalledOnce(function() { + [/* hole */, "reverse"].reverse(); +}, "reverse", 0); + +ensureSetterCalledOnce(function() { + ["reverse", /* hole */,].reverse(); +}, "reverse", 1); + +ensureSetterCalledOnce(function() { + [/* hole */, "shift"].shift(); +}, "shift", 0); + +ensureSetterCalledOnce(function() { + [/* hole */, "sort"].sort(); +}, "sort", 0); + +ensureSetterCalledOnce(function() { + [/* hole */, undefined].sort(); +}, undefined, 0); + +ensureSetterCalledOnce(function() { + [].splice(0, 0, "splice"); +}, "splice", 0); + +ensureSetterCalledOnce(function() { + [/* hole */, "splice"].splice(0, 1); +}, "splice", 0); + +ensureSetterCalledOnce(function(v) { + ["splice", /* hole */,].splice(0, 0, "item"); +}, "splice", 1); + +ensureSetterCalledOnce(function() { + [].unshift("unshift"); +}, "unshift", 0); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/shell.js b/js/src/tests/non262/Array/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/Array/shell.js diff --git a/js/src/tests/non262/Array/shift-no-has-trap.js b/js/src/tests/non262/Array/shift-no-has-trap.js new file mode 100644 index 0000000000..e941ba7205 --- /dev/null +++ b/js/src/tests/non262/Array/shift-no-has-trap.js @@ -0,0 +1,64 @@ +// Test that Array.prototype.shift doesn't call the [[HasProperty]] internal +// method of objects when retrieving the element at index 0. + +var log = []; +var array = []; +var proxy = new Proxy(array, new Proxy({}, { + get(t, trap, r) { + return (t, pk, ...more) => { + log.push(`${trap}:${String(pk)}`); + return Reflect[trap](t, pk, ...more); + }; + } +})); + +var result; + +result = Array.prototype.shift.call(proxy); +assertEqArray(log, [ + "get:length", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, undefined); + +log.length = 0; +array.push(1); + +result = Array.prototype.shift.call(proxy); +assertEqArray(log, [ + "get:length", + "get:0", + "deleteProperty:0", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 1); + +log.length = 0; +array.push(2, 3); + +result = Array.prototype.shift.call(proxy); +assertEqArray(log, [ + "get:length", + "get:0", + "has:1", "get:1", "set:0", "getOwnPropertyDescriptor:0", "defineProperty:0", + "deleteProperty:1", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 2); + +log.length = 0; +array.push(4, 5); + +result = Array.prototype.shift.call(proxy); +assertEqArray(log, [ + "get:length", + "get:0", + "has:1", "get:1", "set:0", "getOwnPropertyDescriptor:0", "defineProperty:0", + "has:2", "get:2", "set:1", "getOwnPropertyDescriptor:1", "defineProperty:1", + "deleteProperty:2", + "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length" +]); +assertEq(result, 3); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/shift_for_in.js b/js/src/tests/non262/Array/shift_for_in.js new file mode 100644 index 0000000000..1bbf2d9c2d --- /dev/null +++ b/js/src/tests/non262/Array/shift_for_in.js @@ -0,0 +1,13 @@ +var BUGNUMBER = 1247701; +var summary = 'Array.prototype.shift on a dense array with holes should update for-in enumeration properties.'; + +print(BUGNUMBER + ": " + summary); + +var x = ["a", , "b", , "c", "d" , "e", "f", "g"]; +for (var p in x) { + assertEq(p in x, true); + x.shift(); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/slice-sparse-with-large-index.js b/js/src/tests/non262/Array/slice-sparse-with-large-index.js new file mode 100644 index 0000000000..7fcf571e53 --- /dev/null +++ b/js/src/tests/non262/Array/slice-sparse-with-large-index.js @@ -0,0 +1,18 @@ +var array = []; +array[2**31 - 2] = "INT32_MAX - 1"; +array[2**31 - 1] = "INT32_MAX"; +array[2**31 - 0] = "INT32_MAX + 1"; +array[2**32 - 2] = "UINT32_MAX - 1"; +array[2**32 - 1] = "UINT32_MAX"; +array[2**32 - 0] = "UINT32_MAX + 1"; + +var copy = array.slice(); +assertEq(copy[2**31 - 2], "INT32_MAX - 1"); +assertEq(copy[2**31 - 1], "INT32_MAX"); +assertEq(copy[2**31 - 0], "INT32_MAX + 1"); +assertEq(copy[2**32 - 2], "UINT32_MAX - 1"); +assertEq(copy[2**32 - 1], undefined); +assertEq(copy[2**32 - 0], undefined); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/sort-01.js b/js/src/tests/non262/Array/sort-01.js new file mode 100644 index 0000000000..da90220aef --- /dev/null +++ b/js/src/tests/non262/Array/sort-01.js @@ -0,0 +1,23 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 604971; +var summary = 'array.sort compare-function gets incorrect this'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +[1, 2, 3].sort(function() { "use strict"; assertEq(this, undefined); }); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/sort-array-with-holes-and-undefined.js b/js/src/tests/non262/Array/sort-array-with-holes-and-undefined.js new file mode 100644 index 0000000000..b77c6d3411 --- /dev/null +++ b/js/src/tests/non262/Array/sort-array-with-holes-and-undefined.js @@ -0,0 +1,32 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 664528; +var summary = + "Sorting an array containing only holes and |undefined| should move all " + + "|undefined| to the start of the array"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var a = [, , , undefined]; +a.sort(); + +assertEq(a.hasOwnProperty(0), true); +assertEq(a[0], undefined); +assertEq(a.hasOwnProperty(1), false); +assertEq(a.hasOwnProperty(2), false); +assertEq(a.hasOwnProperty(3), false); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/sort-delete-ascending-order.js b/js/src/tests/non262/Array/sort-delete-ascending-order.js new file mode 100644 index 0000000000..99df536df7 --- /dev/null +++ b/js/src/tests/non262/Array/sort-delete-ascending-order.js @@ -0,0 +1,37 @@ +// Calls Array.prototype.sort and tests that properties are deleted in the same order in the +// native and the self-hosted implementation. + +function createProxy() { + var deleted = []; + var proxy = new Proxy([, , 0], { + deleteProperty(t, pk){ + deleted.push(pk); + return delete t[pk]; + } + }); + + return {proxy, deleted}; +} + +function compareFn(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +// Sort an array without a comparator function. This calls the native sort implementation. + +var {proxy, deleted} = createProxy(); + +assertEqArray(deleted, []); +proxy.sort() +assertEqArray(deleted, ["1", "2"]); + +// Now sort an array with a comparator function. This calls the self-hosted sort implementation. + +var {proxy, deleted} = createProxy(); + +assertEqArray(deleted, []); +proxy.sort(compareFn); +assertEqArray(deleted, ["1", "2"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/sort-non-function.js b/js/src/tests/non262/Array/sort-non-function.js new file mode 100644 index 0000000000..91e3045932 --- /dev/null +++ b/js/src/tests/non262/Array/sort-non-function.js @@ -0,0 +1,22 @@ +// Array.prototype.sort throws if the comparator is neither undefined nor +// a callable object. + +// Use a zero length array, so we can provide any kind of callable object +// without worrying that the function is actually a valid comparator function. +const array = new Array(0); + +// Throws if the comparator is neither undefined nor callable. +for (let invalidComparator of [null, 0, true, Symbol(), {}, []]) { + assertThrowsInstanceOf(() => array.sort(invalidComparator), TypeError); +} + +// Doesn't throw if the comparator is undefined or a callable object. +for (let validComparator of [undefined, () => {}, Math.max, class {}, new Proxy(function(){}, {})]) { + array.sort(validComparator); +} + +// Also doesn't throw if no comparator was provided at all. +array.sort(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/sort-typedarray-with-own-length.js b/js/src/tests/non262/Array/sort-typedarray-with-own-length.js new file mode 100644 index 0000000000..18e4ea5cd6 --- /dev/null +++ b/js/src/tests/non262/Array/sort-typedarray-with-own-length.js @@ -0,0 +1,33 @@ +function sortTypedArray(comparator) { + // Create a typed array with three elements, but also add an own "length" + // property with the value `2` to restrict the range of elements which + // will be sorted by Array.prototype.sort(). + var ta = new Int8Array([3, 2, 1]); + Object.defineProperty(ta, "length", {value: 2}); + + // Sort with Array.prototype.sort(), not %TypedArray%.prototype.sort()! + Array.prototype.sort.call(ta, comparator); + + return ta; +} + +// Comparators using the form |return a - b| are special-cased in +// Array.prototype.sort(). +function optimizedComparator(a, b) { + return a - b; +} + +// This comparator doesn't compile to the byte code sequence which gets +// special-cased in Array.prototype.sort(). +function nonOptimizedComparator(a, b) { + var d = a - b; + return d; +} + +// Both comparators should produce the same result. +assertEq(sortTypedArray(optimizedComparator).toString(), "2,3,1"); +assertEq(sortTypedArray(nonOptimizedComparator).toString(), "2,3,1"); + + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/sort_basics.js b/js/src/tests/non262/Array/sort_basics.js new file mode 100644 index 0000000000..e3011e5c48 --- /dev/null +++ b/js/src/tests/non262/Array/sort_basics.js @@ -0,0 +1,46 @@ +// Note: failed runs should include their "SEED" value in error messages, +// setting "const SEED" to that value will recreate the data from any such run. +const SEED = (Math.random() * 10) + 1; + +// Create an array filled with random values, 'size' is the desired length of +// the array and 'seed' is an initial value supplied to a pseudo-random number +// generator. +function genRandomArray(size, seed) { + return Array.from(XorShiftGenerator(seed, size)); +} + +function SortTest(size, seed) { + let arrOne = genRandomArray(size, seed); + let arrTwo = Array.from(arrOne); + let arrThree = Array.from(arrOne); + let typedArrays = [ + new Uint8Array(arrOne), + new Int32Array(arrOne), + new Float32Array(arrOne) + ]; + + let sorted = Array.from((Int32Array.from(arrOne)).sort()); + + // Test numeric comparators against typed array sort. + assertDeepEq(sorted, arrTwo.sort((x, y) => (x - y)), + `The array is not properly sorted! seed: ${SEED}`); + + // Use multiplication to kill comparator optimization and trigger + // self-hosted sorting. + assertDeepEq(sorted, arrThree.sort((x, y) => (1*x - 1*y)), + `The array is not properly sorted! seed: ${SEED}`); + + // Ensure that typed arrays are also sorted property. + for (typedArr of typedArrays) { + let sortedTypedArray = Array.prototype.sort.call(typedArr, (x, y) => (1*x - 1*y)) + assertDeepEq(sorted, Array.from(sortedTypedArray), + `The array is not properly sorted! seed: ${SEED}`); + } +} + +SortTest(2048, SEED); +SortTest(16, SEED); +SortTest(0, SEED); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/sort_holes.js b/js/src/tests/non262/Array/sort_holes.js new file mode 100644 index 0000000000..7424bd8894 --- /dev/null +++ b/js/src/tests/non262/Array/sort_holes.js @@ -0,0 +1,66 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// We should preserve holes when sorting sparce arrays. +// See bug: 1246860 + +function denseCount(arr) { + var c = 0; + for (var i = 0; i < arr.length; i++) + if (i in arr) + c++; + return c; +} + +let a = [,,,,,,,,,,,,,,,,,,,,{size: 1},{size: 2}]; +let b = [,,,,,,,,,,,,,,,,,,,,{size: 1},{size: 2}].sort(); +let c = [,,,,,,,,,,,,,,,,,,,,{size: 1},{size: 2}].sort((a, b) => {+a.size - +b.size}); + +assertEq(a.length, 22); +assertEq(denseCount(a), 2); +assertEq(a.length, b.length); +assertEq(b.length, c.length); +assertEq(denseCount(a), denseCount(b)); +assertEq(denseCount(b), denseCount(c)); + +let superSparce = new Array(5000); +superSparce[0] = 99; +superSparce[4000] = 0; +superSparce[4999] = -1; + +assertEq(superSparce.length, 5000); +assertEq(denseCount(superSparce), 3); + +superSparce.sort((a, b) => 1*a-b); +assertEq(superSparce.length, 5000); +assertEq(denseCount(superSparce), 3); +assertEq(superSparce[0], -1); +assertEq(superSparce[1], 0); +assertEq(superSparce[2], 99); + +let allHoles = new Array(2600); +assertEq(allHoles.length, 2600); +assertEq(denseCount(allHoles), 0); +allHoles.sort((a, b) => 1*a-b); +assertEq(allHoles.length, 2600); +assertEq(denseCount(allHoles), 0); + +let oneHole = new Array(2600); +oneHole[1399] = {size: 27}; +assertEq(oneHole.length, 2600); +assertEq(denseCount(oneHole), 1); +oneHole.sort((a, b) => {+a.size - +b.size}); +assertDeepEq(oneHole[0], {size: 27}); +assertEq(oneHole.length, 2600); +assertEq(denseCount(oneHole), 1); + +// Sealed objects should be sortable, including those with holes (so long +// as the holes appear at the end, so that they don't need to be moved). +assertDeepEq(Object.seal([0, 99, -1]).sort((x, y) => 1 * x - y), + Object.seal([-1, 0, 99])); + +assertDeepEq(Object.seal([1, 5, 4, , ,]).sort((x, y) => 1 * x - y), + Object.seal([1, 4, 5, , ,])); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/sort_native_string_nan.js b/js/src/tests/non262/Array/sort_native_string_nan.js new file mode 100644 index 0000000000..67ec08a015 --- /dev/null +++ b/js/src/tests/non262/Array/sort_native_string_nan.js @@ -0,0 +1,16 @@ + +var array = ["not-a-number", "also-not-a-number"]; +var copy = [...array]; + +// The sort comparator must be exactly equal to the bytecode pattern: +// +// JSOp::GetArg 0/1 +// JSOp::GetArg 1/0 +// JSOp::Sub +// JSOp::Return +array.sort(function(a, b) { return a - b; }); + +assertEqArray(array, copy); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/sort_proxy.js b/js/src/tests/non262/Array/sort_proxy.js new file mode 100644 index 0000000000..40ebb907bf --- /dev/null +++ b/js/src/tests/non262/Array/sort_proxy.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure, via proxy, that only get, set, delete, has, and getOwnPropertyDescriptor +// are touched during sorting. + +const handler = { + set: function(target, prop, value) { + target[prop] = value; + return value; + }, + getPrototypeOf: () => { throw "You shouldn't touch getPrototypeOf!" }, + setPrototypeOf: () => { throw "You shouldn't touch setPrototypeOf!" }, + isExtensible: () => { throw "You shouldn't touch isExtensible!" }, + preventExtensions: () => { throw "You shouldn't touch preventExtensions!" }, + defineProperty: () => { throw "You shouldn't touch defineProperty!" }, + ownKeys: () => { throw "You shouldn't touch ownKeys!" }, + apply: () => { throw "You shouldn't touch apply!" }, + construct: () => { throw "You shouldn't touch construct!" }, +} + +function testArray(arr) { + let proxy = new Proxy(arr, handler) + + // The supplied comparators trigger a JavaScript implemented sort. + proxy.sort((x, y) => 1 * x - y); + arr.sort((x, y) => 1 * x - y); + + for (let i in arr) + assertEq(arr[i], proxy[i]); +} + +testArray([-1]); +testArray([5, -1, 2, 99]); +testArray([5, -1, , , , 2, 99]); +testArray([]); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/sort_small.js b/js/src/tests/non262/Array/sort_small.js new file mode 100644 index 0000000000..fa8b789e10 --- /dev/null +++ b/js/src/tests/non262/Array/sort_small.js @@ -0,0 +1,33 @@ +// Sort every possible permutation of some arrays. +function sortAllPermutations(data, comparefn) { + for (let permutation of Permutations(Array.from(data))) { + let sorted = (Array.from(permutation)).sort(comparefn); + for (let i in sorted) { + assertEq(sorted[i], data[i], + [`[${permutation}].sort(${comparefn})`, + `returned ${sorted}, expected ${data}`].join(' ')); + } + } +} + +let lex = [2112, "bob", "is", "my", "name"]; +let nans = [1/undefined, NaN, Number.NaN] + +let num1 = [-11, 0, 0, 100, 101]; +let num2 = [-11, 100, 201234.23, undefined, undefined]; + +sortAllPermutations(lex); +sortAllPermutations(nans); + +sortAllPermutations(nans, (x, y) => x - y); +// Multiplication kills comparator optimization. +sortAllPermutations(nans, (x, y) => (1*x - 1*y)); + +sortAllPermutations(num1, (x, y) => x - y); +sortAllPermutations(num1, (x, y) => (1*x - 1*y)); + +sortAllPermutations(num2, (x, y) => x - y); +sortAllPermutations(num2, (x, y) => (1*x - 1*y)); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/species.js b/js/src/tests/non262/Array/species.js new file mode 100644 index 0000000000..3669c286f6 --- /dev/null +++ b/js/src/tests/non262/Array/species.js @@ -0,0 +1,182 @@ +var BUGNUMBER = 1165052; +var summary = 'Use ArraySpeciesCreate in Array.prototype.{concat,filter,map,slice,splice}.'; + +print(BUGNUMBER + ": " + summary); + +function test(funcName, args, expectedLength, expectedLogs) { + // modified @@species + function FakeArray(n) { + this.length = n; + } + var a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: FakeArray + }; + var b = a[funcName](...args); + assertEq(b.constructor, FakeArray); + + function FakeArrayWithSpecies(n) { + this.length = n; + } + FakeArrayWithSpecies[Symbol.species] = FakeArrayWithSpecies; + a = [1, 2, 3, 4, 5]; + a.constructor = FakeArrayWithSpecies; + b = a[funcName](...args); + assertEq(b.constructor, FakeArrayWithSpecies); + + function FakeArrayWithHook(n) { + return new Proxy(new FakeArray(n), { + set(that, name, value) { + logs += "set:" + name + ":" + value + ","; + return true; + }, + defineProperty(that, name, desc) { + logs += "define:" + name + ":" + desc.value + ":" + desc.configurable + ":" + desc.enumerable + ":" + desc.writable + ","; + return true; + } + }); + } + var logs = ""; + var ctorProxy = new Proxy({}, { + get(that, name) { + logs += "c-get:" + name.toString() + ","; + if (name == Symbol.species) + return FakeArrayWithHook; + + return undefined; + } + }); + a = new Proxy([1, 2, 3, 4, 5], { + get(that, name) { + logs += "get:" + name.toString() + ","; + if (name == "constructor") + return ctorProxy; + return that[name]; + } + }); + b = a[funcName](...args); + assertEq(b.constructor, FakeArray); + assertEq(Object.keys(b).sort().join(","), "length"); + assertEq(b.length, expectedLength); + assertEq(logs, expectedLogs); + + // no @@species + a = [1, 2, 3, 4, 5]; + a.constructor = FakeArray; + b = a[funcName](...args); + assertEq(b.constructor, Array); + + a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: undefined + }; + b = a[funcName](...args); + assertEq(b.constructor, Array); + + a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: null + }; + b = a[funcName](...args); + assertEq(b.constructor, Array); + + // invalid @@species + for (var species of [0, 1.1, true, false, "a", /a/, Symbol.iterator, [], {}]) { + a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: species + }; + assertThrowsInstanceOf(() => a[funcName](...args), TypeError); + } + + // undefined constructor + a = [1, 2, 3, 4, 5]; + a.constructor = undefined; + b = a[funcName](...args); + assertEq(b.constructor, Array); + + // invalid constructor + for (var ctor of [null, 0, 1.1, true, false, "a", Symbol.iterator]) { + a = [1, 2, 3, 4, 5]; + a.constructor = ctor; + assertThrowsInstanceOf(() => a[funcName](...args), TypeError); + } + + // not an array + a = new Proxy({ + 0: 1, 1: 2, 2: 3, 3: 4, 4: 5, + length: 5, + [funcName]: Array.prototype[funcName] + }, { + get(that, name) { + assertEq(name !== "constructor", true); + return that[name]; + } + }); + b = a[funcName](...args); + assertEq(b.constructor, Array); + + // @@species from different global + var g = newGlobal(); + g.eval("function FakeArray(n) { this.length = n; }"); + a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: g.FakeArray + }; + b = a[funcName](...args); + assertEq(b.constructor, g.FakeArray); + + a = [1, 2, 3, 4, 5]; + a.constructor = { + [Symbol.species]: g.Array + }; + b = a[funcName](...args); + assertEq(b.constructor, g.Array); + + // constructor from different global + g.eval("function FakeArrayWithSpecies(n) { this.length = n; }"); + g.eval("FakeArrayWithSpecies[Symbol.species] = FakeArrayWithSpecies;"); + a = [1, 2, 3, 4, 5]; + a.constructor = g.FakeArrayWithSpecies; + b = a[funcName](...args); + assertEq(b.constructor, g.FakeArrayWithSpecies); + + g.eval("var a = [1, 2, 3, 4, 5];"); + b = Array.prototype[funcName].apply(g.a, args); + assertEq(b.constructor, Array); + + // running in different global + b = g.a[funcName](...args); + assertEq(b.constructor, g.Array); + + // subclasses + // not-modified @@species + eval(` +class ${funcName}Class extends Array { +} +a = new ${funcName}Class(1, 2, 3, 4, 5); +b = a[funcName](...args); +assertEq(b.constructor, ${funcName}Class); +`); + + // modified @@species + eval(` +class ${funcName}Class2 extends Array { + static get [Symbol.species]() { + return Date; + } +} +a = new ${funcName}Class2(1, 2, 3, 4, 5); +b = a[funcName](...args); +assertEq(b.constructor, Date); +`); +} + +test("concat", [], 0, "get:concat,get:constructor,c-get:Symbol(Symbol.species),get:Symbol(Symbol.isConcatSpreadable),get:length,get:0,define:0:1:true:true:true,get:1,define:1:2:true:true:true,get:2,define:2:3:true:true:true,get:3,define:3:4:true:true:true,get:4,define:4:5:true:true:true,set:length:5,"); +test("filter", [x => x % 2], 0, "get:filter,get:length,get:constructor,c-get:Symbol(Symbol.species),get:0,define:0:1:true:true:true,get:1,get:2,define:1:3:true:true:true,get:3,get:4,define:2:5:true:true:true,"); +test("map", [x => x * 2], 5, "get:map,get:length,get:constructor,c-get:Symbol(Symbol.species),get:0,define:0:2:true:true:true,get:1,define:1:4:true:true:true,get:2,define:2:6:true:true:true,get:3,define:3:8:true:true:true,get:4,define:4:10:true:true:true,"); +test("slice", [], 5, "get:slice,get:length,get:constructor,c-get:Symbol(Symbol.species),get:0,define:0:1:true:true:true,get:1,define:1:2:true:true:true,get:2,define:2:3:true:true:true,get:3,define:3:4:true:true:true,get:4,define:4:5:true:true:true,set:length:5,"); +test("splice", [0, 5], 5, "get:splice,get:length,get:constructor,c-get:Symbol(Symbol.species),get:0,define:0:1:true:true:true,get:1,define:1:2:true:true:true,get:2,define:2:3:true:true:true,get:3,define:3:4:true:true:true,get:4,define:4:5:true:true:true,set:length:5,"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/splice-return-array-elements-defined-not-set.js b/js/src/tests/non262/Array/splice-return-array-elements-defined-not-set.js new file mode 100644 index 0000000000..2f5eca610d --- /dev/null +++ b/js/src/tests/non262/Array/splice-return-array-elements-defined-not-set.js @@ -0,0 +1,46 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 668024; +var summary = + 'Array.prototype.splice should define, not set, the elements of the array ' + + 'it returns'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +Object.defineProperty(Object.prototype, 2, + { + set: function(v) + { + throw new Error("setter on Object.prototype called!"); + }, + get: function() { return "fnord"; }, + enumerable: false, + configurable: true + }); + +var arr = [0, 1, 2, 3, 4, 5]; +var removed = arr.splice(0, 6); + +assertEq(arr.length, 0); +assertEq(removed.length, 6); +assertEq(removed[0], 0); +assertEq(removed[1], 1); +assertEq(removed[2], 2); +assertEq(removed[3], 3); +assertEq(removed[4], 4); +assertEq(removed[5], 5); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/splice-species-changes-length.js b/js/src/tests/non262/Array/splice-species-changes-length.js new file mode 100644 index 0000000000..0e4ed32b01 --- /dev/null +++ b/js/src/tests/non262/Array/splice-species-changes-length.js @@ -0,0 +1,48 @@ +// Case 1: splice() removes an element from the array. +{ + let array = []; + array.push(0, 1, 2); + + array.constructor = { + [Symbol.species]: function(n) { + // Increase the initialized length of the array. + array.push(3, 4, 5); + + // Make the length property non-writable. + Object.defineProperty(array, "length", {writable: false}); + + return new Array(n); + } + } + + assertThrowsInstanceOf(() => Array.prototype.splice.call(array, 0, 1), TypeError); + + assertEq(array.length, 6); + assertEqArray(array, [1, 2, /* hole */, 3, 4, 5]); +} + +// Case 2: splice() adds an element to the array. +{ + let array = []; + array.push(0, 1, 2); + + array.constructor = { + [Symbol.species]: function(n) { + // Increase the initialized length of the array. + array.push(3, 4, 5); + + // Make the length property non-writable. + Object.defineProperty(array, "length", {writable: false}); + + return new Array(n); + } + } + + assertThrowsInstanceOf(() => Array.prototype.splice.call(array, 0, 0, 123), TypeError); + + assertEq(array.length, 6); + assertEqArray(array, [123, 0, 1, 2, 4, 5]); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/splice-suppresses-unvisited-indexes.js b/js/src/tests/non262/Array/splice-suppresses-unvisited-indexes.js new file mode 100644 index 0000000000..717116fa0a --- /dev/null +++ b/js/src/tests/non262/Array/splice-suppresses-unvisited-indexes.js @@ -0,0 +1,61 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 668024; +var summary = + 'Array.prototype.splice, when it deletes elements, should make sure any ' + + 'deleted but not visited elements are suppressed from subsequent enumeration'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr = [0, 1, 2, 3, 4, 5, , 7]; + +var seen = []; +var sawOneBeforeThree = true; +for (var p in arr) +{ + if (p === "1") + { + // The order of enumeration of properties is unspecified, so technically, + // it would be kosher to enumerate "1" last, say, such that all properties + // in the array actually were enumerated, including an index which splice + // would delete. Don't flag that case as a failure. (SpiderMonkey doesn't + // do this, and neither do any of the other browser engines, but it is + // permissible behavior.) + if (seen.indexOf("3") >= 0) + { + sawOneBeforeThree = false; + break; + } + + arr.splice(2, 3); + } + + seen.push(p); +} + +if (sawOneBeforeThree) +{ + // ES5 12.6.4 states: + // + // If a property that has not yet been visited during enumeration is + // deleted, then it will not be visited. + // + // So if we haven't seen "3" by the time we see "1", the splice call above + // will delete "3", and therefore we must not see it. + assertEq(seen.indexOf("3"), -1); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/Array/to-length.js b/js/src/tests/non262/Array/to-length.js new file mode 100644 index 0000000000..d6b4c6d7cd --- /dev/null +++ b/js/src/tests/non262/Array/to-length.js @@ -0,0 +1,40 @@ +const max = Number.MAX_SAFE_INTEGER; + +assertEq(Array.prototype.indexOf.call({length: Infinity, [max - 1]: 'test'}, 'test', max - 3), max - 1); +assertEq(Array.prototype.lastIndexOf.call({length: Infinity, [max - 2]: 'test', [max - 1]: 'test2'}, 'test'), max - 2); + +// ToLength doesn't truncate Infinity to zero, so the callback should be invoked +assertThrowsValue(() => Array.prototype.every.call({length: Infinity, [0]: undefined}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.some.call({length: Infinity, [0]: undefined}, () => { throw "invoked" }), "invoked"); +// Timeout before calling our callback +// assertThrowsValue(() => Array.prototype.sort.call({length: Infinity}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.forEach.call({length: Infinity, [0]: undefined}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.filter.call({length: Infinity, [0]: undefined}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.reduce.call({length: Infinity, [0]: undefined, [1]: undefined}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.reduceRight.call({length: Infinity, [max - 1]: undefined, [max - 2]: undefined}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.find.call({length: Infinity}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.findIndex.call({length: Infinity}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.fill.call({length: Infinity, set "0"(v) { throw "invoked"; }}, () => { throw "invoked" }), "invoked"); +assertThrowsValue(() => Array.prototype.copyWithin.call({length: Infinity, get [max - 2]() { throw "invoked"; }}, max - 2, max - 2), "invoked"); + +assertEq(Array.prototype.includes.call({length: Infinity, [max - 1]: "test"}, "test", max - 3), true); + +// Invoking the Array constructor with MAX_SAFE_INTEGER will throw, 0 won't +assertThrowsInstanceOf(() => Array.from({length: Infinity}), RangeError); + +// Make sure ArraySpeciesCreate is called with ToLength applied to the length property +var proxy = new Proxy([], { + get(target, property) { + if (property === "length") + return Infinity; + + assertEq(property, "constructor"); + function fakeConstructor(length) { assertEq(length, max); throw "invoked"; }; + fakeConstructor[Symbol.species] = fakeConstructor; + return fakeConstructor; + } +}) +assertThrowsValue(() => Array.prototype.map.call(proxy, () => { }), "invoked"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/toLocaleString-01.js b/js/src/tests/non262/Array/toLocaleString-01.js new file mode 100644 index 0000000000..458283e635 --- /dev/null +++ b/js/src/tests/non262/Array/toLocaleString-01.js @@ -0,0 +1,36 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 562446; +var summary = 'ES5: Array.prototype.toLocaleString'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var o; + +o = { length: 2, 0: 7, 1: { toLocaleString: function() { return "baz" } } }; +assertEq(Array.prototype.toLocaleString.call(o), "7,baz"); + +o = {}; +assertEq(Array.prototype.toLocaleString.call(o), ""); + +var log = ''; +arr = {length: {valueOf: function () { log += "L"; return 2; }}, + 0: "x", 1: "z"}; +assertEq(Array.prototype.toLocaleString.call(arr), "x,z"); +assertEq(log, "L"); + +/******************************************************************************/ + +reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/toLocaleString-nointl.js b/js/src/tests/non262/Array/toLocaleString-nointl.js new file mode 100644 index 0000000000..b0cee83049 --- /dev/null +++ b/js/src/tests/non262/Array/toLocaleString-nointl.js @@ -0,0 +1,26 @@ +if (typeof Intl !== "object") { + const localeSep = [,,].toLocaleString(); + + const obj = { + toLocaleString() { + assertEq(arguments.length, 0); + return "pass"; + } + }; + + // Ensure no arguments are passed to the array elements. + // - Single element case. + assertEq([obj].toLocaleString(), "pass"); + // - More than one element. + assertEq([obj, obj].toLocaleString(), "pass" + localeSep + "pass"); + + // Ensure no arguments are passed to the array elements even if supplied. + const locales = {}, options = {}; + // - Single element case. + assertEq([obj].toLocaleString(locales, options), "pass"); + // - More than one element. + assertEq([obj, obj].toLocaleString(locales, options), "pass" + localeSep + "pass"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/toLocaleString.js b/js/src/tests/non262/Array/toLocaleString.js new file mode 100644 index 0000000000..4a16cb4a49 --- /dev/null +++ b/js/src/tests/non262/Array/toLocaleString.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(String.prototype, "toLocaleString", { + get() { + assertEq(typeof this, "string"); + + return function() { return typeof this; }; + } +}) + +assertEq(["test"].toLocaleString(), "string"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/toSpliced-dense.js b/js/src/tests/non262/Array/toSpliced-dense.js new file mode 100644 index 0000000000..1108f1b5f8 --- /dev/null +++ b/js/src/tests/non262/Array/toSpliced-dense.js @@ -0,0 +1,127 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.toSpliced) + +const startIndices = [ + -10, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 10, +]; + +const deleteCounts = [ + 0, 1, 2, 3, 4, 5, 10, +]; + +const insertCounts = [ + 0, 1, 2, 3, 4, 5, 10, +]; + +const itemsList = insertCounts.map(count => { + return new Array(count).fill(0); +}); + +const arrays = [ + // Dense no holes. + [], + [1], + [1,2], + [1,2,3], + [1,2,3,4], + [1,2,3,4,5,6,7,8], + + // Dense trailing holes. + [,], + [1,,], + [1,2,,], + [1,2,3,,], + [1,2,3,4,,], + [1,2,3,4,5,6,7,8,,], + + // Dense leading holes. + [,], + [,1], + [,1,2], + [,1,2,3], + [,1,2,3,4], + [,1,2,3,4,5,6,7,8], + + // Dense with holes. + [1,,3], + [1,2,,4], + [1,,3,,5,6,,8], +]; + +const objects = arrays.map(array => { + let obj = { + length: array.length, + }; + for (let i = 0; i < array.length; ++i) { + if (i in array) { + obj[i] = array[i]; + } + } + return obj; +}); + +const objectsWithLargerDenseInitializedLength = arrays.map(array => { + let obj = { + length: array.length, + }; + for (let i = 0; i < array.length; ++i) { + if (i in array) { + obj[i] = array[i]; + } + } + + // Add some extra dense elements after |length|. + for (let i = 0; i < 5; ++i) { + obj[array.length + i] = "extra"; + } + + return obj; +}); + +const thisValues = [ + ...arrays, + ...objects, + ...objectsWithLargerDenseInitializedLength, +]; + +for (let thisValue of thisValues) { + for (let startIndex of startIndices) { + for (let deleteCount of deleteCounts) { + for (let items of itemsList) { + let res = Array.prototype.toSpliced.call(thisValue, startIndex, deleteCount, ...items); + + // Array.prototype.toSpliced(), steps 3-6. + let actualStart; + if (startIndex < 0) { + actualStart = Math.max(thisValue.length + startIndex, 0); + } else { + actualStart = Math.min(startIndex, thisValue.length); + } + + // Array.prototype.toSpliced(), step 10. + let actualDeleteCount = Math.min(Math.max(0, deleteCount), thisValue.length - actualStart); + + let newLength = thisValue.length + items.length - actualDeleteCount; + assertEq(res.length, newLength); + + for (let i = 0; i < actualStart; ++i) { + assertEq(Object.hasOwn(res, i), true); + assertEq(res[i], thisValue[i]); + } + + for (let i = 0; i < items.length; ++i) { + assertEq(Object.hasOwn(res, actualStart + i), true); + assertEq(res[actualStart + i], items[i]); + } + + for (let i = 0; i < newLength - actualStart - items.length; ++i) { + assertEq(Object.hasOwn(res, actualStart + items.length + i), true); + assertEq(res[actualStart + items.length + i], + thisValue[actualStart + actualDeleteCount + i]); + } + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/toSpliced.js b/js/src/tests/non262/Array/toSpliced.js new file mode 100644 index 0000000000..7c8faf917b --- /dev/null +++ b/js/src/tests/non262/Array/toSpliced.js @@ -0,0 +1,27 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.toSpliced) + +Object.defineProperty(Array.prototype, 0, { + set() { + throw "bad 0"; + }, +}); + +Object.defineProperty(Array.prototype, 1, { + set() { + throw "bad 1"; + }, +}); + +assertDeepEq([].toSpliced(0, 0, 1), [1]); + +assertDeepEq([0].toSpliced(0, 0, 0), [0, 0]); +assertDeepEq([0].toSpliced(0, 0, 1), [1, 0]); +assertDeepEq([0].toSpliced(0, 1, 0), [0]); +assertDeepEq([0].toSpliced(0, 1, 1), [1]); +assertDeepEq([0].toSpliced(1, 0, 0), [0, 0]); +assertDeepEq([0].toSpliced(1, 0, 1), [0, 1]); +assertDeepEq([0].toSpliced(1, 1, 0), [0, 0]); +assertDeepEq([0].toSpliced(1, 1, 1), [0, 1]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/toString-01.js b/js/src/tests/non262/Array/toString-01.js new file mode 100644 index 0000000000..a92fe6369b --- /dev/null +++ b/js/src/tests/non262/Array/toString-01.js @@ -0,0 +1,52 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden <jwalden+code@mit.edu> + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 562446; +var summary = 'ES5: Array.prototype.toString'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var o; + +o = { join: function() { assertEq(arguments.length, 0); return "ohai"; } }; +assertEq(Array.prototype.toString.call(o), "ohai"); + +o = {}; +assertEq(Array.prototype.toString.call(o), "[object Object]"); + +Array.prototype.join = function() { return "kthxbai"; }; +assertEq(Array.prototype.toString.call([]), "kthxbai"); + +o = { join: 17 }; +assertEq(Array.prototype.toString.call(o), "[object Object]"); + +o = { get join() { throw 42; } }; +try +{ + var str = Array.prototype.toString.call(o); + assertEq(true, false, + "expected an exception calling [].toString on an object with a " + + "join getter that throws, got " + str + " instead"); +} +catch (e) +{ + assertEq(e, 42, + "expected thrown e === 42 when calling [].toString on an object " + + "with a join getter that throws, got " + e); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/unscopables.js b/js/src/tests/non262/Array/unscopables.js new file mode 100644 index 0000000000..43bc983ea5 --- /dev/null +++ b/js/src/tests/non262/Array/unscopables.js @@ -0,0 +1,73 @@ +let Array_unscopables = Array.prototype[Symbol.unscopables]; + +let desc = Reflect.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables); +assertDeepEq(desc, { + value: Array_unscopables, + writable: false, + enumerable: false, + configurable: true +}); + +assertEq(Reflect.getPrototypeOf(Array_unscopables), null); + +let desc2 = Object.getOwnPropertyDescriptor(Array_unscopables, "values"); +assertDeepEq(desc2, { + value: true, + writable: true, + enumerable: true, + configurable: true +}); + +let keys = Reflect.ownKeys(Array_unscopables); + +// FIXME: Once bug 1826643 is fixed, change this test so that all +// the keys are in alphabetical order +let expectedKeys = ["at", + "copyWithin", + "entries", + "fill", + "find", + "findIndex", + "findLast", + "findLastIndex", + "flat", + "flatMap", + "includes", + "keys", + "values", + "toReversed", + "toSorted", + "toSpliced"]; + +if (typeof getBuildConfiguration === "undefined") { + var getBuildConfiguration = SpecialPowers.Cu.getJSTestingFunctions().getBuildConfiguration; +} + +if (typeof getRealmConfiguration === "undefined") { + var getRealmConfiguration = SpecialPowers.Cu.getJSTestingFunctions().getRealmConfiguration; +} + +if (!getBuildConfiguration().release_or_beta && getRealmConfiguration().enableArrayGrouping) { + expectedKeys.push("group", "groupToMap"); +} + +assertDeepEq(keys, expectedKeys); + +for (let key of keys) + assertEq(Array_unscopables[key], true); + +// Test that it actually works +assertThrowsInstanceOf(() => { + with ([]) { + return entries; + } +}, ReferenceError); + +{ + let fill = 33; + with (Array.prototype) { + assertEq(fill, 33); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/unshift-01.js b/js/src/tests/non262/Array/unshift-01.js new file mode 100644 index 0000000000..fbae3294a7 --- /dev/null +++ b/js/src/tests/non262/Array/unshift-01.js @@ -0,0 +1,44 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 614070; +var summary = 'Array.prototype.unshift without args'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +// ES6 ToLength clamps length values to 2^53 - 1. +var MAX_LENGTH = 2**53 - 1; + +var a = {}; +a.length = MAX_LENGTH + 1; +assertEq([].unshift.call(a), MAX_LENGTH); +assertEq(a.length, MAX_LENGTH); + +function testGetSet(len, expected) { + var newlen; + var a = { get length() { return len; }, set length(v) { newlen = v; } }; + var res = [].unshift.call(a); + assertEq(res, expected); + assertEq(newlen, expected); +} + +testGetSet(0, 0); +testGetSet(10, 10); +testGetSet("1", 1); +testGetSet(null, 0); +testGetSet(MAX_LENGTH + 2, MAX_LENGTH); +testGetSet(-5, 0); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/Array/unshift-with-enumeration.js b/js/src/tests/non262/Array/unshift-with-enumeration.js new file mode 100644 index 0000000000..fbf451b8cc --- /dev/null +++ b/js/src/tests/non262/Array/unshift-with-enumeration.js @@ -0,0 +1,18 @@ +function f(array, method, args) { + var called = false; + var keys = []; + for (var key in array) { + keys.push(key); + if (!called) { + called = true; + Reflect.apply(method, array, args); + } + } + return keys; +} + +assertEqArray(f([1, /* hole */, 3], Array.prototype.unshift, [0]), ["0"]); +assertEqArray(f([1, /* hole */, 3], Array.prototype.splice, [0, 0, 0]), ["0"]); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Array/values.js b/js/src/tests/non262/Array/values.js new file mode 100644 index 0000000000..902a668123 --- /dev/null +++ b/js/src/tests/non262/Array/values.js @@ -0,0 +1,20 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +if (Array.prototype.values) { + assertEq(Array.prototype.values, Array.prototype[Symbol.iterator]); + assertEq(Array.prototype.values.name, "values"); + assertEq(Array.prototype.values.length, 0); + + function valuesUnscopeable() { + var values = "foo"; + with ([1, 2, 3]) { + assertEq(indexOf, Array.prototype.indexOf); + assertEq(values, "foo"); + } + } + valuesUnscopeable(); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/with-dense.js b/js/src/tests/non262/Array/with-dense.js new file mode 100644 index 0000000000..6ace2a8457 --- /dev/null +++ b/js/src/tests/non262/Array/with-dense.js @@ -0,0 +1,103 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.with) + +const indices = [ + -10, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 10, +]; + +const arrays = [ + // Dense no holes. + [], + [1], + [1,2], + [1,2,3], + [1,2,3,4], + [1,2,3,4,5,6,7,8], + + // Dense trailing holes. + [,], + [1,,], + [1,2,,], + [1,2,3,,], + [1,2,3,4,,], + [1,2,3,4,5,6,7,8,,], + + // Dense leading holes. + [,], + [,1], + [,1,2], + [,1,2,3], + [,1,2,3,4], + [,1,2,3,4,5,6,7,8], + + // Dense with holes. + [1,,3], + [1,2,,4], + [1,,3,,5,6,,8], +]; + +const objects = arrays.map(array => { + let obj = { + length: array.length, + }; + for (let i = 0; i < array.length; ++i) { + if (i in array) { + obj[i] = array[i]; + } + } + return obj; +}); + +const objectsWithLargerDenseInitializedLength = arrays.map(array => { + let obj = { + length: array.length, + }; + for (let i = 0; i < array.length; ++i) { + if (i in array) { + obj[i] = array[i]; + } + } + + // Add some extra dense elements after |length|. + for (let i = 0; i < 5; ++i) { + obj[array.length + i] = "extra"; + } + + return obj; +}); + +const thisValues = [ + ...arrays, + ...objects, + ...objectsWithLargerDenseInitializedLength, +]; + +const replacement = {}; + +for (let thisValue of thisValues) { + for (let index of indices) { + let actualIndex = index; + if (actualIndex < 0) { + actualIndex += thisValue.length; + } + + if (actualIndex < 0 || actualIndex >= thisValue.length) { + continue; + } + + let res = Array.prototype.with.call(thisValue, index, replacement); + assertEq(res.length, thisValue.length); + + for (let i = 0; i < thisValue.length; ++i) { + assertEq(Object.hasOwn(res, i), true); + + if (i === actualIndex) { + assertEq(res[i], replacement); + } else { + assertEq(res[i], thisValue[i]); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/Array/with.js b/js/src/tests/non262/Array/with.js new file mode 100644 index 0000000000..a50fba74c0 --- /dev/null +++ b/js/src/tests/non262/Array/with.js @@ -0,0 +1,17 @@ +// |reftest| shell-option(--enable-change-array-by-copy) skip-if(!Array.prototype.with) + +Object.defineProperty(Array.prototype, 0, { + set() { + throw "bad"; + }, +}); + +// Single element case. +assertDeepEq([0].with(0, 1), [1]); + +// More than one element. +assertDeepEq([1, 2].with(0, 3), [3, 2]); +assertDeepEq([1, 2].with(1, 3), [1, 3]); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/ArrayBuffer/CloneArrayBuffer.js b/js/src/tests/non262/ArrayBuffer/CloneArrayBuffer.js new file mode 100644 index 0000000000..480314ad8e --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/CloneArrayBuffer.js @@ -0,0 +1,35 @@ +var BUGNUMBER = 1264941; +var summary = 'CloneArrayBuffer should be called with byteLength of source typedArray'; + +print(BUGNUMBER + ": " + summary); + +function test(ctor, byteLength) { + var abuf = new ctor(byteLength); + assertEq(abuf.byteLength, byteLength); + + for (var byteOffset of [0, 16]) { + for (var elementLength = 0; + elementLength < (byteLength - byteOffset) / Float64Array.BYTES_PER_ELEMENT; + elementLength++) { + var a1 = new Float64Array(abuf, byteOffset, elementLength); + assertEq(a1.buffer.byteLength, byteLength); + assertEq(a1.byteLength, elementLength * Float64Array.BYTES_PER_ELEMENT); + assertEq(a1.byteOffset, byteOffset); + + var a2 = new Float64Array(a1); + assertEq(a2.buffer.byteLength, a1.byteLength); + assertEq(a2.byteLength, a1.byteLength); + assertEq(a2.byteOffset, 0); + } + } +} + +test(ArrayBuffer, 16); +test(ArrayBuffer, 128); + +class MyArrayBuffer extends ArrayBuffer {} +test(MyArrayBuffer, 16); +test(MyArrayBuffer, 128); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/ArrayBuffer/browser.js b/js/src/tests/non262/ArrayBuffer/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/browser.js diff --git a/js/src/tests/non262/ArrayBuffer/bug1689503.js b/js/src/tests/non262/ArrayBuffer/bug1689503.js new file mode 100644 index 0000000000..475ae533cc --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/bug1689503.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs Debugger + +let g = newGlobal({ newCompartment: true }); +let dbg = new Debugger(g); +dbg.memory.trackingAllocationSites = true; +g.createExternalArrayBuffer(64); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/ArrayBuffer/bug1777413.js b/js/src/tests/non262/ArrayBuffer/bug1777413.js new file mode 100644 index 0000000000..3c58aecbfc --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/bug1777413.js @@ -0,0 +1,7 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs shell functions + +var b = createExternalArrayBuffer(0); +assertEq(b.byteLength, 0); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/ArrayBuffer/constructorNotCallable.js b/js/src/tests/non262/ArrayBuffer/constructorNotCallable.js new file mode 100644 index 0000000000..9df97fe867 --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/constructorNotCallable.js @@ -0,0 +1,8 @@ +assertThrowsInstanceOf(() => ArrayBuffer(), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer(1), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer.call(null), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer.apply(null, []), TypeError); +assertThrowsInstanceOf(() => Reflect.apply(ArrayBuffer, null, []), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/non262/ArrayBuffer/getter-name.js b/js/src/tests/non262/ArrayBuffer/getter-name.js new file mode 100644 index 0000000000..434782bfe8 --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/getter-name.js @@ -0,0 +1,10 @@ +var BUGNUMBER = 1180290; +var summary = 'ArrayBuffer getters should have get prefix'; + +print(BUGNUMBER + ": " + summary); + +assertEq(Object.getOwnPropertyDescriptor(ArrayBuffer, Symbol.species).get.name, "get [Symbol.species]"); +assertEq(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get.name, "get byteLength"); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/ArrayBuffer/shell.js b/js/src/tests/non262/ArrayBuffer/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/shell.js diff --git a/js/src/tests/non262/ArrayBuffer/slice-species.js b/js/src/tests/non262/ArrayBuffer/slice-species.js new file mode 100644 index 0000000000..6f025d162c --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/slice-species.js @@ -0,0 +1,180 @@ +const tests = [ + [Int8Array, [9, 10, 11, 12, 13, 14, 15, 16]], + [Uint8Array, [9, 10, 11, 12, 13, 14, 15, 16]], + [Uint8ClampedArray, [9, 10, 11, 12, 13, 14, 15, 16]], + [Int16Array, [5, 6, 7, 8]], + [Uint16Array, [5, 6, 7, 8]], + [Int32Array, [3, 4]], + [Uint32Array, [3, 4]], + [Float32Array, [3, 4]], + [Float64Array, [2]], +]; + +let logs = []; +for (let [ctor, answer] of tests) { + let arr = new ctor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + + let proxyProto = new Proxy({}, { + get(that, name) { + throw new Error("unexpected prop access"); + } + }); + + class MyArrayBuffer extends ArrayBuffer {} + + arr.buffer.constructor = new Proxy({}, { + get(that, name) { + if (name == Symbol.species) { + logs.push("get @@species"); + let C = new Proxy(function(...args) { + logs.push("call ctor"); + return new MyArrayBuffer(...args); + }, { + get(that, name) { + logs.push("get ctor." + String(name)); + if (name == "prototype") { + return proxyProto; + } + throw new Error("unexpected prop access"); + } + }); + return C; + } + throw new Error("unexpected prop access"); + } + }); + + logs.length = 0; + let buf = arr.buffer.slice(8, 16); + assertEq(buf.constructor, MyArrayBuffer); + assertDeepEq(logs, ["get @@species", "get ctor.prototype", "call ctor"]); + assertDeepEq([...new ctor(buf)], answer); + + + // modified @@species + let a = arr.buffer; + a.constructor = { + [Symbol.species]: MyArrayBuffer + }; + let b = a.slice(8, 16); + assertEq(b.constructor, MyArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + class MyArrayBufferWithSpecies extends ArrayBuffer { + get [Symbol.species]() { + return MyArrayBufferWithSpecies; + } + } + a = arr.buffer; + a.constructor = MyArrayBufferWithSpecies; + b = a.slice(8, 16); + assertEq(b.constructor, MyArrayBufferWithSpecies); + assertDeepEq([...new ctor(b)], answer); + + // no @@species + a = arr.buffer; + a.constructor = { + [Symbol.species]: undefined + }; + b = a.slice(8, 16); + assertEq(b.constructor, ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + a = arr.buffer; + a.constructor = { + [Symbol.species]: null + }; + b = a.slice(8, 16); + assertEq(b.constructor, ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + // invalid @@species + for (let species of [0, 1.1, true, false, "a", /a/, Symbol.iterator, [], {}]) { + a = arr.buffer; + a.constructor = { + [Symbol.species]: species + }; + assertThrowsInstanceOf(() => a.slice(8, 16), TypeError); + } + + // undefined constructor + a = arr.buffer; + a.constructor = undefined; + b = a.slice(8, 16); + assertEq(b.constructor, ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + // invalid constructor + for (let ctor of [null, 0, 1.1, true, false, "a", Symbol.iterator]) { + a = arr.buffer; + a.constructor = ctor; + assertThrowsInstanceOf(() => a.slice(8, 16), TypeError); + } + + // @@species from different global + let g = newGlobal(); + g.eval("var MyArrayBuffer = class MyArrayBuffer extends ArrayBuffer {};"); + a = arr.buffer; + a.constructor = { + [Symbol.species]: g.MyArrayBuffer + }; + b = a.slice(8, 16); + assertEq(b.constructor, g.MyArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + a = arr.buffer; + a.constructor = { + [Symbol.species]: g.ArrayBuffer + }; + b = a.slice(8, 16); + assertEq(b.constructor, g.ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + // constructor from different global + g.eval(` +var MyArrayBufferWithSpecies = class MyArrayBufferWithSpecies extends ArrayBuffer { + get [Symbol.species]() { + return MyArrayBufferWithSpecies; + } +}; +`); + a = arr.buffer; + a.constructor = g.MyArrayBufferWithSpecies; + b = a.slice(8, 16); + assertEq(b.constructor, g.MyArrayBufferWithSpecies); + assertDeepEq([...new ctor(b)], answer); + + g.eval(` +var arr = new ${ctor.name}([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); +var a = arr.buffer; +`); + b = ArrayBuffer.prototype.slice.call(g.a, 8, 16); + assertEq(b.constructor, g.ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + // running in different global + b = g.a.slice(8, 16); + assertEq(b.constructor, g.ArrayBuffer); + assertDeepEq([...new ctor(b)], answer); + + // subclasses + // not-modified @@species + a = new MyArrayBuffer(16); + b = a.slice(8, 16); + assertEq(b.constructor, MyArrayBuffer); + + // modified @@species + class MyArrayBuffer2 extends ArrayBuffer { + } + class MyArrayBuffer3 extends ArrayBuffer { + static get [Symbol.species]() { + return MyArrayBuffer2; + } + } + a = new MyArrayBuffer3(16); + b = a.slice(8, 16); + assertEq(b.constructor, MyArrayBuffer2); +} + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); |