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