blob: 0ab2f952146bdb5cc9464642532c8b702b176bdc [file] [log] [blame]
// Copyright 2019 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.
#include 'src/builtins/builtins-typed-array-gen.h'
namespace typed_array {
const kBuiltinNameSet: constexpr string = '%TypedArray%.prototype.set';
extern runtime TypedArraySet(Context, JSTypedArray, Object, Number, Number):
void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyFastNumberJSArrayElementsToTypedArray(
Context,
FastJSArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
extern macro
TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray(
AttachedJSTypedArray, // source
AttachedJSTypedArray, // dest
uintptr, // sourceLength
uintptr // destOffset
): void;
// %TypedArray%.prototype.set ( overloaded [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-overloaded-offset
transitioning javascript builtin TypedArrayPrototypeSet(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// Steps 2-8 are the same for
// %TypedArray%.prototype.set ( array [ , offset ] ) and
// %TypedArray%.prototype.set ( typedArray [ , offset ] ) overloads.
let target: JSTypedArray;
try {
// 2. Let target be the this value.
// 3. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
// 4. Assert: target has a [[ViewedArrayBuffer]] internal slot.
target = Cast<JSTypedArray>(receiver) otherwise NotTypedArray;
} label NotTypedArray deferred {
ThrowTypeError(MessageTemplate::kNotTypedArray, kBuiltinNameSet);
}
try {
// 5. Let targetOffset be ? ToInteger(offset).
// 6. If targetOffset < 0, throw a RangeError exception.
let targetOffsetOverflowed: bool = false;
let targetOffset: uintptr = 0;
if (arguments.length > 1) {
const offsetArg = arguments[1];
try {
targetOffset = ToUintPtr(offsetArg)
// On values less than zero throw RangeError immediately.
otherwise OffsetOutOfBounds,
// On UintPtr or SafeInteger range overflow throw RangeError after
// performing observable steps to follow the spec.
OffsetOverflow, OffsetOverflow;
} label OffsetOverflow {
targetOffsetOverflowed = true;
}
} else {
// If the offset argument is not provided then the targetOffset is 0.
}
// 7. Let targetBuffer be target.[[ViewedArrayBuffer]].
// 8. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError
// exception.
const attachedTargetAndLength = EnsureAttachedAndReadLength(target)
otherwise IsDetachedOrOutOfBounds;
const overloadedArg = arguments[0];
try {
// 1. Choose SetTypedArrayFromTypedArray or SetTypedArrayFromArrayLike
// depending on whether the overloadedArg has a [[TypedArrayName]]
// internal slot.
const typedArray =
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
// Step 3 is not observable, do it later.
// 4. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
// 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
// exception.
const attachedSourceAndLength = EnsureAttachedAndReadLength(typedArray)
otherwise IsDetachedOrOutOfBounds;
TypedArrayPrototypeSetTypedArray(
attachedTargetAndLength, attachedSourceAndLength, targetOffset,
targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
} label NotTypedArray deferred {
TypedArrayPrototypeSetArray(
target, attachedTargetAndLength.length, overloadedArg, targetOffset,
targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
}
} label OffsetOutOfBounds deferred {
ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
} label IsDetachedOrOutOfBounds deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
}
}
// SetTypedArrayFromArrayLike
// https://tc39.es/ecma262/#sec-settypedarrayfromarraylike
transitioning macro TypedArrayPrototypeSetArray(
implicit context: Context, receiver: JSAny)(target: JSTypedArray,
targetLength: uintptr, arrayArg: JSAny, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// 4. Let src be ? ToObject(source).
const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 5. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src);
// 6. If targetOffset is +∞, throw a RangeError exception.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 7. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
otherwise IfOffsetOutOfBounds;
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
try {
// BigInt typed arrays are not handled by
// CopyFastNumberJSArrayElementsToTypedArray.
if (IsBigInt64ElementsKind(target.elements_kind)) goto IfSlow;
const fastSrc: FastJSArray = Cast<FastJSArray>(src) otherwise goto IfSlow;
const srcKind: ElementsKind = fastSrc.map.elements_kind;
// CopyFastNumberJSArrayElementsToTypedArray() can be used only with the
// following elements kinds:
// PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS,
// HOLEY_DOUBLE_ELEMENTS.
if (IsElementsKindInRange(
srcKind, ElementsKind::PACKED_SMI_ELEMENTS,
ElementsKind::HOLEY_SMI_ELEMENTS) ||
IsElementsKindInRange(
srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS,
ElementsKind::HOLEY_DOUBLE_ELEMENTS)) {
// If the source is a JSArray (no custom length getter or elements
// getter), there's nothing that could detach or resize the target, so
// it's always non-detached here. Also we don't need to reload the length.
const utarget = typed_array::EnsureAttached(target) otherwise unreachable;
CallCCopyFastNumberJSArrayElementsToTypedArray(
context, fastSrc, utarget, srcLength, targetOffset);
} else {
goto IfSlow;
}
} label IfSlow deferred {
TypedArraySet(
context, target, src, srcLengthNum, Convert<Number>(targetOffset));
}
}
// SetTypedArrayFromTypedArray
// https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray
transitioning macro TypedArrayPrototypeSetTypedArray(
implicit context: Context, receiver: JSAny)(
attachedTargetAndLength: AttachedJSTypedArrayAndLength,
attachedSourceAndLength: AttachedJSTypedArrayAndLength,
targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 6-14 are not observable, so we can handle offset overflow
// at step 15 here.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 3. Let targetLength be IntegerIndexedObjectLength(target).
const target = attachedTargetAndLength.array;
const targetLength = attachedTargetAndLength.length;
// 13. Let srcLength be IntegerIndexedObjectLength(source).
const source = attachedSourceAndLength.array;
const srcLength = attachedSourceAndLength.length;
// 16. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// 6. Let targetName be the String value of target.[[TypedArrayName]].
// 7. Let targetType be the Element Type value in Table 62 for
// targetName.
// 8. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
const targetElementsInfo = GetTypedArrayElementsInfo(target);
// 10. Let srcName be the String value of source.[[TypedArrayName]].
// 11. Let srcType be the Element Type value in Table 62 for srcName.
// 12. Let srcElementSize be the Element Size value specified in
// Table 62 for srcName.
const srcKind: ElementsKind = source.elements_kind;
// We skip steps 18-20 because both memmove and
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
// regions.
// 18. If both IsSharedArrayBuffer(srcBuffer) and
// IsSharedArrayBuffer(targetBuffer) are true, then
// a. If srcBuffer.[[ArrayBufferData]] and
// targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
// values, let same be true; else let same be false.
// 19. Else, let same be SameValue(srcBuffer, targetBuffer).
// 20. If same is true, then
// a. Let srcByteLength be source.[[ByteLength]].
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset,
// srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known
// to not have any observable side-effects.
// d. Let srcByteIndex be 0.
try {
// Use memmove if possible.
// TODO(v8:11111): Enable fast copying between a RAB/GSAB element kind and
// the corresponding non-RAB/GSAB element kind.
if (srcKind != targetElementsInfo.kind) {
// Uint8/Uint8Clamped elements could still be copied with memmove.
if (!IsUint8ElementsKind(srcKind) ||
!IsUint8ElementsKind(targetElementsInfo.kind)) {
goto IfSlow;
}
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// Source and destination typed arrays have same elements kinds (modulo
// Uint8-Uint8Clamped difference) so we can use targetElementsInfo for
// calculations.
// Since the source and destination typed arrays already exist, the byte
// size can't be too big, so the error case is unreachable.
const countBytes: uintptr =
targetElementsInfo.CalculateByteLength(srcLength)
otherwise unreachable;
const startOffset: uintptr =
targetElementsInfo.CalculateByteLength(targetOffset)
otherwise unreachable;
const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);
// We've already checked for detachedness, and there's nothing that could've
// detached the buffers until here.
@if(DEBUG) {
const targetByteLength = LoadJSArrayBufferViewByteLength(
target, target.buffer) otherwise unreachable;
const sourceByteLength = LoadJSArrayBufferViewByteLength(
source, source.buffer) otherwise unreachable;
dcheck(countBytes <= targetByteLength - startOffset);
dcheck(countBytes <= sourceByteLength);
}
// 24. If srcType is the same as targetType, then
// a. NOTE: If srcType and targetType are the same, the transfer must
// be performed in a manner that preserves the bit-level encoding of
// the source data.
// b. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8,
// true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8,
// value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
if (IsSharedArrayBuffer(target.buffer) ||
IsSharedArrayBuffer(source.buffer)) {
// SABs need a relaxed memmove to preserve atomicity.
CallCRelaxedMemmove(dstPtr, source.data_ptr, countBytes);
} else {
CallCMemmove(dstPtr, source.data_ptr, countBytes);
}
} label IfSlow deferred {
// 17. If target.[[ContentType]] is not equal to
// source.[[ContentType]], throw a TypeError exception.
if (IsBigInt64ElementsKind(srcKind) !=
IsBigInt64ElementsKind(targetElementsInfo.kind))
deferred {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
}
// All the obvervable side effects are executed, so there's nothing else
// to do with the empty source array.
if (srcLength == 0) return;
// 25. Else,
// a. Repeat, while targetByteIndex < limit
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
// srcType, true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex,
// targetType, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
CallCCopyTypedArrayElementsToTypedArray(
source, target, srcLength, targetOffset);
}
}
}