%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/snapshot/
Upload File :
Create Path :
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/snapshot/deserializer.cc

// 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(ExternalPointerTag tag) const {
    return object_->RawExternalPointerField(offset_, tag);
  }
  Handle<HeapObject> object() const { return object_; }
  int offset() const { return offset_; }

  // Writes the given value to this slot, 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(Tagged<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);
  }

  int WriteIndirect(Tagged<HeapObject> value) {
    // TODO(saelo): currently, only Code objects can be referenced indirectly
    // through a pointer table, and so here we directly downcast to Code
    // because we need to know the offset at which the object has its pointer
    // table entry index. However, in the future we might have other types of
    // objects be referenced through the table. In that case, we should
    // probably introduce some kind of "ExternalHeapObject" class which, in
    // sandbox builds, owns a pointer table entry and contains the handle for
    // it. Then we can unify this handling here to expect an ExternalHeapObject
    // and simply copy the handle into the slot. For that we don't even need to
    // know which table the object uses.
    CHECK(IsCode(value));
    Tagged<Code> code = Code::cast(value);
    InstanceType instance_type = value->map()->instance_type();
    IndirectPointerTag tag = IndirectPointerTagFromInstanceType(instance_type);
    IndirectPointerSlot dest = object_->RawIndirectPointerField(offset_, tag);
    dest.store(code);

    IndirectPointerWriteBarrier(*object_, dest, value, UPDATE_WRITE_BARRIER);
    return 1;
  }

 private:
  SlotAccessorForHeapObject(Handle<HeapObject> object, int offset)
      : object_(object), offset_(offset) {}

  const Handle<HeapObject> object_;
  const int offset_;
};

// A SlotAccessor for absolute full slot addresses.
class SlotAccessorForRootSlots {
 public:
  explicit SlotAccessorForRootSlots(FullMaybeObjectSlot slot) : slot_(slot) {}

  FullMaybeObjectSlot slot() const { return slot_; }
  ExternalPointerSlot external_pointer_slot(ExternalPointerTag tag) const {
    UNREACHABLE();
  }
  Handle<HeapObject> object() const { UNREACHABLE(); }
  int offset() const { UNREACHABLE(); }

  // Writes the given value to this slot, 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(Tagged<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);
  }
  int WriteIndirect(Tagged<HeapObject> value) { UNREACHABLE(); }

 private:
  const FullMaybeObjectSlot slot_;
};

