Revert of Revert of [es6] implement Array.prototype.copyWithin() (patchset #1 id...
authormachenbach <machenbach@chromium.org>
Wed, 22 Apr 2015 09:43:32 +0000 (02:43 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 22 Apr 2015 09:43:13 +0000 (09:43 +0000)
Reason for revert:
Check if this CL fails independently of https://chromium.googlesource.com/v8/v8/+/580d66bcda66220d2f3062ac58daf925436df74c

Original issue's description:
> Revert of [es6] implement Array.prototype.copyWithin() (patchset #7 id:120001 of https://codereview.chromium.org/376623004/)
>
> Reason for revert:
> [Sheriff] This causes test failures on mac gc stress:
> http://build.chromium.org/p/client.v8/builders/V8%20Mac%20GC%20Stress/builds/1027
>
> Original issue's description:
> > [es6] implement Array.prototype.copyWithin()
> >
> > https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.prototype.copywithin
> >
> > BUG=v8:4039
> > R=adamk@chromium.org
> > LOG=N
>
> TBR=dslomov@chromium.org,rossberg@chromium.org,adamk@chromium.org,caitpotter88@gmail.com
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=v8:4039
>
> Committed: https://crrev.com/9283fc89710e59445bdc4479454fba97ab9ebdd7
> Cr-Commit-Position: refs/heads/master@{#27984}

TBR=dslomov@chromium.org,rossberg@chromium.org,adamk@chromium.org,caitpotter88@gmail.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:4039

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

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

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

index 18cda17..934d3b7 100644 (file)
@@ -13,6 +13,59 @@ 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);
+
+  target = TO_INTEGER(target);
+  var to;
+  if (target < 0) {
+    to = $max(length + target, 0);
+  } else {
+    to = $min(target, length);
+  }
+
+  start = TO_INTEGER(start);
+  var from;
+  if (start < 0) {
+    from = $max(length + start, 0);
+  } else {
+    from = $min(start, length);
+  }
+
+  end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
+  var final;
+  if (end < 0) {
+    final = $max(length + end, 0);
+  } else {
+    final = $min(end, length);
+  }
+
+  var count = $min(final - from, length - to);
+  var direction = 1;
+  if (from < to && to < (from + count)) {
+    direction = -1;
+    from = from + count - 1;
+    to = to + count - 1;
+  }
+
+  while (count > 0) {
+    if (from in array) {
+      array[to] = array[from];
+    } else {
+      delete array[to];
+    }
+    from = from + direction;
+    to = to + direction;
+    count--;
+  }
+
+  return array;
+}
+
 // ES6 draft 07-15-13, section 15.4.3.23
 function ArrayFind(predicate /* thisArg */) {  // length == 1
   CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
@@ -216,6 +269,7 @@ InstallConstants(GlobalSymbol, [
   "isConcatSpreadable", symbolIsConcatSpreadable
 ]);
 
+%FunctionSetLength(ArrayCopyWithin, 2);
 %FunctionSetLength(ArrayFrom, 1);
 
 // Set up non-enumerable functions on the Array object.
@@ -226,6 +280,7 @@ InstallFunctions(GlobalArray, DONT_ENUM, [
 
 // Set up the non-enumerable functions on the Array prototype object.
 InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
+  "copyWithin", ArrayCopyWithin,
   "find", ArrayFind,
   "findIndex", ArrayFindIndex,
   "fill", ArrayFill
diff --git a/test/mjsunit/harmony/array-copywithin.js b/test/mjsunit/harmony/array-copywithin.js
new file mode 100644 (file)
index 0000000..c3a0c14
--- /dev/null
@@ -0,0 +1,338 @@
+// 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
+
+(function copyWithinArity() {
+  assertEquals(Array.prototype.copyWithin.length, 2);
+})();
+
+
+(function copyWithinTargetAndStart() {
+  // works with two arguemnts
+  assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3));
+  assertArrayEquals([1, 4, 5, 4, 5], [1, 2, 3, 4, 5].copyWithin(1, 3));
+  assertArrayEquals([1, 3, 4, 5, 5], [1, 2, 3, 4, 5].copyWithin(1, 2));
+  assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(2, 2));
+})();
+
+
+(function copyWithinTargetStartAndEnd() {
+  // works with three arguments
+  assertArrayEquals([1, 2, 3, 4, 5].copyWithin(0, 3, 4), [4, 2, 3, 4, 5]);
+  assertArrayEquals([1, 2, 3, 4, 5].copyWithin(1, 3, 4), [1, 4, 3, 4, 5]);
+  assertArrayEquals([1, 2, 3, 4, 5].copyWithin(1, 2, 4), [1, 3, 4, 4, 5]);
+})();
+
+
+(function copyWithinNegativeRelativeOffsets() {
+  // works with negative arguments
+  assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, -2));
+  assertArrayEquals([4, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, -2, -1));
+  assertArrayEquals([1, 3, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3, -2));
+  assertArrayEquals([1, 3, 4, 4, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3, -1));
+  assertArrayEquals([1, 3, 4, 5, 5], [1, 2, 3, 4, 5].copyWithin(-4, -3));
+  // test with arguments equal to -this.length
+  assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-5, 0));
+})();
+
+
+(function copyWithinArrayLikeValues() {
+  // works with array-like values
+  var args = (function () { return arguments; }(1, 2, 3));
+  Array.prototype.copyWithin.call(args, -2, 0);
+  assertArrayEquals([1, 1, 2], Array.prototype.slice.call(args));
+
+  // [[Class]] does not change
+  assertArrayEquals("[object Arguments]", Object.prototype.toString.call(args));
+})();
+
+
+(function copyWithinNullThis() {
+  // throws on null/undefined values
+  assertThrows(function() {
+    return Array.prototype.copyWithin.call(null, 0, 3);
+  }, TypeError);
+})();
+
+
+(function copyWithinUndefinedThis() {
+  assertThrows(function() {
+    return Array.prototype.copyWithin.call(undefined, 0, 3);
+  }, TypeError);
+})();
+
+
+// TODO(caitp): indexed properties of String are read-only and setting them
+//              should throw in strict mode. See bug v8:4042
+// (function copyWithinStringThis() {
+//   // test with this value as string
+//   assertThrows(function() {
+//     return Array.prototype.copyWithin.call("hello world", 0, 3);
+//   }, TypeError);
+// })();
+
+
+(function copyWithinNumberThis() {
+  // test with this value as number
+  assertEquals(34, Array.prototype.copyWithin.call(34, 0, 3).valueOf());
+})();
+
+
+(function copyWithinSymbolThis() {
+  // test with this value as number
+  var sym = Symbol("test");
+  assertEquals(sym, Array.prototype.copyWithin.call(sym, 0, 3).valueOf());
+})();
+
+
+(function copyyWithinTypedArray() {
+  // test with this value as TypedArray
+  var buffer = new ArrayBuffer(16);
+  var int32View = new Int32Array(buffer);
+  for (var i=0; i<int32View.length; i++) {
+    int32View[i] = i*2;
+  }
+  assertArrayEquals(new Int32Array([2, 4, 6, 6]),
+                    Array.prototype.copyWithin.call(int32View, 0, 1));
+})();
+
+
+(function copyWithinSloppyArguments() {
+  // if arguments object is sloppy, copyWithin must move the arguments around
+  function f(a, b, c, d, e) {
+    [].copyWithin.call(arguments, 1, 3);
+    return [a, b, c, d, e];
+  }
+  assertArrayEquals([1, 4, 5, 4, 5], f(1, 2, 3, 4, 5));
+})();
+
+
+(function copyWithinStartLessThanTarget() {
+  // test with target > start on 2 arguments
+  assertArrayEquals([1, 2, 3, 1, 2], [1, 2, 3, 4, 5].copyWithin(3, 0));
+
+  // test with target > start on 3 arguments
+  assertArrayEquals([1, 2, 3, 1, 2], [1, 2, 3, 4, 5].copyWithin(3, 0, 4));
+})();
+
+
+(function copyWithinArrayWithHoles() {
+  // test on array with holes
+  var arr = new Array(6);
+  for (var i = 0; i < arr.length; i += 2) {
+    arr[i] = i;
+  }
+  assertArrayEquals([, 4, , , 4, , ], arr.copyWithin(0, 3));
+})();
+
+
+(function copyWithinArrayLikeWithHoles() {
+  // test on array-like object with holes
+  assertArrayEquals({
+    length: 6,
+    1: 4,
+    4: 4
+  }, Array.prototype.copyWithin.call({
+    length: 6,
+    0: 0,
+    2: 2,
+    4: 4
+  }, 0, 3));
+})();
+
+
+(function copyWithinNonIntegerRelativeOffsets() {
+  // test on fractional arguments
+  assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0.2, 3.9));
+})();
+
+
+(function copyWithinNegativeZeroTarget() {
+  // test with -0
+  assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-0, 3));
+})();
+
+
+(function copyWithinTargetOutsideStart() {
+  // test with arguments more than this.length
+  assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 7));
+
+  // test with arguments less than -this.length
+  assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(-7, 0));
+})();
+
+
+(function copyWithinEmptyArray() {
+  // test on empty array
+  assertArrayEquals([], [].copyWithin(0, 3));
+})();
+
+
+(function copyWithinTargetCutOff() {
+  // test with target range being shorter than end - start
+  assertArrayEquals([1, 2, 2, 3, 4], [1, 2, 3, 4, 5].copyWithin(2, 1, 4));
+})();
+
+
+(function copyWithinOverlappingRanges() {
+  // 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));
+})();
+
+
+(function copyWithinStrictDelete() {
+  // check that [[Delete]] is strict (non-extensible via freeze)
+  assertThrows(function() {
+    return Object.freeze([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
+  }, TypeError);
+
+  // check that [[Delete]] is strict (non-extensible via seal)
+  assertThrows(function() {
+    return Object.seal([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
+  }, TypeError);
+
+  // check that [[Delete]] is strict (non-extensible via preventExtensions)
+  assertThrows(function() {
+    return Object.preventExtensions([1, , 3, , 4, 5]).copyWithin(2, 1, 4);
+  }, TypeError);
+})();
+
+
+(function copyWithinStrictSet() {
+  // check that [[Set]] is strict (non-extensible via freeze)
+  assertThrows(function() {
+    return Object.freeze([1, 2, 3, 4, 5]).copyWithin(0, 3);
+  }, TypeError);
+
+  // check that [[Set]] is strict (non-extensible via seal)
+  assertThrows(function() {
+    return Object.seal([, 2, 3, 4, 5]).copyWithin(0, 3);
+  }, TypeError);
+
+  // check that [[Set]] is strict (non-extensible via preventExtensions)
+  assertThrows(function() {
+    return Object.preventExtensions([ , 2, 3, 4, 5]).copyWithin(0, 3);
+  }, TypeError);
+})();
+
+
+(function copyWithinSetterThrows() {
+  function Boom() {}
+  // test if we throw in between
+  var arr = Object.defineProperty([1, 2, 3, 4, 5], 1, {
+    set: function () {
+      throw new Boom();
+    }
+  });
+
+  assertThrows(function() {
+    return arr.copyWithin(1, 3);
+  }, Boom);
+
+  assertArrayEquals([1, , 3, 4, 5], arr);
+})();
+
+
+(function copyWithinDefaultEnd() {
+  // undefined as third argument
+  assertArrayEquals(
+      [4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3, undefined));
+})();
+
+
+(function copyWithinGetLengthOnce() {
+  // test that this.length is called only once
+  var count = 0;
+  var arr = Object.defineProperty({ 0: 1, 1: 2, 2: 3, 3: 4, 4: 5 }, "length", {
+    get: function () {
+      count++;
+      return 5;
+    }
+  });
+  Array.prototype.copyWithin.call(arr, 1, 3);
+  assertEquals(1, count);
+
+  Array.prototype.copyWithin.call(arr, 1, 3, 4);
+  assertEquals(2, count);
+})();
+
+
+(function copyWithinLargeArray() {
+  var large = 10000;
+
+  // test on a large array
+  var arr = new Array(large);
+  assertArrayEquals(arr, arr.copyWithin(45, 9000));
+
+  var expected = new Array(large);
+  // test on floating point numbers
+  for (var i = 0; i < large; i++) {
+    arr[i] = Math.random();
+    expected[i] = arr[i];
+    if (i >= 9000) {
+      expected[(i - 9000) + 45] = arr[i];
+    }
+  }
+  assertArrayEquals(expected, arr.copyWithin(45, 9000));
+
+  // test on array of objects
+  for (var i = 0; i < large; i++) {
+    arr[i] = { num: Math.random() };
+  } + 45
+  arr.copyWithin(45, 9000);
+
+  // test copied by reference
+  for (var i = 9000; i < large; ++i) {
+    assertSame(arr[(i - 9000) + 45], arr[i]);
+  }
+
+  // test array length remains same
+  assertEquals(large, arr.length);
+})();
+
+
+(function copyWithinSuperLargeLength() {
+  // 2^53 - 1 is the maximum value returned from ToLength()
+  var large = Math.pow(2, 53) - 1;
+  var object = { length: large };
+
+  // Initialize last 10 entries
+  for (var i = 1; i <= 10; ++i) {
+    object[(large - 11) + i] = { num: i };
+  }
+
+  Array.prototype.copyWithin.call(object, 1, large - 10);
+
+  // Test copied values
+  for (var i = 1; i <= 10; ++i) {
+    var old_ref = object[(large - 11) + i];
+    var new_ref = object[i];
+    assertSame(old_ref, new_ref);
+    assertSame(new_ref.num, i);
+  }
+
+  // Assert length has not changed
+  assertEquals(large, object.length);
+})();
+
+
+(function copyWithinNullEnd() {
+  // test null on third argument is converted to +0
+  assertArrayEquals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3, null));
+})();
+
+
+(function copyWithinElementsInObjectsPrototype() {
+  // tamper the global Object prototype and test this works
+  Object.prototype[2] = 1;
+  assertArrayEquals([4, 5, 3, 4, 5], [1, 2, 3, 4, 5].copyWithin(0, 3));
+  delete Object.prototype[2];
+
+  Object.prototype[3] = "FAKE";
+  assertArrayEquals(["FAKE", 5, 3, "FAKE", 5], [1, 2, 3, , 5].copyWithin(0, 3));
+  delete Object.prototype[3];
+})();