Line data Source code
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/partial-serializer.h"
6 : #include "src/snapshot/startup-serializer.h"
7 :
8 : #include "src/api-inl.h"
9 : #include "src/math-random.h"
10 : #include "src/microtask-queue.h"
11 : #include "src/objects-inl.h"
12 : #include "src/objects/slots.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 241 : PartialSerializer::PartialSerializer(
18 : Isolate* isolate, StartupSerializer* startup_serializer,
19 : v8::SerializeEmbedderFieldsCallback callback)
20 : : Serializer(isolate),
21 : startup_serializer_(startup_serializer),
22 : serialize_embedder_fields_(callback),
23 241 : can_be_rehashed_(true) {
24 241 : InitializeCodeAddressMap();
25 482 : allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
26 241 : }
27 :
28 482 : PartialSerializer::~PartialSerializer() {
29 241 : OutputStatistics("PartialSerializer");
30 241 : }
31 :
32 241 : void PartialSerializer::Serialize(Context* o, bool include_global_proxy) {
33 241 : context_ = *o;
34 : DCHECK(context_->IsNativeContext());
35 : reference_map()->AddAttachedReference(
36 723 : reinterpret_cast<void*>(context_->global_proxy()->ptr()));
37 : // The bootstrap snapshot has a code-stub context. When serializing the
38 : // partial snapshot, it is chained into the weak context list on the isolate
39 : // and it's next context pointer may point to the code-stub context. Clear
40 : // it before serializing, it will get re-added to the context list
41 : // explicitly when it's loaded.
42 : context_->set(Context::NEXT_CONTEXT_LINK,
43 : ReadOnlyRoots(isolate()).undefined_value());
44 : DCHECK(!context_->global_object()->IsUndefined());
45 : // Reset math random cache to get fresh random numbers.
46 241 : MathRandom::ResetContext(context_);
47 :
48 : #ifdef DEBUG
49 : MicrotaskQueue* microtask_queue =
50 : context_->native_context()->microtask_queue();
51 : DCHECK_EQ(0, microtask_queue->size());
52 : DCHECK(!microtask_queue->HasMicrotasksSuppressions());
53 : DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
54 : DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
55 : #endif
56 : context_->native_context()->set_microtask_queue(nullptr);
57 :
58 482 : VisitRootPointer(Root::kPartialSnapshotCache, nullptr, FullObjectSlot(o));
59 241 : SerializeDeferredObjects();
60 :
61 : // Add section for embedder-serialized embedder fields.
62 241 : if (!embedder_fields_sink_.data()->empty()) {
63 : sink_.Put(kEmbedderFieldsData, "embedder fields data");
64 5 : sink_.Append(embedder_fields_sink_);
65 : sink_.Put(kSynchronize, "Finished with embedder fields data");
66 : }
67 :
68 241 : Pad();
69 241 : }
70 :
71 3052906 : void PartialSerializer::SerializeObject(HeapObject obj) {
72 : DCHECK(!ObjectIsBytecodeHandler(obj)); // Only referenced in dispatch table.
73 :
74 5562374 : if (SerializeHotObject(obj)) return;
75 :
76 2424624 : if (SerializeRoot(obj)) return;
77 :
78 1285644 : if (SerializeBackReference(obj)) return;
79 :
80 1050771 : if (startup_serializer_->SerializeUsingReadOnlyObjectCache(&sink_, obj)) {
81 : return;
82 : }
83 :
84 1046504 : if (ShouldBeInThePartialSnapshotCache(obj)) {
85 502891 : startup_serializer_->SerializeUsingPartialSnapshotCache(&sink_, obj);
86 502891 : return;
87 : }
88 :
89 : // Pointers from the partial snapshot to the objects in the startup snapshot
90 : // should go through the root array or through the partial snapshot cache.
91 : // If this is not the case you may have to add something to the root array.
92 : DCHECK(!startup_serializer_->ReferenceMapContains(obj));
93 : // All the internalized strings that the partial snapshot needs should be
94 : // either in the root table or in the partial snapshot cache.
95 : DCHECK(!obj->IsInternalizedString());
96 : // Function and object templates are not context specific.
97 : DCHECK(!obj->IsTemplateInfo());
98 : // We should not end up at another native context.
99 : DCHECK_IMPLIES(obj != context_, !obj->IsNativeContext());
100 :
101 : // Clear literal boilerplates and feedback.
102 543613 : if (obj->IsFeedbackVector()) FeedbackVector::cast(obj)->ClearSlots(isolate());
103 :
104 : // Clear InterruptBudget when serializing FeedbackCell.
105 543613 : if (obj->IsFeedbackCell()) {
106 : FeedbackCell::cast(obj)->set_interrupt_budget(
107 : FeedbackCell::GetInitialInterruptBudget());
108 : }
109 :
110 543613 : if (SerializeJSObjectWithEmbedderFields(obj)) {
111 : return;
112 : }
113 :
114 543438 : if (obj->IsJSFunction()) {
115 : // Unconditionally reset the JSFunction to its SFI's code, since we can't
116 : // serialize optimized code anyway.
117 195583 : JSFunction closure = JSFunction::cast(obj);
118 195583 : closure->ResetIfBytecodeFlushed();
119 338832 : if (closure->is_compiled()) closure->set_code(closure->shared()->GetCode());
120 : }
121 :
122 543438 : CheckRehashability(obj);
123 :
124 : // Object has not yet been serialized. Serialize it here.
125 : ObjectSerializer serializer(this, obj, &sink_);
126 543438 : serializer.Serialize();
127 : }
128 :
129 1046504 : bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject o) {
130 : // Scripts should be referred only through shared function infos. We can't
131 : // allow them to be part of the partial snapshot because they contain a
132 : // unique ID, and deserializing several partial snapshots containing script
133 : // would cause dupes.
134 : DCHECK(!o->IsScript());
135 2419950 : return o->IsName() || o->IsSharedFunctionInfo() || o->IsHeapNumber() ||
136 1631717 : o->IsCode() || o->IsScopeInfo() || o->IsAccessorInfo() ||
137 2133750 : o->IsTemplateInfo() || o->IsClassPositions() ||
138 543618 : o->map() == ReadOnlyRoots(startup_serializer_->isolate())
139 1046504 : .fixed_cow_array_map();
140 : }
141 :
142 : namespace {
143 : bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
144 : } // anonymous namespace
145 :
146 543613 : bool PartialSerializer::SerializeJSObjectWithEmbedderFields(Object obj) {
147 543613 : if (!obj->IsJSObject()) return false;
148 265504 : JSObject js_obj = JSObject::cast(obj);
149 265504 : int embedder_fields_count = js_obj->GetEmbedderFieldCount();
150 265504 : if (embedder_fields_count == 0) return false;
151 175 : CHECK_GT(embedder_fields_count, 0);
152 : DCHECK(!js_obj->NeedsRehashing());
153 :
154 : DisallowHeapAllocation no_gc;
155 350 : DisallowJavascriptExecution no_js(isolate());
156 : DisallowCompilation no_compile(isolate());
157 :
158 : HandleScope scope(isolate());
159 : Handle<JSObject> obj_handle(js_obj, isolate());
160 175 : v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(obj_handle);
161 :
162 : std::vector<EmbedderDataSlot::RawData> original_embedder_values;
163 : std::vector<StartupData> serialized_data;
164 :
165 : // 1) Iterate embedder fields. Hold onto the original value of the fields.
166 : // Ignore references to heap objects since these are to be handled by the
167 : // serializer. For aligned pointers, call the serialize callback. Hold
168 : // onto the result.
169 935 : for (int i = 0; i < embedder_fields_count; i++) {
170 : EmbedderDataSlot embedder_data_slot(js_obj, i);
171 380 : original_embedder_values.emplace_back(embedder_data_slot.load_raw(no_gc));
172 : Object object = embedder_data_slot.load_tagged();
173 380 : if (object->IsHeapObject()) {
174 : DCHECK(isolate()->heap()->Contains(HeapObject::cast(object)));
175 220 : serialized_data.push_back({nullptr, 0});
176 : } else {
177 : // If no serializer is provided and the field was empty, we serialize it
178 : // by default to nullptr.
179 270 : if (serialize_embedder_fields_.callback == nullptr &&
180 : object->ptr() == 0) {
181 40 : serialized_data.push_back({nullptr, 0});
182 : } else {
183 : DCHECK_NOT_NULL(serialize_embedder_fields_.callback);
184 250 : StartupData data = serialize_embedder_fields_.callback(
185 250 : api_obj, i, serialize_embedder_fields_.data);
186 250 : serialized_data.push_back(data);
187 : }
188 : }
189 : }
190 :
191 : // 2) Embedder fields for which the embedder callback produced non-zero
192 : // serialized data should be considered aligned pointers to objects owned
193 : // by the embedder. Clear these memory addresses to avoid non-determism
194 : // in the snapshot. This is done separately to step 1 to no not interleave
195 : // with embedder callbacks.
196 935 : for (int i = 0; i < embedder_fields_count; i++) {
197 760 : if (!DataIsEmpty(serialized_data[i])) {
198 : EmbedderDataSlot(js_obj, i).store_raw(kNullAddress, no_gc);
199 : }
200 : }
201 :
202 : // 3) Serialize the object. References from embedder fields to heap objects or
203 : // smis are serialized regularly.
204 525 : ObjectSerializer(this, js_obj, &sink_).Serialize();
205 :
206 : // 4) Obtain back reference for the serialized object.
207 : SerializerReference reference =
208 350 : reference_map()->LookupReference(reinterpret_cast<void*>(js_obj->ptr()));
209 : DCHECK(reference.is_back_reference());
210 :
211 : // 5) Write data returned by the embedder callbacks into a separate sink,
212 : // headed by the back reference. Restore the original embedder fields.
213 935 : for (int i = 0; i < embedder_fields_count; i++) {
214 760 : StartupData data = serialized_data[i];
215 380 : if (DataIsEmpty(data)) continue;
216 : // Restore original values from cleared fields.
217 15 : EmbedderDataSlot(js_obj, i).store_raw(original_embedder_values[i], no_gc);
218 : embedder_fields_sink_.Put(kNewObject + reference.space(),
219 : "embedder field holder");
220 15 : embedder_fields_sink_.PutInt(reference.chunk_index(), "BackRefChunkIndex");
221 15 : embedder_fields_sink_.PutInt(reference.chunk_offset(),
222 15 : "BackRefChunkOffset");
223 15 : embedder_fields_sink_.PutInt(i, "embedder field index");
224 15 : embedder_fields_sink_.PutInt(data.raw_size, "embedder fields data size");
225 : embedder_fields_sink_.PutRaw(reinterpret_cast<const byte*>(data.data),
226 15 : data.raw_size, "embedder fields data");
227 15 : delete[] data.data;
228 : }
229 :
230 : // 6) The content of the separate sink is appended eventually to the default
231 : // sink. The ensures that during deserialization, we call the deserializer
232 : // callback at the end, and can guarantee that the deserialized objects are
233 : // in a consistent state. See PartialSerializer::Serialize.
234 : return true;
235 : }
236 :
237 543438 : void PartialSerializer::CheckRehashability(HeapObject obj) {
238 543438 : if (!can_be_rehashed_) return;
239 538853 : if (!obj->NeedsRehashing()) return;
240 37327 : if (obj->CanBeRehashed()) return;
241 5 : can_be_rehashed_ = false;
242 : }
243 :
244 : } // namespace internal
245 121996 : } // namespace v8
|