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 482 : 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 3052375 : void PartialSerializer::SerializeObject(HeapObject obj) {
72 : DCHECK(!ObjectIsBytecodeHandler(obj)); // Only referenced in dispatch table.
73 :
74 5561312 : if (SerializeHotObject(obj)) return;
75 :
76 2426495 : if (SerializeRoot(obj)) return;
77 :
78 1287410 : 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 543613 : if (SerializeJSObjectWithEmbedderFields(obj)) {
105 : return;
106 : }
107 :
108 543438 : if (obj->IsJSFunction()) {
109 : // Unconditionally reset the JSFunction to its SFI's code, since we can't
110 : // serialize optimized code anyway.
111 195583 : JSFunction closure = JSFunction::cast(obj);
112 195583 : closure->ResetIfBytecodeFlushed();
113 338832 : if (closure->is_compiled()) closure->set_code(closure->shared()->GetCode());
114 : }
115 :
116 543438 : CheckRehashability(obj);
117 :
118 : // Object has not yet been serialized. Serialize it here.
119 : ObjectSerializer serializer(this, obj, &sink_);
120 543438 : serializer.Serialize();
121 : }
122 :
123 1046504 : bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject o) {
124 : // Scripts should be referred only through shared function infos. We can't
125 : // allow them to be part of the partial snapshot because they contain a
126 : // unique ID, and deserializing several partial snapshots containing script
127 : // would cause dupes.
128 : DCHECK(!o->IsScript());
129 2419950 : return o->IsName() || o->IsSharedFunctionInfo() || o->IsHeapNumber() ||
130 1631717 : o->IsCode() || o->IsScopeInfo() || o->IsAccessorInfo() ||
131 2133750 : o->IsTemplateInfo() || o->IsClassPositions() ||
132 543618 : o->map() == ReadOnlyRoots(startup_serializer_->isolate())
133 1046504 : .fixed_cow_array_map();
134 : }
135 :
136 : namespace {
137 : bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
138 : } // anonymous namespace
139 :
140 543613 : bool PartialSerializer::SerializeJSObjectWithEmbedderFields(Object obj) {
141 543613 : if (!obj->IsJSObject()) return false;
142 265504 : JSObject js_obj = JSObject::cast(obj);
143 265504 : int embedder_fields_count = js_obj->GetEmbedderFieldCount();
144 265504 : if (embedder_fields_count == 0) return false;
145 175 : CHECK_GT(embedder_fields_count, 0);
146 : DCHECK(!js_obj->NeedsRehashing());
147 :
148 : DisallowHeapAllocation no_gc;
149 350 : DisallowJavascriptExecution no_js(isolate());
150 : DisallowCompilation no_compile(isolate());
151 :
152 : HandleScope scope(isolate());
153 : Handle<JSObject> obj_handle(js_obj, isolate());
154 175 : v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(obj_handle);
155 :
156 : std::vector<EmbedderDataSlot::RawData> original_embedder_values;
157 : std::vector<StartupData> serialized_data;
158 :
159 : // 1) Iterate embedder fields. Hold onto the original value of the fields.
160 : // Ignore references to heap objects since these are to be handled by the
161 : // serializer. For aligned pointers, call the serialize callback. Hold
162 : // onto the result.
163 935 : for (int i = 0; i < embedder_fields_count; i++) {
164 : EmbedderDataSlot embedder_data_slot(js_obj, i);
165 380 : original_embedder_values.emplace_back(embedder_data_slot.load_raw(no_gc));
166 : Object object = embedder_data_slot.load_tagged();
167 380 : if (object->IsHeapObject()) {
168 : DCHECK(isolate()->heap()->Contains(HeapObject::cast(object)));
169 220 : serialized_data.push_back({nullptr, 0});
170 : } else {
171 : // If no serializer is provided and the field was empty, we serialize it
172 : // by default to nullptr.
173 270 : if (serialize_embedder_fields_.callback == nullptr &&
174 : object->ptr() == 0) {
175 40 : serialized_data.push_back({nullptr, 0});
176 : } else {
177 : DCHECK_NOT_NULL(serialize_embedder_fields_.callback);
178 250 : StartupData data = serialize_embedder_fields_.callback(
179 250 : api_obj, i, serialize_embedder_fields_.data);
180 250 : serialized_data.push_back(data);
181 : }
182 : }
183 : }
184 :
185 : // 2) Embedder fields for which the embedder callback produced non-zero
186 : // serialized data should be considered aligned pointers to objects owned
187 : // by the embedder. Clear these memory addresses to avoid non-determism
188 : // in the snapshot. This is done separately to step 1 to no not interleave
189 : // with embedder callbacks.
190 935 : for (int i = 0; i < embedder_fields_count; i++) {
191 760 : if (!DataIsEmpty(serialized_data[i])) {
192 : EmbedderDataSlot(js_obj, i).store_raw(kNullAddress, no_gc);
193 : }
194 : }
195 :
196 : // 3) Serialize the object. References from embedder fields to heap objects or
197 : // smis are serialized regularly.
198 525 : ObjectSerializer(this, js_obj, &sink_).Serialize();
199 :
200 : // 4) Obtain back reference for the serialized object.
201 : SerializerReference reference =
202 350 : reference_map()->LookupReference(reinterpret_cast<void*>(js_obj->ptr()));
203 : DCHECK(reference.is_back_reference());
204 :
205 : // 5) Write data returned by the embedder callbacks into a separate sink,
206 : // headed by the back reference. Restore the original embedder fields.
207 935 : for (int i = 0; i < embedder_fields_count; i++) {
208 760 : StartupData data = serialized_data[i];
209 380 : if (DataIsEmpty(data)) continue;
210 : // Restore original values from cleared fields.
211 15 : EmbedderDataSlot(js_obj, i).store_raw(original_embedder_values[i], no_gc);
212 : embedder_fields_sink_.Put(kNewObject + reference.space(),
213 : "embedder field holder");
214 15 : embedder_fields_sink_.PutInt(reference.chunk_index(), "BackRefChunkIndex");
215 15 : embedder_fields_sink_.PutInt(reference.chunk_offset(),
216 15 : "BackRefChunkOffset");
217 15 : embedder_fields_sink_.PutInt(i, "embedder field index");
218 15 : embedder_fields_sink_.PutInt(data.raw_size, "embedder fields data size");
219 : embedder_fields_sink_.PutRaw(reinterpret_cast<const byte*>(data.data),
220 15 : data.raw_size, "embedder fields data");
221 15 : delete[] data.data;
222 : }
223 :
224 : // 6) The content of the separate sink is appended eventually to the default
225 : // sink. The ensures that during deserialization, we call the deserializer
226 : // callback at the end, and can guarantee that the deserialized objects are
227 : // in a consistent state. See PartialSerializer::Serialize.
228 : return true;
229 : }
230 :
231 543438 : void PartialSerializer::CheckRehashability(HeapObject obj) {
232 543438 : if (!can_be_rehashed_) return;
233 538853 : if (!obj->NeedsRehashing()) return;
234 37327 : if (obj->CanBeRehashed()) return;
235 5 : can_be_rehashed_ = false;
236 : }
237 :
238 : } // namespace internal
239 120216 : } // namespace v8
|