| // 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); |
| } |
| } |
| } |