[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

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

Cr-Commit-Position: refs/heads/master@{#27983}
This commit is contained in:
caitpotter88 2015-04-21 12:05:49 -07:00 committed by Commit bot
parent 9974348674
commit 6d00703e5e
2 changed files with 393 additions and 0 deletions

View 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

View File

@ -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];
})();