// A SlotAccessor for creating a Handle, which saves a Handle allocation when
// a Handle already exists.
template <typename IsolateT>
class SlotAccessorForHandle {
 public:
  SlotAccessorForHandle(Handle<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(MaybeObject value, int slot_offset = 0) { UNREACHABLE(); }
  int Write(Tagged<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;
  }
  int WriteIndirect(Tagged<HeapObject> value) { UNREACHABLE(); }

 private:
  Handle<HeapObject>* handle_;
  IsolateT* isolate_;
};

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::WriteHeapPointer(SlotAccessor slot_accessor,
                                             Tagged<HeapObject> heap_object,
                                             ReferenceDescriptor descr) {
  if (descr.is_indirect_pointer) {
    return slot_accessor.WriteIndirect(heap_object);
  } else {
    return slot_accessor.Write(heap_object, descr.type);
  }
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::WriteHeapPointer(SlotAccessor slot_accessor,
                                             Handle<HeapObject> heap_object,
                                             ReferenceDescriptor descr) {
  if (descr.is_indirect_pointer) {
    return slot_accessor.WriteIndirect(*heap_object);
  } else {
    return slot_accessor.Write(heap_object, descr.type);
  }
}

template <typename IsolateT>
int Deserializer<IsolateT>::WriteExternalPointer(ExternalPointerSlot dest,
                                                 Address value) {
  DCHECK(!next_reference_is_weak_ && !next_reference_is_indirect_pointer_);
  dest.init(main_thread_isolate(), value);
  // ExternalPointers can only be written into HeapObject fields, therefore they
  // cover (kExternalPointerSlotSize / kTaggedSize) slots.
  return (kExternalPointerSlotSize / kTaggedSize);
}

namespace {
#ifdef DEBUG
int GetNumApiReferences(Isolate* isolate) {
  int num_api_references = 0;
  // The read-only deserializer is run by read-only heap set-up before the
  // heap is fully set up. External reference table relies on a few parts of
  // this set-up (like old-space), so it may be uninitialized at this point.
  if (isolate->isolate_data()->external_reference_table()->is_initialized()) {
    // Count the number of external references registered through the API.
    if (isolate->api_external_references() != nullptr) {
      while (isolate->api_external_references()[num_api_references] != 0) {
        num_api_references++;
      }
    }
  }
  return num_api_references;
}
int GetNumApiReferences(LocalIsolate* isolate) { return 0; }
#endif
}  // namespace

template <typename IsolateT>
Deserializer<IsolateT>::Deserializer(IsolateT* isolate,
                                     base::Vector<const uint8_t> payload,
                                     uint32_t magic_number,
                                     bool deserializing_user_code,
                                     bool can_rehash)
    : isolate_(isolate),
      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 uint8_t 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(Tagged<Script> script) {
  DisallowGarbageCollection no_gc;
  LOG(isolate(), ScriptEvent(ScriptEventType::kDeserialize, script->id()));
  LOG(isolate(), ScriptDetails(script));
}

namespace {
template <typename IsolateT>
uint32_t ComputeRawHashField(IsolateT* isolate, Tagged<String> string) {
  // Make sure raw_hash_field() is computed.
  string->EnsureHash(SharedStringAccessGuardIfNeeded(isolate));
  return string->raw_hash_field();
}
}  // namespace

StringTableInsertionKey::StringTableInsertionKey(
    Isolate* isolate, 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(IsInternalizedString(*string));
}

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(IsInternalizedString(*string));
}

template <typename IsolateT>
bool StringTableInsertionKey::IsMatch(IsolateT* isolate,
                                      Tagged<String> string) {
  // We want to compare the content of two strings here.
  return string_->SlowEquals(string, SharedStringAccessGuardIfNeeded(isolate));
}
template bool StringTableInsertionKey::IsMatch(Isolate* isolate,
                                               Tagged<String> string);
template bool StringTableInsertionKey::IsMatch(LocalIsolate* isolate,
                                               Tagged<String> string);

namespace {

void NoExternalReferencesCallback() {
  // The following check will trigger if a function or object template
  // with references to native functions have been deserialized from
  // snapshot, but no actual external references were provided when the
  // isolate was created.
  FATAL("No external references provided via API");
}

void PostProcessExternalString(Tagged<ExternalString> string,
                               Isolate* isolate) {
  DisallowGarbageCollection no_gc;
  uint32_t index = string->GetResourceRefForDeserialization();
  Address address =
      static_cast<Address>(isolate->api_external_references()[index]);
  string->InitExternalPointerFields(isolate);
  string->set_address_as_resource(isolate, address);
  isolate->heap()->UpdateExternalString(string, 0,
                                        string->ExternalPayloadSize());
  isolate->heap()->RegisterExternalString(string);
}

}  // namespace

// Should be called only on the main thread (not thread safe).
template <>
void Deserializer<Isolate>::PostProcessNewJSReceiver(Tagged<Map> map,
                                                     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(
    Tagged<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;
  Tagged<Map> raw_map = *map;
  DCHECK_EQ(raw_map, obj->map(isolate_));
  InstanceType instance_type = raw_map->instance_type();
  Tagged<HeapObject> raw_obj = *obj;
  DCHECK_IMPLIES(deserializing_user_code(), should_rehash());
  if (should_rehash()) {
    if (InstanceTypeChecker::IsString(instance_type)) {
      // Uninitialize hash field as we need to recompute the hash.
      Tagged<String> string = 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);
        Tagged<String> result =
            *isolate()->string_table()->LookupKey(isolate(), &key);

        if (result != raw_obj) {
          String::cast(raw_obj)->MakeThin(isolate(), result);
          // Mutate the given object handle so that the backreference entry is
          // also updated.
          obj.PatchValue(result);
        }
        return;
      } else if (InstanceTypeChecker::IsScript(instance_type)) {
        new_scripts_.push_back(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)) {
    Tagged<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::IsSharedFunctionInfo(instance_type)) {
    Tagged<SharedFunctionInfo> sfi = SharedFunctionInfo::cast(raw_obj);
    // Reset the id to avoid collisions - it must be unique in this isolate.
    sfi->set_unique_id(isolate()->GetAndIncNextUniqueSfiId());
  } else if (InstanceTypeChecker::IsMap(instance_type)) {
    if (v8_flags.log_maps) {
      // Keep track of all seen Maps to log them later since they might be only
      // partially initialized at this point.
      new_maps_.push_back(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>
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;
  return desc;
}

template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::GetBackReferencedObject() {
  Handle<HeapObject> obj = back_refs_[source_.GetUint30()];

  // We don't allow ThinStrings in backreferences -- if internalization produces
  // a thin string, then it should also update the backref handle.
  DCHECK(!IsThinString(*obj, isolate()));

  hot_objects_.Add(obj);
  DCHECK(!HasWeakHeapObjectTag(*obj));
  return obj;
}

template <typename IsolateT>
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;
    case SnapshotSpace::kTrusted:
      return AllocationType::kTrusted;
  }
}
}  // namespace

template <typename IsolateT>
Handle<HeapObject> Deserializer<IsolateT>::ReadObject(SnapshotSpace space) {
  const int size_in_tagged = source_.GetUint30();
  const int size_in_bytes = size_in_tagged * kTaggedSize;

  // The map can't be a forward ref. If you want the map to be a forward ref,
  // then you're probably serializing the meta-map, in which case you want to
  // use the 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`).
  Tagged<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 (IsSharedFunctionInfo(raw_obj, isolate())) {
    SharedFunctionInfo::cast(raw_obj)->set_age(0);
  } else if (IsEphemeronHashTable(raw_obj)) {
    // Make sure EphemeronHashTables have valid HeapObject keys, so that the
    // marker does not break when marking EphemeronHashTable, see
    // MarkingVisitorBase::VisitEphemeronHashTable.
    Tagged<EphemeronHashTable> table = 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 (IsJSObject(raw_obj, cage_base) &&
      JSObject::cast(raw_obj)->MayHaveEmbedderFields()) {
    Tagged<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 (IsEmbedderDataArray(raw_obj, cage_base)) {
    Tagged<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 (IsInstructionStream(*obj, 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;

  Tagged<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;
}

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 uint8_t VerifyBytecodeCount(uint8_t bytecode) {
  static_assert(byte_code_count == expected);
  return bytecode;
}

}  // namespace

// Helper macro (and its implementation detail) for specifying a range of cases.
// Use as "case CASE_RANGE(byte_code, num_bytecodes):"
#define CASE_RANGE(byte_code, num_bytecodes) \
  CASE_R##num_bytecodes(                     \
      (VerifyBytecodeCount<byte_code##Count, num_bytecodes>(byte_code)))
#define CASE_R1(byte_code) byte_code
#define CASE_R2(byte_code) CASE_R1(byte_code) : case CASE_R1(byte_code + 1)
#define CASE_R3(byte_code) CASE_R2(byte_code) : case CASE_R1(byte_code + 2)
#define CASE_R4(byte_code) CASE_R2(byte_code) : case CASE_R2(byte_code + 2)
#define CASE_R8(byte_code) CASE_R4(byte_code) : case CASE_R4(byte_code + 4)
#define CASE_R16(byte_code) CASE_R8(byte_code) : case CASE_R8(byte_code + 8)
#define CASE_R32(byte_code) CASE_R16(byte_code) : case CASE_R16(byte_code + 16)

// This generates a case range for all the spaces.
// clang-format off
#define CASE_RANGE_ALL_SPACES(bytecode)                                \
  SpaceEncoder<bytecode>::Encode(SnapshotSpace::kOld):                 \
    case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kCode):         \
    case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kReadOnlyHeap): \
    case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kTrusted)
// clang-format on

template <typename IsolateT>
void Deserializer<IsolateT>::ReadData(Handle<HeapObject> object,
                                      int start_slot_index,
                                      int end_slot_index) {
  int current = start_slot_index;
  while (current < end_slot_index) {
    uint8_t data = source_.Get();
    current += ReadSingleBytecodeData(
        data, SlotAccessorForHeapObject::ForSlotIndex(object, current));
  }
  CHECK_EQ(current, end_slot_index);
}

template <typename IsolateT>
void Deserializer<IsolateT>::ReadData(FullMaybeObjectSlot start,
                                      FullMaybeObjectSlot end) {
  FullMaybeObjectSlot current = start;
  while (current < end) {
    uint8_t data = source_.Get();
    current += ReadSingleBytecodeData(data, SlotAccessorForRootSlots(current));
  }
  CHECK_EQ(current, end);
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadSingleBytecodeData(uint8_t data,
                                                   SlotAccessor slot_accessor) {
  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 kNewMetaMap:
      return ReadNewMetaMap(data, slot_accessor);
    case kSandboxedExternalReference:
    case kExternalReference:
      return ReadExternalReference(data, slot_accessor);
    case kSandboxedRawExternalReference:
      return ReadRawExternalReference(data, slot_accessor);
    case kAttachedReference:
      return ReadAttachedReference(data, slot_accessor);
    case kNop:
      return 0;
    case kRegisterPendingForwardRef:
      return ReadRegisterPendingForwardRef(data, slot_accessor);
    case kResolvePendingForwardRef:
      return ReadResolvePendingForwardRef(data, slot_accessor);
    case kSynchronize:
      //  If we get here then that indicates that you have a mismatch between
      //  the number of GC roots when serializing and deserializing.
      UNREACHABLE();
    case kVariableRawData:
      return ReadVariableRawData(data, slot_accessor);
    case 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 kIndirectPointerPrefix:
      return ReadIndirectPointerPrefix(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(uint8_t data,
                                          SlotAccessor slot_accessor) {
  SnapshotSpace space = NewObject::Decode(data);
  DCHECK_IMPLIES(V8_STATIC_ROOTS_BOOL, space != SnapshotSpace::kReadOnlyHeap);
  // Save the descriptor before recursing down into reading the object.
  ReferenceDescriptor descr = GetAndResetNextReferenceDescriptor();
  Handle<HeapObject> heap_object = ReadObject(space);
  return WriteHeapPointer(slot_accessor, heap_object, descr);
}

// Find a recently deserialized object using its offset from the current
// allocation point and write a pointer to it to the current object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadBackref(uint8_t data,
                                        SlotAccessor slot_accessor) {
  Handle<HeapObject> heap_object = GetBackReferencedObject();
  return WriteHeapPointer(slot_accessor, heap_object,
                          GetAndResetNextReferenceDescriptor());
}

// Reference an object in the read-only heap.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadReadOnlyHeapRef(uint8_t data,
                                                SlotAccessor slot_accessor) {
  uint32_t chunk_index = source_.GetUint30();
  uint32_t chunk_offset = source_.GetUint30();

  ReadOnlySpace* read_only_space = isolate()->heap()->read_only_space();
  ReadOnlyPage* page = read_only_space->pages()[chunk_index];
  Address address = page->OffsetToAddress(chunk_offset);
  Tagged<HeapObject> heap_object = HeapObject::FromAddress(address);

  return WriteHeapPointer(slot_accessor, heap_object,
                          GetAndResetNextReferenceDescriptor());
}

// Find an object in the roots array and write a pointer to it to the
// current object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadRootArray(uint8_t data,
                                          SlotAccessor slot_accessor) {
  int id = source_.GetUint30();
  RootIndex root_index = static_cast<RootIndex>(id);
  Handle<HeapObject> heap_object =
      Handle<HeapObject>::cast(isolate()->root_handle(root_index));
  hot_objects_.Add(heap_object);
  return WriteHeapPointer(slot_accessor, heap_object,
                          GetAndResetNextReferenceDescriptor());
}

// Find an object in the startup object cache and write a pointer to it to
// the current object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadStartupObjectCache(uint8_t data,
                                                   SlotAccessor slot_accessor) {
  int cache_index = source_.GetUint30();
  // TODO(leszeks): Could we use the address of the startup_object_cache
  // entry as a Handle backing?
  Tagged<HeapObject> heap_object = HeapObject::cast(
      main_thread_isolate()->startup_object_cache()->at(cache_index));
  return WriteHeapPointer(slot_accessor, heap_object,
                          GetAndResetNextReferenceDescriptor());
}

// Find an object in the shared heap object cache and write a pointer to it
// to the current object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadSharedHeapObjectCache(
    uint8_t data, SlotAccessor slot_accessor) {
  int cache_index = source_.GetUint30();
  // TODO(leszeks): Could we use the address of the
  // shared_heap_object_cache entry as a Handle backing?
  Tagged<HeapObject> heap_object = HeapObject::cast(
      main_thread_isolate()->shared_heap_object_cache()->at(cache_index));
  DCHECK(SharedHeapSerializer::ShouldBeInSharedHeapObjectCache(heap_object));
  return WriteHeapPointer(slot_accessor, heap_object,
                          GetAndResetNextReferenceDescriptor());
}

// Deserialize a new meta-map and write a pointer to it to the current
// object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadNewMetaMap(uint8_t data,
                                           SlotAccessor slot_accessor) {
  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(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();
  }
  return WriteExternalPointer(slot_accessor.external_pointer_slot(tag),
                              address);
}

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();
  }
  return WriteExternalPointer(slot_accessor.external_pointer_slot(tag),
                              address);
}

