TypedArray.prototype.copyWithin method
authordehrenberg <dehrenberg@chromium.org>
Tue, 12 May 2015 19:20:58 +0000 (12:20 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 12 May 2015 19:20:56 +0000 (19:20 +0000)
This patch adds the copyWithin method to TypedArrays. For the first
pass, the internals of Array.copyWithin are used. Eventually, a more
efficient form based on memcpy could be used instead.

BUG=v8:3578
LOG=Y
R=adamk@chromium.org, arv@chromium.org, caitpotter88@gmail.com

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

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

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

index 8520f35..acf1b14 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var $innerArrayCopyWithin;
+
 (function(global, exports) {
 
 'use strict';
@@ -13,13 +15,7 @@ var GlobalSymbol = global.Symbol;
 
 // -------------------------------------------------------------------
 
-// ES6 draft 03-17-15, section 22.1.3.3
-function ArrayCopyWithin(target, start, end) {
-  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
-
-  var array = TO_OBJECT_INLINE(this);
-  var length = $toLength(array.length);
-
+function InnerArrayCopyWithin(target, start, end, array, length) {
   target = TO_INTEGER(target);
   var to;
   if (target < 0) {
@@ -65,6 +61,17 @@ function ArrayCopyWithin(target, start, end) {
 
   return array;
 }
+$innerArrayCopyWithin = InnerArrayCopyWithin;
+
+// ES6 draft 03-17-15, section 22.1.3.3
+function ArrayCopyWithin(target, start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
+
+  var array = TO_OBJECT_INLINE(this);
+  var length = $toLength(array.length);
+
+  return InnerArrayCopyWithin(target, start, end, array, length);
+}
 
 // ES6 draft 07-15-13, section 15.4.3.23
 function ArrayFind(predicate /* thisArg */) {  // length == 1
index bbdb91f..c33e08b 100644 (file)
@@ -29,6 +29,16 @@ TYPED_ARRAYS(DECLARE_GLOBALS)
 
 // -------------------------------------------------------------------
 
+function TypedArrayCopyWithin(target, start, end) {
+  if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  // TODO(dehrenberg): Replace with a memcpy for better performance
+  return $innerArrayCopyWithin(target, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayCopyWithin, 2);
+
 // ES6 draft 05-05-15, section 22.2.3.7
 function TypedArrayEvery(f, receiver) {
   if (!%IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
@@ -67,6 +77,7 @@ macro EXTEND_TYPED_ARRAY(NAME)
 
   // Set up non-enumerable functions on the prototype object.
   $installFunctions(GlobalNAME.prototype, DONT_ENUM, [
+    "copyWithin", TypedArrayCopyWithin,
     "every", TypedArrayEvery,
     "forEach", TypedArrayForEach
   ]);
diff --git a/test/mjsunit/harmony/typedarray-copywithin.js b/test/mjsunit/harmony/typedarray-copywithin.js
new file mode 100644 (file)
index 0000000..cccbc42
--- /dev/null
@@ -0,0 +1,175 @@
+// 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];
+
+function CheckEachTypedArray(fn) {
+  typedArrayConstructors.forEach(fn);
+}
+
+CheckEachTypedArray(function copyWithinArity(constructor) {
+  assertEquals(new constructor([]).copyWithin.length, 2);
+});
+
+
+CheckEachTypedArray(function copyWithinTargetAndStart(constructor) {
+  // works with two arguments
+  assertArrayEquals([4, 5, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3));
+  assertArrayEquals([1, 4, 5, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(1, 3));
+  assertArrayEquals([1, 3, 4, 5, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(1, 2));
+  assertArrayEquals([1, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(2, 2));
+});
+
+
+CheckEachTypedArray(function copyWithinTargetStartAndEnd(constructor) {
+  // works with three arguments
+  assertArrayEquals(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, 4),
+                                    [4, 2, 3, 4, 5]);
+  assertArrayEquals(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 3, 4),
+                                    [1, 4, 3, 4, 5]);
+  assertArrayEquals(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 2, 4),
+                                    [1, 3, 4, 4, 5]);
+});
+
+
+CheckEachTypedArray(function copyWithinNegativeRelativeOffsets(constructor) {
+  // works with negative arguments
+  assertArrayEquals([4, 5, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, -2));
+  assertArrayEquals([4, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, -2, -1));
+  assertArrayEquals([1, 3, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3, -2));
+  assertArrayEquals([1, 3, 4, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3, -1));
+  assertArrayEquals([1, 3, 4, 5, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3));
+  // test with arguments equal to -this.length
+  assertArrayEquals([1, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-5, 0));
+});
+
+
+CheckEachTypedArray(function mustBeTypedArray(constructor) {
+  // throws on non-TypedArray values
+  assertThrows(function() {
+    return constructor.prototype.copyWithin.call(null, 0, 3);
+  }, TypeError);
+  assertThrows(function() {
+    return constructor.prototype.copyWithin.call(undefined, 0, 3);
+  }, TypeError);
+  assertThrows(function() {
+    return constructor.prototype.copyWithin.call(34, 0, 3);
+  }, TypeError);
+  assertThrows(function() {
+    return constructor.prototype.copyWithin.call([1, 2, 3, 4, 5], 0, 3);
+  }, TypeError);
+});
+
+
+CheckEachTypedArray(function copyWithinStartLessThanTarget(constructor) {
+  // test with target > start on 2 arguments
+  assertArrayEquals([1, 2, 3, 1, 2],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(3, 0));
+
+  // test with target > start on 3 arguments
+  assertArrayEquals([1, 2, 3, 1, 2],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(3, 0, 4));
+});
+
+CheckEachTypedArray(function copyWithinNonIntegerRelativeOffsets(constructor) {
+  // test on fractional arguments
+  assertArrayEquals([4, 5, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0.2, 3.9));
+});
+
+
+CheckEachTypedArray(function copyWithinNegativeZeroTarget(constructor) {
+  // test with -0
+  assertArrayEquals([4, 5, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-0, 3));
+});
+
+
+CheckEachTypedArray(function copyWithinTargetOutsideStart(constructor) {
+  // test with arguments more than this.length
+  assertArrayEquals([1, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, 7));
+
+  // test with arguments less than -this.length
+  assertArrayEquals([1, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(-7, 0));
+});
+
+
+CheckEachTypedArray(function copyWithinEmptyArray(constructor) {
+  // test on empty array
+  assertArrayEquals([], new constructor([]).copyWithin(0, 3));
+});
+
+
+CheckEachTypedArray(function copyWithinTargetCutOff(constructor) {
+  // test with target range being shorter than end - start
+  assertArrayEquals([1, 2, 2, 3, 4], [1, 2, 3, 4, 5].copyWithin(2, 1, 4));
+});
+
+
+CheckEachTypedArray(function copyWithinOverlappingRanges(constructor) {
+  // test overlapping ranges
+  var arr = [1, 2, 3, 4, 5];
+  arr.copyWithin(2, 1, 4);
+  assertArrayEquals([1, 2, 2, 2, 3], arr.copyWithin(2, 1, 4));
+});
+
+
+CheckEachTypedArray(function copyWithinDefaultEnd(constructor) {
+  // undefined as third argument
+  assertArrayEquals([4, 5, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, undefined));
+});
+
+
+CheckEachTypedArray(function copyWithinLargeArray(constructor) {
+  var large = 10000;
+
+  // test on a large array
+  var arr = new constructor(large);
+  assertArrayEquals(arr, arr.copyWithin(45, 9000));
+
+  var expected = new Array(large);
+  // test on numbers
+  for (var i = 0; i < large; i++) {
+    arr[i] = Math.random() * 100;  // May be cast to an int
+    expected[i] = arr[i];
+    if (i >= 9000) {
+      expected[(i - 9000) + 45] = arr[i];
+    }
+  }
+  assertArrayEquals(expected, arr.copyWithin(45, 9000));
+
+  // test array length remains same
+  assertEquals(large, arr.length);
+});
+
+
+CheckEachTypedArray(function copyWithinNullEnd(constructor) {
+  // test null on third argument is converted to +0
+  assertArrayEquals([1, 2, 3, 4, 5],
+                    new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, null));
+});