serializer.cc 51.5 KB
Newer Older
1 2 3 4 5 6
// 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/serializer.h"

7
#include "src/codegen/assembler-inl.h"
8
#include "src/common/globals.h"
9
#include "src/handles/global-handles-inl.h"
10
#include "src/heap/heap-inl.h"  // For Space::identity().
11
#include "src/heap/memory-chunk-inl.h"
12
#include "src/heap/read-only-heap.h"
13
#include "src/interpreter/interpreter.h"
14
#include "src/objects/code.h"
15
#include "src/objects/js-array-buffer-inl.h"
16
#include "src/objects/js-array-inl.h"
17
#include "src/objects/map.h"
18
#include "src/objects/objects-body-descriptors-inl.h"
19
#include "src/objects/slots-inl.h"
20
#include "src/objects/smi.h"
21
#include "src/snapshot/serializer-deserializer.h"
22
#include "src/snapshot/serializer-inl.h"
23 24 25 26

namespace v8 {
namespace internal {

27
Serializer::Serializer(Isolate* isolate, Snapshot::SerializerFlags flags)
28
    : isolate_(isolate),
29 30 31
#if V8_COMPRESS_POINTERS
      cage_base_(isolate),
#endif  // V8_COMPRESS_POINTERS
32
      hot_objects_(isolate->heap()),
33
      reference_map_(isolate),
34 35
      external_reference_encoder_(isolate),
      root_index_map_(isolate),
36
      deferred_objects_(isolate->heap()),
37
      forward_refs_per_pending_object_(isolate->heap()),
38 39 40 41 42 43 44
      flags_(flags)
#ifdef DEBUG
      ,
      back_refs_(isolate->heap()),
      stack_(isolate->heap())
#endif
{
45 46
#ifdef OBJECT_PRINT
  if (FLAG_serialization_statistics) {
47
    for (int space = 0; space < kNumberOfSnapshotSpaces; ++space) {
48 49 50
      // Value-initialized to 0.
      instance_type_count_[space] = std::make_unique<int[]>(kInstanceTypes);
      instance_type_size_[space] = std::make_unique<size_t[]>(kInstanceTypes);
51
    }
52 53 54 55
  }
#endif  // OBJECT_PRINT
}

56 57 58 59
#ifdef DEBUG
void Serializer::PopStack() { stack_.Pop(); }
#endif

60 61 62
void Serializer::CountAllocation(Map map, int size, SnapshotSpace space) {
  DCHECK(FLAG_serialization_statistics);

63
  const int space_number = static_cast<int>(space);
64 65
  allocation_size_[space_number] += size;
#ifdef OBJECT_PRINT
66
  int instance_type = map.instance_type();
67 68
  instance_type_count_[space_number][instance_type]++;
  instance_type_size_[space_number][instance_type] += size;
69
#endif  // OBJECT_PRINT
70
}
71

72 73 74 75 76 77 78 79
int Serializer::TotalAllocationSize() const {
  int sum = 0;
  for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
    sum += allocation_size_[space];
  }
  return sum;
}

80
void Serializer::OutputStatistics(const char* name) {
81
  if (!FLAG_serialization_statistics) return;
82

83
  PrintF("%s:\n", name);
84 85 86 87 88 89 90 91 92 93 94 95

  PrintF("  Spaces (bytes):\n");

  for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
    PrintF("%16s",
           BaseSpace::GetSpaceName(static_cast<AllocationSpace>(space)));
  }
  PrintF("\n");

  for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
    PrintF("%16zu", allocation_size_[space]);
  }
96

97 98
#ifdef OBJECT_PRINT
  PrintF("  Instance types (count and bytes):\n");
99
#define PRINT_INSTANCE_TYPE(Name)                                          \
100
  for (int space = 0; space < kNumberOfSnapshotSpaces; ++space) {          \
101 102 103 104 105 106
    if (instance_type_count_[space][Name]) {                               \
      PrintF("%10d %10zu  %-10s %s\n", instance_type_count_[space][Name],  \
             instance_type_size_[space][Name],                             \
             BaseSpace::GetSpaceName(static_cast<AllocationSpace>(space)), \
             #Name);                                                       \
    }                                                                      \
107 108 109
  }
  INSTANCE_TYPE_LIST(PRINT_INSTANCE_TYPE)
#undef PRINT_INSTANCE_TYPE
110
#endif  // OBJECT_PRINT
111

112 113 114
  PrintF("\n");
}

115
void Serializer::SerializeDeferredObjects() {
116 117 118
  if (FLAG_trace_serializer) {
    PrintF("Serializing deferred objects\n");
  }
119 120 121
  WHILE_WITH_HANDLE_SCOPE(isolate(), !deferred_objects_.empty(), {
    Handle<HeapObject> obj = handle(deferred_objects_.Pop(), isolate());

122
    ObjectSerializer obj_serializer(this, obj, &sink_);
123
    obj_serializer.SerializeDeferred();
124
  });
125
  sink_.Put(kSynchronize, "Finished with deferred objects");
126 127
}

128 129 130 131 132
void Serializer::SerializeObject(Handle<HeapObject> obj) {
  // ThinStrings are just an indirection to an internalized string, so elide the
  // indirection and serialize the actual string directly.
  if (obj->IsThinString(isolate())) {
    obj = handle(ThinString::cast(*obj).actual(isolate()), isolate());
133
  } else if (obj->IsCodeT(isolate())) {
134 135 136 137 138 139 140
    Code code = FromCodeT(CodeT::cast(*obj));
    if (code.kind() == CodeKind::BASELINE) {
      // For now just serialize the BytecodeArray instead of baseline code.
      // TODO(v8:11429,pthier): Handle Baseline code in cases we want to
      // serialize it.
      obj = handle(code.bytecode_or_interpreter_data(isolate()), isolate());
    }
141 142 143 144
  }
  SerializeObjectImpl(obj);
}

145
bool Serializer::MustBeDeferred(HeapObject object) { return false; }
146

147
void Serializer::VisitRootPointers(Root root, const char* description,
148 149
                                   FullObjectSlot start, FullObjectSlot end) {
  for (FullObjectSlot current = start; current < end; ++current) {
150
    SerializeRootObject(current);
151 152 153
  }
}

154 155 156 157
void Serializer::SerializeRootObject(FullObjectSlot slot) {
  Object o = *slot;
  if (o.IsSmi()) {
    PutSmiRoot(slot);
158
  } else {
159
    SerializeObject(Handle<HeapObject>(slot.location()));
160 161 162 163
  }
}

#ifdef DEBUG
164 165 166
void Serializer::PrintStack() { PrintStack(std::cout); }

void Serializer::PrintStack(std::ostream& out) {
167
  for (const auto o : stack_) {
168
    o->Print(out);
169
    out << "\n";
170 171
  }
}
172 173
#endif  // DEBUG

174
bool Serializer::SerializeRoot(HeapObject obj) {
175 176 177
  RootIndex root_index;
  // Derived serializers are responsible for determining if the root has
  // actually been serialized before calling this.
178
  if (root_index_map()->Lookup(obj, &root_index)) {
179
    PutRoot(root_index);
180 181 182 183 184
    return true;
  }
  return false;
}

185 186
bool Serializer::SerializeHotObject(HeapObject obj) {
  DisallowGarbageCollection no_gc;
187
  // Encode a reference to a hot object by its index in the working set.
188
  int index = hot_objects_.Find(obj);
189
  if (index == HotObjectsList::kNotFound) return false;
190
  DCHECK(index >= 0 && index < kHotObjectCount);
191 192
  if (FLAG_trace_serializer) {
    PrintF(" Encoding hot object %d:", index);
193
    obj.ShortPrint();
194
    PrintF("\n");
195
  }
196
  sink_.Put(HotObject::Encode(index), "HotObject");
197 198
  return true;
}
199

