From a863c4d3d82f72cc5f7e54eaf66a2a4336f1fdc2 Mon Sep 17 00:00:00 2001 From: dehrenberg Date: Tue, 12 May 2015 12:20:58 -0700 Subject: [PATCH] TypedArray.prototype.copyWithin method 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 | 21 ++-- src/harmony-typedarray.js | 11 ++ test/mjsunit/harmony/typedarray-copywithin.js | 175 ++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 test/mjsunit/harmony/typedarray-copywithin.js diff --git a/src/harmony-array.js b/src/harmony-array.js index 8520f35..acf1b14 100644 --- a/src/harmony-array.js +++ b/src/harmony-array.js @@ -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 diff --git a/src/harmony-typedarray.js b/src/harmony-typedarray.js index bbdb91f..c33e08b 100644 --- a/src/harmony-typedarray.js +++ b/src/harmony-typedarray.js @@ -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 index 0000000..cccbc42 --- /dev/null +++ b/test/mjsunit/harmony/typedarray-copywithin.js @@ -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)); +}); -- 2.7.4