| // 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.h" |
| #include "src/objects-inl.h" |
| #include "src/v8threads.h" |
| |
| #if V8_OS_STARBOARD |
| #include "src/poems.h" |
| #endif |
| |
| namespace v8 { |
| namespace internal { |
| |
| StartupSerializer::StartupSerializer( |
| Isolate* isolate, |
| v8::SnapshotCreator::FunctionCodeHandling function_code_handling) |
| : Serializer(isolate), |
| clear_function_code_(function_code_handling == |
| v8::SnapshotCreator::FunctionCodeHandling::kClear), |
| can_be_rehashed_(true) { |
| InitializeCodeAddressMap(); |
| } |
| |
| StartupSerializer::~StartupSerializer() { |
| RestoreExternalReferenceRedirectors(accessor_infos_); |
| RestoreExternalReferenceRedirectors(call_handler_infos_); |
| OutputStatistics("StartupSerializer"); |
| } |
| |
| void StartupSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, |
| WhereToPoint where_to_point, int skip) { |
| DCHECK(!ObjectIsBytecodeHandler(obj)); // Only referenced in dispatch table. |
| DCHECK(!obj->IsJSFunction()); |
| |
| if (clear_function_code() && obj->IsBytecodeArray()) { |
| obj = isolate()->heap()->undefined_value(); |
| } |
| |
| BuiltinReferenceSerializationMode mode = |
| clear_function_code() ? kCanonicalizeCompileLazy : kDefault; |
| if (SerializeBuiltinReference(obj, how_to_code, where_to_point, skip, mode)) { |
| return; |
| } |
| if (SerializeHotObject(obj, how_to_code, where_to_point, skip)) return; |
| |
| int root_index = root_index_map()->Lookup(obj); |
| // We can only encode roots as such if it has already been serialized. |
| // That applies to root indices below the wave front. |
| if (root_index != RootIndexMap::kInvalidRootIndex) { |
| if (root_has_been_serialized(root_index)) { |
| PutRoot(root_index, obj, how_to_code, where_to_point, skip); |
| return; |
| } |
| } |
| |
| if (SerializeBackReference(obj, how_to_code, where_to_point, skip)) return; |
| |
| FlushSkip(skip); |
| |
| if (isolate()->external_reference_redirector() && obj->IsAccessorInfo()) { |
| // Wipe external reference redirects in the accessor info. |
| AccessorInfo* info = AccessorInfo::cast(obj); |
| Address original_address = Foreign::cast(info->getter())->foreign_address(); |
| Foreign::cast(info->js_getter())->set_foreign_address(original_address); |
| accessor_infos_.push_back(info); |
| } else if (isolate()->external_reference_redirector() && |
| obj->IsCallHandlerInfo()) { |
| CallHandlerInfo* info = CallHandlerInfo::cast(obj); |
| Address original_address = |
| Foreign::cast(info->callback())->foreign_address(); |
| Foreign::cast(info->js_callback())->set_foreign_address(original_address); |
| call_handler_infos_.push_back(info); |
| } else if (obj->IsScript() && Script::cast(obj)->IsUserJavaScript()) { |
| Script::cast(obj)->set_context_data( |
| isolate()->heap()->uninitialized_symbol()); |
| } else if (obj->IsSharedFunctionInfo()) { |
| // Clear inferred name for native functions. |
| SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj); |
| if (!shared->IsSubjectToDebugging() && shared->HasInferredName()) { |
| shared->set_inferred_name(isolate()->heap()->empty_string()); |
| } |
| } |
| |
| CheckRehashability(obj); |
| |
| // Object has not yet been serialized. Serialize it here. |
| ObjectSerializer object_serializer(this, obj, &sink_, how_to_code, |
| where_to_point); |
| object_serializer.Serialize(); |
| } |
| |
| void StartupSerializer::SerializeWeakReferencesAndDeferred() { |
| // This comes right after serialization of the partial snapshot, where we |
| // add entries to the partial snapshot cache of the startup snapshot. Add |
| // one entry with 'undefined' to terminate the partial snapshot cache. |
| Object* undefined = isolate()->heap()->undefined_value(); |
| VisitRootPointer(Root::kPartialSnapshotCache, &undefined); |
| isolate()->heap()->IterateWeakRoots(this, VISIT_FOR_SERIALIZATION); |
| SerializeDeferredObjects(); |
| Pad(); |
| } |
| |
| int StartupSerializer::PartialSnapshotCacheIndex(HeapObject* heap_object) { |
| int index; |
| if (!partial_cache_index_map_.LookupOrInsert(heap_object, &index)) { |
| // This object is not part of the partial snapshot cache yet. Add it to the |
| // startup snapshot so we can refer to it via partial snapshot index from |
| // the partial snapshot. |
| VisitRootPointer(Root::kPartialSnapshotCache, |
| reinterpret_cast<Object**>(&heap_object)); |
| } |
| return index; |
| } |
| |
| void StartupSerializer::Synchronize(VisitorSynchronization::SyncTag tag) { |
| sink_.Put(kSynchronize, "Synchronize"); |
| } |
| |
| void StartupSerializer::SerializeStrongReferences() { |
| Isolate* isolate = this->isolate(); |
| // No active threads. |
| CHECK_NULL(isolate->thread_manager()->FirstThreadStateInUse()); |
| // No active or weak handles. |
| CHECK(isolate->handle_scope_implementer()->blocks()->empty()); |
| |
| // Visit smi roots. |
| // Clear the stack limits to make the snapshot reproducible. |
| // Reset it again afterwards. |
| isolate->heap()->ClearStackLimits(); |
| isolate->heap()->IterateSmiRoots(this); |
| isolate->heap()->SetStackLimits(); |
| // First visit immortal immovables to make sure they end up in the first page. |
| isolate->heap()->IterateStrongRoots(this, VISIT_FOR_SERIALIZATION); |
| } |
| |
| void StartupSerializer::VisitRootPointers(Root root, Object** start, |
| Object** end) { |
| if (start == isolate()->heap()->roots_array_start()) { |
| // Serializing the root list needs special handling: |
| // - The first pass over the root list only serializes immortal immovables. |
| // - The second pass over the root list serializes the rest. |
| // - Only root list elements that have been fully serialized can be |
| // referenced via as root by using kRootArray bytecodes. |
| int skip = 0; |
| for (Object** current = start; current < end; current++) { |
| int root_index = static_cast<int>(current - start); |
| if ((*current)->IsSmi()) { |
| FlushSkip(skip); |
| PutSmi(Smi::cast(*current)); |
| } else { |
| SerializeObject(HeapObject::cast(*current), kPlain, kStartOfObject, |
| skip); |
| } |
| root_has_been_serialized_.set(root_index); |
| skip = 0; |
| } |
| FlushSkip(skip); |
| } else { |
| Serializer::VisitRootPointers(root, start, end); |
| } |
| } |
| |
| void StartupSerializer::CheckRehashability(HeapObject* obj) { |
| if (!can_be_rehashed_) return; |
| if (!obj->NeedsRehashing()) return; |
| if (obj->CanBeRehashed()) return; |
| can_be_rehashed_ = false; |
| } |
| |
| bool StartupSerializer::MustBeDeferred(HeapObject* object) { |
| if (root_has_been_serialized_.test(Heap::kFreeSpaceMapRootIndex) && |
| root_has_been_serialized_.test(Heap::kOnePointerFillerMapRootIndex) && |
| root_has_been_serialized_.test(Heap::kTwoPointerFillerMapRootIndex)) { |
| // All required root objects are serialized, so any aligned objects can |
| // be saved without problems. |
| return false; |
| } |
| // Just defer everything except of Map objects until all required roots are |
| // serialized. Some objects may have special alignment requirements, that may |
| // not be fulfilled during deserialization until few first root objects are |
| // serialized. But we must serialize Map objects since deserializer checks |
| // that these root objects are indeed Maps. |
| return !object->IsMap(); |
| } |
| |
| SerializedHandleChecker::SerializedHandleChecker( |
| Isolate* isolate, std::vector<Context*>* contexts) |
| : isolate_(isolate) { |
| AddToSet(isolate->heap()->serialized_objects()); |
| for (auto const& context : *contexts) { |
| AddToSet(context->serialized_objects()); |
| } |
| } |
| |
| 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, Object** start, |
| Object** end) { |
| for (Object** p = start; p < end; p++) { |
| if (serialized_.find(*p) != serialized_.end()) continue; |
| PrintF("%s handle not serialized: ", |
| root == Root::kGlobalHandles ? "global" : "eternal"); |
| (*p)->Print(); |
| ok_ = false; |
| } |
| } |
| |
| bool SerializedHandleChecker::CheckGlobalAndEternalHandles() { |
| isolate_->global_handles()->IterateAllRoots(this); |
| isolate_->eternal_handles()->IterateAllRoots(this); |
| return ok_; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |