Bug 1107645 - Implement TypedArray.forEach in accordance with the ecma 6 revision 31 spec. r=evilpie
authorEric Skoglund <eric@pagefault.se>
Mon, 26 Jan 2015 12:46:00 -0500
changeset 239480 3dbbf3baf9f0dc88f6c8468b7f766927edf94570
parent 239479 605f1d807083df8efdbe2966d356cb1eb6c82021
child 239481 42755ce4d13f3261e95595301327d78abd40b8c2
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewersevilpie
bugs1107645
milestone38.0a1
Bug 1107645 - Implement TypedArray.forEach in accordance with the ecma 6 revision 31 spec. r=evilpie Added reference to the forEach function in TypedArrayObject.cpp. Also added the correct specification reference to the function. Tests added for the TypedArray#forEach based on the tests for TypedArray#some Added reference to forEach in test_xrayToJs.xul Added test to assert that forEach returns undefined. Added new Proxy() to list of invalid receivers.
js/src/builtin/TypedArray.js
js/src/tests/ecma_6/TypedArray/forEach.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
@@ -165,16 +165,51 @@ function TypedArrayFindIndex(predicate, 
         if (callFunction(predicate, T, O[k], k, O))
             return k;
     }
 
     // Step 10.
     return -1;
 }
 
+// ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
+function TypedArrayForEach(callbackfn, thisArg = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+	return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+			    "TypedArrayForEach");
+    }
+
+    // Step 1-2.
+    var O = this;
+
+    // Step 3-4.
+    var len = TypedArrayLength(O);
+
+    // Step 5.
+    if (arguments.length === 0)
+	ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
+    if (!IsCallable(callbackfn))
+	ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 6.
+    var T = thisArg;
+
+    // Step 7-8.
+    // Step 7, 8a (implicit) and 8e.
+    for (var k = 0; k < len; k++) {
+	// Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
+	// Step 8d.
+	callFunction(callbackfn, T, O[k], k, O);
+    }
+
+    // Step 9.
+    return undefined;
+}
+
 // ES6 draft rev29 (2014/12/06) 22.2.3.13 %TypedArray%.prototype.indexOf(searchElement[, fromIndex]).
 function TypedArrayIndexOf(searchElement, fromIndex = 0) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, searchElement, fromIndex,
                             "TypedArrayIndexOf");
     }
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/forEach.js
@@ -0,0 +1,105 @@
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array
+];
+
+// Tests for TypedArray#forEach
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.forEach.length, 1);
+
+    var arr = new constructor([1, 2, 3, 4, 5]);
+    // Tests for `thisArg` argument.
+    function assertThisArg(thisArg, thisValue) {
+        // In sloppy mode, `this` could be global object or a wrapper of `thisArg`.
+        arr.forEach(function() {
+            assertDeepEq(this, thisValue);
+            return false;
+        }, thisArg);
+
+        // In strict mode, `this` strictly equals `thisArg`.
+        arr.forEach(function() {
+            "use strict";
+            assertDeepEq(this, thisArg);
+            return false;
+        }, thisArg);
+
+        // Passing `thisArg` has no effect if callback is an arrow function.
+        var self = this;
+        arr.forEach(() => {
+            assertEq(this, self);
+            return false;
+        }, thisArg);
+    }
+    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 {
+        assertEq(arr.forEach((v) => {
+            count++;
+            sum += v;
+            if (v === 3) {
+                throw "forEach";
+            }
+        }), undefined)
+    } catch(e) {
+        assertEq(e, "forEach");
+        thrown = true;
+    }
+    assertEq(thrown, true);
+    assertEq(sum, 6);
+    assertEq(count, 3);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.forEach();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.forEach(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.forEach(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var forEach = newGlobal()[constructor.name].prototype.forEach;
+        var sum = 0;
+        forEach.call(new constructor([1, 2, 3]), v => {
+            sum += v;
+        });
+        assertEq(sum, 6);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./,
+			    new Proxy(new constructor(), {})];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.forEach.call(invalidReceiver, () => true);
+        }, TypeError, "Assert that some fails if this value is not a TypedArray");
+    });
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -805,16 +805,17 @@ TypedArrayObject::subarray(JSContext *cx
 TypedArrayObject::protoFunctions[] = {
     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("forEach", "TypedArrayForEach", 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("reduce", "TypedArrayReduce", 1, 0),
     JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
     JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
     JS_SELF_HOSTED_FN("some", "TypedArraySome", 2, 0),
     JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -173,17 +173,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   if (isNightlyBuild) {
     gPrototypeProperties['Array'].push('includes');
   }
   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",
+     "set", "copyWithin", "find", "findIndex", "forEach","indexOf", "lastIndexOf", "reverse",
      "join", "every", "some", "reduce", "reduceRight", "entries", "keys", "values"];
   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