200 201
bool Serializer::SerializeBackReference(HeapObject obj) {
  DisallowGarbageCollection no_gc;
202 203
  const SerializerReference* reference = reference_map_.LookupReference(obj);
  if (reference == nullptr) return false;
204 205 206 207
  // Encode the location of an already deserialized object in order to write
  // its location into a later object.  We can encode the location as an
  // offset fromthe start of the deserialized objects or as an offset
  // backwards from thecurrent allocation pointer.
208
  if (reference->is_attached_reference()) {
209 210
    if (FLAG_trace_serializer) {
      PrintF(" Encoding attached reference %d\n",
211
             reference->attached_reference_index());
212
    }
213
    PutAttachedReference(*reference);
214
  } else {
215
    DCHECK(reference->is_back_reference());
216 217
    if (FLAG_trace_serializer) {
      PrintF(" Encoding back reference to: ");
218
      obj.ShortPrint();
219 220 221
      PrintF("\n");
    }

222 223
    sink_.Put(kBackref, "Backref");
    PutBackReference(obj, *reference);
224
  }
225
  return true;
226 227
}

228
bool Serializer::SerializePendingObject(HeapObject obj) {
229 230 231
  PendingObjectReferences* refs_to_object =
      forward_refs_per_pending_object_.Find(obj);
  if (refs_to_object == nullptr) {
232 233
    return false;
  }
234
  PutPendingForwardReference(*refs_to_object);
235 236 237
  return true;
}

238 239 240
bool Serializer::ObjectIsBytecodeHandler(HeapObject obj) const {
  if (!obj.IsCode()) return false;
  return (Code::cast(obj).kind() == CodeKind::BYTECODE_HANDLER);
241 242
}

243
void Serializer::PutRoot(RootIndex root) {
244
  DisallowGarbageCollection no_gc;
245
  int root_index = static_cast<int>(root);
246
  HeapObject object = HeapObject::cast(isolate()->root(root));
247 248
  if (FLAG_trace_serializer) {
    PrintF(" Encoding root %d:", root_index);
249
    object.ShortPrint();
250 251 252
    PrintF("\n");
  }

253 254
  // Assert that the first 32 root array items are a conscious choice. They are
  // chosen so that the most common ones can be encoded more efficiently.
255
  STATIC_ASSERT(static_cast<int>(RootIndex::kArgumentsMarker) ==
256
                kRootArrayConstantsCount - 1);
257

258
  // TODO(ulan): Check that it works with young large objects.
259
  if (root_index < kRootArrayConstantsCount &&
260
      !Heap::InYoungGeneration(object)) {
261
    sink_.Put(RootArrayConstant::Encode(root), "RootConstant");
262
  } else {
263
    sink_.Put(kRootArray, "RootSerialization");
264
    sink_.PutInt(root_index, "root_index");
265
    hot_objects_.Add(object);
266 267 268
  }
}

269 270 271 272 273 274 275 276
void Serializer::PutSmiRoot(FullObjectSlot slot) {
  // Serializing a smi root in compressed pointer builds will serialize the
  // full object slot (of kSystemPointerSize) to avoid complications during
  // deserialization (endianness or smi sequences).
  STATIC_ASSERT(decltype(slot)::kSlotDataSize == sizeof(Address));
  STATIC_ASSERT(decltype(slot)::kSlotDataSize == kSystemPointerSize);
  static constexpr int bytes_to_output = decltype(slot)::kSlotDataSize;
  static constexpr int size_in_tagged = bytes_to_output >> kTaggedSizeLog2;
277
  sink_.Put(FixedRawDataWithSize::Encode(size_in_tagged), "Smi");
278 279 280

  Address raw_value = Smi::cast(*slot).ptr();
  const byte* raw_value_as_bytes = reinterpret_cast<const byte*>(&raw_value);
281
  sink_.PutRaw(raw_value_as_bytes, bytes_to_output, "Bytes");
282 283
}

284
void Serializer::PutBackReference(HeapObject object,
285
                                  SerializerReference reference) {
286
  DCHECK_EQ(object, *back_refs_[reference.back_ref_index()]);
287
  sink_.PutInt(reference.back_ref_index(), "BackRefIndex");
288
  hot_objects_.Add(object);
289 290
}

291
void Serializer::PutAttachedReference(SerializerReference reference) {
292
  DCHECK(reference.is_attached_reference());
293
  sink_.Put(kAttachedReference, "AttachedRef");
294
  sink_.PutInt(reference.attached_reference_index(), "AttachedRefIndex");
295 296
}

297 298
void Serializer::PutRepeat(int repeat_count) {
  if (repeat_count <= kLastEncodableFixedRepeatCount) {
299
    sink_.Put(FixedRepeatWithCount::Encode(repeat_count), "FixedRepeat");
300 301
  } else {
    sink_.Put(kVariableRepeat, "VariableRepeat");
302
    sink_.PutInt(VariableRepeatCount::Encode(repeat_count), "repeat count");
303 304 305
  }
}

306
void Serializer::PutPendingForwardReference(PendingObjectReferences& refs) {
307 308
  sink_.Put(kRegisterPendingForwardRef, "RegisterPendingForwardRef");
  unresolved_forward_refs_++;
309 310
  // Register the current slot with the pending object.
  int forward_ref_id = next_forward_ref_id_++;
311 312 313 314 315 316 317 318
  if (refs == nullptr) {
    // The IdentityMap holding the pending object reference vectors does not
    // support non-trivial types; in particular it doesn't support destructors
    // on values. So, we manually allocate a vector with new, and delete it when
    // resolving the pending object.
    refs = new std::vector<int>();
  }
  refs->push_back(forward_ref_id);
319 320 321 322 323 324 325 326 327 328 329 330 331 332
}

void Serializer::ResolvePendingForwardReference(int forward_reference_id) {
  sink_.Put(kResolvePendingForwardRef, "ResolvePendingForwardRef");
  sink_.PutInt(forward_reference_id, "with this index");
  unresolved_forward_refs_--;

  // If there are no more unresolved forward refs, reset the forward ref id to
  // zero so that future forward refs compress better.
  if (unresolved_forward_refs_ == 0) {
    next_forward_ref_id_ = 0;
  }
}

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
ExternalReferenceEncoder::Value Serializer::EncodeExternalReference(
    Address addr) {
  Maybe<ExternalReferenceEncoder::Value> result =
      external_reference_encoder_.TryEncode(addr);
  if (result.IsNothing()) {
#ifdef DEBUG
    PrintStack(std::cerr);
#endif
    void* addr_ptr = reinterpret_cast<void*>(addr);
    v8::base::OS::PrintError("Unknown external reference %p.\n", addr_ptr);
    v8::base::OS::PrintError("%s\n",
                             ExternalReferenceTable::ResolveSymbol(addr_ptr));
    v8::base::OS::Abort();
  }
  return result.FromJust();
}

