1// Copyright 2021 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/shared-heap-serializer.h" 6 7#include "src/heap/heap-inl.h" 8#include "src/heap/read-only-heap.h" 9#include "src/objects/objects-inl.h" 10#include "src/snapshot/read-only-serializer.h" 11 12namespace v8 { 13namespace internal { 14 15// static 16bool SharedHeapSerializer::CanBeInSharedOldSpace(HeapObject obj) { 17 if (ReadOnlyHeap::Contains(obj)) return false; 18 if (obj.IsString()) { 19 return obj.IsInternalizedString() || 20 String::IsInPlaceInternalizable(String::cast(obj)); 21 } 22 return false; 23} 24 25// static 26bool SharedHeapSerializer::ShouldBeInSharedHeapObjectCache(HeapObject obj) { 27 // To keep the shared heap object cache lean, only include objects that should 28 // not be duplicated. Currently, that is only internalized strings. In-place 29 // internalizable strings will still be allocated in the shared heap by the 30 // deserializer, but do not need to be kept alive forever in the cache. 31 if (CanBeInSharedOldSpace(obj)) { 32 if (obj.IsInternalizedString()) return true; 33 } 34 return false; 35} 36 37SharedHeapSerializer::SharedHeapSerializer( 38 Isolate* isolate, Snapshot::SerializerFlags flags, 39 ReadOnlySerializer* read_only_serializer) 40 : RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot), 41 read_only_serializer_(read_only_serializer) 42#ifdef DEBUG 43 , 44 serialized_objects_(isolate->heap()) 45#endif 46{ 47 if (ShouldReconstructSharedHeapObjectCacheForTesting()) { 48 ReconstructSharedHeapObjectCacheForTesting(); 49 } 50} 51 52SharedHeapSerializer::~SharedHeapSerializer() { 53 OutputStatistics("SharedHeapSerializer"); 54} 55 56void SharedHeapSerializer::FinalizeSerialization() { 57 // This is called after serialization of the startup and context snapshots 58 // which entries are added to the shared heap object cache. Terminate the 59 // cache with an undefined. 60 Object undefined = ReadOnlyRoots(isolate()).undefined_value(); 61 VisitRootPointer(Root::kSharedHeapObjectCache, nullptr, 62 FullObjectSlot(&undefined)); 63 64 // When FLAG_shared_string_table is true, all internalized and 65 // internalizable-in-place strings are in the shared heap. 66 SerializeStringTable(isolate()->string_table()); 67 SerializeDeferredObjects(); 68 Pad(); 69 70#ifdef DEBUG 71 // Check that all serialized object are in shared heap and not RO. RO objects 72 // should be in the RO snapshot. 73 IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope( 74 &serialized_objects_); 75 for (auto it = it_scope.begin(); it != it_scope.end(); ++it) { 76 HeapObject obj = HeapObject::cast(it.key()); 77 CHECK(CanBeInSharedOldSpace(obj)); 78 CHECK(!ReadOnlyHeap::Contains(obj)); 79 } 80#endif 81} 82 83bool SharedHeapSerializer::SerializeUsingReadOnlyObjectCache( 84 SnapshotByteSink* sink, Handle<HeapObject> obj) { 85 return read_only_serializer_->SerializeUsingReadOnlyObjectCache(sink, obj); 86} 87 88bool SharedHeapSerializer::SerializeUsingSharedHeapObjectCache( 89 SnapshotByteSink* sink, Handle<HeapObject> obj) { 90 if (!ShouldBeInSharedHeapObjectCache(*obj)) return false; 91 int cache_index = SerializeInObjectCache(obj); 92 93 // When testing deserialization of a snapshot from a live Isolate where there 94 // is also a shared Isolate, the shared object cache needs to be extended 95 // because the live isolate may have had new internalized strings that were 96 // not present in the startup snapshot to be serialized. 97 if (ShouldReconstructSharedHeapObjectCacheForTesting()) { 98 std::vector<Object>* existing_cache = 99 isolate()->shared_isolate()->shared_heap_object_cache(); 100 const size_t existing_cache_size = existing_cache->size(); 101 // This is strictly < because the existing cache contains the terminating 102 // undefined value, which the reconstructed cache does not. 103 DCHECK_LT(base::checked_cast<size_t>(cache_index), existing_cache_size); 104 if (base::checked_cast<size_t>(cache_index) == existing_cache_size - 1) { 105 ReadOnlyRoots roots(isolate()); 106 DCHECK(existing_cache->back().IsUndefined(roots)); 107 existing_cache->back() = *obj; 108 existing_cache->push_back(roots.undefined_value()); 109 } 110 } 111 112 sink->Put(kSharedHeapObjectCache, "SharedHeapObjectCache"); 113 sink->PutInt(cache_index, "shared_heap_object_cache_index"); 114 return true; 115} 116 117void SharedHeapSerializer::SerializeStringTable(StringTable* string_table) { 118 // A StringTable is serialized as: 119 // 120 // N : int 121 // string 1 122 // string 2 123 // ... 124 // string N 125 // 126 // Notably, the hashmap structure, including empty and deleted elements, is 127 // not serialized. 128 129 sink_.PutInt(string_table->NumberOfElements(), 130 "String table number of elements"); 131 132 // Custom RootVisitor which walks the string table, but only serializes the 133 // string entries. This is an inline class to be able to access the non-public 134 // SerializeObject method. 135 class SharedHeapSerializerStringTableVisitor : public RootVisitor { 136 public: 137 explicit SharedHeapSerializerStringTableVisitor( 138 SharedHeapSerializer* serializer) 139 : serializer_(serializer) {} 140 141 void VisitRootPointers(Root root, const char* description, 142 FullObjectSlot start, FullObjectSlot end) override { 143 UNREACHABLE(); 144 } 145 146 void VisitRootPointers(Root root, const char* description, 147 OffHeapObjectSlot start, 148 OffHeapObjectSlot end) override { 149 DCHECK_EQ(root, Root::kStringTable); 150 Isolate* isolate = serializer_->isolate(); 151 for (OffHeapObjectSlot current = start; current < end; ++current) { 152 Object obj = current.load(isolate); 153 if (obj.IsHeapObject()) { 154 DCHECK(obj.IsInternalizedString()); 155 serializer_->SerializeObject(handle(HeapObject::cast(obj), isolate)); 156 } 157 } 158 } 159 160 private: 161 SharedHeapSerializer* serializer_; 162 }; 163 164 SharedHeapSerializerStringTableVisitor string_table_visitor(this); 165 isolate()->string_table()->IterateElements(&string_table_visitor); 166} 167 168void SharedHeapSerializer::SerializeObjectImpl(Handle<HeapObject> obj) { 169 // Objects in the shared heap cannot depend on per-Isolate roots but can 170 // depend on RO roots since sharing objects requires sharing the RO space. 171 DCHECK(CanBeInSharedOldSpace(*obj) || ReadOnlyHeap::Contains(*obj)); 172 { 173 DisallowGarbageCollection no_gc; 174 HeapObject raw = *obj; 175 if (SerializeHotObject(raw)) return; 176 if (IsRootAndHasBeenSerialized(raw) && SerializeRoot(raw)) return; 177 } 178 if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return; 179 { 180 DisallowGarbageCollection no_gc; 181 HeapObject raw = *obj; 182 if (SerializeBackReference(raw)) return; 183 CheckRehashability(raw); 184 185 DCHECK(!ReadOnlyHeap::Contains(raw)); 186 } 187 188 ObjectSerializer object_serializer(this, obj, &sink_); 189 object_serializer.Serialize(); 190 191#ifdef DEBUG 192 CHECK_NULL(serialized_objects_.Find(obj)); 193 // There's no "IdentitySet", so use an IdentityMap with a value that is 194 // later ignored. 195 serialized_objects_.Insert(obj, 0); 196#endif 197} 198 199bool SharedHeapSerializer::ShouldReconstructSharedHeapObjectCacheForTesting() 200 const { 201 // When the live Isolate being serialized is not a client Isolate, there's no 202 // need to reconstruct the shared heap object cache because it is not actually 203 // shared. 204 return reconstruct_read_only_and_shared_object_caches_for_testing() && 205 isolate()->shared_isolate() != nullptr; 206} 207 208void SharedHeapSerializer::ReconstructSharedHeapObjectCacheForTesting() { 209 std::vector<Object>* cache = 210 isolate()->shared_isolate()->shared_heap_object_cache(); 211 // Don't reconstruct the final element, which is always undefined and marks 212 // the end of the cache, since serializing the live Isolate may extend the 213 // shared object cache. 214 for (size_t i = 0, size = cache->size(); i < size - 1; i++) { 215 Handle<HeapObject> obj(HeapObject::cast(cache->at(i)), isolate()); 216 DCHECK(ShouldBeInSharedHeapObjectCache(*obj)); 217 int cache_index = SerializeInObjectCache(obj); 218 USE(cache_index); 219 DCHECK_EQ(cache_index, i); 220 } 221 DCHECK(cache->back().IsUndefined(isolate())); 222} 223 224} // namespace internal 225} // namespace v8 226