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

Previous version: https://chromium-review.googlesource.com/c/v8/v8/+/3259648

Fix 1: ValueSerializer <3 JSArrayBufferView
Fix 2: set flags correctly when creating DataViews via the API

Bug: v8:11111
Change-Id: I9cbfdaff29f97c7823eaa3d931689b363e1f4cf7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3297708
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78046}
This commit is contained in:
Marja Hölttä 2021-11-23 13:35:27 +01:00 committed by V8 LUCI CQ
parent 9c8a8c6d5d
commit 8d0404d80b
23 changed files with 1071 additions and 180 deletions

View File

@ -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,9 +93,9 @@ BUILTIN(DataViewConstructor) {
view_byte_length = byte_length->Number();
}
// 9. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
// "%DataViewPrototype%", «[[DataView]], [[ViewedArrayBuffer]],
// [[ByteLength]], [[ByteOffset]]»).
// 12. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
// "%DataViewPrototype%", «[[DataView]], [[ViewedArrayBuffer]],
// [[ByteLength]], [[ByteOffset]]»).
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, 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;
}

View File

@ -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,8 +156,8 @@ 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,
&not_detached_nor_oob);
IsJSArrayBufferViewDetachedOrOutOfBounds(CAST(receiver), &detached_or_oob,
&not_detached_nor_oob);
BIND(&detached_or_oob);
Return(ChangeUintPtrToTagged(UintPtrConstant(0)));

View File

@ -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 (WasDetached(dataView)) {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteLength);
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);
}
return Convert<Number>(dataView.byte_length);
}
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);
}
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 (

View File

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

View File

@ -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);

View File

@ -3609,15 +3609,20 @@ 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,
Label* detached_or_oob,
Label* not_detached_nor_oob);
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);
TNode<IntPtrT> RabGsabElementsKindToElementByteSize(
TNode<Int32T> elementsKind);

View File

@ -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>();

View File

@ -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") \

View File

@ -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") \

View File

@ -2905,6 +2905,9 @@ Handle<JSDataView> Factory::NewJSDataView(Handle<JSArrayBuffer> buffer,
map, empty_fixed_array(), buffer, byte_offset, byte_length));
obj->set_data_pointer(
isolate(), static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
// TODO(v8:11111): Support creating length tracking DataViews via the API.
obj->set_is_length_tracking(false);
obj->set_is_backed_by_rab(!buffer->is_shared() && buffer->is_resizable());
return obj;
}

View File

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

View File

@ -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;

View File

@ -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 +

View File

@ -40,43 +40,44 @@ macro IsResizableArrayBuffer(buffer: JSArrayBuffer): bool {
return buffer.bit_field.is_resizable;
}
// 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 JSArrayBufferViewFlags extends uint32 {
is_length_tracking: bool: 1 bit;
is_backed_by_rab: bool: 1 bit;
}
@abstract
extern class JSArrayBufferView extends JSObject {
buffer: JSArrayBuffer;
byte_offset: uintptr;
byte_length: uintptr;
}
// We have 4 different 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 {
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;
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;
}

View File

@ -938,6 +938,7 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView view) {
WriteVarint(static_cast<uint8_t>(tag));
WriteVarint(static_cast<uint32_t>(view.byte_offset()));
WriteVarint(static_cast<uint32_t>(view.byte_length()));
WriteVarint(static_cast<uint8_t>(view.bit_field()));
return ThrowIfOutOfMemory();
}
@ -1862,10 +1863,11 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
uint8_t tag = 0;
uint32_t byte_offset = 0;
uint32_t byte_length = 0;
uint8_t flags = 0;
if (!ReadVarint<uint8_t>().To(&tag) ||
!ReadVarint<uint32_t>().To(&byte_offset) ||
!ReadVarint<uint32_t>().To(&byte_length) ||
byte_offset > buffer_byte_length ||
!ReadVarint<uint8_t>().To(&flags) || byte_offset > buffer_byte_length ||
byte_length > buffer_byte_length - byte_offset) {
return MaybeHandle<JSArrayBufferView>();
}
@ -1878,6 +1880,7 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
Handle<JSDataView> data_view =
isolate_->factory()->NewJSDataView(buffer, byte_offset, byte_length);
AddObjectWithID(id, data_view);
data_view->set_bit_field(flags);
return data_view;
}
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
@ -1894,6 +1897,7 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
}
Handle<JSTypedArray> typed_array = isolate_->factory()->NewJSTypedArray(
external_array_type, buffer, byte_offset, byte_length / element_size);
typed_array->set_bit_field(flags);
AddObjectWithID(id, typed_array);
return typed_array;
}

View File

@ -0,0 +1,113 @@
// 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);
})();

View File

@ -0,0 +1,193 @@
// 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);
}
})();

View File

@ -0,0 +1,434 @@
// 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);
}
})();

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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],

View File

