v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
context-serializer.cc
Go to the documentation of this file.
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
6
7#include "src/api/api-inl.h"
14#include "src/objects/slots.h"
17
18namespace v8 {
19namespace internal {
20
21namespace {
22
23// During serialization, puts the native context into a state understood by the
24// serializer (e.g. by clearing lists of InstructionStream objects). After
25// serialization, the original state is restored.
26class V8_NODISCARD SanitizeNativeContextScope final {
27 public:
28 SanitizeNativeContextScope(Isolate* isolate,
29 Tagged<NativeContext> native_context,
30 bool allow_active_isolate_for_testing,
31 const DisallowGarbageCollection& no_gc)
33#ifdef DEBUG
34 if (!allow_active_isolate_for_testing) {
35 // Microtasks.
36 MicrotaskQueue* microtask_queue = native_context_->microtask_queue();
37 DCHECK_EQ(0, microtask_queue->size());
38 DCHECK(!microtask_queue->HasMicrotasksSuppressions());
39 DCHECK_EQ(0, microtask_queue->GetMicrotasksScopeDepth());
40 DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
41 }
42#endif
45 ->RawExternalPointerField(NativeContext::kMicrotaskQueueOffset,
46 kNativeContextMicrotaskQueueTag)
47 .GetAndClearContentForSerialization(no_gc);
48 }
49
50 ~SanitizeNativeContextScope() {
51 // Restore saved fields.
53 ->RawExternalPointerField(NativeContext::kMicrotaskQueueOffset,
54 kNativeContextMicrotaskQueueTag)
55 .RestoreContentAfterSerialization(microtask_queue_external_pointer_,
56 no_gc_);
57 }
58
59 private:
60 Tagged<NativeContext> native_context_;
61 ExternalPointerSlot::RawContent microtask_queue_external_pointer_;
62 const DisallowGarbageCollection& no_gc_;
63};
64
65} // namespace
66
69 StartupSerializer* startup_serializer,
71 : Serializer(isolate, flags),
72 startup_serializer_(startup_serializer),
73 serialize_embedder_fields_(callback),
74 can_be_rehashed_(true) {
76}
77
81
83 const DisallowGarbageCollection& no_gc) {
84 context_ = *o;
85 DCHECK(IsNativeContext(context_));
86
87 // Upon deserialization, references to the global proxy and its map will be
88 // replaced.
90 reference_map()->AddAttachedReference(context_->global_proxy()->map());
91
92 // The bootstrap snapshot has a code-stub context. When serializing the
93 // context snapshot, it is chained into the weak context list on the isolate
94 // and it's next context pointer may point to the code-stub context. Clear
95 // it before serializing, it will get re-added to the context list
96 // explicitly when it's loaded.
97 // TODO(v8:10416): These mutations should not observably affect the running
98 // context.
100 ReadOnlyRoots(isolate()).undefined_value());
101 DCHECK(!IsUndefined(context_->global_object()));
102 // Reset math random cache to get fresh random numbers.
104
105 SanitizeNativeContextScope sanitize_native_context(
107 no_gc);
108
109 VisitRootPointer(Root::kStartupObjectCache, nullptr, FullObjectSlot(o));
111
112 // Add section for embedder-serialized embedder fields.
113 if (!embedder_fields_sink_.data()->empty()) {
114 sink_.Put(kEmbedderFieldsData, "embedder fields data");
116 sink_.Put(kSynchronize, "Finished with embedder fields data");
117 }
118
119 // Add section for embedder-serializer API wrappers.
120 if (!api_wrapper_sink_.data()->empty()) {
121 sink_.Put(kApiWrapperFieldsData, "api wrapper fields data");
123 sink_.Put(kSynchronize, "Finished with api wrapper fields data");
124 }
125
126 Pad();
127}
128
130 int index, bool field_is_nullptr,
132 v8::Local<v8::Object> api_obj) {
133 // If no serializer is provided and the field was empty, we
134 // serialize it by default to nullptr.
135 if (user_callback.callback == nullptr && field_is_nullptr) {
136 return StartupData{nullptr, 0};
137 }
138
139 DCHECK(user_callback.callback);
140 return user_callback.callback(api_obj, index, user_callback.data);
141}
142
144 int index, bool field_is_nullptr,
146 v8::Local<v8::Context> api_obj) {
147 // For compatibility, we do not require all non-null context pointer
148 // fields to be serialized by a proper user callback. Instead, if no
149 // user callback is provided, we serialize it verbatim, which was
150 // the old behavior before we introduce context data callbacks.
151 if (user_callback.callback == nullptr) {
152 return StartupData{nullptr, 0};
153 }
154
155 return user_callback.callback(api_obj, index, user_callback.data);
156}
157
159 SlotType slot_type) {
160 DCHECK(!ObjectIsBytecodeHandler(*obj)); // Only referenced in dispatch table.
161
163 // When serializing a snapshot intended for real use, we should not end up
164 // at another native context.
165 // But in test scenarios there is no way to avoid this. Since we only
166 // serialize a single context in these cases, and this context does not
167 // have to be executable, we can simply ignore this.
168 DCHECK_IMPLIES(IsNativeContext(*obj), *obj == context_);
169 }
170
171 {
173 Tagged<HeapObject> raw = *obj;
174 if (SerializeHotObject(raw)) return;
175 if (SerializeRoot(raw)) return;
176 if (SerializeBackReference(raw)) return;
177 if (SerializeReadOnlyObjectReference(raw, &sink_)) return;
178 }
179
181 return;
182 }
183
186 return;
187 }
188
189 // Pointers from the context snapshot to the objects in the startup snapshot
190 // should go through the root array or through the startup object cache.
191 // If this is not the case you may have to add something to the root array.
193 // All the internalized strings that the context snapshot needs should be
194 // either in the root table or in the shared heap object cache.
195 DCHECK(!IsInternalizedString(*obj));
196 // Function and object templates are not context specific.
197 DCHECK(!IsTemplateInfo(*obj));
198
199 InstanceType instance_type = obj->map()->instance_type();
200 if (InstanceTypeChecker::IsFeedbackVector(instance_type)) {
201 // Clear literal boilerplates and feedback.
202 Cast<FeedbackVector>(obj)->ClearSlots(isolate());
203 } else if (InstanceTypeChecker::IsJSObject(instance_type)) {
204 Handle<JSObject> js_obj = Cast<JSObject>(obj);
205 int embedder_fields_count = js_obj->GetEmbedderFieldCount();
206 if (embedder_fields_count > 0) {
207 DCHECK(!js_obj->NeedsRehashing(cage_base()));
208 v8::Local<v8::Object> api_obj = v8::Utils::ToLocal(js_obj);
211 SerializeObjectWithEmbedderFields(js_obj, embedder_fields_count,
213 user_callback, api_obj);
214 if (IsJSApiWrapperObject(*js_obj)) {
216 }
217 return;
218 }
219 if (InstanceTypeChecker::IsJSFunction(instance_type)) {
221 // Unconditionally reset the JSFunction to its SFI's code, since we can't
222 // serialize optimized code anyway.
223 Tagged<JSFunction> closure = Cast<JSFunction>(*obj);
224 if (closure->shared()->HasBytecodeArray()) {
225 closure->SetInterruptBudget(isolate(), BudgetModification::kReset);
226 }
227 closure->ResetIfCodeFlushed(isolate());
228 if (closure->is_compiled(isolate())) {
229 if (closure->shared()->HasBaselineCode()) {
230 closure->shared()->FlushBaselineCode();
231 }
232 Tagged<Code> sfi_code = closure->shared()->GetCode(isolate());
233 if (!sfi_code.SafeEquals(closure->code(isolate()))) {
234 closure->UpdateCode(sfi_code);
235 }
236 }
237 }
238 } else if (InstanceTypeChecker::IsEmbedderDataArray(instance_type) &&
240 DCHECK_EQ(*obj, context_->embedder_data());
242 int embedder_fields_count = embedder_data->length();
243 if (embedder_data->length() > 0) {
244 DirectHandle<Context> context_handle(context_, isolate());
245 v8::Local<v8::Context> api_obj =
246 v8::Utils::ToLocal(Cast<NativeContext>(context_handle));
249 SerializeObjectWithEmbedderFields(embedder_data, embedder_fields_count,
251 user_callback, api_obj);
252 return;
253 }
254 }
255
256 CheckRehashability(*obj);
257
258 // Object has not yet been serialized. Serialize it here.
259 ObjectSerializer serializer(this, obj, &sink_);
260 serializer.Serialize(slot_type);
261 if (IsJSApiWrapperObject(obj->map())) {
263 }
264}
265
267 // We can't allow scripts to be part of the context snapshot because they
268 // contain a unique ID, and deserializing several context snapshots containing
269 // script would cause dupes.
270 return IsName(o) || IsScript(o) || IsSharedFunctionInfo(o) ||
271 IsHeapNumber(o) || IsCode(o) || IsInstructionStream(o) ||
272 IsScopeInfo(o) || IsAccessorInfo(o) || IsTemplateInfo(o) ||
273 IsClassPositions(o) ||
274 o->map() == ReadOnlyRoots(isolate()).fixed_cow_array_map();
275}
276
278 // v8_flags.shared_string_table may be true during deserialization, so put
279 // internalized strings into the shared object snapshot.
280 return IsInternalizedString(o);
281}
282
283namespace {
284bool DataIsEmpty(const StartupData& data) { return data.raw_size == 0; }
285} // anonymous namespace
286
288 DirectHandle<JSObject> js_object) {
289 DCHECK(IsJSApiWrapperObject(*js_object));
290 auto* cpp_heap_pointer =
291 JSApiWrapper(*js_object)
293 const auto& callback_data = serialize_embedder_fields_.api_wrapper_callback;
294 if (callback_data.callback == nullptr && cpp_heap_pointer == nullptr) {
295 // No need to serialize anything as empty handles or handles pointing to
296 // null objects will be preserved.
297 return;
298 }
299 DCHECK_NOT_NULL(callback_data.callback);
300 const auto data = callback_data.callback(
301 v8::Utils::ToLocal(js_object), cpp_heap_pointer, callback_data.data);
302 if (DataIsEmpty(data)) {
303 return;
304 }
305 const SerializerReference* reference =
306 reference_map()->LookupReference(*js_object);
307 DCHECK_NOT_NULL(reference);
308 DCHECK(reference->is_back_reference());
309 api_wrapper_sink_.Put(kNewObject, "api wrapper field holder");
310 api_wrapper_sink_.PutUint30(reference->back_ref_index(), "BackRefIndex");
311 api_wrapper_sink_.PutUint30(data.raw_size, "api wrapper raw field data size");
312 api_wrapper_sink_.PutRaw(reinterpret_cast<const uint8_t*>(data.data),
313 data.raw_size, "api wrapper raw field data");
314}
315
316template <typename V8Type, typename UserSerializerWrapper,
317 typename UserCallback, typename ApiObjectType>
319 Handle<V8Type> data_holder, int embedder_fields_count,
320 UserSerializerWrapper wrapper, UserCallback user_callback,
321 ApiObjectType api_obj) {
323 CHECK_GT(embedder_fields_count, 0);
324 DisallowJavascriptExecution no_js(isolate());
325 DisallowCompilation no_compile(isolate());
326
327 auto raw_obj = *data_holder;
328
329 std::vector<EmbedderDataSlot::RawData> original_embedder_values;
330 std::vector<StartupData> serialized_data;
331 std::vector<bool> should_clear_slot;
332
333 // 1) Iterate embedder fields. Hold onto the original value of the fields.
334 // Ignore references to heap objects since these are to be handled by the
335 // serializer. For aligned pointers, call the serialize callback. Hold
336 // onto the result.
337 for (int i = 0; i < embedder_fields_count; i++) {
338 EmbedderDataSlot slot(raw_obj, i);
339 original_embedder_values.emplace_back(slot.load_raw(isolate(), no_gc));
340 Tagged<Object> object = slot.load_tagged();
341 if (IsHeapObject(object)) {
343 serialized_data.push_back({nullptr, 0});
344 should_clear_slot.push_back(false);
345 } else {
346 StartupData data =
347 wrapper(i, object == Smi::zero(), user_callback, api_obj);
348 serialized_data.push_back(data);
349 bool clear_slot =
350 !DataIsEmpty(data) || slot.MustClearDuringSerialization(no_gc);
351 should_clear_slot.push_back(clear_slot);
352 }
353 }
354
355 // 2) Prevent embedder fields that are not V8 objects from ending up in the
356 // blob. This is done separately to step 1 so as to not interleave with
357 // embedder callbacks.
358 for (int i = 0; i < embedder_fields_count; i++) {
359 if (should_clear_slot[i]) {
360 EmbedderDataSlot(raw_obj, i).store_raw(isolate(), kNullAddress, no_gc);
361 }
362 }
363
364 // 3) Serialize the object. References from embedder fields to heap objects or
365 // smis are serialized regularly.
366 {
367 AllowGarbageCollection allow_gc;
369 // Reload raw pointer.
370 raw_obj = *data_holder;
371 }
372
373 // 4) Obtain back reference for the serialized object.
374 const SerializerReference* reference =
375 reference_map()->LookupReference(raw_obj);
376 DCHECK_NOT_NULL(reference);
377 DCHECK(reference->is_back_reference());
378
379 // 5) Write data returned by the embedder callbacks into a separate sink,
380 // headed by the back reference. Restore the original embedder fields.
381 for (int i = 0; i < embedder_fields_count; i++) {
382 StartupData data = serialized_data[i];
383 if (!should_clear_slot[i]) continue;
384 // Restore original values from cleared fields.
385 EmbedderDataSlot(raw_obj, i)
386 .store_raw(isolate(), original_embedder_values[i], no_gc);
387 if (DataIsEmpty(data)) continue;
388 embedder_fields_sink_.Put(kNewObject, "embedder field holder");
390 "BackRefIndex");
391 embedder_fields_sink_.PutUint30(i, "embedder field index");
392 embedder_fields_sink_.PutUint30(data.raw_size, "embedder fields data size");
393 embedder_fields_sink_.PutRaw(reinterpret_cast<const uint8_t*>(data.data),
394 data.raw_size, "embedder fields data");
395 delete[] data.data;
396 }
397
398 // 6) The content of the separate sink is appended eventually to the default
399 // sink. The ensures that during deserialization, we call the deserializer
400 // callback at the end, and can guarantee that the deserialized objects are
401 // in a consistent state. See ContextSerializer::Serialize.
402}
403
405 if (!can_be_rehashed_) return;
406 if (!obj->NeedsRehashing(cage_base())) return;
407 if (obj->CanBeRehashed(cage_base())) return;
408 can_be_rehashed_ = false;
409}
410
411} // namespace internal
412} // namespace v8
void Serialize(Tagged< Context > *o, const DisallowGarbageCollection &no_gc)
void SerializeApiWrapperFields(DirectHandle< JSObject > js_object)
ContextSerializer(Isolate *isolate, Snapshot::SerializerFlags flags, StartupSerializer *startup_serializer, SerializeEmbedderFieldsCallback callback)
void SerializeObjectWithEmbedderFields(Handle< V8Type > data_holder, int embedder_fields_count, UserSerializerWrapper wrapper, UserCallback user_callback, ApiObjectType api_obj)
void SerializeObjectImpl(Handle< HeapObject > o, SlotType slot_type) override
SerializeEmbedderFieldsCallback serialize_embedder_fields_
void CheckRehashability(Tagged< HeapObject > obj)
bool ShouldBeInTheSharedObjectCache(Tagged< HeapObject > o)
bool ShouldBeInTheStartupObjectCache(Tagged< HeapObject > o)
StartupSerializer * startup_serializer_
V8_INLINE void store_raw(IsolateForSandbox isolate, RawData data, const DisallowGarbageCollection &no_gc)
V8_INLINE Tagged< Object > load_tagged() const
V8_INLINE bool MustClearDuringSerialization(const DisallowGarbageCollection &no_gc)
V8_INLINE RawData load_raw(IsolateForSandbox isolate, const DisallowGarbageCollection &no_gc) const
Handle< NativeContext > native_context()
Definition isolate-inl.h:48
V8_INLINE void * GetCppHeapWrappable(IsolateForPointerCompression isolate) const
static void ResetContext(Tagged< Context > native_context)
virtual void VisitRootPointer(Root root, const char *description, FullObjectSlot p)
Definition visitors.h:75
const SerializerReference * LookupReference(Tagged< HeapObject > object) const
Definition references.h:108
SerializerReference AddAttachedReference(Tagged< HeapObject > object)
Definition references.h:133
bool ReferenceMapContains(DirectHandle< HeapObject > o)
Definition serializer.h:191
PtrComprCageBase cage_base() const
Definition serializer.h:199
bool ObjectIsBytecodeHandler(Tagged< HeapObject > obj) const
Isolate * isolate() const
Definition serializer.h:195
SnapshotByteSink sink_
Definition serializer.h:323
bool SerializeRoot(Tagged< HeapObject > obj)
bool SerializeReadOnlyObjectReference(Tagged< HeapObject > obj, SnapshotByteSink *sink)
void OutputStatistics(const char *name)
void Pad(int padding_offset=0)
bool allow_active_isolate_for_testing() const
Definition serializer.h:328
SerializerReferenceMap * reference_map()
Definition serializer.h:320
bool SerializeHotObject(Tagged< HeapObject > obj)
bool SerializeBackReference(Tagged< HeapObject > obj)
static constexpr Tagged< Smi > zero()
Definition smi.h:99
void Append(const SnapshotByteSink &other)
void PutUint30(uint32_t integer, const char *description)
void PutRaw(const uint8_t *data, int number_of_bytes, const char *description)
void Put(uint8_t b, const char *description)
const std::vector< uint8_t > * data() const
void SerializeUsingStartupObjectCache(SnapshotByteSink *sink, Handle< HeapObject > obj)
bool SerializeUsingSharedHeapObjectCache(SnapshotByteSink *sink, Handle< HeapObject > obj)
constexpr bool SafeEquals(TaggedImpl< kOtherRefType, StorageType > other) const
Definition tagged-impl.h:93
Tagged< NativeContext > native_context_
ExternalPointerSlot::RawContent microtask_queue_external_pointer_
MicrotaskQueue * microtask_queue
Definition execution.cc:77
DisallowGarbageCollection no_gc_
TNode< Object > callback
v8::StartupData ContextDataSerializeWrapper(int index, bool field_is_nullptr, v8::SerializeContextDataCallback user_callback, v8::Local< v8::Context > api_obj)
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:669
bool IsJSApiWrapperObject(Tagged< Map > map)
static constexpr Address kNullAddress
Definition v8-internal.h:53
V8_WARN_UNUSED_RESULT bool IsValidHeapObject(Heap *heap, Tagged< HeapObject > object)
v8::StartupData InternalFieldSerializeWrapper(int index, bool field_is_nullptr, v8::SerializeInternalFieldsCallback user_callback, v8::Local< v8::Object > api_obj)
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
constexpr CppHeapPointerTagRange kAnyCppHeapPointer(CppHeapPointerTag::kFirstTag, CppHeapPointerTag::kLastTag)
#define CHECK_GT(lhs, rhs)
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
v8::SerializeInternalFieldsCallback js_object_callback
#define V8_NODISCARD
Definition v8config.h:693