| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/snapshot/deserializer.h" |
| |
| #include <inttypes.h> |
| |
| #include "src/base/logging.h" |
| #include "src/codegen/assembler-inl.h" |
| #include "src/codegen/reloc-info-inl.h" |
| #include "src/common/assert-scope.h" |
| #include "src/common/globals.h" |
| #include "src/execution/isolate.h" |
| #include "src/handles/global-handles-inl.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/heap-write-barrier-inl.h" |
| #include "src/heap/heap-write-barrier.h" |
| #include "src/heap/heap.h" |
| #include "src/heap/local-heap-inl.h" |
| #include "src/logging/local-logger.h" |
| #include "src/logging/log.h" |
| #include "src/objects/backing-store.h" |
| #include "src/objects/js-array-buffer-inl.h" |
| #include "src/objects/maybe-object.h" |
| #include "src/objects/objects-body-descriptors-inl.h" |
| #include "src/objects/objects.h" |
| #include "src/objects/slots.h" |
| #include "src/objects/string.h" |
| #include "src/roots/roots.h" |
| #include "src/sandbox/js-dispatch-table-inl.h" |
| #include "src/snapshot/embedded/embedded-data-inl.h" |
| #include "src/snapshot/references.h" |
| #include "src/snapshot/serializer-deserializer.h" |
| #include "src/snapshot/shared-heap-serializer.h" |
| #include "src/snapshot/snapshot-data.h" |
| #include "src/utils/memcopy.h" |
| |
| // Has to be the last include (doesn't have include guards) |
| #include "src/objects/object-macros.h" |
| |
| namespace v8::internal { |
| |
| #ifdef V8_COMPRESS_POINTERS |
| #define PRIxTAGGED PRIx32 |
| #else |
| #define PRIxTAGGED PRIxPTR |
| #endif |
| |
| // A SlotAccessor for a slot in a HeapObject, which abstracts the slot |
| // operations done by the deserializer in a way which is GC-safe. In particular, |
| // rather than an absolute slot address, this accessor holds a Handle to the |
| // HeapObject, which is updated if the HeapObject moves. |
| class SlotAccessorForHeapObject { |
| public: |
| static SlotAccessorForHeapObject ForSlotIndex(Handle<HeapObject> object, |
| int index) { |
| return SlotAccessorForHeapObject(object, index * kTaggedSize); |
| } |
| static SlotAccessorForHeapObject ForSlotOffset(Handle<HeapObject> object, |
| int offset) { |
| return SlotAccessorForHeapObject(object, offset); |
| } |
| |
| MaybeObjectSlot slot() const { return object_->RawMaybeWeakField(offset_); } |
| ExternalPointerSlot external_pointer_slot(ExternalPointerTag tag) const { |
| return object_->RawExternalPointerField(offset_, tag); |
| } |
| Handle<HeapObject> object() const { return object_; } |
| int offset() const { return offset_; } |
| |
| // Writes the given value to this slot, with an offset (e.g. for repeat |
| // writes). Returns the number of slots written (which is one). |
| int Write(Tagged<MaybeObject> value, int slot_offset, WriteBarrierMode mode) { |
| MaybeObjectSlot current_slot = slot() + slot_offset; |
| current_slot.Relaxed_Store(value); |
| #ifdef V8_STATIC_ROOTS_BOOL |
| if (mode != SKIP_WRITE_BARRIER && FastInReadOnlySpaceOrSmallSmi(value)) { |
| // TODO(jgruber): Remove this once WriteBarrier::ForValue() contains the |
| // same check. |
| mode = SKIP_WRITE_BARRIER; |
| } |
| #endif // V8_STATIC_ROOTS_BOOL |
| WriteBarrier::ForValue(*object_, current_slot, value, mode); |
| return 1; |
| } |
| int Write(Tagged<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| return Write(Tagged<HeapObjectReference>(value, ref_type), slot_offset, |
| mode); |
| } |
| int Write(DirectHandle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| return Write(*value, ref_type, slot_offset, mode); |
| } |
| |
| int WriteIndirectPointerTo(Tagged<HeapObject> value, WriteBarrierMode mode) { |
| // Only ExposedTrustedObjects can be referenced via indirect pointers, so |
| // we must have one of these objects here. See the comments in |
| // trusted-object.h for more details. |
| DCHECK(IsExposedTrustedObject(value)); |
| Tagged<ExposedTrustedObject> object = |
| TrustedCast<ExposedTrustedObject>(value); |
| |
| InstanceType instance_type = value->map()->instance_type(); |
| bool shared = HeapLayout::InAnySharedSpace(value); |
| IndirectPointerTag tag = |
| IndirectPointerTagFromInstanceType(instance_type, shared); |
| IndirectPointerSlot dest = object_->RawIndirectPointerField(offset_, tag); |
| dest.store(object); |
| |
| WriteBarrier::ForIndirectPointer(*object_, dest, value, mode); |
| return 1; |
| } |
| |
| int WriteProtectedPointerTo(Tagged<TrustedObject> value, |
| WriteBarrierMode mode) { |
| DCHECK(IsTrustedObject(*object_)); |
| Tagged<TrustedObject> host = TrustedCast<TrustedObject>(*object_); |
| ProtectedPointerSlot dest = host->RawProtectedPointerField(offset_); |
| dest.store(value); |
| WriteBarrier::ForProtectedPointer(host, dest, value, mode); |
| return 1; |
| } |
| |
| private: |
| SlotAccessorForHeapObject(Handle<HeapObject> object, int offset) |
| : object_(object), offset_(offset) {} |
| |
| const Handle<HeapObject> object_; |
| const int offset_; |
| }; |
| |
| // A SlotAccessor for absolute full slot addresses. |
| class SlotAccessorForRootSlots { |
| public: |
| explicit SlotAccessorForRootSlots(FullMaybeObjectSlot slot) : slot_(slot) {} |
| |
| FullMaybeObjectSlot slot() const { return slot_; } |
| ExternalPointerSlot external_pointer_slot(ExternalPointerTag tag) const { |
| UNREACHABLE(); |
| } |
| Handle<HeapObject> object() const { UNREACHABLE(); } |
| int offset() const { UNREACHABLE(); } |
| |
| // Writes the given value to this slot, with an offset (e.g. for repeat |
| // writes). Returns the number of slots written (which is one). |
| int Write(Tagged<MaybeObject> value, int slot_offset, WriteBarrierMode mode) { |
| FullMaybeObjectSlot current_slot = slot() + slot_offset; |
| current_slot.Relaxed_Store(value); |
| return 1; |
| } |
| int Write(Tagged<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| return Write(Tagged<HeapObjectReference>(value, ref_type), slot_offset, |
| mode); |
| } |
| int Write(DirectHandle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| return Write(*value, ref_type, slot_offset, mode); |
| } |
| int WriteIndirectPointerTo(Tagged<HeapObject> value, WriteBarrierMode mode) { |
| UNREACHABLE(); |
| } |
| int WriteProtectedPointerTo(Tagged<TrustedObject> value, |
| WriteBarrierMode mode) { |
| UNREACHABLE(); |
| } |
| |
| private: |
| const FullMaybeObjectSlot slot_; |
| }; |
| |
| // A SlotAccessor for creating a Handle, which saves a Handle allocation when |
| // a Handle already exists. |
| template <typename IsolateT> |
| class SlotAccessorForHandle { |
| public: |
| SlotAccessorForHandle(DirectHandle<HeapObject>* handle, IsolateT* isolate) |
| : handle_(handle), isolate_(isolate) {} |
| |
| MaybeObjectSlot slot() const { UNREACHABLE(); } |
| ExternalPointerSlot external_pointer_slot(ExternalPointerTag tag) const { |
| UNREACHABLE(); |
| } |
| Handle<HeapObject> object() const { UNREACHABLE(); } |
| int offset() const { UNREACHABLE(); } |
| |
| int Write(Tagged<MaybeObject> value, int slot_offset, WriteBarrierMode mode) { |
| UNREACHABLE(); |
| } |
| int Write(Tagged<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| DCHECK_EQ(slot_offset, 0); |
| DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); |
| *handle_ = direct_handle(value, isolate_); |
| return 1; |
| } |
| int Write(DirectHandle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset, WriteBarrierMode mode) { |
| DCHECK_EQ(slot_offset, 0); |
| DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); |
| *handle_ = value; |
| return 1; |
| } |
| int WriteIndirectPointerTo(Tagged<HeapObject> value, WriteBarrierMode mode) { |
| UNREACHABLE(); |
| } |
| int WriteProtectedPointerTo(Tagged<TrustedObject> value, |
| WriteBarrierMode mode) { |
| UNREACHABLE(); |
| } |
| |
| private: |
| DirectHandle<HeapObject>* handle_; |
| IsolateT* isolate_; |
| }; |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::WriteHeapPointer(SlotAccessor slot_accessor, |
| Tagged<HeapObject> heap_object, |
| ReferenceDescriptor descr, |
| WriteBarrierMode mode) { |
| if (descr.is_indirect_pointer) { |
| return slot_accessor.WriteIndirectPointerTo(heap_object, mode); |
| } else { |
| return slot_accessor.Write(heap_object, descr.type, 0, mode); |
| } |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::WriteHeapPointer( |
| SlotAccessor slot_accessor, DirectHandle<HeapObject> heap_object, |
| ReferenceDescriptor descr, WriteBarrierMode mode) { |
| if (descr.is_indirect_pointer) { |
| return slot_accessor.WriteIndirectPointerTo(*heap_object, mode); |
| } else if (descr.is_protected_pointer) { |
| DCHECK(IsTrustedObject(*heap_object)); |
| return slot_accessor.WriteProtectedPointerTo( |
| TrustedCast<TrustedObject>(*heap_object), mode); |
| } else { |
| return slot_accessor.Write(heap_object, descr.type, 0, mode); |
| } |
| } |
| |
| template <typename IsolateT> |
| int Deserializer<IsolateT>::WriteExternalPointer(Tagged<HeapObject> host, |
| ExternalPointerSlot dest, |
| Address value, |
| ExternalPointerTag tag) { |
| DCHECK(!next_reference_is_weak_ && !next_reference_is_indirect_pointer_ && |
| !next_reference_is_protected_pointer); |
| |
| #ifdef V8_ENABLE_SANDBOX |
| ExternalPointerTable::ManagedResource* managed_resource = nullptr; |
| ExternalPointerTable* owning_table = nullptr; |
| ExternalPointerHandle original_handle = kNullExternalPointerHandle; |
| if (IsManagedExternalPointerType(tag)) { |
| // This can currently only happen during snapshot stress mode as we cannot |
| // normally serialized managed resources. In snapshot stress mode, the new |
| // isolate will be destroyed and the old isolate (really, the old isolate's |
| // external pointer table) therefore effectively retains ownership of the |
| // resource. As such, we need to save and restore the relevant fields of |
| // the external resource. Once the external pointer table itself destroys |
| // the managed resource when freeing the corresponding table entry, this |
| // workaround can be removed again. |
| DCHECK(v8_flags.stress_snapshot); |
| managed_resource = |
| reinterpret_cast<ExternalPointerTable::ManagedResource*>(value); |
| owning_table = managed_resource->owning_table_; |
| original_handle = managed_resource->ept_entry_; |
| managed_resource->owning_table_ = nullptr; |
| managed_resource->ept_entry_ = kNullExternalPointerHandle; |
| } |
| #endif // V8_ENABLE_SANDBOX |
| |
| if (tag == kExternalPointerNullTag && value == kNullAddress) { |
| dest.init_lazily_initialized(); |
| } else { |
| dest.init(main_thread_isolate(), host, value, tag); |
| } |
| |
| #ifdef V8_ENABLE_SANDBOX |
| if (managed_resource) { |
| managed_resource->owning_table_ = owning_table; |
| managed_resource->ept_entry_ = original_handle; |
| } |
| #endif // V8_ENABLE_SANDBOX |
| |
| // ExternalPointers can only be written into HeapObject fields, therefore they |
| // cover (kExternalPointerSlotSize / kTaggedSize) slots. |
| return (kExternalPointerSlotSize / kTaggedSize); |
| } |
| |
| namespace { |
| #ifdef DEBUG |
| int GetNumApiReferences(Isolate* isolate) { |
| int num_api_references = 0; |
| // The read-only deserializer is run by read-only heap set-up before the |
| // heap is fully set up. External reference table relies on a few parts of |
| // this set-up (like old-space), so it may be uninitialized at this point. |
| if (isolate->isolate_data()->external_reference_table()->is_initialized()) { |
| // Count the number of external references registered through the API. |
| if (isolate->api_external_references() != nullptr) { |
| while (isolate->api_external_references()[num_api_references] != 0) { |
| num_api_references++; |
| } |
| } |
| } |
| return num_api_references; |
| } |
| int GetNumApiReferences(LocalIsolate* isolate) { return 0; } |
| #endif |
| } // namespace |
| |
| template <typename IsolateT> |
| Deserializer<IsolateT>::Deserializer(IsolateT* isolate, |
| base::Vector<const uint8_t> payload, |
| uint32_t magic_number, |
| bool deserializing_user_code, |
| bool can_rehash) |
| : isolate_(isolate), |
| attached_objects_(isolate), |
| source_(payload), |
| magic_number_(magic_number), |
| new_maps_(isolate), |
| new_allocation_sites_(isolate), |
| new_instruction_stream_objects_(isolate), |
| accessor_infos_(isolate), |
| interceptor_infos_(isolate), |
| function_template_infos_(isolate), |
| new_scripts_(isolate), |
| new_descriptor_arrays_(isolate->heap()), |
| deserializing_user_code_(deserializing_user_code), |
| should_rehash_((v8_flags.rehash_snapshot && can_rehash) || |
| deserializing_user_code), |
| to_rehash_(isolate) { |
| DCHECK_NOT_NULL(isolate); |
| isolate->RegisterDeserializerStarted(); |
| |
| // We start the indices here at 1, so that we can distinguish between an |
| // actual index and an empty backing store (serialized as |
| // kEmptyBackingStoreRefSentinel) in a deserialized object requiring fix-up. |
| static_assert(kEmptyBackingStoreRefSentinel == 0); |
| backing_stores_.push_back({}); |
| |
| back_refs_.reserve(2048); |
| js_dispatch_entries_.reserve(512); |
| |
| #ifdef DEBUG |
| num_api_references_ = GetNumApiReferences(isolate); |
| #endif // DEBUG |
| CHECK_EQ(magic_number_, SerializedData::kMagicNumber); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::Rehash() { |
| DCHECK(should_rehash()); |
| for (DirectHandle<HeapObject> item : to_rehash_) { |
| item->RehashBasedOnMap(isolate()); |
| } |
| } |
| |
| template <typename IsolateT> |
| Deserializer<IsolateT>::~Deserializer() { |
| #ifdef DEBUG |
| // Do not perform checks if we aborted deserialization. |
| if (source_.position() == 0) return; |
| // Check that we only have padding bytes remaining. |
| while (source_.HasMore()) DCHECK_EQ(kNop, source_.Get()); |
| // Check that there are no remaining forward refs. |
| DCHECK_EQ(num_unresolved_forward_refs_, 0); |
| DCHECK(unresolved_forward_refs_.empty()); |
| #endif // DEBUG |
| isolate_->RegisterDeserializerFinished(); |
| } |
| |
| // This is called on the roots. It is the driver of the deserialization |
| // process. It is also called on the body of each function. |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::VisitRootPointers(Root root, |
| const char* description, |
| FullObjectSlot start, |
| FullObjectSlot end) { |
| ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end)); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::Synchronize(VisitorSynchronization::SyncTag tag) { |
| static const uint8_t expected = kSynchronize; |
| CHECK_EQ(expected, source_.Get()); |
| if (v8_flags.trace_deserialization) { |
| const char* name; |
| switch (tag) { |
| #define CASE(ID, NAME) \ |
| case VisitorSynchronization::ID: \ |
| name = NAME; \ |
| break; |
| ROOT_ID_LIST(CASE) |
| #undef CASE |
| default: |
| name = "(!unknown!)"; |
| break; |
| } |
| PrintF("Synchronize %d %s\n", tag, name); |
| } |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::DeserializeDeferredObjects() { |
| if (v8_flags.trace_deserialization) { |
| PrintF("-- Deferred objects\n"); |
| } |
| for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) { |
| SnapshotSpace space = NewObject::Decode(code); |
| ReadObject(space); |
| } |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::LogNewMapEvents() { |
| if (V8_LIKELY(!v8_flags.log_maps)) return; |
| DisallowGarbageCollection no_gc; |
| for (DirectHandle<Map> map : new_maps_) { |
| DCHECK(v8_flags.log_maps); |
| LOG(isolate(), MapCreate(*map)); |
| LOG(isolate(), MapDetails(*map)); |
| } |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::WeakenDescriptorArrays() { |
| isolate()->heap()->WeakenDescriptorArrays(std::move(new_descriptor_arrays_)); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::LogScriptEvents(Tagged<Script> script) { |
| DisallowGarbageCollection no_gc; |
| LOG(isolate(), ScriptEvent(ScriptEventType::kDeserialize, script->id())); |
| LOG(isolate(), ScriptDetails(script)); |
| } |
| |
| namespace { |
| template <typename IsolateT> |
| uint32_t ComputeRawHashField(IsolateT* isolate, Tagged<String> string) { |
| // Make sure raw_hash_field() is computed. |
| string->EnsureHash(SharedStringAccessGuardIfNeeded(isolate)); |
| return string->raw_hash_field(); |
| } |
| } // namespace |
| |
| StringTableInsertionKey::StringTableInsertionKey( |
| Isolate* isolate, DirectHandle<String> string, |
| DeserializingUserCodeOption deserializing_user_code) |
| : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), |
| string_(string) { |
| #ifdef DEBUG |
| deserializing_user_code_ = deserializing_user_code; |
| #endif |
| DCHECK(IsInternalizedString(*string)); |
| } |
| |
| StringTableInsertionKey::StringTableInsertionKey( |
| LocalIsolate* isolate, DirectHandle<String> string, |
| DeserializingUserCodeOption deserializing_user_code) |
| : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), |
| string_(string) { |
| #ifdef DEBUG |
| deserializing_user_code_ = deserializing_user_code; |
| #endif |
| DCHECK(IsInternalizedString(*string)); |
| } |
| |
| template <typename IsolateT> |
| bool StringTableInsertionKey::IsMatch(IsolateT* isolate, |
| Tagged<String> string) { |
| // We want to compare the content of two strings here. |
| return string_->SlowEquals(string, SharedStringAccessGuardIfNeeded(isolate)); |
| } |
| template bool StringTableInsertionKey::IsMatch(Isolate* isolate, |
| Tagged<String> string); |
| template bool StringTableInsertionKey::IsMatch(LocalIsolate* isolate, |
| Tagged<String> string); |
| |
| namespace { |
| |
| void NoExternalReferencesCallback() { |
| // The following check will trigger if a function or object template |
| // with references to native functions have been deserialized from |
| // snapshot, but no actual external references were provided when the |
| // isolate was created. |
| FATAL("No external references provided via API"); |
| } |
| |
| void PostProcessExternalString(Tagged<ExternalString> string, |
| Isolate* isolate) { |
| DisallowGarbageCollection no_gc; |
| uint32_t index = string->GetResourceRefForDeserialization(); |
| // Our (sandbox) fuzzers can sometimes get here by mutating an in-sandbox |
| // object after deserialization but before post-processing, and making it |
| // look like an ExternalString. In that case, the Isolate may not have any |
| // external references and this CHECK then avoids false-positive crashes. |
| // Technically we should probably also check that the index is in-bounds if |
| // we do have external references on the Isolate, but in our current fuzzer |
| // setup, this doesn't seem to be the case. |
| CHECK_NE(isolate->api_external_references(), nullptr); |
| Address address = |
| static_cast<Address>(isolate->api_external_references()[index]); |
| string->InitExternalPointerFields(isolate); |
| string->set_address_as_resource(isolate, address); |
| isolate->heap()->UpdateExternalString(string, 0, |
| string->ExternalPayloadSize()); |
| isolate->heap()->RegisterExternalString(string); |
| } |
| |
| } // namespace |
| |
| // Should be called only on the main thread (not thread safe). |
| template <> |
| void Deserializer<Isolate>::PostProcessNewJSReceiver( |
| Tagged<Map> map, DirectHandle<JSReceiver> obj, InstanceType instance_type, |
| SnapshotSpace space) { |
| DCHECK_EQ(map->instance_type(), instance_type); |
| |
| if (InstanceTypeChecker::IsJSDataView(instance_type) || |
| InstanceTypeChecker::IsJSRabGsabDataView(instance_type)) { |
| auto data_view = Cast<JSDataViewOrRabGsabDataView>(*obj); |
| auto buffer = Cast<JSArrayBuffer>(data_view->buffer()); |
| if (buffer->was_detached()) { |
| // Directly set the data pointer to point to the EmptyBackingStoreBuffer. |
| // Otherwise, we might end up setting it to EmptyBackingStoreBuffer() + |
| // byte_offset() which would result in an invalid pointer. |
| data_view->set_data_pointer(main_thread_isolate(), |
| EmptyBackingStoreBuffer()); |
| } else { |
| void* backing_store = buffer->backing_store(); |
| data_view->set_data_pointer( |
| main_thread_isolate(), |
| reinterpret_cast<uint8_t*>(backing_store) + data_view->byte_offset()); |
| } |
| } else if (InstanceTypeChecker::IsJSTypedArray(instance_type)) { |
| auto typed_array = Cast<JSTypedArray>(*obj); |
| // Note: ByteArray objects must not be deferred s.t. they are |
| // available here for is_on_heap(). See also: CanBeDeferred. |
| // Fixup typed array pointers. |
| if (typed_array->is_on_heap()) { |
| typed_array->AddExternalPointerCompensationForDeserialization( |
| main_thread_isolate()); |
| } else { |
| // Serializer writes backing store ref as a DataPtr() value. |
| uint32_t store_index = |
| typed_array->GetExternalBackingStoreRefForDeserialization(); |
| auto backing_store = backing_stores_[store_index]; |
| if (backing_store && backing_store->buffer_start()) { |
| typed_array->SetOffHeapDataPtr(main_thread_isolate(), |
| backing_store->buffer_start(), |
| typed_array->byte_offset()); |
| } else { |
| // Directly set the data pointer to point to the |
| // EmptyBackingStoreBuffer. Otherwise, we might end up setting it to |
| // EmptyBackingStoreBuffer() + byte_offset() which would result in an |
| // invalid pointer. |
| typed_array->SetOffHeapDataPtr(main_thread_isolate(), |
| EmptyBackingStoreBuffer(), 0); |
| } |
| } |
| } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) { |
| auto buffer = Cast<JSArrayBuffer>(*obj); |
| uint32_t store_index = buffer->GetBackingStoreRefForDeserialization(); |
| buffer->init_extension(); |
| if (store_index == kEmptyBackingStoreRefSentinel) { |
| buffer->set_backing_store(main_thread_isolate(), |
| EmptyBackingStoreBuffer()); |
| } else { |
| auto bs = backing_store(store_index); |
| SharedFlag shared = |
| bs && bs->is_shared() ? SharedFlag::kShared : SharedFlag::kNotShared; |
| DCHECK_IMPLIES(bs, |
| buffer->is_resizable_by_js() == bs->is_resizable_by_js()); |
| ResizableFlag resizable = bs && bs->is_resizable_by_js() |
| ? ResizableFlag::kResizable |
| : ResizableFlag::kNotResizable; |
| buffer->Setup(shared, resizable, bs, main_thread_isolate(), |
| buffer->views()); |
| } |
| } else if (InstanceTypeChecker::IsJSDate(instance_type)) { |
| Cast<JSDate>(*obj)->UpdateFieldsAfterDeserialization(main_thread_isolate()); |
| } |
| } |
| |
| template <> |
| void Deserializer<LocalIsolate>::PostProcessNewJSReceiver( |
| Tagged<Map> map, DirectHandle<JSReceiver> obj, InstanceType instance_type, |
| SnapshotSpace space) { |
| UNREACHABLE(); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::PostProcessNewObject(DirectHandle<Map> map, |
| Handle<HeapObject> obj, |
| SnapshotSpace space) { |
| DisallowGarbageCollection no_gc; |
| Tagged<Map> raw_map = *map; |
| DCHECK_EQ(raw_map, obj->map(isolate_)); |
| InstanceType instance_type = raw_map->instance_type(); |
| Tagged<HeapObject> raw_obj = *obj; |
| DCHECK_IMPLIES(deserializing_user_code(), should_rehash()); |
| if (should_rehash()) { |
| if (InstanceTypeChecker::IsString(instance_type)) { |
| // Uninitialize hash field as we need to recompute the hash. |
| Tagged<String> string = Cast<String>(raw_obj); |
| string->set_raw_hash_field(String::kEmptyHashField); |
| // Rehash strings before read-only space is sealed. Strings outside |
| // read-only space are rehashed lazily. (e.g. when rehashing dictionaries) |
| if (space == SnapshotSpace::kReadOnlyHeap) { |
| PushObjectToRehash(obj); |
| } |
| } else if (raw_obj->NeedsRehashing(instance_type)) { |
| PushObjectToRehash(obj); |
| } |
| |
| if (deserializing_user_code()) { |
| if (InstanceTypeChecker::IsInternalizedString(instance_type)) { |
| // Canonicalize the internalized string. If it already exists in the |
| // string table, set the string to point to the existing one and patch |
| // the deserialized string handle to point to the existing one. |
| // TODO(leszeks): This handle patching is ugly, consider adding an |
| // explicit internalized string bytecode. Also, the new thin string |
| // should be dead, try immediately freeing it. |
| DirectHandle<String> string = Cast<String>(obj); |
| |
| StringTableInsertionKey key( |
| isolate(), string, |
| DeserializingUserCodeOption::kIsDeserializingUserCode); |
| Tagged<String> result = |
| *isolate()->string_table()->LookupKey(isolate(), &key); |
| |
| if (result != raw_obj) { |
| Cast<String>(raw_obj)->MakeThin(isolate(), result); |
| // Mutate the given object handle so that the backreference entry is |
| // also updated. |
| obj.PatchValue(result); |
| } |
| return; |
| } else if (InstanceTypeChecker::IsScript(instance_type)) { |
| new_scripts_.push_back(Cast<Script>(obj)); |
| } else if (InstanceTypeChecker::IsAllocationSite(instance_type)) { |
| // We should link new allocation sites, but we can't do this immediately |
| // because |AllocationSite::HasWeakNext()| internally accesses |
| // |Heap::roots_| that may not have been initialized yet. So defer this |
| // to |ObjectDeserializer::CommitPostProcessedObjects()|. |
| new_allocation_sites_.push_back(Cast<AllocationSite>(obj)); |
| } else { |
| // We dont defer ByteArray because JSTypedArray needs the base_pointer |
| // ByteArray immediately if it's on heap. |
| DCHECK(CanBeDeferred(*obj, SlotType::kAnySlot) || |
| InstanceTypeChecker::IsByteArray(instance_type)); |
| } |
| } |
| } |
| |
| if (InstanceTypeChecker::IsInstructionStream(instance_type)) { |
| // We flush all code pages after deserializing the startup snapshot. |
| // Hence we only remember each individual code object when deserializing |
| // user code. |
| if (deserializing_user_code()) { |
| new_instruction_stream_objects_.push_back( |
| TrustedCast<InstructionStream>(obj)); |
| } |
| } else if (InstanceTypeChecker::IsCode(instance_type)) { |
| Tagged<Code> code = TrustedCast<Code>(raw_obj); |
| if (!code->has_instruction_stream()) { |
| code->SetInstructionStartForOffHeapBuiltin( |
| main_thread_isolate(), EmbeddedData::FromBlob(main_thread_isolate()) |
| .InstructionStartOf(code->builtin_id())); |
| } else { |
| code->UpdateInstructionStart(main_thread_isolate(), |
| code->instruction_stream()); |
| } |
| } else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) { |
| Tagged<SharedFunctionInfo> sfi = Cast<SharedFunctionInfo>(raw_obj); |
| // Reset the id to avoid collisions - it must be unique in this isolate. |
| sfi->set_unique_id(isolate()->GetAndIncNextUniqueSfiId()); |
| } else if (InstanceTypeChecker::IsMap(instance_type)) { |
| if (v8_flags.log_maps) { |
| // Keep track of all seen Maps to log them later since they might be only |
| // partially initialized at this point. |
| new_maps_.push_back(Cast<Map>(obj)); |
| } |
| } else if (USE_SIMULATOR_BOOL && |
| InstanceTypeChecker::IsAccessorInfo(instance_type)) { |
| accessor_infos_.push_back(Cast<AccessorInfo>(obj)); |
| } else if (USE_SIMULATOR_BOOL && |
| InstanceTypeChecker::IsInterceptorInfo(instance_type)) { |
| interceptor_infos_.push_back(Cast<InterceptorInfo>(obj)); |
| } else if (USE_SIMULATOR_BOOL && |
| InstanceTypeChecker::IsFunctionTemplateInfo(instance_type)) { |
| function_template_infos_.push_back(Cast<FunctionTemplateInfo>(obj)); |
| } else if (InstanceTypeChecker::IsExternalString(instance_type)) { |
| PostProcessExternalString(Cast<ExternalString>(raw_obj), |
| main_thread_isolate()); |
| } else if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // PostProcessNewJSReceiver may trigger GC. |
| no_gc.Release(); |
| return PostProcessNewJSReceiver(raw_map, Cast<JSReceiver>(obj), |
| instance_type, space); |
| } else if (InstanceTypeChecker::IsDescriptorArray(instance_type)) { |
| DCHECK(InstanceTypeChecker::IsStrongDescriptorArray(instance_type)); |
| auto descriptors = Cast<DescriptorArray>(obj); |
| new_descriptor_arrays_.Push(*descriptors); |
| } else if (InstanceTypeChecker::IsNativeContext(instance_type)) { |
| Tagged<NativeContext> context = Cast<NativeContext>(raw_obj); |
| context->init_microtask_queue(main_thread_isolate(), nullptr); |
| main_thread_isolate()->heap()->AddToWeakNativeContextList(context); |
| } else if (InstanceTypeChecker::IsScript(instance_type)) { |
| LogScriptEvents(Cast<Script>(*obj)); |
| } |
| } |
| |
| template <typename IsolateT> |
| typename Deserializer<IsolateT>::ReferenceDescriptor |
| Deserializer<IsolateT>::GetAndResetNextReferenceDescriptor() { |
| DCHECK(!(next_reference_is_weak_ && next_reference_is_indirect_pointer_)); |
| ReferenceDescriptor desc; |
| desc.type = next_reference_is_weak_ ? HeapObjectReferenceType::WEAK |
| : HeapObjectReferenceType::STRONG; |
| next_reference_is_weak_ = false; |
| desc.is_indirect_pointer = next_reference_is_indirect_pointer_; |
| next_reference_is_indirect_pointer_ = false; |
| desc.is_protected_pointer = next_reference_is_protected_pointer; |
| next_reference_is_protected_pointer = false; |
| return desc; |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::GetBackReferencedObject() { |
| return GetBackReferencedObject(source_.GetUint30()); |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::GetBackReferencedObject( |
| uint32_t index) { |
| Handle<HeapObject> obj = back_refs_[index]; |
| |
| // We don't allow ThinStrings in backreferences -- if internalization produces |
| // a thin string, then it should also update the backref handle. |
| DCHECK(!IsThinString(*obj, isolate())); |
| |
| hot_objects_.Add(obj); |
| DCHECK(!HasWeakHeapObjectTag(*obj)); |
| return obj; |
| } |
| |
| template <typename IsolateT> |
| DirectHandle<HeapObject> Deserializer<IsolateT>::ReadObject() { |
| DirectHandle<HeapObject> ret; |
| CHECK_EQ(ReadSingleBytecodeData( |
| source_.Get(), SlotAccessorForHandle<IsolateT>(&ret, isolate())), |
| 1); |
| return ret; |
| } |
| |
| namespace { |
| AllocationType SpaceToAllocation(SnapshotSpace space) { |
| switch (space) { |
| case SnapshotSpace::kCode: |
| return AllocationType::kCode; |
| case SnapshotSpace::kOld: |
| return AllocationType::kOld; |
| case SnapshotSpace::kReadOnlyHeap: |
| return AllocationType::kReadOnly; |
| case SnapshotSpace::kTrusted: |
| return AllocationType::kTrusted; |
| } |
| } |
| } // namespace |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::ReadObject(SnapshotSpace space) { |
| const int size_in_tagged = source_.GetUint30(); |
| const int size_in_bytes = size_in_tagged * kTaggedSize; |
| |
| // The map can't be a forward ref. If you want the map to be a forward ref, |
| // then you're probably serializing the meta-map, in which case you want to |
| // use the kNewContextlessMetaMap/kNewContextfulMetaMap bytecode. |
| DCHECK_NE(source()->Peek(), kRegisterPendingForwardRef); |
| DirectHandle<Map> map = Cast<Map>(ReadObject()); |
| |
| AllocationType allocation = SpaceToAllocation(space); |
| |
| // When sharing a string table, all in-place internalizable and internalized |
| // strings internalized strings are allocated in the shared heap. |
| // |
| // TODO(12007): When shipping, add a new SharedOld SnapshotSpace. |
| if (v8_flags.shared_string_table) { |
| InstanceType instance_type = map->instance_type(); |
| if (InstanceTypeChecker::IsInternalizedString(instance_type) || |
| String::IsInPlaceInternalizable(instance_type)) { |
| allocation = isolate() |
| ->factory() |
| ->RefineAllocationTypeForInPlaceInternalizableString( |
| allocation, *map); |
| } |
| } |
| |
| // Filling an object's fields can cause GCs and heap walks, so this object has |
| // to be in a 'sufficiently initialised' state by the time the next allocation |
| // can happen. For this to be the case, the object is carefully deserialized |
| // as follows: |
| // * The space for the object is allocated. |
| // * The map is set on the object so that the GC knows what type the object |
| // has. |
| // * The rest of the object is filled with a fixed Smi value |
| // - This is a Smi so that tagged fields become initialized to a valid |
| // tagged value. |
| // - It's a fixed value, "Smi::uninitialized_deserialization_value()", so |
| // that we can DCHECK for it when reading objects that are assumed to be |
| // partially initialized objects. |
| // * The fields of the object are deserialized in order, under the |
| // assumption that objects are laid out in such a way that any fields |
| // required for object iteration (e.g. length fields) are deserialized |
| // before fields with objects. |
| // - We ensure this is the case by DCHECKing on object allocation that the |
| // previously allocated object has a valid size (see `Allocate`). |
| |
| const InSharedSpace in_shared_space = |
| IsSharedAllocationType(allocation) ? kInSharedSpace : kNotInSharedSpace; |
| Tagged<HeapObject> raw_obj = |
| Allocate(allocation, size_in_bytes, |
| HeapObject::RequiredAlignment(in_shared_space, *map)); |
| raw_obj->set_map_after_allocation(isolate_, *map); |
| MemsetTagged(raw_obj->RawField(kTaggedSize), |
| Smi::uninitialized_deserialization_value(), size_in_tagged - 1); |
| DCHECK(raw_obj->CheckRequiredAlignment(isolate())); |
| |
| // Make sure BytecodeArrays have a valid age, so that the marker doesn't |
| // break when making them older. |
| if (IsSharedFunctionInfo(raw_obj, isolate())) { |
| Cast<SharedFunctionInfo>(raw_obj)->set_age(0); |
| } else if (IsEphemeronHashTable(raw_obj)) { |
| // Make sure EphemeronHashTables have valid HeapObject keys, so that the |
| // marker does not break when marking EphemeronHashTable, see |
| // MarkingVisitorBase::VisitEphemeronHashTable. |
| Tagged<EphemeronHashTable> table = Cast<EphemeronHashTable>(raw_obj); |
| MemsetTagged(Cast<HeapObject>(table)->RawField(table->kElementsStartOffset), |
| ReadOnlyRoots(isolate()).undefined_value(), |
| (size_in_bytes - table->kElementsStartOffset) / kTaggedSize); |
| } |
| |
| #ifdef DEBUG |
| PtrComprCageBase cage_base(isolate()); |
| // We want to make sure that all embedder pointers are initialized to null. |
| if (IsJSObject(raw_obj, cage_base) && |
| Cast<JSObject>(raw_obj)->MayHaveEmbedderFields()) { |
| Tagged<JSObject> js_obj = Cast<JSObject>(raw_obj); |
| for (int i = 0; i < js_obj->GetEmbedderFieldCount(); ++i) { |
| void* pointer; |
| CHECK(EmbedderDataSlot(js_obj, i).DeprecatedToAlignedPointer( |
| main_thread_isolate(), &pointer)); |
| CHECK_NULL(pointer); |
| } |
| } else if (IsEmbedderDataArray(raw_obj, cage_base)) { |
| Tagged<EmbedderDataArray> array = Cast<EmbedderDataArray>(raw_obj); |
| EmbedderDataSlot start(array, 0); |
| EmbedderDataSlot end(array, array->length()); |
| for (EmbedderDataSlot slot = start; slot < end; ++slot) { |
| void* pointer; |
| CHECK(slot.DeprecatedToAlignedPointer(main_thread_isolate(), &pointer)); |
| CHECK_NULL(pointer); |
| } |
| } |
| #endif |
| |
| Handle<HeapObject> obj = handle(raw_obj, isolate()); |
| back_refs_.push_back(obj); |
| if (v8_flags.trace_deserialization) { |
| PrintF(" %*s(set obj backref %u)\n", depth_, "", |
| static_cast<int>(back_refs_.size() - 1)); |
| } |
| |
| ReadData(obj, 1, size_in_tagged); |
| PostProcessNewObject(map, obj, space); |
| |
| #ifdef DEBUG |
| if (IsInstructionStream(*obj, cage_base)) { |
| DCHECK(space == SnapshotSpace::kCode || |
| space == SnapshotSpace::kReadOnlyHeap); |
| } else { |
| DCHECK_NE(space, SnapshotSpace::kCode); |
| } |
| if (IsTrustedObject(*obj)) { |
| DCHECK_EQ(space, SnapshotSpace::kTrusted); |
| } else { |
| DCHECK_NE(space, SnapshotSpace::kTrusted); |
| } |
| #endif // DEBUG |
| |
| return obj; |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::ReadMetaMap(SnapshotSpace space) { |
| const int size_in_bytes = Map::kSize; |
| const int size_in_tagged = size_in_bytes / kTaggedSize; |
| |
| Tagged<HeapObject> raw_obj = |
| Allocate(SpaceToAllocation(space), size_in_bytes, kTaggedAligned); |
| raw_obj->set_map_after_allocation(isolate_, UncheckedCast<Map>(raw_obj)); |
| MemsetTagged(raw_obj->RawField(kTaggedSize), |
| Smi::uninitialized_deserialization_value(), size_in_tagged - 1); |
| DCHECK(raw_obj->CheckRequiredAlignment(isolate())); |
| |
| Handle<HeapObject> obj = handle(raw_obj, isolate()); |
| back_refs_.push_back(obj); |
| if (v8_flags.trace_deserialization) { |
| PrintF(" %*s(set obj backref %u)\n", depth_, "", |
| static_cast<int>(back_refs_.size() - 1)); |
| } |
| |
| // Set the instance-type manually, to allow backrefs to read it. |
| UncheckedCast<Map>(*obj)->set_instance_type(MAP_TYPE); |
| |
| ReadData(obj, 1, size_in_tagged); |
| PostProcessNewObject(Cast<Map>(obj), obj, space); |
| |
| return obj; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRepeatedRoot(SlotAccessor slot_accessor, |
| int repeat_count) { |
| CHECK_LE(2, repeat_count); |
| |
| uint8_t id = source_.Get(); |
| RootIndex root_index = static_cast<RootIndex>(id); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%s", RootsTable::name(root_index)); |
| } |
| DCHECK(RootsTable::IsReadOnly(root_index)); |
| |
| Tagged<HeapObject> heap_object = |
| Cast<HeapObject>(isolate()->root(root_index)); |
| |
| for (int i = 0; i < repeat_count; i++) { |
| slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG, i, |
| SKIP_WRITE_BARRIER); |
| } |
| return repeat_count; |
| } |
| |
| namespace { |
| |
| // Template used by the below CASE_RANGE macro to statically verify that the |
| // given number of cases matches the number of expected cases for that bytecode. |
| template <int byte_code_count, int expected> |
| constexpr uint8_t VerifyBytecodeCount(uint8_t bytecode) { |
| static_assert(byte_code_count == expected); |
| return bytecode; |
| } |
| |
| } // namespace |
| |
| // Helper macro (and its implementation detail) for specifying a range of cases. |
| // Use as "case CASE_RANGE(byte_code, num_bytecodes):" |
| #define CASE_RANGE(byte_code, num_bytecodes) \ |
| CASE_R##num_bytecodes( \ |
| (VerifyBytecodeCount<byte_code##Count, num_bytecodes>(byte_code))) |
| #define CASE_R1(byte_code) byte_code |
| #define CASE_R2(byte_code) CASE_R1(byte_code) : case CASE_R1(byte_code + 1) |
| #define CASE_R3(byte_code) CASE_R2(byte_code) : case CASE_R1(byte_code + 2) |
| #define CASE_R4(byte_code) CASE_R2(byte_code) : case CASE_R2(byte_code + 2) |
| #define CASE_R8(byte_code) CASE_R4(byte_code) : case CASE_R4(byte_code + 4) |
| #define CASE_R16(byte_code) CASE_R8(byte_code) : case CASE_R8(byte_code + 8) |
| #define CASE_R32(byte_code) CASE_R16(byte_code) : case CASE_R16(byte_code + 16) |
| |
| // This generates a case range for all the spaces. |
| // clang-format off |
| #define CASE_RANGE_ALL_SPACES(bytecode) \ |
| SpaceEncoder<bytecode>::Encode(SnapshotSpace::kOld): \ |
| case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kCode): \ |
| case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kReadOnlyHeap): \ |
| case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kTrusted) |
| // clang-format on |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::ReadData(Handle<HeapObject> object, |
| int start_slot_index, |
| int end_slot_index) { |
| int current = start_slot_index; |
| while (current < end_slot_index) { |
| uint8_t data = source_.Get(); |
| current += ReadSingleBytecodeData( |
| data, SlotAccessorForHeapObject::ForSlotIndex(object, current)); |
| } |
| CHECK_EQ(current, end_slot_index); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::ReadData(FullMaybeObjectSlot start, |
| FullMaybeObjectSlot end) { |
| FullMaybeObjectSlot current = start; |
| while (current < end) { |
| uint8_t data = source_.Get(); |
| current += ReadSingleBytecodeData(data, SlotAccessorForRootSlots(current)); |
| } |
| CHECK_EQ(current, end); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadSingleBytecodeData(uint8_t data, |
| SlotAccessor slot_accessor) { |
| if (v8_flags.trace_deserialization) { |
| PrintF("%02x ", data); |
| } |
| switch (data) { |
| case CASE_RANGE_ALL_SPACES(kNewObject): |
| return ReadNewObject(data, slot_accessor); |
| case kBackref: |
| return ReadBackref(data, slot_accessor); |
| case kReadOnlyHeapRef: |
| return ReadReadOnlyHeapRef(data, slot_accessor); |
| case kRootArray: |
| return ReadRootArray(data, slot_accessor); |
| case kStartupObjectCache: |
| return ReadStartupObjectCache(data, slot_accessor); |
| case kSharedHeapObjectCache: |
| return ReadSharedHeapObjectCache(data, slot_accessor); |
| case kNewContextlessMetaMap: |
| case kNewContextfulMetaMap: |
| return ReadNewMetaMap(data, slot_accessor); |
| case kSandboxedExternalReference: |
| case kExternalReference: |
| return ReadExternalReference(data, slot_accessor); |
| case kSandboxedRawExternalReference: |
| return ReadRawExternalReference(data, slot_accessor); |
| case kAttachedReference: |
| return ReadAttachedReference(data, slot_accessor); |
| case kNop: |
| return 0; |
| case kRegisterPendingForwardRef: |
| return ReadRegisterPendingForwardRef(data, slot_accessor); |
| case kResolvePendingForwardRef: |
| return ReadResolvePendingForwardRef(data, slot_accessor); |
| case kSynchronize: |
| // If we get here then that indicates that you have a mismatch between |
| // the number of GC roots when serializing and deserializing. |
| UNREACHABLE(); |
| case kVariableRawData: |
| return ReadVariableRawData(data, slot_accessor); |
| case kVariableRepeatRoot: |
| return ReadVariableRepeatRoot(data, slot_accessor); |
| case kOffHeapBackingStore: |
| case kOffHeapResizableBackingStore: |
| return ReadOffHeapBackingStore(data, slot_accessor); |
| case kSandboxedApiReference: |
| case kApiReference: |
| return ReadApiReference(data, slot_accessor); |
| case kClearedWeakReference: |
| return ReadClearedWeakReference(data, slot_accessor); |
| case kWeakPrefix: |
| return ReadWeakPrefix(data, slot_accessor); |
| case kIndirectPointerPrefix: |
| return ReadIndirectPointerPrefix(data, slot_accessor); |
| case kInitializeSelfIndirectPointer: |
| return ReadInitializeSelfIndirectPointer(data, slot_accessor); |
| case kAllocateJSDispatchEntry: |
| return ReadAllocateJSDispatchEntry(data, slot_accessor); |
| case kJSDispatchEntry: |
| return ReadJSDispatchEntry(data, slot_accessor); |
| case kProtectedPointerPrefix: |
| return ReadProtectedPointerPrefix(data, slot_accessor); |
| case CASE_RANGE(kRootArrayConstants, 32): |
| return ReadRootArrayConstants(data, slot_accessor); |
| case CASE_RANGE(kHotObject, 8): |
| return ReadHotObject(data, slot_accessor); |
| case CASE_RANGE(kFixedRawData, 32): |
| return ReadFixedRawData(data, slot_accessor); |
| case CASE_RANGE(kFixedRepeatRoot, 16): |
| return ReadFixedRepeatRoot(data, slot_accessor); |
| |
| #ifdef DEBUG |
| #define UNUSED_CASE(byte_code) \ |
| case byte_code: \ |
| UNREACHABLE(); |
| UNUSED_SERIALIZER_BYTE_CODES(UNUSED_CASE) |
| #endif |
| #undef UNUSED_CASE |
| } |
| |
| // The above switch, including UNUSED_SERIALIZER_BYTE_CODES, covers all |
| // possible bytecodes; but, clang doesn't realize this, so we have an explicit |
| // UNREACHABLE here too. |
| UNREACHABLE(); |
| } |
| |
| namespace { |
| const char* SnapshotSpaceName(SnapshotSpace space) { |
| switch (space) { |
| case SnapshotSpace::kReadOnlyHeap: |
| return "ReadOnlyHeap"; |
| case SnapshotSpace::kOld: |
| return "Old"; |
| case SnapshotSpace::kCode: |
| return "Code"; |
| case SnapshotSpace::kTrusted: |
| return "Trusted"; |
| } |
| return "(!unknown space!)"; |
| } |
| } // namespace |
| |
| // Deserialize a new object and write a pointer to it to the current |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadNewObject(uint8_t data, |
| SlotAccessor slot_accessor) { |
| SnapshotSpace space = NewObject::Decode(data); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sNewObject [%s]\n", depth_, "", SnapshotSpaceName(space)); |
| ++depth_; |
| } |
| DCHECK_IMPLIES(V8_STATIC_ROOTS_BOOL, space != SnapshotSpace::kReadOnlyHeap); |
| // Save the descriptor before recursing down into reading the object. |
| ReferenceDescriptor descr = GetAndResetNextReferenceDescriptor(); |
| DirectHandle<HeapObject> heap_object = ReadObject(space); |
| if (v8_flags.trace_deserialization) { |
| --depth_; |
| } |
| return WriteHeapPointer(slot_accessor, heap_object, descr); |
| } |
| |
| // Find a recently deserialized object using its offset from the current |
| // allocation point and write a pointer to it to the current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadBackref(uint8_t data, |
| SlotAccessor slot_accessor) { |
| uint32_t index = source_.GetUint30(); |
| DirectHandle<HeapObject> heap_object = GetBackReferencedObject(index); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sBackref [%u]\n", depth_, "", index); |
| // Don't print the backref object, since it might still be being |
| // initialized. |
| // TODO(leszeks): Have some sort of initialization marker on backrefs to |
| // allow them to be printed when valid. |
| } |
| return WriteHeapPointer(slot_accessor, heap_object, |
| GetAndResetNextReferenceDescriptor()); |
| } |
| |
| // Reference an object in the read-only heap. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadReadOnlyHeapRef(uint8_t data, |
| SlotAccessor slot_accessor) { |
| uint32_t chunk_index = source_.GetUint30(); |
| uint32_t chunk_offset = source_.GetUint30(); |
| |
| ReadOnlySpace* read_only_space = isolate()->heap()->read_only_space(); |
| ReadOnlyPage* page = read_only_space->pages()[chunk_index]; |
| Address address = page->OffsetToAddress(chunk_offset); |
| Tagged<HeapObject> heap_object = HeapObject::FromAddress(address); |
| |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sReadOnlyHeapRef [%u, %u] : ", depth_, "", chunk_index, |
| chunk_offset); |
| ShortPrint(heap_object); |
| PrintF("\n"); |
| } |
| |
| return WriteHeapPointer(slot_accessor, heap_object, |
| GetAndResetNextReferenceDescriptor(), |
| SKIP_WRITE_BARRIER); |
| } |
| |
| // Find an object in the roots array and write a pointer to it to the |
| // current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRootArray(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int id = source_.GetUint30(); |
| RootIndex root_index = static_cast<RootIndex>(id); |
| DirectHandle<HeapObject> heap_object = |
| Cast<HeapObject>(isolate()->root_handle(root_index)); |
| |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sRootArray [%u] : %s\n", depth_, "", id, |
| RootsTable::name(root_index)); |
| } |
| hot_objects_.Add(heap_object); |
| return WriteHeapPointer( |
| slot_accessor, heap_object, GetAndResetNextReferenceDescriptor(), |
| RootsTable::IsReadOnly(root_index) ? SKIP_WRITE_BARRIER |
| : UPDATE_WRITE_BARRIER); |
| } |
| |
| // Find an object in the startup object cache and write a pointer to it to |
| // the current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadStartupObjectCache(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int cache_index = source_.GetUint30(); |
| // TODO(leszeks): Could we use the address of the startup_object_cache |
| // entry as a Handle backing? |
| Tagged<HeapObject> heap_object = Cast<HeapObject>( |
| main_thread_isolate()->startup_object_cache()->at(cache_index)); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sStartupObjectCache [%u] : ", depth_, "", cache_index); |
| ShortPrint(*heap_object); |
| PrintF("\n"); |
| } |
| return WriteHeapPointer(slot_accessor, heap_object, |
| GetAndResetNextReferenceDescriptor()); |
| } |
| |
| // Find an object in the shared heap object cache and write a pointer to it |
| // to the current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadSharedHeapObjectCache( |
| uint8_t data, SlotAccessor slot_accessor) { |
| int cache_index = source_.GetUint30(); |
| // TODO(leszeks): Could we use the address of the |
| // shared_heap_object_cache entry as a Handle backing? |
| Tagged<HeapObject> heap_object = Cast<HeapObject>( |
| main_thread_isolate()->shared_heap_object_cache()->at(cache_index)); |
| DCHECK(SharedHeapSerializer::ShouldBeInSharedHeapObjectCache(heap_object)); |
| return WriteHeapPointer(slot_accessor, heap_object, |
| GetAndResetNextReferenceDescriptor()); |
| } |
| |
| // Deserialize a new meta-map and write a pointer to it to the current |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadNewMetaMap(uint8_t data, |
| SlotAccessor slot_accessor) { |
| SnapshotSpace space = data == kNewContextlessMetaMap |
| ? SnapshotSpace::kReadOnlyHeap |
| : SnapshotSpace::kOld; |
| DirectHandle<HeapObject> heap_object = ReadMetaMap(space); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sNewMetaMap [%s]\n", depth_, "", SnapshotSpaceName(space)); |
| } |
| return slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG, 0, |
| UPDATE_WRITE_BARRIER); |
| } |
| |
| // Find an external reference and write a pointer to it to the current |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadExternalReference(uint8_t data, |
| SlotAccessor slot_accessor) { |
| DCHECK_IMPLIES(data == kSandboxedExternalReference, V8_ENABLE_SANDBOX_BOOL); |
| Address address = ReadExternalReferenceCase(); |
| ExternalPointerTag tag = kExternalPointerNullTag; |
| if (data == kSandboxedExternalReference) { |
| tag = ReadExternalPointerTag(); |
| } |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sExternalReference [%" PRIxPTR ", %i]\n", depth_, "", address, |
| tag); |
| } |
| return WriteExternalPointer(*slot_accessor.object(), |
| slot_accessor.external_pointer_slot(tag), address, |
| tag); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRawExternalReference( |
| uint8_t data, SlotAccessor slot_accessor) { |
| DCHECK_IMPLIES(data == kSandboxedExternalReference, V8_ENABLE_SANDBOX_BOOL); |
| Address address; |
| source_.CopyRaw(&address, kSystemPointerSize); |
| ExternalPointerTag tag = kExternalPointerNullTag; |
| if (data == kSandboxedRawExternalReference) { |
| tag = ReadExternalPointerTag(); |
| } |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sRawExternalReference [%" PRIxPTR ", %i]\n", depth_, "", address, |
| tag); |
| } |
| return WriteExternalPointer(*slot_accessor.object(), |
| slot_accessor.external_pointer_slot(tag), address, |
| tag); |
| } |
| |
| // Find an object in the attached references and write a pointer to it to |
| // the current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadAttachedReference(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int index = source_.GetUint30(); |
| DirectHandle<HeapObject> heap_object = attached_objects_[index]; |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sAttachedReference [%u] : ", depth_, "", index); |
| ShortPrint(*heap_object); |
| PrintF("\n"); |
| } |
| return WriteHeapPointer(slot_accessor, heap_object, |
| GetAndResetNextReferenceDescriptor()); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRegisterPendingForwardRef( |
| uint8_t data, SlotAccessor slot_accessor) { |
| ReferenceDescriptor descr = GetAndResetNextReferenceDescriptor(); |
| unresolved_forward_refs_.emplace_back(slot_accessor.object(), |
| slot_accessor.offset(), descr); |
| num_unresolved_forward_refs_++; |
| return 1; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadResolvePendingForwardRef( |
| uint8_t data, SlotAccessor slot_accessor) { |
| // Pending forward refs can only be resolved after the heap object's map |
| // field is deserialized; currently they only appear immediately after |
| // the map field or after the 'self' indirect pointer for trusted objects. |
| DCHECK(slot_accessor.offset() == HeapObject::kHeaderSize || |
| slot_accessor.offset() == ExposedTrustedObject::kHeaderSize); |
| DirectHandle<HeapObject> obj = slot_accessor.object(); |
| int index = source_.GetUint30(); |
| auto& forward_ref = unresolved_forward_refs_[index]; |
| auto slot = SlotAccessorForHeapObject::ForSlotOffset(forward_ref.object, |
| forward_ref.offset); |
| WriteHeapPointer(slot, obj, forward_ref.descr); |
| num_unresolved_forward_refs_--; |
| if (num_unresolved_forward_refs_ == 0) { |
| // If there's no more pending fields, clear the entire pending field |
| // vector. |
| unresolved_forward_refs_.clear(); |
| } else { |
| // Otherwise, at least clear the pending field. |
| forward_ref.object = Handle<HeapObject>(); |
| } |
| return 0; |
| } |
| |
| // Deserialize raw data of variable length. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadVariableRawData(uint8_t data, |
| SlotAccessor slot_accessor) { |
| // This operation is only supported for tagged-size slots, else we might |
| // become misaligned. |
| DCHECK_EQ(decltype(slot_accessor.slot())::kSlotDataSize, kTaggedSize); |
| int size_in_tagged = source_.GetUint30(); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sVariableRawData [%u] :", depth_, "", size_in_tagged); |
| for (int i = 0; i < size_in_tagged; ++i) { |
| PrintF(" %0*" PRIxTAGGED, kTaggedSize / 2, |
| reinterpret_cast<const Tagged_t*>(source_.data())[i]); |
| } |
| PrintF("\n"); |
| } |
| // TODO(leszeks): Only copy slots when there are Smis in the serialized |
| // data. |
| source_.CopySlots(slot_accessor.slot().location(), size_in_tagged); |
| return size_in_tagged; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadVariableRepeatRoot(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int repeats = VariableRepeatRootCount::Decode(source_.GetUint30()); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sVariableRepeat [%u] : ", depth_, "", repeats); |
| } |
| int ret = ReadRepeatedRoot(slot_accessor, repeats); |
| if (v8_flags.trace_deserialization) { |
| PrintF("\n"); |
| } |
| return ret; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadOffHeapBackingStore( |
| uint8_t data, SlotAccessor slot_accessor) { |
| uint32_t byte_length = source_.GetUint32(); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sOffHeapBackingStore [%u]\n", depth_, "", byte_length); |
| } |
| |
| std::unique_ptr<BackingStore> backing_store; |
| if (data == kOffHeapBackingStore) { |
| backing_store = BackingStore::Allocate(main_thread_isolate(), byte_length, |
| SharedFlag::kNotShared, |
| InitializedFlag::kUninitialized); |
| } else { |
| uint32_t max_byte_length = source_.GetUint32(); |
| size_t page_size, initial_pages, max_pages; |
| Maybe<bool> result = |
| JSArrayBuffer::GetResizableBackingStorePageConfiguration( |
| nullptr, byte_length, max_byte_length, kDontThrow, &page_size, |
| &initial_pages, &max_pages); |
| DCHECK(result.FromJust()); |
| USE(result); |
| backing_store = BackingStore::TryAllocateAndPartiallyCommitMemory( |
| main_thread_isolate(), byte_length, max_byte_length, page_size, |
| initial_pages, max_pages, WasmMemoryFlag::kNotWasm, |
| SharedFlag::kNotShared); |
| } |
| CHECK_NOT_NULL(backing_store); |
| source_.CopyRaw(backing_store->buffer_start(), byte_length); |
| backing_stores_.push_back(std::move(backing_store)); |
| return 0; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadApiReference(uint8_t data, |
| SlotAccessor slot_accessor) { |
| DCHECK_IMPLIES(data == kSandboxedApiReference, V8_ENABLE_SANDBOX_BOOL); |
| uint32_t reference_id = static_cast<uint32_t>(source_.GetUint30()); |
| Address address; |
| if (main_thread_isolate()->api_external_references()) { |
| DCHECK_WITH_MSG(reference_id < num_api_references_, |
| "too few external references provided through the API"); |
| address = static_cast<Address>( |
| main_thread_isolate()->api_external_references()[reference_id]); |
| } else { |
| address = reinterpret_cast<Address>(NoExternalReferencesCallback); |
| } |
| ExternalPointerTag tag = kExternalPointerNullTag; |
| if (data == kSandboxedApiReference) { |
| tag = ReadExternalPointerTag(); |
| } |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sApiReference [%" PRIxPTR ", %i]\n", depth_, "", address, tag); |
| } |
| return WriteExternalPointer(*slot_accessor.object(), |
| slot_accessor.external_pointer_slot(tag), address, |
| tag); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadClearedWeakReference( |
| uint8_t data, SlotAccessor slot_accessor) { |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sClearedWeakReference\n", depth_, ""); |
| } |
| return slot_accessor.Write(ClearedValue(), 0, SKIP_WRITE_BARRIER); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadWeakPrefix(uint8_t data, |
| SlotAccessor slot_accessor) { |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sWeakPrefix\n", depth_, ""); |
| } |
| // We shouldn't have two weak prefixes in a row. |
| DCHECK(!next_reference_is_weak_); |
| // We shouldn't have weak refs without a current object. |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| next_reference_is_weak_ = true; |
| return 0; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadIndirectPointerPrefix( |
| uint8_t data, SlotAccessor slot_accessor) { |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sIndirectPointerPrefix\n", depth_, ""); |
| } |
| // We shouldn't have two indirect pointer prefixes in a row. |
| DCHECK(!next_reference_is_indirect_pointer_); |
| // We shouldn't have a indirect pointer prefix without a current object. |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| next_reference_is_indirect_pointer_ = true; |
| return 0; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadInitializeSelfIndirectPointer( |
| uint8_t data, SlotAccessor slot_accessor) { |
| #ifdef V8_ENABLE_SANDBOX |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| DCHECK(IsExposedTrustedObject(*slot_accessor.object())); |
| DCHECK_EQ(slot_accessor.offset(), |
| ExposedTrustedObject::kSelfIndirectPointerOffset); |
| |
| Tagged<ExposedTrustedObject> host = |
| TrustedCast<ExposedTrustedObject>(*slot_accessor.object()); |
| host->InitAndPublish(isolate()); |
| |
| return 1; |
| #else |
| UNREACHABLE(); |
| #endif // V8_ENABLE_SANDBOX |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadAllocateJSDispatchEntry( |
| uint8_t data, SlotAccessor slot_accessor) { |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| DirectHandle<HeapObject> host = slot_accessor.object(); |
| |
| uint32_t parameter_count = source_.GetUint30(); |
| DCHECK_LE(parameter_count, kMaxUInt16); |
| |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sAllocateJSDispatchEntry [%u]\n", depth_, "", parameter_count); |
| } |
| |
| DirectHandle<Code> code = TrustedCast<Code>(ReadObject()); |
| |
| JSDispatchTable::Space* space = |
| isolate()->GetJSDispatchTableSpaceFor(host->address()); |
| JSDispatchHandle handle = |
| isolate()->factory()->NewJSDispatchHandle(parameter_count, code, space); |
| js_dispatch_entries_.push_back(handle); |
| host->Relaxed_WriteField<JSDispatchHandle::underlying_type>( |
| slot_accessor.offset(), handle.value()); |
| JS_DISPATCH_HANDLE_WRITE_BARRIER(*host, handle); |
| |
| return 1; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadJSDispatchEntry(uint8_t data, |
| SlotAccessor slot_accessor) { |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| DirectHandle<HeapObject> host = slot_accessor.object(); |
| uint32_t entry_id = source_.GetUint30(); |
| DCHECK_LT(entry_id, js_dispatch_entries_.size()); |
| |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sJSDispatchEntry [%u]\n", depth_, "", entry_id); |
| } |
| |
| JSDispatchHandle handle = js_dispatch_entries_[entry_id]; |
| DCHECK_NE(handle, kNullJSDispatchHandle); |
| |
| host->Relaxed_WriteField<JSDispatchHandle::underlying_type>( |
| slot_accessor.offset(), handle.value()); |
| JS_DISPATCH_HANDLE_WRITE_BARRIER(*host, handle); |
| |
| return 1; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadProtectedPointerPrefix( |
| uint8_t data, SlotAccessor slot_accessor) { |
| // We shouldn't have two protected pointer prefixes in a row. |
| DCHECK(!next_reference_is_protected_pointer); |
| // We shouldn't have a protected pointer prefix without a current object. |
| DCHECK_NE(slot_accessor.object()->address(), kNullAddress); |
| next_reference_is_protected_pointer = true; |
| return 0; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRootArrayConstants(uint8_t data, |
| SlotAccessor slot_accessor) { |
| // First kRootArrayConstantsCount roots are guaranteed to be in |
| // the old space. |
| static_assert(static_cast<int>(RootIndex::kFirstImmortalImmovableRoot) == 0); |
| static_assert(kRootArrayConstantsCount <= |
| static_cast<int>(RootIndex::kLastImmortalImmovableRoot)); |
| |
| RootIndex root_index = RootArrayConstant::Decode(data); |
| DirectHandle<HeapObject> heap_object = |
| Cast<HeapObject>(isolate()->root_handle(root_index)); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sRootArrayConstants [%u] : %s\n", depth_, "", |
| static_cast<int>(root_index), RootsTable::name(root_index)); |
| } |
| return slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG, 0, |
| SKIP_WRITE_BARRIER); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadHotObject(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int index = HotObject::Decode(data); |
| DirectHandle<HeapObject> hot_object = hot_objects_.Get(index); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sHotObject [%u] : ", depth_, "", index); |
| ShortPrint(*hot_object); |
| PrintF("\n"); |
| } |
| return WriteHeapPointer(slot_accessor, hot_object, |
| GetAndResetNextReferenceDescriptor()); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadFixedRawData(uint8_t data, |
| SlotAccessor slot_accessor) { |
| using TSlot = decltype(slot_accessor.slot()); |
| |
| // Deserialize raw data of fixed length from 1 to 32 times kTaggedSize. |
| int size_in_tagged = FixedRawDataWithSize::Decode(data); |
| static_assert(TSlot::kSlotDataSize == kTaggedSize || |
| TSlot::kSlotDataSize == 2 * kTaggedSize); |
| int size_in_slots = size_in_tagged / (TSlot::kSlotDataSize / kTaggedSize); |
| // kFixedRawData can have kTaggedSize != TSlot::kSlotDataSize when |
| // serializing Smi roots in pointer-compressed builds. In this case, the |
| // size in bytes is unconditionally the (full) slot size. |
| DCHECK_IMPLIES(kTaggedSize != TSlot::kSlotDataSize, size_in_slots == 1); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sFixedRawData [%u] :", depth_, "", size_in_tagged); |
| for (int i = 0; i < size_in_tagged; ++i) { |
| PrintF(" %0*" PRIxTAGGED, kTaggedSize / 2, |
| reinterpret_cast<const Tagged_t*>(source_.data())[i]); |
| } |
| PrintF("\n"); |
| } |
| // TODO(leszeks): Only copy slots when there are Smis in the serialized |
| // data. |
| source_.CopySlots(slot_accessor.slot().location(), size_in_slots); |
| return size_in_slots; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadFixedRepeatRoot(uint8_t data, |
| SlotAccessor slot_accessor) { |
| int repeats = FixedRepeatRootWithCount::Decode(data); |
| if (v8_flags.trace_deserialization) { |
| PrintF("%*sFixedRepeat [%u] : ", depth_, "", repeats); |
| } |
| int ret = ReadRepeatedRoot(slot_accessor, repeats); |
| if (v8_flags.trace_deserialization) { |
| PrintF("\n"); |
| } |
| return ret; |
| } |
| |
| #undef CASE_RANGE_ALL_SPACES |
| #undef CASE_RANGE |
| #undef CASE_R32 |
| #undef CASE_R16 |
| #undef CASE_R8 |
| #undef CASE_R4 |
| #undef CASE_R3 |
| #undef CASE_R2 |
| #undef CASE_R1 |
| |
| template <typename IsolateT> |
| Address Deserializer<IsolateT>::ReadExternalReferenceCase() { |
| uint32_t reference_id = static_cast<uint32_t>(source_.GetUint30()); |
| return main_thread_isolate()->external_reference_table()->address( |
| reference_id); |
| } |
| |
| template <typename IsolateT> |
| ExternalPointerTag Deserializer<IsolateT>::ReadExternalPointerTag() { |
| return static_cast<ExternalPointerTag>(source_.GetUint30()); |
| } |
| |
| template <typename IsolateT> |
| Tagged<HeapObject> Deserializer<IsolateT>::Allocate( |
| AllocationType allocation, int size, AllocationAlignment alignment) { |
| #ifdef DEBUG |
| if (!previous_allocation_obj_.is_null()) { |
| // Make sure that the previous object is initialized sufficiently to |
| // be iterated over by the GC. |
| int object_size = previous_allocation_obj_->Size(isolate_); |
| DCHECK_LE(object_size, previous_allocation_size_); |
| } |
| #endif |
| |
| Tagged<HeapObject> obj = |
| HeapObject::FromAddress(isolate()->heap()->AllocateRawOrFail( |
| size, allocation, AllocationOrigin::kRuntime, alignment)); |
| |
| #ifdef DEBUG |
| previous_allocation_obj_ = direct_handle(obj, isolate()); |
| previous_allocation_size_ = size; |
| #endif |
| |
| return obj; |
| } |
| |
| template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Deserializer<Isolate>; |
| template class EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) |
| Deserializer<LocalIsolate>; |
| |
| } // namespace v8::internal |
| |
| #include "src/objects/object-macros-undef.h" |