// 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/base/platform/wrappers.h"
#include "src/codegen/assembler-inl.h"
#include "src/common/assert-scope.h"
#include "src/common/external-pointer.h"
#include "src/common/globals.h"
#include "src/execution/isolate.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/read-only-heap.h"
#include "src/interpreter/interpreter.h"
#include "src/logging/log.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/cell-inl.h"
#include "src/objects/embedder-data-array-inl.h"
#include "src/objects/hash-table.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-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/smi.h"
#include "src/objects/string.h"
#include "src/roots/roots.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/snapshot/references.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/snapshot-data.h"
#include "src/snapshot/snapshot.h"
#include "src/tracing/trace-event.h"
#include "src/tracing/traced-value.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_); }
  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);
    WriteBarrier::Marking(*object_, current_slot, value);
    // No need for a generational write barrier.
    DCHECK(!Heap::InYoungGeneration(value));
    return 1;
  }
  int Write(HeapObject value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    return Write(HeapObjectReference::From(value, ref_type), slot_offset);
  }
  int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    return Write(*value, ref_type, slot_offset);
  }

  // Same as Write, but additionally with a generational barrier.
  int WriteWithGenerationalBarrier(MaybeObject value) {
    MaybeObjectSlot current_slot = slot();
    current_slot.Relaxed_Store(value);
    WriteBarrier::Marking(*object_, current_slot, value);
    if (Heap::InYoungGeneration(value)) {
      GenerationalBarrier(*object_, current_slot, value);
    }
    return 1;
  }
  int WriteWithGenerationalBarrier(HeapObject value,
                                   HeapObjectReferenceType ref_type) {
    return WriteWithGenerationalBarrier(
        HeapObjectReference::From(value, ref_type));
  }
  int WriteWithGenerationalBarrier(Handle<HeapObject> value,
                                   HeapObjectReferenceType ref_type) {
    return WriteWithGenerationalBarrier(*value, ref_type);
  }

 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_; }
  Handle<HeapObject> object() const { UNREACHABLE(); }
  int offset() const { UNREACHABLE(); }

  // Writes the given value to this slot, optionally with an offset (e.g. for
  // repeat writes). Returns the number of slots written (which is one).
  int Write(MaybeObject value, int slot_offset = 0) {
    FullMaybeObjectSlot current_slot = slot() + slot_offset;
    current_slot.Relaxed_Store(value);
    return 1;
  }
  int Write(HeapObject value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    return Write(HeapObjectReference::From(value, ref_type), slot_offset);
  }
  int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    return Write(*value, ref_type, slot_offset);
  }

  int WriteWithGenerationalBarrier(MaybeObject value) { return Write(value); }
  int WriteWithGenerationalBarrier(HeapObject value,
                                   HeapObjectReferenceType ref_type) {
    return WriteWithGenerationalBarrier(
        HeapObjectReference::From(value, ref_type));
  }
  int WriteWithGenerationalBarrier(Handle<HeapObject> value,
                                   HeapObjectReferenceType ref_type) {
    return WriteWithGenerationalBarrier(*value, ref_type);
  }

 private:
  const FullMaybeObjectSlot slot_;
};

// A SlotAccessor for creating a Handle, which saves a Handle allocation when
// a Handle already exists.
class SlotAccessorForHandle {
 public:
  SlotAccessorForHandle(Handle<HeapObject>* handle, Isolate* isolate)
      : handle_(handle), isolate_(isolate) {}

  MaybeObjectSlot slot() const { UNREACHABLE(); }
  Handle<HeapObject> object() const { UNREACHABLE(); }
  int offset() const { UNREACHABLE(); }

  int Write(MaybeObject value, int slot_offset = 0) { UNREACHABLE(); }
  int Write(HeapObject value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    DCHECK_EQ(slot_offset, 0);
    DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG);
    *handle_ = handle(value, isolate_);
    return 1;
  }
  int Write(Handle<HeapObject> value, HeapObjectReferenceType ref_type,
            int slot_offset = 0) {
    DCHECK_EQ(slot_offset, 0);
    DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG);
    *handle_ = value;
    return 1;
  }

  int WriteWithGenerationalBarrier(HeapObject value,
                                   HeapObjectReferenceType ref_type) {
    return Write(value, ref_type);
  }
  int WriteWithGenerationalBarrier(Handle<HeapObject> value,
                                   HeapObjectReferenceType ref_type) {
    return Write(value, ref_type);
  }

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

