| // Copyright 2019 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/heap/read-only-heap.h" |
| |
| #include <cstring> |
| |
| #include "src/base/lsan.h" |
| #include "src/base/once.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/heap-write-barrier-inl.h" |
| #include "src/heap/spaces.h" |
| #include "src/objects/heap-object-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/smi.h" |
| #include "src/snapshot/read-only-deserializer.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| #ifdef V8_SHARED_RO_HEAP |
| V8_DECLARE_ONCE(setup_ro_heap_once); |
| ReadOnlyHeap* ReadOnlyHeap::shared_ro_heap_ = nullptr; |
| #endif |
| |
| // static |
| void ReadOnlyHeap::SetUp(Isolate* isolate, ReadOnlyDeserializer* des) { |
| DCHECK_NOT_NULL(isolate); |
| #ifdef V8_SHARED_RO_HEAP |
| bool call_once_ran = false; |
| base::Optional<Checksum> des_checksum; |
| #ifdef DEBUG |
| if (des != nullptr) des_checksum = des->GetChecksum(); |
| #endif // DEBUG |
| |
| base::CallOnce(&setup_ro_heap_once, |
| [isolate, des, des_checksum, &call_once_ran]() { |
| USE(des_checksum); |
| shared_ro_heap_ = CreateAndAttachToIsolate(isolate); |
| if (des != nullptr) { |
| #ifdef DEBUG |
| shared_ro_heap_->read_only_blob_checksum_ = des_checksum; |
| #endif // DEBUG |
| shared_ro_heap_->DeseralizeIntoIsolate(isolate, des); |
| } |
| call_once_ran = true; |
| }); |
| |
| USE(call_once_ran); |
| USE(des_checksum); |
| #ifdef DEBUG |
| const base::Optional<Checksum> last_checksum = |
| shared_ro_heap_->read_only_blob_checksum_; |
| if (last_checksum || des_checksum) { |
| // The read-only heap was set up from a snapshot. Make sure it's the always |
| // the same snapshot. |
| CHECK_EQ(last_checksum, des_checksum); |
| } else { |
| // The read-only heap objects were created. Make sure this happens only |
| // once, during this call. |
| CHECK(call_once_ran); |
| } |
| #endif // DEBUG |
| |
| isolate->SetUpFromReadOnlyHeap(shared_ro_heap_); |
| if (des != nullptr) { |
| void* const isolate_ro_roots = reinterpret_cast<void*>( |
| isolate->roots_table().read_only_roots_begin().address()); |
| std::memcpy(isolate_ro_roots, shared_ro_heap_->read_only_roots_, |
| kEntriesCount * sizeof(Address)); |
| } |
| #else |
| auto* ro_heap = CreateAndAttachToIsolate(isolate); |
| if (des != nullptr) ro_heap->DeseralizeIntoIsolate(isolate, des); |
| #endif // V8_SHARED_RO_HEAP |
| } |
| |
| void ReadOnlyHeap::DeseralizeIntoIsolate(Isolate* isolate, |
| ReadOnlyDeserializer* des) { |
| DCHECK_NOT_NULL(des); |
| des->DeserializeInto(isolate); |
| InitFromIsolate(isolate); |
| } |
| |
| void ReadOnlyHeap::OnCreateHeapObjectsComplete(Isolate* isolate) { |
| DCHECK_NOT_NULL(isolate); |
| InitFromIsolate(isolate); |
| } |
| |
| // static |
| ReadOnlyHeap* ReadOnlyHeap::CreateAndAttachToIsolate(Isolate* isolate) { |
| auto* ro_heap = new ReadOnlyHeap(new ReadOnlySpace(isolate->heap())); |
| isolate->SetUpFromReadOnlyHeap(ro_heap); |
| return ro_heap; |
| } |
| |
| void ReadOnlyHeap::InitFromIsolate(Isolate* isolate) { |
| DCHECK(!init_complete_); |
| #ifdef V8_SHARED_RO_HEAP |
| void* const isolate_ro_roots = reinterpret_cast<void*>( |
| isolate->roots_table().read_only_roots_begin().address()); |
| std::memcpy(read_only_roots_, isolate_ro_roots, |
| kEntriesCount * sizeof(Address)); |
| // N.B. Since pages are manually allocated with mmap, Lsan doesn't track |
| // their pointers. Seal explicitly ignores the necessary objects. |
| LSAN_IGNORE_OBJECT(this); |
| read_only_space_->Seal(ReadOnlySpace::SealMode::kDetachFromHeapAndForget); |
| #else |
| read_only_space_->Seal(ReadOnlySpace::SealMode::kDoNotDetachFromHeap); |
| #endif |
| init_complete_ = true; |
| } |
| |
| void ReadOnlyHeap::OnHeapTearDown() { |
| #ifndef V8_SHARED_RO_HEAP |
| delete read_only_space_; |
| delete this; |
| #endif |
| } |
| |
| // static |
| void ReadOnlyHeap::ClearSharedHeapForTest() { |
| #ifdef V8_SHARED_RO_HEAP |
| DCHECK_NOT_NULL(shared_ro_heap_); |
| // TODO(v8:7464): Just leak read-only space for now. The paged-space heap |
| // is null so there isn't a nice way to do this. |
| shared_ro_heap_ = nullptr; |
| setup_ro_heap_once = 0; |
| #endif |
| } |
| |
| // static |
| bool ReadOnlyHeap::Contains(HeapObject object) { |
| return MemoryChunk::FromHeapObject(object)->InReadOnlySpace(); |
| } |
| |
| Object* ReadOnlyHeap::ExtendReadOnlyObjectCache() { |
| read_only_object_cache_.push_back(Smi::kZero); |
| return &read_only_object_cache_.back(); |
| } |
| |
| Object ReadOnlyHeap::cached_read_only_object(size_t i) const { |
| DCHECK_LE(i, read_only_object_cache_.size()); |
| return read_only_object_cache_[i]; |
| } |
| |
| bool ReadOnlyHeap::read_only_object_cache_is_initialized() const { |
| return read_only_object_cache_.size() > 0; |
| } |
| |
| ReadOnlyHeapObjectIterator::ReadOnlyHeapObjectIterator(ReadOnlyHeap* ro_heap) |
| : ReadOnlyHeapObjectIterator(ro_heap->read_only_space()) {} |
| |
| ReadOnlyHeapObjectIterator::ReadOnlyHeapObjectIterator(ReadOnlySpace* ro_space) |
| : ro_space_(ro_space), |
| current_page_(ro_space->first_page()), |
| current_addr_(current_page_->area_start()) {} |
| |
| HeapObject ReadOnlyHeapObjectIterator::Next() { |
| if (current_page_ == nullptr) { |
| return HeapObject(); |
| } |
| |
| for (;;) { |
| DCHECK_LE(current_addr_, current_page_->area_end()); |
| if (current_addr_ == current_page_->area_end()) { |
| // Progress to the next page. |
| current_page_ = current_page_->next_page(); |
| if (current_page_ == nullptr) { |
| return HeapObject(); |
| } |
| current_addr_ = current_page_->area_start(); |
| } |
| |
| if (current_addr_ == ro_space_->top() && |
| current_addr_ != ro_space_->limit()) { |
| current_addr_ = ro_space_->limit(); |
| continue; |
| } |
| HeapObject object = HeapObject::FromAddress(current_addr_); |
| const int object_size = object.Size(); |
| current_addr_ += object_size; |
| |
| if (object.IsFiller()) { |
| continue; |
| } |
| |
| DCHECK_OBJECT_SIZE(object_size); |
| return object; |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |