From ea058db5ed227ac7f08294ccc4ed29b138c684e2 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Thu, 18 Sep 2014 17:22:05 +0800 Subject: [PATCH] changeset: 229386:2c92e43e29d8 user: ziyunfei <446240525@qq.com> files: js/src/builtin/Array.js js/src/builtin/Utilities.js js/src/jsarray.cpp js/src/tests/ecma_7/Array/browser.js js/src/tests/ecma_7/Array/contains.js js/src/tests/ecma_7/Array/shell.js js/src/tests/ecma_7/browser.js js/src/tests/ecma_7/shell.js description: Bug 1069063 - Implement Array.prototype.contains. r=till,securityAudit=bholley --HG-- rename : js/src/tests/ecma_6/Array/browser.js => js/src/tests/ecma_7/Array/browser.js rename : js/src/tests/ecma_6/Array/browser.js => js/src/tests/ecma_7/Array/shell.js rename : js/src/tests/ecma_6/browser.js => js/src/tests/ecma_7/browser.js rename : js/src/tests/ecma_6/shell.js => js/src/tests/ecma_7/shell.js --- js/src/builtin/Array.js | 50 +++++ js/src/builtin/Utilities.js | 5 + js/src/jsarray.cpp | 6 + js/src/tests/Makefile.in | 1 + js/src/tests/ecma_7/Array/browser.js | 0 js/src/tests/ecma_7/Array/contains.js | 61 ++++++ js/src/tests/ecma_7/Array/shell.js | 0 js/src/tests/ecma_7/browser.js | 0 js/src/tests/ecma_7/shell.js | 197 ++++++++++++++++++++ js/xpconnect/tests/chrome/test_xrayToJS.xul | 1 + 10 files changed, 321 insertions(+) create mode 100644 js/src/tests/ecma_7/Array/browser.js create mode 100644 js/src/tests/ecma_7/Array/contains.js create mode 100644 js/src/tests/ecma_7/Array/shell.js create mode 100644 js/src/tests/ecma_7/browser.js create mode 100644 js/src/tests/ecma_7/shell.js diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index eaaf2c3b0eb2..fbfec82aa5d5 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -581,6 +581,56 @@ function ArrayFill(value, start = 0, end = undefined) { return O; } +// Proposed for ES7: +// https://github.com/domenic/Array.prototype.contains/blob/master/spec.md +function ArrayContains(searchElement, fromIndex = 0) { + // Steps 1-2. + var O = ToObject(this); + + // Steps 3-4. + var len = ToLength(O.length); + + // Step 5. + if (len === 0) + return false; + + // Steps 6-7. + var n = ToInteger(fromIndex); + + // Step 8. + if (n >= len) + return false; + + // Step 9. + var k; + if (n >= 0) + k = n; + // Step 10. + else { + // Step a. + k = len + n; + // Step b. + if (k < 0) + k = 0; + } + + // Step 11. + while (k < len) { + // Steps a-b. + var elementK = O[k]; + + // Step c. + if (SameValueZero(searchElement, elementK)) + return true; + + // Step d. + k++; + } + + // Step 12. + return false; +} + #define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0 #define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1 #define ARRAY_ITERATOR_SLOT_ITEM_KIND 2 diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index fae2a5b68650..237378f4617c 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -156,6 +156,11 @@ function ToLength(v) { return v < 0x1fffffffffffff ? v : 0x1fffffffffffff; } +/* Spec: ECMAScript Draft, 6 edition Aug 24, 2014, 7.2.4 */ +function SameValueZero(x, y) { + return x !== x && y !== y || x === y +} + /********** Testing code **********/ #ifdef ENABLE_PARALLEL_JS diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index a191279f2b77..8a3e729ffb5c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3023,6 +3023,12 @@ static const JSFunctionSpec array_methods[] = { JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0,0), JS_SELF_HOSTED_FN("entries", "ArrayEntries", 0,0), JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0), + + /* ES7 additions */ +#ifdef NIGHTLY_BUILD + JS_SELF_HOSTED_FN("contains", "ArrayContains", 2,0), +#endif + JS_FS_END }; diff --git a/js/src/tests/Makefile.in b/js/src/tests/Makefile.in index 1d67364c947a..817d545787a1 100644 --- a/js/src/tests/Makefile.in +++ b/js/src/tests/Makefile.in @@ -18,6 +18,7 @@ TEST_FILES = \ ecma_3_1/ \ ecma_5/ \ ecma_6/ \ + ecma_7/ \ Intl/ \ js1_1/ \ js1_2/ \ diff --git a/js/src/tests/ecma_7/Array/browser.js b/js/src/tests/ecma_7/Array/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_7/Array/contains.js b/js/src/tests/ecma_7/Array/contains.js new file mode 100644 index 000000000000..90746a7fe090 --- /dev/null +++ b/js/src/tests/ecma_7/Array/contains.js @@ -0,0 +1,61 @@ +/* + * Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ + */ + +var BUGNUMBER = 1069063; +var summary = "Implement Array.prototype.contains"; + +print(BUGNUMBER + ": " + summary); + +if ('contains' in Array.prototype) { + assertEq(typeof [].contains, "function"); + assertEq([].contains.length, 1); + + assertTrue([1, 2, 3].contains(2)); + assertTrue([1,,2].contains(2)); + assertTrue([1, 2, 3].contains(2, 1)); + assertTrue([1, 2, 3].contains(2, -2)); + assertTrue([1, 2, 3].contains(2, -100)); + assertTrue([Object, Function, Array].contains(Function)); + assertTrue([-0].contains(0)); + assertTrue([NaN].contains(NaN)); + assertTrue([,].contains()); + assertTrue(staticContains("123", "2")); + assertTrue(staticContains({length: 3, 1: 2}, 2)); + assertTrue(staticContains({length: 3, 1: 2, get 3(){throw ""}}, 2)); + assertTrue(staticContains({length: 3, get 1() {return 2}}, 2)); + assertTrue(staticContains({__proto__: {1: 2}, length: 3}, 2)); + assertTrue(staticContains(new Proxy([1], {get(){return 2}}), 2)); + + assertFalse([1, 2, 3].contains("2")); + assertFalse([1, 2, 3].contains(2, 2)); + assertFalse([1, 2, 3].contains(2, -1)); + assertFalse([undefined].contains(NaN)); + assertFalse([{}].contains({})); + assertFalse(staticContains({length: 3, 1: 2}, 2, 2)); + assertFalse(staticContains({length: 3, get 0(){delete this[1]}, 1: 2}, 2)); + assertFalse(staticContains({length: -100, 0: 1}, 1)); + + assertThrowsInstanceOf(() => staticContains(), TypeError); + assertThrowsInstanceOf(() => staticContains(null), TypeError); + assertThrowsInstanceOf(() => staticContains({get length(){throw TypeError()}}), TypeError); + assertThrowsInstanceOf(() => staticContains({length: 3, get 1() {throw TypeError()}}, 2), TypeError); + assertThrowsInstanceOf(() => staticContains({__proto__: {get 1() {throw TypeError()}}, length: 3}, 2), TypeError); + assertThrowsInstanceOf(() => staticContains(new Proxy([1], {get(){throw TypeError()}})), TypeError); +} + +function assertTrue(v){ + assertEq(v, true) +} + +function assertFalse(v){ + assertEq(v, false) +} + +function staticContains(o, v, f){ + return [].contains.call(o, v, f) +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_7/Array/shell.js b/js/src/tests/ecma_7/Array/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_7/browser.js b/js/src/tests/ecma_7/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_7/shell.js b/js/src/tests/ecma_7/shell.js new file mode 100644 index 000000000000..8334db7443a7 --- /dev/null +++ b/js/src/tests/ecma_7/shell.js @@ -0,0 +1,197 @@ +/* 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/. */ + + +if (typeof assertThrowsInstanceOf === 'undefined') { + var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) { + var fullmsg; + try { + f(); + } catch (exc) { + if (exc instanceof ctor) + return; + fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); + }; +} + +if (typeof assertThrowsValue === 'undefined') { + var assertThrowsValue = function assertThrowsValue(f, val, msg) { + var fullmsg; + try { + f(); + } catch (exc) { + if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val)) + return; + fullmsg = "Assertion failed: expected exception " + val + ", got " + exc; + } + if (fullmsg === undefined) + fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown"; + if (msg !== undefined) + fullmsg += " - " + msg; + throw new Error(fullmsg); + }; +} + +if (typeof assertDeepEq === 'undefined') { + var assertDeepEq = (function(){ + var call = Function.prototype.call, + Map_ = Map, + Error_ = Error, + Map_has = call.bind(Map.prototype.has), + Map_get = call.bind(Map.prototype.get), + Map_set = call.bind(Map.prototype.set), + Object_toString = call.bind(Object.prototype.toString), + Function_toString = call.bind(Function.prototype.toString), + Object_getPrototypeOf = Object.getPrototypeOf, + Object_hasOwnProperty = call.bind(Object.prototype.hasOwnProperty), + Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, + Object_isExtensible = Object.isExtensible, + Object_getOwnPropertyNames = Object.getOwnPropertyNames, + uneval_ = uneval; + + // Return true iff ES6 Type(v) isn't Object. + // Note that `typeof document.all === "undefined"`. + function isPrimitive(v) { + return (v === null || + v === undefined || + typeof v === "boolean" || + typeof v === "number" || + typeof v === "string" || + typeof v === "symbol"); + } + + function assertSameValue(a, b, msg) { + try { + assertEq(a, b); + } catch (exc) { + throw Error_(exc.message + (msg ? " " + msg : "")); + } + } + + function assertSameClass(a, b, msg) { + var ac = Object_toString(a), bc = Object_toString(b); + assertSameValue(ac, bc, msg); + switch (ac) { + case "[object Function]": + assertSameValue(Function_toString(a), Function_toString(b), msg); + } + } + + function at(prevmsg, segment) { + return prevmsg ? prevmsg + segment : "at _" + segment; + } + + // Assert that the arguments a and b are thoroughly structurally equivalent. + // + // For the sake of speed, we cut a corner: + // var x = {}, y = {}, ax = [x]; + // assertDeepEq([ax, x], [ax, y]); // passes (?!) + // + // Technically this should fail, since the two object graphs are different. + // (The graph of [ax, y] contains one more object than the graph of [ax, x].) + // + // To get technically correct behavior, pass {strictEquivalence: true}. + // This is slower because we have to walk the entire graph, and Object.prototype + // is big. + // + return function assertDeepEq(a, b, options) { + var strictEquivalence = options ? options.strictEquivalence : false; + + function assertSameProto(a, b, msg) { + check(Object_getPrototypeOf(a), Object_getPrototypeOf(b), at(msg, ".__proto__")); + } + + function failPropList(na, nb, msg) { + throw Error_("got own properties " + uneval_(na) + ", expected " + uneval_(nb) + + (msg ? " " + msg : "")); + } + + function assertSameProps(a, b, msg) { + var na = Object_getOwnPropertyNames(a), + nb = Object_getOwnPropertyNames(b); + if (na.length !== nb.length) + failPropList(na, nb, msg); + for (var i = 0; i < na.length; i++) { + var name = na[i]; + if (name !== nb[i]) + failPropList(na, nb, msg); + var da = Object_getOwnPropertyDescriptor(a, name), + db = Object_getOwnPropertyDescriptor(b, name); + var pmsg = at(msg, /^[_$A-Za-z0-9]+$/.test(name) + ? /0|[1-9][0-9]*/.test(name) ? "[" + name + "]" : "." + name + : "[" + uneval_(name) + "]"); + assertSameValue(da.configurable, db.configurable, at(pmsg, ".[[Configurable]]")); + assertSameValue(da.enumerable, db.enumerable, at(pmsg, ".[[Enumerable]]")); + if (Object_hasOwnProperty(da, "value")) { + if (!Object_hasOwnProperty(db, "value")) + throw Error_("got data property, expected accessor property" + pmsg); + check(da.value, db.value, pmsg); + } else { + if (Object_hasOwnProperty(db, "value")) + throw Error_("got accessor property, expected data property" + pmsg); + check(da.get, db.get, at(pmsg, ".[[Get]]")); + check(da.set, db.set, at(pmsg, ".[[Set]]")); + } + } + }; + + var ab = Map_(); + var bpath = Map_(); + + function check(a, b, path) { + if (typeof a === "symbol") { + // Symbols are primitives, but they have identity. + // Symbol("x") !== Symbol("x") but + // assertDeepEq(Symbol("x"), Symbol("x")) should pass. + if (typeof b !== "symbol") { + throw Error_("got " + uneval_(a) + ", expected " + uneval_(b) + " " + path); + } else if (uneval_(a) !== uneval_(b)) { + // We lamely use uneval_ to distinguish well-known symbols + // from user-created symbols. The standard doesn't offer + // a convenient way to do it. + throw Error_("got " + uneval_(a) + ", expected " + uneval_(b) + " " + path); + } else if (Map_has(ab, a)) { + assertSameValue(Map_get(ab, a), b, path); + } else if (Map_has(bpath, b)) { + var bPrevPath = Map_get(bpath, b) || "_"; + throw Error_("got distinct symbols " + at(path, "") + " and " + + at(bPrevPath, "") + ", expected the same symbol both places"); + } else { + Map_set(ab, a, b); + Map_set(bpath, b, path); + } + } else if (isPrimitive(a)) { + assertSameValue(a, b, path); + } else if (isPrimitive(b)) { + throw Error_("got " + Object_toString(a) + ", expected " + uneval_(b) + " " + path); + } else if (Map_has(ab, a)) { + assertSameValue(Map_get(ab, a), b, path); + } else if (Map_has(bpath, b)) { + var bPrevPath = Map_get(bpath, b) || "_"; + throw Error_("got distinct objects " + at(path, "") + " and " + at(bPrevPath, "") + + ", expected the same object both places"); + } else { + Map_set(ab, a, b); + Map_set(bpath, b, path); + if (a !== b || strictEquivalence) { + assertSameClass(a, b, path); + assertSameProto(a, b, path); + assertSameProps(a, b, path); + assertSameValue(Object_isExtensible(a), + Object_isExtensible(b), + at(path, ".[[Extensible]]")); + } + } + } + + check(a, b, ""); + }; + })(); +} diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index d5fdff2aa673..03c169ea9c03 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -176,6 +176,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", "findIndex", "copyWithin", "fill", "@@iterator", "entries", "keys", "constructor"]; if (isNightlyBuild) { + gPrototypeProperties['Array'].push('contains'); let pjsMethods = ['mapPar', 'reducePar', 'scanPar', 'scatterPar', 'filterPar']; gPrototypeProperties['Array'] = gPrototypeProperties['Array'].concat(pjsMethods); }