template <typename TSlot>
int Deserializer::WriteAddress(TSlot dest, Address value) {
  DCHECK(!next_reference_is_weak_);
  base::Memcpy(dest.ToVoidPtr(), &value, kSystemPointerSize);
  STATIC_ASSERT(IsAligned(kSystemPointerSize, TSlot::kSlotDataSize));
  return (kSystemPointerSize / TSlot::kSlotDataSize);
}

template <typename TSlot>
int Deserializer::WriteExternalPointer(TSlot dest, Address value,
                                       ExternalPointerTag tag) {
  DCHECK(!next_reference_is_weak_);
  InitExternalPointerField(dest.address(), isolate(), value, tag);
  STATIC_ASSERT(IsAligned(kExternalPointerSize, TSlot::kSlotDataSize));
  return (kExternalPointerSize / TSlot::kSlotDataSize);
}

Deserializer::Deserializer(Isolate* isolate, Vector<const byte> payload,
                           uint32_t magic_number, bool deserializing_user_code,
                           bool can_rehash)
    : isolate_(isolate),
      source_(payload),
      magic_number_(magic_number),
      deserializing_user_code_(deserializing_user_code),
      can_rehash_(can_rehash) {
  DCHECK_NOT_NULL(isolate);
  isolate_->RegisterDeserializerStarted();

  // We start the indices here at 1, so that we can distinguish between an
  // actual index and a nullptr (serialized as kNullRefSentinel) in a
  // deserialized object requiring fix-up.
  STATIC_ASSERT(kNullRefSentinel == 0);
  backing_stores_.push_back({});

#ifdef DEBUG
  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_++;
      }
    }
  }
#endif  // DEBUG
  CHECK_EQ(magic_number_, SerializedData::kMagicNumber);
}

void Deserializer::Rehash() {
  DCHECK(can_rehash() || deserializing_user_code());
  for (Handle<HeapObject> item : to_rehash_) {
    item->RehashBasedOnMap(isolate());
  }
}

Deserializer::~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.
void Deserializer::VisitRootPointers(Root root, const char* description,
                                     FullObjectSlot start, FullObjectSlot end) {
  ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end));
}

void Deserializer::Synchronize(VisitorSynchronization::SyncTag tag) {
  static const byte expected = kSynchronize;
  CHECK_EQ(expected, source_.Get());
}

void Deserializer::DeserializeDeferredObjects() {
  for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) {
    SnapshotSpace space = NewObject::Decode(code);
    ReadObject(space);
  }
}

void Deserializer::LogNewMapEvents() {
  DisallowGarbageCollection no_gc;
  for (Handle<Map> map : new_maps_) {
    DCHECK(FLAG_trace_maps);
    LOG(isolate(), MapCreate(*map));
    LOG(isolate(), MapDetails(*map));
  }
}

void Deserializer::WeakenDescriptorArrays() {
  DisallowGarbageCollection no_gc;
  for (Handle<DescriptorArray> descriptor_array : new_descriptor_arrays_) {
    DCHECK(descriptor_array->IsStrongDescriptorArray());
    descriptor_array->set_map(ReadOnlyRoots(isolate()).descriptor_array_map());
    WriteBarrier::Marking(*descriptor_array,
                          descriptor_array->number_of_descriptors());
  }
}

void Deserializer::LogScriptEvents(Script script) {
  DisallowGarbageCollection no_gc;
  LOG(isolate(),
      ScriptEvent(Logger::ScriptEventType::kDeserialize, script.id()));
  LOG(isolate(), ScriptDetails(script));
}

StringTableInsertionKey::StringTableInsertionKey(Handle<String> string)
    : StringTableKey(ComputeRawHashField(*string), string->length()),
      string_(string) {
  DCHECK(string->IsInternalizedString());
}

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

Handle<String> StringTableInsertionKey::AsHandle(Isolate* isolate) {
  return string_;
}

uint32_t StringTableInsertionKey::ComputeRawHashField(String string) {
  // Make sure raw_hash_field() is computed.
  string.EnsureHash();
  return string.raw_hash_field();
}

void Deserializer::PostProcessNewObject(Handle<Map> map, Handle<HeapObject> obj,
                                        SnapshotSpace space) {
  DCHECK_EQ(*map, obj->map());
  DisallowGarbageCollection no_gc;
  InstanceType instance_type = map->instance_type();

  if ((FLAG_rehash_snapshot && can_rehash_) || deserializing_user_code()) {
    if (InstanceTypeChecker::IsString(instance_type)) {
      // Uninitialize hash field as we need to recompute the hash.
      Handle<String> string = Handle<String>::cast(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) {
        to_rehash_.push_back(obj);
      }
    } else if (obj->NeedsRehashing(instance_type)) {
      to_rehash_.push_back(obj);
    }
  }

  if (deserializing_user_code()) {
    if (InstanceTypeChecker::IsInternalizedString(instance_type)) {
      // Canonicalize the internalized string. If it already exists in the
      // string table, set it to forward to the existing one.
      Handle<String> string = Handle<String>::cast(obj);

      StringTableInsertionKey key(string);
      Handle<String> result =
          isolate()->string_table()->LookupKey(isolate(), &key);

      if (FLAG_thin_strings && *result != *string) {
        string->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 {
      DCHECK(CanBeDeferred(*obj));
    }
  }

  if (InstanceTypeChecker::IsScript(instance_type)) {
    LogScriptEvents(Script::cast(*obj));
  } else if (InstanceTypeChecker::IsCode(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<Code>::cast(obj));
    }
  } else if (InstanceTypeChecker::IsMap(instance_type)) {
    if (FLAG_trace_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)) {
    Handle<ExternalString> string = Handle<ExternalString>::cast(obj);
    uint32_t index = string->GetResourceRefForDeserialization();
    Address address =
        static_cast<Address>(isolate()->api_external_references()[index]);
    string->AllocateExternalPointerEntries(isolate());
    string->set_address_as_resource(isolate(), address);
    isolate()->heap()->UpdateExternalString(*string, 0,
                                            string->ExternalPayloadSize());
    isolate()->heap()->RegisterExternalString(*string);
  } else if (InstanceTypeChecker::IsJSDataView(instance_type)) {
    Handle<JSDataView> data_view = Handle<JSDataView>::cast(obj);
    JSArrayBuffer buffer = JSArrayBuffer::cast(data_view->buffer());
    void* backing_store = nullptr;
    uint32_t store_index = buffer.GetBackingStoreRefForDeserialization();
    if (store_index != kNullRefSentinel) {
      // The backing store of the JSArrayBuffer has not been correctly restored
      // yet, as that may trigger GC. The backing_store field currently contains
      // a numbered reference to an already deserialized backing store.
      backing_store = backing_stores_[store_index]->buffer_start();
    }
    data_view->AllocateExternalPointerEntries(isolate());
    data_view->set_data_pointer(
        isolate(),
        reinterpret_cast<uint8_t*>(backing_store) + data_view->byte_offset());
  } else if (InstanceTypeChecker::IsJSTypedArray(instance_type)) {
    Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(obj);
    // Fixup typed array pointers.
    if (typed_array->is_on_heap()) {
      Address raw_external_pointer = typed_array->external_pointer_raw();
      typed_array->AllocateExternalPointerEntries(isolate());
      typed_array->SetOnHeapDataPtr(
          isolate(), HeapObject::cast(typed_array->base_pointer()),
          raw_external_pointer);
    } else {
      // Serializer writes backing store ref as a DataPtr() value.
      uint32_t store_index =
          typed_array->GetExternalBackingStoreRefForDeserialization();
      auto backing_store = backing_stores_[store_index];
      auto start = backing_store
                       ? reinterpret_cast<byte*>(backing_store->buffer_start())
                       : nullptr;
      typed_array->AllocateExternalPointerEntries(isolate());
      typed_array->SetOffHeapDataPtr(isolate(), start,
                                     typed_array->byte_offset());
    }
  } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) {
    Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(obj);
    // Postpone allocation of backing store to avoid triggering the GC.
    if (buffer->GetBackingStoreRefForDeserialization() != kNullRefSentinel) {
      new_off_heap_array_buffers_.push_back(buffer);
    } else {
      buffer->AllocateExternalPointerEntries(isolate());
      buffer->set_backing_store(isolate(), nullptr);
    }
  } else if (InstanceTypeChecker::IsBytecodeArray(instance_type)) {
    // TODO(mythria): Remove these once we store the default values for these
    // fields in the serializer.
    Handle<BytecodeArray> bytecode_array = Handle<BytecodeArray>::cast(obj);
    bytecode_array->set_osr_loop_nesting_level(0);
  } else if (InstanceTypeChecker::IsDescriptorArray(instance_type)) {
    DCHECK(InstanceTypeChecker::IsStrongDescriptorArray(instance_type));
    Handle<DescriptorArray> descriptors = Handle<DescriptorArray>::cast(obj);
    new_descriptor_arrays_.push_back(descriptors);
  }

  // Check alignment.
  DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(),
                                    HeapObject::RequiredAlignment(*map)));
}

HeapObjectReferenceType Deserializer::GetAndResetNextReferenceType() {
  HeapObjectReferenceType type = next_reference_is_weak_
                                     ? HeapObjectReferenceType::WEAK
                                     : HeapObjectReferenceType::STRONG;
  next_reference_is_weak_ = false;
  return type;
}

Handle<HeapObject> Deserializer::GetBackReferencedObject() {
  Handle<HeapObject> obj = back_refs_[source_.GetInt()];

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

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

Handle<HeapObject> Deserializer::ReadObject() {
  Handle<HeapObject> ret;
  CHECK_EQ(ReadSingleBytecodeData(source_.Get(),
                                  SlotAccessorForHandle(&ret, isolate())),
           1);
  return ret;
}

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

  // The map can't be a forward ref. If you want the map to be a forward ref,
  // then you're probably serializing the meta-map, in which case you want to
  // use the kNewMetaMap bytecode.
  DCHECK_NE(source()->Peek(), kRegisterPendingForwardRef);
  Handle<Map> map = Handle<Map>::cast(ReadObject());

  // 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, "uninitialized_field_value", so that we can
  //       DCHECK for it when reading objects that are assumed to be partially
  //       initialized objects.
  //   * The fields of the object are deserialized in order, under the
  //     assumption that objects are laid out in such a way that any fields
  //     required for object iteration (e.g. length fields) are deserialized
  //     before fields with objects.
  //     - We ensure this is the case by DCHECKing on object allocation that the
  //       previously allocated object has a valid size (see `Allocate`).
  HeapObject raw_obj =
      Allocate(space, size_in_bytes, HeapObject::RequiredAlignment(*map));
  raw_obj.set_map_after_allocation(*map);
  MemsetTagged(raw_obj.RawField(kTaggedSize), uninitialized_field_value(),
               size_in_tagged - 1);

  // Make sure BytecodeArrays have a valid age, so that the marker doesn't
  // break when making them older.
  if (raw_obj.IsBytecodeArray(isolate())) {
    BytecodeArray::cast(raw_obj).set_bytecode_age(
        BytecodeArray::kFirstBytecodeAge);
  }

#ifdef DEBUG
  // We want to make sure that all embedder pointers are initialized to null.
  if (raw_obj.IsJSObject() && JSObject::cast(raw_obj).IsApiWrapper()) {
    JSObject js_obj = JSObject::cast(raw_obj);
    for (int i = 0; i < js_obj.GetEmbedderFieldCount(); ++i) {
      void* pointer;
      CHECK(EmbedderDataSlot(js_obj, i).ToAlignedPointerSafe(isolate(),
                                                             &pointer));
      CHECK_NULL(pointer);
    }
  } else if (raw_obj.IsEmbedderDataArray()) {
    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.ToAlignedPointerSafe(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);

  DCHECK(!obj->IsThinString(isolate()));

#ifdef DEBUG
  if (obj->IsCode()) {
    DCHECK(space == SnapshotSpace::kCode ||
           space == SnapshotSpace::kReadOnlyHeap);
  } else {
    DCHECK_NE(space, SnapshotSpace::kCode);
  }
#endif  // DEBUG

  return obj;
}

Handle<HeapObject> Deserializer::ReadMetaMap() {
  const SnapshotSpace space = SnapshotSpace::kReadOnlyHeap;
  const int size_in_bytes = Map::kSize;
  const int size_in_tagged = size_in_bytes / kTaggedSize;

  HeapObject raw_obj = Allocate(space, size_in_bytes, kWordAligned);
  raw_obj.set_map_after_allocation(Map::unchecked_cast(raw_obj));
  MemsetTagged(raw_obj.RawField(kTaggedSize), uninitialized_field_value(),
               size_in_tagged - 1);

  Handle<HeapObject> obj = handle(raw_obj, isolate());
  back_refs_.push_back(obj);

  // Set the instance-type manually, to allow backrefs to read it.
  Map::unchecked_cast(*obj).set_instance_type(MAP_TYPE);

  ReadData(obj, 1, size_in_tagged);
  PostProcessNewObject(Handle<Map>::cast(obj), obj, space);

  return obj;
}

class Deserializer::RelocInfoVisitor {
 public:
  RelocInfoVisitor(Deserializer* deserializer,
                   const std::vector<Handle<HeapObject>>* objects)
      : deserializer_(deserializer), objects_(objects), current_object_(0) {}
  ~RelocInfoVisitor() { DCHECK_EQ(current_object_, objects_->size()); }

  void VisitCodeTarget(Code host, RelocInfo* rinfo);
  void VisitEmbeddedPointer(Code host, RelocInfo* rinfo);
  void VisitRuntimeEntry(Code host, RelocInfo* rinfo);
  void VisitExternalReference(Code host, RelocInfo* rinfo);
  void VisitInternalReference(Code host, RelocInfo* rinfo);
  void VisitOffHeapTarget(Code host, RelocInfo* rinfo);

 private:
  Isolate* isolate() { return deserializer_->isolate(); }
  SnapshotByteSource& source() { return deserializer_->source_; }

  Deserializer* deserializer_;
  const std::vector<Handle<HeapObject>>* objects_;
  int current_object_;
};

void Deserializer::RelocInfoVisitor::VisitCodeTarget(Code host,
                                                     RelocInfo* rinfo) {
  HeapObject object = *objects_->at(current_object_++);
  rinfo->set_target_address(Code::cast(object).raw_instruction_start());
}

void Deserializer::RelocInfoVisitor::VisitEmbeddedPointer(Code host,
                                                          RelocInfo* rinfo) {
  HeapObject object = *objects_->at(current_object_++);
  // Embedded object reference must be a strong one.
  rinfo->set_target_object(isolate()->heap(), object);
}

void Deserializer::RelocInfoVisitor::VisitRuntimeEntry(Code host,
                                                       RelocInfo* rinfo) {
  // We no longer serialize code that contains runtime entries.
  UNREACHABLE();
}

void Deserializer::RelocInfoVisitor::VisitExternalReference(Code host,
                                                            RelocInfo* rinfo) {
  byte data = source().Get();
  CHECK_EQ(data, kExternalReference);

  Address address = deserializer_->ReadExternalReferenceCase();

  if (rinfo->IsCodedSpecially()) {
    Address location_of_branch_data = rinfo->pc();
    Assembler::deserialization_set_special_target_at(location_of_branch_data,
                                                     host, address);
  } else {
    WriteUnalignedValue(rinfo->target_address_address(), address);
  }
}

void Deserializer::RelocInfoVisitor::VisitInternalReference(Code host,
                                                            RelocInfo* rinfo) {
  byte data = source().Get();
  CHECK_EQ(data, kInternalReference);

  // Internal reference target is encoded as an offset from code entry.
  int target_offset = source().GetInt();
  // TODO(jgruber,v8:11036): We are being permissive for this DCHECK, but
  // consider using raw_instruction_size() instead of raw_body_size() in the
  // future.
  STATIC_ASSERT(Code::kOnHeapBodyIsContiguous);
  DCHECK_LT(static_cast<unsigned>(target_offset),
            static_cast<unsigned>(host.raw_body_size()));
  Address target = host.entry() + target_offset;
  Assembler::deserialization_set_target_internal_reference_at(
      rinfo->pc(), target, rinfo->rmode());
}

void Deserializer::RelocInfoVisitor::VisitOffHeapTarget(Code host,
                                                        RelocInfo* rinfo) {
  byte data = source().Get();
  CHECK_EQ(data, kOffHeapTarget);

  int builtin_index = source().GetInt();
  DCHECK(Builtins::IsBuiltinId(builtin_index));

  CHECK_NOT_NULL(isolate()->embedded_blob_code());
  EmbeddedData d = EmbeddedData::FromBlob();
  Address address = d.InstructionStartOfBuiltin(builtin_index);
  CHECK_NE(kNullAddress, address);

  // TODO(ishell): implement RelocInfo::set_target_off_heap_target()
  if (RelocInfo::OffHeapTargetIsCodedSpecially()) {
    Address location_of_branch_data = rinfo->pc();
    Assembler::deserialization_set_special_target_at(location_of_branch_data,
                                                     host, address);
  } else {
    WriteUnalignedValue(rinfo->target_address_address(), address);
  }
}

template <typename SlotAccessor>
int Deserializer::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 {

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");
}

// Template used by the below CASE_RANGE macro to statically verify that the
// given number of cases matches the number of expected cases for that bytecode.
template <int byte_code_count, int expected>
constexpr byte VerifyBytecodeCount(byte bytecode) {
  STATIC_ASSERT(byte_code_count == expected);
  return bytecode;
}

}  // namespace

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

