[change-array-by-copy] Implement Array.prototype.toSpliced
Drive-by: add unscopable test for Array.prototype.toReversed. Bug: v8:12764 Change-Id: I9d7dd8d4eae6d23811382b6795c2c6ff7f76be72 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3717552 Reviewed-by: Marja Hölttä <marja@chromium.org> Commit-Queue: Shu-yu Guo <syg@chromium.org> Cr-Commit-Position: refs/heads/main@{#81364}
This commit is contained in:
parent
902b23272a
commit
3c4c25dbd4
@ -785,6 +785,7 @@ filegroup(
|
||||
"src/builtins/array-some.tq",
|
||||
"src/builtins/array-splice.tq",
|
||||
"src/builtins/array-to-reversed.tq",
|
||||
"src/builtins/array-to-spliced.tq",
|
||||
"src/builtins/array-unshift.tq",
|
||||
"src/builtins/array-with.tq",
|
||||
"src/builtins/array.tq",
|
||||
|
1
BUILD.gn
1
BUILD.gn
@ -1686,6 +1686,7 @@ torque_files = [
|
||||
"src/builtins/array-some.tq",
|
||||
"src/builtins/array-splice.tq",
|
||||
"src/builtins/array-to-reversed.tq",
|
||||
"src/builtins/array-to-spliced.tq",
|
||||
"src/builtins/array-unshift.tq",
|
||||
"src/builtins/array-with.tq",
|
||||
"src/builtins/array.tq",
|
||||
|
@ -58,6 +58,26 @@ macro DoCopyElements<FixedArrayType : type extends FixedArrayBase>(
|
||||
Convert<intptr>(srcIndex), Convert<intptr>(count));
|
||||
}
|
||||
|
||||
macro InsertArgumentsIntoFastPackedArray<
|
||||
FixedArrayType : type extends FixedArrayBase, ElementType: type>(
|
||||
dst: JSArray, dstStart: Smi, args: Arguments,
|
||||
argsStart: constexpr IntegerLiteral, insertCount: Smi): void {
|
||||
// Copy arguments.
|
||||
let k: Smi = dstStart;
|
||||
if (insertCount > 0) {
|
||||
dcheck(Convert<intptr>(insertCount) == args.length - argsStart);
|
||||
const typedNewElements: FixedArrayType =
|
||||
UnsafeCast<FixedArrayType>(dst.elements);
|
||||
for (let i: intptr = ConstexprIntegerLiteralToIntptr(argsStart);
|
||||
i < args.length; ++i) {
|
||||
const e: JSAny = args[i];
|
||||
// The argument elements were already validated to be an appropriate
|
||||
// {ElementType} to store in {FixedArrayType}.
|
||||
typedNewElements[k++] = UnsafeCast<ElementType>(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro
|
||||
FastSplice<FixedArrayType : type extends FixedArrayBase, ElementType: type>(
|
||||
implicit context: Context)(
|
||||
@ -97,22 +117,47 @@ FastSplice<FixedArrayType : type extends FixedArrayBase, ElementType: type>(
|
||||
}
|
||||
|
||||
// Copy arguments.
|
||||
let k: Smi = actualStart;
|
||||
if (insertCount > 0) {
|
||||
const typedNewElements: FixedArrayType =
|
||||
UnsafeCast<FixedArrayType>(a.elements);
|
||||
for (let i: intptr = 2; i < args.length; ++i) {
|
||||
const e: JSAny = args[i];
|
||||
// The argument elements were already validated to be an appropriate
|
||||
// {ElementType} to store in {FixedArrayType}.
|
||||
typedNewElements[k++] = UnsafeCast<ElementType>(e);
|
||||
}
|
||||
}
|
||||
InsertArgumentsIntoFastPackedArray<FixedArrayType, ElementType>(
|
||||
a, actualStart, args, 2, insertCount);
|
||||
|
||||
// Update the array's length after all the FixedArray shuffling is done.
|
||||
a.length = newLength;
|
||||
}
|
||||
|
||||
transitioning macro TransitionElementsKindForInsertionIfNeeded(
|
||||
context: Context, a: JSArray, originalElementsKind: ElementsKind,
|
||||
args: Arguments, argsStart: constexpr IntegerLiteral): ElementsKind {
|
||||
dcheck(a.map.elements_kind == originalElementsKind);
|
||||
|
||||
let elementsKind = originalElementsKind;
|
||||
for (let k: intptr = ConstexprIntegerLiteralToIntptr(argsStart);
|
||||
k < args.length; ++k) {
|
||||
const e = args[k];
|
||||
if (IsFastSmiElementsKind(elementsKind)) {
|
||||
if (TaggedIsNotSmi(e)) {
|
||||
const heapObject: HeapObject = UnsafeCast<HeapObject>(e);
|
||||
elementsKind = IsHeapNumber(heapObject) ?
|
||||
AllowDoubleElements(elementsKind) :
|
||||
AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
} else if (IsDoubleElementsKind(elementsKind)) {
|
||||
if (!IsNumber(e)) {
|
||||
elementsKind = AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
} else {
|
||||
// Already generic.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsKind != originalElementsKind) {
|
||||
const smiElementsKind: Smi = Convert<Smi>(Convert<int32>(elementsKind));
|
||||
TransitionElementsKindWithKind(context, a, smiElementsKind);
|
||||
}
|
||||
|
||||
return elementsKind;
|
||||
}
|
||||
|
||||
transitioning macro FastArraySplice(
|
||||
context: Context, args: Arguments, o: JSReceiver,
|
||||
originalLengthNumber: Number, actualStartNumber: Number, insertCount: Smi,
|
||||
@ -136,27 +181,8 @@ transitioning macro FastArraySplice(
|
||||
let elementsKind: ElementsKind = EnsureArrayPushable(map) otherwise Bailout;
|
||||
if (!IsFastElementsKind(elementsKind)) goto Bailout;
|
||||
|
||||
const oldElementsKind: ElementsKind = elementsKind;
|
||||
for (let i: intptr = 2; i < args.length; ++i) {
|
||||
const e: JSAny = args[i];
|
||||
if (IsFastSmiElementsKind(elementsKind)) {
|
||||
if (TaggedIsNotSmi(e)) {
|
||||
const heapObject: HeapObject = UnsafeCast<HeapObject>(e);
|
||||
elementsKind = IsHeapNumber(heapObject) ?
|
||||
AllowDoubleElements(elementsKind) :
|
||||
AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
} else if (IsDoubleElementsKind(elementsKind)) {
|
||||
if (!IsNumber(e)) {
|
||||
elementsKind = AllowNonNumberElements(elementsKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elementsKind != oldElementsKind) {
|
||||
const smiElementsKind: Smi = Convert<Smi>(Convert<int32>(elementsKind));
|
||||
TransitionElementsKindWithKind(context, a, smiElementsKind);
|
||||
}
|
||||
elementsKind = TransitionElementsKindForInsertionIfNeeded(
|
||||
context, a, elementsKind, args, 2);
|
||||
|
||||
// Make sure that the length hasn't been changed by side-effect.
|
||||
const length: Smi = Cast<Smi>(a.length) otherwise Bailout;
|
||||
@ -403,7 +429,7 @@ ArrayPrototypeSplice(
|
||||
// Bailout exception.
|
||||
const newLength: Number = len + insertCount - actualDeleteCount;
|
||||
if (newLength > kMaxSafeInteger) {
|
||||
ThrowTypeError(MessageTemplate::kInvalidArrayLength, start);
|
||||
ThrowTypeError(MessageTemplate::kInvalidArrayLength, newLength);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -56,8 +56,8 @@ macro TryFastPackedArrayToReversed(implicit context: Context)(receiver: JSAny):
|
||||
goto Slow;
|
||||
}
|
||||
|
||||
transitioning macro GenericArrayToReversed(
|
||||
context: Context, receiver: JSAny): JSAny {
|
||||
transitioning macro GenericArrayToReversed(implicit context: Context)(
|
||||
receiver: JSAny): JSAny {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
const object: JSReceiver = ToObject_Inline(context, receiver);
|
||||
|
||||
@ -96,7 +96,7 @@ transitioning javascript builtin ArrayPrototypeToReversed(
|
||||
try {
|
||||
return TryFastPackedArrayToReversed(receiver) otherwise Slow;
|
||||
} label Slow {
|
||||
return GenericArrayToReversed(context, receiver);
|
||||
return GenericArrayToReversed(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
227
src/builtins/array-to-spliced.tq
Normal file
227
src/builtins/array-to-spliced.tq
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
namespace array {
|
||||
// Makes a copy of the source array for toSpliced without inserting the new
|
||||
// items.
|
||||
macro CopyFastPackedArrayForToSpliced(implicit context: Context)(
|
||||
kind: constexpr ElementsKind, array: JSArray, newLenSmi: Smi,
|
||||
actualStartSmi: Smi, insertCountSmi: Smi,
|
||||
actualDeleteCountSmi: Smi): JSArray {
|
||||
const newLen: intptr = Convert<intptr>(newLenSmi);
|
||||
const actualStart: intptr = Convert<intptr>(actualStartSmi);
|
||||
const insertCount: intptr = Convert<intptr>(insertCountSmi);
|
||||
const actualDeleteCount: intptr = Convert<intptr>(actualDeleteCountSmi);
|
||||
|
||||
const copy: FixedArrayBase =
|
||||
AllocateFixedArray(kind, newLen, AllocationFlag::kNone);
|
||||
|
||||
// Copy the part before the inserted items.
|
||||
CopyElements(kind, copy, 0, array.elements, 0, actualStart);
|
||||
|
||||
// Leave the items to be inserted for later since it could transition the
|
||||
// ElementsKind.
|
||||
|
||||
// Copy the part after the inserted items.
|
||||
const secondPartStart: intptr = actualStart + insertCount;
|
||||
const secondPartLen: intptr = newLen - secondPartStart;
|
||||
const r: intptr = actualStart + actualDeleteCount;
|
||||
dcheck(Convert<Smi>(r + secondPartLen) <= array.length);
|
||||
CopyElements(kind, copy, secondPartStart, array.elements, r, secondPartLen);
|
||||
|
||||
const map: Map = LoadJSArrayElementsMap(kind, LoadNativeContext(context));
|
||||
return NewJSArray(map, copy);
|
||||
}
|
||||
|
||||
transitioning macro TryFastArrayToSpliced(implicit context: Context)(
|
||||
args: Arguments, o: JSReceiver, originalLenNumber: Number,
|
||||
newLenNumber: Number, actualStartNumber: Number, insertCount: Smi,
|
||||
actualDeleteCountNumber: Number): JSArray labels Slow {
|
||||
const newLen: Smi = Cast<Smi>(newLenNumber) otherwise Slow;
|
||||
const actualStart: Smi = Cast<Smi>(actualStartNumber) otherwise Slow;
|
||||
const actualDeleteCount: Smi =
|
||||
Cast<Smi>(actualDeleteCountNumber) otherwise Slow;
|
||||
|
||||
const array: FastJSArray = Cast<FastJSArray>(o) otherwise Slow;
|
||||
|
||||
// If any argument coercion shrunk the source array, go to the slow case.
|
||||
const originalLen: Smi = Cast<Smi>(originalLenNumber) otherwise Slow;
|
||||
if (originalLen > array.length) goto Slow;
|
||||
|
||||
// Array#toSpliced does not preserve holes and always creates packed Arrays.
|
||||
// Holes in the source array-like are treated like any other element and the
|
||||
// value is computed with Get. So, there are only fast paths for packed
|
||||
// elements.
|
||||
let elementsKind: ElementsKind = array.map.elements_kind;
|
||||
if (!IsFastPackedElementsKind(elementsKind)) goto Slow;
|
||||
|
||||
// Make a copy before inserting the new items, as doing so can transition the
|
||||
// ElementsKind.
|
||||
let copy: JSArray;
|
||||
if (elementsKind == ElementsKind::PACKED_SMI_ELEMENTS) {
|
||||
copy = CopyFastPackedArrayForToSpliced(
|
||||
ElementsKind::PACKED_SMI_ELEMENTS, array, newLen, actualStart,
|
||||
insertCount, actualDeleteCount);
|
||||
} else if (elementsKind == ElementsKind::PACKED_ELEMENTS) {
|
||||
copy = CopyFastPackedArrayForToSpliced(
|
||||
ElementsKind::PACKED_ELEMENTS, array, newLen, actualStart, insertCount,
|
||||
actualDeleteCount);
|
||||
} else {
|
||||
dcheck(elementsKind == ElementsKind::PACKED_DOUBLE_ELEMENTS);
|
||||
copy = CopyFastPackedArrayForToSpliced(
|
||||
ElementsKind::PACKED_DOUBLE_ELEMENTS, array, newLen, actualStart,
|
||||
insertCount, actualDeleteCount);
|
||||
}
|
||||
|
||||
// Array#toSpliced's parameters are (start, deleteCount, ...items), so the
|
||||
// first item to insert is at index 2.
|
||||
const kArgsStart = 2;
|
||||
elementsKind = TransitionElementsKindForInsertionIfNeeded(
|
||||
context, copy, elementsKind, args, kArgsStart);
|
||||
|
||||
// Insert the items.
|
||||
dcheck(IsFastPackedElementsKind(elementsKind));
|
||||
if (IsFastSmiOrTaggedElementsKind(elementsKind)) {
|
||||
InsertArgumentsIntoFastPackedArray<FixedArray, JSAny>(
|
||||
copy, actualStart, args, kArgsStart, insertCount);
|
||||
} else {
|
||||
InsertArgumentsIntoFastPackedArray<FixedDoubleArray, Number>(
|
||||
copy, actualStart, args, kArgsStart, insertCount);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
transitioning macro GenericArrayToSpliced(implicit context: Context)(
|
||||
args: Arguments, o: JSReceiver, newLen: Number, actualStart: Number,
|
||||
actualDeleteCount: Number): JSArray {
|
||||
// 13. Let A be ? ArrayCreate(𝔽(newLen)).
|
||||
const copy = ArrayCreate(newLen);
|
||||
|
||||
// 14. Let i be 0.
|
||||
let i: Number = 0;
|
||||
|
||||
// 15. Let r be actualStart + actualDeleteCount.
|
||||
let r: Number = actualStart + actualDeleteCount;
|
||||
|
||||
// 16. Repeat, while i < actualStart,
|
||||
while (i < actualStart) {
|
||||
// a. Let Pi be ! ToString(𝔽(i)).
|
||||
// b. Let iValue be ? Get(O, Pi).
|
||||
const iValue = GetProperty(o, i);
|
||||
|
||||
// c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue).
|
||||
FastCreateDataProperty(copy, i, iValue);
|
||||
|
||||
// d. Set i to i + 1.
|
||||
++i;
|
||||
}
|
||||
|
||||
if (args.length > 2) {
|
||||
// 17. For each element E of items, do
|
||||
for (let k: intptr = 2; k < args.length; ++k) {
|
||||
const e = args[k];
|
||||
|
||||
// a. Let Pi be ! ToString(𝔽(i)).
|
||||
// b. Perform ! CreateDataPropertyOrThrow(A, Pi, E).
|
||||
FastCreateDataProperty(copy, i, e);
|
||||
|
||||
// c. Set i to i + 1.
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// 18. Repeat, while i < newLen,
|
||||
while (i < newLen) {
|
||||
// a. Let Pi be ! ToString(𝔽(i)).
|
||||
// b. Let from be ! ToString(𝔽(r)).
|
||||
// c. Let fromValue be ? Get(O, from).
|
||||
const fromValue = GetProperty(o, r);
|
||||
|
||||
// d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue).
|
||||
FastCreateDataProperty(copy, i, fromValue);
|
||||
|
||||
// e. Set i to i + 1.
|
||||
++i;
|
||||
|
||||
// f. Set r to r + 1.
|
||||
++r;
|
||||
}
|
||||
|
||||
// 19. Return A.
|
||||
return copy;
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-change-array-by-copy/#sec-array.prototype.toSpliced
|
||||
transitioning javascript builtin ArrayPrototypeToSpliced(
|
||||
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
||||
const start = arguments[0];
|
||||
const deleteCount = arguments[1];
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
const o: JSReceiver = ToObject(context, receiver);
|
||||
|
||||
// 2. Let len be ? LengthOfArrayLike(O).
|
||||
const len: Number = GetLengthProperty(o);
|
||||
|
||||
// 3. Let relativeStart be ? ToIntegerOrInfinity(start).
|
||||
const relativeStart: Number = ToInteger_Inline(start);
|
||||
|
||||
// 4. If relativeStart is -∞, let actualStart be 0.
|
||||
// 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart,
|
||||
// 0).
|
||||
// 6. Else, let actualStart be min(relativeStart, len).
|
||||
//
|
||||
// TODO(syg): Support Number length values in ConvertAndClampRelativeIndex.
|
||||
const actualStart = relativeStart < 0 ? Max((len + relativeStart), 0) :
|
||||
Min(relativeStart, len);
|
||||
|
||||
let insertCount: Smi;
|
||||
let actualDeleteCount: Number;
|
||||
if (arguments.length == 0) {
|
||||
// 7. Let insertCount be the number of elements in items.
|
||||
insertCount = 0;
|
||||
|
||||
// 8. If start is not present, then
|
||||
// a. Let actualDeleteCount be 0.
|
||||
actualDeleteCount = 0;
|
||||
} else if (arguments.length == 1) {
|
||||
// 7. Let insertCount be the number of elements in items.
|
||||
insertCount = 0;
|
||||
|
||||
// 9. Else if deleteCount is not present, then
|
||||
// a. Let actualDeleteCount be len - actualStart.
|
||||
actualDeleteCount = len - actualStart;
|
||||
} else {
|
||||
// 7. Let insertCount be the number of elements in items.
|
||||
insertCount = Convert<Smi>(arguments.length) - 2;
|
||||
|
||||
// 10. Else,
|
||||
// a. Let dc be ? ToIntegerOrInfinity(deleteCount).
|
||||
// b. Let actualDeleteCount be the result of clamping dc between 0 and len
|
||||
// - actualStart.
|
||||
const dc = ToInteger_Inline(deleteCount);
|
||||
actualDeleteCount = Min(Max(0, dc), len - actualStart);
|
||||
}
|
||||
|
||||
// 11. Let newLen be len + insertCount - actualDeleteCount.
|
||||
const newLen = len + insertCount - actualDeleteCount;
|
||||
|
||||
// 12. If newLen > 2^53 - 1, throw a TypeError exception.
|
||||
if (newLen > kMaxSafeInteger) {
|
||||
ThrowTypeError(MessageTemplate::kInvalidArrayLength, newLen);
|
||||
}
|
||||
|
||||
if (newLen == 0) return ArrayCreate(0);
|
||||
|
||||
try {
|
||||
return TryFastArrayToSpliced(
|
||||
arguments, o, len, newLen, actualStart, insertCount, actualDeleteCount)
|
||||
otherwise Slow;
|
||||
} label Slow {
|
||||
return GenericArrayToSpliced(
|
||||
arguments, o, newLen, actualStart, actualDeleteCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -4524,6 +4524,8 @@ void Genesis::InitializeGlobal_harmony_change_array_by_copy() {
|
||||
|
||||
SimpleInstallFunction(isolate_, array_prototype, "toReversed",
|
||||
Builtin::kArrayPrototypeToReversed, 0, true);
|
||||
SimpleInstallFunction(isolate_, array_prototype, "toSpliced",
|
||||
Builtin::kArrayPrototypeToSpliced, 2, false);
|
||||
SimpleInstallFunction(isolate_, array_prototype, "with",
|
||||
Builtin::kArrayPrototypeWith, 2, true);
|
||||
|
||||
@ -4533,6 +4535,7 @@ void Genesis::InitializeGlobal_harmony_change_array_by_copy() {
|
||||
.ToHandleChecked());
|
||||
|
||||
InstallTrueValuedProperty(isolate_, unscopables, "toReversed");
|
||||
InstallTrueValuedProperty(isolate_, unscopables, "toSpliced");
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -74,12 +74,11 @@ assertEquals("toReversed", Array.prototype.toReversed.name);
|
||||
let a = [,,,,];
|
||||
Array.prototype[3] = "on proto";
|
||||
let r = a.toReversed();
|
||||
assertEquals([,,,,], a);
|
||||
assertEquals(["on proto",undefined,undefined,undefined], r);
|
||||
assertTrue(r.hasOwnProperty('0'));
|
||||
assertTrue(r.hasOwnProperty('1'));
|
||||
assertTrue(r.hasOwnProperty('2'));
|
||||
assertTrue(r.hasOwnProperty('3'));
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
assertFalse(a.hasOwnProperty(i));
|
||||
assertTrue(r.hasOwnProperty(i));
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestMidIterationShenanigans() {
|
||||
@ -105,3 +104,5 @@ assertEquals("toReversed", Array.prototype.toReversed.name);
|
||||
r = a.toReversed();
|
||||
assertEquals([4,3,2,"poof"], r);
|
||||
})();
|
||||
|
||||
assertEquals(Array.prototype[Symbol.unscopables].toReversed, true);
|
||||
|
144
test/mjsunit/harmony/array-to-spliced.js
Normal file
144
test/mjsunit/harmony/array-to-spliced.js
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2022 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-change-array-by-copy
|
||||
|
||||
assertEquals(2, Array.prototype.toSpliced.length);
|
||||
assertEquals("toSpliced", Array.prototype.toSpliced.name);
|
||||
|
||||
function TerribleCopy(input) {
|
||||
let copy;
|
||||
if (Array.isArray(input)) {
|
||||
copy = [...input];
|
||||
} else {
|
||||
copy = { length: input.length };
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
copy[i] = input[i];
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function AssertToSplicedAndSpliceSameResult(input, ...args) {
|
||||
const orig = TerribleCopy(input);
|
||||
const s = Array.prototype.toSpliced.apply(input, args);
|
||||
const copy = TerribleCopy(input);
|
||||
Array.prototype.splice.apply(copy, args);
|
||||
|
||||
// The in-place spliced version should be pairwise equal to the toSpliced,
|
||||
// modulo being an actual Array if the input is generic.
|
||||
if (Array.isArray(input)) {
|
||||
assertEquals(copy, s);
|
||||
} else {
|
||||
assertEquals(copy.length, s.length);
|
||||
for (let i = 0; i < copy.length; i++) {
|
||||
assertEquals(copy[i], s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// The original input should be unchanged.
|
||||
assertEquals(orig, input);
|
||||
|
||||
// The result of toSpliced() is a copy.
|
||||
assertFalse(s === input);
|
||||
}
|
||||
|
||||
function TestToSplicedBasicBehaviorHelper(input, itemsToInsert) {
|
||||
const startIndices = [0, 1, -1, -100, Infinity, -Infinity];
|
||||
const deleteCounts = [0, 1, 2, 3];
|
||||
|
||||
AssertToSplicedAndSpliceSameResult(input);
|
||||
|
||||
for (let startIndex of startIndices) {
|
||||
AssertToSplicedAndSpliceSameResult(input, startIndex);
|
||||
for (let deleteCount of deleteCounts) {
|
||||
AssertToSplicedAndSpliceSameResult(input, startIndex, deleteCount);
|
||||
AssertToSplicedAndSpliceSameResult(input, startIndex, deleteCount,
|
||||
...itemsToInsert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smi packed
|
||||
TestToSplicedBasicBehaviorHelper([1,2,3,4], [5,6]);
|
||||
|
||||
// Double packed
|
||||
TestToSplicedBasicBehaviorHelper([1.1,2.2,3.3,4.4], [5.5,6.6]);
|
||||
|
||||
// Packed
|
||||
TestToSplicedBasicBehaviorHelper([true,false,1,42.42], [null,"foo"]);
|
||||
|
||||
// Generic
|
||||
TestToSplicedBasicBehaviorHelper({ length: 4,
|
||||
get "0"() { return "hello"; },
|
||||
get "1"() { return "cursed"; },
|
||||
get "2"() { return "java"; },
|
||||
get "3"() { return "script" } },
|
||||
["foo","bar"]);
|
||||
|
||||
(function TestReadOrder() {
|
||||
const order = [];
|
||||
const a = { length: 4,
|
||||
get "0"() { order.push("1st"); return "1st"; },
|
||||
get "1"() { order.push("2nd"); return "2nd"; },
|
||||
get "2"() { order.push("3rd"); return "3rd"; },
|
||||
get "3"() { order.push("4th"); return "4th"; } };
|
||||
const s = Array.prototype.toSpliced.call(a);
|
||||
assertEquals(["1st","2nd","3rd","4th"], s);
|
||||
assertEquals(["1st","2nd","3rd","4th"], order);
|
||||
})();
|
||||
|
||||
(function TestTooBig() {
|
||||
const a = { length: Math.pow(2, 32) };
|
||||
assertThrows(() => Array.prototype.toSpliced.call(a), RangeError);
|
||||
})();
|
||||
|
||||
(function TestNoSpecies() {
|
||||
class MyArray extends Array {
|
||||
static get [Symbol.species]() { return MyArray; }
|
||||
}
|
||||
assertEquals(Array, (new MyArray()).toSpliced().constructor);
|
||||
})();
|
||||
|
||||
// All tests after this have an invalidated elements-on-prototype protector.
|
||||
(function TestNoHoles() {
|
||||
const a = [,,,,];
|
||||
Array.prototype[3] = "on proto";
|
||||
const s = a.toSpliced();
|
||||
assertEquals([undefined,undefined,undefined,"on proto"], s);
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
assertFalse(a.hasOwnProperty(i));
|
||||
assertTrue(s.hasOwnProperty(i));
|
||||
}
|
||||
})();
|
||||
|
||||
(function TestMidIterationShenanigans() {
|
||||
{
|
||||
const a = { length: 4,
|
||||
"0": 1,
|
||||
get "1"() { a.length = 1; return 2; },
|
||||
"2": 3,
|
||||
"3": 4,
|
||||
__proto__: Array.prototype };
|
||||
// The length is cached before iteration, so mid-iteration resizing does not
|
||||
// affect the copied array length.
|
||||
const s = a.toSpliced();
|
||||
assertEquals(1, a.length);
|
||||
assertEquals([1,2,3,4], s);
|
||||
}
|
||||
|
||||
{
|
||||
// Values can be changed mid-iteration.
|
||||
const a = { length: 4,
|
||||
"0": 1,
|
||||
get "1"() { a[3] = "poof"; return 2; },
|
||||
"2": 3,
|
||||
"3": 4,
|
||||
__proto__: Array.prototype };
|
||||
const s = a.toSpliced();
|
||||
assertEquals([1,2,3,"poof"], s);
|
||||
}
|
||||
})();
|
||||
|
||||
assertEquals(Array.prototype[Symbol.unscopables].toSpliced, true);
|
@ -540,30 +540,30 @@ KNOWN_OBJECTS = {
|
||||
("old_space", 0x04a25): "StringSplitCache",
|
||||
("old_space", 0x04e2d): "RegExpMultipleCache",
|
||||
("old_space", 0x05235): "BuiltinsConstantsTable",
|
||||
("old_space", 0x05671): "AsyncFunctionAwaitRejectSharedFun",
|
||||
("old_space", 0x05695): "AsyncFunctionAwaitResolveSharedFun",
|
||||
("old_space", 0x056b9): "AsyncGeneratorAwaitRejectSharedFun",
|
||||
("old_space", 0x056dd): "AsyncGeneratorAwaitResolveSharedFun",
|
||||
("old_space", 0x05701): "AsyncGeneratorYieldResolveSharedFun",
|
||||
("old_space", 0x05725): "AsyncGeneratorReturnResolveSharedFun",
|
||||
("old_space", 0x05749): "AsyncGeneratorReturnClosedRejectSharedFun",
|
||||
("old_space", 0x0576d): "AsyncGeneratorReturnClosedResolveSharedFun",
|
||||
("old_space", 0x05791): "AsyncIteratorValueUnwrapSharedFun",
|
||||
("old_space", 0x057b5): "PromiseAllResolveElementSharedFun",
|
||||
("old_space", 0x057d9): "PromiseAllSettledResolveElementSharedFun",
|
||||
("old_space", 0x057fd): "PromiseAllSettledRejectElementSharedFun",
|
||||
("old_space", 0x05821): "PromiseAnyRejectElementSharedFun",
|
||||
("old_space", 0x05845): "PromiseCapabilityDefaultRejectSharedFun",
|
||||
("old_space", 0x05869): "PromiseCapabilityDefaultResolveSharedFun",
|
||||
("old_space", 0x0588d): "PromiseCatchFinallySharedFun",
|
||||
("old_space", 0x058b1): "PromiseGetCapabilitiesExecutorSharedFun",
|
||||
("old_space", 0x058d5): "PromiseThenFinallySharedFun",
|
||||
("old_space", 0x058f9): "PromiseThrowerFinallySharedFun",
|
||||
("old_space", 0x0591d): "PromiseValueThunkFinallySharedFun",
|
||||
("old_space", 0x05941): "ProxyRevokeSharedFun",
|
||||
("old_space", 0x05965): "ShadowRealmImportValueFulfilledSFI",
|
||||
("old_space", 0x05989): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
|
||||
("old_space", 0x059ad): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
|
||||
("old_space", 0x05675): "AsyncFunctionAwaitRejectSharedFun",
|
||||
("old_space", 0x05699): "AsyncFunctionAwaitResolveSharedFun",
|
||||
("old_space", 0x056bd): "AsyncGeneratorAwaitRejectSharedFun",
|
||||
("old_space", 0x056e1): "AsyncGeneratorAwaitResolveSharedFun",
|
||||
("old_space", 0x05705): "AsyncGeneratorYieldResolveSharedFun",
|
||||
("old_space", 0x05729): "AsyncGeneratorReturnResolveSharedFun",
|
||||
("old_space", 0x0574d): "AsyncGeneratorReturnClosedRejectSharedFun",
|
||||
("old_space", 0x05771): "AsyncGeneratorReturnClosedResolveSharedFun",
|
||||
("old_space", 0x05795): "AsyncIteratorValueUnwrapSharedFun",
|
||||
("old_space", 0x057b9): "PromiseAllResolveElementSharedFun",
|
||||
("old_space", 0x057dd): "PromiseAllSettledResolveElementSharedFun",
|
||||
("old_space", 0x05801): "PromiseAllSettledRejectElementSharedFun",
|
||||
("old_space", 0x05825): "PromiseAnyRejectElementSharedFun",
|
||||
("old_space", 0x05849): "PromiseCapabilityDefaultRejectSharedFun",
|
||||
("old_space", 0x0586d): "PromiseCapabilityDefaultResolveSharedFun",
|
||||
("old_space", 0x05891): "PromiseCatchFinallySharedFun",
|
||||
("old_space", 0x058b5): "PromiseGetCapabilitiesExecutorSharedFun",
|
||||
("old_space", 0x058d9): "PromiseThenFinallySharedFun",
|
||||
("old_space", 0x058fd): "PromiseThrowerFinallySharedFun",
|
||||
("old_space", 0x05921): "PromiseValueThunkFinallySharedFun",
|
||||
("old_space", 0x05945): "ProxyRevokeSharedFun",
|
||||
("old_space", 0x05969): "ShadowRealmImportValueFulfilledSFI",
|
||||
("old_space", 0x0598d): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
|
||||
("old_space", 0x059b1): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
|
||||
}
|
||||
|
||||
# Lower 32 bits of first page addresses for various heap spaces.
|
||||
|
Loading…
Reference in New Issue
Block a user