// 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" #include "src/snapshot/shared-heap-serializer.h" namespace v8 { namespace internal { namespace { // The isolate roots may not point at context-specific objects during // serialization. class V8_NODISCARD 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, SharedHeapSerializer* shared_heap_serializer) : RootsSerializer(isolate, flags, RootIndex::kFirstStrongRoot), read_only_serializer_(read_only_serializer), shared_heap_serializer_(shared_heap_serializer), accessor_infos_(isolate->heap()), call_handler_infos_(isolate->heap()) { InitializeCodeAddressMap(); } StartupSerializer::~StartupSerializer() { for (Handle info : accessor_infos_) { RestoreExternalReferenceRedirector(isolate(), *info); } for (Handle 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_id()) { case Builtin::kAbort: case Builtin::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: case Builtin::kInterpreterEntryTrampoline: case Builtin::kRecordWriteEmitRememberedSetSaveFP: case Builtin::kRecordWriteOmitRememberedSetSaveFP: case Builtin::kRecordWriteEmitRememberedSetIgnoreFP: case Builtin::kRecordWriteOmitRememberedSetIgnoreFP: #ifdef V8_IS_TSAN case Builtin::kTSANRelaxedStore8IgnoreFP: case Builtin::kTSANRelaxedStore8SaveFP: case Builtin::kTSANRelaxedStore16IgnoreFP: case Builtin::kTSANRelaxedStore16SaveFP: case Builtin::kTSANRelaxedStore32IgnoreFP: case Builtin::kTSANRelaxedStore32SaveFP: case Builtin::kTSANRelaxedStore64IgnoreFP: case Builtin::kTSANRelaxedStore64SaveFP: case Builtin::kTSANRelaxedLoad32IgnoreFP: case Builtin::kTSANRelaxedLoad32SaveFP: case Builtin::kTSANRelaxedLoad64IgnoreFP: case Builtin::kTSANRelaxedLoad64SaveFP: #endif // V8_IS_TSAN return false; default: return true; } UNREACHABLE(); } } // namespace #endif // DEBUG void StartupSerializer::SerializeObjectImpl(Handle obj) { PtrComprCageBase cage_base(isolate()); #ifdef DEBUG if (obj->IsJSFunction(cage_base)) { 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 { DisallowGarbageCollection no_gc; HeapObject raw = *obj; DCHECK(!IsUnexpectedCodeObject(isolate(), raw)); if (SerializeHotObject(raw)) return; if (IsRootAndHasBeenSerialized(raw) && SerializeRoot(raw)) return; } if (SerializeUsingReadOnlyObjectCache(&sink_, obj)) return; if (SerializeUsingSharedHeapObjectCache(&sink_, obj)) return; if (SerializeBackReference(*obj)) return; bool use_simulator = false; #ifdef USE_SIMULATOR use_simulator = true; #endif if (use_simulator && obj->IsAccessorInfo(cage_base)) { // Wipe external reference redirects in the accessor info. Handle info = Handle::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(cage_base)) { Handle info = Handle::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(cage_base) && Handle