350 351 352
void Serializer::RegisterObjectIsPending(HeapObject obj) {
  DisallowGarbageCollection no_gc;
  if (IsNotMappedSymbol(obj)) return;
353

354
  // Add the given object to the pending objects -> forward refs map.
355 356
  auto find_result = forward_refs_per_pending_object_.FindOrInsert(obj);
  USE(find_result);
357

358 359 360 361
  // If the above emplace didn't actually add the object, then the object must
  // already have been registered pending by deferring. It might not be in the
  // deferred objects queue though, since it may be the very object we just
  // popped off that queue, so just check that it can be deferred.
362
  DCHECK_IMPLIES(find_result.already_exists, *find_result.entry != nullptr);
363
  DCHECK_IMPLIES(find_result.already_exists, CanBeDeferred(obj));
364
}
365

366 367 368
void Serializer::ResolvePendingObject(HeapObject obj) {
  DisallowGarbageCollection no_gc;
  if (IsNotMappedSymbol(obj)) return;
369 370 371 372 373 374 375 376 377 378

  std::vector<int>* refs;
  CHECK(forward_refs_per_pending_object_.Delete(obj, &refs));
  if (refs) {
    for (int index : *refs) {
      ResolvePendingForwardReference(index);
    }
    // See PutPendingForwardReference -- we have to manually manage the memory
    // of non-trivial IdentityMap values.
    delete refs;
379 380 381
  }
}

382
void Serializer::Pad(int padding_offset) {
383 384 385
  // The non-branching GetInt will read up to 3 bytes too far, so we need
  // to pad the snapshot to make sure we don't read over the end.
  for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) {
386
    sink_.Put(kNop, "Padding");
387 388
  }
  // Pad up to pointer size for checksum.
389
  while (!IsAligned(sink_.Position() + padding_offset, kPointerAlignment)) {
390
    sink_.Put(kNop, "Padding");
391 392 393
  }
}

394
void Serializer::InitializeCodeAddressMap() {
395
  isolate_->InitializeLoggingAndCounters();
396
  code_address_map_ = std::make_unique<CodeAddressMap>(isolate_);
397 398
}

399
Code Serializer::CopyCode(Code code) {
400
  code_buffer_.clear();  // Clear buffer without deleting backing store.
401
  int size = code.CodeSize();
402
  code_buffer_.insert(code_buffer_.end(),
403 404
                      reinterpret_cast<byte*>(code.address()),
                      reinterpret_cast<byte*>(code.address() + size));
405 406 407
  // When pointer compression is enabled the checked cast will try to
  // decompress map field of off-heap Code object.
  return Code::unchecked_cast(HeapObject::FromAddress(
408
      reinterpret_cast<Address>(&code_buffer_.front())));
409 410
}

411
void Serializer::ObjectSerializer::SerializePrologue(SnapshotSpace space,
412
                                                     int size, Map map) {
413 414
  if (serializer_->code_address_map_) {
    const char* code_name =
415
        serializer_->code_address_map_->Lookup(object_->address());
416
    LOG(serializer_->isolate_,
417
        CodeNameEvent(object_->address(), sink_->Position(), code_name));
418 419
  }

420
  if (map == *object_) {
421
    DCHECK_EQ(*object_, ReadOnlyRoots(isolate()).meta_map());
422 423 424 425
    DCHECK_EQ(space, SnapshotSpace::kReadOnlyHeap);
    sink_->Put(kNewMetaMap, "NewMetaMap");

    DCHECK_EQ(size, Map::kSize);
426
  } else {
427
    sink_->Put(NewObject::Encode(space), "NewObject");
428 429

    // TODO(leszeks): Skip this when the map has a fixed size.
430
    sink_->PutInt(size >> kObjectAlignmentBits, "ObjectSizeInWords");
431 432

    // Until the space for the object is allocated, it is considered "pending".
433
    serializer_->RegisterObjectIsPending(*object_);
434 435 436 437

    // Serialize map (first word of the object) before anything else, so that
    // the deserializer can access it when allocating. Make sure that the map
    // isn't a pending object.
438
    DCHECK_NULL(serializer_->forward_refs_per_pending_object_.Find(map));
439
    DCHECK(map.IsMap());
440
    serializer_->SerializeObject(handle(map, isolate()));
441 442 443

    // Make sure the map serialization didn't accidentally recursively serialize
    // this object.
444
    DCHECK_IMPLIES(
445
        !serializer_->IsNotMappedSymbol(*object_),
446
        serializer_->reference_map()->LookupReference(object_) == nullptr);
447 448 449

    // Now that the object is allocated, we can resolve pending references to
    // it.
450
    serializer_->ResolvePendingObject(*object_);
451 452 453
  }

  if (FLAG_serialization_statistics) {
454
    serializer_->CountAllocation(object_->map(), size, space);
455 456
  }

457 458
  // Mark this object as already serialized, and add it to the reference map so
  // that it can be accessed by backreference by future objects.
459 460 461 462 463
  serializer_->num_back_refs_++;
#ifdef DEBUG
  serializer_->back_refs_.Push(*object_);
  DCHECK_EQ(serializer_->back_refs_.size(), serializer_->num_back_refs_);
#endif
464
  if (!serializer_->IsNotMappedSymbol(*object_)) {
465 466 467
    // Only add the object to the map if it's not not_mapped_symbol, else
    // the reference IdentityMap has issues. We don't expect to have back
    // references to the not_mapped_symbol anyway, so it's fine.
468 469
    SerializerReference back_reference =
        SerializerReference::BackReference(serializer_->num_back_refs_ - 1);
470 471 472 473 474 475 476
    serializer_->reference_map()->Add(*object_, back_reference);
    DCHECK_EQ(*object_,
              *serializer_->back_refs_[back_reference.back_ref_index()]);
    DCHECK_EQ(back_reference.back_ref_index(), serializer_->reference_map()
                                                   ->LookupReference(object_)
                                                   ->back_ref_index());
  }
477 478
}

479
uint32_t Serializer::ObjectSerializer::SerializeBackingStore(
480
    void* backing_store, int32_t byte_length, Maybe<int32_t> max_byte_length) {
481
  DisallowGarbageCollection no_gc;
482 483
  const SerializerReference* reference_ptr =
      serializer_->reference_map()->LookupBackingStore(backing_store);
484 485

  // Serialize the off-heap backing store.
486
  if (reference_ptr) {
487
    return reference_ptr->off_heap_backing_store_index();
488
  }
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
  if (max_byte_length.IsJust()) {
    sink_->Put(kOffHeapResizableBackingStore,
               "Off-heap resizable backing store");
  } else {
    sink_->Put(kOffHeapBackingStore, "Off-heap backing store");
  }
  sink_->PutInt(byte_length, "length");
  if (max_byte_length.IsJust()) {
    sink_->PutInt(max_byte_length.FromJust(), "max length");
  }
  sink_->PutRaw(static_cast<byte*>(backing_store), byte_length, "BackingStore");
  DCHECK_NE(0, serializer_->seen_backing_stores_index_);
  SerializerReference reference =
      SerializerReference::OffHeapBackingStoreReference(
          serializer_->seen_backing_stores_index_++);
  // Mark this backing store as already serialized.
  serializer_->reference_map()->AddBackingStore(backing_store, reference);
  return reference.off_heap_backing_store_index();
507 508
}

509
void Serializer::ObjectSerializer::SerializeJSTypedArray() {
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
  {
    DisallowGarbageCollection no_gc;
    JSTypedArray typed_array = JSTypedArray::cast(*object_);
    if (typed_array.is_on_heap()) {
      typed_array.RemoveExternalPointerCompensationForSerialization(isolate());
    } else {
      if (!typed_array.WasDetached()) {
        // Explicitly serialize the backing store now.
        JSArrayBuffer buffer = JSArrayBuffer::cast(typed_array.buffer());
        // We cannot store byte_length or max_byte_length larger than int32
        // range in the snapshot.
        CHECK_LE(buffer.byte_length(), std::numeric_limits<int32_t>::max());
        int32_t byte_length = static_cast<int32_t>(buffer.byte_length());
        Maybe<int32_t> max_byte_length = Nothing<int32_t>();
        if (buffer.is_resizable()) {
          CHECK_LE(buffer.max_byte_length(),
                   std::numeric_limits<int32_t>::max());
          max_byte_length =
              Just(static_cast<int32_t>(buffer.max_byte_length()));
        }
        size_t byte_offset = typed_array.byte_offset();
531

532 533 534 535
        // We need to calculate the backing store from the data pointer
        // because the ArrayBuffer may already have been serialized.
        void* backing_store = reinterpret_cast<void*>(
            reinterpret_cast<Address>(typed_array.DataPtr()) - byte_offset);
536

537 538 539 540 541 542
        uint32_t ref =
            SerializeBackingStore(backing_store, byte_length, max_byte_length);
        typed_array.SetExternalBackingStoreRefForSerialization(ref);
      } else {
        typed_array.SetExternalBackingStoreRefForSerialization(0);
      }
543 544 545
    }
  }
  SerializeObject();
546 547
}

548
void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
  ArrayBufferExtension* extension;
  void* backing_store;
  {
    DisallowGarbageCollection no_gc;
    JSArrayBuffer buffer = JSArrayBuffer::cast(*object_);
    backing_store = buffer.backing_store();
    // We cannot store byte_length or max_byte_length larger than int32 range in
    // the snapshot.
    CHECK_LE(buffer.byte_length(), std::numeric_limits<int32_t>::max());
    int32_t byte_length = static_cast<int32_t>(buffer.byte_length());
    Maybe<int32_t> max_byte_length = Nothing<int32_t>();
    if (buffer.is_resizable()) {
      CHECK_LE(buffer.max_byte_length(), std::numeric_limits<int32_t>::max());
      max_byte_length = Just(static_cast<int32_t>(buffer.max_byte_length()));
    }
    extension = buffer.extension();
565

566 567 568 569 570 571 572
    // Only serialize non-empty backing stores.
    if (buffer.IsEmpty()) {
      buffer.SetBackingStoreRefForSerialization(kEmptyBackingStoreRefSentinel);
    } else {
      uint32_t ref =
          SerializeBackingStore(backing_store, byte_length, max_byte_length);
      buffer.SetBackingStoreRefForSerialization(ref);
573

574 575 576 577 578
      // Ensure deterministic output by setting extension to null during
      // serialization.
      buffer.set_extension(nullptr);
    }
  }
579
  SerializeObject();
580 581 582 583 584
  {
    JSArrayBuffer buffer = JSArrayBuffer::cast(*object_);
    buffer.set_backing_store(isolate(), backing_store);
    buffer.set_extension(extension);
  }
585 586
}

587
void Serializer::ObjectSerializer::SerializeExternalString() {
588 589 590
  // For external strings with known resources, we replace the resource field
  // with the encoded external reference, which we restore upon deserialize.
  // For the rest we serialize them to look like ordinary sequential strings.
591 592
  Handle<ExternalString> string = Handle<ExternalString>::cast(object_);
  Address resource = string->resource_as_address();
593 594 595 596
  ExternalReferenceEncoder::Value reference;
  if (serializer_->external_reference_encoder_.TryEncode(resource).To(
          &reference)) {
    DCHECK(reference.is_from_api());
Samuel Groß's avatar
Samuel Groß committed
597
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
598 599
    uint32_t external_pointer_entry =
        string->GetResourceRefForDeserialization();
600
#endif
601
    string->SetResourceRefForSerialization(reference.index());
602
    SerializeObject();
Samuel Groß's avatar
Samuel Groß committed
603
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
604
    string->SetResourceRefForSerialization(external_pointer_entry);
605
#else
606
    string->set_address_as_resource(isolate(), resource);
607
#endif
608 609
  } else {
    SerializeExternalStringAsSequentialString();
610 611 612
  }
}

613
void Serializer::ObjectSerializer::SerializeExternalStringAsSequentialString() {
614 615
  // Instead of serializing this as an external string, we serialize
  // an imaginary sequential string with the same content.
616
  ReadOnlyRoots roots(isolate());
617 618
  PtrComprCageBase cage_base(isolate());
  DCHECK(object_->IsExternalString(cage_base));
619 620
  Handle<ExternalString> string = Handle<ExternalString>::cast(object_);
  int length = string->length();
621
  Map map;
622 623 624 625
  int content_size;
  int allocation_size;
  const byte* resource;
  // Find the map and size for the imaginary sequential string.
626 627
  bool internalized = object_->IsInternalizedString(cage_base);
  if (object_->IsExternalOneByteString(cage_base)) {
628 629
    map = internalized ? roots.one_byte_internalized_string_map()
                       : roots.one_byte_string_map();
630 631 632
    allocation_size = SeqOneByteString::SizeFor(length);
    content_size = length * kCharSize;
    resource = reinterpret_cast<const byte*>(
633
        Handle<ExternalOneByteString>::cast(string)->resource()->data());
634
  } else {
635
    map = internalized ? roots.internalized_string_map() : roots.string_map();
636 637 638
    allocation_size = SeqTwoByteString::SizeFor(length);
    content_size = length * kShortSize;
    resource = reinterpret_cast<const byte*>(
639
        Handle<ExternalTwoByteString>::cast(string)->resource()->data());
640 641
  }

642
  SnapshotSpace space = SnapshotSpace::kOld;
643 644 645 646
  SerializePrologue(space, allocation_size, map);

  // Output the rest of the imaginary string.
  int bytes_to_output = allocation_size - HeapObject::kHeaderSize;
647
  DCHECK(IsAligned(bytes_to_output, kTaggedSize));
648
  int slots_to_output = bytes_to_output >> kTaggedSizeLog2;
649 650 651

  // Output raw data header. Do not bother with common raw length cases here.
  sink_->Put(kVariableRawData, "RawDataForString");
652
  sink_->PutInt(slots_to_output, "length");
653 654

  // Serialize string header (except for map).
655
  byte* string_start = reinterpret_cast<byte*>(string->address());
656
  for (int i = HeapObject::kHeaderSize; i < SeqString::kHeaderSize; i++) {
657
    sink_->Put(string_start[i], "StringHeader");
658 659 660 661 662 663 664 665 666
  }

  // Serialize string content.
  sink_->PutRaw(resource, content_size, "StringContent");

  // Since the allocation size is rounded up to object alignment, there
  // maybe left-over bytes that need to be padded.
  int padding_size = allocation_size - SeqString::kHeaderSize - content_size;
  DCHECK(0 <= padding_size && padding_size < kObjectAlignment);
667
  for (int i = 0; i < padding_size; i++) {
668
    sink_->Put(static_cast<byte>(0), "StringPadding");
669
  }
670 671 672 673
}

// Clear and later restore the next link in the weak cell or allocation site.
// TODO(all): replace this with proper iteration of weak slots in serializer.
674
class V8_NODISCARD UnlinkWeakNextScope {
675
 public:
676
  explicit UnlinkWeakNextScope(Heap* heap, HeapObject object) {
677
    Isolate* isolate = heap->isolate();
678 679
    if (object.IsAllocationSite(isolate) &&
        AllocationSite::cast(object).HasWeakNext()) {
680
      object_ = object;
681 682
      next_ = AllocationSite::cast(object).weak_next();
      AllocationSite::cast(object).set_weak_next(
683
          ReadOnlyRoots(isolate).undefined_value());
684 685 686 687
    }
  }

