#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"
#include "src/objects/object-macros.h"
namespace v8::internal {
#ifdef V8_COMPRESS_POINTERS
#define PRIxTAGGED PRIx32
#else
#define PRIxTAGGED PRIxPTR
#endif
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_; }
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)) {
mode = SKIP_WRITE_BARRIER;
}
#endif
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) {
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_;
};
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(); }
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_;
};
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)) {
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
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
return (kExternalPointerSlotSize / kTaggedSize);
}
namespace {
#ifdef DEBUG
int GetNumApiReferences(Isolate* isolate) {
int num_api_references = 0;
if (isolate->isolate_data()->external_reference_table()->is_initialized()) {
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
}
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_code_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();
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
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
if (source_.position() == 0) return;
while (source_.HasMore()) DCHECK_EQ(kNop, source_.Get());
DCHECK_EQ(num_unresolved_forward_refs_, 0);
DCHECK(unresolved_forward_refs_.empty());
#endif
isolate_->RegisterDeserializerFinished();
}
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) {
string->EnsureHash(SharedStringAccessGuardIfNeeded(isolate));
return string->raw_hash_field();
}
}
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) {
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() {
FATAL("No external references provided via API");
}
void PostProcessExternalString(Tagged<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);
}
}
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()) {
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);
if (typed_array->is_on_heap()) {
typed_array->AddExternalPointerCompensationForDeserialization(
main_thread_isolate());
} else {
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 {
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());
}
} 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)) {
Tagged<String> string = Cast<String>(raw_obj);
string->set_raw_hash_field(String::kEmptyHashField);
if (space == SnapshotSpace::kReadOnlyHeap) {
PushObjectToRehash(obj);
}
} else if (raw_obj->NeedsRehashing(instance_type)) {
PushObjectToRehash(obj);
}
if (deserializing_user_code()) {
if (InstanceTypeChecker::IsInternalizedString(instance_type)) {
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);
obj.PatchValue(result);
}
return;
} else if (InstanceTypeChecker::IsScript(instance_type)) {
new_scripts_.push_back(Cast<Script>(obj));
} else if (InstanceTypeChecker::IsAllocationSite(instance_type)) {
new_allocation_sites_.push_back(Cast<AllocationSite>(obj));
} else {
DCHECK(CanBeDeferred(*obj, SlotType::kAnySlot) ||
InstanceTypeChecker::IsByteArray(instance_type));
}
}
}
if (InstanceTypeChecker::IsInstructionStream(instance_type)) {
if (deserializing_user_code()) {
new_code_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);
sfi->set_unique_id(isolate()->GetAndIncNextUniqueSfiId());
} else if (InstanceTypeChecker::IsMap(instance_type)) {
if (v8_flags.log_maps) {
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)) {
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];
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;
}
}
}
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;
DCHECK_NE(source()->Peek(), kRegisterPendingForwardRef);
DirectHandle<Map> map = Cast<Map>(ReadObject());
AllocationType allocation = SpaceToAllocation(space);
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);
}
}
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()));
if (IsSharedFunctionInfo(raw_obj, isolate())) {
Cast<SharedFunctionInfo>(raw_obj)->set_age(0);
} else if (IsEphemeronHashTable(raw_obj)) {
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());
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
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));
}
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 <int byte_code_count, int expected>
constexpr uint8_t VerifyBytecodeCount(uint8_t bytecode) {
static_assert(byte_code_count == expected);
return bytecode;
}
}
#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)
#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)
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:
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
}
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!)";
}
}
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);
ReferenceDescriptor descr = GetAndResetNextReferenceDescriptor();
DirectHandle<HeapObject> heap_object = ReadObject(space);
if (v8_flags.trace_deserialization) {
--depth_;
}
return WriteHeapPointer(slot_accessor, heap_object, descr);
}
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);
}
return WriteHeapPointer(slot_accessor, heap_object,
GetAndResetNextReferenceDescriptor());
}
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();
ReadOnlyPageMetadata* 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);
}
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);
}
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadStartupObjectCache(uint8_t data,
SlotAccessor slot_accessor) {
int cache_index = source_.GetUint30();
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());
}
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadSharedHeapObjectCache(
uint8_t data, SlotAccessor slot_accessor) {
int cache_index = source_.GetUint30();
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());
}
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);
}
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);
}
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) {
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) {
unresolved_forward_refs_.clear();
} else {
forward_ref.object = Handle<HeapObject>();
}
return 0;
}
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadVariableRawData(uint8_t data,
SlotAccessor slot_accessor) {
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");
}
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) {
int byte_length = source_.GetUint32();
if (v8_flags.trace_deserialization) {
PrintF("%*sOffHeapBackingStore [%d]\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 {
int 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_, "");
}
DCHECK(!next_reference_is_weak_);
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_, "");
}
DCHECK(!next_reference_is_indirect_pointer_);
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
}
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) {
DCHECK(!next_reference_is_protected_pointer);
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) {
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());
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);
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");
}
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()) {
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>;
}
#include "src/objects/object-macros-undef.h"