diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/tests/ecma_6/Array | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/tests/ecma_6/Array')
39 files changed, 2021 insertions, 0 deletions
diff --git a/js/src/tests/ecma_6/Array/browser.js b/js/src/tests/ecma_6/Array/browser.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/js/src/tests/ecma_6/Array/browser.js diff --git a/js/src/tests/ecma_6/Array/concat-proxy.js b/js/src/tests/ecma_6/Array/concat-proxy.js new file mode 100644 index 000000000..ab733e2f1 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/concat-spreadable-basic.js b/js/src/tests/ecma_6/Array/concat-spreadable-basic.js new file mode 100644 index 000000000..c13f8f5cd --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/concat-spreadable-primitive.js b/js/src/tests/ecma_6/Array/concat-spreadable-primitive.js new file mode 100644 index 000000000..d2264bfa2 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/fill.js b/js/src/tests/ecma_6/Array/fill.js new file mode 100644 index 000000000..70f1e0b52 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/find_findindex.js b/js/src/tests/ecma_6/Array/find_findindex.js new file mode 100644 index 000000000..6e32ea6b0 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/for_of_1.js b/js/src/tests/ecma_6/Array/for_of_1.js new file mode 100644 index 000000000..0233c1d27 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/for_of_2.js b/js/src/tests/ecma_6/Array/for_of_2.js new file mode 100644 index 000000000..1d5dd81fc --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/for_of_3.js b/js/src/tests/ecma_6/Array/for_of_3.js new file mode 100644 index 000000000..c90eb1f00 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/for_of_4.js b/js/src/tests/ecma_6/Array/for_of_4.js new file mode 100644 index 000000000..4572727a1 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_basics.js b/js/src/tests/ecma_6/Array/from_basics.js new file mode 100644 index 000000000..623207a41 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_constructor.js b/js/src/tests/ecma_6/Array/from_constructor.js new file mode 100644 index 000000000..6846ef09a --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_errors.js b/js/src/tests/ecma_6/Array/from_errors.js new file mode 100644 index 000000000..cbf0eb195 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_iterable.js b/js/src/tests/ecma_6/Array/from_iterable.js new file mode 100644 index 000000000..568181961 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_length_setter.js b/js/src/tests/ecma_6/Array/from_length_setter.js new file mode 100644 index 000000000..43f35f73e --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_mapping.js b/js/src/tests/ecma_6/Array/from_mapping.js new file mode 100644 index 000000000..568b9d6d7 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_primitive.js b/js/src/tests/ecma_6/Array/from_primitive.js new file mode 100644 index 000000000..e3f68d8bc --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_proxy.js b/js/src/tests/ecma_6/Array/from_proxy.js new file mode 100644 index 000000000..c3c82d0b7 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_realms.js b/js/src/tests/ecma_6/Array/from_realms.js new file mode 100644 index 000000000..e3a863606 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_string.js b/js/src/tests/ecma_6/Array/from_string.js new file mode 100644 index 000000000..3ad19e253 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_surfaces.js b/js/src/tests/ecma_6/Array/from_surfaces.js new file mode 100644 index 000000000..680b64817 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/from_this.js b/js/src/tests/ecma_6/Array/from_this.js new file mode 100644 index 000000000..978c90b87 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/getter-name.js b/js/src/tests/ecma_6/Array/getter-name.js new file mode 100644 index 000000000..3829c729b --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/indexOf-never-returns-negative-zero.js b/js/src/tests/ecma_6/Array/indexOf-never-returns-negative-zero.js new file mode 100644 index 000000000..0b39bf0a1 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/isArray.js b/js/src/tests/ecma_6/Array/isArray.js new file mode 100644 index 000000000..b752ad54e --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/iterator_edge_cases.js b/js/src/tests/ecma_6/Array/iterator_edge_cases.js new file mode 100644 index 000000000..156aaba5f --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/lastIndexOf-never-returns-negative-zero.js b/js/src/tests/ecma_6/Array/lastIndexOf-never-returns-negative-zero.js new file mode 100644 index 000000000..296f87ec1 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/shell.js b/js/src/tests/ecma_6/Array/shell.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/js/src/tests/ecma_6/Array/shell.js diff --git a/js/src/tests/ecma_6/Array/shift_for_in.js b/js/src/tests/ecma_6/Array/shift_for_in.js new file mode 100644 index 000000000..1bbf2d9c2 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/sort_basics.js b/js/src/tests/ecma_6/Array/sort_basics.js new file mode 100644 index 000000000..e3011e5c4 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/sort_holes.js b/js/src/tests/ecma_6/Array/sort_holes.js new file mode 100644 index 000000000..7424bd889 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/sort_proxy.js b/js/src/tests/ecma_6/Array/sort_proxy.js new file mode 100644 index 000000000..40ebb907b --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/sort_small.js b/js/src/tests/ecma_6/Array/sort_small.js new file mode 100644 index 000000000..fa8b789e1 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/species.js b/js/src/tests/ecma_6/Array/species.js new file mode 100644 index 000000000..3669c286f --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/to-length.js b/js/src/tests/ecma_6/Array/to-length.js new file mode 100644 index 000000000..d6b4c6d7c --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/toLocaleString-nointl.js b/js/src/tests/ecma_6/Array/toLocaleString-nointl.js new file mode 100644 index 000000000..b0cee8304 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/toLocaleString.js b/js/src/tests/ecma_6/Array/toLocaleString.js new file mode 100644 index 000000000..4a16cb4a4 --- /dev/null +++ b/js/src/tests/ecma_6/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/ecma_6/Array/unscopables.js b/js/src/tests/ecma_6/Array/unscopables.js new file mode 100644 index 000000000..6685309a0 --- /dev/null +++ b/js/src/tests/ecma_6/Array/unscopables.js @@ -0,0 +1,51 @@ +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); +print(uneval(keys)); +assertDeepEq(keys, [ + "copyWithin", + "entries", + "fill", + "find", + "findIndex", + "includes", + "keys", + "values" +]); + +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/ecma_6/Array/values.js b/js/src/tests/ecma_6/Array/values.js new file mode 100644 index 000000000..902a66812 --- /dev/null +++ b/js/src/tests/ecma_6/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); |