  ~UnlinkWeakNextScope() {
688 689 690
    if (next_ == Smi::zero()) return;
    AllocationSite::cast(object_).set_weak_next(next_,
                                                UPDATE_WEAK_WRITE_BARRIER);
691 692 693
  }

 private:
694 695
  HeapObject object_;
  Object next_ = Smi::zero();
696
  DISALLOW_GARBAGE_COLLECTION(no_gc_)
697 698
};

699
void Serializer::ObjectSerializer::Serialize() {
700 701
  RecursionScope recursion(serializer_);

702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
  {
    DisallowGarbageCollection no_gc;
    HeapObject raw = *object_;
    // Defer objects as "pending" if they cannot be serialized now, or if we
    // exceed a certain recursion depth. Some objects cannot be deferred.
    if ((recursion.ExceedsMaximum() && CanBeDeferred(raw)) ||
        serializer_->MustBeDeferred(raw)) {
      DCHECK(CanBeDeferred(raw));
      if (FLAG_trace_serializer) {
        PrintF(" Deferring heap object: ");
        object_->ShortPrint();
        PrintF("\n");
      }
      // Deferred objects are considered "pending".
      serializer_->RegisterObjectIsPending(raw);
      serializer_->PutPendingForwardReference(
          *serializer_->forward_refs_per_pending_object_.Find(raw));
      serializer_->QueueDeferredObject(raw);
      return;
    }

723
    if (FLAG_trace_serializer) {
724
      PrintF(" Encoding heap object: ");
725
      object_->ShortPrint();
726 727
      PrintF("\n");
    }
728 729
  }

730
  PtrComprCageBase cage_base(isolate());
731 732
  InstanceType instance_type = object_->map(cage_base).instance_type();
  if (InstanceTypeChecker::IsExternalString(instance_type)) {
733
    SerializeExternalString();
734
    return;
735
  } else if (!ReadOnlyHeap::Contains(*object_)) {
736 737
    // Only clear padding for strings outside the read-only heap. Read-only heap
    // should have been cleared elsewhere.
738
    if (object_->IsSeqOneByteString(cage_base)) {
739 740
      // Clear padding bytes at the end. Done here to avoid having to do this
      // at allocation sites in generated code.
741
      Handle<SeqOneByteString>::cast(object_)->clear_padding();
742
    } else if (object_->IsSeqTwoByteString(cage_base)) {
743
      Handle<SeqTwoByteString>::cast(object_)->clear_padding();
744
    }
745
  }
746
  if (InstanceTypeChecker::IsJSTypedArray(instance_type)) {
747 748
    SerializeJSTypedArray();
    return;
749
  } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) {
750 751
    SerializeJSArrayBuffer();
    return;
752
  } else if (InstanceTypeChecker::IsScript(instance_type)) {
753
    // Clear cached line ends.
754
    Oddball undefined = ReadOnlyRoots(isolate()).undefined_value();
755
    Handle<Script>::cast(object_)->set_line_ends(undefined);
756 757
  }

758 759 760
  // We don't expect fillers.
  DCHECK(!object_->IsFreeSpaceOrFiller(cage_base));

761
  SerializeObject();
762 763
}

764
namespace {
765
SnapshotSpace GetSnapshotSpace(HeapObject object) {
766
  if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) {
767
    if (object.IsCode()) {
768
      return SnapshotSpace::kCode;
769
    } else if (ReadOnlyHeap::Contains(object)) {
770
      return SnapshotSpace::kReadOnlyHeap;
771
    } else if (object.IsMap()) {
772 773
      return SnapshotSpace::kMap;
    } else {
774
      return SnapshotSpace::kOld;
775
    }
776
  } else if (ReadOnlyHeap::Contains(object)) {
777
    return SnapshotSpace::kReadOnlyHeap;
778 779
  } else {
    AllocationSpace heap_space =
780
        MemoryChunk::FromHeapObject(object)->owner_identity();
781 782 783
    // Large code objects are not supported and cannot be expressed by
    // SnapshotSpace.
    DCHECK_NE(heap_space, CODE_LO_SPACE);
784
    switch (heap_space) {
785
      case OLD_SPACE:
786 787 788
      // Young generation objects are tenured, as objects that have survived
      // until snapshot building probably deserve to be considered 'old'.
      case NEW_SPACE:
789 790 791
      // Large objects (young and old) are encoded as simply 'old' snapshot
      // obects, as "normal" objects vs large objects is a heap implementation
      // detail and isn't relevant to the snapshot.
792
      case NEW_LO_SPACE:
793 794 795 796 797 798 799 800 801
      case LO_SPACE:
        return SnapshotSpace::kOld;
      case CODE_SPACE:
        return SnapshotSpace::kCode;
      case MAP_SPACE:
        return SnapshotSpace::kMap;
      case CODE_LO_SPACE:
      case RO_SPACE:
        UNREACHABLE();
802
    }
803
  }
804 805 806 807
}
}  // namespace

void Serializer::ObjectSerializer::SerializeObject() {
808 809
  Map map = object_->map(serializer_->cage_base());
  int size = object_->SizeFromMap(map);
810 811 812 813 814 815 816 817 818

  // Descriptor arrays have complex element weakness, that is dependent on the
  // maps pointing to them. During deserialization, this can cause them to get
  // prematurely trimmed one of their owners isn't deserialized yet. We work
  // around this by forcing all descriptor arrays to be serialized as "strong",
  // i.e. no custom weakness, and "re-weaken" them in the deserializer once
  // deserialization completes.
  //
  // See also `Deserializer::WeakenDescriptorArrays`.
819 820
  if (map == ReadOnlyRoots(isolate()).descriptor_array_map()) {
    map = ReadOnlyRoots(isolate()).strong_descriptor_array_map();
821
  }
822
  SnapshotSpace space = GetSnapshotSpace(*object_);
823
  SerializePrologue(space, size, map);
824

825 826
  // Serialize the rest of the object.
  CHECK_EQ(0, bytes_processed_so_far_);
827
  bytes_processed_so_far_ = kTaggedSize;
828

829
  SerializeContent(map, size);
830 831
}

832
void Serializer::ObjectSerializer::SerializeDeferred() {
833 834
  const SerializerReference* back_reference =
      serializer_->reference_map()->LookupReference(object_);
835

836
  if (back_reference != nullptr) {
837 838
    if (FLAG_trace_serializer) {
      PrintF(" Deferred heap object ");
839
      object_->ShortPrint();
840 841 842 843
      PrintF(" was already serialized\n");
    }
    return;
  }
844

845 846 847 848
  if (FLAG_trace_serializer) {
    PrintF(" Encoding deferred heap object\n");
  }
  Serialize();
849
}
850

851
void Serializer::ObjectSerializer::SerializeContent(Map map, int size) {
852 853 854
  HeapObject raw = *object_;
  UnlinkWeakNextScope unlink_weak_next(isolate()->heap(), raw);
  if (raw.IsCode()) {
855 856
    // For code objects, perform a custom serialization.
    SerializeCode(map, size);
857 858
  } else {
    // For other objects, iterate references first.
859
    raw.IterateBody(map, size, this);
860
    // Then output data payload, if any.
861
    OutputRawData(raw.address() + size);
862
  }
863 864
}

865
void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
866 867
                                                 ObjectSlot start,
                                                 ObjectSlot end) {
868
  VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
869
}
870

