Bug 1116390 - Implement %TypedArray%.prototype.{every, some}. r=evilpie
authorziyunfei <446240525@qq.com>
Sat, 03 Jan 2015 08:26:00 -0500
changeset 247970 c963d611da18251d853d4befe8656ffa350a017a
parent 247969 9a84c3fd8d993e175539b485ec8639d23bfafe34
child 247971 7070c9621457ef4383b674c8c8569b3c8a9c2ed7
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1116390
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1116390 - Implement %TypedArray%.prototype.{every, some}. r=evilpie
js/src/builtin/TypedArray.js
js/src/tests/ecma_6/TypedArray/every-and-some.js
js/src/vm/TypedArrayObject.cpp
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1,12 +1,53 @@
 /* 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/. */
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.7 %TypedArray%.prototype.every(callbackfn[, thisArg]).
+function TypedArrayEvery(callbackfn, thisArg = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+                            "TypedArrayEvery");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (arguments.length === 0)
+        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    var T = thisArg;
+
+    // Steps 8-9.
+    // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
+    for (var k = 0; k < len; k++) {
+        // Steps 9.d.i-9.d.ii.
+        var kValue = O[k];
+
+        // Steps 9.d.iii-9.d.iv.
+        var testResult = callFunction(callbackfn, T, kValue, k, O);
+
+        // Step 9.d.v.
+        if (!testResult)
+            return false;
+    }
+
+    // Step 10.
+    return true;
+}
+
 // ES6 draft rev29 (2014/12/06) 22.2.3.8 %TypedArray%.prototype.fill(value [, start [, end ]])
 function TypedArrayFill(value, start = 0, end = undefined) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, value, start, end,
                             "TypedArrayFill");
     }
 
@@ -278,16 +319,57 @@ function TypedArrayReverse() {
         O[lower] = upperValue;
         O[upper] = lowerValue;
     }
 
     // Step 9.
     return O;
 }
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
+function TypedArraySome(callbackfn, thisArg = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+                            "TypedArraySome");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (arguments.length === 0)
+        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    var T = thisArg;
+
+    // Steps 8-9.
+    // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
+    for (var k = 0; k < len; k++) {
+        // Steps 9.d.i-9.d.ii.
+        var kValue = O[k];
+
+        // Steps 9.d.iii-9.d.iv.
+        var testResult = callFunction(callbackfn, T, kValue, k, O);
+
+        // Step 9.d.v.
+        if (testResult)
+            return true;
+    }
+
+    // Step 10.
+    return false;
+}
+
 // 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");
     }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/every-and-some.js
@@ -0,0 +1,261 @@
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array
+];
+
+// Tests for TypedArray#every.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.every.length, 1);
+
+    // Basic tests.
+    assertEq(new constructor([1, 3, 5]).every(v => v % 2), true);
+    assertEq(new constructor([1, 3, 5]).every(v => v > 2), false);
+    assertEq(new constructor(10).every(v => v === 0), true);
+    assertEq(new constructor().every(v => v > 1), true);
+
+    var arr = new constructor([1, 2, 3, 4, 5]);
+    var sum = 0;
+    var count = 0;
+    assertEq(arr.every((v, k, o) => {
+        count++;
+        sum += v;
+        assertEq(k, v - 1);
+        assertEq(o, arr);
+        return v < 3;
+    }), false);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // Tests for `thisArg` argument.
+    function assertThisArg(thisArg, thisValue) {
+        // In sloppy mode, `this` could be global object or a wrapper of `thisArg`.
+        assertEq(arr.every(function() {
+            assertDeepEq(this, thisValue);
+            return true;
+        }, thisArg), true);
+
+        // In strict mode, `this` strictly equals `thisArg`.
+        assertEq(arr.every(function() {
+            "use strict";
+            assertDeepEq(this, thisArg);
+            return true;
+        }, thisArg), true);
+
+        // Passing `thisArg` has no effect if callback is an arrow function.
+        var self = this;
+        assertEq(arr.every(() => {
+            assertEq(this, self);
+            return true;
+        }, thisArg), true);
+    }
+    assertThisArg([1, 2, 3], [1, 2, 3]);
+    assertThisArg(Object, Object);
+    assertThisArg(1, Object(1));
+    assertThisArg("1", Object("1"));
+    assertThisArg(false, Object(false));
+    assertThisArg(undefined, this);
+    assertThisArg(null, this);
+
+    // Throw an exception in the callback.
+    var sum = 0;
+    var count = 0;
+    var thrown = false;
+    try {
+        arr.every((v, k, o) => {
+            count++;
+            sum += v;
+            assertEq(k, v - 1);
+            assertEq(o, arr);
+            if (v === 3) {
+                throw "every";
+            }
+            return true
+        })
+    } catch(e) {
+        assertEq(e, "every");
+        thrown = true;
+    }
+    assertEq(thrown, true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.every();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.every(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.every(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var every = newGlobal()[constructor.name].prototype.every;
+        var sum = 0;
+        assertEq(every.call(new constructor([1, 2, 3]), v => sum += v), true);
+        assertEq(sum, 6);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.every.call(invalidReceiver, () => true);
+        }, TypeError, "Assert that every fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.every.call(new Proxy(new constructor(), {}), () => true);
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).every(() => true), true);
+}
+
+assertEq(new Float32Array([undefined, , NaN]).every(v => Object.is(v, NaN)), true);
+assertEq(new Float64Array([undefined, , NaN]).every(v => Object.is(v, NaN)), true);
+
+// Tests for TypedArray#some.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.some.length, 1);
+
+    // Basic tests.
+    assertEq(new constructor([1, 2, 3]).some(v => v % 2), true);
+    assertEq(new constructor([0, 2, 4]).some(v => v % 2), false);
+    assertEq(new constructor([1, 3, 5]).some(v => v > 2), true);
+    assertEq(new constructor([1, 3, 5]).some(v => v < 0), false);
+    assertEq(new constructor(10).some(v => v !== 0), false);
+    assertEq(new constructor().some(v => v > 1), false);
+
+    var arr = new constructor([1, 2, 3, 4, 5]);
+    var sum = 0;
+    var count = 0;
+    assertEq(arr.some((v, k, o) => {
+        count++;
+        sum += v;
+        assertEq(k, v - 1);
+        assertEq(o, arr);
+        return v > 2;
+    }), true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // Tests for `thisArg` argument.
+    function assertThisArg(thisArg, thisValue) {
+        // In sloppy mode, `this` could be global object or a wrapper of `thisArg`.
+        assertEq(arr.some(function() {
+            assertDeepEq(this, thisValue);
+            return false;
+        }, thisArg), false);
+
+        // In strict mode, `this` strictly equals `thisArg`.
+        assertEq(arr.some(function() {
+            "use strict";
+            assertDeepEq(this, thisArg);
+            return false;
+        }, thisArg), false);
+
+        // Passing `thisArg` has no effect if callback is an arrow function.
+        var self = this;
+        assertEq(arr.some(() => {
+            assertEq(this, self);
+            return false;
+        }, thisArg), false);
+    }
+    assertThisArg([1, 2, 3], [1, 2, 3]);
+    assertThisArg(Object, Object);
+    assertThisArg(1, Object(1));
+    assertThisArg("1", Object("1"));
+    assertThisArg(false, Object(false));
+    assertThisArg(undefined, this);
+    assertThisArg(null, this);
+
+    // Throw an exception in the callback.
+    var sum = 0;
+    var count = 0;
+    var thrown = false;
+    try {
+        arr.some((v, k, o) => {
+            count++;
+            sum += v;
+            assertEq(k, v - 1);
+            assertEq(o, arr);
+            if (v === 3) {
+                throw "some";
+            }
+            return false
+        })
+    } catch(e) {
+        assertEq(e, "some");
+        thrown = true;
+    }
+    assertEq(thrown, true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.some();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.some(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.some(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var some = newGlobal()[constructor.name].prototype.some;
+        var sum = 0;
+        assertEq(some.call(new constructor([1, 2, 3]), v => {
+            sum += v;
+            return false;
+        }), false);
+        assertEq(sum, 6);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.some.call(invalidReceiver, () => true);
+        }, TypeError, "Assert that some fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.some.call(new Proxy(new constructor(), {}), () => false);
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).some(() => false), false);
+}
+
+assertEq(new Float32Array([undefined, , NaN]).some(v => v === v), false);
+assertEq(new Float64Array([undefined, , NaN]).some(v => v === v), false);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
\ No newline at end of file
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -776,23 +776,25 @@ TypedArrayObject::subarray(JSContext *cx
 }
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::protoFunctions[] = {
     JS_SELF_HOSTED_SYM_FN(iterator, "ArrayValues", 0, 0),                          \
     JS_FN("subarray", TypedArrayObject::subarray, 2, 0),
     JS_FN("set", TypedArrayObject::set, 2, 0),
     JS_FN("copyWithin", TypedArrayObject::copyWithin, 2, 0),
+    JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 2, 0),
     JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
     JS_SELF_HOSTED_FN("find", "TypedArrayFind", 2, 0),
     JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 2, 0),
     JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
     JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
     JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0),
     JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
+    JS_SELF_HOSTED_FN("some", "TypedArraySome", 2, 0),
 #ifdef NIGHTLY_BUILD
     JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
 #endif
     JS_FS_END
 };
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::staticFunctions[] = {
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -176,17 +176,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     gPrototypeProperties['Array'] = gPrototypeProperties['Array'].concat(pjsMethods);
   }
   for (var c of typedArrayClasses) {
     gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
   }
   gPrototypeProperties['TypedArray'] =
     ["length", "buffer", "byteLength", "byteOffset", kIteratorSymbol, "subarray",
      "set", "copyWithin", "find", "findIndex", "indexOf", "lastIndexOf", "reverse",
-     "join"];
+     "join", "every", "some"];
   if (isNightlyBuild) {
     gPrototypeProperties['TypedArray'].push('includes');
   }
   for (var c of errorObjectClasses) {
       gPrototypeProperties[c] = ["constructor", "name",
                                  // We don't actually resolve these empty data properties
                                  // onto the Xray prototypes, but we list them here to make
                                  // the test happy.