From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- js/src/tests/non262/Array/11.1.4.js | 68 +++ js/src/tests/non262/Array/15.4.4.5-1.js | 187 ++++++ js/src/tests/non262/Array/15.4.4.5-2.js | 189 ++++++ js/src/tests/non262/Array/15.4.4.5-3.js | 145 +++++ js/src/tests/non262/Array/array-001.js | 85 +++ .../non262/Array/array-length-set-during-for-in.js | 10 + .../non262/Array/array-length-set-on-nonarray.js | 26 + js/src/tests/non262/Array/at.js | 39 ++ js/src/tests/non262/Array/browser.js | 0 ...hange-array-by-copy-cross-compartment-create.js | 26 + ...ange-array-by-copy-errors-from-correct-realm.js | 77 +++ js/src/tests/non262/Array/concat-proxy.js | 25 + .../tests/non262/Array/concat-spreadable-basic.js | 37 ++ .../non262/Array/concat-spreadable-primitive.js | 34 ++ js/src/tests/non262/Array/fill.js | 97 +++ js/src/tests/non262/Array/filter.js | 53 ++ .../tests/non262/Array/findLast_findLastIndex.js | 285 +++++++++ js/src/tests/non262/Array/find_findindex.js | 285 +++++++++ js/src/tests/non262/Array/for_of_1.js | 138 +++++ js/src/tests/non262/Array/for_of_2.js | 58 ++ js/src/tests/non262/Array/for_of_3.js | 60 ++ js/src/tests/non262/Array/for_of_4.js | 64 ++ js/src/tests/non262/Array/from-iterator-close.js | 183 ++++++ js/src/tests/non262/Array/from_async.js | 302 ++++++++++ js/src/tests/non262/Array/from_basics.js | 51 ++ js/src/tests/non262/Array/from_constructor.js | 56 ++ js/src/tests/non262/Array/from_errors.js | 152 +++++ js/src/tests/non262/Array/from_iterable.js | 50 ++ js/src/tests/non262/Array/from_length_setter.js | 13 + js/src/tests/non262/Array/from_mapping.js | 41 ++ js/src/tests/non262/Array/from_primitive.js | 21 + js/src/tests/non262/Array/from_proxy.js | 55 ++ js/src/tests/non262/Array/from_realms.js | 37 ++ js/src/tests/non262/Array/from_string.js | 23 + js/src/tests/non262/Array/from_surfaces.js | 13 + js/src/tests/non262/Array/from_this.js | 48 ++ js/src/tests/non262/Array/frozen-dense-array.js | 42 ++ .../tests/non262/Array/frozen-dict-mode-length.js | 18 + js/src/tests/non262/Array/getter-name.js | 9 + .../non262/Array/group-callback-evaluation.js | 18 + .../non262/Array/group-propertkey-is-length.js | 15 + js/src/tests/non262/Array/group.js | 88 +++ .../tests/non262/Array/includes-trailing-holes.js | 16 + js/src/tests/non262/Array/includes.js | 59 ++ .../non262/Array/index-with-null-character.js | 18 + .../Array/indexOf-never-returns-negative-zero.js | 4 + js/src/tests/non262/Array/indexOf-packed-array.js | 40 ++ js/src/tests/non262/Array/isArray.js | 67 +++ js/src/tests/non262/Array/iterator_edge_cases.js | 50 ++ js/src/tests/non262/Array/join-01.js | 83 +++ js/src/tests/non262/Array/join-no-has-trap.js | 36 ++ .../lastIndexOf-never-returns-negative-zero.js | 4 + js/src/tests/non262/Array/length-01.js | 71 +++ .../Array/length-nonwritable-redefine-nop.js | 70 +++ js/src/tests/non262/Array/length-set-object.js | 68 +++ .../length-truncate-nonconfigurable-sparse.js | 110 ++++ .../Array/length-truncate-nonconfigurable.js | 48 ++ .../non262/Array/length-truncate-with-indexed.js | 101 ++++ js/src/tests/non262/Array/pop-empty-nonwritable.js | 32 + js/src/tests/non262/Array/pop-no-has-trap.js | 58 ++ .../non262/Array/pop-nonarray-higher-elements.js | 91 +++ .../tests/non262/Array/redefine-length-accessor.js | 42 ++ .../non262/Array/redefine-length-frozen-array.js | 26 + .../redefine-length-frozen-dictionarymode-array.js | 36 ++ ...ritable-length-custom-conversion-call-counts.js | 45 ++ ...e-nonwritable-length-custom-conversion-throw.js | 58 ++ .../redefine-nonwritable-length-nonnumeric.js | 32 + js/src/tests/non262/Array/regress-101488.js | 135 +++++ js/src/tests/non262/Array/regress-107138.js | 174 ++++++ js/src/tests/non262/Array/regress-108440.js | 77 +++ js/src/tests/non262/Array/regress-130451.js | 182 ++++++ js/src/tests/non262/Array/regress-154338.js | 87 +++ js/src/tests/non262/Array/regress-157652.js | 117 ++++ js/src/tests/non262/Array/regress-178722.js | 127 ++++ js/src/tests/non262/Array/regress-255555.js | 32 + js/src/tests/non262/Array/regress-290592.js | 661 +++++++++++++++++++++ js/src/tests/non262/Array/regress-299644.js | 27 + js/src/tests/non262/Array/regress-300858.js | 21 + js/src/tests/non262/Array/regress-304828.js | 256 ++++++++ js/src/tests/non262/Array/regress-305002.js | 25 + js/src/tests/non262/Array/regress-310351.js | 54 ++ js/src/tests/non262/Array/regress-310425-01.js | 27 + js/src/tests/non262/Array/regress-310425-02.js | 17 + js/src/tests/non262/Array/regress-311515.js | 20 + js/src/tests/non262/Array/regress-315509-01.js | 28 + js/src/tests/non262/Array/regress-322135-01.js | 39 ++ js/src/tests/non262/Array/regress-330812.js | 33 + js/src/tests/non262/Array/regress-345961.js | 33 + js/src/tests/non262/Array/regress-348810.js | 25 + js/src/tests/non262/Array/regress-350256-01.js | 42 ++ js/src/tests/non262/Array/regress-350256-02.js | 43 ++ js/src/tests/non262/Array/regress-352742-01.js | 36 ++ js/src/tests/non262/Array/regress-352742-02.js | 29 + js/src/tests/non262/Array/regress-360681-01.js | 30 + js/src/tests/non262/Array/regress-360681-02.js | 55 ++ js/src/tests/non262/Array/regress-364104.js | 74 +++ js/src/tests/non262/Array/regress-386030.js | 64 ++ js/src/tests/non262/Array/regress-387501.js | 48 ++ js/src/tests/non262/Array/regress-390598.js | 37 ++ js/src/tests/non262/Array/regress-415451.js | 24 + js/src/tests/non262/Array/regress-421325.js | 31 + js/src/tests/non262/Array/regress-422286.js | 31 + js/src/tests/non262/Array/regress-424954.js | 27 + js/src/tests/non262/Array/regress-430717.js | 29 + js/src/tests/non262/Array/regress-451483.js | 28 + js/src/tests/non262/Array/regress-451906.js | 27 + js/src/tests/non262/Array/regress-456845.js | 48 ++ js/src/tests/non262/Array/regress-465980-01.js | 32 + js/src/tests/non262/Array/regress-465980-02.js | 107 ++++ js/src/tests/non262/Array/regress-474529.js | 52 ++ js/src/tests/non262/Array/regress-488989.js | 32 + js/src/tests/non262/Array/regress-566651.js | 20 + js/src/tests/non262/Array/regress-599159.js | 10 + js/src/tests/non262/Array/regress-619970.js | 8 + js/src/tests/non262/Array/regress-94257.js | 83 +++ .../Array/reverse-order-of-low-high-accesses.js | 88 +++ ...set-with-indexed-property-on-prototype-chain.js | 60 ++ js/src/tests/non262/Array/shell.js | 0 js/src/tests/non262/Array/shift-no-has-trap.js | 64 ++ js/src/tests/non262/Array/shift_for_in.js | 13 + .../non262/Array/slice-sparse-with-large-index.js | 18 + js/src/tests/non262/Array/sort-01.js | 23 + .../Array/sort-array-with-holes-and-undefined.js | 32 + .../non262/Array/sort-delete-ascending-order.js | 37 ++ js/src/tests/non262/Array/sort-non-function.js | 22 + .../Array/sort-typedarray-with-own-length.js | 33 + js/src/tests/non262/Array/sort_basics.js | 46 ++ js/src/tests/non262/Array/sort_holes.js | 66 ++ .../tests/non262/Array/sort_native_string_nan.js | 16 + js/src/tests/non262/Array/sort_proxy.js | 38 ++ js/src/tests/non262/Array/sort_small.js | 33 + js/src/tests/non262/Array/species.js | 182 ++++++ ...splice-return-array-elements-defined-not-set.js | 46 ++ .../non262/Array/splice-species-changes-length.js | 48 ++ .../Array/splice-suppresses-unvisited-indexes.js | 61 ++ js/src/tests/non262/Array/to-length.js | 40 ++ js/src/tests/non262/Array/toLocaleString-01.js | 36 ++ js/src/tests/non262/Array/toLocaleString-nointl.js | 26 + js/src/tests/non262/Array/toLocaleString.js | 14 + js/src/tests/non262/Array/toSpliced-dense.js | 127 ++++ js/src/tests/non262/Array/toSpliced.js | 27 + js/src/tests/non262/Array/toString-01.js | 52 ++ js/src/tests/non262/Array/unscopables.js | 61 ++ js/src/tests/non262/Array/unshift-01.js | 44 ++ .../tests/non262/Array/unshift-with-enumeration.js | 18 + js/src/tests/non262/Array/values.js | 20 + js/src/tests/non262/Array/with-dense.js | 103 ++++ js/src/tests/non262/Array/with.js | 17 + 148 files changed, 9243 insertions(+) create mode 100644 js/src/tests/non262/Array/11.1.4.js create mode 100644 js/src/tests/non262/Array/15.4.4.5-1.js create mode 100644 js/src/tests/non262/Array/15.4.4.5-2.js create mode 100644 js/src/tests/non262/Array/15.4.4.5-3.js create mode 100644 js/src/tests/non262/Array/array-001.js create mode 100644 js/src/tests/non262/Array/array-length-set-during-for-in.js create mode 100644 js/src/tests/non262/Array/array-length-set-on-nonarray.js create mode 100644 js/src/tests/non262/Array/at.js create mode 100644 js/src/tests/non262/Array/browser.js create mode 100644 js/src/tests/non262/Array/change-array-by-copy-cross-compartment-create.js create mode 100644 js/src/tests/non262/Array/change-array-by-copy-errors-from-correct-realm.js create mode 100644 js/src/tests/non262/Array/concat-proxy.js create mode 100644 js/src/tests/non262/Array/concat-spreadable-basic.js create mode 100644 js/src/tests/non262/Array/concat-spreadable-primitive.js create mode 100644 js/src/tests/non262/Array/fill.js create mode 100644 js/src/tests/non262/Array/filter.js create mode 100644 js/src/tests/non262/Array/findLast_findLastIndex.js create mode 100644 js/src/tests/non262/Array/find_findindex.js create mode 100644 js/src/tests/non262/Array/for_of_1.js create mode 100644 js/src/tests/non262/Array/for_of_2.js create mode 100644 js/src/tests/non262/Array/for_of_3.js create mode 100644 js/src/tests/non262/Array/for_of_4.js create mode 100644 js/src/tests/non262/Array/from-iterator-close.js create mode 100644 js/src/tests/non262/Array/from_async.js create mode 100644 js/src/tests/non262/Array/from_basics.js create mode 100644 js/src/tests/non262/Array/from_constructor.js create mode 100644 js/src/tests/non262/Array/from_errors.js create mode 100644 js/src/tests/non262/Array/from_iterable.js create mode 100644 js/src/tests/non262/Array/from_length_setter.js create mode 100644 js/src/tests/non262/Array/from_mapping.js create mode 100644 js/src/tests/non262/Array/from_primitive.js create mode 100644 js/src/tests/non262/Array/from_proxy.js create mode 100644 js/src/tests/non262/Array/from_realms.js create mode 100644 js/src/tests/non262/Array/from_string.js create mode 100644 js/src/tests/non262/Array/from_surfaces.js create mode 100644 js/src/tests/non262/Array/from_this.js create mode 100644 js/src/tests/non262/Array/frozen-dense-array.js create mode 100644 js/src/tests/non262/Array/frozen-dict-mode-length.js create mode 100644 js/src/tests/non262/Array/getter-name.js create mode 100644 js/src/tests/non262/Array/group-callback-evaluation.js create mode 100644 js/src/tests/non262/Array/group-propertkey-is-length.js create mode 100644 js/src/tests/non262/Array/group.js create mode 100644 js/src/tests/non262/Array/includes-trailing-holes.js create mode 100644 js/src/tests/non262/Array/includes.js create mode 100644 js/src/tests/non262/Array/index-with-null-character.js create mode 100644 js/src/tests/non262/Array/indexOf-never-returns-negative-zero.js create mode 100644 js/src/tests/non262/Array/indexOf-packed-array.js create mode 100644 js/src/tests/non262/Array/isArray.js create mode 100644 js/src/tests/non262/Array/iterator_edge_cases.js create mode 100644 js/src/tests/non262/Array/join-01.js create mode 100644 js/src/tests/non262/Array/join-no-has-trap.js create mode 100644 js/src/tests/non262/Array/lastIndexOf-never-returns-negative-zero.js create mode 100644 js/src/tests/non262/Array/length-01.js create mode 100644 js/src/tests/non262/Array/length-nonwritable-redefine-nop.js create mode 100644 js/src/tests/non262/Array/length-set-object.js create mode 100644 js/src/tests/non262/Array/length-truncate-nonconfigurable-sparse.js create mode 100644 js/src/tests/non262/Array/length-truncate-nonconfigurable.js create mode 100644 js/src/tests/non262/Array/length-truncate-with-indexed.js create mode 100644 js/src/tests/non262/Array/pop-empty-nonwritable.js create mode 100644 js/src/tests/non262/Array/pop-no-has-trap.js create mode 100644 js/src/tests/non262/Array/pop-nonarray-higher-elements.js create mode 100644 js/src/tests/non262/Array/redefine-length-accessor.js create mode 100644 js/src/tests/non262/Array/redefine-length-frozen-array.js create mode 100644 js/src/tests/non262/Array/redefine-length-frozen-dictionarymode-array.js create mode 100644 js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-call-counts.js create mode 100644 js/src/tests/non262/Array/redefine-nonwritable-length-custom-conversion-throw.js create mode 100644 js/src/tests/non262/Array/redefine-nonwritable-length-nonnumeric.js create mode 100644 js/src/tests/non262/Array/regress-101488.js create mode 100644 js/src/tests/non262/Array/regress-107138.js create mode 100644 js/src/tests/non262/Array/regress-108440.js create mode 100644 js/src/tests/non262/Array/regress-130451.js create mode 100644 js/src/tests/non262/Array/regress-154338.js create mode 100644 js/src/tests/non262/Array/regress-157652.js create mode 100644 js/src/tests/non262/Array/regress-178722.js create mode 100644 js/src/tests/non262/Array/regress-255555.js create mode 100644 js/src/tests/non262/Array/regress-290592.js create mode 100644 js/src/tests/non262/Array/regress-299644.js create mode 100644 js/src/tests/non262/Array/regress-300858.js create mode 100644 js/src/tests/non262/Array/regress-304828.js create mode 100644 js/src/tests/non262/Array/regress-305002.js create mode 100644 js/src/tests/non262/Array/regress-310351.js create mode 100644 js/src/tests/non262/Array/regress-310425-01.js create mode 100644 js/src/tests/non262/Array/regress-310425-02.js create mode 100644 js/src/tests/non262/Array/regress-311515.js create mode 100644 js/src/tests/non262/Array/regress-315509-01.js create mode 100644 js/src/tests/non262/Array/regress-322135-01.js create mode 100644 js/src/tests/non262/Array/regress-330812.js create mode 100644 js/src/tests/non262/Array/regress-345961.js create mode 100644 js/src/tests/non262/Array/regress-348810.js create mode 100644 js/src/tests/non262/Array/regress-350256-01.js create mode 100644 js/src/tests/non262/Array/regress-350256-02.js create mode 100644 js/src/tests/non262/Array/regress-352742-01.js create mode 100644 js/src/tests/non262/Array/regress-352742-02.js create mode 100644 js/src/tests/non262/Array/regress-360681-01.js create mode 100644 js/src/tests/non262/Array/regress-360681-02.js create mode 100644 js/src/tests/non262/Array/regress-364104.js create mode 100644 js/src/tests/non262/Array/regress-386030.js create mode 100644 js/src/tests/non262/Array/regress-387501.js create mode 100644 js/src/tests/non262/Array/regress-390598.js create mode 100644 js/src/tests/non262/Array/regress-415451.js create mode 100644 js/src/tests/non262/Array/regress-421325.js create mode 100644 js/src/tests/non262/Array/regress-422286.js create mode 100644 js/src/tests/non262/Array/regress-424954.js create mode 100644 js/src/tests/non262/Array/regress-430717.js create mode 100644 js/src/tests/non262/Array/regress-451483.js create mode 100644 js/src/tests/non262/Array/regress-451906.js create mode 100644 js/src/tests/non262/Array/regress-456845.js create mode 100644 js/src/tests/non262/Array/regress-465980-01.js create mode 100644 js/src/tests/non262/Array/regress-465980-02.js create mode 100644 js/src/tests/non262/Array/regress-474529.js create mode 100644 js/src/tests/non262/Array/regress-488989.js create mode 100644 js/src/tests/non262/Array/regress-566651.js create mode 100644 js/src/tests/non262/Array/regress-599159.js create mode 100644 js/src/tests/non262/Array/regress-619970.js create mode 100644 js/src/tests/non262/Array/regress-94257.js create mode 100644 js/src/tests/non262/Array/reverse-order-of-low-high-accesses.js create mode 100644 js/src/tests/non262/Array/set-with-indexed-property-on-prototype-chain.js create mode 100644 js/src/tests/non262/Array/shell.js create mode 100644 js/src/tests/non262/Array/shift-no-has-trap.js create mode 100644 js/src/tests/non262/Array/shift_for_in.js create mode 100644 js/src/tests/non262/Array/slice-sparse-with-large-index.js create mode 100644 js/src/tests/non262/Array/sort-01.js create mode 100644 js/src/tests/non262/Array/sort-array-with-holes-and-undefined.js create mode 100644 js/src/tests/non262/Array/sort-delete-ascending-order.js create mode 100644 js/src/tests/non262/Array/sort-non-function.js create mode 100644 js/src/tests/non262/Array/sort-typedarray-with-own-length.js create mode 100644 js/src/tests/non262/Array/sort_basics.js create mode 100644 js/src/tests/non262/Array/sort_holes.js create mode 100644 js/src/tests/non262/Array/sort_native_string_nan.js create mode 100644 js/src/tests/non262/Array/sort_proxy.js create mode 100644 js/src/tests/non262/Array/sort_small.js create mode 100644 js/src/tests/non262/Array/species.js create mode 100644 js/src/tests/non262/Array/splice-return-array-elements-defined-not-set.js create mode 100644 js/src/tests/non262/Array/splice-species-changes-length.js create mode 100644 js/src/tests/non262/Array/splice-suppresses-unvisited-indexes.js create mode 100644 js/src/tests/non262/Array/to-length.js create mode 100644 js/src/tests/non262/Array/toLocaleString-01.js create mode 100644 js/src/tests/non262/Array/toLocaleString-nointl.js create mode 100644 js/src/tests/non262/Array/toLocaleString.js create mode 100644 js/src/tests/non262/Array/toSpliced-dense.js create mode 100644 js/src/tests/non262/Array/toSpliced.js create mode 100644 js/src/tests/non262/Array/toString-01.js create mode 100644 js/src/tests/non262/Array/unscopables.js create mode 100644 js/src/tests/non262/Array/unshift-01.js create mode 100644 js/src/tests/non262/Array/unshift-with-enumeration.js create mode 100644 js/src/tests/non262/Array/values.js create mode 100644 js/src/tests/non262/Array/with-dense.js create mode 100644 js/src/tests/non262/Array/with.js (limited to 'js/src/tests/non262/Array') 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 : + */ +//----------------------------------------------------------------------------- +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 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..6aab79b27e --- /dev/null +++ b/js/src/tests/non262/Array/change-array-by-copy-errors-from-correct-realm.js @@ -0,0 +1,77 @@ +// |reftest| + +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..d39f191f7b --- /dev/null +++ b/js/src/tests/non262/Array/from_async.js @@ -0,0 +1,302 @@ +// |reftest| skip-if(!xulRuntime.shell) -- 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 + */ +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 + */ +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..4fbb6467e7 --- /dev/null +++ b/js/src/tests/non262/Array/group-callback-evaluation.js @@ -0,0 +1,18 @@ +var array = [1, 2, 3]; + +var calls = 0; + +var grouped = Object.groupBy(array, () => { + 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..49eb512ec6 --- /dev/null +++ b/js/src/tests/non262/Array/group-propertkey-is-length.js @@ -0,0 +1,15 @@ +var array = [0]; + +var grouped = Object.groupBy(array, () => "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..9e89d1889d --- /dev/null +++ b/js/src/tests/non262/Array/group.js @@ -0,0 +1,88 @@ +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 = Object.groupBy(a1, x => isNeg(x) ? 'neg' : 'pos'); + const mappedArray = Map.groupBy(a1, 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(Object.groupBy([1,2,3], () => {}), expectedObj2); + assertDeepEq(Object.groupBy([], () => {}), Object.create(null)); + assertDeepEq((Map.groupBy([1,2,3], () => {})).get(undefined), [1,2,3]); + assertEq((Map.groupBy([1,2,3], () => {})).size, 1); + + const negMappedArray = Map.groupBy(a1, x => isNeg(x) ? -0 : 0); + assertDeepEq(negMappedArray.get(0), a1); + assertDeepEq(negMappedArray.size, 1); + + assertThrowsInstanceOf(() => Object.groupBy([], undefined), TypeError); + assertThrowsInstanceOf(() => Object.groupBy([], null), TypeError); + assertThrowsInstanceOf(() => Object.groupBy([], 0), TypeError); + assertThrowsInstanceOf(() => Object.groupBy([], ""), TypeError); + assertThrowsInstanceOf(() => Map.groupBy([], undefined), TypeError); + assertThrowsInstanceOf(() => Map.groupBy([], null), TypeError); + assertThrowsInstanceOf(() => Map.groupBy([], 0), TypeError); + assertThrowsInstanceOf(() => Map.groupBy([], ""), 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 = Map.groupBy(array, 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 = Map.groupBy(array, key => key.length); +const arr = Object.groupBy(array, 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"); } +}); +Object.groupBy([1, 2, 3], () => 'foo'); + +// Ensure property key is correctly accessed +count = 0; +p = Object.groupBy([1], () => ({ toString() { count++; return 10 } })); +assertEq(count, 1); +Map.groupBy([1], () => ({ 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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + */ + +//----------------------------------------------------------------------------- +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 + * 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 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 { + 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 + */ + +//----------------------------------------------------------------------------- +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..9671f55bfc --- /dev/null +++ b/js/src/tests/non262/Array/toSpliced-dense.js @@ -0,0 +1,127 @@ +// |reftest| + +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..926b032f38 --- /dev/null +++ b/js/src/tests/non262/Array/toSpliced.js @@ -0,0 +1,27 @@ +// |reftest| + +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 + */ + +//----------------------------------------------------------------------------- +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..de970ca1bd --- /dev/null +++ b/js/src/tests/non262/Array/unscopables.js @@ -0,0 +1,61 @@ +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", + "toReversed", + "toSorted", + "toSpliced", + "values"]; + +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..4f457fbedd --- /dev/null +++ b/js/src/tests/non262/Array/with-dense.js @@ -0,0 +1,103 @@ +// |reftest| + +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..54585ebc79 --- /dev/null +++ b/js/src/tests/non262/Array/with.js @@ -0,0 +1,17 @@ +// |reftest| + +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); -- cgit v1.2.3