[rab / gsab] RAB / GSAB support for TA.p.set
Bug: v8:11111 Change-Id: I757e67cbcad98b6cacb3ad08b6a364194feead1f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3427201 Reviewed-by: Shu-yu Guo <syg@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/main@{#78937}
This commit is contained in:
parent
efd28c14c2
commit
f733dc0f31
@ -186,8 +186,12 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
|
||||
|
||||
TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
|
||||
TNode<Int32T> kind) {
|
||||
return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
|
||||
Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
|
||||
return Word32Or(
|
||||
Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
|
||||
Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS))),
|
||||
Word32Or(
|
||||
Word32Equal(kind, Int32Constant(RAB_GSAB_UINT8_ELEMENTS)),
|
||||
Word32Equal(kind, Int32Constant(RAB_GSAB_UINT8_CLAMPED_ELEMENTS))));
|
||||
}
|
||||
|
||||
TNode<BoolT> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
|
||||
|
@ -28,10 +28,12 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<UintPtrT> CalculateExternalPointer(TNode<UintPtrT> backing_store,
|
||||
TNode<UintPtrT> byte_offset);
|
||||
|
||||
// Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS.
|
||||
// Returns true if kind is either UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS,
|
||||
// RAB_GSAB_UINT8_ELEMENTS, or RAB_GSAB_UINT8_CLAMPED_ELEMENTS.
|
||||
TNode<BoolT> IsUint8ElementsKind(TNode<Int32T> kind);
|
||||
|
||||
// Returns true if kind is either BIGINT64_ELEMENTS or BIGUINT64_ELEMENTS.
|
||||
// Returns true if kind is either BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS,
|
||||
// RAB_GSAB_BIGINT64_ELEMENTS, or RAB_GSAB_BIGUINT64_ELEMENTS.
|
||||
TNode<BoolT> IsBigInt64ElementsKind(TNode<Int32T> kind);
|
||||
|
||||
// Returns the byte size of an element for a TypedArray elements kind.
|
||||
|
@ -84,11 +84,6 @@ javascript builtin DataViewPrototypeGetBuffer(
|
||||
return dataView.buffer;
|
||||
}
|
||||
|
||||
extern macro IsJSArrayBufferViewDetachedOrOutOfBounds(JSArrayBufferView):
|
||||
never labels DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
|
||||
extern macro LoadVariableLengthJSArrayBufferViewByteLength(
|
||||
JSArrayBufferView, JSArrayBuffer): uintptr labels DetachedOrOutOfBounds;
|
||||
|
||||
// ES6 section 24.2.4.2 get DataView.prototype.byteLength
|
||||
javascript builtin DataViewPrototypeGetByteLength(
|
||||
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
|
||||
@ -118,7 +113,7 @@ javascript builtin DataViewPrototypeGetByteOffset(
|
||||
const dataView: JSDataView =
|
||||
ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset');
|
||||
try {
|
||||
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
|
||||
} label DetachedOrOutOfBounds {
|
||||
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteOffset);
|
||||
@ -399,7 +394,7 @@ transitioning macro DataViewGet(
|
||||
// 7. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
|
||||
// TypeError exception.
|
||||
try {
|
||||
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
|
||||
} label DetachedOrOutOfBounds {
|
||||
ThrowTypeError(
|
||||
@ -718,7 +713,7 @@ transitioning macro DataViewSet(
|
||||
// 10. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
|
||||
// TypeError exception.
|
||||
try {
|
||||
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
|
||||
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
|
||||
} label DetachedOrOutOfBounds {
|
||||
ThrowTypeError(
|
||||
|
@ -70,63 +70,62 @@ TypedArrayPrototypeSet(
|
||||
// 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 attachedTargetAndLength = EnsureAttachedAndReadLength(target)
|
||||
otherwise IsDetachedOrOutOfBounds;
|
||||
|
||||
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.
|
||||
// 1. Choose SetTypedArrayFromTypedArray or SetTypedArrayFromArrayLike
|
||||
// depending on whether the overloadedArg has a [[TypedArrayName]]
|
||||
// internal slot.
|
||||
const typedArray =
|
||||
Cast<JSTypedArray>(overloadedArg) otherwise NotTypedArray;
|
||||
|
||||
// Step 9 is not observable, do it later.
|
||||
// Step 3 is not observable, do it later.
|
||||
|
||||
// 10. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
|
||||
// 11. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
|
||||
// 4. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
|
||||
// 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError
|
||||
// exception.
|
||||
const utypedArray =
|
||||
typed_array::EnsureAttached(typedArray) otherwise IsDetached;
|
||||
|
||||
const attachedSourceAndLength = EnsureAttachedAndReadLength(typedArray)
|
||||
otherwise IsDetachedOrOutOfBounds;
|
||||
TypedArrayPrototypeSetTypedArray(
|
||||
utarget, utypedArray, targetOffset, targetOffsetOverflowed)
|
||||
attachedTargetAndLength, attachedSourceAndLength, targetOffset,
|
||||
targetOffsetOverflowed)
|
||||
otherwise OffsetOutOfBounds;
|
||||
return Undefined;
|
||||
} label NotTypedArray deferred {
|
||||
TypedArrayPrototypeSetArray(
|
||||
utarget, overloadedArg, targetOffset, targetOffsetOverflowed)
|
||||
otherwise OffsetOutOfBounds, IsDetached;
|
||||
target, attachedTargetAndLength.length, overloadedArg, targetOffset,
|
||||
targetOffsetOverflowed)
|
||||
otherwise OffsetOutOfBounds;
|
||||
return Undefined;
|
||||
}
|
||||
} label OffsetOutOfBounds deferred {
|
||||
ThrowRangeError(MessageTemplate::kTypedArraySetOffsetOutOfBounds);
|
||||
} label IsDetached deferred {
|
||||
} label IsDetachedOrOutOfBounds deferred {
|
||||
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameSet);
|
||||
}
|
||||
}
|
||||
|
||||
// %TypedArray%.prototype.set ( array [ , offset ] )
|
||||
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-array-offset
|
||||
// SetTypedArrayFromArrayLike
|
||||
// https://tc39.es/ecma262/#sec-settypedarrayfromarraylike
|
||||
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.
|
||||
target: JSTypedArray, targetLength: uintptr, arrayArg: JSAny,
|
||||
targetOffset: uintptr,
|
||||
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
|
||||
// Steps 3-7 are not observable, do them later.
|
||||
|
||||
// 14. Let src be ? ToObject(array).
|
||||
// 8. Let src be ? ToObject(source).
|
||||
const src: JSReceiver = ToObject_Inline(context, arrayArg);
|
||||
|
||||
// 15. Let srcLength be ? LengthOfArrayLike(src).
|
||||
// 9. Let srcLength be ? LengthOfArrayLike(src).
|
||||
const srcLengthNum: Number = GetLengthProperty(src);
|
||||
|
||||
// 10. If targetOffset is +∞, throw a RangeError exception.
|
||||
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
|
||||
|
||||
// 9. Let targetLength be target.[[ArrayLength]].
|
||||
const targetLength = target.length;
|
||||
|
||||
// 16. If srcLength + targetOffset > targetLength, throw a RangeError
|
||||
// 11. If srcLength + targetOffset > targetLength, throw a RangeError
|
||||
// exception.
|
||||
const srcLength = ChangeSafeIntegerNumberToUintPtr(srcLengthNum)
|
||||
otherwise IfOffsetOutOfBounds;
|
||||
@ -137,10 +136,10 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
|
||||
// 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
|
||||
// 4. Let targetName be the String value of target.[[TypedArrayName]].
|
||||
// 5. Let targetElementSize be the Element Size value specified in
|
||||
// Table 69 for targetName.
|
||||
// 6. Let targetType be the Element Type value in Table 69 for
|
||||
// targetName.
|
||||
|
||||
try {
|
||||
@ -161,7 +160,10 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
|
||||
IsElementsKindInRange(
|
||||
srcKind, ElementsKind::PACKED_DOUBLE_ELEMENTS,
|
||||
ElementsKind::HOLEY_DOUBLE_ELEMENTS)) {
|
||||
const utarget = typed_array::EnsureAttached(target) otherwise IfDetached;
|
||||
// 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);
|
||||
|
||||
@ -174,56 +176,56 @@ TypedArrayPrototypeSetArray(implicit context: Context, receiver: JSAny)(
|
||||
}
|
||||
}
|
||||
|
||||
// %TypedArray%.prototype.set ( typedArray [ , offset ] )
|
||||
// https://tc39.es/ecma262/#sec-%typedarray%.prototype.set-typedarray-offset
|
||||
// SetTypedArrayFromTypedArray
|
||||
// https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray
|
||||
transitioning macro
|
||||
TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
target: AttachedJSTypedArray, typedArray: AttachedJSTypedArray,
|
||||
attachedTargetAndLength: AttachedJSTypedArrayAndLength,
|
||||
attachedSourceAndLength: AttachedJSTypedArrayAndLength,
|
||||
targetOffset: uintptr,
|
||||
targetOffsetOverflowed: bool): void labels IfOffsetOutOfBounds {
|
||||
// Steps 12-20 are not observable, so we can handle offset overflow
|
||||
// at step 21 here.
|
||||
// Steps 6-14 are not observable, so we can handle offset overflow
|
||||
// at step 15 here.
|
||||
if (targetOffsetOverflowed) goto IfOffsetOutOfBounds;
|
||||
|
||||
// 9. Let targetLength be target.[[ArrayLength]].
|
||||
const targetLength = target.length;
|
||||
// 3. Let targetLength be IntegerIndexedObjectLength(target).
|
||||
const target = attachedTargetAndLength.array;
|
||||
const targetLength = attachedTargetAndLength.length;
|
||||
|
||||
// 19. Let srcLength be typedArray.[[ArrayLength]].
|
||||
const srcLength: uintptr = typedArray.length;
|
||||
// 13. Let srcLength be IntegerIndexedObjectLength(source).
|
||||
const source = attachedSourceAndLength.array;
|
||||
const srcLength = attachedSourceAndLength.length;
|
||||
|
||||
// Steps 12-20 are not observable, so we can do step 21 here.
|
||||
|
||||
// 21. If srcLength + targetOffset > targetLength, throw a RangeError
|
||||
// 16. 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
|
||||
// 6. Let targetName be the String value of target.[[TypedArrayName]].
|
||||
// 7. Let targetType be the Element Type value in Table 62 for
|
||||
// targetName.
|
||||
// 14. Let targetElementSize be the Element Size value specified in
|
||||
// 8. 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
|
||||
// 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 = typedArray.elements_kind;
|
||||
// const srcElementsInfo = GetTypedArrayElementsInfo(typedArray);
|
||||
const srcKind: ElementsKind = source.elements_kind;
|
||||
|
||||
// We skip steps 23-25 because both memmove and
|
||||
// We skip steps 18-20 because both memmove and
|
||||
// CopyTypedArrayElementsToTypedArray() properly handle overlapping
|
||||
// regions.
|
||||
|
||||
// 23. If both IsSharedArrayBuffer(srcBuffer) and
|
||||
// 18. If both IsSharedArrayBuffer(srcBuffer) and
|
||||
// IsSharedArrayBuffer(targetBuffer) are true, then
|
||||
// 23a. If srcBuffer.[[ArrayBufferData]] and
|
||||
// a. 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]].
|
||||
// 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
|
||||
@ -232,6 +234,8 @@ TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
|
||||
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) ||
|
||||
@ -255,10 +259,19 @@ TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
otherwise unreachable;
|
||||
const dstPtr: RawPtr = target.data_ptr + Convert<intptr>(startOffset);
|
||||
|
||||
dcheck(countBytes <= target.byte_length - startOffset);
|
||||
dcheck(countBytes <= typedArray.byte_length);
|
||||
// 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;
|
||||
|
||||
// 29. If srcType is the same as targetType, then
|
||||
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.
|
||||
@ -271,13 +284,13 @@ TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
// iv. Set targetByteIndex to targetByteIndex + 1.
|
||||
if (IsSharedArrayBuffer(target.buffer)) {
|
||||
// SABs need a relaxed memmove to preserve atomicity.
|
||||
CallCRelaxedMemmove(dstPtr, typedArray.data_ptr, countBytes);
|
||||
CallCRelaxedMemmove(dstPtr, source.data_ptr, countBytes);
|
||||
} else {
|
||||
CallCMemmove(dstPtr, typedArray.data_ptr, countBytes);
|
||||
CallCMemmove(dstPtr, source.data_ptr, countBytes);
|
||||
}
|
||||
} label IfSlow deferred {
|
||||
// 22. If target.[[ContentType]] is not equal to
|
||||
// typedArray.[[ContentType]], throw a TypeError exception.
|
||||
// 17. If target.[[ContentType]] is not equal to
|
||||
// source.[[ContentType]], throw a TypeError exception.
|
||||
if (IsBigInt64ElementsKind(srcKind) !=
|
||||
IsBigInt64ElementsKind(targetElementsInfo.kind))
|
||||
deferred {
|
||||
@ -288,7 +301,7 @@ TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
// to do with the empty source array.
|
||||
if (srcLength == 0) return;
|
||||
|
||||
// 30. Else,
|
||||
// 25. Else,
|
||||
// a. Repeat, while targetByteIndex < limit
|
||||
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex,
|
||||
// srcType, true, Unordered).
|
||||
@ -297,7 +310,7 @@ TypedArrayPrototypeSetTypedArray(implicit context: Context, receiver: JSAny)(
|
||||
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
|
||||
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
|
||||
CallCCopyTypedArrayElementsToTypedArray(
|
||||
typedArray, target, srcLength, targetOffset);
|
||||
source, target, srcLength, targetOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3611,6 +3611,7 @@ class TypedElementsAccessor
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(v8:11111): Update this once we have external RAB / GSAB array types.
|
||||
static bool HasSimpleRepresentation(ExternalArrayType type) {
|
||||
return !(type == kExternalFloat32Array || type == kExternalFloat64Array ||
|
||||
type == kExternalUint8ClampedArray);
|
||||
@ -3642,9 +3643,9 @@ class TypedElementsAccessor
|
||||
CHECK(!source.WasDetached());
|
||||
CHECK(!destination.WasDetached());
|
||||
|
||||
DCHECK_LE(offset, destination.length());
|
||||
DCHECK_LE(length, destination.length() - offset);
|
||||
DCHECK_LE(length, source.length());
|
||||
DCHECK_LE(offset, destination.GetLength());
|
||||
DCHECK_LE(length, destination.GetLength() - offset);
|
||||
DCHECK_LE(length, source.GetLength());
|
||||
|
||||
ExternalArrayType source_type = source.type();
|
||||
ExternalArrayType destination_type = destination.type();
|
||||
@ -3705,6 +3706,7 @@ class TypedElementsAccessor
|
||||
source_shared || destination_shared ? kShared : kUnshared); \
|
||||
break;
|
||||
TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
@ -3739,12 +3741,15 @@ class TypedElementsAccessor
|
||||
static bool TryCopyElementsFastNumber(Context context, JSArray source,
|
||||
JSTypedArray destination, size_t length,
|
||||
size_t offset) {
|
||||
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) return false;
|
||||
if (IsBigIntTypedArrayElementsKind(Kind)) return false;
|
||||
Isolate* isolate = source.GetIsolate();
|
||||
DisallowGarbageCollection no_gc;
|
||||
DisallowJavascriptExecution no_js(isolate);
|
||||
|
||||
CHECK(!destination.WasDetached());
|
||||
bool out_of_bounds = false;
|
||||
CHECK(destination.GetLengthOrOutOfBounds(out_of_bounds) >= length);
|
||||
CHECK(!out_of_bounds);
|
||||
|
||||
size_t current_length;
|
||||
DCHECK(source.length().IsNumber() &&
|
||||
@ -3752,7 +3757,7 @@ class TypedElementsAccessor
|
||||
length <= current_length);
|
||||
USE(current_length);
|
||||
|
||||
size_t dest_length = destination.length();
|
||||
size_t dest_length = destination.GetLength();
|
||||
DCHECK(length + offset <= dest_length);
|
||||
USE(dest_length);
|
||||
|
||||
@ -3830,15 +3835,16 @@ class TypedElementsAccessor
|
||||
LookupIterator it(isolate, source, i);
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
|
||||
Object::GetProperty(&it));
|
||||
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
|
||||
if (IsBigIntTypedArrayElementsKind(Kind)) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
|
||||
BigInt::FromObject(isolate, elem));
|
||||
} else {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
|
||||
Object::ToNumber(isolate, elem));
|
||||
}
|
||||
|
||||
if (V8_UNLIKELY(destination->WasDetached())) {
|
||||
bool out_of_bounds = false;
|
||||
size_t new_length = destination->GetLengthOrOutOfBounds(out_of_bounds);
|
||||
if (V8_UNLIKELY(out_of_bounds || destination->WasDetached())) {
|
||||
const char* op = "set";
|
||||
const MessageTemplate message = MessageTemplate::kDetachedOperation;
|
||||
Handle<String> operation =
|
||||
@ -3846,8 +3852,14 @@ class TypedElementsAccessor
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
|
||||
NewTypeError(message, operation));
|
||||
}
|
||||
// The spec says we store the length, then get each element, so we don't
|
||||
// need to check changes to length.
|
||||
if (V8_UNLIKELY(new_length <= offset + i)) {
|
||||
// Proceed with the loop so that we call get getters for the source even
|
||||
// though we don't set the values in the target.
|
||||
// TODO(v8:11111): Maybe change this, depending on how
|
||||
// https://github.com/tc39/proposal-resizablearraybuffer/issues/86 is
|
||||
// resolved.
|
||||
continue;
|
||||
}
|
||||
SetImpl(destination, InternalIndex(offset + i), *elem);
|
||||
}
|
||||
return *isolate->factory()->undefined_value();
|
||||
@ -3860,15 +3872,24 @@ class TypedElementsAccessor
|
||||
Handle<JSObject> destination,
|
||||
size_t length, size_t offset) {
|
||||
Isolate* isolate = destination->GetIsolate();
|
||||
if (length == 0) return *isolate->factory()->undefined_value();
|
||||
|
||||
Handle<JSTypedArray> destination_ta =
|
||||
Handle<JSTypedArray>::cast(destination);
|
||||
DCHECK_LE(offset + length, destination_ta->length());
|
||||
|
||||
if (length == 0) return *isolate->factory()->undefined_value();
|
||||
|
||||
// All conversions from TypedArrays can be done without allocation.
|
||||
if (source->IsJSTypedArray()) {
|
||||
// TODO(v8:11111): Add RAB/GSAB support.
|
||||
DCHECK(!destination_ta->is_length_tracking());
|
||||
DCHECK(!destination_ta->is_backed_by_rab());
|
||||
DCHECK(!Handle<JSTypedArray>::cast(source)->is_length_tracking());
|
||||
DCHECK(!Handle<JSTypedArray>::cast(source)->is_backed_by_rab());
|
||||
|
||||
CHECK(!destination_ta->WasDetached());
|
||||
bool out_of_bounds = false;
|
||||
CHECK_LE(offset + length,
|
||||
destination_ta->GetLengthOrOutOfBounds(out_of_bounds));
|
||||
CHECK(!out_of_bounds);
|
||||
Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
|
||||
ElementsKind source_kind = source_ta->GetElementsKind();
|
||||
bool source_is_bigint =
|
||||
@ -3884,6 +3905,10 @@ class TypedElementsAccessor
|
||||
}
|
||||
} else if (source->IsJSArray()) {
|
||||
CHECK(!destination_ta->WasDetached());
|
||||
bool out_of_bounds = false;
|
||||
CHECK_LE(offset + length,
|
||||
destination_ta->GetLengthOrOutOfBounds(out_of_bounds));
|
||||
CHECK(!out_of_bounds);
|
||||
// Fast cases for packed numbers kinds where we don't need to allocate.
|
||||
Handle<JSArray> source_js_array = Handle<JSArray>::cast(source);
|
||||
size_t current_length;
|
||||
@ -3898,7 +3923,8 @@ class TypedElementsAccessor
|
||||
}
|
||||
}
|
||||
// Final generic case that handles prototype chain lookups, getters, proxies
|
||||
// and observable side effects via valueOf, etc.
|
||||
// and observable side effects via valueOf, etc. In this case, it's possible
|
||||
// that the length getter detached / resized the underlying buffer.
|
||||
return CopyElementsHandleSlow(source, destination_ta, length, offset);
|
||||
}
|
||||
};
|
||||
@ -5209,6 +5235,7 @@ void CopyFastNumberJSArrayElementsToTypedArray(Address raw_context,
|
||||
context, source, destination, length, offset)); \
|
||||
break;
|
||||
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
|
||||
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAYS_CASE)
|
||||
#undef TYPED_ARRAYS_CASE
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -5228,6 +5255,7 @@ void CopyTypedArrayElementsToTypedArray(Address raw_source,
|
||||
length, offset); \
|
||||
break;
|
||||
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
|
||||
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAYS_CASE)
|
||||
#undef TYPED_ARRAYS_CASE
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -73,6 +73,20 @@ macro IsLengthTrackingJSArrayBufferView(array: JSArrayBufferView): bool {
|
||||
return array.bit_field.is_length_tracking;
|
||||
}
|
||||
|
||||
extern macro LoadVariableLengthJSArrayBufferViewByteLength(
|
||||
JSArrayBufferView, JSArrayBuffer): uintptr labels DetachedOrOutOfBounds;
|
||||
|
||||
@export
|
||||
macro LoadJSArrayBufferViewByteLength(
|
||||
view: JSArrayBufferView, buffer: JSArrayBuffer):
|
||||
uintptr labels DetachedOrOutOfBounds {
|
||||
if (IsVariableLengthJSArrayBufferView(view)) {
|
||||
return LoadVariableLengthJSArrayBufferViewByteLength(view, buffer)
|
||||
otherwise DetachedOrOutOfBounds;
|
||||
}
|
||||
return view.byte_length;
|
||||
}
|
||||
|
||||
extern class JSTypedArray extends JSArrayBufferView {
|
||||
length: uintptr;
|
||||
// A SandboxedPtr if the sandbox is enabled
|
||||
|
@ -67,6 +67,7 @@ class BuildFlags : public ContextualClass<BuildFlags> {
|
||||
#else
|
||||
build_flags_["V8_ENABLE_WEBASSEMBLY"] = false;
|
||||
#endif
|
||||
build_flags_["DEBUG"] = DEBUG_BOOL;
|
||||
}
|
||||
static bool GetFlag(const std::string& name, const char* production) {
|
||||
auto it = Get().build_flags_.find(name);
|
||||
|
@ -2859,3 +2859,317 @@ function TestIterationAndGrow(ta, expected, gsab, grow_after,
|
||||
assertEquals([10, 8, 6, 4, 0, 2], ToNumbers(wholeArrayView));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetWithGrowableTarget() {
|
||||
for (let ctor of ctors) {
|
||||
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
const fixedLength = new ctor(gsab, 0, 4);
|
||||
const fixedLengthWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
const lengthTracking = new ctor(gsab, 0);
|
||||
const lengthTrackingWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Write some data into the array.
|
||||
const taFull = new ctor(gsab);
|
||||
|
||||
// Orig. array: [0, 0, 0, 0]
|
||||
// [0, 0, 0, 0] << fixedLength
|
||||
// [0, 0] << fixedLengthWithOffset
|
||||
// [0, 0, 0, 0, ...] << lengthTracking
|
||||
// [0, 0, ...] << lengthTrackingWithOffset
|
||||
|
||||
SetHelper(fixedLength, [1, 2]);
|
||||
assertEquals([1, 2, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLength, [3, 4], 1);
|
||||
assertEquals([1, 3, 4, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0, 0])}, RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0], 1)}, RangeError);
|
||||
assertEquals([1, 3, 4, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(fixedLengthWithOffset, [5, 6]);
|
||||
assertEquals([1, 3, 5, 6], ToNumbers(taFull));
|
||||
SetHelper(fixedLengthWithOffset, [7], 1);
|
||||
assertEquals([1, 3, 5, 7], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([1, 3, 5, 7], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [8, 9]);
|
||||
assertEquals([8, 9, 5, 7], ToNumbers(taFull));
|
||||
SetHelper(lengthTracking, [10, 11], 1);
|
||||
assertEquals([8, 10, 11, 7], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([8, 10, 11, 7], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTrackingWithOffset, [12, 13]);
|
||||
assertEquals([8, 10, 12, 13], ToNumbers(taFull));
|
||||
SetHelper(lengthTrackingWithOffset, [14], 1);
|
||||
assertEquals([8, 10, 12, 14], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([8, 10, 12, 14], ToNumbers(taFull));
|
||||
|
||||
// Grow.
|
||||
gsab.grow(6 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Orig. array: [8, 10, 12, 14, 0, 0]
|
||||
// [8, 10, 12, 14] << fixedLength
|
||||
// [12, 14] << fixedLengthWithOffset
|
||||
// [8, 10, 12, 14, 0, 0, ...] << lengthTracking
|
||||
// [12, 14, 0, 0, ...] << lengthTrackingWithOffset
|
||||
SetHelper(fixedLength, [21, 22]);
|
||||
assertEquals([21, 22, 12, 14, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLength, [23, 24], 1);
|
||||
assertEquals([21, 23, 24, 14, 0, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0, 0])}, RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0], 1)}, RangeError);
|
||||
assertEquals([21, 23, 24, 14, 0, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(fixedLengthWithOffset, [25, 26]);
|
||||
assertEquals([21, 23, 25, 26, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLengthWithOffset, [27], 1);
|
||||
assertEquals([21, 23, 25, 27, 0, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([21, 23, 25, 27, 0, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [28, 29, 30, 31, 32, 33]);
|
||||
assertEquals([28, 29, 30, 31, 32, 33], ToNumbers(taFull));
|
||||
SetHelper(lengthTracking, [34, 35, 36, 37, 38], 1);
|
||||
assertEquals([28, 34, 35, 36, 37, 38], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([28, 34, 35, 36, 37, 38], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTrackingWithOffset, [39, 40, 41, 42]);
|
||||
assertEquals([28, 34, 39, 40, 41, 42], ToNumbers(taFull));
|
||||
SetHelper(lengthTrackingWithOffset, [43, 44, 45], 1);
|
||||
assertEquals([28, 34, 39, 43, 44, 45], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([28, 34, 39, 43, 44, 45], ToNumbers(taFull));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetSourceLengthGetterGrowsTarget() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateGsabForTest(ctor) {
|
||||
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(gsab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return gsab;
|
||||
}
|
||||
|
||||
let gsab;
|
||||
let growTo;
|
||||
function CreateSourceProxy(length) {
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
gsab.grow(growTo);
|
||||
return length;
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Test that we still throw for lengthTracking TAs if the source length is
|
||||
// too large, even though we resized in the length getter (we check against
|
||||
// the original length).
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const lengthTracking = new ctor(gsab, 0);
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTracking.set(CreateSourceProxy(6)); },
|
||||
RangeError);
|
||||
assertEquals([0, 2, 4, 6, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(6)); },
|
||||
RangeError);
|
||||
assertEquals([0, 2, 4, 6, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetGrowTargetMidIteration() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateGsabForTest(ctor) {
|
||||
const gsab = CreateGrowableSharedArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(gsab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return gsab;
|
||||
}
|
||||
|
||||
let gsab;
|
||||
// Growing will happen when we're calling Get for the `growAt`:th data
|
||||
// element, but we haven't yet written it to the target.
|
||||
let growAt;
|
||||
let growTo;
|
||||
function CreateSourceProxy(length) {
|
||||
let requestedIndices = [];
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
return length;
|
||||
}
|
||||
requestedIndices.push(prop);
|
||||
if (requestedIndices.length == growAt) {
|
||||
gsab.grow(growTo);
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const fixedLength = new ctor(gsab, 0, 4);
|
||||
growAt = 2;
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLength.set(CreateSourceProxy(4));
|
||||
assertEquals([1, 1, 1, 1], ToNumbers(fixedLength));
|
||||
assertEquals([1, 1, 1, 1, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
growAt = 1;
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLengthWithOffset.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1], ToNumbers(fixedLengthWithOffset));
|
||||
assertEquals([0, 2, 1, 1, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const lengthTracking = new ctor(gsab, 0);
|
||||
growAt = 2;
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTracking.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1, 4, 6, 0, 0], ToNumbers(lengthTracking));
|
||||
assertEquals([1, 1, 4, 6, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
gsab = CreateGsabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(gsab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
growAt = 1;
|
||||
growTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1, 0, 0], ToNumbers(lengthTrackingWithOffset));
|
||||
assertEquals([0, 2, 1, 1, 0, 0], ToNumbers(new ctor(gsab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetWithGrowableSource() {
|
||||
for (let targetIsGrowable of [false, true]) {
|
||||
for (let targetCtor of ctors) {
|
||||
for (let sourceCtor of ctors) {
|
||||
const gsab = CreateGrowableSharedArrayBuffer(
|
||||
4 * sourceCtor.BYTES_PER_ELEMENT,
|
||||
8 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
const fixedLength = new sourceCtor(gsab, 0, 4);
|
||||
const fixedLengthWithOffset = new sourceCtor(
|
||||
gsab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
|
||||
const lengthTracking = new sourceCtor(gsab, 0);
|
||||
const lengthTrackingWithOffset = new sourceCtor(
|
||||
gsab, 2 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Write some data into the array.
|
||||
const taFull = new sourceCtor(gsab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taFull, i, i + 1);
|
||||
}
|
||||
|
||||
// Orig. array: [1, 2, 3, 4]
|
||||
// [1, 2, 3, 4] << fixedLength
|
||||
// [3, 4] << fixedLengthWithOffset
|
||||
// [1, 2, 3, 4, ...] << lengthTracking
|
||||
// [3, 4, ...] << lengthTrackingWithOffset
|
||||
|
||||
const targetAb = targetIsGrowable ?
|
||||
new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT) :
|
||||
new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT,
|
||||
{maxByteLength: 8 * targetCtor.BYTES_PER_ELEMENT});
|
||||
const target = new targetCtor(targetAb);
|
||||
|
||||
if (IsBigIntTypedArray(target) != IsBigIntTypedArray(taFull)) {
|
||||
// Can't mix BigInt and non-BigInt types.
|
||||
continue;
|
||||
}
|
||||
|
||||
SetHelper(target, fixedLength);
|
||||
assertEquals([1, 2, 3, 4, 0, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, fixedLengthWithOffset);
|
||||
assertEquals([3, 4, 3, 4, 0, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTracking, 1);
|
||||
assertEquals([3, 1, 2, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTrackingWithOffset, 1);
|
||||
assertEquals([3, 3, 4, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
// Grow.
|
||||
gsab.grow(6 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
WriteToTypedArray(taFull, i, i + 1);
|
||||
}
|
||||
|
||||
// Orig. array: [1, 2, 3, 4, 5, 6]
|
||||
// [1, 2, 3, 4] << fixedLength
|
||||
// [3, 4] << fixedLengthWithOffset
|
||||
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
|
||||
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
|
||||
|
||||
SetHelper(target, fixedLength);
|
||||
assertEquals([1, 2, 3, 4, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, fixedLengthWithOffset);
|
||||
assertEquals([3, 4, 3, 4, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTracking, 0);
|
||||
assertEquals([1, 2, 3, 4, 5, 6], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTrackingWithOffset, 1);
|
||||
assertEquals([1, 3, 4, 5, 6, 6], ToNumbers(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -176,6 +176,20 @@ function LastIndexOfHelper(array, n, fromIndex) {
|
||||
return array.lastIndexOf(n, fromIndex);
|
||||
}
|
||||
|
||||
function SetHelper(target, source, offset) {
|
||||
if (target instanceof BigInt64Array || target instanceof BigUint64Array) {
|
||||
const bigIntSource = [];
|
||||
for (s of source) {
|
||||
bigIntSource.push(BigInt(s));
|
||||
}
|
||||
source = bigIntSource;
|
||||
}
|
||||
if (offset == undefined) {
|
||||
return target.set(source);
|
||||
}
|
||||
return target.set(source, offset);
|
||||
}
|
||||
|
||||
function testDataViewMethodsUpToSize(view, bufferSize) {
|
||||
for (const [getter, setter, size, isBigInt] of dataViewAccessorsAndSizes) {
|
||||
for (let i = 0; i <= bufferSize - size; ++i) {
|
||||
|
@ -1125,3 +1125,156 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
|
||||
Helper(lengthTracking));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetSourceLengthGetterDetachesTarget() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
function CreateSourceProxy(length) {
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
%ArrayBufferDetach(rab);
|
||||
return length;
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Tests where the length getter returns a non-zero value -> these throw.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
assertThrows(() => { fixedLength.set(CreateSourceProxy(1)); }, TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(1)); },
|
||||
TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
assertThrows(() => { lengthTracking.set(CreateSourceProxy(1)); },
|
||||
TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(1)); },
|
||||
TypeError);
|
||||
}
|
||||
|
||||
// Tests where the length getter returns a zero -> these don't throw.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
fixedLength.set(CreateSourceProxy(0));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
fixedLengthWithOffset.set(CreateSourceProxy(0));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
lengthTracking.set(CreateSourceProxy(0));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(0));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetDetachTargetMidIteration() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
// Detaching will happen when we're calling Get for the `detachAt`:th data
|
||||
// element, but we haven't yet written it to the target.
|
||||
let detachAt;
|
||||
function CreateSourceProxy(length) {
|
||||
let requestedIndices = [];
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
return length;
|
||||
}
|
||||
requestedIndices.push(prop);
|
||||
if (requestedIndices.length == detachAt) {
|
||||
%ArrayBufferDetach(rab);
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
detachAt = 2;
|
||||
assertThrows(() => { fixedLength.set(CreateSourceProxy(4)); }, TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
detachAt = 2;
|
||||
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(2)); },
|
||||
TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
detachAt = 2;
|
||||
assertThrows(() => { lengthTracking.set(CreateSourceProxy(2)); },
|
||||
TypeError);
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
detachAt = 2;
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(2)); },
|
||||
TypeError);
|
||||
}
|
||||
})();
|
||||
|
@ -5173,3 +5173,628 @@ function TestIterationAndResize(ta, expected, rab, resize_after,
|
||||
assertEquals([10, 8, 6, 4, 0, 2], ToNumbers(wholeArrayView));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetWithResizableTarget() {
|
||||
for (let ctor of ctors) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
const taFull = new ctor(rab);
|
||||
|
||||
// Orig. array: [0, 0, 0, 0]
|
||||
// [0, 0, 0, 0] << fixedLength
|
||||
// [0, 0] << fixedLengthWithOffset
|
||||
// [0, 0, 0, 0, ...] << lengthTracking
|
||||
// [0, 0, ...] << lengthTrackingWithOffset
|
||||
|
||||
// For making sure we're not calling the source length or element getters
|
||||
// if the target is OOB.
|
||||
const throwingProxy = new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
throw new Error('Called getter for ' + prop);
|
||||
}});
|
||||
|
||||
SetHelper(fixedLength, [1, 2]);
|
||||
assertEquals([1, 2, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLength, [3, 4], 1);
|
||||
assertEquals([1, 3, 4, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([1, 3, 4, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(fixedLengthWithOffset, [5, 6]);
|
||||
assertEquals([1, 3, 5, 6], ToNumbers(taFull));
|
||||
SetHelper(fixedLengthWithOffset, [7], 1);
|
||||
assertEquals([1, 3, 5, 7], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([1, 3, 5, 7], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [8, 9]);
|
||||
assertEquals([8, 9, 5, 7], ToNumbers(taFull));
|
||||
SetHelper(lengthTracking, [10, 11], 1);
|
||||
assertEquals([8, 10, 11, 7], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([8, 10, 11, 7], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTrackingWithOffset, [12, 13]);
|
||||
assertEquals([8, 10, 12, 13], ToNumbers(taFull));
|
||||
SetHelper(lengthTrackingWithOffset, [14], 1);
|
||||
assertEquals([8, 10, 12, 14], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0])});
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0], 1)});
|
||||
assertEquals([8, 10, 12, 14], ToNumbers(taFull));
|
||||
|
||||
// Shrink so that fixed length TAs go out of bounds.
|
||||
rab.resize(3 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Orig. array: [8, 10, 12]
|
||||
// [8, 10, 12, ...] << lengthTracking
|
||||
// [12, ...] << lengthTrackingWithOffset
|
||||
|
||||
assertThrows(() => { SetHelper(fixedLength, throwingProxy)}, TypeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, throwingProxy)},
|
||||
TypeError);
|
||||
assertEquals([8, 10, 12], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [15, 16]);
|
||||
assertEquals([15, 16, 12], ToNumbers(taFull));
|
||||
SetHelper(lengthTracking, [17, 18], 1);
|
||||
assertEquals([15, 17, 18], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0])}, RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0], 1)}, RangeError);
|
||||
assertEquals([15, 17, 18], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTrackingWithOffset, [19]);
|
||||
assertEquals([15, 17, 19], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0], 1)},
|
||||
RangeError);
|
||||
assertEquals([15, 17, 19], ToNumbers(taFull));
|
||||
|
||||
// Shrink so that the TAs with offset go out of bounds.
|
||||
rab.resize(1 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
assertThrows(() => { SetHelper(fixedLength, throwingProxy)}, TypeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, throwingProxy)},
|
||||
TypeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, throwingProxy)},
|
||||
TypeError);
|
||||
assertEquals([15], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [20]);
|
||||
assertEquals([20], ToNumbers(taFull));
|
||||
|
||||
// Shrink to zero.
|
||||
rab.resize(0);
|
||||
|
||||
assertThrows(() => { SetHelper(fixedLength, throwingProxy)}, TypeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, throwingProxy)},
|
||||
TypeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, throwingProxy)},
|
||||
TypeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0])}, RangeError);
|
||||
assertEquals([], ToNumbers(taFull));
|
||||
|
||||
// Grow so that all TAs are back in-bounds.
|
||||
rab.resize(6 * ctor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Orig. array: [0, 0, 0, 0, 0, 0]
|
||||
// [0, 0, 0, 0] << fixedLength
|
||||
// [0, 0] << fixedLengthWithOffset
|
||||
// [0, 0, 0, 0, 0, 0, ...] << lengthTracking
|
||||
// [0, 0, 0, 0, ...] << lengthTrackingWithOffset
|
||||
SetHelper(fixedLength, [21, 22]);
|
||||
assertEquals([21, 22, 0, 0, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLength, [23, 24], 1);
|
||||
assertEquals([21, 23, 24, 0, 0, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0, 0])}, RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLength, [0, 0, 0, 0], 1)}, RangeError);
|
||||
assertEquals([21, 23, 24, 0, 0, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(fixedLengthWithOffset, [25, 26]);
|
||||
assertEquals([21, 23, 25, 26, 0, 0], ToNumbers(taFull));
|
||||
SetHelper(fixedLengthWithOffset, [27], 1);
|
||||
assertEquals([21, 23, 25, 27, 0, 0], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(fixedLengthWithOffset, [0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([21, 23, 25, 27, 0, 0], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTracking, [28, 29, 30, 31, 32, 33]);
|
||||
assertEquals([28, 29, 30, 31, 32, 33], ToNumbers(taFull));
|
||||
SetHelper(lengthTracking, [34, 35, 36, 37, 38], 1);
|
||||
assertEquals([28, 34, 35, 36, 37, 38], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTracking, [0, 0, 0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([28, 34, 35, 36, 37, 38], ToNumbers(taFull));
|
||||
|
||||
SetHelper(lengthTrackingWithOffset, [39, 40, 41, 42]);
|
||||
assertEquals([28, 34, 39, 40, 41, 42], ToNumbers(taFull));
|
||||
SetHelper(lengthTrackingWithOffset, [43, 44, 45], 1);
|
||||
assertEquals([28, 34, 39, 43, 44, 45], ToNumbers(taFull));
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0, 0, 0])},
|
||||
RangeError);
|
||||
assertThrows(() => { SetHelper(lengthTrackingWithOffset, [0, 0, 0, 0], 1)},
|
||||
RangeError);
|
||||
assertEquals([28, 34, 39, 43, 44, 45], ToNumbers(taFull));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetSourceLengthGetterShrinksTarget() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
let resizeTo;
|
||||
function CreateSourceProxy(length) {
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
rab.resize(resizeTo);
|
||||
return length;
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Tests where the length getter returns a non-zero value -> these throw if
|
||||
// the TA went OOB.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { fixedLength.set(CreateSourceProxy(1)); }, TypeError);
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(1)); },
|
||||
TypeError);
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTracking.set(CreateSourceProxy(1));
|
||||
assertEquals([1, 2, 4], ToNumbers(lengthTracking));
|
||||
assertEquals([1, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(1));
|
||||
assertEquals([1], ToNumbers(lengthTrackingWithOffset));
|
||||
assertEquals([0, 2, 1], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
// Length-tracking TA goes OOB because of the offset.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(1)); },
|
||||
TypeError);
|
||||
assertEquals([0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
// Tests where the length getter returns a zero -> these don't throw even if
|
||||
// the TA went OOB.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLength.set(CreateSourceProxy(0));
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLengthWithOffset.set(CreateSourceProxy(0));
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTracking.set(CreateSourceProxy(0));
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(0));
|
||||
assertEquals([0, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
// Length-tracking TA goes OOB because of the offset.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(0));
|
||||
assertEquals([0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetSourceLengthGetterGrowsTarget() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
let resizeTo;
|
||||
function CreateSourceProxy(length) {
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
rab.resize(resizeTo);
|
||||
return length;
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Test that we still throw for lengthTracking TAs if the source length is
|
||||
// too large, even though we resized in the length getter (we check against
|
||||
// the original length).
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTracking.set(CreateSourceProxy(6)); },
|
||||
RangeError);
|
||||
assertEquals([0, 2, 4, 6, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(4)); },
|
||||
RangeError);
|
||||
assertEquals([0, 2, 4, 6, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetShrinkTargetMidIteration() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
// Resizing will happen when we're calling Get for the `resizeAt`:th data
|
||||
// element, but we haven't yet written it to the target.
|
||||
let resizeAt;
|
||||
let resizeTo;
|
||||
function CreateSourceProxy(length) {
|
||||
let requestedIndices = [];
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
return length;
|
||||
}
|
||||
requestedIndices.push(prop);
|
||||
if (requestedIndices.length == resizeAt) {
|
||||
rab.resize(resizeTo);
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
resizeAt = 2;
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { fixedLength.set(CreateSourceProxy(4)); }, TypeError);
|
||||
assertEquals([1, 2, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
resizeAt = 2;
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(2)); },
|
||||
TypeError);
|
||||
assertEquals([0, 2, 1], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
resizeAt = 2;
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTracking.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1, 4], ToNumbers(lengthTracking));
|
||||
assertEquals([1, 1, 4], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeAt = 2;
|
||||
resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(2));
|
||||
assertEquals([1], ToNumbers(lengthTrackingWithOffset));
|
||||
assertEquals([0, 2, 1], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
// Length-tracking TA goes OOB because of the offset.
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeAt = 1;
|
||||
resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
|
||||
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(2)); },
|
||||
TypeError);
|
||||
assertEquals([0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetGrowTargetMidIteration() {
|
||||
// Orig. array: [0, 2, 4, 6]
|
||||
// [0, 2, 4, 6] << fixedLength
|
||||
// [4, 6] << fixedLengthWithOffset
|
||||
// [0, 2, 4, 6, ...] << lengthTracking
|
||||
// [4, 6, ...] << lengthTrackingWithOffset
|
||||
function CreateRabForTest(ctor) {
|
||||
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
|
||||
8 * ctor.BYTES_PER_ELEMENT);
|
||||
// Write some data into the array.
|
||||
const taWrite = new ctor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taWrite, i, 2 * i);
|
||||
}
|
||||
return rab;
|
||||
}
|
||||
|
||||
let rab;
|
||||
// Resizing will happen when we're calling Get for the `resizeAt`:th data
|
||||
// element, but we haven't yet written it to the target.
|
||||
let resizeAt;
|
||||
let resizeTo;
|
||||
function CreateSourceProxy(length) {
|
||||
let requestedIndices = [];
|
||||
return new Proxy({}, {
|
||||
get(target, prop, receiver) {
|
||||
if (prop == 'length') {
|
||||
return length;
|
||||
}
|
||||
requestedIndices.push(prop);
|
||||
if (requestedIndices.length == resizeAt) {
|
||||
rab.resize(resizeTo);
|
||||
}
|
||||
return true; // Can be converted to both BigInt and Number.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLength = new ctor(rab, 0, 4);
|
||||
resizeAt = 2;
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLength.set(CreateSourceProxy(4));
|
||||
assertEquals([1, 1, 1, 1], ToNumbers(fixedLength));
|
||||
assertEquals([1, 1, 1, 1, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
|
||||
resizeAt = 1;
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
fixedLengthWithOffset.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1], ToNumbers(fixedLengthWithOffset));
|
||||
assertEquals([0, 2, 1, 1, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTracking = new ctor(rab, 0);
|
||||
resizeAt = 2;
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTracking.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1, 4, 6, 0, 0], ToNumbers(lengthTracking));
|
||||
assertEquals([1, 1, 4, 6, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
|
||||
for (let ctor of ctors) {
|
||||
rab = CreateRabForTest(ctor);
|
||||
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
|
||||
resizeAt = 1;
|
||||
resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
|
||||
lengthTrackingWithOffset.set(CreateSourceProxy(2));
|
||||
assertEquals([1, 1, 0, 0], ToNumbers(lengthTrackingWithOffset));
|
||||
assertEquals([0, 2, 1, 1, 0, 0], ToNumbers(new ctor(rab)));
|
||||
}
|
||||
})();
|
||||
|
||||
(function SetWithResizableSource() {
|
||||
for (let targetIsResizable of [false, true]) {
|
||||
for (let targetCtor of ctors) {
|
||||
for (let sourceCtor of ctors) {
|
||||
const rab = CreateResizableArrayBuffer(
|
||||
4 * sourceCtor.BYTES_PER_ELEMENT,
|
||||
8 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
const fixedLength = new sourceCtor(rab, 0, 4);
|
||||
const fixedLengthWithOffset = new sourceCtor(
|
||||
rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
|
||||
const lengthTracking = new sourceCtor(rab, 0);
|
||||
const lengthTrackingWithOffset = new sourceCtor(
|
||||
rab, 2 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Write some data into the array.
|
||||
const taFull = new sourceCtor(rab);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
WriteToTypedArray(taFull, i, i + 1);
|
||||
}
|
||||
|
||||
// Orig. array: [1, 2, 3, 4]
|
||||
// [1, 2, 3, 4] << fixedLength
|
||||
// [3, 4] << fixedLengthWithOffset
|
||||
// [1, 2, 3, 4, ...] << lengthTracking
|
||||
// [3, 4, ...] << lengthTrackingWithOffset
|
||||
|
||||
const targetAb = targetIsResizable ?
|
||||
new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT) :
|
||||
new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT,
|
||||
{maxByteLength: 8 * targetCtor.BYTES_PER_ELEMENT});
|
||||
const target = new targetCtor(targetAb);
|
||||
|
||||
if (IsBigIntTypedArray(target) != IsBigIntTypedArray(taFull)) {
|
||||
// Can't mix BigInt and non-BigInt types.
|
||||
continue;
|
||||
}
|
||||
|
||||
SetHelper(target, fixedLength);
|
||||
assertEquals([1, 2, 3, 4, 0, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, fixedLengthWithOffset);
|
||||
assertEquals([3, 4, 3, 4, 0, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTracking, 1);
|
||||
assertEquals([3, 1, 2, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTrackingWithOffset, 1);
|
||||
assertEquals([3, 3, 4, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
// Shrink so that fixed length TAs go out of bounds.
|
||||
rab.resize(3 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
// Orig. array: [1, 2, 3]
|
||||
// [1, 2, 3, ...] << lengthTracking
|
||||
// [3, ...] << lengthTrackingWithOffset
|
||||
|
||||
assertThrows(() => { SetHelper(target, fixedLength)}, TypeError);
|
||||
assertThrows(() => { SetHelper(target, fixedLengthWithOffset)},
|
||||
TypeError);
|
||||
assertEquals([3, 3, 4, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTracking);
|
||||
assertEquals([1, 2, 3, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTrackingWithOffset);
|
||||
assertEquals([3, 2, 3, 3, 4, 0], ToNumbers(target));
|
||||
|
||||
// Shrink so that the TAs with offset go out of bounds.
|
||||
rab.resize(1 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
assertThrows(() => { SetHelper(target, fixedLength)}, TypeError);
|
||||
assertThrows(() => { SetHelper(target, fixedLengthWithOffset)},
|
||||
TypeError);
|
||||
assertThrows(() => { SetHelper(target, lengthTrackingWithOffset)},
|
||||
TypeError);
|
||||
|
||||
SetHelper(target, lengthTracking, 3);
|
||||
assertEquals([3, 2, 3, 1, 4, 0], ToNumbers(target));
|
||||
|
||||
// Shrink to zero.
|
||||
rab.resize(0);
|
||||
|
||||
assertThrows(() => { SetHelper(target, fixedLength)}, TypeError);
|
||||
assertThrows(() => { SetHelper(target, fixedLengthWithOffset)},
|
||||
TypeError);
|
||||
assertThrows(() => { SetHelper(target, lengthTrackingWithOffset)},
|
||||
TypeError);
|
||||
|
||||
SetHelper(target, lengthTracking, 4);
|
||||
assertEquals([3, 2, 3, 1, 4, 0], ToNumbers(target));
|
||||
|
||||
// Grow so that all TAs are back in-bounds.
|
||||
rab.resize(6 * sourceCtor.BYTES_PER_ELEMENT);
|
||||
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
WriteToTypedArray(taFull, i, i + 1);
|
||||
}
|
||||
|
||||
// Orig. array: [1, 2, 3, 4, 5, 6]
|
||||
// [1, 2, 3, 4] << fixedLength
|
||||
// [3, 4] << fixedLengthWithOffset
|
||||
// [1, 2, 3, 4, 5, 6, ...] << lengthTracking
|
||||
// [3, 4, 5, 6, ...] << lengthTrackingWithOffset
|
||||
|
||||
SetHelper(target, fixedLength);
|
||||
assertEquals([1, 2, 3, 4, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, fixedLengthWithOffset);
|
||||
assertEquals([3, 4, 3, 4, 4, 0], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTracking, 0);
|
||||
assertEquals([1, 2, 3, 4, 5, 6], ToNumbers(target));
|
||||
|
||||
SetHelper(target, lengthTrackingWithOffset, 1);
|
||||
assertEquals([1, 3, 4, 5, 6, 6], ToNumbers(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -283,9 +283,6 @@
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=11111
|
||||
'built-ins/ArrayBuffer/prototype/transfer/*': [FAIL],
|
||||
'built-ins/ArrayBuffer/prototype/transfer/this-is-sharedarraybuffer': [PASS],
|
||||
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-target-out-of-bounds': [SKIP],
|
||||
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-set-values-same-buffer-same-type-resized': [SKIP],
|
||||
'built-ins/TypedArray/prototype/set/typedarray-arg-target-out-of-bounds': [SKIP],
|
||||
'built-ins/TypedArray/prototype/sort/BigInt/return-abrupt-from-this-out-of-bounds': [FAIL],
|
||||
'built-ins/TypedArray/prototype/sort/return-abrupt-from-this-out-of-bounds': [FAIL],
|
||||
'built-ins/TypedArrayConstructors/ctors/typedarray-arg/out-of-bounds-when-species-retrieved-different-type': [FAIL],
|
||||
|
Loading…
Reference in New Issue
Block a user