blob: b5c9dcb261df65e86d2b6053d98f16a0893f92c9 [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 utarget = typed_array::EnsureAttached(target) otherwise IsDetached;
const overloadedArg = arguments[0];
try {
// 1. Choose 22.2.3.23.2 or 22.2.3.23.1 depending on whether the
// overloadedArg has a [[TypedArrayName]] internal slot.
// If it does, the definition in 22.2.3.23.2 applies.
// If it does not, the definition in 22.2.3.23.1 applies.
const typedArray =
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
// Step 9 is not observable, do it later.
// 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
// 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
// exception.
const utypedArray =
typed_array::EnsureAttached(typedArray) otherwise IsDetached;
TypedArrayPrototypeSetTypedArray(
utarget, utypedArray, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds;
return Undefined;
} label NotTypedArray deferred {
TypedArrayPrototypeSetArray(
utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
otherwise OffsetOutOfBounds, IsDetached;
return Undefined;
}
} label OffsetOutOfBounds deferred {
ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
} label IsDetached deferred {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
}
}
// %TypedArray%.prototype.set ( array [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
transitioning macro
TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
target: JSTypedArray, arrayArg: JSAny, targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds,
IfDetached {
// Steps 9-13 are not observable, do them later.
// TODO(v8:8906): This ported behaviour is an observable spec violation and
// the comment below seems to be outdated. Consider removing this code.
try {
const _arrayArgNum = Cast<Number>(arrayArg) otherwise NotNumber;
// For number as a first argument, throw TypeError instead of silently
// ignoring the call, so that users know they did something wrong.
// (Consistent with Firefox and Blink/WebKit)
ThrowTypeError(MessageTemplate::kInvalidArgument);
} label NotNumber {
// Proceed to step 14.
}
// 14. Let src be ? ToObject(array).
const src: JSReceiver = ToObject_Inline(context, arrayArg);
// 15. Let srcLength be ? LengthOfArrayLike(src).
const srcLengthNum: Number = GetLengthProperty(src);
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 16. 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;
// 10. Let targetName be the String value of target.[[TypedArrayName]].
// 11. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
// 12. Let targetType be the Element Type value in Table 62 for
// targetName.
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)) {
const utarget = typed_array::EnsureAttached(target) otherwise IfDetached;
CallCCopyFastNumberJSArrayElementsToTypedArray(
context, fastSrc, utarget, srcLength, targetOffset);
} else {
goto IfSlow;
}
} label IfSlow deferred {
TypedArraySet(
context, target, src, srcLengthNum, Convert<Number>(targetOffset));
}
}
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
transitioning macro
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
targetOffset: uintptr,
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
// Steps 12-20 are not observable, so we can handle offset overflow
// at step 21 here.
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
// 9. Let targetLength be target.[[ArrayLength]].
const targetLength = target.length;
// 19. Let srcLength be typedArray.[[ArrayLength]].
const srcLength: uintptr = typedArray.length;
// Steps 12-20 are not observable, so we can do step 21 here.
// 21. If srcLength + targetOffset > targetLength, throw a RangeError
// exception.
CheckIntegerIndexAdditionOverflow(srcLength, targetOffset, targetLength)
otherwise IfOffsetOutOfBounds;
// 12. Let targetName be the String value of target.[[TypedArrayName]].
// 13. Let targetType be the Element Type value in Table 62 for
// targetName.
// 14. Let targetElementSize be the Element Size value specified in
// Table 62 for targetName.
const targetElementsInfo = GetTypedArrayElementsInfo(target);
// 16. Let srcName be the String value of typedArray.[[TypedArrayName]].
// 17. Let srcType be the Element Type value in Table 62 for srcName.
// 18. Let srcElementSize be the Element Size value specified in
// Table 62 for srcName.
const srcKind: ElementsKind = typedArray.elements_kind;
// const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);
// We skip steps 23-25 because both memmove and
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
// regions.
// 23. If both IsSharedArrayBuffer(srcBuffer) and
// IsSharedArrayBuffer(targetBuffer) are true, then
// 23a. If srcBuffer.[[ArrayBufferData]] and
// targetBuffer.[[ArrayBufferData]] are the same Shared Data Block
// values, let same be true; else let same be false.
// 24. Else, let same be SameValue(srcBuffer, targetBuffer).
// 25. If same is true, then
// a. Let srcByteLength be typedArray.[[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.
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.
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);
assert(countBytes <= target.byte_length - startOffset);
assert(countBytes <= typedArray.byte_length);
// 29. 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.
CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
} label IfSlow deferred {
// 22. If target.[[ContentType]] is not equal to
// typedArray.[[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;
// 30. 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(
typedArray, target, srcLength, targetOffset);
}
}
}