File: | out/../deps/v8/src/snapshot/context-serializer.cc |
Warning: | line 259, column 28 Called function pointer is null (null dereference) |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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/context-serializer.h" | |||
6 | #include "src/snapshot/startup-serializer.h" | |||
7 | ||||
8 | #include "src/api/api-inl.h" | |||
9 | #include "src/execution/microtask-queue.h" | |||
10 | #include "src/heap/combined-heap.h" | |||
11 | #include "src/numbers/math-random.h" | |||
12 | #include "src/objects/objects-inl.h" | |||
13 | #include "src/objects/slots.h" | |||
14 | ||||
15 | namespace v8 { | |||
16 | namespace internal { | |||
17 | ||||
18 | namespace { | |||
19 | ||||
20 | // During serialization, puts the native context into a state understood by the | |||
21 | // serializer (e.g. by clearing lists of Code objects). After serialization, | |||
22 | // the original state is restored. | |||
23 | class V8_NODISCARD[[nodiscard]] SanitizeNativeContextScope final { | |||
24 | public: | |||
25 | SanitizeNativeContextScope(Isolate* isolate, NativeContext native_context, | |||
26 | bool allow_active_isolate_for_testing, | |||
27 | const DisallowGarbageCollection& no_gc) | |||
28 | : isolate_(isolate), | |||
29 | native_context_(native_context), | |||
30 | microtask_queue_(native_context.microtask_queue()), | |||
31 | optimized_code_list_(native_context.OptimizedCodeListHead()), | |||
32 | deoptimized_code_list_(native_context.DeoptimizedCodeListHead()) { | |||
33 | #ifdef DEBUG | |||
34 | if (!allow_active_isolate_for_testing) { | |||
35 | // Microtasks. | |||
36 | DCHECK_EQ(0, microtask_queue_->size())((void) 0); | |||
37 | DCHECK(!microtask_queue_->HasMicrotasksSuppressions())((void) 0); | |||
38 | DCHECK_EQ(0, microtask_queue_->GetMicrotasksScopeDepth())((void) 0); | |||
39 | DCHECK(microtask_queue_->DebugMicrotasksScopeDepthIsZero())((void) 0); | |||
40 | // Code lists. | |||
41 | DCHECK(optimized_code_list_.IsUndefined(isolate))((void) 0); | |||
42 | DCHECK(deoptimized_code_list_.IsUndefined(isolate))((void) 0); | |||
43 | } | |||
44 | #endif | |||
45 | Object undefined = ReadOnlyRoots(isolate).undefined_value(); | |||
46 | native_context.set_microtask_queue(isolate, nullptr); | |||
47 | native_context.SetOptimizedCodeListHead(undefined); | |||
48 | native_context.SetDeoptimizedCodeListHead(undefined); | |||
49 | } | |||
50 | ||||
51 | ~SanitizeNativeContextScope() { | |||
52 | // Restore saved fields. | |||
53 | native_context_.SetDeoptimizedCodeListHead(optimized_code_list_); | |||
54 | native_context_.SetOptimizedCodeListHead(deoptimized_code_list_); | |||
55 | native_context_.set_microtask_queue(isolate_, microtask_queue_); | |||
56 | } | |||
57 | ||||
58 | private: | |||
59 | Isolate* isolate_; | |||
60 | NativeContext native_context_; | |||
61 | MicrotaskQueue* const microtask_queue_; | |||
62 | const Object optimized_code_list_; | |||
63 | const Object deoptimized_code_list_; | |||
64 | }; | |||
65 | ||||
66 | } // namespace | |||
67 | ||||
68 | ContextSerializer::ContextSerializer( | |||
69 | Isolate* isolate, Snapshot::SerializerFlags flags, | |||
70 | StartupSerializer* startup_serializer, | |||
71 | v8::SerializeEmbedderFieldsCallback callback) | |||
72 | : Serializer(isolate, flags), | |||
73 | startup_serializer_(startup_serializer), | |||
74 | serialize_embedder_fields_(callback), | |||
75 | can_be_rehashed_(true) { | |||
76 | InitializeCodeAddressMap(); | |||
77 | } | |||
78 | ||||
79 | ContextSerializer::~ContextSerializer() { | |||
80 | OutputStatistics("ContextSerializer"); | |||
81 | } | |||
82 | ||||
83 | void ContextSerializer::Serialize(Context* o, | |||
84 | const DisallowGarbageCollection& no_gc) { | |||
85 | context_ = *o; | |||
86 | DCHECK(context_.IsNativeContext())((void) 0); | |||
87 | ||||
88 | // Upon deserialization, references to the global proxy and its map will be | |||
89 | // replaced. | |||
90 | reference_map()->AddAttachedReference(context_.global_proxy()); | |||
91 | reference_map()->AddAttachedReference(context_.global_proxy().map()); | |||
92 | ||||
93 | // The bootstrap snapshot has a code-stub context. When serializing the | |||
94 | // context snapshot, it is chained into the weak context list on the isolate | |||
95 | // and it's next context pointer may point to the code-stub context. Clear | |||
96 | // it before serializing, it will get re-added to the context list | |||
97 | // explicitly when it's loaded. | |||
98 | // TODO(v8:10416): These mutations should not observably affect the running | |||
99 | // context. | |||
100 | context_.set(Context::NEXT_CONTEXT_LINK, | |||
101 | ReadOnlyRoots(isolate()).undefined_value()); | |||
102 | DCHECK(!context_.global_object().IsUndefined())((void) 0); | |||
103 | // Reset math random cache to get fresh random numbers. | |||
104 | MathRandom::ResetContext(context_); | |||
105 | ||||
106 | SanitizeNativeContextScope sanitize_native_context( | |||
107 | isolate(), context_.native_context(), allow_active_isolate_for_testing(), | |||
108 | no_gc); | |||
109 | ||||
110 | VisitRootPointer(Root::kStartupObjectCache, nullptr, FullObjectSlot(o)); | |||
111 | SerializeDeferredObjects(); | |||
112 | ||||
113 | // Add section for embedder-serialized embedder fields. | |||
114 | if (!embedder_fields_sink_.data()->empty()) { | |||
115 | sink_.Put(kEmbedderFieldsData, "embedder fields data"); | |||
116 | sink_.Append(embedder_fields_sink_); | |||
117 | sink_.Put(kSynchronize, "Finished with embedder fields data"); | |||
118 | } | |||
119 | ||||
120 | Pad(); | |||
121 | } | |||
122 | ||||
123 | void ContextSerializer::SerializeObjectImpl(Handle<HeapObject> obj) { | |||
124 | DCHECK(!ObjectIsBytecodeHandler(*obj))((void) 0); // Only referenced in dispatch table. | |||
125 | ||||
126 | if (!allow_active_isolate_for_testing()) { | |||
| ||||
127 | // When serializing a snapshot intended for real use, we should not end up | |||
128 | // at another native context. | |||
129 | // But in test scenarios there is no way to avoid this. Since we only | |||
130 | // serialize a single context in these cases, and this context does not | |||
131 | // have to be executable, we can simply ignore this. | |||
132 | DCHECK_IMPLIES(obj->IsNativeContext(), *obj == context_)((void) 0); | |||
133 | } | |||
134 | ||||
135 | { | |||
136 | DisallowGarbageCollection no_gc; | |||
137 | HeapObject raw = *obj; | |||
138 | if (SerializeHotObject(raw)) return; | |||
139 | if (SerializeRoot(raw)) return; | |||
140 | if (SerializeBackReference(raw)) return; | |||
141 | } | |||
142 | ||||
143 | if (startup_serializer_->SerializeUsingReadOnlyObjectCache(&sink_, obj)) { | |||
144 | return; | |||
145 | } | |||
146 | ||||
147 | if (startup_serializer_->SerializeUsingSharedHeapObjectCache(&sink_, obj)) { | |||
148 | return; | |||
149 | } | |||
150 | ||||
151 | if (ShouldBeInTheStartupObjectCache(*obj)) { | |||
152 | startup_serializer_->SerializeUsingStartupObjectCache(&sink_, obj); | |||
153 | return; | |||
154 | } | |||
155 | ||||
156 | // Pointers from the context snapshot to the objects in the startup snapshot | |||
157 | // should go through the root array or through the startup object cache. | |||
158 | // If this is not the case you may have to add something to the root array. | |||
159 | DCHECK(!startup_serializer_->ReferenceMapContains(obj))((void) 0); | |||
160 | // All the internalized strings that the context snapshot needs should be | |||
161 | // either in the root table or in the shared heap object cache. | |||
162 | DCHECK(!obj->IsInternalizedString())((void) 0); | |||
163 | // Function and object templates are not context specific. | |||
164 | DCHECK(!obj->IsTemplateInfo())((void) 0); | |||
165 | ||||
166 | InstanceType instance_type = obj->map().instance_type(); | |||
167 | if (InstanceTypeChecker::IsFeedbackVector(instance_type)) { | |||
168 | // Clear literal boilerplates and feedback. | |||
169 | Handle<FeedbackVector>::cast(obj)->ClearSlots(isolate()); | |||
170 | } else if (InstanceTypeChecker::IsFeedbackCell(instance_type)) { | |||
171 | // Clear InterruptBudget when serializing FeedbackCell. | |||
172 | Handle<FeedbackCell>::cast(obj)->SetInitialInterruptBudget(); | |||
173 | } else if (InstanceTypeChecker::IsJSObject(instance_type)) { | |||
174 | if (SerializeJSObjectWithEmbedderFields(Handle<JSObject>::cast(obj))) { | |||
175 | return; | |||
176 | } | |||
177 | if (InstanceTypeChecker::IsJSFunction(instance_type)) { | |||
178 | DisallowGarbageCollection no_gc; | |||
179 | // Unconditionally reset the JSFunction to its SFI's code, since we can't | |||
180 | // serialize optimized code anyway. | |||
181 | JSFunction closure = JSFunction::cast(*obj); | |||
182 | closure.ResetIfCodeFlushed(); | |||
183 | if (closure.is_compiled()) { | |||
184 | if (closure.shared().HasBaselineCode()) { | |||
185 | closure.shared().FlushBaselineCode(); | |||
186 | } | |||
187 | closure.set_code(closure.shared().GetCode(), kReleaseStore); | |||
188 | } | |||
189 | } | |||
190 | } | |||
191 | ||||
192 | CheckRehashability(*obj); | |||
193 | ||||
194 | // Object has not yet been serialized. Serialize it here. | |||
195 | ObjectSerializer serializer(this, obj, &sink_); | |||
196 | serializer.Serialize(); | |||
197 | } | |||
198 | ||||
199 | bool ContextSerializer::ShouldBeInTheStartupObjectCache(HeapObject o) { | |||
200 | // Scripts should be referred only through shared function infos. We can't | |||
201 | // allow them to be part of the context snapshot because they contain a | |||
202 | // unique ID, and deserializing several context snapshots containing script | |||
203 | // would cause dupes. | |||
204 | DCHECK(!o.IsScript())((void) 0); | |||
205 | return o.IsName() || o.IsSharedFunctionInfo() || o.IsHeapNumber() || | |||
206 | (V8_EXTERNAL_CODE_SPACE_BOOLfalse && o.IsCodeDataContainer()) || | |||
207 | o.IsCode() || o.IsScopeInfo() || o.IsAccessorInfo() || | |||
208 | o.IsTemplateInfo() || o.IsClassPositions() || | |||
209 | o.map() == ReadOnlyRoots(isolate()).fixed_cow_array_map(); | |||
210 | } | |||
211 | ||||
212 | bool ContextSerializer::ShouldBeInTheSharedObjectCache(HeapObject o) { | |||
213 | // FLAG_shared_string_table may be true during deserialization, so put | |||
214 | // internalized strings into the shared object snapshot. | |||
215 | return o.IsInternalizedString(); | |||
216 | } | |||
217 | ||||
218 | namespace { | |||
219 | bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; } | |||
220 | } // anonymous namespace | |||
221 | ||||
222 | bool ContextSerializer::SerializeJSObjectWithEmbedderFields( | |||
223 | Handle<JSObject> obj) { | |||
224 | DisallowGarbageCollection no_gc; | |||
225 | JSObject js_obj = *obj; | |||
226 | int embedder_fields_count = js_obj.GetEmbedderFieldCount(); | |||
227 | if (embedder_fields_count == 0) return false; | |||
228 | CHECK_GT(embedder_fields_count, 0)do { bool _cmp = ::v8::base::CmpGTImpl< typename ::v8::base ::pass_value_or_ref<decltype(embedder_fields_count)>::type , typename ::v8::base::pass_value_or_ref<decltype(0)>:: type>((embedder_fields_count), (0)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "embedder_fields_count" " " ">" " " "0"); } } while (false); } while (false); | |||
229 | DCHECK(!js_obj.NeedsRehashing(cage_base()))((void) 0); | |||
230 | ||||
231 | DisallowJavascriptExecution no_js(isolate()); | |||
232 | DisallowCompilation no_compile(isolate()); | |||
233 | ||||
234 | v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(obj); | |||
235 | ||||
236 | std::vector<EmbedderDataSlot::RawData> original_embedder_values; | |||
237 | std::vector<StartupData> serialized_data; | |||
238 | ||||
239 | // 1) Iterate embedder fields. Hold onto the original value of the fields. | |||
240 | // Ignore references to heap objects since these are to be handled by the | |||
241 | // serializer. For aligned pointers, call the serialize callback. Hold | |||
242 | // onto the result. | |||
243 | for (int i = 0; i
| |||
244 | EmbedderDataSlot embedder_data_slot(js_obj, i); | |||
245 | original_embedder_values.emplace_back( | |||
246 | embedder_data_slot.load_raw(isolate(), no_gc)); | |||
247 | Object object = embedder_data_slot.load_tagged(); | |||
248 | if (object.IsHeapObject()) { | |||
249 | DCHECK(IsValidHeapObject(isolate()->heap(), HeapObject::cast(object)))((void) 0); | |||
250 | serialized_data.push_back({nullptr, 0}); | |||
251 | } else { | |||
252 | // If no serializer is provided and the field was empty, we serialize it | |||
253 | // by default to nullptr. | |||
254 | if (serialize_embedder_fields_.callback == nullptr && | |||
255 | object == Smi::zero()) { | |||
256 | serialized_data.push_back({nullptr, 0}); | |||
257 | } else { | |||
258 | DCHECK_NOT_NULL(serialize_embedder_fields_.callback)((void) 0); | |||
259 | StartupData data = serialize_embedder_fields_.callback( | |||
| ||||
260 | api_obj, i, serialize_embedder_fields_.data); | |||
261 | serialized_data.push_back(data); | |||
262 | } | |||
263 | } | |||
264 | } | |||
265 | ||||
266 | // 2) Embedder fields for which the embedder callback produced non-zero | |||
267 | // serialized data should be considered aligned pointers to objects owned | |||
268 | // by the embedder. Clear these memory addresses to avoid non-determism | |||
269 | // in the snapshot. This is done separately to step 1 to no not interleave | |||
270 | // with embedder callbacks. | |||
271 | for (int i = 0; i < embedder_fields_count; i++) { | |||
272 | if (!DataIsEmpty(serialized_data[i])) { | |||
273 | EmbedderDataSlot(js_obj, i).store_raw(isolate(), kNullAddress, no_gc); | |||
274 | } | |||
275 | } | |||
276 | ||||
277 | // 3) Serialize the object. References from embedder fields to heap objects or | |||
278 | // smis are serialized regularly. | |||
279 | { | |||
280 | AllowGarbageCollection allow_gc; | |||
281 | ObjectSerializer(this, obj, &sink_).Serialize(); | |||
282 | // Reload raw pointer. | |||
283 | js_obj = *obj; | |||
284 | } | |||
285 | ||||
286 | // 4) Obtain back reference for the serialized object. | |||
287 | const SerializerReference* reference = | |||
288 | reference_map()->LookupReference(js_obj); | |||
289 | DCHECK_NOT_NULL(reference)((void) 0); | |||
290 | DCHECK(reference->is_back_reference())((void) 0); | |||
291 | ||||
292 | // 5) Write data returned by the embedder callbacks into a separate sink, | |||
293 | // headed by the back reference. Restore the original embedder fields. | |||
294 | for (int i = 0; i < embedder_fields_count; i++) { | |||
295 | StartupData data = serialized_data[i]; | |||
296 | if (DataIsEmpty(data)) continue; | |||
297 | // Restore original values from cleared fields. | |||
298 | EmbedderDataSlot(js_obj, i).store_raw(isolate(), | |||
299 | original_embedder_values[i], no_gc); | |||
300 | embedder_fields_sink_.Put(kNewObject, "embedder field holder"); | |||
301 | embedder_fields_sink_.PutInt(reference->back_ref_index(), "BackRefIndex"); | |||
302 | embedder_fields_sink_.PutInt(i, "embedder field index"); | |||
303 | embedder_fields_sink_.PutInt(data.raw_size, "embedder fields data size"); | |||
304 | embedder_fields_sink_.PutRaw(reinterpret_cast<const byte*>(data.data), | |||
305 | data.raw_size, "embedder fields data"); | |||
306 | delete[] data.data; | |||
307 | } | |||
308 | ||||
309 | // 6) The content of the separate sink is appended eventually to the default | |||
310 | // sink. The ensures that during deserialization, we call the deserializer | |||
311 | // callback at the end, and can guarantee that the deserialized objects are | |||
312 | // in a consistent state. See ContextSerializer::Serialize. | |||
313 | return true; | |||
314 | } | |||
315 | ||||
316 | void ContextSerializer::CheckRehashability(HeapObject obj) { | |||
317 | if (!can_be_rehashed_) return; | |||
318 | if (!obj.NeedsRehashing(cage_base())) return; | |||
319 | if (obj.CanBeRehashed(cage_base())) return; | |||
320 | can_be_rehashed_ = false; | |||
321 | } | |||
322 | ||||
323 | } // namespace internal | |||
324 | } // namespace v8 |