| // 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 "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/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" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // 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() const { |
| return object_->RawExternalPointerField(offset_); |
| } |
| Handle<HeapObject> object() const { return object_; } |
| int offset() const { return offset_; } |
| |
| // Writes the given value to this slot, optionally with an offset (e.g. for |
| // repeat writes). Returns the number of slots written (which is one). |
| int Write(MaybeObject value, int slot_offset = 0) { |
| MaybeObjectSlot current_slot = slot() + slot_offset; |
| current_slot.Relaxed_Store(value); |
| CombinedWriteBarrier(*object_, current_slot, value, UPDATE_WRITE_BARRIER); |
| return 1; |
| } |
| int Write(HeapObject value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| return Write(HeapObjectReference::From(value, ref_type), slot_offset); |
| } |
| int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| return Write(*value, ref_type, slot_offset); |
| } |
| |
| 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() const { UNREACHABLE(); } |
| Handle<HeapObject> object() const { UNREACHABLE(); } |
| int offset() const { UNREACHABLE(); } |
| |
| // Writes the given value to this slot, optionally with an offset (e.g. for |
| // repeat writes). Returns the number of slots written (which is one). |
| int Write(MaybeObject value, int slot_offset = 0) { |
| FullMaybeObjectSlot current_slot = slot() + slot_offset; |
| current_slot.Relaxed_Store(value); |
| return 1; |
| } |
| int Write(HeapObject value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| return Write(HeapObjectReference::From(value, ref_type), slot_offset); |
| } |
| int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| return Write(*value, ref_type, slot_offset); |
| } |
| |
| 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(Handle<HeapObject>* handle, IsolateT* isolate) |
| : handle_(handle), isolate_(isolate) {} |
| |
| MaybeObjectSlot slot() const { UNREACHABLE(); } |
| ExternalPointerSlot external_pointer_slot() const { UNREACHABLE(); } |
| Handle<HeapObject> object() const { UNREACHABLE(); } |
| int offset() const { UNREACHABLE(); } |
| |
| int Write(MaybeObject value, int slot_offset = 0) { UNREACHABLE(); } |
| int Write(HeapObject value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| DCHECK_EQ(slot_offset, 0); |
| DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); |
| *handle_ = handle(value, isolate_); |
| return 1; |
| } |
| int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type, |
| int slot_offset = 0) { |
| DCHECK_EQ(slot_offset, 0); |
| DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); |
| *handle_ = value; |
| return 1; |
| } |
| |
| private: |
| Handle<HeapObject>* handle_; |
| IsolateT* isolate_; |
| }; |
| |
| template <typename IsolateT> |
| template <typename TSlot> |
| int Deserializer<IsolateT>::WriteAddress(TSlot dest, Address value) { |
| DCHECK(!next_reference_is_weak_); |
| memcpy(dest.ToVoidPtr(), &value, kSystemPointerSize); |
| static_assert(IsAligned(kSystemPointerSize, TSlot::kSlotDataSize)); |
| return (kSystemPointerSize / TSlot::kSlotDataSize); |
| } |
| |
| template <typename IsolateT> |
| int Deserializer<IsolateT>::WriteExternalPointer(ExternalPointerSlot dest, |
| Address value, |
| ExternalPointerTag tag) { |
| DCHECK(!next_reference_is_weak_); |
| dest.init(main_thread_isolate(), value, tag); |
| // 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 byte> payload, |
| uint32_t magic_number, |
| bool deserializing_user_code, |
| bool can_rehash) |
| : isolate_(isolate), |
| source_(payload), |
| magic_number_(magic_number), |
| new_descriptor_arrays_(isolate->heap()), |
| deserializing_user_code_(deserializing_user_code), |
| should_rehash_((v8_flags.rehash_snapshot && can_rehash) || |
| deserializing_user_code) { |
| 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({}); |
| |
| #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 (Handle<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 byte expected = kSynchronize; |
| CHECK_EQ(expected, source_.Get()); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::DeserializeDeferredObjects() { |
| 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 (Handle<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(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, String string) { |
| // Make sure raw_hash_field() is computed. |
| string.EnsureHash(SharedStringAccessGuardIfNeeded(isolate)); |
| return string.raw_hash_field(); |
| } |
| } // namespace |
| |
| StringTableInsertionKey::StringTableInsertionKey( |
| Isolate* isolate, Handle<String> string, |
| DeserializingUserCodeOption deserializing_user_code) |
| : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), |
| string_(string) { |
| #ifdef DEBUG |
| deserializing_user_code_ = deserializing_user_code; |
| #endif |
| DCHECK(string->IsInternalizedString()); |
| } |
| |
| StringTableInsertionKey::StringTableInsertionKey( |
| LocalIsolate* isolate, Handle<String> string, |
| DeserializingUserCodeOption deserializing_user_code) |
| : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), |
| string_(string) { |
| #ifdef DEBUG |
| deserializing_user_code_ = deserializing_user_code; |
| #endif |
| DCHECK(string->IsInternalizedString()); |
| } |
| |
| template <typename IsolateT> |
| bool StringTableInsertionKey::IsMatch(IsolateT* isolate, String string) { |
| // We want to compare the content of two strings here. |
| return string_->SlowEquals(string, SharedStringAccessGuardIfNeeded(isolate)); |
| } |
| template bool StringTableInsertionKey::IsMatch(Isolate* isolate, String string); |
| template bool StringTableInsertionKey::IsMatch(LocalIsolate* isolate, |
| 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(ExternalString string, Isolate* isolate) { |
| DisallowGarbageCollection no_gc; |
| uint32_t index = string.GetResourceRefForDeserialization(); |
| 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(Map map, |
| Handle<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 = JSDataViewOrRabGsabDataView::cast(*obj); |
| auto buffer = JSArrayBuffer::cast(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 = JSTypedArray::cast(*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]; |
| void* start = backing_store ? backing_store->buffer_start() : nullptr; |
| if (!start) start = EmptyBackingStoreBuffer(); |
| typed_array.SetOffHeapDataPtr(main_thread_isolate(), start, |
| typed_array.byte_offset()); |
| } |
| } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) { |
| auto buffer = JSArrayBuffer::cast(*obj); |
| uint32_t store_index = buffer.GetBackingStoreRefForDeserialization(); |
| if (store_index == kEmptyBackingStoreRefSentinel) { |
| buffer.set_extension(nullptr); |
| 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()); |
| } |
| } |
| } |
| |
| template <> |
| void Deserializer<LocalIsolate>::PostProcessNewJSReceiver( |
| Map map, Handle<JSReceiver> obj, InstanceType instance_type, |
| SnapshotSpace space) { |
| UNREACHABLE(); |
| } |
| |
| template <typename IsolateT> |
| void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map, |
| Handle<HeapObject> obj, |
| SnapshotSpace space) { |
| DisallowGarbageCollection no_gc; |
| Map raw_map = *map; |
| DCHECK_EQ(raw_map, obj->map(isolate_)); |
| InstanceType instance_type = raw_map.instance_type(); |
| 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. |
| String string = String::cast(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. |
| Handle<String> string = Handle<String>::cast(obj); |
| |
| StringTableInsertionKey key( |
| isolate(), string, |
| DeserializingUserCodeOption::kIsDeserializingUserCode); |
| String result = *isolate()->string_table()->LookupKey(isolate(), &key); |
| |
| if (result != raw_obj) { |
| // Updating invalidated object size from a background thread would |
| // race. We are allowed to skip this here since this string hasn't |
| // transitioned so far. |
| String::cast(raw_obj).MakeThin(isolate(), result, |
| UpdateInvalidatedObjectSize::kNo); |
| // 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(Handle<Script>::cast(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(Handle<AllocationSite>::cast(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_code_objects_.push_back(Handle<InstructionStream>::cast(obj)); |
| } |
| } else if (InstanceTypeChecker::IsCode(instance_type)) { |
| Code code = Code::cast(raw_obj); |
| code.init_instruction_start(main_thread_isolate(), kNullAddress); |
| 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::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(Handle<Map>::cast(obj)); |
| } |
| } else if (InstanceTypeChecker::IsAccessorInfo(instance_type)) { |
| #ifdef USE_SIMULATOR |
| accessor_infos_.push_back(Handle<AccessorInfo>::cast(obj)); |
| #endif |
| } else if (InstanceTypeChecker::IsCallHandlerInfo(instance_type)) { |
| #ifdef USE_SIMULATOR |
| call_handler_infos_.push_back(Handle<CallHandlerInfo>::cast(obj)); |
| #endif |
| } else if (InstanceTypeChecker::IsExternalString(instance_type)) { |
| PostProcessExternalString(ExternalString::cast(raw_obj), |
| main_thread_isolate()); |
| } else if (InstanceTypeChecker::IsJSReceiver(instance_type)) { |
| // PostProcessNewJSReceiver may trigger GC. |
| no_gc.Release(); |
| return PostProcessNewJSReceiver(raw_map, Handle<JSReceiver>::cast(obj), |
| instance_type, space); |
| } else if (InstanceTypeChecker::IsDescriptorArray(instance_type)) { |
| DCHECK(InstanceTypeChecker::IsStrongDescriptorArray(instance_type)); |
| Handle<DescriptorArray> descriptors = Handle<DescriptorArray>::cast(obj); |
| new_descriptor_arrays_.Push(*descriptors); |
| } else if (InstanceTypeChecker::IsNativeContext(instance_type)) { |
| NativeContext::cast(raw_obj).init_microtask_queue(main_thread_isolate(), |
| nullptr); |
| } else if (InstanceTypeChecker::IsScript(instance_type)) { |
| LogScriptEvents(Script::cast(*obj)); |
| } |
| } |
| |
| template <typename IsolateT> |
| HeapObjectReferenceType Deserializer<IsolateT>::GetAndResetNextReferenceType() { |
| HeapObjectReferenceType type = next_reference_is_weak_ |
| ? HeapObjectReferenceType::WEAK |
| : HeapObjectReferenceType::STRONG; |
| next_reference_is_weak_ = false; |
| return type; |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::GetBackReferencedObject() { |
| Handle<HeapObject> obj = back_refs_[source_.GetInt()]; |
| |
| // We don't allow ThinStrings in backreferences -- if internalization produces |
| // a thin string, then it should also update the backref handle. |
| DCHECK(!obj->IsThinString(isolate())); |
| |
| hot_objects_.Add(obj); |
| DCHECK(!HasWeakHeapObjectTag(*obj)); |
| return obj; |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::ReadObject() { |
| Handle<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; |
| } |
| } |
| } // namespace |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::ReadObject(SnapshotSpace space) { |
| const int size_in_tagged = source_.GetInt(); |
| 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 kNewMetaMap bytecode. |
| DCHECK_NE(source()->Peek(), kRegisterPendingForwardRef); |
| Handle<Map> map = Handle<Map>::cast(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`). |
| HeapObject raw_obj = |
| Allocate(allocation, size_in_bytes, HeapObject::RequiredAlignment(*map)); |
| raw_obj.set_map_after_allocation(*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 (raw_obj.IsBytecodeArray(isolate())) { |
| BytecodeArray::cast(raw_obj).set_bytecode_age(0); |
| } else if (raw_obj.IsEphemeronHashTable()) { |
| // Make sure EphemeronHashTables have valid HeapObject keys, so that the |
| // marker does not break when marking EphemeronHashTable, see |
| // MarkingVisitorBase::VisitEphemeronHashTable. |
| EphemeronHashTable table = EphemeronHashTable::cast(raw_obj); |
| MemsetTagged(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 (raw_obj.IsJSObject(cage_base) && |
| JSObject::cast(raw_obj).MayHaveEmbedderFields()) { |
| JSObject js_obj = JSObject::cast(raw_obj); |
| for (int i = 0; i < js_obj.GetEmbedderFieldCount(); ++i) { |
| void* pointer; |
| CHECK(EmbedderDataSlot(js_obj, i).ToAlignedPointer(main_thread_isolate(), |
| &pointer)); |
| CHECK_NULL(pointer); |
| } |
| } else if (raw_obj.IsEmbedderDataArray(cage_base)) { |
| EmbedderDataArray array = EmbedderDataArray::cast(raw_obj); |
| EmbedderDataSlot start(array, 0); |
| EmbedderDataSlot end(array, array.length()); |
| for (EmbedderDataSlot slot = start; slot < end; ++slot) { |
| void* pointer; |
| CHECK(slot.ToAlignedPointer(main_thread_isolate(), &pointer)); |
| CHECK_NULL(pointer); |
| } |
| } |
| #endif |
| |
| Handle<HeapObject> obj = handle(raw_obj, isolate()); |
| back_refs_.push_back(obj); |
| |
| ReadData(obj, 1, size_in_tagged); |
| PostProcessNewObject(map, obj, space); |
| |
| #ifdef DEBUG |
| if (obj->IsInstructionStream(cage_base)) { |
| DCHECK(space == SnapshotSpace::kCode || |
| space == SnapshotSpace::kReadOnlyHeap); |
| } else { |
| DCHECK_NE(space, SnapshotSpace::kCode); |
| } |
| #endif // DEBUG |
| |
| return obj; |
| } |
| |
| template <typename IsolateT> |
| Handle<HeapObject> Deserializer<IsolateT>::ReadMetaMap() { |
| const SnapshotSpace space = SnapshotSpace::kReadOnlyHeap; |
| const int size_in_bytes = Map::kSize; |
| const int size_in_tagged = size_in_bytes / kTaggedSize; |
| |
| HeapObject raw_obj = |
| Allocate(SpaceToAllocation(space), size_in_bytes, kTaggedAligned); |
| raw_obj.set_map_after_allocation(Map::unchecked_cast(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); |
| |
| // Set the instance-type manually, to allow backrefs to read it. |
| Map::unchecked_cast(*obj).set_instance_type(MAP_TYPE); |
| |
| ReadData(obj, 1, size_in_tagged); |
| PostProcessNewObject(Handle<Map>::cast(obj), obj, space); |
| |
| return obj; |
| } |
| |
| class DeserializerRelocInfoVisitor { |
| public: |
| DeserializerRelocInfoVisitor(Deserializer<Isolate>* deserializer, |
| const std::vector<Handle<HeapObject>>* objects) |
| : deserializer_(deserializer), objects_(objects), current_object_(0) {} |
| |
| DeserializerRelocInfoVisitor(Deserializer<LocalIsolate>* deserializer, |
| const std::vector<Handle<HeapObject>>* objects) { |
| UNREACHABLE(); |
| } |
| |
| ~DeserializerRelocInfoVisitor() { |
| DCHECK_EQ(current_object_, objects_->size()); |
| } |
| |
| void VisitCodeTarget(InstructionStream host, RelocInfo* rinfo); |
| void VisitEmbeddedPointer(InstructionStream host, RelocInfo* rinfo); |
| void VisitExternalReference(InstructionStream host, RelocInfo* rinfo); |
| void VisitInternalReference(InstructionStream host, RelocInfo* rinfo); |
| void VisitOffHeapTarget(InstructionStream host, RelocInfo* rinfo); |
| |
| private: |
| Isolate* isolate() { return deserializer_->isolate(); } |
| SnapshotByteSource& source() { return deserializer_->source_; } |
| |
| Deserializer<Isolate>* deserializer_; |
| const std::vector<Handle<HeapObject>>* objects_; |
| int current_object_; |
| }; |
| |
| void DeserializerRelocInfoVisitor::VisitCodeTarget(InstructionStream host, |
| RelocInfo* rinfo) { |
| HeapObject object = *objects_->at(current_object_++); |
| rinfo->set_target_address( |
| host, InstructionStream::cast(object).instruction_start()); |
| } |
| |
| void DeserializerRelocInfoVisitor::VisitEmbeddedPointer(InstructionStream host, |
| RelocInfo* rinfo) { |
| HeapObject object = *objects_->at(current_object_++); |
| // Embedded object reference must be a strong one. |
| rinfo->set_target_object(host, object); |
| } |
| |
| void DeserializerRelocInfoVisitor::VisitExternalReference( |
| InstructionStream host, RelocInfo* rinfo) { |
| byte data = source().Get(); |
| CHECK_EQ(data, Deserializer<Isolate>::kExternalReference); |
| |
| Address address = deserializer_->ReadExternalReferenceCase(); |
| |
| if (rinfo->IsCodedSpecially()) { |
| Address location_of_branch_data = rinfo->pc(); |
| Assembler::deserialization_set_special_target_at( |
| location_of_branch_data, host.code(kAcquireLoad), address); |
| } else { |
| WriteUnalignedValue(rinfo->target_address_address(), address); |
| } |
| } |
| |
| void DeserializerRelocInfoVisitor::VisitInternalReference( |
| InstructionStream host, RelocInfo* rinfo) { |
| byte data = source().Get(); |
| CHECK_EQ(data, Deserializer<Isolate>::kInternalReference); |
| |
| // An internal reference target is encoded as an offset from code entry. |
| int target_offset = source().GetInt(); |
| static_assert(InstructionStream::kOnHeapBodyIsContiguous); |
| DCHECK_LT(static_cast<unsigned>(target_offset), |
| static_cast<unsigned>(host.code(kAcquireLoad).instruction_size())); |
| Address target = host.instruction_start() + target_offset; |
| Assembler::deserialization_set_target_internal_reference_at( |
| rinfo->pc(), target, rinfo->rmode()); |
| } |
| |
| void DeserializerRelocInfoVisitor::VisitOffHeapTarget(InstructionStream host, |
| RelocInfo* rinfo) { |
| // Currently we don't serialize code that contains near builtin entries. |
| DCHECK_NE(rinfo->rmode(), RelocInfo::NEAR_BUILTIN_ENTRY); |
| |
| byte data = source().Get(); |
| CHECK_EQ(data, Deserializer<Isolate>::kOffHeapTarget); |
| |
| Builtin builtin = Builtins::FromInt(source().GetInt()); |
| |
| CHECK_NOT_NULL(isolate()->embedded_blob_code()); |
| EmbeddedData d = EmbeddedData::FromBlob(isolate()); |
| Address address = d.InstructionStartOf(builtin); |
| CHECK_NE(kNullAddress, address); |
| |
| // TODO(ishell): implement RelocInfo::set_target_off_heap_target() |
| if (RelocInfo::OffHeapTargetIsCodedSpecially()) { |
| Address location_of_branch_data = rinfo->pc(); |
| Assembler::deserialization_set_special_target_at( |
| location_of_branch_data, host.code(kAcquireLoad), address); |
| } else { |
| WriteUnalignedValue(rinfo->target_address_address(), address); |
| } |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRepeatedObject(SlotAccessor slot_accessor, |
| int repeat_count) { |
| CHECK_LE(2, repeat_count); |
| |
| Handle<HeapObject> heap_object = ReadObject(); |
| DCHECK(!Heap::InYoungGeneration(*heap_object)); |
| for (int i = 0; i < repeat_count; i++) { |
| // TODO(leszeks): Use a ranged barrier here. |
| slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG, i); |
| } |
| 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 byte VerifyBytecodeCount(byte 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) |
| // 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) { |
| byte 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) { |
| byte data = source_.Get(); |
| current += ReadSingleBytecodeData(data, SlotAccessorForRootSlots(current)); |
| } |
| CHECK_EQ(current, end); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadSingleBytecodeData(byte data, |
| SlotAccessor slot_accessor) { |
| 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 kReadOnlyObjectCache: |
| return ReadReadOnlyObjectCache(data, slot_accessor); |
| case kSharedHeapObjectCache: |
| return ReadSharedHeapObjectCache(data, slot_accessor); |
| case kNewMetaMap: |
| return ReadNewMetaMap(data, slot_accessor); |
| case kSandboxedExternalReference: |
| case kExternalReference: |
| return ReadExternalReference(data, slot_accessor); |
| case kSandboxedRawExternalReference: |
| case kRawExternalReference: |
| return ReadRawExternalReference(data, slot_accessor); |
| case kInternalReference: |
| case kOffHeapTarget: |
| // These bytecodes are expected only during RelocInfo iteration. |
| UNREACHABLE(); |
| 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 kCodeBody: |
| return ReadCodeBody(data, slot_accessor); |
| case kVariableRepeat: |
| return ReadVariableRepeat(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 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(kFixedRepeat, 16): |
| return ReadFixedRepeat(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(); |
| } |
| |
| // Deserialize a new object and write a pointer to it to the current |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadNewObject(byte data, |
| SlotAccessor slot_accessor) { |
| SnapshotSpace space = NewObject::Decode(data); |
| DCHECK_IMPLIES(V8_STATIC_ROOTS_BOOL, space != SnapshotSpace::kReadOnlyHeap); |
| // Save the reference type before recursing down into reading the object. |
| HeapObjectReferenceType ref_type = GetAndResetNextReferenceType(); |
| Handle<HeapObject> heap_object = ReadObject(space); |
| return slot_accessor.Write(heap_object, ref_type); |
| } |
| |
| // 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(byte data, SlotAccessor slot_accessor) { |
| Handle<HeapObject> heap_object = GetBackReferencedObject(); |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // Reference an object in the read-only heap. This should be used when an |
| // object is read-only, but is not a root. Except with static roots we |
| // always use this reference to refer to read only objects since they are |
| // created by loading a memory dump of r/o space. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadReadOnlyHeapRef(byte data, |
| SlotAccessor slot_accessor) { |
| DCHECK(isolate()->heap()->deserialization_complete() || V8_STATIC_ROOTS_BOOL); |
| uint32_t chunk_index = source_.GetInt(); |
| uint32_t chunk_offset = source_.GetInt(); |
| |
| ReadOnlySpace* read_only_space = isolate()->heap()->read_only_space(); |
| ReadOnlyPage* page = read_only_space->pages()[chunk_index]; |
| Address address = page->OffsetToAddress(chunk_offset); |
| HeapObject heap_object = HeapObject::FromAddress(address); |
| |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // 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(byte data, |
| SlotAccessor slot_accessor) { |
| int id = source_.GetInt(); |
| RootIndex root_index = static_cast<RootIndex>(id); |
| Handle<HeapObject> heap_object = |
| Handle<HeapObject>::cast(isolate()->root_handle(root_index)); |
| hot_objects_.Add(heap_object); |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // 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(byte data, |
| SlotAccessor slot_accessor) { |
| int cache_index = source_.GetInt(); |
| // TODO(leszeks): Could we use the address of the startup_object_cache |
| // entry as a Handle backing? |
| HeapObject heap_object = HeapObject::cast( |
| main_thread_isolate()->startup_object_cache()->at(cache_index)); |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // Find an object in the read-only object cache and write a pointer to it |
| // to the current object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadReadOnlyObjectCache( |
| byte data, SlotAccessor slot_accessor) { |
| DCHECK(!V8_STATIC_ROOTS_BOOL); |
| int cache_index = source_.GetInt(); |
| // TODO(leszeks): Could we use the address of the cached_read_only_object |
| // entry as a Handle backing? |
| HeapObject heap_object = HeapObject::cast( |
| isolate()->read_only_heap()->cached_read_only_object(cache_index)); |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // 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( |
| byte data, SlotAccessor slot_accessor) { |
| int cache_index = source_.GetInt(); |
| // TODO(leszeks): Could we use the address of the |
| // shared_heap_object_cache entry as a Handle backing? |
| HeapObject heap_object = HeapObject::cast( |
| main_thread_isolate()->shared_heap_object_cache()->at(cache_index)); |
| DCHECK(SharedHeapSerializer::ShouldBeInSharedHeapObjectCache(heap_object)); |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| // 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(byte data, |
| SlotAccessor slot_accessor) { |
| Handle<HeapObject> heap_object = ReadMetaMap(); |
| return slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG); |
| } |
| |
| // Find an external reference and write a pointer to it to the current |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadExternalReference(byte data, |
| SlotAccessor slot_accessor) { |
| DCHECK_IMPLIES(data == kSandboxedExternalReference, V8_ENABLE_SANDBOX_BOOL); |
| Address address = ReadExternalReferenceCase(); |
| ExternalPointerTag tag = kExternalPointerNullTag; |
| if (data == kSandboxedExternalReference) { |
| tag = ReadExternalPointerTag(); |
| } |
| return WriteExternalPointer(slot_accessor.external_pointer_slot(), address, |
| tag); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRawExternalReference( |
| byte 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(); |
| } |
| return WriteExternalPointer(slot_accessor.external_pointer_slot(), 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(byte data, |
| SlotAccessor slot_accessor) { |
| int index = source_.GetInt(); |
| Handle<HeapObject> heap_object = attached_objects_[index]; |
| return slot_accessor.Write(heap_object, GetAndResetNextReferenceType()); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadRegisterPendingForwardRef( |
| byte data, SlotAccessor slot_accessor) { |
| HeapObjectReferenceType ref_type = GetAndResetNextReferenceType(); |
| unresolved_forward_refs_.emplace_back(slot_accessor.object(), |
| slot_accessor.offset(), ref_type); |
| num_unresolved_forward_refs_++; |
| return 1; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadResolvePendingForwardRef( |
| byte 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. |
| DCHECK_EQ(slot_accessor.offset(), HeapObject::kHeaderSize); |
| Handle<HeapObject> obj = slot_accessor.object(); |
| int index = source_.GetInt(); |
| auto& forward_ref = unresolved_forward_refs_[index]; |
| SlotAccessorForHeapObject::ForSlotOffset(forward_ref.object, |
| forward_ref.offset) |
| .Write(*obj, forward_ref.ref_type); |
| 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(byte 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_.GetInt(); |
| // 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; |
| } |
| |
| // Custom deserialization for a Code object and its associated InstructionStream |
| // object. |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadCodeBody(byte 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); |
| // CodeBody can only occur right after the heap object header. |
| DCHECK_EQ(slot_accessor.offset(), HeapObject::kHeaderSize); |
| |
| int size_in_tagged = source_.GetInt(); |
| int size_in_bytes = size_in_tagged * kTaggedSize; |
| |
| { |
| DisallowGarbageCollection no_gc; |
| InstructionStream istream = |
| InstructionStream::cast(*slot_accessor.object()); |
| |
| // First deserialize the untagged region of the InstructionStream object. |
| source_.CopyRaw(reinterpret_cast<void*>(istream.address() + |
| InstructionStream::kDataStart), |
| size_in_bytes); |
| } |
| |
| // Then deserialize the InstructionStream header |
| ReadData(slot_accessor.object(), HeapObject::kHeaderSize / kTaggedSize, |
| InstructionStream::kDataStart / kTaggedSize); |
| |
| // Then deserialize the pre-serialized RelocInfo objects. |
| std::vector<Handle<HeapObject>> preserialized_objects; |
| while (source_.Peek() != kSynchronize) { |
| Handle<HeapObject> obj = ReadObject(); |
| preserialized_objects.push_back(obj); |
| } |
| // Skip the synchronize bytecode. |
| source_.Advance(1); |
| |
| // Finally iterate RelocInfos (the same way it was done by the serializer) |
| // and deserialize respective data into RelocInfos. The RelocIterator |
| // holds a raw pointer to the code, so we have to disable garbage |
| // collection here. It's ok though, any objects it would have needed are |
| // in the preserialized_objects vector. |
| { |
| DisallowGarbageCollection no_gc; |
| |
| InstructionStream istream = |
| InstructionStream::cast(*slot_accessor.object()); |
| Code code = istream.code(kAcquireLoad); |
| DeserializerRelocInfoVisitor visitor(this, &preserialized_objects); |
| for (RelocIterator it(code, istream, istream.relocation_info(), |
| InstructionStream::BodyDescriptor::kRelocModeMask); |
| !it.done(); it.next()) { |
| it.rinfo()->Visit(istream, &visitor); |
| } |
| } |
| |
| // Advance to the end of the code object. |
| return (int{InstructionStream::kDataStart} - HeapObject::kHeaderSize) / |
| kTaggedSize + |
| size_in_tagged; |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadVariableRepeat(byte data, |
| SlotAccessor slot_accessor) { |
| int repeats = VariableRepeatCount::Decode(source_.GetInt()); |
| return ReadRepeatedObject(slot_accessor, repeats); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadOffHeapBackingStore( |
| byte data, SlotAccessor slot_accessor) { |
| int byte_length = source_.GetInt(); |
| std::unique_ptr<BackingStore> backing_store; |
| if (data == kOffHeapBackingStore) { |
| backing_store = BackingStore::Allocate(main_thread_isolate(), byte_length, |
| SharedFlag::kNotShared, |
| InitializedFlag::kUninitialized); |
| } else { |
| int max_byte_length = source_.GetInt(); |
| 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(byte data, |
| SlotAccessor slot_accessor) { |
| DCHECK_IMPLIES(data == kSandboxedExternalReference, V8_ENABLE_SANDBOX_BOOL); |
| uint32_t reference_id = static_cast<uint32_t>(source_.GetInt()); |
| 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(); |
| } |
| return WriteExternalPointer(slot_accessor.external_pointer_slot(), address, |
| tag); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadClearedWeakReference( |
| byte data, SlotAccessor slot_accessor) { |
| return slot_accessor.Write(HeapObjectReference::ClearedValue(isolate())); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadWeakPrefix(byte data, |
| SlotAccessor slot_accessor) { |
| // 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>::ReadRootArrayConstants(byte 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); |
| Handle<HeapObject> heap_object = |
| Handle<HeapObject>::cast(isolate()->root_handle(root_index)); |
| return slot_accessor.Write(heap_object, HeapObjectReferenceType::STRONG); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadHotObject(byte data, |
| SlotAccessor slot_accessor) { |
| int index = HotObject::Decode(data); |
| Handle<HeapObject> hot_object = hot_objects_.Get(index); |
| return slot_accessor.Write(hot_object, GetAndResetNextReferenceType()); |
| } |
| |
| template <typename IsolateT> |
| template <typename SlotAccessor> |
| int Deserializer<IsolateT>::ReadFixedRawData(byte 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); |
| // 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>::ReadFixedRepeat(byte data, |
| SlotAccessor slot_accessor) { |
| int repeats = FixedRepeatWithCount::Decode(data); |
| return ReadRepeatedObject(slot_accessor, repeats); |
| } |
| |
| #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_.GetInt()); |
| return main_thread_isolate()->external_reference_table()->address( |
| reference_id); |
| } |
| |
| template <typename IsolateT> |
| ExternalPointerTag Deserializer<IsolateT>::ReadExternalPointerTag() { |
| uint64_t shifted_tag = static_cast<uint64_t>(source_.GetInt()); |
| return static_cast<ExternalPointerTag>(shifted_tag |
| << kExternalPointerTagShift); |
| } |
| |
| template <typename IsolateT> |
| 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 |
| |
| HeapObject obj = HeapObject::FromAddress(isolate()->heap()->AllocateRawOrFail( |
| size, allocation, AllocationOrigin::kRuntime, alignment)); |
| |
| #ifdef DEBUG |
| previous_allocation_obj_ = 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 internal |
| } // namespace v8 |