Bug 1117350 - Part 2: Implement %TypedArray%.prototype.{reduce, reduceRight}. r=evilpie
authorziyunfei <446240525@qq.com>
Wed, 07 Jan 2015 01:55:00 -0500
changeset 222647 3b29b9cdedfc
parent 222646 f09142ac24fc
child 222680 79d541cf6db2
push id53684
push userryanvm@gmail.com
push dateThu, 08 Jan 2015 14:15:18 +0000
treeherdermozilla-inbound@3b29b9cdedfc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1117350
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 1117350 - Part 2: Implement %TypedArray%.prototype.{reduce, reduceRight}. r=evilpie
js/src/builtin/TypedArray.js
js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.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
@@ -281,16 +281,94 @@ function TypedArrayLastIndexOf(searchEle
         if (O[k] === searchElement)
             return k;
     }
 
     // Step 12.
     return -1;
 }
 
+// ES6 draft rev30 (2014/12/24) 22.2.3.19 %TypedArray%.prototype.reduce(callbackfn[, initialValue]).
+function TypedArrayReduce(callbackfn/*, initialValue*/) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this))
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduce");
+
+    // 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.reduce");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    if (len === 0 && arguments.length === 1)
+        ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
+
+    // Step 8.
+    var k = 0;
+
+    // Steps 9-10.
+    // Omit some steps, since 'accumulator' should always be O[0] in step 10 for typed arrays.
+    var accumulator = arguments.length > 1 ? arguments[1] : O[k++];
+
+    // Step 11.
+    // Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
+    for (; k < len; k++) {
+        accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
+    }
+
+    // Step 12.
+    return accumulator;
+}
+
+// ES6 draft rev30 (2014/12/24) 22.2.3.20 %TypedArray%.prototype.reduceRight(callbackfn[, initialValue]).
+function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this))
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, "TypedArrayReduceRight");
+
+    // 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.reduceRight");
+    if (!IsCallable(callbackfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+
+    // Step 7.
+    if (len === 0 && arguments.length === 1)
+        ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
+
+    // Step 8.
+    var k = len - 1;
+
+    // Steps 9-10.
+    // Omit some steps, since 'accumulator' should always be O[len-1] in step 10 for typed arrays.
+    var accumulator = arguments.length > 1 ? arguments[1] : O[k--];
+
+    // Step 11.
+    // Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
+    for (; k >= 0; k--) {
+        accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
+    }
+
+    // Step 12.
+    return accumulator;
+}
+
 // ES6 draft rev29 (2014/12/06) 22.2.3.21 %TypedArray%.prototype.reverse().
 function TypedArrayReverse() {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, "TypedArrayReverse");
     }
 
     // Steps 1-2.
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js
@@ -0,0 +1,202 @@
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array
+];
+
+// Tests for TypedArray#reduce.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.reduce.length, 1);
+
+    // Basic tests.
+    var arr = new constructor([1, 2, 3, 4, 5]);
+
+    assertEq(arr.reduce((previous, current) => previous + current), 15);
+    assertEq(arr.reduce((previous, current) => current - previous), 3);
+
+    var count = 0;
+    var sum = 0;
+    assertEq(arr.reduce((previous, current, index, array) => {
+        count++;
+        sum += current;
+        assertEq(current - 1, index);
+        assertEq(current, arr[index]);
+        assertEq(array, arr);
+        return previous * current;
+    }), 120);
+    assertEq(count, 4);
+    assertEq(sum, 14);
+
+    // Tests for `initialValue` argument.
+    assertEq(arr.reduce((previous, current) => previous + current, -15), 0);
+    assertEq(arr.reduce((previous, current) => previous + current, ""), "12345");
+    assertDeepEq(arr.reduce((previous, current) => previous.concat(current), []), [1, 2, 3, 4, 5]);
+
+    // Tests for `this` value.
+    var global = this;
+    arr.reduce(function(){
+        assertEq(this, global);
+    });
+    arr.reduce(function(){
+        "use strict";
+        assertEq(this, undefined);
+    });
+    arr.reduce(() => assertEq(this, global));
+
+    // Throw an exception in the callback.
+    var count = 0;
+    var sum = 0;
+    assertThrowsInstanceOf(() => {
+        arr.reduce((previous, current, index, array) => {
+            count++;
+            sum += current;
+            if (index === 3) {
+                throw TypeError("reduce");
+            }
+        })
+    }, TypeError);
+    assertEq(count, 3);
+    assertEq(sum, 9);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.reduce();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.reduce(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.reduce(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var reduce = newGlobal()[constructor.name].prototype.reduce;
+        assertEq(reduce.call(arr, (previous, current) => Math.min(previous, current)), 1);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.reduce.call(invalidReceiver, () => {});
+        }, TypeError, "Assert that reduce fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.reduce.call(new Proxy(new constructor(3), {}), () => {});
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(arr, "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).reduce((previous, current) => Math.max(previous, current)), 5);
+}
+
+// Tests for TypedArray#reduceRight.
+for (var constructor of constructors) {
+    assertEq(constructor.prototype.reduceRight.length, 1);
+
+    // Basic tests.
+    var arr = new constructor([1, 2, 3, 4, 5]);
+
+    assertEq(arr.reduceRight((previous, current) => previous + current), 15);
+    assertEq(arr.reduceRight((previous, current) => current - previous), 3);
+
+    var count = 0;
+    var sum = 0;
+    assertEq(arr.reduceRight((previous, current, index, array) => {
+        count++;
+        sum += current;
+        assertEq(current - 1, index);
+        assertEq(current, arr[index]);
+        assertEq(array, arr);
+        return previous * current;
+    }), 120);
+    assertEq(count, 4);
+    assertEq(sum, 10);
+
+    // Tests for `initialValue` argument.
+    assertEq(arr.reduceRight((previous, current) => previous + current, -15), 0);
+    assertEq(arr.reduceRight((previous, current) => previous + current, ""), "54321");
+    assertDeepEq(arr.reduceRight((previous, current) => previous.concat(current), []), [5, 4, 3, 2, 1]);
+
+    // Tests for `this` value.
+    var global = this;
+    arr.reduceRight(function(){
+        assertEq(this, global);
+    });
+    arr.reduceRight(function(){
+        "use strict";
+        assertEq(this, undefined);
+    });
+    arr.reduceRight(() => assertEq(this, global));
+
+    // Throw an exception in the callback.
+    var count = 0;
+    var sum = 0;
+    assertThrowsInstanceOf(() => {
+        arr.reduceRight((previous, current, index, array) => {
+            count++;
+            sum += current;
+            if (index === 1) {
+                throw TypeError("reduceRight");
+            }
+        })
+    }, TypeError);
+    assertEq(count, 3);
+    assertEq(sum, 9);
+
+    // There is no callback or callback is not a function.
+    assertThrowsInstanceOf(() => {
+        arr.reduceRight();
+    }, TypeError);
+    var invalidCallbacks = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidCallbacks.forEach(callback => {
+        assertThrowsInstanceOf(() => {
+            arr.reduceRight(callback);
+        }, TypeError);
+    })
+
+    // Callback is a generator.
+    arr.reduceRight(function*(){
+        throw "This line will not be executed";
+    });
+
+    // Called from other globals.
+    if (typeof newGlobal === "function") {
+        var reduceRight = newGlobal()[constructor.name].prototype.reduceRight;
+        assertEq(reduceRight.call(arr, (previous, current) => Math.min(previous, current)), 1);
+    }
+
+    // Throws if `this` isn't a TypedArray.
+    var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./];
+    invalidReceivers.forEach(invalidReceiver => {
+        assertThrowsInstanceOf(() => {
+            constructor.prototype.reduceRight.call(invalidReceiver, () => {});
+        }, TypeError, "Assert that reduceRight fails if this value is not a TypedArray");
+    });
+    // FIXME: Should throw exception if `this` is a proxy, see bug 1115361.
+    constructor.prototype.reduceRight.call(new Proxy(new constructor(3), {}), () => {});
+
+    // Test that the length getter is never called.
+    assertEq(Object.defineProperty(arr, "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).reduceRight((previous, current) => Math.max(previous, current)), 5);
+}
+
+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
@@ -785,16 +785,18 @@ TypedArrayObject::protoFunctions[] = {
     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("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),
 #ifdef NIGHTLY_BUILD
     JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
 #endif
     JS_FS_END
 };
 
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -174,17 +174,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     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",
-     "join", "every", "some"];
+     "join", "every", "some", "reduce", "reduceRight"];
   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.