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 ¤t_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