From 8dc8822499adc15a556365ef3c6dca0106f5d9e3 Mon Sep 17 00:00:00 2001 From: ziyunfei <446240525@qq.com> Date: Wed, 24 Dec 2014 07:34:00 -0500 Subject: [PATCH] Bug 1111869 - Implement %TypedArray%.prototype.includes. r=till --- js/src/builtin/Array.js | 2 +- js/src/builtin/TypedArray.js | 50 +++++++++++++++++ js/src/tests/ecma_6/TypedArray/fill.js | 12 ++-- js/src/tests/ecma_6/TypedArray/includes.js | 56 +++++++++++++++++++ .../TypedArray/indexOf-and-lastIndexOf.js | 45 ++++++++------- js/src/tests/ecma_6/TypedArray/reverse.js | 19 +++---- js/src/vm/TypedArrayObject.cpp | 3 + js/xpconnect/tests/chrome/test_xrayToJS.xul | 4 +- 8 files changed, 150 insertions(+), 41 deletions(-) create mode 100644 js/src/tests/ecma_6/TypedArray/includes.js diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 3f1b14dddab..c8fd1104969 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -582,7 +582,7 @@ function ArrayFill(value, start = 0, end = undefined) { } // Proposed for ES7: -// https://github.com/domenic/Array.prototype.includes/blob/master/spec.md +// https://github.com/tc39/Array.prototype.includes/blob/7c023c19a0/spec.md function ArrayIncludes(searchElement, fromIndex = 0) { // Steps 1-2. var O = ToObject(this); diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index 99c495d5dc0..4ba7baa474a 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -235,3 +235,53 @@ function TypedArrayReverse() { // Step 9. return O; } + +// Proposed for ES7: +// https://github.com/tc39/Array.prototype.includes/blob/7c023c19a0/spec.md +function TypedArrayIncludes(searchElement, fromIndex = 0) { + // This function is not generic. + if (!IsObject(this) || !IsTypedArray(this)) { + return callFunction(CallTypedArrayMethodIfWrapped, this, searchElement, + fromIndex, "TypedArrayIncludes"); + } + + // Steps 1-2. + var O = this; + + // Steps 3-4. + var len = TypedArrayLength(O); + + // Step 5. + if (len === 0) + return false; + + // Steps 6-7. + var n = ToInteger(fromIndex); + + var k; + // Step 8. + if (n >= 0) { + k = n; + } + // Step 9. + else { + // Step a. + k = len + n; + // Step b. + if (k < 0) + k = 0; + } + + // Step 10. + while (k < len) { + // Steps a-c. + if (SameValueZero(searchElement, O[k])) + return true; + + // Step d. + k++; + } + + // Step 11. + return false; +} diff --git a/js/src/tests/ecma_6/TypedArray/fill.js b/js/src/tests/ecma_6/TypedArray/fill.js index 9093239c89d..9195428a865 100644 --- a/js/src/tests/ecma_6/TypedArray/fill.js +++ b/js/src/tests/ecma_6/TypedArray/fill.js @@ -53,14 +53,14 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var nonTypedArrays = [undefined, null, 1, false, "", Symbol(), [], {}, /./, - /* new Proxy(new constructor(), {}) // This probably should throw */ - ]; - nonTypedArrays.forEach(nonTypedArray => { - assertThrowsInstanceOf(function() { - constructor.prototype.fill.call(nonTypedArray, 1); + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./] + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.fill.call(invalidReceiver, 1); }, TypeError); }); + // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. + constructor.prototype.fill.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/includes.js b/js/src/tests/ecma_6/TypedArray/includes.js new file mode 100644 index 00000000000..5560a97481f --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/includes.js @@ -0,0 +1,56 @@ +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +for (var constructor of constructors) { + if (!("includes" in constructor.prototype)) + break; + + assertEq(constructor.prototype.includes.length, 1); + + assertEq(new constructor([1, 2, 3]).includes(1), true); + assertEq(new constructor([1, 2, 3]).includes(2), true); + assertEq(new constructor([1, 2, 3]).includes(3), true); + assertEq(new constructor([1, 2, 3]).includes(2, 1), true); + assertEq(new constructor([1, 2, 3]).includes(2, -2), true); + assertEq(new constructor([1, 2, 3]).includes(2, -100), true); + + assertEq(new constructor([1, 2, 3]).includes("2"), false); + assertEq(new constructor([1, 2, 3]).includes(2, 2), false); + assertEq(new constructor([1, 2, 3]).includes(2, -1), false); + assertEq(new constructor([1, 2, 3]).includes(2, 100), false); + + // Called from other globals. + if (typeof newGlobal === "function") { + var includes = newGlobal()[constructor.name].prototype.includes; + assertEq(includes.call(new constructor([1, 2, 3]), 2), true); + } + + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.includes.call(invalidReceiver); + }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); + }); + // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. + constructor.prototype.includes.call(new Proxy(new constructor(), {})); + + // Test that the length getter is never called. + assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { + get() { + throw new Error("length accessor called"); + } + }).includes(2), true); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js b/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js index b285d978154..9dfdd8c9b63 100644 --- a/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js +++ b/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js @@ -10,12 +10,11 @@ const constructors = [ Float64Array ]; -// Tests for TypedArray#indexOf +// Tests for TypedArray#indexOf. for (var constructor of constructors) { - assertEq(constructor.prototype.indexOf.length, 1); - // works with one argument + // Works with one argument. assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(0), -1); assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1), 0); assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(5), 4); @@ -33,7 +32,7 @@ for (var constructor of constructors) { assertEq(new constructor([NaN, 0, -0]).indexOf(-0), 0); } - // works with two arguments + // Works with two arguments. assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1, 1), -1); assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(1, -100), 0); assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(3, 100), -1); @@ -41,15 +40,15 @@ for (var constructor of constructors) { assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, 2), 2); assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, -2), 4); - // throws if `this` isn't a TypedArray - var nonTypedArrays = [undefined, null, 1, false, "", Symbol(), [], {}, /./, - /* new Proxy(new constructor(), {}) // this probably should throw */ - ]; - nonTypedArrays.forEach(nonTypedArray => { - assertThrowsInstanceOf(function() { - constructor.prototype.indexOf.call(nonTypedArray); + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.indexOf.call(invalidReceiver); }, TypeError, "Assert that indexOf fails if this value is not a TypedArray"); }); + // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. + constructor.prototype.indexOf.call(new Proxy(new constructor(), {})); // test that this.length is never called assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { @@ -63,12 +62,12 @@ assertEq(new Float32Array([.1, .2, .3]).indexOf(.2), -1); assertEq(new Float32Array([.1, .2, .3]).indexOf(Math.fround(.2)), 1); assertEq(new Float64Array([.1, .2, .3]).indexOf(.2), 1); -// Tests for TypedArray#lastIndexOf +// Tests for TypedArray#lastIndexOf. for (var constructor of constructors) { assertEq(constructor.prototype.lastIndexOf.length, 1); - // works with one argument + // Works with one arguments. assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(0), -1); assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1), 0); assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(5), 4); @@ -80,13 +79,13 @@ for (var constructor of constructors) { assertEq(new constructor([NaN, 0, -0]).lastIndexOf(0), 2); assertEq(new constructor([NaN, 0, -0]).lastIndexOf(-0), 2); } else { - // [NaN, 0, -0] will be coerced to [0, 0, 0] + // [NaN, 0, -0] will be coerced to [0, 0, 0]. assertEq(new constructor([NaN, 0, -0]).lastIndexOf(NaN), -1); assertEq(new constructor([NaN, 0, -0]).lastIndexOf(0), 2); assertEq(new constructor([NaN, 0, -0]).lastIndexOf(-0), 2); } - // works with two arguments + // Works with two arguments. assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1, 1), 0); assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(1, -100), -1); assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(3, 100), 2); @@ -94,17 +93,17 @@ for (var constructor of constructors) { assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, 2), 2); assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, -2), 2); - // throws if `this` isn't a TypedArray - var nonTypedArrays = [undefined, null, 1, false, "", Symbol(), [], {}, /./, - /* new Proxy(new constructor(), {}) // this probably should throw */ - ]; - nonTypedArrays.forEach(nonTypedArray => { - assertThrowsInstanceOf(function() { - constructor.prototype.lastIndexOf.call(nonTypedArray); + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.lastIndexOf.call(invalidReceiver); }, TypeError, "Assert that lastIndexOf fails if this value is not a TypedArray"); }); + // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. + constructor.prototype.lastIndexOf.call(new Proxy(new constructor(), {})); - // test that this.length is never called + // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { get() { throw new Error("length accessor called"); diff --git a/js/src/tests/ecma_6/TypedArray/reverse.js b/js/src/tests/ecma_6/TypedArray/reverse.js index 48775f3032a..0404d46bc8e 100644 --- a/js/src/tests/ecma_6/TypedArray/reverse.js +++ b/js/src/tests/ecma_6/TypedArray/reverse.js @@ -11,7 +11,6 @@ const constructors = [ ]; for (var constructor of constructors) { - assertDeepEq(constructor.prototype.reverse.length, 0); assertDeepEq(new constructor().reverse(), new constructor()); @@ -24,23 +23,23 @@ for (var constructor of constructors) { assertDeepEq(new constructor([1, 2, 3, 4, 5]).reverse(), new constructor([5, 4, 3, 2, 1])); assertDeepEq(new constructor([.1, .2, .3]).reverse(), new constructor([.3, .2, .1])); - // called from other globals + // Called from other globals. if (typeof newGlobal === "function") { var reverse = newGlobal()[constructor.name].prototype.reverse; assertDeepEq(reverse.call(new constructor([3, 2, 1])), new constructor([1, 2, 3])); } - // throws if `this` isn't a TypedArray - var nonTypedArrays = [undefined, null, 1, false, "", Symbol(), [], {}, /./, - /* new Proxy(new constructor(), {}) // this probably should throw */ - ]; - nonTypedArrays.forEach(nonTypedArray => { - assertThrowsInstanceOf(function() { - constructor.prototype.reverse.call(nonTypedArray); + // Throws if `this` isn't a TypedArray. + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + invalidReceivers.forEach(invalidReceiver => { + assertThrowsInstanceOf(() => { + constructor.prototype.reverse.call(invalidReceiver); }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); }); + // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. + constructor.prototype.reverse.call(new Proxy(new constructor(), {})); - // test that this.length is never called + // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { get() { throw new Error("length accessor called"); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 9ef703c055c..010b3f80f22 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -787,6 +787,9 @@ TypedArrayObject::protoFunctions[] = { JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0), JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0), JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0), +#ifdef NIGHTLY_BUILD + JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0), +#endif JS_FS_END }; diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 4c674b70c25..ba79ca07b6b 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -181,7 +181,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 gPrototypeProperties['TypedArray'] = ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray", "set", "copyWithin", "find", "findIndex", "indexOf", "lastIndexOf", "reverse"]; - + if (isNightlyBuild) { + gPrototypeProperties['TypedArray'].push('includes'); + } for (var c of errorObjectClasses) { gPrototypeProperties[c] = ["constructor", "name", // We don't actually resolve these empty data properties