blob: c325aea7e6b1db8e8baf9d4cc5e6f6bddb89e4d2 [file] [log] [blame]
// 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