871
void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
872 873
                                                 MaybeObjectSlot start,
                                                 MaybeObjectSlot end) {
874
  HandleScope scope(isolate());
875
  PtrComprCageBase cage_base(isolate());
876
  DisallowGarbageCollection no_gc;
877

878
  MaybeObjectSlot current = start;
879
  while (current < end) {
880
    while (current < end && current.load(cage_base).IsSmi()) {
881
      ++current;
882 883
    }
    if (current < end) {
884
      OutputRawData(current.address());
885
    }
886 887
    // TODO(ishell): Revisit this change once we stick to 32-bit compressed
    // tagged values.
888
    while (current < end && current.load(cage_base).IsCleared()) {
889
      sink_->Put(kClearedWeakReference, "ClearedWeakReference");
890
      bytes_processed_so_far_ += kTaggedSize;
891 892
      ++current;
    }
893
    HeapObject current_contents;
894
    HeapObjectReferenceType reference_type;
895 896
    while (current < end && current.load(cage_base).GetHeapObject(
                                &current_contents, &reference_type)) {
897 898 899 900 901 902
      // Write a weak prefix if we need it. This has to be done before the
      // potential pending object serialization.
      if (reference_type == HeapObjectReferenceType::WEAK) {
        sink_->Put(kWeakPrefix, "WeakReference");
      }

903
      Handle<HeapObject> obj = handle(current_contents, isolate());
904
      if (serializer_->SerializePendingObject(*obj)) {
905 906 907 908 909
        bytes_processed_so_far_ += kTaggedSize;
        ++current;
        continue;
      }

910
      RootIndex root_index;
911
      // Compute repeat count and write repeat prefix if applicable.
912
      // Repeats are not subject to the write barrier so we can only use
913
      // immortal immovable root members.
914 915
      MaybeObjectSlot repeat_end = current + 1;
      if (repeat_end < end &&
916
          serializer_->root_index_map()->Lookup(*obj, &root_index) &&
917
          RootsTable::IsImmortalImmovable(root_index) &&
918
          *current == *repeat_end) {
919
        DCHECK_EQ(reference_type, HeapObjectReferenceType::STRONG);
920
        DCHECK(!Heap::InYoungGeneration(*obj));
921 922
        while (repeat_end < end && *repeat_end == *current) {
          repeat_end++;
923
        }
924 925
        int repeat_count = static_cast<int>(repeat_end - current);
        current = repeat_end;
926
        bytes_processed_so_far_ += repeat_count * kTaggedSize;
927
        serializer_->PutRepeat(repeat_count);
928
      } else {
929
        bytes_processed_so_far_ += kTaggedSize;
930
        ++current;
931
      }
932
      // Now write the object itself.
933
      serializer_->SerializeObject(obj);
934 935 936 937
    }
  }
}

938 939 940
void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host,
                                                    CodeObjectSlot slot) {
  CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
941 942 943 944
  // A version of VisitPointers() customized for CodeObjectSlot.
  HandleScope scope(isolate());
  DisallowGarbageCollection no_gc;

945
#ifdef V8_EXTERNAL_CODE_SPACE
946 947 948 949
  PtrComprCageBase code_cage_base(isolate()->code_cage_base());
#else
  PtrComprCageBase code_cage_base(isolate());
#endif
950 951 952 953 954
  Object contents = slot.load(code_cage_base);
  DCHECK(HAS_STRONG_HEAP_OBJECT_TAG(contents.ptr()));
  DCHECK(contents.IsCode());

  Handle<HeapObject> obj = handle(HeapObject::cast(contents), isolate());
955
  if (!serializer_->SerializePendingObject(*obj)) {
956 957 958
    serializer_->SerializeObject(obj);
  }
  bytes_processed_so_far_ += kTaggedSize;
959 960
}

961 962
void Serializer::ObjectSerializer::OutputExternalReference(
    Address target, int target_size, bool sandboxify, ExternalPointerTag tag) {
963
  DCHECK_LE(target_size, sizeof(target));  // Must fit in Address.
964
  DCHECK_IMPLIES(sandboxify, tag != kExternalPointerNullTag);
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
  ExternalReferenceEncoder::Value encoded_reference;
  bool encoded_successfully;

  if (serializer_->allow_unknown_external_references_for_testing()) {
    encoded_successfully =
        serializer_->TryEncodeExternalReference(target).To(&encoded_reference);
  } else {
    encoded_reference = serializer_->EncodeExternalReference(target);
    encoded_successfully = true;
  }

  if (!encoded_successfully) {
    // In this case the serialized snapshot will not be used in a different
    // Isolate and thus the target address will not change between
    // serialization and deserialization. We can serialize seen external
    // references verbatim.
    CHECK(serializer_->allow_unknown_external_references_for_testing());
982
    CHECK(IsAligned(target_size, kTaggedSize));
983
    CHECK_LE(target_size, kFixedRawDataCount * kTaggedSize);
984
    int size_in_tagged = target_size >> kTaggedSizeLog2;
985
    sink_->Put(FixedRawDataWithSize::Encode(size_in_tagged), "FixedRawData");
986 987
    sink_->PutRaw(reinterpret_cast<byte*>(&target), target_size, "Bytes");
  } else if (encoded_reference.is_from_api()) {
Samuel Groß's avatar
Samuel Groß committed
988
    if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
989 990 991 992
      sink_->Put(kSandboxedApiReference, "SandboxedApiRef");
    } else {
      sink_->Put(kApiReference, "ApiRef");
    }
993
    sink_->PutInt(encoded_reference.index(), "reference index");
994
  } else {
Samuel Groß's avatar
Samuel Groß committed
995
    if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
996 997 998 999
      sink_->Put(kSandboxedExternalReference, "SandboxedExternalRef");
    } else {
      sink_->Put(kExternalReference, "ExternalRef");
    }
1000
    sink_->PutInt(encoded_reference.index(), "reference index");
1001
  }
1002 1003 1004 1005
  if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
    sink_->PutInt(static_cast<uint32_t>(tag >> kExternalPointerTagShift),
                  "external pointer tag");
  }
1006 1007 1008 1009
}

void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
                                                          Address* p) {
1010
  // "Sandboxify" external reference.
1011 1012
  OutputExternalReference(host.foreign_address(), kSystemPointerSize, true,
                          kForeignForeignAddressTag);
1013 1014 1015 1016 1017 1018 1019 1020 1021
  bytes_processed_so_far_ += kExternalPointerSize;
}

class Serializer::ObjectSerializer::RelocInfoObjectPreSerializer {
 public:
  explicit RelocInfoObjectPreSerializer(Serializer* serializer)
      : serializer_(serializer) {}

  void VisitEmbeddedPointer(Code host, RelocInfo* target) {
1022 1023
    HeapObject object = target->target_object(isolate());
    serializer_->SerializeObject(handle(object, isolate()));
1024 1025 1026 1027 1028 1029 1030
    num_serialized_objects_++;
  }
  void VisitCodeTarget(Code host, RelocInfo* target) {
#ifdef V8_TARGET_ARCH_ARM
    DCHECK(!RelocInfo::IsRelativeCodeTarget(target->rmode()));
#endif
    Code object = Code::GetCodeFromTargetAddress(target->target_address());
1031
    serializer_->SerializeObject(handle(object, isolate()));
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
    num_serialized_objects_++;
  }

  void VisitExternalReference(Code host, RelocInfo* rinfo) {}
  void VisitInternalReference(Code host, RelocInfo* rinfo) {}
  void VisitRuntimeEntry(Code host, RelocInfo* reloc) { UNREACHABLE(); }
  void VisitOffHeapTarget(Code host, RelocInfo* target) {}

  int num_serialized_objects() const { return num_serialized_objects_; }

1042 1043
  Isolate* isolate() { return serializer_->isolate(); }

1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
 private:
  Serializer* serializer_;
  int num_serialized_objects_ = 0;
};

void Serializer::ObjectSerializer::VisitEmbeddedPointer(Code host,
                                                        RelocInfo* rinfo) {
  // Target object should be pre-serialized by RelocInfoObjectPreSerializer, so
  // just track the pointer's existence as kTaggedSize in
  // bytes_processed_so_far_.
  // TODO(leszeks): DCHECK that RelocInfoObjectPreSerializer serialized this
  // specific object already.
  bytes_processed_so_far_ += kTaggedSize;
1057 1058
}

1059
void Serializer::ObjectSerializer::VisitExternalReference(Code host,
1060
                                                          RelocInfo* rinfo) {
1061
  Address target = rinfo->target_external_reference();
1062
  DCHECK_NE(target, kNullAddress);  // Code does not reference null.
1063 1064
  DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(),
                 !rinfo->IsCodedSpecially());
1065
  // Don't "sandboxify" external references embedded in the code.
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
  OutputExternalReference(target, rinfo->target_address_size(), false,
                          kExternalPointerNullTag);
}

void Serializer::ObjectSerializer::VisitExternalPointer(HeapObject host,
                                                        ExternalPointer_t ptr) {
  // TODO(v8:12700) handle other external references here as well. This should
  // allow removing some of the other Visit* methods, should unify the sandbox
  // vs no-sandbox implementation, and should allow removing various
  // XYZForSerialization methods throughout the codebase.
  if (host.IsJSExternalObject()) {
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
    // TODO(saelo) maybe add a helper method for this conversion if also needed
    // in other places? This might require a ExternalPointerTable::Get variant
    // that drops the pointer tag completely.
    uint32_t index = ptr >> kExternalPointerIndexShift;
    Address value =
        isolate()->external_pointer_table().Get(index, kExternalObjectValueTag);
#else
    Address value = ptr;
#endif
    // TODO(v8:12700) should we specify here whether we expect the references to
    // be internal or external (or either)?
    OutputExternalReference(value, kSystemPointerSize, true,
                            kExternalObjectValueTag);
    bytes_processed_so_far_ += kExternalPointerSize;
  }
1093 1094
}

1095
void Serializer::ObjectSerializer::VisitInternalReference(Code host,
1096
                                                          RelocInfo* rinfo) {
1097
  Address entry = Handle<Code>::cast(object_)->entry();
1098 1099
  DCHECK_GE(rinfo->target_internal_reference(), entry);
  uintptr_t target_offset = rinfo->target_internal_reference() - entry;
1100 1101 1102
  // 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.
1103
  STATIC_ASSERT(Code::kOnHeapBodyIsContiguous);
1104
  DCHECK_LE(target_offset, Handle<Code>::cast(object_)->raw_body_size());
1105
  sink_->Put(kInternalReference, "InternalRef");
1106
  sink_->PutInt(target_offset, "internal ref value");
1107 1108
}

1109
void Serializer::ObjectSerializer::VisitRuntimeEntry(Code host,
1110
                                                     RelocInfo* rinfo) {
1111 1112
  // We no longer serialize code that contains runtime entries.
  UNREACHABLE();
1113 1114
}

1115
void Serializer::ObjectSerializer::VisitOffHeapTarget(Code host,
1116
                                                      RelocInfo* rinfo) {
1117
  STATIC_ASSERT(EmbeddedData::kTableSize == Builtins::kBuiltinCount);
1118 1119 1120 1121

  Address addr = rinfo->target_off_heap_target();
  CHECK_NE(kNullAddress, addr);

1122
  Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate(), addr);
1123 1124
  CHECK(Builtins::IsBuiltinId(builtin));
  CHECK(Builtins::IsIsolateIndependent(builtin));
1125 1126

  sink_->Put(kOffHeapTarget, "OffHeapTarget");
1127
  sink_->PutInt(static_cast<int>(builtin), "builtin index");
1128 1129
}

1130
void Serializer::ObjectSerializer::VisitCodeTarget(Code host,
1131
                                                   RelocInfo* rinfo) {
1132 1133 1134 1135 1136 1137
  // Target object should be pre-serialized by RelocInfoObjectPreSerializer, so
  // just track the pointer's existence as kTaggedSize in
  // bytes_processed_so_far_.
  // TODO(leszeks): DCHECK that RelocInfoObjectPreSerializer serialized this
  // specific object already.
  bytes_processed_so_far_ += kTaggedSize;
1138 1139
}

1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
namespace {

// Similar to OutputRawData, but substitutes the given field with the given
// value instead of reading it from the object.
void OutputRawWithCustomField(SnapshotByteSink* sink, Address object_start,
                              int written_so_far, int bytes_to_write,
                              int field_offset, int field_size,
                              const byte* field_value) {
  int offset = field_offset - written_so_far;
  if (0 <= offset && offset < bytes_to_write) {
    DCHECK_GE(bytes_to_write, offset + field_size);
    sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far), offset,
                 "Bytes");
    sink->PutRaw(field_value, field_size, "Bytes");
    written_so_far += offset + field_size;
    bytes_to_write -= offset + field_size;
    sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far),
                 bytes_to_write, "Bytes");
  } else {
    sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far),
                 bytes_to_write, "Bytes");
  }
}
}  // anonymous namespace

1165
void Serializer::ObjectSerializer::OutputRawData(Address up_to) {
1166
  Address object_start = object_->address();
1167 1168 1169 1170
  int base = bytes_processed_so_far_;
  int up_to_offset = static_cast<int>(up_to - object_start);
  int to_skip = up_to_offset - bytes_processed_so_far_;
  int bytes_to_output = to_skip;
1171 1172
  DCHECK(IsAligned(bytes_to_output, kTaggedSize));
  int tagged_to_output = bytes_to_output / kTaggedSize;
1173
  bytes_processed_so_far_ += to_skip;
1174
  DCHECK_GE(to_skip, 0);
1175 1176
  if (bytes_to_output != 0) {
    DCHECK(to_skip == bytes_to_output);
1177 1178 1179
    if (tagged_to_output <= kFixedRawDataCount) {
      sink_->Put(FixedRawDataWithSize::Encode(tagged_to_output),
                 "FixedRawData");
1180 1181
    } else {
      sink_->Put(kVariableRawData, "VariableRawData");
1182
      sink_->PutInt(tagged_to_output, "length");
1183 1184 1185
    }
#ifdef MEMORY_SANITIZER
    // Check that we do not serialize uninitialized memory.
1186 1187
    __msan_check_mem_is_initialized(
        reinterpret_cast<void*>(object_start + base), bytes_to_output);
1188
#endif  // MEMORY_SANITIZER
1189 1190
    PtrComprCageBase cage_base(isolate_);
    if (object_->IsBytecodeArray(cage_base)) {
1191
      // The bytecode age field can be changed by GC concurrently.
1192 1193
      static_assert(BytecodeArray::kBytecodeAgeSize == kUInt16Size);
      uint16_t field_value = BytecodeArray::kNoAgeBytecodeAge;
1194 1195
      OutputRawWithCustomField(sink_, object_start, base, bytes_to_output,
                               BytecodeArray::kBytecodeAgeOffset,
1196 1197
                               sizeof(field_value),
                               reinterpret_cast<byte*>(&field_value));
1198
    } else if (object_->IsDescriptorArray(cage_base)) {
1199 1200
      // The number of marked descriptors field can be changed by GC
      // concurrently.
1201
      static byte field_value[2] = {0};
1202 1203 1204 1205
      OutputRawWithCustomField(
          sink_, object_start, base, bytes_to_output,
          DescriptorArray::kRawNumberOfMarkedDescriptorsOffset,
          sizeof(field_value), field_value);
1206 1207
    } else if (V8_EXTERNAL_CODE_SPACE_BOOL &&
               object_->IsCodeDataContainer(cage_base)) {
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
      // code_cage_base and code_entry_point fields contain raw values that
      // will be recomputed after deserialization, so write zeros to keep the
      // snapshot deterministic.
      CHECK_EQ(CodeDataContainer::kCodeCageBaseUpper32BitsOffset + kTaggedSize,
               CodeDataContainer::kCodeEntryPointOffset);
      static byte field_value[kTaggedSize + kExternalPointerSize] = {0};
      OutputRawWithCustomField(
          sink_, object_start, base, bytes_to_output,
          CodeDataContainer::kCodeCageBaseUpper32BitsOffset,
          sizeof(field_value), field_value);
1218
    } else {
1219 1220
      sink_->PutRaw(reinterpret_cast<byte*>(object_start + base),
                    bytes_to_output, "Bytes");
1221
    }
1222 1223 1224
  }
}

