• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/snapshot/serializer.h"
6 
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 namespace v8 {
25 namespace internal {
26 
Serializer(Isolate * isolate,Snapshot::SerializerFlags flags)27 Serializer::Serializer(Isolate* isolate, Snapshot::SerializerFlags flags)
28     : isolate_(isolate),
29 #if V8_COMPRESS_POINTERS
30       cage_base_(isolate),
31 #endif  // V8_COMPRESS_POINTERS
32       hot_objects_(isolate->heap()),
33       reference_map_(isolate),
34       external_reference_encoder_(isolate),
35       root_index_map_(isolate),
36       deferred_objects_(isolate->heap()),
37       forward_refs_per_pending_object_(isolate->heap()),
38       flags_(flags)
39 #ifdef DEBUG
40       ,
41       back_refs_(isolate->heap()),
42       stack_(isolate->heap())
43 #endif
44 {
45 #ifdef OBJECT_PRINT
46   if (FLAG_serialization_statistics) {
47     for (int space = 0; space < kNumberOfSnapshotSpaces; ++space) {
48       // Value-initialized to 0.
49       instance_type_count_[space] = std::make_unique<int[]>(kInstanceTypes);
50       instance_type_size_[space] = std::make_unique<size_t[]>(kInstanceTypes);
51     }
52   }
53 #endif  // OBJECT_PRINT
54 }
55 
56 #ifdef DEBUG
PopStack()57 void Serializer::PopStack() { stack_.Pop(); }
58 #endif
59 
CountAllocation(Map map,int size,SnapshotSpace space)60 void Serializer::CountAllocation(Map map, int size, SnapshotSpace space) {
61   DCHECK(FLAG_serialization_statistics);
62 
63   const int space_number = static_cast<int>(space);
64   allocation_size_[space_number] += size;
65 #ifdef OBJECT_PRINT
66   int instance_type = map.instance_type();
67   instance_type_count_[space_number][instance_type]++;
68   instance_type_size_[space_number][instance_type] += size;
69 #endif  // OBJECT_PRINT
70 }
71 
TotalAllocationSize() const72 int Serializer::TotalAllocationSize() const {
73   int sum = 0;
74   for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
75     sum += allocation_size_[space];
76   }
77   return sum;
78 }
79 
OutputStatistics(const char * name)80 void Serializer::OutputStatistics(const char* name) {
81   if (!FLAG_serialization_statistics) return;
82 
83   PrintF("%s:\n", name);
84 
85   PrintF("  Spaces (bytes):\n");
86 
87   for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
88     PrintF("%16s",
89            BaseSpace::GetSpaceName(static_cast<AllocationSpace>(space)));
90   }
91   PrintF("\n");
92 
93   for (int space = 0; space < kNumberOfSnapshotSpaces; space++) {
94     PrintF("%16zu", allocation_size_[space]);
95   }
96 
97 #ifdef OBJECT_PRINT
98   PrintF("  Instance types (count and bytes):\n");
99 #define PRINT_INSTANCE_TYPE(Name)                                          \
100   for (int space = 0; space < kNumberOfSnapshotSpaces; ++space) {          \
101     if (instance_type_count_[space][Name]) {                               \
102       PrintF("%10d %10zu  %-10s %s\n", instance_type_count_[space][Name],  \
103              instance_type_size_[space][Name],                             \
104              BaseSpace::GetSpaceName(static_cast<AllocationSpace>(space)), \
105              #Name);                                                       \
106     }                                                                      \
107   }
108   INSTANCE_TYPE_LIST(PRINT_INSTANCE_TYPE)
109 #undef PRINT_INSTANCE_TYPE
110 #endif  // OBJECT_PRINT
111 
112   PrintF("\n");
113 }
114 
SerializeDeferredObjects()115 void Serializer::SerializeDeferredObjects() {
116   if (FLAG_trace_serializer) {
117     PrintF("Serializing deferred objects\n");
118   }
119   WHILE_WITH_HANDLE_SCOPE(isolate(), !deferred_objects_.empty(), {
120     Handle<HeapObject> obj = handle(deferred_objects_.Pop(), isolate());
121 
122     ObjectSerializer obj_serializer(this, obj, &sink_);
123     obj_serializer.SerializeDeferred();
124   });
125   sink_.Put(kSynchronize, "Finished with deferred objects");
126 }
127 
SerializeObject(Handle<HeapObject> obj)128 void Serializer::SerializeObject(Handle<HeapObject> obj) {
129   // ThinStrings are just an indirection to an internalized string, so elide the
130   // indirection and serialize the actual string directly.
131   if (obj->IsThinString(isolate())) {
132     obj = handle(ThinString::cast(*obj).actual(isolate()), isolate());
133   } else if (obj->IsCodeT(isolate())) {
134     Code code = FromCodeT(CodeT::cast(*obj));
135     if (code.kind() == CodeKind::BASELINE) {
136       // For now just serialize the BytecodeArray instead of baseline code.
137       // TODO(v8:11429,pthier): Handle Baseline code in cases we want to
138       // serialize it.
139       obj = handle(code.bytecode_or_interpreter_data(isolate()), isolate());
140     }
141   }
142   SerializeObjectImpl(obj);
143 }
144 
MustBeDeferred(HeapObject object)145 bool Serializer::MustBeDeferred(HeapObject object) { return false; }
146 
VisitRootPointers(Root root,const char * description,FullObjectSlot start,FullObjectSlot end)147 void Serializer::VisitRootPointers(Root root, const char* description,
148                                    FullObjectSlot start, FullObjectSlot end) {
149   for (FullObjectSlot current = start; current < end; ++current) {
150     SerializeRootObject(current);
151   }
152 }
153 
SerializeRootObject(FullObjectSlot slot)154 void Serializer::SerializeRootObject(FullObjectSlot slot) {
155   Object o = *slot;
156   if (o.IsSmi()) {
157     PutSmiRoot(slot);
158   } else {
159     SerializeObject(Handle<HeapObject>(slot.location()));
160   }
161 }
162 
163 #ifdef DEBUG
PrintStack()164 void Serializer::PrintStack() { PrintStack(std::cout); }
165 
PrintStack(std::ostream & out)166 void Serializer::PrintStack(std::ostream& out) {
167   for (const auto o : stack_) {
168     o->Print(out);
169     out << "\n";
170   }
171 }
172 #endif  // DEBUG
173 
SerializeRoot(HeapObject obj)174 bool Serializer::SerializeRoot(HeapObject obj) {
175   RootIndex root_index;
176   // Derived serializers are responsible for determining if the root has
177   // actually been serialized before calling this.
178   if (root_index_map()->Lookup(obj, &root_index)) {
179     PutRoot(root_index);
180     return true;
181   }
182   return false;
183 }
184 
SerializeHotObject(HeapObject obj)185 bool Serializer::SerializeHotObject(HeapObject obj) {
186   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   if (FLAG_trace_serializer) {
192     PrintF(" Encoding hot object %d:", index);
193     obj.ShortPrint();
194     PrintF("\n");
195   }
196   sink_.Put(HotObject::Encode(index), "HotObject");
197   return true;
198 }
199 
SerializeBackReference(HeapObject obj)200 bool Serializer::SerializeBackReference(HeapObject obj) {
201   DisallowGarbageCollection no_gc;
202   const SerializerReference* reference = reference_map_.LookupReference(obj);
203   if (reference == nullptr) return false;
204   // Encode the location of an already deserialized object in order to write
205   // its location into a later object.  We can encode the location as an
206   // offset fromthe start of the deserialized objects or as an offset
207   // backwards from thecurrent allocation pointer.
208   if (reference->is_attached_reference()) {
209     if (FLAG_trace_serializer) {
210       PrintF(" Encoding attached reference %d\n",
211              reference->attached_reference_index());
212     }
213     PutAttachedReference(*reference);
214   } else {
215     DCHECK(reference->is_back_reference());
216     if (FLAG_trace_serializer) {
217       PrintF(" Encoding back reference to: ");
218       obj.ShortPrint();
219       PrintF("\n");
220     }
221 
222     sink_.Put(kBackref, "Backref");
223     PutBackReference(obj, *reference);
224   }
225   return true;
226 }
227 
SerializePendingObject(HeapObject obj)228 bool Serializer::SerializePendingObject(HeapObject obj) {
229   PendingObjectReferences* refs_to_object =
230       forward_refs_per_pending_object_.Find(obj);
231   if (refs_to_object == nullptr) {
232     return false;
233   }
234   PutPendingForwardReference(*refs_to_object);
235   return true;
236 }
237 
ObjectIsBytecodeHandler(HeapObject obj) const238 bool Serializer::ObjectIsBytecodeHandler(HeapObject obj) const {
239   if (!obj.IsCode()) return false;
240   return (Code::cast(obj).kind() == CodeKind::BYTECODE_HANDLER);
241 }
242 
PutRoot(RootIndex root)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   if (FLAG_trace_serializer) {
248     PrintF(" Encoding root %d:", root_index);
249     object.ShortPrint();
250     PrintF("\n");
251   }
252 
253   // Assert that the first 32 root array items are a conscious choice. They are
254   // 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 
PutSmiRoot(FullObjectSlot slot)269 void Serializer::PutSmiRoot(FullObjectSlot slot) {
270   // Serializing a smi root in compressed pointer builds will serialize the
271   // full object slot (of kSystemPointerSize) to avoid complications during
272   // deserialization (endianness or smi sequences).
273   STATIC_ASSERT(decltype(slot)::kSlotDataSize == sizeof(Address));
274   STATIC_ASSERT(decltype(slot)::kSlotDataSize == kSystemPointerSize);
275   static constexpr int bytes_to_output = decltype(slot)::kSlotDataSize;
276   static constexpr int size_in_tagged = bytes_to_output >> kTaggedSizeLog2;
277   sink_.Put(FixedRawDataWithSize::Encode(size_in_tagged), "Smi");
278 
279   Address raw_value = Smi::cast(*slot).ptr();
280   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 
PutBackReference(HeapObject object,SerializerReference reference)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 
PutAttachedReference(SerializerReference reference)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 
PutRepeat(int repeat_count)297 void Serializer::PutRepeat(int repeat_count) {
298   if (repeat_count <= kLastEncodableFixedRepeatCount) {
299     sink_.Put(FixedRepeatWithCount::Encode(repeat_count), "FixedRepeat");
300   } else {
301     sink_.Put(kVariableRepeat, "VariableRepeat");
302     sink_.PutInt(VariableRepeatCount::Encode(repeat_count), "repeat count");
303   }
304 }
305 
PutPendingForwardReference(PendingObjectReferences & refs)306 void Serializer::PutPendingForwardReference(PendingObjectReferences& refs) {
307   sink_.Put(kRegisterPendingForwardRef, "RegisterPendingForwardRef");
308   unresolved_forward_refs_++;
309   // Register the current slot with the pending object.
310   int forward_ref_id = next_forward_ref_id_++;
311   if (refs == nullptr) {
312     // The IdentityMap holding the pending object reference vectors does not
313     // support non-trivial types; in particular it doesn't support destructors
314     // on values. So, we manually allocate a vector with new, and delete it when
315     // resolving the pending object.
316     refs = new std::vector<int>();
317   }
318   refs->push_back(forward_ref_id);
319 }
320 
ResolvePendingForwardReference(int forward_reference_id)321 void Serializer::ResolvePendingForwardReference(int forward_reference_id) {
322   sink_.Put(kResolvePendingForwardRef, "ResolvePendingForwardRef");
323   sink_.PutInt(forward_reference_id, "with this index");
324   unresolved_forward_refs_--;
325 
326   // If there are no more unresolved forward refs, reset the forward ref id to
327   // zero so that future forward refs compress better.
328   if (unresolved_forward_refs_ == 0) {
329     next_forward_ref_id_ = 0;
330   }
331 }
332 
EncodeExternalReference(Address addr)333 ExternalReferenceEncoder::Value Serializer::EncodeExternalReference(
334     Address addr) {
335   Maybe<ExternalReferenceEncoder::Value> result =
336       external_reference_encoder_.TryEncode(addr);
337   if (result.IsNothing()) {
338 #ifdef DEBUG
339     PrintStack(std::cerr);
340 #endif
341     void* addr_ptr = reinterpret_cast<void*>(addr);
342     v8::base::OS::PrintError("Unknown external reference %p.\n", addr_ptr);
343     v8::base::OS::PrintError("%s\n",
344                              ExternalReferenceTable::ResolveSymbol(addr_ptr));
345     v8::base::OS::Abort();
346   }
347   return result.FromJust();
348 }
349 
RegisterObjectIsPending(HeapObject obj)350 void Serializer::RegisterObjectIsPending(HeapObject obj) {
351   DisallowGarbageCollection no_gc;
352   if (IsNotMappedSymbol(obj)) return;
353 
354   // Add the given object to the pending objects -> forward refs map.
355   auto find_result = forward_refs_per_pending_object_.FindOrInsert(obj);
356   USE(find_result);
357 
358   // If the above emplace didn't actually add the object, then the object must
359   // already have been registered pending by deferring. It might not be in the
360   // deferred objects queue though, since it may be the very object we just
361   // 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 
ResolvePendingObject(HeapObject obj)366 void Serializer::ResolvePendingObject(HeapObject obj) {
367   DisallowGarbageCollection no_gc;
368   if (IsNotMappedSymbol(obj)) return;
369 
370   std::vector<int>* refs;
371   CHECK(forward_refs_per_pending_object_.Delete(obj, &refs));
372   if (refs) {
373     for (int index : *refs) {
374       ResolvePendingForwardReference(index);
375     }
376     // See PutPendingForwardReference -- we have to manually manage the memory
377     // of non-trivial IdentityMap values.
378     delete refs;
379   }
380 }
381 
Pad(int padding_offset)382 void Serializer::Pad(int padding_offset) {
383   // The non-branching GetInt will read up to 3 bytes too far, so we need
384   // to pad the snapshot to make sure we don't read over the end.
385   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 
InitializeCodeAddressMap()394 void Serializer::InitializeCodeAddressMap() {
395   isolate_->InitializeLoggingAndCounters();
396   code_address_map_ = std::make_unique<CodeAddressMap>(isolate_);
397 }
398 
CopyCode(Code code)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                       reinterpret_cast<byte*>(code.address()),
404                       reinterpret_cast<byte*>(code.address() + size));
405   // When pointer compression is enabled the checked cast will try to
406   // decompress map field of off-heap Code object.
407   return Code::unchecked_cast(HeapObject::FromAddress(
408       reinterpret_cast<Address>(&code_buffer_.front())));
409 }
410 
SerializePrologue(SnapshotSpace space,int size,Map map)411 void Serializer::ObjectSerializer::SerializePrologue(SnapshotSpace space,
412                                                      int size, Map map) {
413   if (serializer_->code_address_map_) {
414     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     DCHECK_EQ(space, SnapshotSpace::kReadOnlyHeap);
423     sink_->Put(kNewMetaMap, "NewMetaMap");
424 
425     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     // Serialize map (first word of the object) before anything else, so that
436     // the deserializer can access it when allocating. Make sure that the map
437     // 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     // Make sure the map serialization didn't accidentally recursively serialize
443     // this object.
444     DCHECK_IMPLIES(
445         !serializer_->IsNotMappedSymbol(*object_),
446         serializer_->reference_map()->LookupReference(object_) == nullptr);
447 
448     // Now that the object is allocated, we can resolve pending references to
449     // it.
450     serializer_->ResolvePendingObject(*object_);
451   }
452 
453   if (FLAG_serialization_statistics) {
454     serializer_->CountAllocation(object_->map(), size, space);
455   }
456 
457   // Mark this object as already serialized, and add it to the reference map so
458   // that it can be accessed by backreference by future objects.
459   serializer_->num_back_refs_++;
460 #ifdef DEBUG
461   serializer_->back_refs_.Push(*object_);
462   DCHECK_EQ(serializer_->back_refs_.size(), serializer_->num_back_refs_);
463 #endif
464   if (!serializer_->IsNotMappedSymbol(*object_)) {
465     // Only add the object to the map if it's not not_mapped_symbol, else
466     // the reference IdentityMap has issues. We don't expect to have back
467     // references to the not_mapped_symbol anyway, so it's fine.
468     SerializerReference back_reference =
469         SerializerReference::BackReference(serializer_->num_back_refs_ - 1);
470     serializer_->reference_map()->Add(*object_, back_reference);
471     DCHECK_EQ(*object_,
472               *serializer_->back_refs_[back_reference.back_ref_index()]);
473     DCHECK_EQ(back_reference.back_ref_index(), serializer_->reference_map()
474                                                    ->LookupReference(object_)
475                                                    ->back_ref_index());
476   }
477 }
478 
SerializeBackingStore(void * backing_store,int32_t byte_length,Maybe<int32_t> max_byte_length)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   const SerializerReference* reference_ptr =
483       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   if (max_byte_length.IsJust()) {
490     sink_->Put(kOffHeapResizableBackingStore,
491                "Off-heap resizable backing store");
492   } else {
493     sink_->Put(kOffHeapBackingStore, "Off-heap backing store");
494   }
495   sink_->PutInt(byte_length, "length");
496   if (max_byte_length.IsJust()) {
497     sink_->PutInt(max_byte_length.FromJust(), "max length");
498   }
499   sink_->PutRaw(static_cast<byte*>(backing_store), byte_length, "BackingStore");
500   DCHECK_NE(0, serializer_->seen_backing_stores_index_);
501   SerializerReference reference =
502       SerializerReference::OffHeapBackingStoreReference(
503           serializer_->seen_backing_stores_index_++);
504   // Mark this backing store as already serialized.
505   serializer_->reference_map()->AddBackingStore(backing_store, reference);
506   return reference.off_heap_backing_store_index();
507 }
508 
SerializeJSTypedArray()509 void Serializer::ObjectSerializer::SerializeJSTypedArray() {
510   {
511     DisallowGarbageCollection no_gc;
512     JSTypedArray typed_array = JSTypedArray::cast(*object_);
513     if (typed_array.is_on_heap()) {
514       typed_array.RemoveExternalPointerCompensationForSerialization(isolate());
515     } else {
516       if (!typed_array.WasDetached()) {
517         // Explicitly serialize the backing store now.
518         JSArrayBuffer buffer = JSArrayBuffer::cast(typed_array.buffer());
519         // We cannot store byte_length or max_byte_length larger than int32
520         // range in the snapshot.
521         CHECK_LE(buffer.byte_length(), std::numeric_limits<int32_t>::max());
522         int32_t byte_length = static_cast<int32_t>(buffer.byte_length());
523         Maybe<int32_t> max_byte_length = Nothing<int32_t>();
524         if (buffer.is_resizable()) {
525           CHECK_LE(buffer.max_byte_length(),
526                    std::numeric_limits<int32_t>::max());
527           max_byte_length =
528               Just(static_cast<int32_t>(buffer.max_byte_length()));
529         }
530         size_t byte_offset = typed_array.byte_offset();
531 
532         // We need to calculate the backing store from the data pointer
533         // because the ArrayBuffer may already have been serialized.
534         void* backing_store = reinterpret_cast<void*>(
535             reinterpret_cast<Address>(typed_array.DataPtr()) - byte_offset);
536 
537         uint32_t ref =
538             SerializeBackingStore(backing_store, byte_length, max_byte_length);
539         typed_array.SetExternalBackingStoreRefForSerialization(ref);
540       } else {
541         typed_array.SetExternalBackingStoreRefForSerialization(0);
542       }
543     }
544   }
545   SerializeObject();
546 }
547 
SerializeJSArrayBuffer()548 void Serializer::ObjectSerializer::SerializeJSArrayBuffer() {
549   ArrayBufferExtension* extension;
550   void* backing_store;
551   {
552     DisallowGarbageCollection no_gc;
553     JSArrayBuffer buffer = JSArrayBuffer::cast(*object_);
554     backing_store = buffer.backing_store();
555     // We cannot store byte_length or max_byte_length larger than int32 range in
556     // the snapshot.
557     CHECK_LE(buffer.byte_length(), std::numeric_limits<int32_t>::max());
558     int32_t byte_length = static_cast<int32_t>(buffer.byte_length());
559     Maybe<int32_t> max_byte_length = Nothing<int32_t>();
560     if (buffer.is_resizable()) {
561       CHECK_LE(buffer.max_byte_length(), std::numeric_limits<int32_t>::max());
562       max_byte_length = Just(static_cast<int32_t>(buffer.max_byte_length()));
563     }
564     extension = buffer.extension();
565 
566     // Only serialize non-empty backing stores.
567     if (buffer.IsEmpty()) {
568       buffer.SetBackingStoreRefForSerialization(kEmptyBackingStoreRefSentinel);
569     } else {
570       uint32_t ref =
571           SerializeBackingStore(backing_store, byte_length, max_byte_length);
572       buffer.SetBackingStoreRefForSerialization(ref);
573 
574       // Ensure deterministic output by setting extension to null during
575       // serialization.
576       buffer.set_extension(nullptr);
577     }
578   }
579   SerializeObject();
580   {
581     JSArrayBuffer buffer = JSArrayBuffer::cast(*object_);
582     buffer.set_backing_store(isolate(), backing_store);
583     buffer.set_extension(extension);
584   }
585 }
586 
SerializeExternalString()587 void Serializer::ObjectSerializer::SerializeExternalString() {
588   // For external strings with known resources, we replace the resource field
589   // with the encoded external reference, which we restore upon deserialize.
590   // For the rest we serialize them to look like ordinary sequential strings.
591   Handle<ExternalString> string = Handle<ExternalString>::cast(object_);
592   Address resource = string->resource_as_address();
593   ExternalReferenceEncoder::Value reference;
594   if (serializer_->external_reference_encoder_.TryEncode(resource).To(
595           &reference)) {
596     DCHECK(reference.is_from_api());
597 #ifdef V8_SANDBOXED_EXTERNAL_POINTERS
598     uint32_t external_pointer_entry =
599         string->GetResourceRefForDeserialization();
600 #endif
601     string->SetResourceRefForSerialization(reference.index());
602     SerializeObject();
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   } else {
609     SerializeExternalStringAsSequentialString();
610   }
611 }
612 
SerializeExternalStringAsSequentialString()613 void Serializer::ObjectSerializer::SerializeExternalStringAsSequentialString() {
614   // Instead of serializing this as an external string, we serialize
615   // an imaginary sequential string with the same content.
616   ReadOnlyRoots roots(isolate());
617   PtrComprCageBase cage_base(isolate());
618   DCHECK(object_->IsExternalString(cage_base));
619   Handle<ExternalString> string = Handle<ExternalString>::cast(object_);
620   int length = string->length();
621   Map map;
622   int content_size;
623   int allocation_size;
624   const byte* resource;
625   // Find the map and size for the imaginary sequential string.
626   bool internalized = object_->IsInternalizedString(cage_base);
627   if (object_->IsExternalOneByteString(cage_base)) {
628     map = internalized ? roots.one_byte_internalized_string_map()
629                        : roots.one_byte_string_map();
630     allocation_size = SeqOneByteString::SizeFor(length);
631     content_size = length * kCharSize;
632     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     allocation_size = SeqTwoByteString::SizeFor(length);
637     content_size = length * kShortSize;
638     resource = reinterpret_cast<const byte*>(
639         Handle<ExternalTwoByteString>::cast(string)->resource()->data());
640   }
641 
642   SnapshotSpace space = SnapshotSpace::kOld;
643   SerializePrologue(space, allocation_size, map);
644 
645   // Output the rest of the imaginary string.
646   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   // Output raw data header. Do not bother with common raw length cases here.
651   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   // Serialize string content.
661   sink_->PutRaw(resource, content_size, "StringContent");
662 
663   // Since the allocation size is rounded up to object alignment, there
664   // maybe left-over bytes that need to be padded.
665   int padding_size = allocation_size - SeqString::kHeaderSize - content_size;
666   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 // Clear and later restore the next link in the weak cell or allocation site.
673 // TODO(all): replace this with proper iteration of weak slots in serializer.
674 class V8_NODISCARD UnlinkWeakNextScope {
675  public:
UnlinkWeakNextScope(Heap * heap,HeapObject object)676   explicit UnlinkWeakNextScope(Heap* heap, HeapObject object) {
677     Isolate* isolate = heap->isolate();
678     if (object.IsAllocationSite(isolate) &&
679         AllocationSite::cast(object).HasWeakNext()) {
680       object_ = object;
681       next_ = AllocationSite::cast(object).weak_next();
682       AllocationSite::cast(object).set_weak_next(
683           ReadOnlyRoots(isolate).undefined_value());
684     }
685   }
686 
~UnlinkWeakNextScope()687   ~UnlinkWeakNextScope() {
688     if (next_ == Smi::zero()) return;
689     AllocationSite::cast(object_).set_weak_next(next_,
690                                                 UPDATE_WEAK_WRITE_BARRIER);
691   }
692 
693  private:
694   HeapObject object_;
695   Object next_ = Smi::zero();
696   DISALLOW_GARBAGE_COLLECTION(no_gc_)
697 };
698 
Serialize()699 void Serializer::ObjectSerializer::Serialize() {
700   RecursionScope recursion(serializer_);
701 
702   {
703     DisallowGarbageCollection no_gc;
704     HeapObject raw = *object_;
705     // Defer objects as "pending" if they cannot be serialized now, or if we
706     // exceed a certain recursion depth. Some objects cannot be deferred.
707     if ((recursion.ExceedsMaximum() && CanBeDeferred(raw)) ||
708         serializer_->MustBeDeferred(raw)) {
709       DCHECK(CanBeDeferred(raw));
710       if (FLAG_trace_serializer) {
711         PrintF(" Deferring heap object: ");
712         object_->ShortPrint();
713         PrintF("\n");
714       }
715       // Deferred objects are considered "pending".
716       serializer_->RegisterObjectIsPending(raw);
717       serializer_->PutPendingForwardReference(
718           *serializer_->forward_refs_per_pending_object_.Find(raw));
719       serializer_->QueueDeferredObject(raw);
720       return;
721     }
722 
723     if (FLAG_trace_serializer) {
724       PrintF(" Encoding heap object: ");
725       object_->ShortPrint();
726       PrintF("\n");
727     }
728   }
729 
730   PtrComprCageBase cage_base(isolate());
731   InstanceType instance_type = object_->map(cage_base).instance_type();
732   if (InstanceTypeChecker::IsExternalString(instance_type)) {
733     SerializeExternalString();
734     return;
735   } else if (!ReadOnlyHeap::Contains(*object_)) {
736     // Only clear padding for strings outside the read-only heap. Read-only heap
737     // should have been cleared elsewhere.
738     if (object_->IsSeqOneByteString(cage_base)) {
739       // Clear padding bytes at the end. Done here to avoid having to do this
740       // 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     SerializeJSTypedArray();
748     return;
749   } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) {
750     SerializeJSArrayBuffer();
751     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   // We don't expect fillers.
759   DCHECK(!object_->IsFreeSpaceOrFiller(cage_base));
760 
761   SerializeObject();
762 }
763 
764 namespace {
GetSnapshotSpace(HeapObject object)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       return SnapshotSpace::kMap;
773     } else {
774       return SnapshotSpace::kOld;
775     }
776   } else if (ReadOnlyHeap::Contains(object)) {
777     return SnapshotSpace::kReadOnlyHeap;
778   } else {
779     AllocationSpace heap_space =
780         MemoryChunk::FromHeapObject(object)->owner_identity();
781     // Large code objects are not supported and cannot be expressed by
782     // SnapshotSpace.
783     DCHECK_NE(heap_space, CODE_LO_SPACE);
784     switch (heap_space) {
785       case OLD_SPACE:
786       // Young generation objects are tenured, as objects that have survived
787       // until snapshot building probably deserve to be considered 'old'.
788       case NEW_SPACE:
789       // Large objects (young and old) are encoded as simply 'old' snapshot
790       // obects, as "normal" objects vs large objects is a heap implementation
791       // detail and isn't relevant to the snapshot.
792       case NEW_LO_SPACE:
793       case LO_SPACE:
794         return SnapshotSpace::kOld;
795       case CODE_SPACE:
796         return SnapshotSpace::kCode;
797       case MAP_SPACE:
798         return SnapshotSpace::kMap;
799       case CODE_LO_SPACE:
800       case RO_SPACE:
801         UNREACHABLE();
802     }
803   }
804 }
805 }  // namespace
806 
SerializeObject()807 void Serializer::ObjectSerializer::SerializeObject() {
808   Map map = object_->map(serializer_->cage_base());
809   int size = object_->SizeFromMap(map);
810 
811   // Descriptor arrays have complex element weakness, that is dependent on the
812   // maps pointing to them. During deserialization, this can cause them to get
813   // prematurely trimmed one of their owners isn't deserialized yet. We work
814   // around this by forcing all descriptor arrays to be serialized as "strong",
815   // i.e. no custom weakness, and "re-weaken" them in the deserializer once
816   // deserialization completes.
817   //
818   // See also `Deserializer::WeakenDescriptorArrays`.
819   if (map == ReadOnlyRoots(isolate()).descriptor_array_map()) {
820     map = ReadOnlyRoots(isolate()).strong_descriptor_array_map();
821   }
822   SnapshotSpace space = GetSnapshotSpace(*object_);
823   SerializePrologue(space, size, map);
824 
825   // Serialize the rest of the object.
826   CHECK_EQ(0, bytes_processed_so_far_);
827   bytes_processed_so_far_ = kTaggedSize;
828 
829   SerializeContent(map, size);
830 }
831 
SerializeDeferred()832 void Serializer::ObjectSerializer::SerializeDeferred() {
833   const SerializerReference* back_reference =
834       serializer_->reference_map()->LookupReference(object_);
835 
836   if (back_reference != nullptr) {
837     if (FLAG_trace_serializer) {
838       PrintF(" Deferred heap object ");
839       object_->ShortPrint();
840       PrintF(" was already serialized\n");
841     }
842     return;
843   }
844 
845   if (FLAG_trace_serializer) {
846     PrintF(" Encoding deferred heap object\n");
847   }
848   Serialize();
849 }
850 
SerializeContent(Map map,int size)851 void Serializer::ObjectSerializer::SerializeContent(Map map, int size) {
852   HeapObject raw = *object_;
853   UnlinkWeakNextScope unlink_weak_next(isolate()->heap(), raw);
854   if (raw.IsCode()) {
855     // For code objects, perform a custom serialization.
856     SerializeCode(map, size);
857   } else {
858     // 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 
VisitPointers(HeapObject host,ObjectSlot start,ObjectSlot end)865 void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
866                                                  ObjectSlot start,
867                                                  ObjectSlot end) {
868   VisitPointers(host, MaybeObjectSlot(start), MaybeObjectSlot(end));
869 }
870 
VisitPointers(HeapObject host,MaybeObjectSlot start,MaybeObjectSlot end)871 void Serializer::ObjectSerializer::VisitPointers(HeapObject host,
872                                                  MaybeObjectSlot start,
873                                                  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     // TODO(ishell): Revisit this change once we stick to 32-bit compressed
887     // tagged values.
888     while (current < end && current.load(cage_base).IsCleared()) {
889       sink_->Put(kClearedWeakReference, "ClearedWeakReference");
890       bytes_processed_so_far_ += kTaggedSize;
891       ++current;
892     }
893     HeapObject current_contents;
894     HeapObjectReferenceType reference_type;
895     while (current < end && current.load(cage_base).GetHeapObject(
896                                 &current_contents, &reference_type)) {
897       // Write a weak prefix if we need it. This has to be done before the
898       // potential pending object serialization.
899       if (reference_type == HeapObjectReferenceType::WEAK) {
900         sink_->Put(kWeakPrefix, "WeakReference");
901       }
902 
903       Handle<HeapObject> obj = handle(current_contents, isolate());
904       if (serializer_->SerializePendingObject(*obj)) {
905         bytes_processed_so_far_ += kTaggedSize;
906         ++current;
907         continue;
908       }
909 
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       MaybeObjectSlot repeat_end = current + 1;
915       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         while (repeat_end < end && *repeat_end == *current) {
922           repeat_end++;
923         }
924         int repeat_count = static_cast<int>(repeat_end - current);
925         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 
VisitCodePointer(HeapObject host,CodeObjectSlot slot)938 void Serializer::ObjectSerializer::VisitCodePointer(HeapObject host,
939                                                     CodeObjectSlot slot) {
940   CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
941   // A version of VisitPointers() customized for CodeObjectSlot.
942   HandleScope scope(isolate());
943   DisallowGarbageCollection no_gc;
944 
945 #ifdef V8_EXTERNAL_CODE_SPACE
946   PtrComprCageBase code_cage_base(isolate()->code_cage_base());
947 #else
948   PtrComprCageBase code_cage_base(isolate());
949 #endif
950   Object contents = slot.load(code_cage_base);
951   DCHECK(HAS_STRONG_HEAP_OBJECT_TAG(contents.ptr()));
952   DCHECK(contents.IsCode());
953 
954   Handle<HeapObject> obj = handle(HeapObject::cast(contents), isolate());
955   if (!serializer_->SerializePendingObject(*obj)) {
956     serializer_->SerializeObject(obj);
957   }
958   bytes_processed_so_far_ += kTaggedSize;
959 }
960 
OutputExternalReference(Address target,int target_size,bool sandboxify,ExternalPointerTag tag)961 void Serializer::ObjectSerializer::OutputExternalReference(
962     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   ExternalReferenceEncoder::Value encoded_reference;
966   bool encoded_successfully;
967 
968   if (serializer_->allow_unknown_external_references_for_testing()) {
969     encoded_successfully =
970         serializer_->TryEncodeExternalReference(target).To(&encoded_reference);
971   } else {
972     encoded_reference = serializer_->EncodeExternalReference(target);
973     encoded_successfully = true;
974   }
975 
976   if (!encoded_successfully) {
977     // In this case the serialized snapshot will not be used in a different
978     // Isolate and thus the target address will not change between
979     // serialization and deserialization. We can serialize seen external
980     // references verbatim.
981     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     sink_->PutRaw(reinterpret_cast<byte*>(&target), target_size, "Bytes");
987   } else if (encoded_reference.is_from_api()) {
988     if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
989       sink_->Put(kSandboxedApiReference, "SandboxedApiRef");
990     } else {
991       sink_->Put(kApiReference, "ApiRef");
992     }
993     sink_->PutInt(encoded_reference.index(), "reference index");
994   } else {
995     if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
996       sink_->Put(kSandboxedExternalReference, "SandboxedExternalRef");
997     } else {
998       sink_->Put(kExternalReference, "ExternalRef");
999     }
1000     sink_->PutInt(encoded_reference.index(), "reference index");
1001   }
1002   if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOL && sandboxify) {
1003     sink_->PutInt(static_cast<uint32_t>(tag >> kExternalPointerTagShift),
1004                   "external pointer tag");
1005   }
1006 }
1007 
VisitExternalReference(Foreign host,Address * p)1008 void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
1009                                                           Address* p) {
1010   // "Sandboxify" external reference.
1011   OutputExternalReference(host.foreign_address(), kSystemPointerSize, true,
1012                           kForeignForeignAddressTag);
1013   bytes_processed_so_far_ += kExternalPointerSize;
1014 }
1015 
1016 class Serializer::ObjectSerializer::RelocInfoObjectPreSerializer {
1017  public:
RelocInfoObjectPreSerializer(Serializer * serializer)1018   explicit RelocInfoObjectPreSerializer(Serializer* serializer)
1019       : serializer_(serializer) {}
1020 
VisitEmbeddedPointer(Code host,RelocInfo * target)1021   void VisitEmbeddedPointer(Code host, RelocInfo* target) {
1022     HeapObject object = target->target_object(isolate());
1023     serializer_->SerializeObject(handle(object, isolate()));
1024     num_serialized_objects_++;
1025   }
VisitCodeTarget(Code host,RelocInfo * target)1026   void VisitCodeTarget(Code host, RelocInfo* target) {
1027 #ifdef V8_TARGET_ARCH_ARM
1028     DCHECK(!RelocInfo::IsRelativeCodeTarget(target->rmode()));
1029 #endif
1030     Code object = Code::GetCodeFromTargetAddress(target->target_address());
1031     serializer_->SerializeObject(handle(object, isolate()));
1032     num_serialized_objects_++;
1033   }
1034 
VisitExternalReference(Code host,RelocInfo * rinfo)1035   void VisitExternalReference(Code host, RelocInfo* rinfo) {}
VisitInternalReference(Code host,RelocInfo * rinfo)1036   void VisitInternalReference(Code host, RelocInfo* rinfo) {}
VisitRuntimeEntry(Code host,RelocInfo * reloc)1037   void VisitRuntimeEntry(Code host, RelocInfo* reloc) { UNREACHABLE(); }
VisitOffHeapTarget(Code host,RelocInfo * target)1038   void VisitOffHeapTarget(Code host, RelocInfo* target) {}
1039 
num_serialized_objects() const1040   int num_serialized_objects() const { return num_serialized_objects_; }
1041 
isolate()1042   Isolate* isolate() { return serializer_->isolate(); }
1043 
1044  private:
1045   Serializer* serializer_;
1046   int num_serialized_objects_ = 0;
1047 };
1048 
VisitEmbeddedPointer(Code host,RelocInfo * rinfo)1049 void Serializer::ObjectSerializer::VisitEmbeddedPointer(Code host,
1050                                                         RelocInfo* rinfo) {
1051   // Target object should be pre-serialized by RelocInfoObjectPreSerializer, so
1052   // just track the pointer's existence as kTaggedSize in
1053   // bytes_processed_so_far_.
1054   // TODO(leszeks): DCHECK that RelocInfoObjectPreSerializer serialized this
1055   // specific object already.
1056   bytes_processed_so_far_ += kTaggedSize;
1057 }
1058 
VisitExternalReference(Code host,RelocInfo * rinfo)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   DCHECK_IMPLIES(serializer_->EncodeExternalReference(target).is_from_api(),
1064                  !rinfo->IsCodedSpecially());
1065   // Don't "sandboxify" external references embedded in the code.
1066   OutputExternalReference(target, rinfo->target_address_size(), false,
1067                           kExternalPointerNullTag);
1068 }
1069 
VisitExternalPointer(HeapObject host,ExternalPointer_t ptr)1070 void Serializer::ObjectSerializer::VisitExternalPointer(HeapObject host,
1071                                                         ExternalPointer_t ptr) {
1072   // TODO(v8:12700) handle other external references here as well. This should
1073   // allow removing some of the other Visit* methods, should unify the sandbox
1074   // vs no-sandbox implementation, and should allow removing various
1075   // XYZForSerialization methods throughout the codebase.
1076   if (host.IsJSExternalObject()) {
1077 #ifdef V8_SANDBOXED_EXTERNAL_POINTERS
1078     // TODO(saelo) maybe add a helper method for this conversion if also needed
1079     // in other places? This might require a ExternalPointerTable::Get variant
1080     // that drops the pointer tag completely.
1081     uint32_t index = ptr >> kExternalPointerIndexShift;
1082     Address value =
1083         isolate()->external_pointer_table().Get(index, kExternalObjectValueTag);
1084 #else
1085     Address value = ptr;
1086 #endif
1087     // TODO(v8:12700) should we specify here whether we expect the references to
1088     // be internal or external (or either)?
1089     OutputExternalReference(value, kSystemPointerSize, true,
1090                             kExternalObjectValueTag);
1091     bytes_processed_so_far_ += kExternalPointerSize;
1092   }
1093 }
1094 
VisitInternalReference(Code host,RelocInfo * rinfo)1095 void Serializer::ObjectSerializer::VisitInternalReference(Code host,
1096                                                           RelocInfo* rinfo) {
1097   Address entry = Handle<Code>::cast(object_)->entry();
1098   DCHECK_GE(rinfo->target_internal_reference(), entry);
1099   uintptr_t target_offset = rinfo->target_internal_reference() - entry;
1100   // TODO(jgruber,v8:11036): We are being permissive for this DCHECK, but
1101   // consider using raw_instruction_size() instead of raw_body_size() in the
1102   // 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 
VisitRuntimeEntry(Code host,RelocInfo * rinfo)1109 void Serializer::ObjectSerializer::VisitRuntimeEntry(Code host,
1110                                                      RelocInfo* rinfo) {
1111   // We no longer serialize code that contains runtime entries.
1112   UNREACHABLE();
1113 }
1114 
VisitOffHeapTarget(Code host,RelocInfo * rinfo)1115 void Serializer::ObjectSerializer::VisitOffHeapTarget(Code host,
1116                                                       RelocInfo* rinfo) {
1117   STATIC_ASSERT(EmbeddedData::kTableSize == Builtins::kBuiltinCount);
1118 
1119   Address addr = rinfo->target_off_heap_target();
1120   CHECK_NE(kNullAddress, addr);
1121 
1122   Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate(), addr);
1123   CHECK(Builtins::IsBuiltinId(builtin));
1124   CHECK(Builtins::IsIsolateIndependent(builtin));
1125 
1126   sink_->Put(kOffHeapTarget, "OffHeapTarget");
1127   sink_->PutInt(static_cast<int>(builtin), "builtin index");
1128 }
1129 
VisitCodeTarget(Code host,RelocInfo * rinfo)1130 void Serializer::ObjectSerializer::VisitCodeTarget(Code host,
1131                                                    RelocInfo* rinfo) {
1132   // Target object should be pre-serialized by RelocInfoObjectPreSerializer, so
1133   // just track the pointer's existence as kTaggedSize in
1134   // bytes_processed_so_far_.
1135   // TODO(leszeks): DCHECK that RelocInfoObjectPreSerializer serialized this
1136   // specific object already.
1137   bytes_processed_so_far_ += kTaggedSize;
1138 }
1139 
1140 namespace {
1141 
1142 // Similar to OutputRawData, but substitutes the given field with the given
1143 // value instead of reading it from the object.
OutputRawWithCustomField(SnapshotByteSink * sink,Address object_start,int written_so_far,int bytes_to_write,int field_offset,int field_size,const byte * field_value)1144 void OutputRawWithCustomField(SnapshotByteSink* sink, Address object_start,
1145                               int written_so_far, int bytes_to_write,
1146                               int field_offset, int field_size,
1147                               const byte* field_value) {
1148   int offset = field_offset - written_so_far;
1149   if (0 <= offset && offset < bytes_to_write) {
1150     DCHECK_GE(bytes_to_write, offset + field_size);
1151     sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far), offset,
1152                  "Bytes");
1153     sink->PutRaw(field_value, field_size, "Bytes");
1154     written_so_far += offset + field_size;
1155     bytes_to_write -= offset + field_size;
1156     sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far),
1157                  bytes_to_write, "Bytes");
1158   } else {
1159     sink->PutRaw(reinterpret_cast<byte*>(object_start + written_so_far),
1160                  bytes_to_write, "Bytes");
1161   }
1162 }
1163 }  // anonymous namespace
1164 
OutputRawData(Address up_to)1165 void Serializer::ObjectSerializer::OutputRawData(Address up_to) {
1166   Address object_start = object_->address();
1167   int base = bytes_processed_so_far_;
1168   int up_to_offset = static_cast<int>(up_to - object_start);
1169   int to_skip = up_to_offset - bytes_processed_so_far_;
1170   int bytes_to_output = to_skip;
1171   DCHECK(IsAligned(bytes_to_output, kTaggedSize));
1172   int tagged_to_output = bytes_to_output / kTaggedSize;
1173   bytes_processed_so_far_ += to_skip;
1174   DCHECK_GE(to_skip, 0);
1175   if (bytes_to_output != 0) {
1176     DCHECK(to_skip == bytes_to_output);
1177     if (tagged_to_output <= kFixedRawDataCount) {
1178       sink_->Put(FixedRawDataWithSize::Encode(tagged_to_output),
1179                  "FixedRawData");
1180     } else {
1181       sink_->Put(kVariableRawData, "VariableRawData");
1182       sink_->PutInt(tagged_to_output, "length");
1183     }
1184 #ifdef MEMORY_SANITIZER
1185     // Check that we do not serialize uninitialized memory.
1186     __msan_check_mem_is_initialized(
1187         reinterpret_cast<void*>(object_start + base), bytes_to_output);
1188 #endif  // MEMORY_SANITIZER
1189     PtrComprCageBase cage_base(isolate_);
1190     if (object_->IsBytecodeArray(cage_base)) {
1191       // The bytecode age field can be changed by GC concurrently.
1192       static_assert(BytecodeArray::kBytecodeAgeSize == kUInt16Size);
1193       uint16_t field_value = BytecodeArray::kNoAgeBytecodeAge;
1194       OutputRawWithCustomField(sink_, object_start, base, bytes_to_output,
1195                                BytecodeArray::kBytecodeAgeOffset,
1196                                sizeof(field_value),
1197                                reinterpret_cast<byte*>(&field_value));
1198     } else if (object_->IsDescriptorArray(cage_base)) {
1199       // The number of marked descriptors field can be changed by GC
1200       // concurrently.
1201       static byte field_value[2] = {0};
1202       OutputRawWithCustomField(
1203           sink_, object_start, base, bytes_to_output,
1204           DescriptorArray::kRawNumberOfMarkedDescriptorsOffset,
1205           sizeof(field_value), field_value);
1206     } else if (V8_EXTERNAL_CODE_SPACE_BOOL &&
1207                object_->IsCodeDataContainer(cage_base)) {
1208       // code_cage_base and code_entry_point fields contain raw values that
1209       // will be recomputed after deserialization, so write zeros to keep the
1210       // snapshot deterministic.
1211       CHECK_EQ(CodeDataContainer::kCodeCageBaseUpper32BitsOffset + kTaggedSize,
1212                CodeDataContainer::kCodeEntryPointOffset);
1213       static byte field_value[kTaggedSize + kExternalPointerSize] = {0};
1214       OutputRawWithCustomField(
1215           sink_, object_start, base, bytes_to_output,
1216           CodeDataContainer::kCodeCageBaseUpper32BitsOffset,
1217           sizeof(field_value), field_value);
1218     } else {
1219       sink_->PutRaw(reinterpret_cast<byte*>(object_start + base),
1220                     bytes_to_output, "Bytes");
1221     }
1222   }
1223 }
1224 
SerializeCode(Map map,int size)1225 void Serializer::ObjectSerializer::SerializeCode(Map map, int size) {
1226   static const int kWipeOutModeMask =
1227       RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
1228       RelocInfo::ModeMask(RelocInfo::FULL_EMBEDDED_OBJECT) |
1229       RelocInfo::ModeMask(RelocInfo::COMPRESSED_EMBEDDED_OBJECT) |
1230       RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
1231       RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
1232       RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
1233       RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
1234       RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
1235 
1236   DCHECK_EQ(HeapObject::kHeaderSize, bytes_processed_so_far_);
1237   Handle<Code> on_heap_code = Handle<Code>::cast(object_);
1238 
1239   // With enabled pointer compression normal accessors no longer work for
1240   // off-heap objects, so we have to get the relocation info data via the
1241   // on-heap code object.
1242   ByteArray relocation_info = on_heap_code->unchecked_relocation_info();
1243 
1244   // To make snapshots reproducible, we make a copy of the code object
1245   // and wipe all pointers in the copy, which we then serialize.
1246   Code off_heap_code = serializer_->CopyCode(*on_heap_code);
1247   for (RelocIterator it(off_heap_code, relocation_info, kWipeOutModeMask);
1248        !it.done(); it.next()) {
1249     RelocInfo* rinfo = it.rinfo();
1250     rinfo->WipeOut();
1251   }
1252   // We need to wipe out the header fields *after* wiping out the
1253   // relocations, because some of these fields are needed for the latter.
1254   off_heap_code.WipeOutHeader();
1255 
1256   // Initially skip serializing the code header. We'll serialize it after the
1257   // Code body, so that the various fields the Code needs for iteration are
1258   // already valid.
1259   sink_->Put(kCodeBody, "kCodeBody");
1260 
1261   // 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   __msan_check_mem_is_initialized(reinterpret_cast<void*>(start),
1272                                   bytes_to_output);
1273 #endif  // MEMORY_SANITIZER
1274   sink_->PutRaw(reinterpret_cast<byte*>(start), bytes_to_output, "Code");
1275 
1276   // Manually serialize the code header. We don't use Code::BodyDescriptor
1277   // here as we don't yet want to walk the RelocInfos.
1278   DCHECK_EQ(HeapObject::kHeaderSize, bytes_processed_so_far_);
1279   VisitPointers(*on_heap_code, on_heap_code->RawField(HeapObject::kHeaderSize),
1280                 on_heap_code->RawField(Code::kDataStart));
1281   DCHECK_EQ(bytes_processed_so_far_, Code::kDataStart);
1282 
1283   // Now serialize RelocInfos. We can't allocate during a RelocInfo walk during
1284   // deserualization, so we have two passes for RelocInfo serialization:
1285   //   1. A pre-serializer which serializes all allocatable objects in the
1286   //      RelocInfo, followed by a kSynchronize bytecode, and
1287   //   2. A walk the RelocInfo with this serializer, serializing any objects
1288   //      implicitly as offsets into the pre-serializer's object array.
1289   // This way, the deserializer can deserialize the allocatable objects first,
1290   // without walking RelocInfo, re-build the pre-serializer's object array, and
1291   // only then walk the RelocInfo itself.
1292   // TODO(leszeks): We only really need to pre-serialize objects which need
1293   // serialization, i.e. no backrefs or roots.
1294   RelocInfoObjectPreSerializer pre_serializer(serializer_);
1295   for (RelocIterator it(*on_heap_code, relocation_info,
1296                         Code::BodyDescriptor::kRelocModeMask);
1297        !it.done(); it.next()) {
1298     it.rinfo()->Visit(&pre_serializer);
1299   }
1300   // Mark that the pre-serialization finished with a kSynchronize bytecode.
1301   sink_->Put(kSynchronize, "PreSerializationFinished");
1302 
1303   // Finally serialize all RelocInfo objects in the on-heap Code, knowing that
1304   // we will not do a recursive serialization.
1305   // TODO(leszeks): Add a scope that DCHECKs this.
1306   for (RelocIterator it(*on_heap_code, relocation_info,
1307                         Code::BodyDescriptor::kRelocModeMask);
1308        !it.done(); it.next()) {
1309     it.rinfo()->Visit(this);
1310   }
1311 
1312   // We record a kTaggedSize for every object encountered during the
1313   // serialization, so DCHECK that bytes_processed_so_far_ matches the expected
1314   // number of bytes (i.e. the code header + a tagged size per pre-serialized
1315   // object).
1316   DCHECK_EQ(
1317       bytes_processed_so_far_,
1318       Code::kDataStart + kTaggedSize * pre_serializer.num_serialized_objects());
1319 }
1320 
HotObjectsList(Heap * heap)1321 Serializer::HotObjectsList::HotObjectsList(Heap* heap) : heap_(heap) {
1322   strong_roots_entry_ = heap->RegisterStrongRoots(
1323       "Serializer::HotObjectsList", FullObjectSlot(&circular_queue_[0]),
1324       FullObjectSlot(&circular_queue_[kSize]));
1325 }
~HotObjectsList()1326 Serializer::HotObjectsList::~HotObjectsList() {
1327   heap_->UnregisterStrongRoots(strong_roots_entry_);
1328 }
1329 
Values(Isolate * isolate)1330 Handle<FixedArray> ObjectCacheIndexMap::Values(Isolate* isolate) {
1331   if (size() == 0) {
1332     return isolate->factory()->empty_fixed_array();
1333   }
1334   Handle<FixedArray> externals = isolate->factory()->NewFixedArray(size());
1335   DisallowGarbageCollection no_gc;
1336   FixedArray raw = *externals;
1337   IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope(
1338       &map_);
1339   for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
1340     raw.set(*it.entry(), it.key());
1341   }
1342 
1343   return externals;
1344 }
1345 
1346 }  // namespace internal
1347 }  // namespace v8
1348