| 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 |