// This generates a case range for all the spaces.
#define CASE_RANGE_ALL_SPACES(bytecode)                           \
  SpaceEncoder<bytecode>::Encode(SnapshotSpace::kOld)             \
      : case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kCode) \
      : case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kMap)  \
      : case SpaceEncoder<bytecode>::Encode(SnapshotSpace::kReadOnlyHeap)

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

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

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

  switch (data) {
    // Deserialize a new object and write a pointer to it to the current
    // object.
    case CASE_RANGE_ALL_SPACES(kNewObject): {
      SnapshotSpace space = NewObject::Decode(data);
      // Save the reference type before recursing down into reading the object.
      HeapObjectReferenceType ref_type = GetAndResetNextReferenceType();
      Handle<HeapObject> heap_object = ReadObject(space);
      return slot_accessor.Write(heap_object, ref_type);
    }

    // Find a recently deserialized object using its offset from the current
    // allocation point and write a pointer to it to the current object.
    case kBackref: {
      Handle<HeapObject> heap_object = GetBackReferencedObject();
      return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
    }

    // Reference an object in the read-only heap. This should be used when an
    // object is read-only, but is not a root.
    case kReadOnlyHeapRef: {
      DCHECK(isolate()->heap()->deserialization_complete());
      uint32_t chunk_index = source_.GetInt();
      uint32_t chunk_offset = source_.GetInt();

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

      return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
    }

    // Find an object in the roots array and write a pointer to it to the
    // current object.
    case kRootArray: {
      int id = source_.GetInt();
      RootIndex root_index = static_cast<RootIndex>(id);
      Handle<HeapObject> heap_object =
          Handle<HeapObject>::cast(isolate()->root_handle(root_index));
      hot_objects_.Add(heap_object);
      return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
    }

    // Find an object in the startup object cache and write a pointer to it to
    // the current object.
    case kStartupObjectCache: {
      int cache_index = source_.GetInt();
      // TODO(leszeks): Could we use the address of the startup_object_cache
      // entry as a Handle backing?
      HeapObject heap_object =
          HeapObject::cast(isolate()->startup_object_cache()->at(cache_index));
      return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
    }

    // Find an object in the read-only object cache and write a pointer to it
    // to the current object.
    case kReadOnlyObjectCache: {
      int cache_index = source_.GetInt();
      // TODO(leszeks): Could we use the address of the cached_read_only_object
      // entry as a Handle backing?
      HeapObject heap_object = HeapObject::cast(
          isolate()->read_only_heap()->cached_read_only_object(cache_index));
      return slot_accessor.Write(heap_object, GetAndResetNextReferenceType());
    }

    // Deserialize a new meta-map and write a pointer to it to the current
    // object.
    case kNewMetaMap: {
      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.
    case kSandboxedExternalReference:
    case kExternalReference: {
      Address address = ReadExternalReferenceCase();
      if (V8_HEAP_SANDBOX_BOOL && data == kSandboxedExternalReference) {
        return WriteExternalPointer(slot_accessor.slot(), address,
                                    kForeignForeignAddressTag);
      } else {
        DCHECK(!V8_HEAP_SANDBOX_BOOL);
        return WriteAddress(slot_accessor.slot(), address);
      }
    }

    case kInternalReference:
    case kOffHeapTarget:
      // These bytecodes are expected only during RelocInfo iteration.
      UNREACHABLE();

    // Find an object in the attached references and write a pointer to it to
    // the current object.
    case kAttachedReference: {
      int index = source_.GetInt();
      Handle<HeapObject> heap_object = attached_objects_[index];

      // This is the only case where we might encounter new space objects, so
      // maybe emit a generational write barrier.
      return slot_accessor.WriteWithGenerationalBarrier(
          heap_object, GetAndResetNextReferenceType());
    }

    case kNop:
      return 0;

    case kRegisterPendingForwardRef: {
      HeapObjectReferenceType ref_type = GetAndResetNextReferenceType();
      unresolved_forward_refs_.emplace_back(slot_accessor.object(),
                                            slot_accessor.offset(), ref_type);
      num_unresolved_forward_refs_++;
      return 1;
    }

    case kResolvePendingForwardRef: {
      // Pending forward refs can only be resolved after the heap object's map
      // field is deserialized; currently they only appear immediately after
      // the map field.
      DCHECK_EQ(slot_accessor.offset(), HeapObject::kHeaderSize);
      Handle<HeapObject> obj = slot_accessor.object();
      int index = source_.GetInt();
      auto& forward_ref = unresolved_forward_refs_[index];
      SlotAccessorForHeapObject::ForSlotOffset(forward_ref.object,
                                               forward_ref.offset)
          .Write(*obj, forward_ref.ref_type);
      num_unresolved_forward_refs_--;
      if (num_unresolved_forward_refs_ == 0) {
        // If there's no more pending fields, clear the entire pending field
        // vector.
        unresolved_forward_refs_.clear();
      } else {
        // Otherwise, at least clear the pending field.
        forward_ref.object = Handle<HeapObject>();
      }
      return 0;
    }

    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();

    // Deserialize raw data of variable length.
    case kVariableRawData: {
      // This operation is only supported for tagged-size slots, else we might
      // become misaligned.
      DCHECK_EQ(TSlot::kSlotDataSize, kTaggedSize);
      int size_in_tagged = source_.GetInt();
      // TODO(leszeks): Only copy slots when there are Smis in the serialized
      // data.
      source_.CopySlots(slot_accessor.slot().location(), size_in_tagged);
      return size_in_tagged;
    }

    // Deserialize raw code directly into the body of the code object.
    case kCodeBody: {
      // This operation is only supported for tagged-size slots, else we might
      // become misaligned.
      DCHECK_EQ(TSlot::kSlotDataSize, kTaggedSize);
      // CodeBody can only occur right after the heap object header.
      DCHECK_EQ(slot_accessor.offset(), HeapObject::kHeaderSize);

      int size_in_tagged = source_.GetInt();
      int size_in_bytes = size_in_tagged * kTaggedSize;

      {
        DisallowGarbageCollection no_gc;
        Code code = Code::cast(*slot_accessor.object());

        // First deserialize the code itself.
        source_.CopyRaw(
            reinterpret_cast<void*>(code.address() + Code::kDataStart),
            size_in_bytes);
      }

      // Then deserialize the code header
      ReadData(slot_accessor.object(), HeapObject::kHeaderSize / kTaggedSize,
               Code::kDataStart / kTaggedSize);

      // Then deserialize the pre-serialized RelocInfo objects.
      std::vector<Handle<HeapObject>> preserialized_objects;
      while (source_.Peek() != kSynchronize) {
        Handle<HeapObject> obj = ReadObject();
        preserialized_objects.push_back(obj);
      }
      // Skip the synchronize bytecode.
      source_.Advance(1);

      // Finally iterate RelocInfos (the same way it was done by the serializer)
      // and deserialize respective data into RelocInfos. The RelocIterator
      // holds a raw pointer to the code, so we have to disable garbage
      // collection here. It's ok though, any objects it would have needed are
      // in the preserialized_objects vector.
      {
        DisallowGarbageCollection no_gc;

        Code code = Code::cast(*slot_accessor.object());
        RelocInfoVisitor visitor(this, &preserialized_objects);
        for (RelocIterator it(code, Code::BodyDescriptor::kRelocModeMask);
             !it.done(); it.next()) {
          it.rinfo()->Visit(&visitor);
        }
      }

      // Advance to the end of the code object.
      return (Code::kDataStart - HeapObject::kHeaderSize) / kTaggedSize +
             size_in_tagged;
    }

    case kVariableRepeat: {
      int repeats = VariableRepeatCount::Decode(source_.GetInt());
      return ReadRepeatedObject(slot_accessor, repeats);
    }

    case kOffHeapBackingStore: {
      AlwaysAllocateScope scope(isolate()->heap());
      int byte_length = source_.GetInt();
      std::unique_ptr<BackingStore> backing_store =
          BackingStore::Allocate(isolate(), byte_length, SharedFlag::kNotShared,
                                 InitializedFlag::kUninitialized);
      CHECK_NOT_NULL(backing_store);
      source_.CopyRaw(backing_store->buffer_start(), byte_length);
      backing_stores_.push_back(std::move(backing_store));
      return 0;
    }

    case kSandboxedApiReference:
    case kApiReference: {
      uint32_t reference_id = static_cast<uint32_t>(source_.GetInt());
      Address address;
      if (isolate()->api_external_references()) {
        DCHECK_WITH_MSG(reference_id < num_api_references_,
                        "too few external references provided through the API");
        address = static_cast<Address>(
            isolate()->api_external_references()[reference_id]);
      } else {
        address = reinterpret_cast<Address>(NoExternalReferencesCallback);
      }
      if (V8_HEAP_SANDBOX_BOOL && data == kSandboxedApiReference) {
        return WriteExternalPointer(slot_accessor.slot(), address,
                                    kForeignForeignAddressTag);
      } else {
        DCHECK(!V8_HEAP_SANDBOX_BOOL);
        return WriteAddress(slot_accessor.slot(), address);
      }
    }

    case kClearedWeakReference:
      return slot_accessor.Write(HeapObjectReference::ClearedValue(isolate()));

    case kWeakPrefix: {
      // 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;
    }

    case CASE_RANGE(kRootArrayConstants, 32): {
      // 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);
    }

    case CASE_RANGE(kHotObject, 8): {
      int index = HotObject::Decode(data);
      Handle<HeapObject> hot_object = hot_objects_.Get(index);
      return slot_accessor.Write(hot_object, GetAndResetNextReferenceType());
    }

    case CASE_RANGE(kFixedRawData, 32): {
      // 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;
    }

    case CASE_RANGE(kFixedRepeat, 16): {
      int repeats = FixedRepeatWithCount::Decode(data);
      return ReadRepeatedObject(slot_accessor, repeats);
    }

#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();
}

#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

Address Deserializer::ReadExternalReferenceCase() {
  uint32_t reference_id = static_cast<uint32_t>(source_.GetInt());
  return isolate()->external_reference_table()->address(reference_id);
}

namespace {
AllocationType SpaceToType(SnapshotSpace space) {
  switch (space) {
    case SnapshotSpace::kCode:
      return AllocationType::kCode;
    case SnapshotSpace::kMap:
      return AllocationType::kMap;
    case SnapshotSpace::kOld:
      return AllocationType::kOld;
    case SnapshotSpace::kReadOnlyHeap:
      return AllocationType::kReadOnly;
  }
}
}  // namespace

HeapObject Deserializer::Allocate(SnapshotSpace space, 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();
    DCHECK_LE(object_size, previous_allocation_size_);
  }
#endif

  HeapObject obj = isolate()->heap()->AllocateRawWith<Heap::kRetryOrFail>(
      size, SpaceToType(space), AllocationOrigin::kRuntime, alignment);

#ifdef DEBUG
  previous_allocation_obj_ = handle(obj, isolate());
  previous_allocation_size_ = size;
#endif

  return obj;
}

}  // namespace internal
}  // namespace v8