Commit 6e68daf7 authored by Samuel Groß's avatar Samuel Groß Committed by V8 LUCI CQ

ArrayBuffer refactoring in preparation for CagedPointers

The main changes of this CL are:

It should no longer be assumed that an empty ArrayBuffer has a nullptr
backing store. This is in preparation for the move to caged pointers,
which cannot represent nullptr, and will instead likely provide a
EmptyBackingStore constant pointing inside the virtual memory cage. For
that reason, a new JSArrayBuffer::IsEmpty() helper is introduced, which
should be used instead of checking against nullptr.

CodeStubAssembler::GetTypedArrayBuffer now checks for on-heap
TypedArrays instead of comparing the backing store pointer to nullptr.
This is consistent with the implementation in JSTypedArray::GetBuffer.

v8::ArrayBufferView::CopyContents now uses JSTypedArray::DataPtr instead
of relying on nullptr backing stores to handle on-heap TypedArrays.

The serializer and deserializer now check for IsEmpty() and use the
kEmptyBackingStoreRefSentinel value to serialize empty backing stores.

Empty ArrayBuffers allocated for on-heap TypedArrays now have a
byte_length of zero. This allows removing the allocation_length() (and
allocation_buffer()) methods, which were only (incorrectly, as they
don't account for GSABs) used for memory measurements.

Bug: chromium:1218005
Change-Id: Ib889ccf855f68525f7a614f3963e46ea56865fa3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3297709Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78069}
parent f60132e9
...@@ -8030,21 +8030,20 @@ Local<ArrayBuffer> v8::ArrayBufferView::Buffer() { ...@@ -8030,21 +8030,20 @@ Local<ArrayBuffer> v8::ArrayBufferView::Buffer() {
size_t v8::ArrayBufferView::CopyContents(void* dest, size_t byte_length) { size_t v8::ArrayBufferView::CopyContents(void* dest, size_t byte_length) {
i::Handle<i::JSArrayBufferView> self = Utils::OpenHandle(this); i::Handle<i::JSArrayBufferView> self = Utils::OpenHandle(this);
size_t byte_offset = self->byte_offset();
size_t bytes_to_copy = std::min(byte_length, self->byte_length()); size_t bytes_to_copy = std::min(byte_length, self->byte_length());
if (bytes_to_copy) { if (bytes_to_copy) {
i::DisallowGarbageCollection no_gc; i::DisallowGarbageCollection no_gc;
i::Isolate* isolate = self->GetIsolate(); i::Isolate* isolate = self->GetIsolate();
i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(self->buffer()), const char* source;
isolate); if (self->IsJSTypedArray()) {
const char* source = reinterpret_cast<char*>(buffer->backing_store()); i::Handle<i::JSTypedArray> array(i::JSTypedArray::cast(*self), isolate);
if (source == nullptr) { source = reinterpret_cast<char*>(array->DataPtr());
DCHECK(self->IsJSTypedArray()); } else {
i::Handle<i::JSTypedArray> typed_array(i::JSTypedArray::cast(*self), DCHECK(self->IsJSDataView());
isolate); i::Handle<i::JSDataView> data_view(i::JSDataView::cast(*self), isolate);
source = reinterpret_cast<char*>(typed_array->DataPtr()); source = reinterpret_cast<char*>(data_view->data_pointer());
} }
memcpy(dest, source + byte_offset, bytes_to_copy); memcpy(dest, source, bytes_to_copy);
} }
return bytes_to_copy; return bytes_to_copy;
} }
......
...@@ -33,7 +33,7 @@ void TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields( ...@@ -33,7 +33,7 @@ void TypedArrayBuiltinsAssembler::SetupTypedArrayEmbedderFields(
// elements. // elements.
// TODO(bmeurer,v8:4153): Rename this and maybe fix up the implementation a bit. // TODO(bmeurer,v8:4153): Rename this and maybe fix up the implementation a bit.
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
TNode<Context> context, TNode<UintPtrT> byte_length) { TNode<Context> context) {
TNode<NativeContext> native_context = LoadNativeContext(context); TNode<NativeContext> native_context = LoadNativeContext(context);
TNode<Map> map = TNode<Map> map =
CAST(LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX)); CAST(LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX));
...@@ -49,7 +49,7 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( ...@@ -49,7 +49,7 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
// Setup the ArrayBuffer. // Setup the ArrayBuffer.
// - Set BitField to 0. // - Set BitField to 0.
// - Set IsExternal and IsDetachable bits of BitFieldSlot. // - Set IsExternal and IsDetachable bits of BitFieldSlot.
// - Set the byte_length field to byte_length. // - Set the byte_length field to zero.
// - Set backing_store to null/Smi(0). // - Set backing_store to null/Smi(0).
// - Set extension to null. // - Set extension to null.
// - Set all embedder fields to Smi(0). // - Set all embedder fields to Smi(0).
...@@ -64,7 +64,7 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( ...@@ -64,7 +64,7 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
Int32Constant(bitfield_value)); Int32Constant(bitfield_value));
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset, StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
byte_length); UintPtrConstant(0));
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset, StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBackingStoreOffset,
PointerConstant(nullptr)); PointerConstant(nullptr));
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset, StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset,
......
...@@ -21,8 +21,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { ...@@ -21,8 +21,7 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<Map> map, TNode<Smi> length, TNode<Map> map, TNode<Smi> length,
TNode<UintPtrT> byte_offset); TNode<UintPtrT> byte_offset);
TNode<JSArrayBuffer> AllocateEmptyOnHeapBuffer(TNode<Context> context, TNode<JSArrayBuffer> AllocateEmptyOnHeapBuffer(TNode<Context> context);
TNode<UintPtrT> byte_length);
TNode<Map> LoadMapForType(TNode<JSTypedArray> array); TNode<Map> LoadMapForType(TNode<JSTypedArray> array);
TNode<BoolT> IsMockArrayBufferAllocatorFlag(); TNode<BoolT> IsMockArrayBufferAllocatorFlag();
......
...@@ -9,7 +9,7 @@ extern builtin IterableToListMayPreserveHoles( ...@@ -9,7 +9,7 @@ extern builtin IterableToListMayPreserveHoles(
Context, Object, Callable): JSArray; Context, Object, Callable): JSArray;
extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer( extern macro TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
implicit context: Context)(uintptr): JSArrayBuffer; implicit context: Context)(): JSArrayBuffer;
extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray; extern macro CodeStubAssembler::AllocateByteArray(uintptr): ByteArray;
extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor( extern macro TypedArrayBuiltinsAssembler::GetDefaultConstructor(
implicit context: Context)(JSTypedArray): JSFunction; implicit context: Context)(JSTypedArray): JSFunction;
...@@ -93,7 +93,7 @@ transitioning macro TypedArrayInitialize(implicit context: Context)( ...@@ -93,7 +93,7 @@ transitioning macro TypedArrayInitialize(implicit context: Context)(
if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap; if (byteLength > kMaxTypedArrayInHeap) goto AllocateOffHeap;
const buffer = AllocateEmptyOnHeapBuffer(byteLength); const buffer = AllocateEmptyOnHeapBuffer();
const isOnHeap: constexpr bool = true; const isOnHeap: constexpr bool = true;
const isLengthTracking: constexpr bool = false; const isLengthTracking: constexpr bool = false;
......
...@@ -14107,10 +14107,10 @@ TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer( ...@@ -14107,10 +14107,10 @@ TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer(
Label call_runtime(this), done(this); Label call_runtime(this), done(this);
TVARIABLE(Object, var_result); TVARIABLE(Object, var_result);
GotoIf(IsOnHeapTypedArray(array), &call_runtime);
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array); TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array);
GotoIf(IsDetachedBuffer(buffer), &call_runtime); GotoIf(IsDetachedBuffer(buffer), &call_runtime);
TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(buffer);
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
var_result = buffer; var_result = buffer;
Goto(&done); Goto(&done);
......
...@@ -401,7 +401,7 @@ void NativeContextStats::IncrementExternalSize(Address context, Map map, ...@@ -401,7 +401,7 @@ void NativeContextStats::IncrementExternalSize(Address context, Map map,
InstanceType instance_type = map.instance_type(); InstanceType instance_type = map.instance_type();
size_t external_size = 0; size_t external_size = 0;
if (instance_type == JS_ARRAY_BUFFER_TYPE) { if (instance_type == JS_ARRAY_BUFFER_TYPE) {
external_size = JSArrayBuffer::cast(object).allocation_length(); external_size = JSArrayBuffer::cast(object).GetByteLength();
} else { } else {
DCHECK(InstanceTypeChecker::IsExternalString(instance_type)); DCHECK(InstanceTypeChecker::IsExternalString(instance_type));
external_size = ExternalString::cast(object).ExternalPayloadSize(); external_size = ExternalString::cast(object).ExternalPayloadSize();
......
...@@ -98,6 +98,11 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { ...@@ -98,6 +98,11 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
bool has_guard_regions() const { return has_guard_regions_; } bool has_guard_regions() const { return has_guard_regions_; }
bool free_on_destruct() const { return free_on_destruct_; } bool free_on_destruct() const { return free_on_destruct_; }
bool IsEmpty() const {
DCHECK_GE(max_byte_length_, byte_length_);
return max_byte_length_ == 0;
}
enum ResizeOrGrowResult { kSuccess, kFailure, kRace }; enum ResizeOrGrowResult { kSuccess, kFailure, kRace };
ResizeOrGrowResult ResizeInPlace(Isolate* isolate, size_t new_byte_length, ResizeOrGrowResult ResizeInPlace(Isolate* isolate, size_t new_byte_length,
......
...@@ -44,6 +44,22 @@ void JSArrayBuffer::set_backing_store(void* value) { ...@@ -44,6 +44,22 @@ void JSArrayBuffer::set_backing_store(void* value) {
WriteField<Address>(kBackingStoreOffset, reinterpret_cast<Address>(value)); WriteField<Address>(kBackingStoreOffset, reinterpret_cast<Address>(value));
} }
std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() const {
if (!extension()) return nullptr;
return extension()->backing_store();
}
size_t JSArrayBuffer::GetByteLength() const {
if V8_UNLIKELY (is_shared() && is_resizable()) {
// Invariant: byte_length for GSAB is 0 (it needs to be read from the
// BackingStore).
DCHECK_EQ(0, byte_length());
return GetBackingStore()->byte_length(std::memory_order_seq_cst);
}
return byte_length();
}
uint32_t JSArrayBuffer::GetBackingStoreRefForDeserialization() const { uint32_t JSArrayBuffer::GetBackingStoreRefForDeserialization() const {
return static_cast<uint32_t>(ReadField<Address>(kBackingStoreOffset)); return static_cast<uint32_t>(ReadField<Address>(kBackingStoreOffset));
} }
...@@ -114,20 +130,6 @@ uint32_t* JSArrayBuffer::extension_hi() const { ...@@ -114,20 +130,6 @@ uint32_t* JSArrayBuffer::extension_hi() const {
} }
#endif #endif
size_t JSArrayBuffer::allocation_length() const {
if (backing_store() == nullptr) {
return 0;
}
return byte_length();
}
void* JSArrayBuffer::allocation_base() const {
if (backing_store() == nullptr) {
return nullptr;
}
return backing_store();
}
void JSArrayBuffer::clear_padding() { void JSArrayBuffer::clear_padding() {
if (FIELD_SIZE(kOptionalPaddingOffset) != 0) { if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset)); DCHECK_EQ(4, FIELD_SIZE(kOptionalPaddingOffset));
...@@ -158,6 +160,13 @@ BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared, ...@@ -158,6 +160,13 @@ BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_shared,
BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_resizable, BIT_FIELD_ACCESSORS(JSArrayBuffer, bit_field, is_resizable,
JSArrayBuffer::IsResizableBit) JSArrayBuffer::IsResizableBit)
bool JSArrayBuffer::IsEmpty() const {
auto backing_store = GetBackingStore();
bool is_empty = !backing_store || backing_store->IsEmpty();
DCHECK_IMPLIES(is_empty, byte_length() == 0);
return is_empty;
}
size_t JSArrayBufferView::byte_offset() const { size_t JSArrayBufferView::byte_offset() const {
return ReadField<size_t>(kByteOffsetOffset); return ReadField<size_t>(kByteOffsetOffset);
} }
......
...@@ -125,22 +125,6 @@ void JSArrayBuffer::Detach(bool force_for_wasm_memory) { ...@@ -125,22 +125,6 @@ void JSArrayBuffer::Detach(bool force_for_wasm_memory) {
set_was_detached(true); set_was_detached(true);
} }
std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() const {
if (!extension()) return nullptr;
return extension()->backing_store();
}
size_t JSArrayBuffer::GetByteLength() const {
if V8_UNLIKELY (is_shared() && is_resizable()) {
// Invariant: byte_length for GSAB is 0 (it needs to be read from the
// BackingStore).
DCHECK_EQ(0, byte_length());
return GetBackingStore()->byte_length(std::memory_order_seq_cst);
}
return byte_length();
}
size_t JSArrayBuffer::GsabByteLength(Isolate* isolate, size_t JSArrayBuffer::GsabByteLength(Isolate* isolate,
Address raw_array_buffer) { Address raw_array_buffer) {
// TODO(v8:11111): Cache the last seen length in JSArrayBuffer and use it // TODO(v8:11111): Cache the last seen length in JSArrayBuffer and use it
...@@ -207,7 +191,7 @@ Handle<JSArrayBuffer> JSTypedArray::GetBuffer() { ...@@ -207,7 +191,7 @@ Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
DCHECK(!array_buffer->is_resizable()); DCHECK(!array_buffer->is_resizable());
// The existing array buffer should be empty. // The existing array buffer should be empty.
DCHECK_NULL(array_buffer->backing_store()); DCHECK(array_buffer->IsEmpty());
// Allocate a new backing store and attach it to the existing array buffer. // Allocate a new backing store and attach it to the existing array buffer.
size_t byte_length = self->byte_length(); size_t byte_length = self->byte_length();
......
...@@ -37,17 +37,13 @@ class JSArrayBuffer ...@@ -37,17 +37,13 @@ class JSArrayBuffer
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t) DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
// [backing_store]: backing memory for this array // [backing_store]: backing memory for this array
// It should not be assumed that this will be nullptr for empty ArrayBuffers.
DECL_GETTER(backing_store, void*) DECL_GETTER(backing_store, void*)
inline void set_backing_store(void* value); inline void set_backing_store(void* value);
// [extension]: extension object used for GC // [extension]: extension object used for GC
DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*) DECL_PRIMITIVE_ACCESSORS(extension, ArrayBufferExtension*)
// For non-wasm, allocation_length and allocation_base are byte_length and
// backing_store, respectively.
inline size_t allocation_length() const;
inline void* allocation_base() const;
// [bit_field]: boolean flags // [bit_field]: boolean flags
DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t) DECL_PRIMITIVE_ACCESSORS(bit_field, uint32_t)
...@@ -80,6 +76,13 @@ class JSArrayBuffer ...@@ -80,6 +76,13 @@ class JSArrayBuffer
// GrowableSharedArrayBuffer. // GrowableSharedArrayBuffer.
DECL_BOOLEAN_ACCESSORS(is_resizable) DECL_BOOLEAN_ACCESSORS(is_resizable)
// An ArrayBuffer is empty if its BackingStore is empty or if there is none.
// An empty ArrayBuffer will have a byte_length of zero but not necessarily a
// nullptr backing_store. An ArrayBuffer with a byte_length of zero may not
// necessarily be empty though, as it may be a GrowableSharedArrayBuffer.
// An ArrayBuffer with a size greater than zero is never empty.
DECL_GETTER(IsEmpty, bool)
// Initializes the fields of the ArrayBuffer. The provided backing_store can // Initializes the fields of the ArrayBuffer. The provided backing_store can
// be nullptr. If it is not nullptr, then the function registers it with // be nullptr. If it is not nullptr, then the function registers it with
// src/heap/array-buffer-tracker.h. // src/heap/array-buffer-tracker.h.
...@@ -104,9 +107,9 @@ class JSArrayBuffer ...@@ -104,9 +107,9 @@ class JSArrayBuffer
// Get a reference to backing store of this array buffer, if there is a // Get a reference to backing store of this array buffer, if there is a
// backing store. Returns nullptr if there is no backing store (e.g. detached // backing store. Returns nullptr if there is no backing store (e.g. detached
// or a zero-length array buffer). // or a zero-length array buffer).
std::shared_ptr<BackingStore> GetBackingStore() const; inline std::shared_ptr<BackingStore> GetBackingStore() const;
size_t GetByteLength() const; inline size_t GetByteLength() const;
static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer); static size_t GsabByteLength(Isolate* isolate, Address raw_array_buffer);
......
...@@ -78,6 +78,12 @@ extern class JSTypedArray extends JSArrayBufferView { ...@@ -78,6 +78,12 @@ extern class JSTypedArray extends JSArrayBufferView {
base_pointer: ByteArray|Smi; base_pointer: ByteArray|Smi;
} }
@export
macro IsOnHeapTypedArray(array: JSTypedArray): bool {
// See JSTypedArray::is_on_heap()
return TaggedNotEqual(array.base_pointer, SmiConstant(0));
}
extern class JSDataView extends JSArrayBufferView { extern class JSDataView extends JSArrayBufferView {
data_pointer: ExternalPointer; data_pointer: ExternalPointer;
} }
......
...@@ -249,9 +249,9 @@ Deserializer<IsolateT>::Deserializer(IsolateT* isolate, ...@@ -249,9 +249,9 @@ Deserializer<IsolateT>::Deserializer(IsolateT* isolate,
isolate->RegisterDeserializerStarted(); isolate->RegisterDeserializerStarted();
// We start the indices here at 1, so that we can distinguish between an // We start the indices here at 1, so that we can distinguish between an
// actual index and a nullptr (serialized as kNullRefSentinel) in a // actual index and an empty backing store (serialized as
// deserialized object requiring fix-up. // kEmptyBackingStoreRefSentinel) in a deserialized object requiring fix-up.
STATIC_ASSERT(kNullRefSentinel == 0); STATIC_ASSERT(kEmptyBackingStoreRefSentinel == 0);
backing_stores_.push_back({}); backing_stores_.push_back({});
#ifdef DEBUG #ifdef DEBUG
...@@ -488,7 +488,7 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map, ...@@ -488,7 +488,7 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
JSArrayBuffer buffer = JSArrayBuffer::cast(data_view->buffer()); JSArrayBuffer buffer = JSArrayBuffer::cast(data_view->buffer());
void* backing_store = nullptr; void* backing_store = nullptr;
uint32_t store_index = buffer.GetBackingStoreRefForDeserialization(); uint32_t store_index = buffer.GetBackingStoreRefForDeserialization();
if (store_index != kNullRefSentinel) { if (store_index != kEmptyBackingStoreRefSentinel) {
// The backing store of the JSArrayBuffer has not been correctly restored // The backing store of the JSArrayBuffer has not been correctly restored
// yet, as that may trigger GC. The backing_store field currently contains // yet, as that may trigger GC. The backing_store field currently contains
// a numbered reference to an already deserialized backing store. // a numbered reference to an already deserialized backing store.
...@@ -519,7 +519,8 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map, ...@@ -519,7 +519,8 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
} else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) { } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) {
Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(obj); Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(obj);
// Postpone allocation of backing store to avoid triggering the GC. // Postpone allocation of backing store to avoid triggering the GC.
if (buffer->GetBackingStoreRefForDeserialization() != kNullRefSentinel) { if (buffer->GetBackingStoreRefForDeserialization() !=
kEmptyBackingStoreRefSentinel) {
new_off_heap_array_buffers_.push_back(buffer); new_off_heap_array_buffers_.push_back(buffer);
} else { } else {
buffer->set_backing_store(nullptr); buffer->set_backing_store(nullptr);
......
...@@ -259,9 +259,9 @@ class SerializerDeserializer : public RootVisitor { ...@@ -259,9 +259,9 @@ class SerializerDeserializer : public RootVisitor {
RootIndex>; RootIndex>;
using HotObject = BytecodeValueEncoder<kHotObject, 0, kHotObjectCount - 1>; using HotObject = BytecodeValueEncoder<kHotObject, 0, kHotObjectCount - 1>;
// This backing store reference value represents nullptr values during // This backing store reference value represents empty backing stores during
// serialization/deserialization. // serialization/deserialization.
static const uint32_t kNullRefSentinel = 0; static const uint32_t kEmptyBackingStoreRefSentinel = 0;
}; };
} // namespace internal } // namespace internal
......
...@@ -528,16 +528,16 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() { ...@@ -528,16 +528,16 @@ void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
int32_t byte_length = static_cast<int32_t>(buffer->byte_length()); int32_t byte_length = static_cast<int32_t>(buffer->byte_length());
ArrayBufferExtension* extension = buffer->extension(); ArrayBufferExtension* extension = buffer->extension();
// The embedder-allocated backing store only exists for the off-heap case. // Only serialize non-empty backing stores.
if (backing_store != nullptr) { if (buffer->IsEmpty()) {
buffer->SetBackingStoreRefForSerialization(kEmptyBackingStoreRefSentinel);
} else {
uint32_t ref = SerializeBackingStore(backing_store, byte_length); uint32_t ref = SerializeBackingStore(backing_store, byte_length);
buffer->SetBackingStoreRefForSerialization(ref); buffer->SetBackingStoreRefForSerialization(ref);
// Ensure deterministic output by setting extension to null during // Ensure deterministic output by setting extension to null during
// serialization. // serialization.
buffer->set_extension(nullptr); buffer->set_extension(nullptr);
} else {
buffer->SetBackingStoreRefForSerialization(kNullRefSentinel);
} }
SerializeObject(); SerializeObject();
......
...@@ -1217,7 +1217,7 @@ UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) { ...@@ -1217,7 +1217,7 @@ UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) {
i::Handle<i::JSArrayBuffer> buffer = i::Handle<i::JSArrayBuffer> buffer =
GetBufferFromTypedArray(CompileRun("x")); GetBufferFromTypedArray(CompileRun("x"));
// The resulting buffer should be on-heap. // The resulting buffer should be on-heap.
CHECK_NULL(buffer->backing_store()); CHECK(buffer->IsEmpty());
creator.SetDefaultContext( creator.SetDefaultContext(
context, v8::SerializeInternalFieldsCallback( context, v8::SerializeInternalFieldsCallback(
SerializeInternalFields, reinterpret_cast<void*>(2016))); SerializeInternalFields, reinterpret_cast<void*>(2016)));
...@@ -1244,14 +1244,14 @@ UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) { ...@@ -1244,14 +1244,14 @@ UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) {
i::Handle<i::JSArrayBuffer> buffer = i::Handle<i::JSArrayBuffer> buffer =
GetBufferFromTypedArray(CompileRun("x")); GetBufferFromTypedArray(CompileRun("x"));
// The resulting buffer should be on-heap. // The resulting buffer should be on-heap.
CHECK_NULL(buffer->backing_store()); CHECK(buffer->IsEmpty());
buffer = GetBufferFromTypedArray(CompileRun("y")); buffer = GetBufferFromTypedArray(CompileRun("y"));
CHECK_NULL(buffer->backing_store()); CHECK(buffer->IsEmpty());
buffer = GetBufferFromTypedArray(CompileRun("z")); buffer = GetBufferFromTypedArray(CompileRun("z"));
// The resulting buffer should be off-heap. // The resulting buffer should be off-heap.
CHECK_NOT_NULL(buffer->backing_store()); CHECK(!buffer->IsEmpty());
} }
isolate->Dispose(); isolate->Dispose();
delete[] blob.data; // We can dispose of the snapshot blob now. delete[] blob.data; // We can dispose of the snapshot blob now.
......
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