Commit 0d6e95b4 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by V8 LUCI CQ

[change-array-by-copy] Implement TypedArray.prototype.toSpliced

Bug: v8:12764
Change-Id: I5f915d1c4dad22f1ce12423f6149a85ad32d6725
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3733043
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81477}
parent 45c29ba6
......@@ -887,6 +887,7 @@ filegroup(
"src/builtins/typed-array-sort.tq",
"src/builtins/typed-array-subarray.tq",
"src/builtins/typed-array-to-reversed.tq",
"src/builtins/typed-array-to-spliced.tq",
"src/builtins/typed-array-values.tq",
"src/builtins/typed-array-with.tq",
"src/builtins/typed-array.tq",
......
......@@ -1793,6 +1793,7 @@ torque_files = [
"src/builtins/typed-array-sort.tq",
"src/builtins/typed-array-subarray.tq",
"src/builtins/typed-array-to-reversed.tq",
"src/builtins/typed-array-to-spliced.tq",
"src/builtins/typed-array-values.tq",
"src/builtins/typed-array-with.tq",
"src/builtins/typed-array.tq",
......
// 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 typed_array {
const kBuiltinNameToSpliced: constexpr string =
'%TypedArray%.prototype.toSpliced';
transitioning macro SlowTypedArrayToSplicedContinuation(
implicit context: Context)(
accessor: TypedArrayAccessor, array: JSTypedArray, arrayLength: uintptr,
copy: JSTypedArray, newLen: uintptr, actualStart: uintptr,
actualDeleteCount: uintptr, insertCount: uintptr): void {
// 17. Let i be 0.
let i: uintptr = 0;
// 18. Let r be actualStart + actualDeleteCount.
let r: uintptr = actualStart + actualDeleteCount;
// 19. Repeat, while i < actualStart,
while (i < actualStart) {
// a. Let Pi be ! ToString(𝔽(i)).
// b. Let iValue be ! Get(O, Pi).
let iValue: JSAny;
if (i < arrayLength) {
iValue = accessor.LoadNumeric(array, i);
} else {
iValue = Undefined;
}
// c. Perform ! Set(target, Pi, iValue, true).
accessor.StoreJSAny(context, copy, i, iValue) otherwise unreachable;
// d. Set i to i + 1.
++i;
}
// 20. For each element E of convertedItems, do
// a. Let Pi be ! ToString(𝔽(i)).
// b. Perform ! Set(A, Pi, E, true).
// c. Set i to i + 1.
//
// Copying already done, just increment i.
i += insertCount;
// 21. 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).
let fromValue: JSAny;
if (r < arrayLength) {
fromValue = accessor.LoadNumeric(array, r);
} else {
fromValue = Undefined;
}
// d. Perform ! Set(A, Pi, fromValue, true).
accessor.StoreJSAny(context, copy, i, fromValue) otherwise unreachable;
// e. Set i to i + 1.
++i;
// f. Set r to r + 1.
++r;
}
}
// https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.toSpliced
transitioning javascript builtin TypedArrayPrototypeToSpliced(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
try {
const start = arguments[0];
const deleteCount = arguments[1];
// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
// 3. Let getBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
// 4. Let len be IntegerIndexedObjectLength(O, getBufferByteLength).
const array: JSTypedArray =
Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
let attachedArrayAndLength = EnsureAttachedAndReadLength(array)
otherwise IsDetachedOrOutOfBounds;
const originalLength = attachedArrayAndLength.length;
// 5. Let relativeStart be ? ToIntegerOrInfinity(start).
const relativeStart: Number = ToInteger_Inline(start);
// 6. If relativeStart is -∞, let actualStart be 0.
// 7. Else if relativeStart < 0, let actualStart be
// max(len + relativeStart, 0).
// 8. Else, let actualStart be min(relativeStart, len).
const actualStart =
ConvertAndClampRelativeIndex(relativeStart, originalLength);
// 9. Let insertCount be the number of elements in items.
let insertCount: uintptr;
let actualDeleteCount: uintptr;
if (arguments.length == 0) {
// 10. If start is not present, then
// a. Let actualDeleteCount be 0.
actualDeleteCount = 0;
insertCount = 0;
} else if (arguments.length == 1) {
// 11. Else if deleteCount is not present, then
// a. Let actualDeleteCount be len - actualStart.
actualDeleteCount = originalLength - actualStart;
insertCount = 0;
} else {
// 12. Else,
// a. Let dc be ? ToIntegerOrInfinity(deleteCount).
// b. Let actualDeleteCount be the result of clamping dc between 0 and
// len - actualStart.
actualDeleteCount = ConvertAndClampRelativeIndex(
deleteCount, originalLength - actualStart);
insertCount = Convert<uintptr>(arguments.length - 2);
}
// 13. Let convertedItems be a new empty List.
const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(array.elements_kind);
// (Step 14 done below, because use of an intermediate list is
// unobservable).
// 15. Let newLen be len + insertCount - actualDeleteCount.
const newLen = originalLength + insertCount - actualDeleteCount;
// 16. Let A be ? TypedArrayCreateSameType(O, « 𝔽(newLen) »).
const copy = TypedArrayCreateSameType(array, newLen);
// 14. For each element E of items, do
for (let k: uintptr = 0; k < insertCount; k++) {
// a. If O.[[ContentType]] is BigInt, let convertedValue be ? ToBigInt(E).
// b. Else, let convertedValue be ? ToNumber(E).
// c. Append convertedValue as the last element of convertedItems.
accessor.StoreJSAny(
context, copy, actualStart + k, arguments[Convert<intptr>(k + 2)])
otherwise unreachable;
}
// 17. Let i be 0.
// 18. Let r be actualStart + actualDeleteCount.
// 19. Repeat, while i < actualStart,
// a. Let Pi be ! ToString(𝔽(i)).
// b. Let iValue be ! Get(O, Pi).
// c. Perform ! Set(target, Pi, iValue, true).
// d. Set i to i + 1.
// 20. For each element E of convertedItems, do
// a. Let Pi be ! ToString(𝔽(i)).
// b. Perform ! Set(A, Pi, E, true).
// c. Set i to i + 1.
// 21. Repeat, while i < newLen,
// a. Let Pi be ! ToString(𝔽(i)).
// b. Let from be ! ToString(𝔽(r)).
// c. Let fromValue be ! Get(O, from).
// d. Perform ! Set(A, Pi, fromValue, true).
// e. Set i to i + 1.
// f. Set r to r + 1.
//
// Step 20 is performed as part of step 13 above.
//
// Steps 17-21 (excluding 20) are implemented by memmove'ing 2 parts: the
// first part before the inserted items, the second part after the inserted
// items.
try {
// If the array shrunk or was detached due to parameter conversion, we go
// to a slow path that implements the spec algorithm naively. While it's
// possible to do partial memmoves, computation of the indices and length
// for the partial moves are harder to reason about, and miscomputation is
// likely a security issue.
attachedArrayAndLength =
EnsureAttachedAndReadLength(array) otherwise goto Slow(0);
const reloadedLength = attachedArrayAndLength.length;
if (reloadedLength < originalLength) goto Slow(reloadedLength);
const info = GetTypedArrayElementsInfo(copy);
const firstPartLength = actualStart;
if (firstPartLength != 0) {
const firstPartCountBytes: uintptr =
info.CalculateByteLength(firstPartLength) otherwise unreachable;
if (IsSharedArrayBuffer(array.buffer)) {
CallCRelaxedMemmove(
copy.data_ptr, array.data_ptr, firstPartCountBytes);
} else {
CallCMemmove(copy.data_ptr, array.data_ptr, firstPartCountBytes);
}
}
const secondPartStartInSrc = actualStart + actualDeleteCount;
const secondPartStartInCopy = actualStart + insertCount;
const secondPartLength = newLen - secondPartStartInCopy;
dcheck(secondPartLength == originalLength - secondPartStartInSrc);
if (secondPartLength != 0) {
const secondPartCountBytes: uintptr =
info.CalculateByteLength(secondPartLength) otherwise unreachable;
const secondPartDstPtr: RawPtr = info.GetDataPointerOffsetByLength(
copy, secondPartStartInCopy) otherwise unreachable;
const secondPartSrcPtr: RawPtr = info.GetDataPointerOffsetByLength(
array, secondPartStartInSrc) otherwise unreachable;
if (IsSharedArrayBuffer(array.buffer)) {
CallCRelaxedMemmove(
secondPartDstPtr, secondPartSrcPtr, secondPartCountBytes);
} else {
CallCMemmove(
secondPartDstPtr, secondPartSrcPtr, secondPartCountBytes);
}
}
} label Slow(reloadedLength: uintptr) deferred {
SlowTypedArrayToSplicedContinuation(
accessor, array, reloadedLength, copy, newLen, actualStart,
actualDeleteCount, insertCount);
}
// 22. Return A.
return copy;
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameToSpliced);
} label IsDetachedOrOutOfBounds deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameToSpliced);
}
}
}
......@@ -50,6 +50,22 @@ struct TypedArrayElementsInfo {
return (bytes & ((1 << this.sizeLog2) - 1)) != 0;
}
// Computes the data pointer with an offset in number of elements.
macro GetDataPointerOffsetByLength(
array: JSTypedArray, offset: uintptr): RawPtr labels IfInvalid {
const dataPtr = array.data_ptr;
const byteOffset: uintptr =
this.CalculateByteLength(offset) otherwise IfInvalid;
@if(DEBUG) {
const byteLength = LoadJSArrayBufferViewByteLength(array, array.buffer)
otherwise unreachable;
dcheck(byteOffset <= byteLength);
}
return dataPtr + Convert<intptr>(byteOffset);
}
sizeLog2: uintptr;
kind: ElementsKind;
}
......
......@@ -4543,6 +4543,8 @@ void Genesis::InitializeGlobal_harmony_change_array_by_copy() {
isolate());
SimpleInstallFunction(isolate_, prototype, "toReversed",
Builtin::kTypedArrayPrototypeToReversed, 0, true);
SimpleInstallFunction(isolate_, prototype, "toSpliced",
Builtin::kTypedArrayPrototypeToSpliced, 2, false);
SimpleInstallFunction(isolate_, prototype, "with",
Builtin::kTypedArrayPrototypeWith, 2, true);
}
......
This diff is collapsed.
......@@ -540,30 +540,30 @@ KNOWN_OBJECTS = {
("old_space", 0x04a15): "StringSplitCache",
("old_space", 0x04e1d): "RegExpMultipleCache",
("old_space", 0x05225): "BuiltinsConstantsTable",
("old_space", 0x05665): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x05689): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x056ad): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x056d1): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x056f5): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x05719): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x0573d): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x05761): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x05785): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x057a9): "PromiseAllResolveElementSharedFun",
("old_space", 0x057cd): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x057f1): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x05815): "PromiseAnyRejectElementSharedFun",
("old_space", 0x05839): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x0585d): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x05881): "PromiseCatchFinallySharedFun",
("old_space", 0x058a5): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x058c9): "PromiseThenFinallySharedFun",
("old_space", 0x058ed): "PromiseThrowerFinallySharedFun",
("old_space", 0x05911): "PromiseValueThunkFinallySharedFun",
("old_space", 0x05935): "ProxyRevokeSharedFun",
("old_space", 0x05959): "ShadowRealmImportValueFulfilledSFI",
("old_space", 0x0597d): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
("old_space", 0x059a1): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
("old_space", 0x05669): "AsyncFunctionAwaitRejectSharedFun",
("old_space", 0x0568d): "AsyncFunctionAwaitResolveSharedFun",
("old_space", 0x056b1): "AsyncGeneratorAwaitRejectSharedFun",
("old_space", 0x056d5): "AsyncGeneratorAwaitResolveSharedFun",
("old_space", 0x056f9): "AsyncGeneratorYieldResolveSharedFun",
("old_space", 0x0571d): "AsyncGeneratorReturnResolveSharedFun",
("old_space", 0x05741): "AsyncGeneratorReturnClosedRejectSharedFun",
("old_space", 0x05765): "AsyncGeneratorReturnClosedResolveSharedFun",
("old_space", 0x05789): "AsyncIteratorValueUnwrapSharedFun",
("old_space", 0x057ad): "PromiseAllResolveElementSharedFun",
("old_space", 0x057d1): "PromiseAllSettledResolveElementSharedFun",
("old_space", 0x057f5): "PromiseAllSettledRejectElementSharedFun",
("old_space", 0x05819): "PromiseAnyRejectElementSharedFun",
("old_space", 0x0583d): "PromiseCapabilityDefaultRejectSharedFun",
("old_space", 0x05861): "PromiseCapabilityDefaultResolveSharedFun",
("old_space", 0x05885): "PromiseCatchFinallySharedFun",
("old_space", 0x058a9): "PromiseGetCapabilitiesExecutorSharedFun",
("old_space", 0x058cd): "PromiseThenFinallySharedFun",
("old_space", 0x058f1): "PromiseThrowerFinallySharedFun",
("old_space", 0x05915): "PromiseValueThunkFinallySharedFun",
("old_space", 0x05939): "ProxyRevokeSharedFun",
("old_space", 0x0595d): "ShadowRealmImportValueFulfilledSFI",
("old_space", 0x05981): "SourceTextModuleExecuteAsyncModuleFulfilledSFI",
("old_space", 0x059a5): "SourceTextModuleExecuteAsyncModuleRejectedSFI",
}
# Lower 32 bits of first page addresses for various heap spaces.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment