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/assembler-inl.h"
8 #include "src/interpreter/interpreter.h"
9 #include "src/objects/code.h"
10 #include "src/objects/map.h"
11 #include "src/snapshot/builtin-serializer-allocator.h"
12 #include "src/snapshot/natives.h"
13 #include "src/snapshot/snapshot.h"
14
15 namespace v8 {
16 namespace internal {
17
18 template <class AllocatorT>
Serializer(Isolate * isolate)19 Serializer<AllocatorT>::Serializer(Isolate* isolate)
20 : isolate_(isolate),
21 external_reference_encoder_(isolate),
22 root_index_map_(isolate),
23 allocator_(this) {
24 #ifdef OBJECT_PRINT
25 if (FLAG_serialization_statistics) {
26 instance_type_count_ = NewArray<int>(kInstanceTypes);
27 instance_type_size_ = NewArray<size_t>(kInstanceTypes);
28 read_only_instance_type_count_ = NewArray<int>(kInstanceTypes);
29 read_only_instance_type_size_ = NewArray<size_t>(kInstanceTypes);
30 for (int i = 0; i < kInstanceTypes; i++) {
31 instance_type_count_[i] = 0;
32 instance_type_size_[i] = 0;
33 read_only_instance_type_count_[i] = 0;
34 read_only_instance_type_size_[i] = 0;
35 }
36 } else {
37 instance_type_count_ = nullptr;
38 instance_type_size_ = nullptr;
39 read_only_instance_type_count_ = nullptr;
40 read_only_instance_type_size_ = nullptr;
41 }
42 #endif // OBJECT_PRINT
43 }
44
45 template <class AllocatorT>
~Serializer()46 Serializer<AllocatorT>::~Serializer() {
47 if (code_address_map_ != nullptr) delete code_address_map_;
48 #ifdef OBJECT_PRINT
49 if (instance_type_count_ != nullptr) {
50 DeleteArray(instance_type_count_);
51 DeleteArray(instance_type_size_);
52 DeleteArray(read_only_instance_type_count_);
53 DeleteArray(read_only_instance_type_size_);
54 }
55 #endif // OBJECT_PRINT
56 }
57
58 #ifdef OBJECT_PRINT
59 template <class AllocatorT>
CountInstanceType(Map * map,int size,AllocationSpace space)60 void Serializer<AllocatorT>::CountInstanceType(Map* map, int size,
61 AllocationSpace space) {
62 int instance_type = map->instance_type();
63 if (space != RO_SPACE) {
64 instance_type_count_[instance_type]++;
65 instance_type_size_[instance_type] += size;
66 } else {
67 read_only_instance_type_count_[instance_type]++;
68 read_only_instance_type_size_[instance_type] += size;
69 }
70 }
71 #endif // OBJECT_PRINT
72
73 template <class AllocatorT>
OutputStatistics(const char * name)74 void Serializer<AllocatorT>::OutputStatistics(const char* name) {
75 if (!FLAG_serialization_statistics) return;
76
77 PrintF("%s:\n", name);
78 allocator()->OutputStatistics();
79
80 #ifdef OBJECT_PRINT
81 PrintF(" Instance types (count and bytes):\n");
82 #define PRINT_INSTANCE_TYPE(Name) \
83 if (instance_type_count_[Name]) { \
84 PrintF("%10d %10" PRIuS " %s\n", instance_type_count_[Name], \
85 instance_type_size_[Name], #Name); \
86 }
87 INSTANCE_TYPE_LIST(PRINT_INSTANCE_TYPE)
88 #undef PRINT_INSTANCE_TYPE
89 size_t read_only_total = 0;
90 #define UPDATE_TOTAL(Name) \
91 read_only_total += read_only_instance_type_size_[Name];
92 INSTANCE_TYPE_LIST(UPDATE_TOTAL)
93 #undef UPDATE_TOTAL
94 if (read_only_total > 0) {
95 PrintF("\n Read Only Instance types (count and bytes):\n");
96 #define PRINT_INSTANCE_TYPE(Name) \
97 if (read_only_instance_type_count_[Name]) { \
98 PrintF("%10d %10" PRIuS " %s\n", read_only_instance_type_count_[Name], \
99 read_only_instance_type_size_[Name], #Name); \
100 }
101 INSTANCE_TYPE_LIST(PRINT_INSTANCE_TYPE)
102 #undef PRINT_INSTANCE_TYPE
103 }
104 PrintF("\n");
105 #endif // OBJECT_PRINT
106 }
107
108 template <class AllocatorT>
SerializeDeferredObjects()109 void Serializer<AllocatorT>::SerializeDeferredObjects() {
110 while (!deferred_objects_.empty()) {
111 HeapObject* obj = deferred_objects_.back();
112 deferred_objects_.pop_back();
113 ObjectSerializer obj_serializer(this, obj, &sink_, kPlain, kStartOfObject);
114 obj_serializer.SerializeDeferred();
115 }
116 sink_.Put(kSynchronize, "Finished with deferred objects");
117 }
118
119 template <class AllocatorT>
MustBeDeferred(HeapObject * object)120 bool Serializer<AllocatorT>::MustBeDeferred(HeapObject* object) {
121 return false;
122 }
123
124 template <class AllocatorT>
VisitRootPointers(Root root,const char * description,Object ** start,Object ** end)125 void Serializer<AllocatorT>::VisitRootPointers(Root root,
126 const char* description,
127 Object** start, Object** end) {
128 // Builtins and bytecode handlers are serialized in a separate pass by the
129 // BuiltinSerializer.
130 if (root == Root::kBuiltins || root == Root::kDispatchTable) return;
131
132 for (Object** current = start; current < end; current++) {
133 if ((*current)->IsSmi()) {
134 PutSmi(Smi::cast(*current));
135 } else {
136 SerializeObject(HeapObject::cast(*current), kPlain, kStartOfObject, 0);
137 }
138 }
139 }
140
141 #ifdef DEBUG
142 template <class AllocatorT>
PrintStack()143 void Serializer<AllocatorT>::PrintStack() {
144 for (const auto o : stack_) {
145 o->Print();
146 PrintF("\n");
147 }
148 }
149 #endif // DEBUG
150
151 template <class AllocatorT>
SerializeHotObject(HeapObject * obj,HowToCode how_to_code,WhereToPoint where_to_point,int skip)152 bool Serializer<AllocatorT>::SerializeHotObject(HeapObject* obj,
153 HowToCode how_to_code,
154 WhereToPoint where_to_point,
155 int skip) {
156 if (how_to_code != kPlain || where_to_point != kStartOfObject) return false;
157 // Encode a reference to a hot object by its index in the working set.
158 int index = hot_objects_.Find(obj);
159 if (index == HotObjectsList::kNotFound) return false;
160 DCHECK(index >= 0 && index < kNumberOfHotObjects);
161 if (FLAG_trace_serializer) {
162 PrintF(" Encoding hot object %d:", index);
163 obj->ShortPrint();
164 PrintF("\n");
165 }
166 if (skip != 0) {
167 sink_.Put(kHotObjectWithSkip + index, "HotObjectWithSkip");
168 sink_.PutInt(skip, "HotObjectSkipDistance");
169 } else {
170 sink_.Put(kHotObject + index, "HotObject");
171 }
172 return true;
173 }
174
175 template <class AllocatorT>
SerializeBackReference(HeapObject * obj,HowToCode how_to_code,WhereToPoint where_to_point,int skip)176 bool Serializer<AllocatorT>::SerializeBackReference(HeapObject* obj,
177 HowToCode how_to_code,
178 WhereToPoint where_to_point,
179 int skip) {
180 SerializerReference reference = reference_map_.Lookup(obj);
181 if (!reference.is_valid()) return false;
182 // Encode the location of an already deserialized object in order to write
183 // its location into a later object. We can encode the location as an
184 // offset fromthe start of the deserialized objects or as an offset
185 // backwards from thecurrent allocation pointer.
186 if (reference.is_attached_reference()) {
187 FlushSkip(skip);
188 if (FLAG_trace_serializer) {
189 PrintF(" Encoding attached reference %d\n",
190 reference.attached_reference_index());
191 }
192 PutAttachedReference(reference, how_to_code, where_to_point);
193 } else {
194 DCHECK(reference.is_back_reference());
195 if (FLAG_trace_serializer) {
196 PrintF(" Encoding back reference to: ");
197 obj->ShortPrint();
198 PrintF("\n");
199 }
200
201 PutAlignmentPrefix(obj);
202 AllocationSpace space = reference.space();
203 if (skip == 0) {
204 sink_.Put(kBackref + how_to_code + where_to_point + space, "BackRef");
205 } else {
206 sink_.Put(kBackrefWithSkip + how_to_code + where_to_point + space,
207 "BackRefWithSkip");
208 sink_.PutInt(skip, "BackRefSkipDistance");
209 }
210 PutBackReference(obj, reference);
211 }
212 return true;
213 }
214
215 template <class AllocatorT>
SerializeBuiltinReference(HeapObject * obj,HowToCode how_to_code,WhereToPoint where_to_point,int skip)216 bool Serializer<AllocatorT>::SerializeBuiltinReference(
217 HeapObject* obj, HowToCode how_to_code, WhereToPoint where_to_point,
218 int skip) {
219 if (!obj->IsCode()) return false;
220
221 Code* code = Code::cast(obj);
222 int builtin_index = code->builtin_index();
223 if (builtin_index < 0) return false;
224
225 DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
226 (how_to_code == kFromCode));
227 DCHECK_LT(builtin_index, Builtins::builtin_count);
228 DCHECK_LE(0, builtin_index);
229
230 if (FLAG_trace_serializer) {
231 PrintF(" Encoding builtin reference: %s\n",
232 isolate()->builtins()->name(builtin_index));
233 }
234
235 FlushSkip(skip);
236 sink_.Put(kBuiltin + how_to_code + where_to_point, "Builtin");
237 sink_.PutInt(builtin_index, "builtin_index");
238
239 return true;
240 }
241
242 template <class AllocatorT>
ObjectIsBytecodeHandler(HeapObject * obj) const243 bool Serializer<AllocatorT>::ObjectIsBytecodeHandler(HeapObject* obj) const {
244 if (!obj->IsCode()) return false;
245 Code* code = Code::cast(obj);
246 if (isolate()->heap()->IsDeserializeLazyHandler(code)) return false;
247 return (code->kind() == Code::BYTECODE_HANDLER);
248 }
249
250 template <class AllocatorT>
PutRoot(int root_index,HeapObject * object,SerializerDeserializer::HowToCode how_to_code,SerializerDeserializer::WhereToPoint where_to_point,int skip)251 void Serializer<AllocatorT>::PutRoot(
252 int root_index, HeapObject* object,
253 SerializerDeserializer::HowToCode how_to_code,
254 SerializerDeserializer::WhereToPoint where_to_point, int skip) {
255 if (FLAG_trace_serializer) {
256 PrintF(" Encoding root %d:", root_index);
257 object->ShortPrint();
258 PrintF("\n");
259 }
260
261 // Assert that the first 32 root array items are a conscious choice. They are
262 // chosen so that the most common ones can be encoded more efficiently.
263 STATIC_ASSERT(Heap::kEmptyDescriptorArrayRootIndex ==
264 kNumberOfRootArrayConstants - 1);
265
266 if (how_to_code == kPlain && where_to_point == kStartOfObject &&
267 root_index < kNumberOfRootArrayConstants &&
268 !isolate()->heap()->InNewSpace(object)) {
269 if (skip == 0) {
270 sink_.Put(kRootArrayConstants + root_index, "RootConstant");
271 } else {
272 sink_.Put(kRootArrayConstantsWithSkip + root_index, "RootConstant");
273 sink_.PutInt(skip, "SkipInPutRoot");
274 }
275 } else {
276 FlushSkip(skip);
277 sink_.Put(kRootArray + how_to_code + where_to_point, "RootSerialization");
278 sink_.PutInt(root_index, "root_index");
279 hot_objects_.Add(object);
280 }
281 }
282
283 template <class AllocatorT>
PutSmi(Smi * smi)284 void Serializer<AllocatorT>::PutSmi(Smi* smi) {
285 sink_.Put(kOnePointerRawData, "Smi");
286 byte* bytes = reinterpret_cast<byte*>(&smi);
287 for (int i = 0; i < kPointerSize; i++) sink_.Put(bytes[i], "Byte");
288 }
289
290 template <class AllocatorT>
PutBackReference(HeapObject * object,SerializerReference reference)291 void Serializer<AllocatorT>::PutBackReference(HeapObject* object,
292 SerializerReference reference) {
293 DCHECK(allocator()->BackReferenceIsAlreadyAllocated(reference));
294 sink_.PutInt(reference.back_reference(), "BackRefValue");
295 hot_objects_.Add(object);
296 }
297
298 template <class AllocatorT>
PutAttachedReference(SerializerReference reference,HowToCode how_to_code,WhereToPoint where_to_point)299 void Serializer<AllocatorT>::PutAttachedReference(SerializerReference reference,
300 HowToCode how_to_code,
301 WhereToPoint where_to_point) {
302 DCHECK(reference.is_attached_reference());
303 DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
304 (how_to_code == kFromCode && where_to_point == kStartOfObject) ||
305 (how_to_code == kFromCode && where_to_point == kInnerPointer));
306 sink_.Put(kAttachedReference + how_to_code + where_to_point, "AttachedRef");
307 sink_.PutInt(reference.attached_reference_index(), "AttachedRefIndex");
308 }
309
310 template <class AllocatorT>
PutAlignmentPrefix(HeapObject * object)311 int Serializer<AllocatorT>::PutAlignmentPrefix(HeapObject* object) {
312 AllocationAlignment alignment = HeapObject::RequiredAlignment(object->map());
313 if (alignment != kWordAligned) {
314 DCHECK(1 <= alignment && alignment <= 3);
315 byte prefix = (kAlignmentPrefix - 1) + alignment;
316 sink_.Put(prefix, "Alignment");
317 return Heap::GetMaximumFillToAlign(alignment);
318 }
319 return 0;
320 }
321
322 template <class AllocatorT>
PutNextChunk(int space)323 void Serializer<AllocatorT>::PutNextChunk(int space) {
324 sink_.Put(kNextChunk, "NextChunk");
325 sink_.Put(space, "NextChunkSpace");
326 }
327
328 template <class AllocatorT>
Pad()329 void Serializer<AllocatorT>::Pad() {
330 // The non-branching GetInt will read up to 3 bytes too far, so we need
331 // to pad the snapshot to make sure we don't read over the end.
332 for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) {
333 sink_.Put(kNop, "Padding");
334 }
335 // Pad up to pointer size for checksum.
336 while (!IsAligned(sink_.Position(), kPointerAlignment)) {
337 sink_.Put(kNop, "Padding");
338 }
339 }
340
341 template <class AllocatorT>
InitializeCodeAddressMap()342 void Serializer<AllocatorT>::InitializeCodeAddressMap() {
343 isolate_->InitializeLoggingAndCounters();
344 code_address_map_ = new CodeAddressMap(isolate_);
345 }
346
347 template <class AllocatorT>
CopyCode(Code * code)348 Code* Serializer<AllocatorT>::CopyCode(Code* code) {
349 code_buffer_.clear(); // Clear buffer without deleting backing store.
350 int size = code->CodeSize();
351 code_buffer_.insert(code_buffer_.end(),
352 reinterpret_cast<byte*>(code->address()),
353 reinterpret_cast<byte*>(code->address() + size));
354 return Code::cast(HeapObject::FromAddress(
355 reinterpret_cast<Address>(&code_buffer_.front())));
356 }
357
358 template <class AllocatorT>
SerializePrologue(AllocationSpace space,int size,Map * map)359 void Serializer<AllocatorT>::ObjectSerializer::SerializePrologue(
360 AllocationSpace space, int size, Map* map) {
361 if (serializer_->code_address_map_) {
362 const char* code_name =
363 serializer_->code_address_map_->Lookup(object_->address());
364 LOG(serializer_->isolate_,
365 CodeNameEvent(object_->address(), sink_->Position(), code_name));
366 }
367
368 SerializerReference back_reference;
369 if (space == LO_SPACE) {
370 sink_->Put(kNewObject + reference_representation_ + space,
371 "NewLargeObject");
372 sink_->PutInt(size >> kObjectAlignmentBits, "ObjectSizeInWords");
373 if (object_->IsCode()) {
374 sink_->Put(EXECUTABLE, "executable large object");
375 } else {
376 sink_->Put(NOT_EXECUTABLE, "not executable large object");
377 }
378 back_reference = serializer_->allocator()->AllocateLargeObject(size);
379 } else if (space == MAP_SPACE) {
380 DCHECK_EQ(Map::kSize, size);
381 back_reference = serializer_->allocator()->AllocateMap();
382 sink_->Put(kNewObject + reference_representation_ + space, "NewMap");
383 // This is redundant, but we include it anyways.
384 sink_->PutInt(size >> kObjectAlignmentBits, "ObjectSizeInWords");
385 } else {
386 int fill = serializer_->PutAlignmentPrefix(object_);
387 back_reference = serializer_->allocator()->Allocate(space, size + fill);
388 sink_->Put(kNewObject + reference_representation_ + space, "NewObject");
389 sink_->PutInt(size >> kObjectAlignmentBits, "ObjectSizeInWords");
390 }
391
392 #ifdef OBJECT_PRINT
393 if (FLAG_serialization_statistics) {
394 serializer_->CountInstanceType(map, size, space);
395 }
396 #endif // OBJECT_PRINT
397
398 // Mark this object as already serialized.
399 serializer_->reference_map()->Add(object_, back_reference);
400
401 // Serialize the map (first word of the object).
402 serializer_->SerializeObject(map, kPlain, kStartOfObject, 0);
403 }
404
405 template <class AllocatorT>
SerializeBackingStore(void * backing_store,int32_t byte_length)406 int32_t Serializer<AllocatorT>::ObjectSerializer::SerializeBackingStore(
407 void* backing_store, int32_t byte_length) {
408 SerializerReference reference =
409 serializer_->reference_map()->Lookup(backing_store);
410
411 // Serialize the off-heap backing store.
412 if (!reference.is_valid()) {
413 sink_->Put(kOffHeapBackingStore, "Off-heap backing store");
414 sink_->PutInt(byte_length, "length");
415 sink_->PutRaw(static_cast<byte*>(backing_store), byte_length,
416 "BackingStore");
417 reference = serializer_->allocator()->AllocateOffHeapBackingStore();
418 // Mark this backing store as already serialized.
419 serializer_->reference_map()->Add(backing_store, reference);
420 }
421
422 return static_cast<int32_t>(reference.off_heap_backing_store_index());
423 }
424
425 template <class AllocatorT>
SerializeJSTypedArray()426 void Serializer<AllocatorT>::ObjectSerializer::SerializeJSTypedArray() {
427 JSTypedArray* typed_array = JSTypedArray::cast(object_);
428 FixedTypedArrayBase* elements =
429 FixedTypedArrayBase::cast(typed_array->elements());
430
431 if (!typed_array->WasNeutered()) {
432 if (!typed_array->is_on_heap()) {
433 // Explicitly serialize the backing store now.
434 JSArrayBuffer* buffer = JSArrayBuffer::cast(typed_array->buffer());
435 CHECK(buffer->byte_length()->IsSmi());
436 CHECK(typed_array->byte_offset()->IsSmi());
437 int32_t byte_length = NumberToInt32(buffer->byte_length());
438 int32_t byte_offset = NumberToInt32(typed_array->byte_offset());
439
440 // We need to calculate the backing store from the external pointer
441 // because the ArrayBuffer may already have been serialized.
442 void* backing_store = reinterpret_cast<void*>(
443 reinterpret_cast<intptr_t>(elements->external_pointer()) -
444 byte_offset);
445 int32_t ref = SerializeBackingStore(backing_store, byte_length);
446
447 // The external_pointer is the backing_store + typed_array->byte_offset.
448 // To properly share the buffer, we set the backing store ref here. On
449 // deserialization we re-add the byte_offset to external_pointer.
450 elements->set_external_pointer(Smi::FromInt(ref));
451 }
452 } else {
453 // When a JSArrayBuffer is neutered, the FixedTypedArray that points to the
454 // same backing store does not know anything about it. This fixup step finds
455 // neutered TypedArrays and clears the values in the FixedTypedArray so that
456 // we don't try to serialize the now invalid backing store.
457 elements->set_external_pointer(Smi::kZero);
458 elements->set_length(0);
459 }
460 SerializeObject();
461 }
462
463 template <class AllocatorT>
SerializeJSArrayBuffer()464 void Serializer<AllocatorT>::ObjectSerializer::SerializeJSArrayBuffer() {
465 JSArrayBuffer* buffer = JSArrayBuffer::cast(object_);
466 void* backing_store = buffer->backing_store();
467 // We cannot store byte_length larger than Smi range in the snapshot.
468 // Attempt to make sure that NumberToInt32 produces something sensible.
469 CHECK(buffer->byte_length()->IsSmi());
470 int32_t byte_length = NumberToInt32(buffer->byte_length());
471
472 // The embedder-allocated backing store only exists for the off-heap case.
473 if (backing_store != nullptr) {
474 int32_t ref = SerializeBackingStore(backing_store, byte_length);
475 buffer->set_backing_store(Smi::FromInt(ref));
476 }
477 SerializeObject();
478 buffer->set_backing_store(backing_store);
479 }
480
481 template <class AllocatorT>
SerializeExternalString()482 void Serializer<AllocatorT>::ObjectSerializer::SerializeExternalString() {
483 Heap* heap = serializer_->isolate()->heap();
484 // For external strings with known resources, we replace the resource field
485 // with the encoded external reference, which we restore upon deserialize.
486 // for native native source code strings, we replace the resource field
487 // with the native source id.
488 // For the rest we serialize them to look like ordinary sequential strings.
489 if (object_->map() != heap->native_source_string_map()) {
490 ExternalString* string = ExternalString::cast(object_);
491 Address resource = string->resource_as_address();
492 ExternalReferenceEncoder::Value reference;
493 if (serializer_->external_reference_encoder_.TryEncode(resource).To(
494 &reference)) {
495 DCHECK(reference.is_from_api());
496 string->set_uint32_as_resource(reference.index());
497 SerializeObject();
498 string->set_address_as_resource(resource);
499 } else {
500 SerializeExternalStringAsSequentialString();
501 }
502 } else {
503 ExternalOneByteString* string = ExternalOneByteString::cast(object_);
504 DCHECK(string->is_short());
505 const NativesExternalStringResource* resource =
506 reinterpret_cast<const NativesExternalStringResource*>(
507 string->resource());
508 // Replace the resource field with the type and index of the native source.
509 string->set_resource(resource->EncodeForSerialization());
510 SerializeObject();
511 // Restore the resource field.
512 string->set_resource(resource);
513 }
514 }
515
516 template <class AllocatorT>
517 void Serializer<
SerializeExternalStringAsSequentialString()518 AllocatorT>::ObjectSerializer::SerializeExternalStringAsSequentialString() {
519 // Instead of serializing this as an external string, we serialize
520 // an imaginary sequential string with the same content.
521 Isolate* isolate = serializer_->isolate();
522 DCHECK(object_->IsExternalString());
523 DCHECK(object_->map() != isolate->heap()->native_source_string_map());
524 ExternalString* string = ExternalString::cast(object_);
525 int length = string->length();
526 Map* map;
527 int content_size;
528 int allocation_size;
529 const byte* resource;
530 // Find the map and size for the imaginary sequential string.
531 bool internalized = object_->IsInternalizedString();
532 if (object_->IsExternalOneByteString()) {
533 map = internalized ? isolate->heap()->one_byte_internalized_string_map()
534 : isolate->heap()->one_byte_string_map();
535 allocation_size = SeqOneByteString::SizeFor(length);
536 content_size = length * kCharSize;
537 resource = reinterpret_cast<const byte*>(
538 ExternalOneByteString::cast(string)->resource()->data());
539 } else {
540 map = internalized ? isolate->heap()->internalized_string_map()
541 : isolate->heap()->string_map();
542 allocation_size = SeqTwoByteString::SizeFor(length);
543 content_size = length * kShortSize;
544 resource = reinterpret_cast<const byte*>(
545 ExternalTwoByteString::cast(string)->resource()->data());
546 }
547
548 AllocationSpace space =
549 (allocation_size > kMaxRegularHeapObjectSize) ? LO_SPACE : OLD_SPACE;
550 SerializePrologue(space, allocation_size, map);
551
552 // Output the rest of the imaginary string.
553 int bytes_to_output = allocation_size - HeapObject::kHeaderSize;
554
555 // Output raw data header. Do not bother with common raw length cases here.
556 sink_->Put(kVariableRawData, "RawDataForString");
557 sink_->PutInt(bytes_to_output, "length");
558
559 // Serialize string header (except for map).
560 uint8_t* string_start = reinterpret_cast<uint8_t*>(string->address());
561 for (int i = HeapObject::kHeaderSize; i < SeqString::kHeaderSize; i++) {
562 sink_->PutSection(string_start[i], "StringHeader");
563 }
564
565 // Serialize string content.
566 sink_->PutRaw(resource, content_size, "StringContent");
567
568 // Since the allocation size is rounded up to object alignment, there
569 // maybe left-over bytes that need to be padded.
570 int padding_size = allocation_size - SeqString::kHeaderSize - content_size;
571 DCHECK(0 <= padding_size && padding_size < kObjectAlignment);
572 for (int i = 0; i < padding_size; i++) sink_->PutSection(0, "StringPadding");
573 }
574
575 // Clear and later restore the next link in the weak cell or allocation site.
576 // TODO(all): replace this with proper iteration of weak slots in serializer.
577 class UnlinkWeakNextScope {
578 public:
UnlinkWeakNextScope(HeapObject * object)579 explicit UnlinkWeakNextScope(HeapObject* object) : object_(nullptr) {
580 if (object->IsAllocationSite()) {
581 object_ = object;
582 next_ = AllocationSite::cast(object)->weak_next();
583 AllocationSite::cast(object)->set_weak_next(
584 object->GetHeap()->undefined_value());
585 }
586 }
587
~UnlinkWeakNextScope()588 ~UnlinkWeakNextScope() {
589 if (object_ != nullptr) {
590 AllocationSite::cast(object_)->set_weak_next(next_,
591 UPDATE_WEAK_WRITE_BARRIER);
592 }
593 }
594
595 private:
596 HeapObject* object_;
597 Object* next_;
598 DisallowHeapAllocation no_gc_;
599 };
600
601 template <class AllocatorT>
Serialize()602 void Serializer<AllocatorT>::ObjectSerializer::Serialize() {
603 if (FLAG_trace_serializer) {
604 PrintF(" Encoding heap object: ");
605 object_->ShortPrint();
606 PrintF("\n");
607 }
608
609 if (object_->IsExternalString()) {
610 SerializeExternalString();
611 return;
612 } else if (!serializer_->isolate()->heap()->InReadOnlySpace(object_)) {
613 // Only clear padding for strings outside RO_SPACE. RO_SPACE should have
614 // been cleared elsewhere.
615 if (object_->IsSeqOneByteString()) {
616 // Clear padding bytes at the end. Done here to avoid having to do this
617 // at allocation sites in generated code.
618 SeqOneByteString::cast(object_)->clear_padding();
619 } else if (object_->IsSeqTwoByteString()) {
620 SeqTwoByteString::cast(object_)->clear_padding();
621 }
622 }
623 if (object_->IsJSTypedArray()) {
624 SerializeJSTypedArray();
625 return;
626 }
627 if (object_->IsJSArrayBuffer()) {
628 SerializeJSArrayBuffer();
629 return;
630 }
631
632 // We don't expect fillers.
633 DCHECK(!object_->IsFiller());
634
635 if (object_->IsScript()) {
636 // Clear cached line ends.
637 Object* undefined = serializer_->isolate()->heap()->undefined_value();
638 Script::cast(object_)->set_line_ends(undefined);
639 }
640
641 SerializeObject();
642 }
643
644 template <class AllocatorT>
SerializeObject()645 void Serializer<AllocatorT>::ObjectSerializer::SerializeObject() {
646 int size = object_->Size();
647 Map* map = object_->map();
648 AllocationSpace space =
649 MemoryChunk::FromAddress(object_->address())->owner()->identity();
650 SerializePrologue(space, size, map);
651
652 // Serialize the rest of the object.
653 CHECK_EQ(0, bytes_processed_so_far_);
654 bytes_processed_so_far_ = kPointerSize;
655
656 RecursionScope recursion(serializer_);
657 // Objects that are immediately post processed during deserialization
658 // cannot be deferred, since post processing requires the object content.
659 if ((recursion.ExceedsMaximum() && CanBeDeferred(object_)) ||
660 serializer_->MustBeDeferred(object_)) {
661 serializer_->QueueDeferredObject(object_);
662 sink_->Put(kDeferred, "Deferring object content");
663 return;
664 }
665
666 SerializeContent(map, size);
667 }
668
669 template <class AllocatorT>
SerializeDeferred()670 void Serializer<AllocatorT>::ObjectSerializer::SerializeDeferred() {
671 if (FLAG_trace_serializer) {
672 PrintF(" Encoding deferred heap object: ");
673 object_->ShortPrint();
674 PrintF("\n");
675 }
676
677 int size = object_->Size();
678 Map* map = object_->map();
679 SerializerReference back_reference =
680 serializer_->reference_map()->Lookup(object_);
681 DCHECK(back_reference.is_back_reference());
682
683 // Serialize the rest of the object.
684 CHECK_EQ(0, bytes_processed_so_far_);
685 bytes_processed_so_far_ = kPointerSize;
686
687 serializer_->PutAlignmentPrefix(object_);
688 sink_->Put(kNewObject + back_reference.space(), "deferred object");
689 serializer_->PutBackReference(object_, back_reference);
690 sink_->PutInt(size >> kPointerSizeLog2, "deferred object size");
691
692 SerializeContent(map, size);
693 }
694
695 template <class AllocatorT>
SerializeContent(Map * map,int size)696 void Serializer<AllocatorT>::ObjectSerializer::SerializeContent(Map* map,
697 int size) {
698 UnlinkWeakNextScope unlink_weak_next(object_);
699 if (object_->IsCode()) {
700 // For code objects, output raw bytes first.
701 OutputCode(size);
702 // Then iterate references via reloc info.
703 object_->IterateBody(map, size, this);
704 // Finally skip to the end.
705 serializer_->FlushSkip(SkipTo(object_->address() + size));
706 } else {
707 // For other objects, iterate references first.
708 object_->IterateBody(map, size, this);
709 // Then output data payload, if any.
710 OutputRawData(object_->address() + size);
711 }
712 }
713
714 template <class AllocatorT>
VisitPointers(HeapObject * host,Object ** start,Object ** end)715 void Serializer<AllocatorT>::ObjectSerializer::VisitPointers(HeapObject* host,
716 Object** start,
717 Object** end) {
718 VisitPointers(host, reinterpret_cast<MaybeObject**>(start),
719 reinterpret_cast<MaybeObject**>(end));
720 }
721
722 template <class AllocatorT>
VisitPointers(HeapObject * host,MaybeObject ** start,MaybeObject ** end)723 void Serializer<AllocatorT>::ObjectSerializer::VisitPointers(
724 HeapObject* host, MaybeObject** start, MaybeObject** end) {
725 MaybeObject** current = start;
726 while (current < end) {
727 while (current < end &&
728 ((*current)->IsSmi() || (*current)->IsClearedWeakHeapObject())) {
729 current++;
730 }
731 if (current < end) {
732 OutputRawData(reinterpret_cast<Address>(current));
733 }
734 HeapObject* current_contents;
735 HeapObjectReferenceType reference_type;
736 while (current < end && (*current)->ToStrongOrWeakHeapObject(
737 ¤t_contents, &reference_type)) {
738 int root_index = serializer_->root_index_map()->Lookup(current_contents);
739 // Repeats are not subject to the write barrier so we can only use
740 // immortal immovable root members. They are never in new space.
741 if (current != start && root_index != RootIndexMap::kInvalidRootIndex &&
742 Heap::RootIsImmortalImmovable(root_index) &&
743 *current == current[-1]) {
744 DCHECK_EQ(reference_type, HeapObjectReferenceType::STRONG);
745 DCHECK(!serializer_->isolate()->heap()->InNewSpace(current_contents));
746 int repeat_count = 1;
747 while (¤t[repeat_count] < end - 1 &&
748 current[repeat_count] == *current) {
749 repeat_count++;
750 }
751 current += repeat_count;
752 bytes_processed_so_far_ += repeat_count * kPointerSize;
753 if (repeat_count > kNumberOfFixedRepeat) {
754 sink_->Put(kVariableRepeat, "VariableRepeat");
755 sink_->PutInt(repeat_count, "repeat count");
756 } else {
757 sink_->Put(kFixedRepeatStart + repeat_count, "FixedRepeat");
758 }
759 } else {
760 if (reference_type == HeapObjectReferenceType::WEAK) {
761 sink_->Put(kWeakPrefix, "WeakReference");
762 }
763 serializer_->SerializeObject(current_contents, kPlain, kStartOfObject,
764 0);
765 bytes_processed_so_far_ += kPointerSize;
766 current++;
767 }
768 }
769 }
770 }
771
772 template <class AllocatorT>
VisitEmbeddedPointer(Code * host,RelocInfo * rinfo)773 void Serializer<AllocatorT>::ObjectSerializer::VisitEmbeddedPointer(
774 Code* host, RelocInfo* rinfo) {
775 int skip = SkipTo(rinfo->target_address_address());
776 HowToCode how_to_code = rinfo->IsCodedSpecially() ? kFromCode : kPlain;
777 Object* object = rinfo->target_object();
778 serializer_->SerializeObject(HeapObject::cast(object), how_to_code,
779 kStartOfObject, skip);
780 bytes_processed_so_far_ += rinfo->target_address_size();
781 }
782
783 template <class AllocatorT>
VisitExternalReference(Foreign * host,Address * p)784 void Serializer<AllocatorT>::ObjectSerializer::VisitExternalReference(
785 Foreign* host, Address* p) {
786 int skip = SkipTo(reinterpret_cast<Address>(p));
787 Address target = *p;
788 auto encoded_reference = serializer_->EncodeExternalReference(target);
789 if (encoded_reference.is_from_api()) {
790 sink_->Put(kApiReference, "ApiRef");
791 } else {
792 sink_->Put(kExternalReference + kPlain + kStartOfObject, "ExternalRef");
793 }
794 sink_->PutInt(skip, "SkipB4ExternalRef");
795 sink_->PutInt(encoded_reference.index(), "reference index");
796 bytes_processed_so_far_ += kPointerSize;
797 }
798
799 template <class AllocatorT>
VisitExternalReference(Code * host,RelocInfo * rinfo)800 void Serializer<AllocatorT>::ObjectSerializer::VisitExternalReference(
801 Code* host, RelocInfo* rinfo) {
802 int skip = SkipTo(rinfo->target_address_address());
803 Address target = rinfo->target_external_reference();
804 auto encoded_reference = serializer_->EncodeExternalReference(target);
805 if (encoded_reference.is_from_api()) {
806 DCHECK(!rinfo->IsCodedSpecially());
807 sink_->Put(kApiReference, "ApiRef");
808 } else {
809 HowToCode how_to_code = rinfo->IsCodedSpecially() ? kFromCode : kPlain;
810 sink_->Put(kExternalReference + how_to_code + kStartOfObject,
811 "ExternalRef");
812 }
813 sink_->PutInt(skip, "SkipB4ExternalRef");
814 DCHECK_NE(target, kNullAddress); // Code does not reference null.
815 sink_->PutInt(encoded_reference.index(), "reference index");
816 bytes_processed_so_far_ += rinfo->target_address_size();
817 }
818
819 template <class AllocatorT>
VisitInternalReference(Code * host,RelocInfo * rinfo)820 void Serializer<AllocatorT>::ObjectSerializer::VisitInternalReference(
821 Code* host, RelocInfo* rinfo) {
822 // We do not use skip from last patched pc to find the pc to patch, since
823 // target_address_address may not return addresses in ascending order when
824 // used for internal references. External references may be stored at the
825 // end of the code in the constant pool, whereas internal references are
826 // inline. That would cause the skip to be negative. Instead, we store the
827 // offset from code entry.
828 Address entry = Code::cast(object_)->entry();
829 DCHECK_GE(rinfo->target_internal_reference_address(), entry);
830 uintptr_t pc_offset = rinfo->target_internal_reference_address() - entry;
831 DCHECK_LE(pc_offset, Code::cast(object_)->raw_instruction_size());
832 DCHECK_GE(rinfo->target_internal_reference(), entry);
833 uintptr_t target_offset = rinfo->target_internal_reference() - entry;
834 DCHECK_LE(target_offset, Code::cast(object_)->raw_instruction_size());
835 sink_->Put(rinfo->rmode() == RelocInfo::INTERNAL_REFERENCE
836 ? kInternalReference
837 : kInternalReferenceEncoded,
838 "InternalRef");
839 sink_->PutInt(pc_offset, "internal ref address");
840 sink_->PutInt(target_offset, "internal ref value");
841 }
842
843 template <class AllocatorT>
VisitRuntimeEntry(Code * host,RelocInfo * rinfo)844 void Serializer<AllocatorT>::ObjectSerializer::VisitRuntimeEntry(
845 Code* host, RelocInfo* rinfo) {
846 int skip = SkipTo(rinfo->target_address_address());
847 HowToCode how_to_code = rinfo->IsCodedSpecially() ? kFromCode : kPlain;
848 Address target = rinfo->target_address();
849 auto encoded_reference = serializer_->EncodeExternalReference(target);
850 DCHECK(!encoded_reference.is_from_api());
851 sink_->Put(kExternalReference + how_to_code + kStartOfObject, "ExternalRef");
852 sink_->PutInt(skip, "SkipB4ExternalRef");
853 sink_->PutInt(encoded_reference.index(), "reference index");
854 bytes_processed_so_far_ += rinfo->target_address_size();
855 }
856
857 template <class AllocatorT>
VisitOffHeapTarget(Code * host,RelocInfo * rinfo)858 void Serializer<AllocatorT>::ObjectSerializer::VisitOffHeapTarget(
859 Code* host, RelocInfo* rinfo) {
860 #ifdef V8_EMBEDDED_BUILTINS
861 {
862 STATIC_ASSERT(EmbeddedData::kTableSize == Builtins::builtin_count);
863 CHECK(Builtins::IsEmbeddedBuiltin(host));
864 Address addr = rinfo->target_off_heap_target();
865 CHECK_NE(kNullAddress, addr);
866 CHECK_NOT_NULL(
867 InstructionStream::TryLookupCode(serializer_->isolate(), addr));
868 }
869
870 int skip = SkipTo(rinfo->target_address_address());
871 sink_->Put(kOffHeapTarget, "OffHeapTarget");
872 sink_->PutInt(skip, "SkipB4OffHeapTarget");
873 sink_->PutInt(host->builtin_index(), "builtin index");
874 bytes_processed_so_far_ += rinfo->target_address_size();
875 #else
876 UNREACHABLE();
877 #endif
878 }
879
880 namespace {
881 class CompareRelocInfo {
882 public:
operator ()(RelocInfo x,RelocInfo y)883 bool operator()(RelocInfo x, RelocInfo y) {
884 // Everything that does not use target_address_address will compare equal.
885 Address x_num = 0;
886 Address y_num = 0;
887 if (HasTargetAddressAddress(x.rmode())) {
888 x_num = x.target_address_address();
889 }
890 if (HasTargetAddressAddress(y.rmode())) {
891 y_num = y.target_address_address();
892 }
893 return x_num > y_num;
894 }
895
896 private:
HasTargetAddressAddress(RelocInfo::Mode mode)897 static bool HasTargetAddressAddress(RelocInfo::Mode mode) {
898 return RelocInfo::IsEmbeddedObject(mode) || RelocInfo::IsCodeTarget(mode) ||
899 RelocInfo::IsExternalReference(mode) ||
900 RelocInfo::IsRuntimeEntry(mode);
901 }
902 };
903 } // namespace
904
905 template <class AllocatorT>
VisitRelocInfo(RelocIterator * it)906 void Serializer<AllocatorT>::ObjectSerializer::VisitRelocInfo(
907 RelocIterator* it) {
908 std::priority_queue<RelocInfo, std::vector<RelocInfo>, CompareRelocInfo>
909 reloc_queue;
910 for (; !it->done(); it->next()) {
911 reloc_queue.push(*it->rinfo());
912 }
913 while (!reloc_queue.empty()) {
914 RelocInfo rinfo = reloc_queue.top();
915 reloc_queue.pop();
916 rinfo.Visit(this);
917 }
918 }
919
920 template <class AllocatorT>
VisitCodeTarget(Code * host,RelocInfo * rinfo)921 void Serializer<AllocatorT>::ObjectSerializer::VisitCodeTarget(
922 Code* host, RelocInfo* rinfo) {
923 int skip = SkipTo(rinfo->target_address_address());
924 Code* object = Code::GetCodeFromTargetAddress(rinfo->target_address());
925 serializer_->SerializeObject(object, kFromCode, kInnerPointer, skip);
926 bytes_processed_so_far_ += rinfo->target_address_size();
927 }
928
929 template <class AllocatorT>
OutputRawData(Address up_to)930 void Serializer<AllocatorT>::ObjectSerializer::OutputRawData(Address up_to) {
931 Address object_start = object_->address();
932 int base = bytes_processed_so_far_;
933 int up_to_offset = static_cast<int>(up_to - object_start);
934 int to_skip = up_to_offset - bytes_processed_so_far_;
935 int bytes_to_output = to_skip;
936 bytes_processed_so_far_ += to_skip;
937 DCHECK_GE(to_skip, 0);
938 if (bytes_to_output != 0) {
939 DCHECK(to_skip == bytes_to_output);
940 if (IsAligned(bytes_to_output, kPointerAlignment) &&
941 bytes_to_output <= kNumberOfFixedRawData * kPointerSize) {
942 int size_in_words = bytes_to_output >> kPointerSizeLog2;
943 sink_->PutSection(kFixedRawDataStart + size_in_words, "FixedRawData");
944 } else {
945 sink_->Put(kVariableRawData, "VariableRawData");
946 sink_->PutInt(bytes_to_output, "length");
947 }
948 #ifdef MEMORY_SANITIZER
949 // Check that we do not serialize uninitialized memory.
950 __msan_check_mem_is_initialized(
951 reinterpret_cast<void*>(object_start + base), bytes_to_output);
952 #endif // MEMORY_SANITIZER
953 if (object_->IsBytecodeArray()) {
954 // The code age byte can be changed concurrently by GC.
955 const int bytes_to_age_byte = BytecodeArray::kBytecodeAgeOffset - base;
956 if (0 <= bytes_to_age_byte && bytes_to_age_byte < bytes_to_output) {
957 sink_->PutRaw(reinterpret_cast<byte*>(object_start + base),
958 bytes_to_age_byte, "Bytes");
959 byte bytecode_age = BytecodeArray::kNoAgeBytecodeAge;
960 sink_->PutRaw(&bytecode_age, 1, "Bytes");
961 const int bytes_written = bytes_to_age_byte + 1;
962 sink_->PutRaw(
963 reinterpret_cast<byte*>(object_start + base + bytes_written),
964 bytes_to_output - bytes_written, "Bytes");
965 } else {
966 sink_->PutRaw(reinterpret_cast<byte*>(object_start + base),
967 bytes_to_output, "Bytes");
968 }
969 } else {
970 sink_->PutRaw(reinterpret_cast<byte*>(object_start + base),
971 bytes_to_output, "Bytes");
972 }
973 }
974 }
975
976 template <class AllocatorT>
SkipTo(Address to)977 int Serializer<AllocatorT>::ObjectSerializer::SkipTo(Address to) {
978 Address object_start = object_->address();
979 int up_to_offset = static_cast<int>(to - object_start);
980 int to_skip = up_to_offset - bytes_processed_so_far_;
981 bytes_processed_so_far_ += to_skip;
982 // This assert will fail if the reloc info gives us the target_address_address
983 // locations in a non-ascending order. We make sure this doesn't happen by
984 // sorting the relocation info.
985 DCHECK_GE(to_skip, 0);
986 return to_skip;
987 }
988
989 template <class AllocatorT>
OutputCode(int size)990 void Serializer<AllocatorT>::ObjectSerializer::OutputCode(int size) {
991 DCHECK_EQ(kPointerSize, bytes_processed_so_far_);
992 Code* code = Code::cast(object_);
993 // To make snapshots reproducible, we make a copy of the code object
994 // and wipe all pointers in the copy, which we then serialize.
995 code = serializer_->CopyCode(code);
996 int mode_mask = RelocInfo::kCodeTargetMask |
997 RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
998 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
999 RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) |
1000 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
1001 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED);
1002 for (RelocIterator it(code, mode_mask); !it.done(); it.next()) {
1003 RelocInfo* rinfo = it.rinfo();
1004 rinfo->WipeOut();
1005 }
1006 // We need to wipe out the header fields *after* wiping out the
1007 // relocations, because some of these fields are needed for the latter.
1008 code->WipeOutHeader();
1009
1010 Address start = code->address() + Code::kDataStart;
1011 int bytes_to_output = size - Code::kDataStart;
1012
1013 sink_->Put(kVariableRawCode, "VariableRawCode");
1014 sink_->PutInt(bytes_to_output, "length");
1015
1016 #ifdef MEMORY_SANITIZER
1017 // Check that we do not serialize uninitialized memory.
1018 __msan_check_mem_is_initialized(reinterpret_cast<void*>(start),
1019 bytes_to_output);
1020 #endif // MEMORY_SANITIZER
1021 sink_->PutRaw(reinterpret_cast<byte*>(start), bytes_to_output, "Code");
1022 }
1023
1024 // Explicit instantiation.
1025 template class Serializer<BuiltinSerializerAllocator>;
1026 template class Serializer<DefaultSerializerAllocator>;
1027
1028 } // namespace internal
1029 } // namespace v8
1030