// Find an object in the attached references and write a pointer to it to
// the current object.
template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadAttachedReference(uint8_t data,
                                                  SlotAccessor slot_accessor) {
  int index = source_.GetUint30();
  Handle<HeapObject> heap_object = attached_objects_[index];
  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();
  DCHECK(!descr.is_indirect_pointer);
  unresolved_forward_refs_.emplace_back(slot_accessor.object(),
                                        slot_accessor.offset(), descr.type);
  num_unresolved_forward_refs_++;
  return 1;
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadResolvePendingForwardRef(
    uint8_t data, SlotAccessor slot_accessor) {
  // Pending forward refs can only be resolved after the heap object's map
  // field is deserialized; currently they only appear immediately after
  // the map field.
  DCHECK_EQ(slot_accessor.offset(), HeapObject::kHeaderSize);
  Handle<HeapObject> obj = slot_accessor.object();
  int index = source_.GetUint30();
  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(uint8_t data,
                                                SlotAccessor slot_accessor) {
  // This operation is only supported for tagged-size slots, else we might
  // become misaligned.
  DCHECK_EQ(decltype(slot_accessor.slot())::kSlotDataSize, kTaggedSize);
  int size_in_tagged = source_.GetUint30();
  // TODO(leszeks): Only copy slots when there are Smis in the serialized
  // data.
  source_.CopySlots(slot_accessor.slot().location(), size_in_tagged);
  return size_in_tagged;
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadVariableRepeat(uint8_t data,
                                               SlotAccessor slot_accessor) {
  int repeats = VariableRepeatCount::Decode(source_.GetUint30());
  return ReadRepeatedObject(slot_accessor, repeats);
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadOffHeapBackingStore(
    uint8_t data, SlotAccessor slot_accessor) {
  int byte_length = source_.GetUint32();
  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();
  }
  return WriteExternalPointer(slot_accessor.external_pointer_slot(tag),
                              address);
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadClearedWeakReference(
    uint8_t data, SlotAccessor slot_accessor) {
  return slot_accessor.Write(HeapObjectReference::ClearedValue(isolate()));
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadWeakPrefix(uint8_t 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>::ReadIndirectPointerPrefix(
    uint8_t data, SlotAccessor slot_accessor) {
  // We shouldn't have two indirect pointer prefixes in a row.
  DCHECK(!next_reference_is_indirect_pointer_);
  // We shouldn't have a indirect pointer prefix without a current object.
  DCHECK_NE(slot_accessor.object()->address(), kNullAddress);
  next_reference_is_indirect_pointer_ = true;
  return 0;
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadRootArrayConstants(uint8_t data,
                                                   SlotAccessor slot_accessor) {
  // First kRootArrayConstantsCount roots are guaranteed to be in
  // the old space.
  static_assert(static_cast<int>(RootIndex::kFirstImmortalImmovableRoot) == 0);
  static_assert(kRootArrayConstantsCount <=
                static_cast<int>(RootIndex::kLastImmortalImmovableRoot));

  RootIndex root_index = RootArrayConstant::Decode(data);
  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(uint8_t data,
                                          SlotAccessor slot_accessor) {
  int index = HotObject::Decode(data);
  Handle<HeapObject> hot_object = hot_objects_.Get(index);
  return WriteHeapPointer(slot_accessor, hot_object,
                          GetAndResetNextReferenceDescriptor());
}

template <typename IsolateT>
template <typename SlotAccessor>
int Deserializer<IsolateT>::ReadFixedRawData(uint8_t data,
                                             SlotAccessor slot_accessor) {
  using TSlot = decltype(slot_accessor.slot());

  // Deserialize raw data of fixed length from 1 to 32 times kTaggedSize.
  int size_in_tagged = FixedRawDataWithSize::Decode(data);
  static_assert(TSlot::kSlotDataSize == kTaggedSize ||
                TSlot::kSlotDataSize == 2 * kTaggedSize);
  int size_in_slots = size_in_tagged / (TSlot::kSlotDataSize / kTaggedSize);
  // kFixedRawData can have kTaggedSize != TSlot::kSlotDataSize when
  // serializing Smi roots in pointer-compressed builds. In this case, the
  // size in bytes is unconditionally the (full) slot size.
  DCHECK_IMPLIES(kTaggedSize != TSlot::kSlotDataSize, size_in_slots == 1);
  // 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(uint8_t 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_.GetUint30());
  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_.GetUint30());
  return static_cast<ExternalPointerTag>(shifted_tag
                                         << kExternalPointerTagShift);
}

template <typename IsolateT>
Tagged<HeapObject> Deserializer<IsolateT>::Allocate(
    AllocationType allocation, int size, AllocationAlignment alignment) {
#ifdef DEBUG
  if (!previous_allocation_obj_.is_null()) {
    // Make sure that the previous object is initialized sufficiently to
    // be iterated over by the GC.
    int object_size = previous_allocation_obj_->Size(isolate_);
    DCHECK_LE(object_size, previous_allocation_size_);
  }
#endif

  Tagged<HeapObject> obj =
      HeapObject::FromAddress(isolate()->heap()->AllocateRawOrFail(
          size, allocation, AllocationOrigin::kRuntime, alignment));

#ifdef DEBUG
  previous_allocation_obj_ = 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

Zerion Mini Shell 1.0