@ -1859,15 +1859,16 @@ TEST_F(ValueSerializerTest, RoundTripTypedArray) {
TEST_F(ValueSerializerTest, DecodeTypedArray) {
// Check that the right type comes out the other side for every kind of typed
// array.
Local<Value> value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42,
0x02, 0x00, 0x00, 0x56, 0x42, 0x00, 0x02});
Local<Value> value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x02, 0x00, 0x00,
0x56, 0x42, 0x00, 0x02, 0x00});
ASSERT_TRUE(value->IsUint8Array());
EXPECT_EQ(2u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
ExpectScriptTrue("Object.getPrototypeOf(result) === Uint8Array.prototype");
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x02, 0x00,
0x00, 0x56, 0x62, 0x00, 0x02});
0x00, 0x56, 0x62, 0x00, 0x02, 0x00});
ASSERT_TRUE(value->IsInt8Array());
EXPECT_EQ(2u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
@ -1875,38 +1876,38 @@ TEST_F(ValueSerializerTest, DecodeTypedArray) {
#if defined(V8_TARGET_LITTLE_ENDIAN)
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x04, 0x00,
0x00, 0x00, 0x00, 0x56, 0x57, 0x00, 0x04});
0x00, 0x00, 0x00, 0x56, 0x57, 0x00, 0x04, 0x00});
ASSERT_TRUE(value->IsUint16Array());
EXPECT_EQ(4u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
ExpectScriptTrue("Object.getPrototypeOf(result) === Uint16Array.prototype");
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x04, 0x00,
0x00, 0x00, 0x00, 0x56, 0x77, 0x00, 0x04});
0x00, 0x00, 0x00, 0x56, 0x77, 0x00, 0x04, 0x00});
ASSERT_TRUE(value->IsInt16Array());
EXPECT_EQ(4u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
ExpectScriptTrue("Object.getPrototypeOf(result) === Int16Array.prototype");
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x44, 0x00, 0x08});
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x56, 0x44, 0x00, 0x08, 0x00});
ASSERT_TRUE(value->IsUint32Array());
EXPECT_EQ(8u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
ExpectScriptTrue("Object.getPrototypeOf(result) === Uint32Array.prototype");
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x64, 0x00, 0x08});
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x56, 0x64, 0x00, 0x08, 0x00});
ASSERT_TRUE(value->IsInt32Array());
EXPECT_EQ(8u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
ExpectScriptTrue("Object.getPrototypeOf(result) === Int32Array.prototype");
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x66, 0x00, 0x08});
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x56, 0x66, 0x00, 0x08, 0x00});
ASSERT_TRUE(value->IsFloat32Array());
EXPECT_EQ(8u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
@ -1915,7 +1916,7 @@ TEST_F(ValueSerializerTest, DecodeTypedArray) {
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x56, 0x46, 0x00, 0x10});
0x00, 0x00, 0x00, 0x00, 0x56, 0x46, 0x00, 0x10, 0x00});
ASSERT_TRUE(value->IsFloat64Array());
EXPECT_EQ(16u, TypedArray::Cast(*value)->ByteLength());
EXPECT_EQ(2u, TypedArray::Cast(*value)->Length());
@ -1929,14 +1930,15 @@ TEST_F(ValueSerializerTest, DecodeTypedArray) {
ExpectScriptTrue("result.toString() === '1,128,255'");
#if defined(V8_TARGET_LITTLE_ENDIAN)
value = DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x06, 0x00,
0x00, 0x00, 0x01, 0x00, 0x80, 0x56, 0x77, 0x00, 0x06});
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x06, 0x00, 0x00,
0x00, 0x01, 0x00, 0x80, 0x56, 0x77, 0x00, 0x06, 0x00});
ExpectScriptTrue("result.toString() === '0,256,-32768'");
value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0xC0, 0x7F,
0x00, 0x00, 0x80, 0x7F, 0x56, 0x66, 0x00, 0x10});
0x00, 0x00, 0x80, 0x7F, 0x56, 0x66, 0x00, 0x10, 0x00});
ExpectScriptTrue("result.toString() === '0,-0.5,NaN,Infinity'");
#endif // V8_TARGET_LITTLE_ENDIAN
@ -1948,10 +1950,10 @@ TEST_F(ValueSerializerTest, DecodeTypedArray) {
0x01, 0x3F, 0x01, 0x42, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x56, 0x42, 0x00, 0x20, 0x3F, 0x03, 0x53, 0x04, 0x75, 0x38, 0x5F,
0x32, 0x3F, 0x03, 0x5E, 0x02, 0x3F, 0x03, 0x53, 0x03, 0x66, 0x33, 0x32,
0x3F, 0x03, 0x3F, 0x03, 0x5E, 0x01, 0x56, 0x66, 0x04, 0x14, 0x3F, 0x04,
0x53, 0x01, 0x62, 0x3F, 0x04, 0x5E, 0x01, 0x7B, 0x04, 0x00});
0x00, 0x56, 0x42, 0x00, 0x20, 0x00, 0x3F, 0x03, 0x53, 0x04, 0x75, 0x38,
0x5F, 0x32, 0x3F, 0x03, 0x5E, 0x02, 0x3F, 0x03, 0x53, 0x03, 0x66, 0x33,
0x32, 0x3F, 0x03, 0x3F, 0x03, 0x5E, 0x01, 0x56, 0x66, 0x04, 0x14, 0x00,
0x3F, 0x04, 0x53, 0x01, 0x62, 0x3F, 0x04, 0x5E, 0x01, 0x7B, 0x04, 0x00});
ExpectScriptTrue("result.u8 instanceof Uint8Array");
ExpectScriptTrue("result.u8 === result.u8_2");
ExpectScriptTrue("result.f32 instanceof Float32Array");
@ -1990,7 +1992,7 @@ TEST_F(ValueSerializerTest, RoundTripDataView) {
TEST_F(ValueSerializerTest, DecodeDataView) {
Local<Value> value =
DecodeTest({0xFF, 0x09, 0x3F, 0x00, 0x3F, 0x00, 0x42, 0x04, 0x00, 0x00,
0x00, 0x00, 0x56, 0x3F, 0x01, 0x02});
0x00, 0x00, 0x56, 0x3F, 0x01, 0x02, 0x00});
ASSERT_TRUE(value->IsDataView());
EXPECT_EQ(1u, DataView::Cast(*value)->ByteOffset());
EXPECT_EQ(2u, DataView::Cast(*value)->ByteLength());