| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/snapshot/startup-serializer.h" |
| |
| #include "src/api/api.h" |
| #include "src/deoptimizer/deoptimizer.h" |
| #include "src/execution/v8threads.h" |
| #include "src/handles/global-handles.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/read-only-heap.h" |
| #include "src/objects/contexts.h" |
| #include "src/objects/foreign-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/slots.h" |
| #include "src/snapshot/read-only-serializer.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| // The isolate roots may not point at context-specific objects during |
| // serialization. |
| class SanitizeIsolateScope final { |
| public: |
| SanitizeIsolateScope(Isolate* isolate, bool allow_active_isolate_for_testing, |
| const DisallowGarbageCollection& no_gc) |
| : isolate_(isolate), |
| feedback_vectors_for_profiling_tools_( |
| isolate->heap()->feedback_vectors_for_profiling_tools()), |
| detached_contexts_(isolate->heap()->detached_contexts()) { |
| #ifdef DEBUG |
| if (!allow_active_isolate_for_testing) { |
| // These should already be empty when creating a real snapshot. |
| DCHECK_EQ(feedback_vectors_for_profiling_tools_, |
| ReadOnlyRoots(isolate).undefined_value()); |
| DCHECK_EQ(detached_contexts_, |
| ReadOnlyRoots(isolate).empty_weak_array_list()); |
| } |
| #endif |
| |
| isolate->SetFeedbackVectorsForProfilingTools( |
| ReadOnlyRoots(isolate).undefined_value()); |
| isolate->heap()->SetDetachedContexts( |
| ReadOnlyRoots(isolate).empty_weak_array_list()); |
| } |
| |
| ~SanitizeIsolateScope() { |
| // Restore saved fields. |
| isolate_->SetFeedbackVectorsForProfilingTools( |
| feedback_vectors_for_profiling_tools_); |
| isolate_->heap()->SetDetachedContexts(detached_contexts_); |
| } |
| |
| private: |
| Isolate* isolate_; |
| const Object feedback_vectors_for_profiling_tools_; |
| const WeakArrayList detached_contexts_; |
| }; |
| |
| } // namespace |
| |
| StartupSerializer::StartupSerializer(Isolate* isolate, |
| Snapshot::SerializerFlags flags, |
| ReadOnlySerializer* read_only_serializer) |
| : RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot), |
| read_only_serializer_(read_only_serializer), |
| accessor_infos_(isolate->heap()), |
| call_handler_infos_(isolate->heap()) { |
| InitializeCodeAddressMap(); |
| } |
| |
| StartupSerializer::~StartupSerializer() { |
| for (Handle<AccessorInfo> info : accessor_infos_) { |
| RestoreExternalReferenceRedirector(isolate(), info); |
| } |
| for (Handle<CallHandlerInfo> info : call_handler_infos_) { |
| RestoreExternalReferenceRedirector(isolate(), info); |
| } |
| OutputStatistics("StartupSerializer"); |
| } |
| |
| #ifdef DEBUG |
| namespace { |
| |
| bool IsUnexpectedCodeObject(Isolate* isolate, HeapObject obj) { |
| if (!obj.IsCode()) return false; |
| |
| Code code = Code::cast(obj); |
| if (code.kind() == CodeKind::REGEXP) return false; |
| if (!code.is_builtin()) return true; |
| if (code.is_off_heap_trampoline()) return false; |
| |
| // An on-heap builtin. We only expect this for the interpreter entry |
| // trampoline copy stored on the root list and transitively called builtins. |
| // See Heap::interpreter_entry_trampoline_for_profiling. |
| |
| switch (code.builtin_index()) { |
| case Builtins::kAbort: |
| case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: |
| case Builtins::kInterpreterEntryTrampoline: |
| case Builtins::kRecordWrite: |
| return false; |
| default: |
| return true; |
| } |
| |
| UNREACHABLE(); |
| } |
| |
| } // namespace |
| #endif // DEBUG |
| |
| void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) { |
| #ifdef DEBUG |
| if (obj->IsJSFunction()) { |
| v8::base::OS::PrintError("Reference stack:\n"); |
| PrintStack(std::cerr); |
| obj->Print(std::cerr); |
| FATAL( |
| "JSFunction should be added through the context snapshot instead of " |
| "the isolate snapshot"); |
| } |
| #endif // DEBUG |
| DCHECK(!IsUnexpectedCodeObject(isolate(), *obj)); |
| |
| if (SerializeHotObject(obj)) return; |
| if (IsRootAndHasBeenSerialized(*obj) && SerializeRoot(obj)) return; |
| if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return; |
| if (SerializeBackReference(obj)) return; |
| |
| bool use_simulator = false; |
| #ifdef USE_SIMULATOR |
| use_simulator = true; |
| #endif |
| |
| if (use_simulator && obj->IsAccessorInfo()) { |
| // Wipe external reference redirects in the accessor info. |
| Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(obj); |
| Address original_address = |
| Foreign::cast(info->getter()).foreign_address(isolate()); |
| Foreign::cast(info->js_getter()) |
| .set_foreign_address(isolate(), original_address); |
| accessor_infos_.Push(*info); |
| } else if (use_simulator && obj->IsCallHandlerInfo()) { |
| Handle<CallHandlerInfo> info = Handle<CallHandlerInfo>::cast(obj); |
| Address original_address = |
| Foreign::cast(info->callback()).foreign_address(isolate()); |
| Foreign::cast(info->js_callback()) |
| .set_foreign_address(isolate(), original_address); |
| call_handler_infos_.Push(*info); |
| } else if (obj->IsScript() && Handle<Script>::cast(obj)->IsUserJavaScript()) { |
| Handle<Script>::cast(obj)->set_context_data( |
| ReadOnlyRoots(isolate()).uninitialized_symbol()); |
| } else if (obj->IsSharedFunctionInfo()) { |
| // Clear inferred name for native functions. |
| Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(obj); |
| if (!shared->IsSubjectToDebugging() && shared->HasUncompiledData()) { |
| shared->uncompiled_data().set_inferred_name( |
| ReadOnlyRoots(isolate()).empty_string()); |
| } |
| } |
| |
| CheckRehashability(*obj); |
| |
| // Object has not yet been serialized. Serialize it here. |
| DCHECK(!ReadOnlyHeap::Contains(*obj)); |
| ObjectSerializer object_serializer(this, obj, &sink_); |
| object_serializer.Serialize(); |
| } |
| |
| void StartupSerializer::SerializeWeakReferencesAndDeferred() { |
| // This comes right after serialization of the context snapshot, where we |
| // add entries to the startup object cache of the startup snapshot. Add |
| // one entry with 'undefined' to terminate the startup object cache. |
| Object undefined = ReadOnlyRoots(isolate()).undefined_value(); |
| VisitRootPointer(Root::kStartupObjectCache, nullptr, |
| FullObjectSlot(&undefined)); |
| |
| SerializeStringTable(isolate()->string_table()); |
| |
| isolate()->heap()->IterateWeakRoots( |
| this, base::EnumSet<SkipRoot>{SkipRoot::kUnserializable}); |
| SerializeDeferredObjects(); |
| Pad(); |
| } |
| |
| void StartupSerializer::SerializeStringTable(StringTable* string_table) { |
| // A StringTable is serialized as: |
| // |
| // N : int |
| // string 1 |
| // string 2 |
| // ... |
| // string N |
| // |
| // Notably, the hashmap structure, including empty and deleted elements, is |
| // not serialized. |
| |
| sink_.PutInt(isolate()->string_table()->NumberOfElements(), |
| "String table number of elements"); |
| |
| // Custom RootVisitor which walks the string table, but only serializes the |
| // string entries. This is an inline class to be able to access the non-public |
| // SerializeObject method. |
| class StartupSerializerStringTableVisitor : public RootVisitor { |
| public: |
| explicit StartupSerializerStringTableVisitor(StartupSerializer* serializer) |
| : serializer_(serializer) {} |
| |
| void VisitRootPointers(Root root, const char* description, |
| FullObjectSlot start, FullObjectSlot end) override { |
| UNREACHABLE(); |
| } |
| |
| void VisitRootPointers(Root root, const char* description, |
| OffHeapObjectSlot start, |
| OffHeapObjectSlot end) override { |
| DCHECK_EQ(root, Root::kStringTable); |
| Isolate* isolate = serializer_->isolate(); |
| for (OffHeapObjectSlot current = start; current < end; ++current) { |
| Object obj = current.load(isolate); |
| if (obj.IsHeapObject()) { |
| DCHECK(obj.IsInternalizedString()); |
| serializer_->SerializeObject(handle(HeapObject::cast(obj), isolate)); |
| } |
| } |
| } |
| |
| private: |
| StartupSerializer* serializer_; |
| }; |
| |
| StartupSerializerStringTableVisitor string_table_visitor(this); |
| isolate()->string_table()->IterateElements(&string_table_visitor); |
| } |
| |
| void StartupSerializer::SerializeStrongReferences( |
| const DisallowGarbageCollection& no_gc) { |
| Isolate* isolate = this->isolate(); |
| // No active threads. |
| CHECK_NULL(isolate->thread_manager()->FirstThreadStateInUse()); |
| |
| SanitizeIsolateScope sanitize_isolate( |
| isolate, allow_active_isolate_for_testing(), no_gc); |
| |
| // Visit smi roots and immortal immovables first to make sure they end up in |
| // the first page. |
| isolate->heap()->IterateSmiRoots(this); |
| isolate->heap()->IterateRoots( |
| this, |
| base::EnumSet<SkipRoot>{SkipRoot::kUnserializable, SkipRoot::kWeak}); |
| } |
| |
| SerializedHandleChecker::SerializedHandleChecker(Isolate* isolate, |
| std::vector<Context>* contexts) |
| : isolate_(isolate) { |
| AddToSet(isolate->heap()->serialized_objects()); |
| for (auto const& context : *contexts) { |
| AddToSet(context.serialized_objects()); |
| } |
| } |
| |
| bool StartupSerializer::SerializeUsingReadOnlyObjectCache( |
| SnapshotByteSink* sink, Handle<HeapObject> obj) { |
| return read_only_serializer_->SerializeUsingReadOnlyObjectCache(sink, obj); |
| } |
| |
| void StartupSerializer::SerializeUsingStartupObjectCache( |
| SnapshotByteSink* sink, Handle<HeapObject> obj) { |
| int cache_index = SerializeInObjectCache(obj); |
| sink->Put(kStartupObjectCache, "StartupObjectCache"); |
| sink->PutInt(cache_index, "startup_object_cache_index"); |
| } |
| |
| void StartupSerializer::CheckNoDirtyFinalizationRegistries() { |
| Isolate* isolate = this->isolate(); |
| CHECK(isolate->heap()->dirty_js_finalization_registries_list().IsUndefined( |
| isolate)); |
| CHECK( |
| isolate->heap()->dirty_js_finalization_registries_list_tail().IsUndefined( |
| isolate)); |
| } |
| |
| void SerializedHandleChecker::AddToSet(FixedArray serialized) { |
| int length = serialized.length(); |
| for (int i = 0; i < length; i++) serialized_.insert(serialized.get(i)); |
| } |
| |
| void SerializedHandleChecker::VisitRootPointers(Root root, |
| const char* description, |
| FullObjectSlot start, |
| FullObjectSlot end) { |
| for (FullObjectSlot p = start; p < end; ++p) { |
| if (serialized_.find(*p) != serialized_.end()) continue; |
| PrintF("%s handle not serialized: ", |
| root == Root::kGlobalHandles ? "global" : "eternal"); |
| (*p).Print(); |
| PrintF("\n"); |
| ok_ = false; |
| } |
| } |
| |
| bool SerializedHandleChecker::CheckGlobalAndEternalHandles() { |
| isolate_->global_handles()->IterateAllRoots(this); |
| isolate_->eternal_handles()->IterateAllRoots(this); |
| return ok_; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |