Commit 8f3e53b8 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[rab/gsab] Add RAB / GSAB support to DataViews

Bug: v8:11111
Change-Id: Ice66accee734484302d499b8098056ae1c68faf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3259648
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78028}
parent 094a7623
......@@ -21,6 +21,7 @@ namespace internal {
BUILTIN(DataViewConstructor) {
const char* const kMethodName = "DataView constructor";
HandleScope scope(isolate);
// 1. If NewTarget is undefined, throw a TypeError exception.
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
......@@ -55,8 +56,8 @@ BUILTIN(DataViewConstructor) {
kMethodName)));
}
// 5. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
size_t const buffer_byte_length = array_buffer->byte_length();
// 5. Let bufferByteLength be ArrayBufferByteLength(buffer, SeqCst).
size_t buffer_byte_length = array_buffer->GetByteLength();
// 6. If offset > bufferByteLength, throw a RangeError exception.
if (view_byte_offset > buffer_byte_length) {
......@@ -64,15 +65,22 @@ BUILTIN(DataViewConstructor) {
isolate, NewRangeError(MessageTemplate::kInvalidOffset, byte_offset));
}
// 7. Let bufferIsResizable be IsResizableArrayBuffer(buffer).
// 8. Let byteLengthChecked be empty.
// 9. If bufferIsResizable is true and byteLength is undefined, then
// a. Let viewByteLength be auto.
// 10. Else if byteLength is undefined, then
// a. Let viewByteLength be bufferByteLength - offset.
size_t view_byte_length;
bool length_tracking = false;
if (byte_length->IsUndefined(isolate)) {
// 7. If byteLength is undefined, then
// a. Let viewByteLength be bufferByteLength - offset.
view_byte_length = buffer_byte_length - view_byte_offset;
length_tracking = array_buffer->is_resizable();
} else {
// 8. Else,
// a. Let viewByteLength be ? ToIndex(byteLength).
// b. If offset+viewByteLength > bufferByteLength, throw a
// 11. Else,
// a. Set byteLengthChecked be ? ToIndex(byteLength).
// b. Let viewByteLength be byteLengthChecked.
// c. If offset + viewByteLength > bufferByteLength, throw a
// RangeError exception.
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, byte_length,
......@@ -85,7 +93,7 @@ BUILTIN(DataViewConstructor) {
view_byte_length = byte_length->Number();
}
// 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
// 12. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
// "%DataViewPrototype%", «[[DataView]], [[ViewedArrayBuffer]],
// [[ByteLength]], [[ByteOffset]]»).
Handle<JSObject> result;
......@@ -97,26 +105,29 @@ BUILTIN(DataViewConstructor) {
// TODO(v8:10391, saelo): Handle external pointers in EmbedderDataSlot
data_view->SetEmbedderField(i, Smi::zero());
}
data_view->set_is_backed_by_rab(array_buffer->is_resizable() &&
!array_buffer->is_shared());
data_view->set_is_length_tracking(length_tracking);
// We have to set the internal slots before the detached check on step 10 or
// We have to set the internal slots before the checks on steps 13 - 17 or
// the TorqueGeneratedClassVerifier ended up complaining that the slot is
// empty or invalid on heap teardown.
// The result object is not observable from JavaScript when step 10 early
// aborts so it is fine to set internal slots here.
// The result object is not observable from JavaScript when steps 13 - 17
// early abort so it is fine to set internal slots here.
// 11. Set O.[[ViewedArrayBuffer]] to buffer.
// 18. Set O.[[ViewedArrayBuffer]] to buffer.
data_view->set_buffer(*array_buffer);
// 12. Set O.[[ByteLength]] to viewByteLength.
data_view->set_byte_length(view_byte_length);
// 19. Set O.[[ByteLength]] to viewByteLength.
data_view->set_byte_length(length_tracking ? 0 : view_byte_length);
// 13. Set O.[[ByteOffset]] to offset.
// 20. Set O.[[ByteOffset]] to offset.
data_view->set_byte_offset(view_byte_offset);
data_view->set_data_pointer(
isolate,
static_cast<uint8_t*>(array_buffer->backing_store()) + view_byte_offset);
// 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 13. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (array_buffer->was_detached()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kDetachedOperation,
......@@ -124,7 +135,27 @@ BUILTIN(DataViewConstructor) {
kMethodName)));
}
// 14. Return O.
// 14. Let getBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(SeqCst).
// 15. Set bufferByteLength be getBufferByteLength(buffer).
buffer_byte_length = array_buffer->GetByteLength();
// 16. If offset > bufferByteLength, throw a RangeError exception.
if (view_byte_offset > buffer_byte_length) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidOffset, byte_offset));
}
// 17. If byteLengthChecked is not empty, then
// a. If offset + viewByteLength > bufferByteLength, throw a RangeError
// exception.
if (!length_tracking &&
view_byte_offset + view_byte_length > buffer_byte_length) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidDataViewLength));
}
// 21. Return O.
return *result;
}
......
......@@ -127,7 +127,8 @@ TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
LoadJSArrayBufferViewBuffer(receiver_array);
Label variable_length(this), normal(this);
Branch(IsVariableLengthTypedArray(receiver_array), &variable_length, &normal);
Branch(IsVariableLengthJSArrayBufferView(receiver_array), &variable_length,
&normal);
BIND(&variable_length);
{
Return(ChangeUintPtrToTagged(LoadVariableLengthJSTypedArrayByteLength(
......@@ -155,7 +156,7 @@ TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
// Default to zero if the {receiver}s buffer was detached / out of bounds.
Label detached_or_oob(this), not_detached_nor_oob(this);
IsJSTypedArrayDetachedOrOutOfBounds(CAST(receiver), &detached_or_oob,
IsJSArrayBufferViewDetachedOrOutOfBounds(CAST(receiver), &detached_or_oob,
&not_detached_nor_oob);
BIND(&detached_or_oob);
Return(ChangeUintPtrToTagged(UintPtrConstant(0)));
......
......@@ -84,15 +84,32 @@ 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 {
const dataView: JSDataView =
ValidateDataView(context, receiver, 'get DataView.prototype.byte_length');
if (IsVariableLengthJSArrayBufferView(dataView)) {
try {
const byteLength = LoadVariableLengthJSArrayBufferViewByteLength(
dataView, dataView.buffer) otherwise DetachedOrOutOfBounds;
return Convert<Number>(byteLength);
} label DetachedOrOutOfBounds {
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameByteLength);
}
} else {
if (WasDetached(dataView)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteLength);
ThrowTypeError(
MessageTemplate::kDetachedOperation, kBuiltinNameByteLength);
}
return Convert<Number>(dataView.byte_length);
}
}
// ES6 section 24.2.4.3 get DataView.prototype.byteOffset
......@@ -100,10 +117,14 @@ javascript builtin DataViewPrototypeGetByteOffset(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
const dataView: JSDataView =
ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset');
if (WasDetached(dataView)) {
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteOffset);
}
} label NotDetachedNorOutOfBounds {
return Convert<Number>(dataView.byte_offset);
}
}
extern macro BitcastInt32ToFloat32(uint32): float32;
......@@ -373,28 +394,40 @@ transitioning macro DataViewGet(
// 5. Let buffer be view.[[ViewedArrayBuffer]].
const buffer: JSArrayBuffer = dataView.buffer;
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer)) {
// 6. Let getBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(Unordered).
// 7. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
// TypeError exception.
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(
MessageTemplate::kDetachedOperation,
MakeDataViewGetterNameString(kind));
}
} label NotDetachedNorOutOfBounds {}
// 7. Let viewOffset be view.[[ByteOffset]].
// 8. Let viewOffset be view.[[ByteOffset]].
const viewOffset: uintptr = dataView.byte_offset;
// 8. Let viewSize be view.[[ByteLength]].
const viewSize: uintptr = dataView.byte_length;
// 9. Let viewSize be GetViewByteLength(view, getBufferByteLength).
let viewSize: uintptr;
if (dataView.bit_field.is_length_tracking) {
viewSize = LoadVariableLengthJSArrayBufferViewByteLength(
dataView, dataView.buffer) otherwise unreachable;
} else {
viewSize = dataView.byte_length;
}
// 9. Let elementSize be the Element Size value specified in Table 62
// 10. Let elementSize be the Element Size value specified in Table 62
// for Element Type type.
const elementSize: uintptr = DataViewElementSize(kind);
// 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
// 11. If getIndex + elementSize > viewSize, throw a RangeError exception.
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
otherwise RangeError;
// 11. Let bufferIndex be getIndex + viewOffset.
// 12. Let bufferIndex be getIndex + viewOffset.
const bufferIndex: uintptr = getIndex + viewOffset;
if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
......@@ -654,9 +687,6 @@ transitioning macro DataViewSet(
// 3. Let getIndex be ? ToIndex(requestIndex).
const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError;
const littleEndian: bool = ToBoolean(requestedLittleEndian);
const buffer: JSArrayBuffer = dataView.buffer;
let numberValue: Numeric;
if constexpr (
kind == ElementsKind::BIGUINT64_ELEMENTS ||
......@@ -669,28 +699,54 @@ transitioning macro DataViewSet(
numberValue = ToNumber(context, value);
}
// 6. Set isLittleEndian to !ToBoolean(isLittleEndian).
const littleEndian: bool = ToBoolean(requestedLittleEndian);
// 7. Let buffer be view.[[ViewedArrayBuffer]].
const buffer: JSArrayBuffer = dataView.buffer;
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if (IsDetachedBuffer(buffer)) {
ThrowTypeError(
MessageTemplate::kDetachedOperation,
MakeDataViewSetterNameString(kind));
}
// 8. Let getBufferByteLength be
// MakeIdempotentArrayBufferByteLengthGetter(Unordered).
// 9. NOTE: Bounds checking is not a synchronizing operation when view's
// backing buffer is a growable SharedArrayBuffer.
// 10. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
// TypeError exception.
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(
MessageTemplate::kDetachedOperation,
MakeDataViewGetterNameString(kind));
} label NotDetachedNorOutOfBounds {}
// 9. Let viewOffset be view.[[ByteOffset]].
// 11. Let viewOffset be view.[[ByteOffset]].
const viewOffset: uintptr = dataView.byte_offset;
// 10. Let viewSize be view.[[ByteLength]].
const viewSize: uintptr = dataView.byte_length;
// 12. Let viewSize be GetViewByteLength(view, getBufferByteLength).
let viewSize: uintptr;
if (dataView.bit_field.is_length_tracking) {
viewSize = LoadVariableLengthJSArrayBufferViewByteLength(
dataView, dataView.buffer) otherwise unreachable;
} else {
viewSize = dataView.byte_length;
}
// 11. Let elementSize be the Element Size value specified in Table 62
// 13. Let elementSize be the Element Size value specified in Table 62
// for Element Type type.
const elementSize: uintptr = DataViewElementSize(kind);
// 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
// 14. If getIndex + elementSize > viewSize, throw a RangeError exception.
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
otherwise RangeError;
// 13. Let bufferIndex be getIndex + viewOffset.
// 15. Let bufferIndex be getIndex + viewOffset.
const bufferIndex: uintptr = getIndex + viewOffset;
if constexpr (
......
......@@ -180,17 +180,18 @@ extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
JSTypedArray, ByteArray, uintptr): void;
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
JSTypedArray, RawPtr, uintptr): void;
extern macro IsJSTypedArrayDetachedOrOutOfBounds(JSTypedArray):
never labels Detached, NotDetached;
extern macro IsJSArrayBufferViewDetachedOrOutOfBounds(JSArrayBufferView):
never labels DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
// AttachedJSTypedArray guards that the array's buffer is not detached.
transient type AttachedJSTypedArray extends JSTypedArray;
macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray
labels Detached {
labels DetachedOrOutOfBounds {
try {
IsJSTypedArrayDetachedOrOutOfBounds(array) otherwise Detached, NotDetached;
} label NotDetached {
IsJSArrayBufferViewDetachedOrOutOfBounds(array)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label NotDetachedNorOutOfBounds {
return %RawDownCast<AttachedJSTypedArray>(array);
}
}
......
......@@ -13888,7 +13888,7 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLengthAndCheckDetached(
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array);
Label variable_length(this), fixed_length(this), end(this);
Branch(IsVariableLengthTypedArray(typed_array), &variable_length,
Branch(IsVariableLengthJSArrayBufferView(typed_array), &variable_length,
&fixed_length);
BIND(&variable_length);
{
......@@ -13911,36 +13911,55 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLengthAndCheckDetached(
// ES #sec-integerindexedobjectlength
TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength(
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer, Label* miss) {
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer,
Label* detached_or_out_of_bounds) {
// byte_length already takes array's offset into account.
TNode<UintPtrT> byte_length = LoadVariableLengthJSArrayBufferViewByteLength(
array, buffer, detached_or_out_of_bounds);
TNode<IntPtrT> element_size =
RabGsabElementsKindToElementByteSize(LoadElementsKind(array));
return Unsigned(IntPtrDiv(Signed(byte_length), element_size));
}
TNode<UintPtrT>
CodeStubAssembler::LoadVariableLengthJSArrayBufferViewByteLength(
TNode<JSArrayBufferView> array, TNode<JSArrayBuffer> buffer,
Label* detached_or_out_of_bounds) {
Label is_gsab(this), is_rab(this), end(this);
TVARIABLE(UintPtrT, result);
TNode<UintPtrT> array_byte_offset = LoadJSArrayBufferViewByteOffset(array);
Branch(IsSharedArrayBuffer(buffer), &is_gsab, &is_rab);
BIND(&is_gsab);
{
// Non-length-tracking GSAB-backed TypedArrays shouldn't end up here.
CSA_DCHECK(this, IsLengthTrackingTypedArray(array));
// Non-length-tracking GSAB-backed ArrayBufferViews shouldn't end up here.
CSA_DCHECK(this, IsLengthTrackingJSArrayBufferView(array));
// Read the byte length from the BackingStore.
const TNode<ExternalReference> length_function = ExternalConstant(
ExternalReference::length_tracking_gsab_backed_typed_array_length());
const TNode<ExternalReference> byte_length_function =
ExternalConstant(ExternalReference::gsab_byte_length());
TNode<ExternalReference> isolate_ptr =
ExternalConstant(ExternalReference::isolate_address(isolate()));
result = UncheckedCast<UintPtrT>(
CallCFunction(length_function, MachineType::UintPtr(),
TNode<UintPtrT> buffer_byte_length = UncheckedCast<UintPtrT>(
CallCFunction(byte_length_function, MachineType::UintPtr(),
std::make_pair(MachineType::Pointer(), isolate_ptr),
std::make_pair(MachineType::AnyTagged(), array)));
std::make_pair(MachineType::AnyTagged(), buffer)));
// Since the SharedArrayBuffer can't shrink, and we've managed to create
// this JSArrayBufferDataView without throwing an exception, we know that
// buffer_byte_length >= array_byte_offset.
CSA_CHECK(this,
UintPtrGreaterThanOrEqual(buffer_byte_length, array_byte_offset));
result = UintPtrSub(buffer_byte_length, array_byte_offset);
Goto(&end);
}
BIND(&is_rab);
{
GotoIf(IsDetachedBuffer(buffer), miss);
GotoIf(IsDetachedBuffer(buffer), detached_or_out_of_bounds);
TNode<UintPtrT> buffer_byte_length = LoadJSArrayBufferByteLength(buffer);
TNode<UintPtrT> array_byte_offset = LoadJSArrayBufferViewByteOffset(array);
Label is_length_tracking(this), not_length_tracking(this);
Branch(IsLengthTrackingTypedArray(array), &is_length_tracking,
Branch(IsLengthTrackingJSArrayBufferView(array), &is_length_tracking,
&not_length_tracking);
BIND(&is_length_tracking);
......@@ -13948,16 +13967,8 @@ TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength(
// The backing RAB might have been shrunk so that the start of the
// TypedArray is already out of bounds.
GotoIfNot(UintPtrLessThanOrEqual(array_byte_offset, buffer_byte_length),
miss);
// length = (buffer_byte_length - byte_offset) / element_size
// Conversion to signed is OK since buffer_byte_length <
// JSArrayBuffer::kMaxByteLength.
TNode<IntPtrT> element_size =
RabGsabElementsKindToElementByteSize(LoadElementsKind(array));
TNode<IntPtrT> length =
IntPtrDiv(Signed(UintPtrSub(buffer_byte_length, array_byte_offset)),
element_size);
result = Unsigned(length);
detached_or_out_of_bounds);
result = UintPtrSub(buffer_byte_length, array_byte_offset);
Goto(&end);
}
......@@ -13970,8 +13981,8 @@ TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength(
GotoIfNot(UintPtrGreaterThanOrEqual(
buffer_byte_length,
UintPtrAdd(array_byte_offset, array_byte_length)),
miss);
result = LoadJSTypedArrayLength(array);
detached_or_out_of_bounds);
result = array_byte_length;
Goto(&end);
}
}
......@@ -13979,13 +13990,13 @@ TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength(
return result.value();
}
void CodeStubAssembler::IsJSTypedArrayDetachedOrOutOfBounds(
TNode<JSTypedArray> array, Label* detached_or_oob,
void CodeStubAssembler::IsJSArrayBufferViewDetachedOrOutOfBounds(
TNode<JSArrayBufferView> array, Label* detached_or_oob,
Label* not_detached_nor_oob) {
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
GotoIf(IsDetachedBuffer(buffer), detached_or_oob);
GotoIfNot(IsVariableLengthTypedArray(array), not_detached_nor_oob);
GotoIfNot(IsVariableLengthJSArrayBufferView(array), not_detached_nor_oob);
GotoIf(IsSharedArrayBuffer(buffer), not_detached_nor_oob);
{
......@@ -13993,7 +14004,7 @@ void CodeStubAssembler::IsJSTypedArrayDetachedOrOutOfBounds(
TNode<UintPtrT> array_byte_offset = LoadJSArrayBufferViewByteOffset(array);
Label length_tracking(this), not_length_tracking(this);
Branch(IsLengthTrackingTypedArray(array), &length_tracking,
Branch(IsLengthTrackingJSArrayBufferView(array), &length_tracking,
&not_length_tracking);
BIND(&length_tracking);
......
......@@ -3609,13 +3609,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
// ResizableArrayBuffer.
TNode<UintPtrT> LoadVariableLengthJSTypedArrayLength(
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer, Label* miss);
TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer,
Label* detached_or_out_of_bounds);
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
// ResizableArrayBuffer.
TNode<UintPtrT> LoadVariableLengthJSTypedArrayByteLength(
TNode<Context> context, TNode<JSTypedArray> array,
TNode<JSArrayBuffer> buffer);
void IsJSTypedArrayDetachedOrOutOfBounds(TNode<JSTypedArray> array,
TNode<UintPtrT> LoadVariableLengthJSArrayBufferViewByteLength(
TNode<JSArrayBufferView> array, TNode<JSArrayBuffer> buffer,
Label* detached_or_out_of_bounds);
void IsJSArrayBufferViewDetachedOrOutOfBounds(TNode<JSArrayBufferView> array,
Label* detached_or_oob,
Label* not_detached_nor_oob);
......
......@@ -878,8 +878,7 @@ ExternalReference ExternalReference::search_string_raw() {
FUNCTION_REFERENCE(jsarray_array_join_concat_to_sequential_string,
JSArray::ArrayJoinConcatToSequentialString)
FUNCTION_REFERENCE(length_tracking_gsab_backed_typed_array_length,
JSTypedArray::LengthTrackingGsabBackedTypedArrayLength)
FUNCTION_REFERENCE(gsab_byte_length, JSArrayBuffer::GsabByteLength)
ExternalReference ExternalReference::search_string_raw_one_one() {
return search_string_raw<const uint8_t, const uint8_t>();
......
......@@ -134,6 +134,7 @@ class StatsCounter;
V(f64_mod_wrapper_function, "f64_mod_wrapper") \
V(get_date_field_function, "JSDate::GetField") \
V(get_or_create_hash_raw, "get_or_create_hash_raw") \
V(gsab_byte_length, "GsabByteLength") \
V(ieee754_acos_function, "base::ieee754::acos") \
V(ieee754_acosh_function, "base::ieee754::acosh") \
V(ieee754_asin_function, "base::ieee754::asin") \
......@@ -163,8 +164,6 @@ class StatsCounter;
V(jsarray_array_join_concat_to_sequential_string, \
"jsarray_array_join_concat_to_sequential_string") \
V(jsreceiver_create_identity_hash, "jsreceiver_create_identity_hash") \
V(length_tracking_gsab_backed_typed_array_length, \
"LengthTrackingGsabBackedTypedArrayLength") \
V(libc_memchr_function, "libc_memchr") \
V(libc_memcpy_function, "libc_memcpy") \
V(libc_memmove_function, "libc_memmove") \
......
......@@ -350,7 +350,7 @@ namespace internal {
T(InvalidCountValue, "Invalid count value") \
T(InvalidDataViewAccessorOffset, \
"Offset is outside the bounds of the DataView") \
T(InvalidDataViewLength, "Invalid DataView length %") \
T(InvalidDataViewLength, "Invalid DataView length") \
T(InvalidOffset, "Start offset % is outside the bounds of the buffer") \
T(InvalidHint, "Invalid hint: %") \
T(InvalidIndex, "Invalid value: not (convertible to) a safe integer") \
......
......@@ -178,12 +178,12 @@ bool JSArrayBufferView::WasDetached() const {
return JSArrayBuffer::cast(buffer()).was_detached();
}
BIT_FIELD_ACCESSORS(JSTypedArray, bit_field, is_length_tracking,
JSTypedArray::IsLengthTrackingBit)
BIT_FIELD_ACCESSORS(JSTypedArray, bit_field, is_backed_by_rab,
JSTypedArray::IsBackedByRabBit)
BIT_FIELD_ACCESSORS(JSArrayBufferView, bit_field, is_length_tracking,
JSArrayBufferView::IsLengthTrackingBit)
BIT_FIELD_ACCESSORS(JSArrayBufferView, bit_field, is_backed_by_rab,
JSArrayBufferView::IsBackedByRabBit)
bool JSTypedArray::IsVariableLength() const {
bool JSArrayBufferView::IsVariableLength() const {
return is_length_tracking() || is_backed_by_rab();
}
......
......@@ -141,6 +141,19 @@ size_t JSArrayBuffer::GetByteLength() const {
return byte_length();
}
size_t JSArrayBuffer::GsabByteLength(Isolate* isolate,
Address raw_array_buffer) {
// TODO(v8:11111): Cache the last seen length in JSArrayBuffer and use it
// in bounds checks to minimize the need for calling this function.
DCHECK(FLAG_harmony_rab_gsab);
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);
JSArrayBuffer buffer = JSArrayBuffer::cast(Object(raw_array_buffer));
CHECK(buffer.is_resizable());
CHECK(buffer.is_shared());
return buffer.GetBackingStore()->byte_length(std::memory_order_seq_cst);
}
ArrayBufferExtension* JSArrayBuffer::EnsureExtension() {
ArrayBufferExtension* extension = this->extension();
if (extension != nullptr) return extension;
......
......@@ -108,6 +108,8 @@ class JSArrayBuffer
size_t GetByteLength() const;
static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer);
// Allocates an ArrayBufferExtension for this array buffer, unless it is
// already associated with an extension.
ArrayBufferExtension* EnsureExtension();
......@@ -236,8 +238,15 @@ class JSArrayBufferView
DECL_VERIFIER(JSArrayBufferView)
// Bit positions for [bit_field].
DEFINE_TORQUE_GENERATED_JS_ARRAY_BUFFER_VIEW_FLAGS()
inline bool WasDetached() const;
DECL_BOOLEAN_ACCESSORS(is_length_tracking)
DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
inline bool IsVariableLength() const;
static constexpr int kEndOfTaggedFieldsOffset = kByteOffsetOffset;
STATIC_ASSERT(IsAligned(kByteOffsetOffset, kUIntptrSize));
......@@ -253,9 +262,6 @@ class JSTypedArray
// eventually.
static constexpr size_t kMaxLength = v8::TypedArray::kMaxLength;
// Bit positions for [bit_field].
DEFINE_TORQUE_GENERATED_JS_TYPED_ARRAY_FLAGS()
// [length]: length of typed array in elements.
DECL_PRIMITIVE_GETTER(length, size_t)
......@@ -287,9 +293,6 @@ class JSTypedArray
inline bool is_on_heap() const;
inline bool is_on_heap(AcquireLoadTag tag) const;
DECL_BOOLEAN_ACCESSORS(is_length_tracking)
DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
inline bool IsVariableLength() const;
inline size_t GetLengthOrOutOfBounds(bool& out_of_bounds) const;
inline size_t GetLength() const;
......@@ -332,8 +335,9 @@ class JSTypedArray
DECL_PRINTER(JSTypedArray)
DECL_VERIFIER(JSTypedArray)
STATIC_ASSERT(IsAligned(kLengthOffset, kUIntptrSize));
STATIC_ASSERT(IsAligned(kExternalPointerOffset, kSystemPointerSize));
// TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
// STATIC_ASSERT(IsAligned(kLengthOffset, kTaggedSize));
// STATIC_ASSERT(IsAligned(kExternalPointerOffset, kTaggedSize));
static const int kSizeWithEmbedderFields =
kHeaderSize +
......@@ -379,7 +383,8 @@ class JSDataView
DECL_PRINTER(JSDataView)
DECL_VERIFIER(JSDataView)
STATIC_ASSERT(IsAligned(kDataPointerOffset, kUIntptrSize));
// TODO(v8:9287): Re-enable when GCMole stops mixing 32/64 bit configs.
// STATIC_ASSERT(IsAligned(kDataPointerOffset, kTaggedSize));
static const int kSizeWithEmbedderFields =
kHeaderSize +
......
......@@ -40,43 +40,44 @@ macro IsResizableArrayBuffer(buffer: JSArrayBuffer): bool {
return buffer.bit_field.is_resizable;
}
@abstract
extern class JSArrayBufferView extends JSObject {
buffer: JSArrayBuffer;
byte_offset: uintptr;
byte_length: uintptr;
}
// We have 4 different TypedArrays:
// We have 4 different DataViews & TypedArrays:
// 1) Normal (backed by AB / SAB) or non-length tracking backed by GSAB (can't
// go oob once constructed) 2) Non-length tracking backed by RAB (can go oob
// once constructed) 3) Length-tracking backed by RAB (JSArrayBuffer stores the
// length) 4) Length-tracking backed by GSAB (BackingStore stores the length)
bitfield struct JSTypedArrayFlags extends uint32 {
// go oob once constructed)
// 2) Non-length tracking backed by RAB (can go oob once constructed)
// 3) Length-tracking backed by RAB (JSArrayBuffer stores the length)
// 4) Length-tracking backed by GSAB (BackingStore stores the length)
bitfield struct JSArrayBufferViewFlags extends uint32 {
is_length_tracking: bool: 1 bit;
is_backed_by_rab: bool: 1 bit;
}
extern class JSTypedArray extends JSArrayBufferView {
length: uintptr;
external_pointer: ExternalPointer;
base_pointer: ByteArray|Smi;
bit_field: JSTypedArrayFlags;
@abstract
extern class JSArrayBufferView extends JSObject {
buffer: JSArrayBuffer;
byte_offset: uintptr;
byte_length: uintptr;
bit_field: JSArrayBufferViewFlags;
// Pads header size to be a multiple of kTaggedSize.
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
}
@export
macro IsVariableLengthTypedArray(array: JSTypedArray): bool {
macro IsVariableLengthJSArrayBufferView(array: JSArrayBufferView): bool {
return array.bit_field.is_length_tracking || array.bit_field.is_backed_by_rab;
}
@export
macro IsLengthTrackingTypedArray(array: JSTypedArray): bool {
macro IsLengthTrackingJSArrayBufferView(array: JSArrayBufferView): bool {
return array.bit_field.is_length_tracking;
}
extern class JSTypedArray extends JSArrayBufferView {
length: uintptr;
external_pointer: ExternalPointer;
base_pointer: ByteArray|Smi;
}
extern class JSDataView extends JSArrayBufferView {
data_pointer: ExternalPointer;
}
......
// Copyright 2021 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.
// Flags: --harmony-rab-gsab --allow-natives-syntax
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
(function DataViewPrototype() {
const gsab = CreateGrowableSharedArrayBuffer(40, 80);
const sab = new SharedArrayBuffer(80);
const dvGsab = new DataView(gsab, 0, 3);
const dvSab = new DataView(sab, 0, 3);
assertEquals(dvGsab.__proto__, dvSab.__proto__);
})();
(function DataViewByteLength() {
const gsab = CreateGrowableSharedArrayBuffer(40, 80);
const dv = new DataView(gsab, 0, 3);
assertEquals(gsab, dv.buffer);
assertEquals(3, dv.byteLength);
const emptyDv = new DataView(gsab, 0, 0);
assertEquals(gsab, emptyDv.buffer);
assertEquals(0, emptyDv.byteLength);
const dvWithOffset = new DataView(gsab, 2, 3);
assertEquals(gsab, dvWithOffset.buffer);
assertEquals(3, dvWithOffset.byteLength);
const emptyDvWithOffset = new DataView(gsab, 2, 0);
assertEquals(gsab, emptyDvWithOffset.buffer);
assertEquals(0, emptyDvWithOffset.byteLength);
const lengthTracking = new DataView(gsab);
assertEquals(gsab, lengthTracking.buffer);
assertEquals(40, lengthTracking.byteLength);
const offset = 8;
const lengthTrackingWithOffset = new DataView(gsab, offset);
assertEquals(gsab, lengthTrackingWithOffset.buffer);
assertEquals(40 - offset, lengthTrackingWithOffset.byteLength);
const emptyLengthTrackingWithOffset = new DataView(gsab, 40);
assertEquals(gsab, emptyLengthTrackingWithOffset.buffer);
assertEquals(0, emptyLengthTrackingWithOffset.byteLength);
})();
(function ConstructInvalid() {
const gsab = CreateGrowableSharedArrayBuffer(40, 80);
// Length too big.
assertThrows(() => { new DataView(gsab, 0, 41); }, RangeError);
// Offset too close to the end.
assertThrows(() => { new DataView(gsab, 39, 2); }, RangeError);
// Offset beyond end.
assertThrows(() => { new DataView(gsab, 40, 1); }, RangeError);
})();
(function ConstructorParameterConversionGrows() {
const gsab = CreateGrowableSharedArrayBuffer(40, 80);
const evil = { valueOf: () => {
gsab.grow(50);
return 0;
}};
// Constructing will fail unless we take the new size into account.
const dv = new DataView(gsab, evil, 50);
assertEquals(50, dv.byteLength);
})();
(function GetAndSet() {
const gsab = CreateGrowableSharedArrayBuffer(64, 128);
const fixedLength = new DataView(gsab, 0, 32);
const fixedLengthWithOffset = new DataView(gsab, 2, 32);
const lengthTracking = new DataView(gsab, 0);
const lengthTrackingWithOffset = new DataView(gsab, 2);
testDataViewMethodsUpToSize(fixedLength, 32);
assertAllDataViewMethodsThrow(fixedLength, 33, RangeError);
testDataViewMethodsUpToSize(fixedLengthWithOffset, 32);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 33, RangeError);
testDataViewMethodsUpToSize(lengthTracking, 64);
assertAllDataViewMethodsThrow(lengthTracking, 65, RangeError);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 64 - 2);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 64 - 2 + 1,
RangeError);
// Grow.
gsab.grow(100);
testDataViewMethodsUpToSize(fixedLength, 32);
assertAllDataViewMethodsThrow(fixedLength, 33, RangeError);
testDataViewMethodsUpToSize(fixedLengthWithOffset, 32);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 33, RangeError);
testDataViewMethodsUpToSize(lengthTracking, 100);
assertAllDataViewMethodsThrow(lengthTracking, 101, RangeError);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 100 - 2);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 100 - 2 + 1,
RangeError);
})();
// Copyright 2021 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.
// Flags: --harmony-rab-gsab --allow-natives-syntax
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
(function ConstructorThrowsIfBufferDetached() {
const rab = CreateResizableArrayBuffer(40, 80);
%ArrayBufferDetach(rab);
assertThrows(() => { new DataView(rab); }, TypeError);
})();
(function TypedArrayLengthAndByteLength() {
const rab = CreateResizableArrayBuffer(40, 80);
let dvs = [];
dvs.push(new DataView(rab, 0, 3));
dvs.push(new DataView(rab, 8, 3));
dvs.push(new DataView(rab));
dvs.push(new DataView(rab, 8));
%ArrayBufferDetach(rab);
for (let dv of dvs) {
assertThrows(() => { dv.byteLength });
}
})();
(function AccessDetachedDataView() {
const rab = CreateResizableArrayBuffer(64, 128);
const fixedLength = new DataView(rab, 0, 32);
const fixedLengthWithOffset = new DataView(rab, 2, 32);
const lengthTracking = new DataView(rab, 0);
const lengthTrackingWithOffset = new DataView(rab, 2);
testDataViewMethodsUpToSize(fixedLength, 32);
assertAllDataViewMethodsThrow(fixedLength, 33, RangeError);
testDataViewMethodsUpToSize(fixedLengthWithOffset, 32);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 33, RangeError);
testDataViewMethodsUpToSize(lengthTracking, 64);
assertAllDataViewMethodsThrow(lengthTracking, 65, RangeError);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 64 - 2);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 64 - 2 + 1,
RangeError);
%ArrayBufferDetach(rab);
assertAllDataViewMethodsThrow(fixedLength, 0, TypeError);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 0, TypeError);
assertAllDataViewMethodsThrow(lengthTracking, 0, TypeError);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 0, TypeError);
})();
(function GetParameterConversionDetaches() {
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.getUint8(evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.getUint8(evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTracking.getUint8(evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTrackingWithOffset.getUint8(evil); },
TypeError);
}
})();
(function SetParameterConversionDetaches() {
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.setUint8(evil, 0); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.setUint8(evil, 0); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTracking.setUint8(evil, 0); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTrackingWithOffset.setUint8(evil, 0); },
TypeError);
}
// The same tests as before, except now the "detaching" parameter is the
// second one, not the first one.
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLength.setUint8(0, evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.setUint8(0, evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTracking.setUint8(0, evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
%ArrayBufferDetach(rab);
return 0;
}};
assertThrows(() => { lengthTrackingWithOffset.setUint8(0, evil); },
TypeError);
}
})();
// Copyright 2021 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.
// Flags: --harmony-rab-gsab --allow-natives-syntax
"use strict";
d8.file.execute('test/mjsunit/typedarray-helpers.js');
(function DataViewPrototype() {
const rab = CreateResizableArrayBuffer(40, 80);
const ab = new ArrayBuffer(80);
const dvRab = new DataView(rab, 0, 3);
const dvAb = new DataView(ab, 0, 3);
assertEquals(dvRab.__proto__, dvAb.__proto__);
})();
(function DataViewByteLength() {
const rab = CreateResizableArrayBuffer(40, 80);
const dv = new DataView(rab, 0, 3);
assertEquals(rab, dv.buffer);
assertEquals(3, dv.byteLength);
const emptyDv = new DataView(rab, 0, 0);
assertEquals(rab, emptyDv.buffer);
assertEquals(0, emptyDv.byteLength);
const dvWithOffset = new DataView(rab, 2, 3);
assertEquals(rab, dvWithOffset.buffer);
assertEquals(3, dvWithOffset.byteLength);
const emptyDvWithOffset = new DataView(rab, 2, 0);
assertEquals(rab, emptyDvWithOffset.buffer);
assertEquals(0, emptyDvWithOffset.byteLength);
const lengthTracking = new DataView(rab);
assertEquals(rab, lengthTracking.buffer);
assertEquals(40, lengthTracking.byteLength);
const offset = 8;
const lengthTrackingWithOffset = new DataView(rab, offset);
assertEquals(rab, lengthTrackingWithOffset.buffer);
assertEquals(40 - offset, lengthTrackingWithOffset.byteLength);
const emptyLengthTrackingWithOffset = new DataView(rab, 40);
assertEquals(rab, emptyLengthTrackingWithOffset.buffer);
assertEquals(0, emptyLengthTrackingWithOffset.byteLength);
})();
(function ConstructInvalid() {
const rab = CreateResizableArrayBuffer(40, 80);
// Length too big.
assertThrows(() => { new DataView(rab, 0, 41); }, RangeError);
// Offset too close to the end.
assertThrows(() => { new DataView(rab, 39, 2); }, RangeError);
// Offset beyond end.
assertThrows(() => { new DataView(rab, 40, 1); }, RangeError);
})();
(function ConstructorParameterConversionShrinks() {
const rab = CreateResizableArrayBuffer(40, 80);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { new DataView(rab, evil, 20);}, RangeError);
})();
(function ConstructorParameterConversionGrows() {
const gsab = CreateResizableArrayBuffer(40, 80);
const evil = { valueOf: () => {
gsab.resize(50);
return 0;
}};
// Constructing will fail unless we take the new size into account.
const dv = new DataView(gsab, evil, 50);
assertEquals(50, dv.byteLength);
})();
(function OrdinaryCreateFromConstructorShrinks() {
{
const rab = CreateResizableArrayBuffer(16, 40);
const newTarget = function() {}.bind(null);
Object.defineProperty(newTarget, "prototype", {
get() {
rab.resize(8);
return DataView.prototype;
}
});
assertThrows(() => {Reflect.construct(DataView, [rab, 0, 16], newTarget); },
RangeError);
}
{
const rab = CreateResizableArrayBuffer(16, 40);
const newTarget = function() {}.bind(null);
Object.defineProperty(newTarget, "prototype", {
get() {
rab.resize(6);
return DataView.prototype;
}
});
assertThrows(() => {Reflect.construct(DataView, [rab, 8, 2], newTarget); },
RangeError);
}
})();
(function DataViewByteLengthWhenResizedOutOfBounds1() {
const rab = CreateResizableArrayBuffer(16, 40);
const fixedLength = new DataView(rab, 0, 8);
const lengthTracking = new DataView(rab);
assertEquals(8, fixedLength.byteLength);
assertEquals(16, lengthTracking.byteLength);
assertEquals(0, fixedLength.byteOffset);
assertEquals(0, lengthTracking.byteOffset);
rab.resize(2);
assertThrows(() => { fixedLength.byteLength; }, TypeError);
assertEquals(2, lengthTracking.byteLength);
assertThrows(() => { fixedLength.byteOffset; }, TypeError);
assertEquals(0, lengthTracking.byteOffset);
rab.resize(8);
assertEquals(8, fixedLength.byteLength);
assertEquals(8, lengthTracking.byteLength);
assertEquals(0, fixedLength.byteOffset);
assertEquals(0, lengthTracking.byteOffset);
rab.resize(40);
assertEquals(8, fixedLength.byteLength);
assertEquals(40, lengthTracking.byteLength);
assertEquals(0, fixedLength.byteOffset);
assertEquals(0, lengthTracking.byteOffset);
rab.resize(0);
assertThrows(() => { fixedLength.byteLength; }, TypeError);
assertEquals(0, lengthTracking.byteLength);
assertThrows(() => { fixedLength.byteOffset; }, TypeError);
assertEquals(0, lengthTracking.byteOffset);
})();
// The previous test with offsets.
(function DataViewByteLengthWhenResizedOutOfBounds2() {
const rab = CreateResizableArrayBuffer(20, 40);
const fixedLengthWithOffset = new DataView(rab, 8, 8);
const lengthTrackingWithOffset = new DataView(rab, 8);
assertEquals(8, fixedLengthWithOffset.byteLength);
assertEquals(12, lengthTrackingWithOffset.byteLength);
assertEquals(8, fixedLengthWithOffset.byteOffset);
assertEquals(8, lengthTrackingWithOffset.byteOffset);
rab.resize(10);
assertThrows(() => { fixedLengthWithOffset.byteLength }, TypeError);
assertEquals(2, lengthTrackingWithOffset.byteLength);
assertThrows(() => { fixedLengthWithOffset.byteOffset }, TypeError);
assertEquals(8, lengthTrackingWithOffset.byteOffset);
rab.resize(16);
assertEquals(8, fixedLengthWithOffset.byteLength);
assertEquals(8, lengthTrackingWithOffset.byteLength);
assertEquals(8, fixedLengthWithOffset.byteOffset);
assertEquals(8, lengthTrackingWithOffset.byteOffset);
rab.resize(40);
assertEquals(8, fixedLengthWithOffset.byteLength);
assertEquals(32, lengthTrackingWithOffset.byteLength);
assertEquals(8, fixedLengthWithOffset.byteOffset);
assertEquals(8, lengthTrackingWithOffset.byteOffset);
rab.resize(6);
assertThrows(() => { fixedLengthWithOffset.byteLength }, TypeError);
assertThrows(() => { lengthTrackingWithOffset.byteLength }, TypeError);
assertThrows(() => { fixedLengthWithOffset.byteOffset }, TypeError);
assertThrows(() => { lengthTrackingWithOffset.byteOffset }, TypeError);
})();
(function GetAndSet() {
const rab = CreateResizableArrayBuffer(64, 128);
const fixedLength = new DataView(rab, 0, 32);
const fixedLengthWithOffset = new DataView(rab, 2, 32);
const lengthTracking = new DataView(rab, 0);
const lengthTrackingWithOffset = new DataView(rab, 2);
testDataViewMethodsUpToSize(fixedLength, 32);
assertAllDataViewMethodsThrow(fixedLength, 33, RangeError);
testDataViewMethodsUpToSize(fixedLengthWithOffset, 32);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 33, RangeError);
testDataViewMethodsUpToSize(lengthTracking, 64);
assertAllDataViewMethodsThrow(lengthTracking, 65, RangeError);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 64 - 2);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 64 - 2 + 1,
RangeError);
// Shrink so that fixed length TAs go out of bounds.
rab.resize(30);
assertAllDataViewMethodsThrow(fixedLength, 0, TypeError);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 0, TypeError);
testDataViewMethodsUpToSize(lengthTracking, 30);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 30 - 2);
// Shrink so that the TAs with offset go out of bounds.
rab.resize(1);
assertAllDataViewMethodsThrow(fixedLength, 0, TypeError);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 0, TypeError);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 0, TypeError);
testDataViewMethodsUpToSize(lengthTracking, 1);
assertAllDataViewMethodsThrow(lengthTracking, 2, RangeError);
// Shrink to zero.
rab.resize(0);
assertAllDataViewMethodsThrow(fixedLength, 0, TypeError);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 0, TypeError);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 0, TypeError);
testDataViewMethodsUpToSize(lengthTracking, 0);
assertAllDataViewMethodsThrow(lengthTracking, 1, RangeError);
// Grow so that all views are back in-bounds.
rab.resize(34);
testDataViewMethodsUpToSize(fixedLength, 32);
assertAllDataViewMethodsThrow(fixedLength, 33, RangeError);
testDataViewMethodsUpToSize(fixedLengthWithOffset, 32);
assertAllDataViewMethodsThrow(fixedLengthWithOffset, 33, RangeError);
testDataViewMethodsUpToSize(lengthTracking, 34);
assertAllDataViewMethodsThrow(lengthTracking, 35, RangeError);
testDataViewMethodsUpToSize(lengthTrackingWithOffset, 34 - 2);
assertAllDataViewMethodsThrow(lengthTrackingWithOffset, 34 - 2 + 1,
RangeError);
})();
(function GetParameterConversionShrinks() {
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLength.getUint8(evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.getUint8(evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
rab.resize(10);
return 12;
}};
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTracking.getUint8(evil); }, RangeError);
assertEquals(0, lengthTracking.getUint8(2));
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
rab.resize(10);
return 12;
}};
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTrackingWithOffset.getUint8(evil); },
RangeError);
evil = { valueOf: () => {
rab.resize(0);
return 0;
}};
// Now the DataView is out of bounds.
assertThrows(() => { lengthTrackingWithOffset.getUint8(evil); }, TypeError);
}
})();
(function SetParameterConversionShrinks() {
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLength.setUint8(evil, 0); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.setUint8(evil, 0); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
rab.resize(10);
return 12;
}};
lengthTracking.setUint8(12, 0); // Does not throw.
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTracking.setUint8(evil, 0); }, RangeError);
lengthTracking.setUint8(2, 0); // Does not throw.
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
rab.resize(10);
return 12;
}};
lengthTrackingWithOffset.setUint8(12, 0); // Does not throw.
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTrackingWithOffset.setUint8(evil, 0); },
RangeError);
evil = { valueOf: () => {
rab.resize(0);
return 0;
}};
// Now the DataView is out of bounds.
assertThrows(() => { lengthTrackingWithOffset.setUint8(evil, 0); },
TypeError);
}
// The same tests as before, except now the "resizing" parameter is the second
// one, not the first one.
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLength = new DataView(rab, 0, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLength.setUint8(0, evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const fixedLengthWithOffset = new DataView(rab, 2, 64);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
assertThrows(() => { fixedLengthWithOffset.setUint8(0, evil); }, TypeError);
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTracking = new DataView(rab);
const evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
lengthTracking.setUint8(12, 0); // Does not throw.
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTracking.setUint8(12, evil); }, RangeError);
lengthTracking.setUint8(2, 0); // Does not throw.
}
{
const rab = CreateResizableArrayBuffer(640, 1280);
const lengthTrackingWithOffset = new DataView(rab, 2);
let evil = { valueOf: () => {
rab.resize(10);
return 0;
}};
// The DataView is not out of bounds but the index is.
assertThrows(() => { lengthTrackingWithOffset.setUint8(12, evil); },
RangeError);
evil = { valueOf: () => {
rab.resize(0);
return 0;
}};
// Now the DataView is out of bounds.
assertThrows(() => { lengthTrackingWithOffset.setUint8(0, evil); },
TypeError);
}
})();
......@@ -8,14 +8,6 @@
d8.file.execute('test/mjsunit/typedarray-helpers.js');
function CreateResizableArrayBuffer(byteLength, maxByteLength) {
return new ArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
function CreateGrowableSharedArrayBuffer(byteLength, maxByteLength) {
return new SharedArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
function resizeHelper(ab, value) {
const return_value = ab.resize(value);
assertEquals(undefined, return_value);
......
......@@ -9,10 +9,6 @@
d8.file.execute('test/mjsunit/typedarray-helpers.js');
function CreateGrowableSharedArrayBuffer(byteLength, maxByteLength) {
return new SharedArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
(function TypedArrayPrototype() {
const gsab = CreateGrowableSharedArrayBuffer(40, 80);
const sab = new SharedArrayBuffer(80);
......
......@@ -22,10 +22,34 @@ const ctors = [
MyBigInt64Array,
];
// Each element of the following array is [getter, setter, size, isBigInt].
const dataViewAccessorsAndSizes = [[DataView.prototype.getUint8,
DataView.prototype.setUint8, 1, false],
[DataView.prototype.getInt8,
DataView.prototype.setInt8, 1, false],
[DataView.prototype.getUint16,
DataView.prototype.setUint16, 2, false],
[DataView.prototype.getInt16,
DataView.prototype.setInt16, 2, false],
[DataView.prototype.getInt32,
DataView.prototype.setInt32, 4, false],
[DataView.prototype.getFloat32,
DataView.prototype.setFloat32, 4, false],
[DataView.prototype.getFloat64,
DataView.prototype.setFloat64, 8, false],
[DataView.prototype.getBigUint64,
DataView.prototype.setBigUint64, 8, true],
[DataView.prototype.getBigInt64,
DataView.prototype.setBigInt64, 8, true]];
function CreateResizableArrayBuffer(byteLength, maxByteLength) {
return new ArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
function CreateGrowableSharedArrayBuffer(byteLength, maxByteLength) {
return new SharedArrayBuffer(byteLength, {maxByteLength: maxByteLength});
}
function ReadDataFromBuffer(ab, ctor) {
let result = [];
const ta = new ctor(ab, 0, ab.byteLength / ctor.BYTES_PER_ELEMENT);
......@@ -106,3 +130,35 @@ function IncludesHelper(array, n, fromIndex) {
}
return array.includes(n, fromIndex);
}
function testDataViewMethodsUpToSize(view, bufferSize) {
for (const [getter, setter, size, isBigInt] of dataViewAccessorsAndSizes) {
for (let i = 0; i <= bufferSize - size; ++i) {
if (isBigInt) {
setter.call(view, i, 3n);
} else {
setter.call(view, i, 3);
}
assertEquals(3, Number(getter.call(view, i)));
}
if (isBigInt) {
assertThrows(() => setter.call(view, bufferSize - size + 1, 0n),
RangeError);
} else {
assertThrows(() => setter.call(view, bufferSize - size + 1, 0),
RangeError);
}
assertThrows(() => getter.call(view, bufferSize - size + 1), RangeError);
}
}
function assertAllDataViewMethodsThrow(view, index, errorType) {
for (const [getter, setter, size, isBigInt] of dataViewAccessorsAndSizes) {
if (isBigInt) {
assertThrows(() => { setter.call(view, index, 3n); }, errorType);
} else {
assertThrows(() => { setter.call(view, index, 3); }, errorType);
}
assertThrows(() => { getter.call(view, index); }, errorType);
}
}
......@@ -283,30 +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/DataView/prototype/byteLength/resizable-array-buffer-auto': [FAIL],
'built-ins/DataView/prototype/byteLength/resizable-array-buffer-fixed': [FAIL],
'built-ins/DataView/prototype/byteOffset/resizable-array-buffer-auto': [FAIL],
'built-ins/DataView/prototype/byteOffset/resizable-array-buffer-fixed': [FAIL],
'built-ins/DataView/prototype/getBigInt64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getBigUint64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getFloat32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getFloat64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getInt16/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getInt32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getInt8/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getUint16/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getUint32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/getUint8/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setBigInt64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setBigUint64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setFloat32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setFloat64/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setInt16/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setInt32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setInt8/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setUint16/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setUint32/resizable-buffer': [FAIL],
'built-ins/DataView/prototype/setUint8/resizable-buffer': [FAIL],
'built-ins/TypedArray/prototype/indexOf/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/indexOf/return-abrupt-from-this-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/join/BigInt/return-abrupt-from-this-out-of-bounds': [SKIP],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment