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 231 : 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 231 : can_be_rehashed_(true) {
24 231 : InitializeCodeAddressMap();
25 231 : allocator()->UseCustomChunkSize(FLAG_serialization_chunk_size);
26 231 : }
27 :
28 462 : PartialSerializer::~PartialSerializer() {
29 231 : OutputStatistics("PartialSerializer");
30 231 : }
31 :
32 231 : void PartialSerializer::Serialize(Context* o, bool include_global_proxy) {
33 231 : context_ = *o;
34 : DCHECK(context_->IsNativeContext());
35 : reference_map()->AddAttachedReference(
36 462 : 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 231 : 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 462 : context_->native_context()->set_microtask_queue(nullptr);
57 :
58 462 : VisitRootPointer(Root::kPartialSnapshotCache, nullptr, FullObjectSlot(o));
59 231 : SerializeDeferredObjects();
60 :
61 : // Add section for embedder-serialized embedder fields.
62 231 : 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 231 : Pad();
69 231 : }
70 :
71 2903548 : void PartialSerializer::SerializeObject(HeapObject obj, HowToCode how_to_code,
72 : WhereToPoint where_to_point, int skip) {
73 : DCHECK(!ObjectIsBytecodeHandler(obj)); // Only referenced in dispatch table.
74 :
75 5292567 : if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return;
76 :
77 2303339 : if (SerializeRoot(obj, how_to_code, where_to_point, skip)) return;
78 :
79 1229187 : if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return;
80 :
81 997802 : if (startup_serializer_->SerializeUsingReadOnlyObjectCache(
82 997802 : &sink_, obj, how_to_code, where_to_point, skip)) {
83 : return;
84 : }
85 :
86 995919 : if (ShouldBeInThePartialSnapshotCache(obj)) {
87 : startup_serializer_->SerializeUsingPartialSnapshotCache(
88 481210 : &sink_, obj, how_to_code, where_to_point, skip);
89 481210 : return;
90 : }
91 :
92 : // Pointers from the partial snapshot to the objects in the startup snapshot
93 : // should go through the root array or through the partial snapshot cache.
94 : // If this is not the case you may have to add something to the root array.
95 : DCHECK(!startup_serializer_->ReferenceMapContains(obj));
96 : // All the internalized strings that the partial snapshot needs should be
97 : // either in the root table or in the partial snapshot cache.
98 : DCHECK(!obj->IsInternalizedString());
99 : // Function and object templates are not context specific.
100 : DCHECK(!obj->IsTemplateInfo());
101 : // We should not end up at another native context.
102 : DCHECK_IMPLIES(obj != context_, !obj->IsNativeContext());
103 :
104 : FlushSkip(skip);
105 :
106 : // Clear literal boilerplates and feedback.
107 514724 : if (obj->IsFeedbackVector()) FeedbackVector::cast(obj)->ClearSlots(isolate());
108 :
109 514709 : if (SerializeJSObjectWithEmbedderFields(obj, how_to_code, where_to_point)) {
110 : return;
111 : }
112 :
113 514544 : if (obj->IsJSFunction()) {
114 : // Unconditionally reset the JSFunction to its SFI's code, since we can't
115 : // serialize optimized code anyway.
116 186766 : JSFunction closure = JSFunction::cast(obj);
117 186766 : closure->ResetIfBytecodeFlushed();
118 186766 : if (closure->is_compiled()) closure->set_code(closure->shared()->GetCode());
119 : }
120 :
121 514544 : CheckRehashability(obj);
122 :
123 : // Object has not yet been serialized. Serialize it here.
124 : ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
125 514544 : serializer.Serialize();
126 : }
127 :
128 995919 : bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject o) {
129 : // Scripts should be referred only through shared function infos. We can't
130 : // allow them to be part of the partial snapshot because they contain a
131 : // unique ID, and deserializing several partial snapshots containing script
132 : // would cause dupes.
133 : DCHECK(!o->IsScript());
134 2297628 : return o->IsName() || o->IsSharedFunctionInfo() || o->IsHeapNumber() ||
135 1544930 : o->IsCode() || o->IsScopeInfo() || o->IsAccessorInfo() ||
136 1510633 : o->IsTemplateInfo() ||
137 : o->map() == ReadOnlyRoots(startup_serializer_->isolate())
138 1510633 : .fixed_cow_array_map();
139 : }
140 :
141 : namespace {
142 : bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
143 : } // anonymous namespace
144 :
145 514709 : bool PartialSerializer::SerializeJSObjectWithEmbedderFields(
146 : Object obj, HowToCode how_to_code, WhereToPoint where_to_point) {
147 514709 : if (!obj->IsJSObject()) return false;
148 255395 : JSObject js_obj = JSObject::cast(obj);
149 255395 : int embedder_fields_count = js_obj->GetEmbedderFieldCount();
150 255395 : if (embedder_fields_count == 0) return false;
151 165 : CHECK_GT(embedder_fields_count, 0);
152 : DCHECK_NOT_NULL(serialize_embedder_fields_.callback);
153 : DCHECK(!js_obj->NeedsRehashing());
154 :
155 : DisallowHeapAllocation no_gc;
156 330 : DisallowJavascriptExecution no_js(isolate());
157 : DisallowCompilation no_compile(isolate());
158 :
159 : HandleScope scope(isolate());
160 : Handle<JSObject> obj_handle(js_obj, isolate());
161 165 : v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(obj_handle);
162 :
163 : std::vector<EmbedderDataSlot::RawData> original_embedder_values;
164 : std::vector<StartupData> serialized_data;
165 :
166 : // 1) Iterate embedder fields. Hold onto the original value of the fields.
167 : // Ignore references to heap objects since these are to be handled by the
168 : // serializer. For aligned pointers, call the serialize callback. Hold
169 : // onto the result.
170 525 : for (int i = 0; i < embedder_fields_count; i++) {
171 : EmbedderDataSlot embedder_data_slot(js_obj, i);
172 360 : original_embedder_values.emplace_back(embedder_data_slot.load_raw(no_gc));
173 360 : Object object = embedder_data_slot.load_tagged();
174 360 : if (object->IsHeapObject()) {
175 : DCHECK(isolate()->heap()->Contains(HeapObject::cast(object)));
176 220 : serialized_data.push_back({nullptr, 0});
177 : } else {
178 : 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 : // 2) Embedder fields for which the embedder callback produced non-zero
185 : // serialized data should be considered aligned pointers to objects owned
186 : // by the embedder. Clear these memory addresses to avoid non-determism
187 : // in the snapshot. This is done separately to step 1 to no not interleave
188 : // with embedder callbacks.
189 360 : for (int i = 0; i < embedder_fields_count; i++) {
190 720 : if (!DataIsEmpty(serialized_data[i])) {
191 15 : EmbedderDataSlot(js_obj, i).store_raw({kNullAddress}, no_gc);
192 : }
193 : }
194 :
195 : // 3) Serialize the object. References from embedder fields to heap objects or
196 : // smis are serialized regularly.
197 : ObjectSerializer(this, js_obj, &sink_, how_to_code, where_to_point)
198 495 : .Serialize();
199 :
200 : // 4) Obtain back reference for the serialized object.
201 : SerializerReference reference =
202 165 : 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 525 : for (int i = 0; i < embedder_fields_count; i++) {
208 720 : StartupData data = serialized_data[i];
209 360 : if (DataIsEmpty(data)) continue;
210 : // Restore original values from cleared fields.
211 30 : 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 165 : return true;
229 : }
230 :
231 514544 : void PartialSerializer::CheckRehashability(HeapObject obj) {
232 514544 : if (!can_be_rehashed_) return;
233 510199 : if (!obj->NeedsRehashing()) return;
234 34838 : if (obj->CanBeRehashed()) return;
235 5 : can_be_rehashed_ = false;
236 : }
237 :
238 : } // namespace internal
239 183867 : } // namespace v8
|