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