Bug 1107601 - Implement %TypedArray%.prototype.{indexOf, lastIndexOf}. r=evilpie
authorziyunfei <446240525@qq.com>
Sat, 13 Dec 2014 07:15:00 -0500
changeset 219743 d70b753290ccc269ac937ab1d6cfb5e447dfbdb1
parent 219742 709aae3e233f6eaee97355fb09b22ab16ae1b4a2
child 219744 4413200ce54dd567ac2f4f581df5981ee76db571
push id27968
push usernigelbabu@gmail.com
push dateTue, 16 Dec 2014 06:59:11 +0000
treeherdermozilla-central@b836016d82b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1107601
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 1107601 - Implement %TypedArray%.prototype.{indexOf, lastIndexOf}. r=evilpie
js/src/builtin/TypedArray.js
js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.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,13 +1,13 @@
 /* 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 rev28 (2014/10/14) 22.2.3.10 %TypedArray%.prototype.find(predicate [,thisArg]).
+// ES6 draft rev28 (2014/10/14) 22.2.3.10 %TypedArray%.prototype.find(predicate[, thisArg]).
 function TypedArrayFind(predicate, thisArg = undefined) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
                             "TypedArrayFind");
     }
 
     // Steps 1-2.
@@ -20,31 +20,31 @@ function TypedArrayFind(predicate, thisA
     if (arguments.length === 0)
         ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.find");
     if (!IsCallable(predicate))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
 
     // Step 7.
     var T = thisArg;
 
-    // Steps 8-9. */
+    // Steps 8-9.
     // Steps a (implicit), and g.
     for (var k = 0; k < len; k++) {
         // Steps a-c.
         var kValue = O[k];
         // Steps d-f.
         if (callFunction(predicate, T, kValue, k, O))
             return kValue;
     }
 
     // Step 10.
     return undefined;
 }
 
-// ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate [,thisArg]).
+// ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate[, thisArg]).
 function TypedArrayFindIndex(predicate, thisArg = undefined) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
         return callFunction(CallTypedArrayMethodIfWrapped, this, predicate, thisArg,
                             "TypedArrayFindIndex");
     }
 
     // Steps 1-2.
@@ -68,8 +68,93 @@ function TypedArrayFindIndex(predicate, 
         // Steps a-f.
         if (callFunction(predicate, T, O[k], k, O))
             return k;
     }
 
     // Step 10.
     return -1;
 }
+
+// ES6 draft rev29 (2014/12/06) 22.2.3.13 %TypedArray%.prototype.indexOf(searchElement[, fromIndex]).
+function TypedArrayIndexOf(searchElement, fromIndex = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, searchElement, fromIndex,
+                            "TypedArrayIndexOf");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (len === 0)
+        return -1;
+
+    // Steps 7-8.
+    var n = ToInteger(fromIndex);
+
+    // Step 9.
+    if (n >= len)
+        return -1;
+
+    var k;
+    // Step 10.
+    if (n >= 0) {
+        k = n;
+    }
+    // Step 11.
+    else {
+        // Step a.
+        k = len + n;
+        // Step b.
+        if (k < 0)
+            k = 0;
+    }
+
+    // Step 12.
+    // Omit steps a-b, since there are no holes in typed arrays.
+    for (; k < len; k++) {
+        if (O[k] === searchElement)
+            return k;
+    }
+
+    // Step 13.
+    return -1;
+}
+
+// ES6 draft rev29 (2014/12/06) 22.2.3.16 %TypedArray%.prototype.lastIndexOf(searchElement [,fromIndex]).
+function TypedArrayLastIndexOf(searchElement, fromIndex = undefined) {
+    // This function is not generic.
+    if (!IsObject(this) || !IsTypedArray(this)) {
+        return callFunction(CallTypedArrayMethodIfWrapped, this, searchElement, fromIndex,
+                            "TypedArrayLastIndexOf");
+    }
+
+    // Steps 1-2.
+    var O = this;
+
+    // Steps 3-5.
+    var len = TypedArrayLength(O);
+
+    // Step 6.
+    if (len === 0)
+        return -1;
+
+    // Steps 7-8.
+    var n = fromIndex === undefined ? len - 1 : ToInteger(fromIndex);
+
+    // Steps 9-10.
+    var k = n >= 0 ? std_Math_min(n, len - 1) : len + n;
+
+    // Step 11.
+    // Omit steps a-b, since there are no holes in typed arrays.
+    for (; k >= 0; k--) {
+        if (O[k] === searchElement)
+            return k;
+    }
+
+    // Step 12.
+    return -1;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js
@@ -0,0 +1,120 @@
+const constructors = [
+    Int8Array,
+    Uint8Array,
+    Uint8ClampedArray,
+    Int16Array,
+    Uint16Array,
+    Int32Array,
+    Uint32Array,
+    Float32Array,
+    Float64Array
+];
+
+// Tests for TypedArray#indexOf
+for (var constructor of constructors) {
+
+    assertEq(constructor.prototype.indexOf.length, 1);
+
+    // 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);
+    assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(6), -1);
+    assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1), 0);
+
+    if (constructor === Float32Array || constructor === Float64Array) {
+        assertEq(new constructor([NaN, 0, -0]).indexOf(NaN), -1);
+        assertEq(new constructor([NaN, 0, -0]).indexOf(0), 1);
+        assertEq(new constructor([NaN, 0, -0]).indexOf(-0), 1);
+    } else {
+        // [NaN, 0, -0] will be coerced to [0, 0, 0]
+        assertEq(new constructor([NaN, 0, -0]).indexOf(NaN), -1);
+        assertEq(new constructor([NaN, 0, -0]).indexOf(0), 0);
+        assertEq(new constructor([NaN, 0, -0]).indexOf(-0), 0);
+    }
+
+    // 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);
+    assertEq(new constructor([1, 2, 3, 4, 5]).indexOf(5, -1), 4);
+    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);
+        }, TypeError, "Assert that indexOf fails if this value is not a TypedArray");
+    });
+
+    // test that this.length is never called
+    assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).indexOf(1), 1);
+}
+
+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
+for (var constructor of constructors) {
+
+    assertEq(constructor.prototype.lastIndexOf.length, 1);
+
+    // works with one argument
+    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);
+    assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(6), -1);
+    assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1), 4);
+
+    if (constructor === Float32Array || constructor === Float64Array) {
+        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);
+    } else {
+        // [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
+    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);
+    assertEq(new constructor([1, 2, 3, 4, 5]).lastIndexOf(5, -1), 4);
+    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);
+        }, TypeError, "Assert that lastIndexOf fails if this value is not a TypedArray");
+    });
+
+    // test that this.length is never called
+    assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", {
+        get() {
+            throw new Error("length accessor called");
+        }
+    }).lastIndexOf(1), 1);
+}
+
+assertEq(new Float32Array([.1, .2, .3]).lastIndexOf(.2), -1);
+assertEq(new Float32Array([.1, .2, .3]).lastIndexOf(Math.fround(.2)), 1);
+assertEq(new Float64Array([.1, .2, .3]).lastIndexOf(.2), 1);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -778,16 +778,18 @@ 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("find", "TypedArrayFind", 2, 0),
     JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 2, 0),
+    JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
+    JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0),
     JS_FS_END
 };
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::staticFunctions[] = {
     // Coming soon...
     JS_FS_END
 };
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -175,17 +175,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     let pjsMethods = ['mapPar', 'reducePar', 'scanPar', 'scatterPar', 'filterPar'];
     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"];
+     "set", "copyWithin", "find", "findIndex", "indexOf", "lastIndexOf"];
 
   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.
                                  "lineNumber", "columnNumber", "fileName", "message", "stack"];
   }