1225
void Serializer::ObjectSerializer::SerializeCode(Map map, int size) {
1226 1227 1228 1229 1230 1231 1232 1233 1234
  static const int kWipeOutModeMask =
      RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
      RelocInfo::ModeMask(RelocInfo::FULL_EMBEDDED_OBJECT) |
      RelocInfo::ModeMask(RelocInfo::COMPRESSED_EMBEDDED_OBJECT) |
      RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
      RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
      RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
      RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
      RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
1235 1236 1237 1238

  DCHECK_EQ(HeapObject::kHeaderSize, bytes_processed_so_far_);
  Handle<Code> on_heap_code = Handle<Code>::cast(object_);

1239 1240 1241
  // With enabled pointer compression normal accessors no longer work for
  // off-heap objects, so we have to get the relocation info data via the
  // on-heap code object.
1242 1243 1244 1245 1246
  ByteArray relocation_info = on_heap_code->unchecked_relocation_info();

  // To make snapshots reproducible, we make a copy of the code object
  // and wipe all pointers in the copy, which we then serialize.
  Code off_heap_code = serializer_->CopyCode(*on_heap_code);
1247 1248
  for (RelocIterator it(off_heap_code, relocation_info, kWipeOutModeMask);
       !it.done(); it.next()) {
1249 1250
    RelocInfo* rinfo = it.rinfo();
    rinfo->WipeOut();
1251
  }
1252 1253
  // We need to wipe out the header fields *after* wiping out the
  // relocations, because some of these fields are needed for the latter.
1254
  off_heap_code.WipeOutHeader();
1255

1256 1257 1258 1259 1260 1261
  // Initially skip serializing the code header. We'll serialize it after the
  // Code body, so that the various fields the Code needs for iteration are
  // already valid.
  sink_->Put(kCodeBody, "kCodeBody");

  // Now serialize the wiped off-heap Code, as length + data.
1262
  Address start = off_heap_code.address() + Code::kDataStart;
1263
  int bytes_to_output = size - Code::kDataStart;
1264
  DCHECK(IsAligned(bytes_to_output, kTaggedSize));
1265
  int tagged_to_output = bytes_to_output / kTaggedSize;
1266

1267
  sink_->PutInt(tagged_to_output, "length");
1268

1269
#ifdef MEMORY_SANITIZER
1270
  // Check that we do not serialize uninitialized memory.
1271 1272
  __msan_check_mem_is_initialized(reinterpret_cast<void*>(start),
                                  bytes_to_output);
1273
#endif  // MEMORY_SANITIZER
1274
  sink_->PutRaw(reinterpret_cast<byte*>(start), bytes_to_output, "Code");
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294

  // Manually serialize the code header. We don't use Code::BodyDescriptor
  // here as we don't yet want to walk the RelocInfos.
  DCHECK_EQ(HeapObject::kHeaderSize, bytes_processed_so_far_);
  VisitPointers(*on_heap_code, on_heap_code->RawField(HeapObject::kHeaderSize),
                on_heap_code->RawField(Code::kDataStart));
  DCHECK_EQ(bytes_processed_so_far_, Code::kDataStart);

  // Now serialize RelocInfos. We can't allocate during a RelocInfo walk during
  // deserualization, so we have two passes for RelocInfo serialization:
  //   1. A pre-serializer which serializes all allocatable objects in the
  //      RelocInfo, followed by a kSynchronize bytecode, and
  //   2. A walk the RelocInfo with this serializer, serializing any objects
  //      implicitly as offsets into the pre-serializer's object array.
  // This way, the deserializer can deserialize the allocatable objects first,
  // without walking RelocInfo, re-build the pre-serializer's object array, and
  // only then walk the RelocInfo itself.
  // TODO(leszeks): We only really need to pre-serialize objects which need
  // serialization, i.e. no backrefs or roots.
  RelocInfoObjectPreSerializer pre_serializer(serializer_);
1295 1296 1297
  for (RelocIterator it(*on_heap_code, relocation_info,
                        Code::BodyDescriptor::kRelocModeMask);
       !it.done(); it.next()) {
1298 1299 1300 1301 1302 1303 1304 1305
    it.rinfo()->Visit(&pre_serializer);
  }
  // Mark that the pre-serialization finished with a kSynchronize bytecode.
  sink_->Put(kSynchronize, "PreSerializationFinished");

  // Finally serialize all RelocInfo objects in the on-heap Code, knowing that
  // we will not do a recursive serialization.
  // TODO(leszeks): Add a scope that DCHECKs this.
1306 1307 1308
  for (RelocIterator it(*on_heap_code, relocation_info,
                        Code::BodyDescriptor::kRelocModeMask);
       !it.done(); it.next()) {
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
    it.rinfo()->Visit(this);
  }

  // We record a kTaggedSize for every object encountered during the
  // serialization, so DCHECK that bytes_processed_so_far_ matches the expected
  // number of bytes (i.e. the code header + a tagged size per pre-serialized
  // object).
  DCHECK_EQ(
      bytes_processed_so_far_,
      Code::kDataStart + kTaggedSize * pre_serializer.num_serialized_objects());
1319 1320
}

1321
Serializer::HotObjectsList::HotObjectsList(Heap* heap) : heap_(heap) {
1322 1323 1324
  strong_roots_entry_ = heap->RegisterStrongRoots(
      "Serializer::HotObjectsList", FullObjectSlot(&circular_queue_[0]),
      FullObjectSlot(&circular_queue_[kSize]));
1325 1326 1327 1328 1329
}
Serializer::HotObjectsList::~HotObjectsList() {
  heap_->UnregisterStrongRoots(strong_roots_entry_);
}

1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
Handle<FixedArray> ObjectCacheIndexMap::Values(Isolate* isolate) {
  if (size() == 0) {
    return isolate->factory()->empty_fixed_array();
  }
  Handle<FixedArray> externals = isolate->factory()->NewFixedArray(size());
  DisallowGarbageCollection no_gc;
  FixedArray raw = *externals;
  IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope(
      &map_);
  for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
    raw.set(*it.entry(), it.key());
  }

  return externals;
}

1346 1347
}  // namespace internal
}  // namespace v8