Implement %TypedArray%.{fill,find,findIndex}
authordehrenberg <dehrenberg@chromium.org>
Wed, 13 May 2015 04:32:12 +0000 (21:32 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 13 May 2015 04:32:08 +0000 (04:32 +0000)
This patch adds three methods to TypedArrays which are already
implemented for arrays. The implementations are made by calling
out to the underlying code used by Arrays.

R=adamk@chromium.org
BUG=v8:3578
LOG=Y

Review URL: https://codereview.chromium.org/1136663005

Cr-Commit-Position: refs/heads/master@{#28382}

src/harmony-array.js
src/harmony-typedarray.js
test/mjsunit/harmony/typedarray-fill.js [new file with mode: 0644]
test/mjsunit/harmony/typedarray-find.js [new file with mode: 0644]
test/mjsunit/harmony/typedarray-findindex.js [new file with mode: 0644]

index acf1b14..c0de2b9 100644 (file)
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 var $innerArrayCopyWithin;
+var $innerArrayFill;
+var $innerArrayFind;
+var $innerArrayFindIndex;
 
 (function(global, exports) {
 
@@ -73,22 +76,11 @@ function ArrayCopyWithin(target, start, end) {
   return InnerArrayCopyWithin(target, start, end, array, length);
 }
 
-// ES6 draft 07-15-13, section 15.4.3.23
-function ArrayFind(predicate /* thisArg */) {  // length == 1
-  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
-
-  var array = $toObject(this);
-  var length = $toInteger(array.length);
-
+function InnerArrayFind(predicate, thisArg, array, length) {
   if (!IS_SPEC_FUNCTION(predicate)) {
     throw MakeTypeError(kCalledNonCallable, predicate);
   }
 
-  var thisArg;
-  if (%_ArgumentsLength() > 1) {
-    thisArg = %_Arguments(1);
-  }
-
   var needs_wrapper = false;
   if (IS_NULL(thisArg)) {
     if (%IsSloppyModeFunction(predicate)) thisArg = UNDEFINED;
@@ -108,24 +100,23 @@ function ArrayFind(predicate /* thisArg */) {  // length == 1
 
   return;
 }
+$innerArrayFind = InnerArrayFind;
 
-
-// ES6 draft 07-15-13, section 15.4.3.24
-function ArrayFindIndex(predicate /* thisArg */) {  // length == 1
-  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
+// ES6 draft 07-15-13, section 15.4.3.23
+function ArrayFind(predicate, thisArg) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
 
   var array = $toObject(this);
   var length = $toInteger(array.length);
 
+  return InnerArrayFind(predicate, thisArg, array, length);
+}
+
+function InnerArrayFindIndex(predicate, thisArg, array, length) {
   if (!IS_SPEC_FUNCTION(predicate)) {
     throw MakeTypeError(kCalledNonCallable, predicate);
   }
 
-  var thisArg;
-  if (%_ArgumentsLength() > 1) {
-    thisArg = %_Arguments(1);
-  }
-
   var needs_wrapper = false;
   if (IS_NULL(thisArg)) {
     if (%IsSloppyModeFunction(predicate)) thisArg = UNDEFINED;
@@ -145,26 +136,22 @@ function ArrayFindIndex(predicate /* thisArg */) {  // length == 1
 
   return -1;
 }
+$innerArrayFindIndex = InnerArrayFindIndex;
 
-
-// ES6, draft 04-05-14, section 22.1.3.6
-function ArrayFill(value /* [, start [, end ] ] */) {  // length == 1
-  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
+// ES6 draft 07-15-13, section 15.4.3.24
+function ArrayFindIndex(predicate, thisArg) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
 
   var array = $toObject(this);
-  var length = TO_UINT32(array.length);
+  var length = $toInteger(array.length);
 
-  var i = 0;
-  var end = length;
+  return InnerArrayFindIndex(predicate, thisArg, array, length);
+}
 
-  if (%_ArgumentsLength() > 1) {
-    i = %_Arguments(1);
-    i = IS_UNDEFINED(i) ? 0 : TO_INTEGER(i);
-    if (%_ArgumentsLength() > 2) {
-      end = %_Arguments(2);
-      end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
-    }
-  }
+// ES6, draft 04-05-14, section 22.1.3.6
+function InnerArrayFill(value, start, end, array, length) {
+  var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
+  var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
 
   if (i < 0) {
     i += length;
@@ -188,6 +175,17 @@ function ArrayFill(value /* [, start [, end ] ] */) {  // length == 1
     array[i] = value;
   return array;
 }
+$innerArrayFill = InnerArrayFill;
+
+// ES6, draft 04-05-14, section 22.1.3.6
+function ArrayFill(value, start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
+
+  var array = $toObject(this);
+  var length = TO_UINT32(array.length);
+
+  return InnerArrayFill(value, start, end, array, length);
+}
 
 // ES6, draft 10-14-14, section 22.1.2.1
 function ArrayFrom(arrayLike, mapfn, receiver) {
@@ -279,6 +277,9 @@ $installConstants(GlobalSymbol, [
 
 %FunctionSetLength(ArrayCopyWithin, 2);
 %FunctionSetLength(ArrayFrom, 1);
+%FunctionSetLength(ArrayFill, 1);
+%FunctionSetLength(ArrayFind, 1);
+%FunctionSetLength(ArrayFindIndex, 1);
 
 // Set up non-enumerable functions on the Array object.
 $installFunctions(GlobalArray, DONT_ENUM, [
index c33e08b..3c4c4a4 100644 (file)
@@ -59,6 +59,37 @@ function TypedArrayForEach(f, receiver) {
 }
 %FunctionSetLength(TypedArrayForEach, 1);
 
+// ES6 draft 04-05-14 section 22.2.3.8
+function TypedArrayFill(value, start , end) {
+  if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return $innerArrayFill(value, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayFill, 1);
+
+// ES6 draft 07-15-13, section 22.2.3.10
+function TypedArrayFind(predicate, thisArg) {
+  if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return $innerArrayFind(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFind, 1);
+
+// ES6 draft 07-15-13, section 22.2.3.11
+function TypedArrayFindIndex(predicate, thisArg) {
+  if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return $innerArrayFindIndex(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFindIndex, 1);
+
+
 // ES6 draft 08-24-14, section 22.2.2.2
 function TypedArrayOf() {
   var length = %_ArgumentsLength();
@@ -79,7 +110,10 @@ macro EXTEND_TYPED_ARRAY(NAME)
   $installFunctions(GlobalNAME.prototype, DONT_ENUM, [
     "copyWithin", TypedArrayCopyWithin,
     "every", TypedArrayEvery,
-    "forEach", TypedArrayForEach
+    "forEach", TypedArrayForEach,
+    "find", TypedArrayFind,
+    "findIndex", TypedArrayFindIndex,
+    "fill", TypedArrayFill
   ]);
 endmacro
 
diff --git a/test/mjsunit/harmony/typedarray-fill.js b/test/mjsunit/harmony/typedarray-fill.js
new file mode 100644 (file)
index 0000000..40605bb
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrays
+
+var typedArrayConstructors = [
+  Uint8Array,
+  Int8Array,
+  Uint16Array,
+  Int16Array,
+  Uint32Array,
+  Int32Array,
+  Uint8ClampedArray,
+  Float32Array,
+  Float64Array];
+
+for (var constructor of typedArrayConstructors) {
+  assertEquals(1, constructor.prototype.fill.length);
+
+  assertArrayEquals(new constructor([]).fill(8), []);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8), [8, 8, 8, 8, 8]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 1), [0, 8, 8, 8, 8]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 10), [0, 0, 0, 0, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, -5), [8, 8, 8, 8, 8]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 1, 4), [0, 8, 8, 8, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 1, -1), [0, 8, 8, 8, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 1, 42), [0, 8, 8, 8, 8]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, -3, 42), [0, 0, 8, 8, 8]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, -3, 4), [0, 0, 8, 8, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, -2, -1), [0, 0, 0, 8, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, -1, -3), [0, 0, 0, 0, 0]);
+  assertArrayEquals(new constructor([0, 0, 0, 0, 0]).fill(8, 0, 4), [8, 8, 8, 8, 0]);
+
+  // Test exceptions
+  assertThrows('constructor.prototype.fill.call(null)', TypeError);
+  assertThrows('constructor.prototype.fill.call(undefined)', TypeError);
+  assertThrows('constructor.prototype.fill.call([])', TypeError);
+}
diff --git a/test/mjsunit/harmony/typedarray-find.js b/test/mjsunit/harmony/typedarray-find.js
new file mode 100644 (file)
index 0000000..5e35125
--- /dev/null
@@ -0,0 +1,179 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrays
+
+var typedArrayConstructors = [
+  Uint8Array,
+  Int8Array,
+  Uint16Array,
+  Int16Array,
+  Uint32Array,
+  Int32Array,
+  Uint8ClampedArray,
+  Float32Array,
+  Float64Array];
+
+for (var constructor of typedArrayConstructors) {
+
+assertEquals(1, constructor.prototype.find.length);
+
+var a = new constructor([21, 22, 23, 24]);
+assertEquals(undefined, a.find(function() { return false; }));
+assertEquals(21, a.find(function() { return true; }));
+assertEquals(undefined, a.find(function(val) { return 121 === val; }));
+assertEquals(24, a.find(function(val) { return 24 === val; }));
+assertEquals(23, a.find(function(val) { return 23 === val; }), null);
+assertEquals(22, a.find(function(val) { return 22 === val; }), undefined);
+
+
+//
+// Test predicate is not called when array is empty
+//
+(function() {
+  var a = new constructor([]);
+  var l = -1;
+  var o = -1;
+  var v = -1;
+  var k = -1;
+
+  a.find(function(val, key, obj) {
+    o = obj;
+    l = obj.length;
+    v = val;
+    k = key;
+
+    return false;
+  });
+
+  assertEquals(-1, l);
+  assertEquals(-1, o);
+  assertEquals(-1, v);
+  assertEquals(-1, k);
+})();
+
+
+//
+// Test predicate is called with correct arguments
+//
+(function() {
+  var a = new constructor([5]);
+  var l = -1;
+  var o = -1;
+  var v = -1;
+  var k = -1;
+
+  var found = a.find(function(val, key, obj) {
+    o = obj;
+    l = obj.length;
+    v = val;
+    k = key;
+
+    return false;
+  });
+
+  assertArrayEquals(a, o);
+  assertEquals(a.length, l);
+  assertEquals(5, v);
+  assertEquals(0, k);
+  assertEquals(undefined, found);
+})();
+
+
+//
+// Test predicate is called array.length times
+//
+(function() {
+  var a = new constructor([1, 2, 3, 4, 5]);
+  var l = 0;
+  var found = a.find(function() {
+    l++;
+    return false;
+  });
+
+  assertEquals(a.length, l);
+  assertEquals(undefined, found);
+})();
+
+
+//
+// Test array modifications
+//
+(function() {
+  a = new constructor([1, 2, 3]);
+  found = a.find(function(val, key) { a[key] = ++val; return false; });
+  assertArrayEquals([2, 3, 4], a);
+  assertEquals(3, a.length);
+  assertEquals(undefined, found);
+})();
+
+//
+// Test thisArg
+//
+(function() {
+  // Test String as a thisArg
+  var found = new constructor([1, 2, 3]).find(function(val, key) {
+    return this.charAt(Number(key)) === String(val);
+  }, "321");
+  assertEquals(2, found);
+
+  // Test object as a thisArg
+  var thisArg = {
+    elementAt: function(key) {
+      return this[key];
+    }
+  };
+  Array.prototype.push.apply(thisArg, [3, 2, 1]);
+
+  found = new constructor([1, 2, 3]).find(function(val, key) {
+    return this.elementAt(key) === val;
+  }, thisArg);
+  assertEquals(2, found);
+
+  // Create a new object in each function call when receiver is a
+  // primitive value. See ECMA-262, Annex C.
+  a = [];
+  new constructor([1, 2]).find(function() { a.push(this) }, "");
+  assertTrue(a[0] !== a[1]);
+
+  // Do not create a new object otherwise.
+  a = [];
+  new constructor([1, 2]).find(function() { a.push(this) }, {});
+  assertEquals(a[0], a[1]);
+
+  // In strict mode primitive values should not be coerced to an object.
+  a = [];
+  new constructor([1, 2]).find(function() { 'use strict'; a.push(this); }, "");
+  assertEquals("", a[0]);
+  assertEquals(a[0], a[1]);
+
+})();
+
+// Test exceptions
+assertThrows('constructor.prototype.find.call(null, function() { })',
+  TypeError);
+assertThrows('constructor.prototype.find.call(undefined, function() { })',
+  TypeError);
+assertThrows('constructor.prototype.find.apply(null, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.find.apply(undefined, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.find.apply([], function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.find.apply({}, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.find.apply("", function() { }, [])',
+  TypeError);
+
+assertThrows('new constructor([]).find(null)', TypeError);
+assertThrows('new constructor([]).find(undefined)', TypeError);
+assertThrows('new constructor([]).find(0)', TypeError);
+assertThrows('new constructor([]).find(true)', TypeError);
+assertThrows('new constructor([]).find(false)', TypeError);
+assertThrows('new constructor([]).find("")', TypeError);
+assertThrows('new constructor([]).find({})', TypeError);
+assertThrows('new constructor([]).find([])', TypeError);
+assertThrows('new constructor([]).find(/\d+/)', TypeError);
+
+}
diff --git a/test/mjsunit/harmony/typedarray-findindex.js b/test/mjsunit/harmony/typedarray-findindex.js
new file mode 100644 (file)
index 0000000..5a0afb4
--- /dev/null
@@ -0,0 +1,179 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrays
+
+var typedArrayConstructors = [
+  Uint8Array,
+  Int8Array,
+  Uint16Array,
+  Int16Array,
+  Uint32Array,
+  Int32Array,
+  Uint8ClampedArray,
+  Float32Array,
+  Float64Array];
+
+for (var constructor of typedArrayConstructors) {
+
+assertEquals(1, constructor.prototype.findIndex.length);
+
+var a = new constructor([21, 22, 23, 24]);
+assertEquals(-1, a.findIndex(function() { return false; }));
+assertEquals(-1, a.findIndex(function(val) { return 121 === val; }));
+assertEquals(0, a.findIndex(function() { return true; }));
+assertEquals(1, a.findIndex(function(val) { return 22 === val; }), undefined);
+assertEquals(2, a.findIndex(function(val) { return 23 === val; }), null);
+assertEquals(3, a.findIndex(function(val) { return 24 === val; }));
+
+
+//
+// Test predicate is not called when array is empty
+//
+(function() {
+  var a = new constructor([]);
+  var l = -1;
+  var o = -1;
+  var v = -1;
+  var k = -1;
+
+  a.findIndex(function(val, key, obj) {
+    o = obj;
+    l = obj.length;
+    v = val;
+    k = key;
+
+    return false;
+  });
+
+  assertEquals(-1, l);
+  assertEquals(-1, o);
+  assertEquals(-1, v);
+  assertEquals(-1, k);
+})();
+
+
+//
+// Test predicate is called with correct argumetns
+//
+(function() {
+  var a = new constructor([5]);
+  var l = -1;
+  var o = -1;
+  var v = -1;
+  var k = -1;
+
+  var index = a.findIndex(function(val, key, obj) {
+    o = obj;
+    l = obj.length;
+    v = val;
+    k = key;
+
+    return false;
+  });
+
+  assertArrayEquals(a, o);
+  assertEquals(a.length, l);
+  assertEquals(5, v);
+  assertEquals(0, k);
+  assertEquals(-1, index);
+})();
+
+
+//
+// Test predicate is called array.length times
+//
+(function() {
+  var a = new constructor([1, 2, 3, 4, 5]);
+  var l = 0;
+
+  a.findIndex(function() {
+    l++;
+    return false;
+  });
+
+  assertEquals(a.length, l);
+})();
+
+
+//
+// Test array modifications
+//
+(function() {
+  a = new constructor([1, 2, 3]);
+  a.findIndex(function(val, key) { a[key] = ++val; return false; });
+  assertArrayEquals([2, 3, 4], a);
+  assertEquals(3, a.length);
+})();
+
+
+//
+// Test thisArg
+//
+(function() {
+  // Test String as a thisArg
+  var index = new constructor([1, 2, 3]).findIndex(function(val, key) {
+    return this.charAt(Number(key)) === String(val);
+  }, "321");
+  assertEquals(1, index);
+
+  // Test object as a thisArg
+  var thisArg = {
+    elementAt: function(key) {
+      return this[key];
+    }
+  };
+  Array.prototype.push.apply(thisArg, [3, 2, 1]);
+
+  index = new constructor([1, 2, 3]).findIndex(function(val, key) {
+    return this.elementAt(key) === val;
+  }, thisArg);
+  assertEquals(1, index);
+
+  // Create a new object in each function call when receiver is a
+  // primitive value. See ECMA-262, Annex C.
+  a = [];
+  new constructor([1, 2]).findIndex(function() { a.push(this) }, "");
+  assertTrue(a[0] !== a[1]);
+
+  // Do not create a new object otherwise.
+  a = [];
+  new constructor([1, 2]).findIndex(function() { a.push(this) }, {});
+  assertEquals(a[0], a[1]);
+
+  // In strict mode primitive values should not be coerced to an object.
+  a = [];
+  new constructor([1, 2]).findIndex(function() { 'use strict'; a.push(this); }, "");
+  assertEquals("", a[0]);
+  assertEquals(a[0], a[1]);
+
+})();
+
+// Test exceptions
+assertThrows('constructor.prototype.findIndex.call(null, function() { })',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.call(undefined, function() { })',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.apply(null, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.apply(undefined, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.apply([], function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.apply({}, function() { }, [])',
+  TypeError);
+assertThrows('constructor.prototype.findIndex.apply("", function() { }, [])',
+  TypeError);
+
+assertThrows('new constructor([]).findIndex(null)', TypeError);
+assertThrows('new constructor([]).findIndex(undefined)', TypeError);
+assertThrows('new constructor([]).findIndex(0)', TypeError);
+assertThrows('new constructor([]).findIndex(true)', TypeError);
+assertThrows('new constructor([]).findIndex(false)', TypeError);
+assertThrows('new constructor([]).findIndex("")', TypeError);
+assertThrows('new constructor([]).findIndex({})', TypeError);
+assertThrows('new constructor([]).findIndex([])', TypeError);
+assertThrows('new constructor([]).findIndex(/\d+/)', TypeError);
+
+}