// 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/deserializer.h" #include "src/base/logging.h" #include "src/base/platform/wrappers.h" #include "src/codegen/assembler-inl.h" #include "src/common/assert-scope.h" #include "src/common/globals.h" #include "src/execution/isolate.h" #include "src/heap/heap-inl.h" #include "src/heap/heap-write-barrier-inl.h" #include "src/heap/heap-write-barrier.h" #include "src/heap/heap.h" #include "src/heap/local-heap-inl.h" #include "src/heap/read-only-heap.h" #include "src/interpreter/interpreter.h" #include "src/logging/local-logger.h" #include "src/logging/log.h" #include "src/objects/api-callbacks.h" #include "src/objects/backing-store.h" #include "src/objects/cell-inl.h" #include "src/objects/embedder-data-array-inl.h" #include "src/objects/hash-table.h" #include "src/objects/js-array-buffer-inl.h" #include "src/objects/js-array-inl.h" #include "src/objects/maybe-object.h" #include "src/objects/objects-body-descriptors-inl.h" #include "src/objects/objects.h" #include "src/objects/slots.h" #include "src/objects/string.h" #include "src/roots/roots.h" #include "src/sandbox/external-pointer.h" #include "src/snapshot/embedded/embedded-data-inl.h" #include "src/snapshot/references.h" #include "src/snapshot/serializer-deserializer.h" #include "src/snapshot/shared-heap-serializer.h" #include "src/snapshot/snapshot-data.h" #include "src/snapshot/snapshot.h" #include "src/tracing/trace-event.h" #include "src/tracing/traced-value.h" #include "src/utils/memcopy.h" namespace v8 { namespace internal { // A SlotAccessor for a slot in a HeapObject, which abstracts the slot // operations done by the deserializer in a way which is GC-safe. In particular, // rather than an absolute slot address, this accessor holds a Handle to the // HeapObject, which is updated if the HeapObject moves. class SlotAccessorForHeapObject { public: static SlotAccessorForHeapObject ForSlotIndex(Handle object, int index) { return SlotAccessorForHeapObject(object, index * kTaggedSize); } static SlotAccessorForHeapObject ForSlotOffset(Handle object, int offset) { return SlotAccessorForHeapObject(object, offset); } MaybeObjectSlot slot() const { return object_->RawMaybeWeakField(offset_); } Handle object() const { return object_; } int offset() const { return offset_; } // Writes the given value to this slot, optionally with an offset (e.g. for // repeat writes). Returns the number of slots written (which is one). int Write(MaybeObject value, int slot_offset = 0) { MaybeObjectSlot current_slot = slot() + slot_offset; current_slot.Relaxed_Store(value); WriteBarrier::Marking(*object_, current_slot, value); // No need for a generational write barrier. DCHECK(!Heap::InYoungGeneration(value)); return 1; } int Write(HeapObject value, HeapObjectReferenceType ref_type, int slot_offset = 0) { return Write(HeapObjectReference::From(value, ref_type), slot_offset); } int Write(Handle value, HeapObjectReferenceType ref_type, int slot_offset = 0) { return Write(*value, ref_type, slot_offset); } // Same as Write, but additionally with a generational barrier. int WriteWithGenerationalBarrier(MaybeObject value) { MaybeObjectSlot current_slot = slot(); current_slot.Relaxed_Store(value); WriteBarrier::Marking(*object_, current_slot, value); if (Heap::InYoungGeneration(value)) { GenerationalBarrier(*object_, current_slot, value); } return 1; } int WriteWithGenerationalBarrier(HeapObject value, HeapObjectReferenceType ref_type) { return WriteWithGenerationalBarrier( HeapObjectReference::From(value, ref_type)); } int WriteWithGenerationalBarrier(Handle value, HeapObjectReferenceType ref_type) { return WriteWithGenerationalBarrier(*value, ref_type); } private: SlotAccessorForHeapObject(Handle object, int offset) : object_(object), offset_(offset) {} const Handle object_; const int offset_; }; // A SlotAccessor for absolute full slot addresses. class SlotAccessorForRootSlots { public: explicit SlotAccessorForRootSlots(FullMaybeObjectSlot slot) : slot_(slot) {} FullMaybeObjectSlot slot() const { return slot_; } Handle object() const { UNREACHABLE(); } int offset() const { UNREACHABLE(); } // Writes the given value to this slot, optionally with an offset (e.g. for // repeat writes). Returns the number of slots written (which is one). int Write(MaybeObject value, int slot_offset = 0) { FullMaybeObjectSlot current_slot = slot() + slot_offset; current_slot.Relaxed_Store(value); return 1; } int Write(HeapObject value, HeapObjectReferenceType ref_type, int slot_offset = 0) { return Write(HeapObjectReference::From(value, ref_type), slot_offset); } int Write(Handle value, HeapObjectReferenceType ref_type, int slot_offset = 0) { return Write(*value, ref_type, slot_offset); } int WriteWithGenerationalBarrier(MaybeObject value) { return Write(value); } int WriteWithGenerationalBarrier(HeapObject value, HeapObjectReferenceType ref_type) { return WriteWithGenerationalBarrier( HeapObjectReference::From(value, ref_type)); } int WriteWithGenerationalBarrier(Handle value, HeapObjectReferenceType ref_type) { return WriteWithGenerationalBarrier(*value, ref_type); } private: const FullMaybeObjectSlot slot_; }; // A SlotAccessor for creating a Handle, which saves a Handle allocation when // a Handle already exists. template class SlotAccessorForHandle { public: SlotAccessorForHandle(Handle* handle, IsolateT* isolate) : handle_(handle), isolate_(isolate) {} MaybeObjectSlot slot() const { UNREACHABLE(); } Handle object() const { UNREACHABLE(); } int offset() const { UNREACHABLE(); } int Write(MaybeObject value, int slot_offset = 0) { UNREACHABLE(); } int Write(HeapObject value, HeapObjectReferenceType ref_type, int slot_offset = 0) { DCHECK_EQ(slot_offset, 0); DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); *handle_ = handle(value, isolate_); return 1; } int Write(Handle value, HeapObjectReferenceType ref_type, int slot_offset = 0) { DCHECK_EQ(slot_offset, 0); DCHECK_EQ(ref_type, HeapObjectReferenceType::STRONG); *handle_ = value; return 1; } int WriteWithGenerationalBarrier(HeapObject value, HeapObjectReferenceType ref_type) { return Write(value, ref_type); } int WriteWithGenerationalBarrier(Handle value, HeapObjectReferenceType ref_type) { return Write(value, ref_type); } private: Handle* handle_; IsolateT* isolate_; }; template template int Deserializer::WriteAddress(TSlot dest, Address value) { DCHECK(!next_reference_is_weak_); memcpy(dest.ToVoidPtr(), &value, kSystemPointerSize); STATIC_ASSERT(IsAligned(kSystemPointerSize, TSlot::kSlotDataSize)); return (kSystemPointerSize / TSlot::kSlotDataSize); } template template int Deserializer::WriteExternalPointer(TSlot dest, Address value, ExternalPointerTag tag) { DCHECK(!next_reference_is_weak_); DCHECK(IsAligned(kExternalPointerSize, TSlot::kSlotDataSize)); InitExternalPointerField(dest.address(), main_thread_isolate(), value, tag); return (kExternalPointerSize / TSlot::kSlotDataSize); } namespace { #ifdef DEBUG int GetNumApiReferences(Isolate* isolate) { int num_api_references = 0; // The read-only deserializer is run by read-only heap set-up before the // heap is fully set up. External reference table relies on a few parts of // this set-up (like old-space), so it may be uninitialized at this point. if (isolate->isolate_data()->external_reference_table()->is_initialized()) { // Count the number of external references registered through the API. if (isolate->api_external_references() != nullptr) { while (isolate->api_external_references()[num_api_references] != 0) { num_api_references++; } } } return num_api_references; } int GetNumApiReferences(LocalIsolate* isolate) { return 0; } #endif } // namespace template Deserializer::Deserializer(IsolateT* isolate, base::Vector payload, uint32_t magic_number, bool deserializing_user_code, bool can_rehash) : isolate_(isolate), source_(payload), magic_number_(magic_number), deserializing_user_code_(deserializing_user_code), should_rehash_((FLAG_rehash_snapshot && can_rehash) || deserializing_user_code) { DCHECK_NOT_NULL(isolate); isolate->RegisterDeserializerStarted(); // We start the indices here at 1, so that we can distinguish between an // actual index and an empty backing store (serialized as // kEmptyBackingStoreRefSentinel) in a deserialized object requiring fix-up. STATIC_ASSERT(kEmptyBackingStoreRefSentinel == 0); backing_stores_.push_back({}); #ifdef DEBUG num_api_references_ = GetNumApiReferences(isolate); #endif // DEBUG CHECK_EQ(magic_number_, SerializedData::kMagicNumber); } template void Deserializer::Rehash() { DCHECK(should_rehash()); for (Handle item : to_rehash_) { item->RehashBasedOnMap(isolate()); } } template Deserializer::~Deserializer() { #ifdef DEBUG // Do not perform checks if we aborted deserialization. if (source_.position() == 0) return; // Check that we only have padding bytes remaining. while (source_.HasMore()) DCHECK_EQ(kNop, source_.Get()); // Check that there are no remaining forward refs. DCHECK_EQ(num_unresolved_forward_refs_, 0); DCHECK(unresolved_forward_refs_.empty()); #endif // DEBUG isolate_->RegisterDeserializerFinished(); } // This is called on the roots. It is the driver of the deserialization // process. It is also called on the body of each function. template void Deserializer::VisitRootPointers(Root root, const char* description, FullObjectSlot start, FullObjectSlot end) { ReadData(FullMaybeObjectSlot(start), FullMaybeObjectSlot(end)); } template void Deserializer::Synchronize(VisitorSynchronization::SyncTag tag) { static const byte expected = kSynchronize; CHECK_EQ(expected, source_.Get()); } template void Deserializer::DeserializeDeferredObjects() { for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) { SnapshotSpace space = NewObject::Decode(code); ReadObject(space); } } template void Deserializer::LogNewMapEvents() { if (V8_LIKELY(!FLAG_log_maps)) return; DisallowGarbageCollection no_gc; for (Handle map : new_maps_) { DCHECK(FLAG_log_maps); LOG(isolate(), MapCreate(*map)); LOG(isolate(), MapDetails(*map)); } } template void Deserializer::WeakenDescriptorArrays() { DisallowGarbageCollection no_gc; Map descriptor_array_map = ReadOnlyRoots(isolate()).descriptor_array_map(); for (Handle descriptor_array : new_descriptor_arrays_) { DescriptorArray raw = *descriptor_array; DCHECK(raw.IsStrongDescriptorArray()); raw.set_map_safe_transition(descriptor_array_map); WriteBarrier::Marking(raw, raw.number_of_descriptors()); } } template void Deserializer::LogScriptEvents(Script script) { DisallowGarbageCollection no_gc; LOG(isolate(), ScriptEvent(Logger::ScriptEventType::kDeserialize, script.id())); LOG(isolate(), ScriptDetails(script)); } namespace { template uint32_t ComputeRawHashField(IsolateT* isolate, String string) { // Make sure raw_hash_field() is computed. string.EnsureHash(SharedStringAccessGuardIfNeeded(isolate)); return string.raw_hash_field(); } } // namespace StringTableInsertionKey::StringTableInsertionKey( Isolate* isolate, Handle string, DeserializingUserCodeOption deserializing_user_code) : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), string_(string) { #ifdef DEBUG deserializing_user_code_ = deserializing_user_code; #endif DCHECK(string->IsInternalizedString()); } StringTableInsertionKey::StringTableInsertionKey( LocalIsolate* isolate, Handle string, DeserializingUserCodeOption deserializing_user_code) : StringTableKey(ComputeRawHashField(isolate, *string), string->length()), string_(string) { #ifdef DEBUG deserializing_user_code_ = deserializing_user_code; #endif DCHECK(string->IsInternalizedString()); } template bool StringTableInsertionKey::IsMatch(IsolateT* isolate, String string) { // We want to compare the content of two strings here. return string_->SlowEquals(string, SharedStringAccessGuardIfNeeded(isolate)); } template bool StringTableInsertionKey::IsMatch(Isolate* isolate, String string); template bool StringTableInsertionKey::IsMatch(LocalIsolate* isolate, String string); namespace { void NoExternalReferencesCallback() { // The following check will trigger if a function or object template // with references to native functions have been deserialized from // snapshot, but no actual external references were provided when the // isolate was created. FATAL("No external references provided via API"); } void PostProcessExternalString(ExternalString string, Isolate* isolate) { DisallowGarbageCollection no_gc; uint32_t index = string.GetResourceRefForDeserialization(); Address address = static_cast
(isolate->api_external_references()[index]); string.AllocateExternalPointerEntries(isolate); string.set_address_as_resource(isolate, address); isolate->heap()->UpdateExternalString(string, 0, string.ExternalPayloadSize()); isolate->heap()->RegisterExternalString(string); } } // namespace template void Deserializer::PostProcessNewJSReceiver( Map map, Handle obj, JSReceiver raw_obj, InstanceType instance_type, SnapshotSpace space) { DisallowGarbageCollection no_gc; DCHECK_EQ(*obj, raw_obj); DCHECK_EQ(raw_obj.map(), map); DCHECK_EQ(map.instance_type(), instance_type); if (InstanceTypeChecker::IsJSDataView(instance_type)) { auto data_view = JSDataView::cast(raw_obj); auto buffer = JSArrayBuffer::cast(data_view.buffer()); void* backing_store = EmptyBackingStoreBuffer(); uint32_t store_index = buffer.GetBackingStoreRefForDeserialization(); if (store_index != kEmptyBackingStoreRefSentinel) { // The backing store of the JSArrayBuffer has not been correctly restored // yet, as that may trigger GC. The backing_store field currently contains // a numbered reference to an already deserialized backing store. backing_store = backing_stores_[store_index]->buffer_start(); } data_view.set_data_pointer( main_thread_isolate(), reinterpret_cast(backing_store) + data_view.byte_offset()); } else if (InstanceTypeChecker::IsJSTypedArray(instance_type)) { auto typed_array = JSTypedArray::cast(raw_obj); // Note: ByteArray objects must not be deferred s.t. they are // available here for is_on_heap(). See also: CanBeDeferred. // Fixup typed array pointers. if (typed_array.is_on_heap()) { typed_array.AddExternalPointerCompensationForDeserialization( main_thread_isolate()); } else { // Serializer writes backing store ref as a DataPtr() value. uint32_t store_index = typed_array.GetExternalBackingStoreRefForDeserialization(); auto backing_store = backing_stores_[store_index]; void* start = backing_store ? backing_store->buffer_start() : EmptyBackingStoreBuffer(); typed_array.SetOffHeapDataPtr(main_thread_isolate(), start, typed_array.byte_offset()); } } else if (InstanceTypeChecker::IsJSArrayBuffer(instance_type)) { auto buffer = JSArrayBuffer::cast(raw_obj); // Postpone allocation of backing store to avoid triggering the GC. if (buffer.GetBackingStoreRefForDeserialization() != kEmptyBackingStoreRefSentinel) { new_off_heap_array_buffers_.push_back(Handle::cast(obj)); } else { buffer.set_backing_store(main_thread_isolate(), EmptyBackingStoreBuffer()); } } // Check alignment. DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), HeapObject::RequiredAlignment(map))); } template void Deserializer::PostProcessNewObject(Handle map, Handle obj, SnapshotSpace space) { DisallowGarbageCollection no_gc; Map raw_map = *map; DCHECK_EQ(raw_map, obj->map(isolate_)); InstanceType instance_type = raw_map.instance_type(); // Check alignment. DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), HeapObject::RequiredAlignment(raw_map))); HeapObject raw_obj = *obj; DCHECK_IMPLIES(deserializing_user_code(), should_rehash()); if (should_rehash()) { if (InstanceTypeChecker::IsString(instance_type)) { // Uninitialize hash field as we need to recompute the hash. String string = String::cast(raw_obj); string.set_raw_hash_field(String::kEmptyHashField); // Rehash strings before read-only space is sealed. Strings outside // read-only space are rehashed lazily. (e.g. when rehashing dictionaries) if (space == SnapshotSpace::kReadOnlyHeap) { to_rehash_.push_back(obj); } } else if (raw_obj.NeedsRehashing(instance_type)) { to_rehash_.push_back(obj); } if (deserializing_user_code()) { if (InstanceTypeChecker::IsInternalizedString(instance_type)) { // Canonicalize the internalized string. If it already exists in the // string table, set the string to point to the existing one and patch // the deserialized string handle to point to the existing one. // TODO(leszeks): This handle patching is ugly, consider adding an // explicit internalized string bytecode. Also, the new thin string // should be dead, try immediately freeing it. Handle string = Handle::cast(obj); StringTableInsertionKey key( isolate(), string, DeserializingUserCodeOption::kIsDeserializingUserCode); String result = *isolate()->string_table()->LookupKey(isolate(), &key); if (result != raw_obj) { String::cast(raw_obj).MakeThin(isolate(), result); // Mutate the given object handle so that the backreference entry is // also updated. obj.PatchValue(result); } return; } else if (InstanceTypeChecker::IsScript(instance_type)) { new_scripts_.push_back(Handle