#ifndef V8_SNAPSHOT_SERIALIZER_H_
#define V8_SNAPSHOT_SERIALIZER_H_
#include "src/codegen/external-reference-encoder.h"
#include "src/common/assert-scope.h"
#include "src/execution/isolate.h"
#include "src/handles/global-handles.h"
#include "src/logging/log.h"
#include "src/objects/abstract-code.h"
#include "src/objects/bytecode-array.h"
#include "src/objects/instruction-stream.h"
#include "src/objects/objects.h"
#include "src/snapshot/serializer-deserializer.h"
#include "src/snapshot/snapshot-source-sink.h"
#include "src/snapshot/snapshot.h"
#include "src/utils/identity-map.h"
namespace v8 {
namespace internal {
class CodeAddressMap : public CodeEventLogger {
public:
explicit CodeAddressMap(Isolate* isolate) : CodeEventLogger(isolate) {
CHECK(isolate->logger()->AddListener(this));
}
~CodeAddressMap() override {
CHECK(isolate_->logger()->RemoveListener(this));
}
void CodeMoveEvent(Tagged<InstructionStream> from,
Tagged<InstructionStream> to) override {
address_to_name_map_.Move(from.address(), to.address());
}
void BytecodeMoveEvent(Tagged<BytecodeArray> from,
Tagged<BytecodeArray> to) override {
address_to_name_map_.Move(from.address(), to.address());
}
void CodeDisableOptEvent(DirectHandle<AbstractCode> code,
DirectHandle<SharedFunctionInfo> shared) override {}
const char* Lookup(Address address) {
return address_to_name_map_.Lookup(address);
}
private:
class NameMap {
public:
NameMap() : impl_() {}
NameMap(const NameMap&) = delete;
NameMap& operator=(const NameMap&) = delete;
~NameMap() {
for (base::HashMap::Entry* p = impl_.Start(); p != nullptr;
p = impl_.Next(p)) {
DeleteArray(static_cast<const char*>(p->value));
}
}
void Insert(Address code_address, const char* name, size_t name_size) {
base::HashMap::Entry* entry = FindOrCreateEntry(code_address);
if (entry->value == nullptr) {
entry->value = CopyName(name, name_size);
}
}
const char* Lookup(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
return (entry != nullptr) ? static_cast<const char*>(entry->value)
: nullptr;
}
void Remove(Address code_address) {
base::HashMap::Entry* entry = FindEntry(code_address);
if (entry != nullptr) {
DeleteArray(static_cast<char*>(entry->value));
RemoveEntry(entry);
}
}
void Move(Address from, Address to) {
if (from == to) return;
base::HashMap::Entry* from_entry = FindEntry(from);
DCHECK_NOT_NULL(from_entry);
void* value = from_entry->value;
RemoveEntry(from_entry);
base::HashMap::Entry* to_entry = FindOrCreateEntry(to);
DCHECK_NULL(to_entry->value);
to_entry->value = value;
}
private:
static char* CopyName(const char* name, size_t name_size) {
char* result = NewArray<char>(name_size + 1);
for (size_t i = 0; i < name_size; ++i) {
char c = name[i];
if (c == '\0') c = ' ';
result[i] = c;
}
result[name_size] = '\0';
return result;
}
base::HashMap::Entry* FindOrCreateEntry(Address code_address) {
return impl_.LookupOrInsert(reinterpret_cast<void*>(code_address),
ComputeAddressHash(code_address));
}
base::HashMap::Entry* FindEntry(Address code_address) {
return impl_.Lookup(reinterpret_cast<void*>(code_address),
ComputeAddressHash(code_address));
}
void RemoveEntry(base::HashMap::Entry* entry) {
impl_.Remove(entry->key, entry->hash);
}
base::HashMap impl_;
};
void LogRecordedBuffer(Tagged<AbstractCode> code,
MaybeDirectHandle<SharedFunctionInfo>,
const char* name, size_t length) override {
DisallowGarbageCollection no_gc;
address_to_name_map_.Insert(code.address(), name, length);
}
#if V8_ENABLE_WEBASSEMBLY
void LogRecordedBuffer(const wasm::WasmCode* code, const char* name,
size_t length) override {
UNREACHABLE();
}
#endif
NameMap address_to_name_map_;
};
class ObjectCacheIndexMap {
public:
explicit ObjectCacheIndexMap(Heap* heap) : map_(heap), next_index_(0) {}
ObjectCacheIndexMap(const ObjectCacheIndexMap&) = delete;
ObjectCacheIndexMap& operator=(const ObjectCacheIndexMap&) = delete;
bool LookupOrInsert(Tagged<HeapObject> obj, int* index_out) {
auto find_result = map_.FindOrInsert(obj);
if (!find_result.already_exists) {
*find_result.entry = next_index_++;
}
*index_out = *find_result.entry;
return find_result.already_exists;
}
bool LookupOrInsert(DirectHandle<HeapObject> obj, int* index_out) {
return LookupOrInsert(*obj, index_out);
}
bool Lookup(Tagged<HeapObject> obj, int* index_out) const {
int* index = map_.Find(obj);
if (index == nullptr) {
return false;
}
*index_out = *index;
return true;
}
DirectHandle<FixedArray> Values(Isolate* isolate);
int size() const { return next_index_; }
private:
IdentityMap<int, base::DefaultAllocationPolicy> map_;
int next_index_;
};
class Serializer : public SerializerDeserializer {
public:
Serializer(Isolate* isolate, Snapshot::SerializerFlags flags);
~Serializer() override { DCHECK_EQ(unresolved_forward_refs_, 0); }
Serializer(const Serializer&) = delete;
Serializer& operator=(const Serializer&) = delete;
const std::vector<uint8_t>* Payload() const { return sink_.data(); }
bool ReferenceMapContains(DirectHandle<HeapObject> o) {
return reference_map()->LookupReference(o) != nullptr;
}
Isolate* isolate() const { return isolate_; }
PtrComprCageBase cage_base() const {
#if V8_COMPRESS_POINTERS
return cage_base_;
#else
return PtrComprCageBase{};
#endif
}
int TotalAllocationSize() const;
protected:
using PendingObjectReferences = std::vector<int>*;
class ObjectSerializer;
class V8_NODISCARD RecursionScope {
public:
explicit RecursionScope(Serializer* serializer) : serializer_(serializer) {
serializer_->recursion_depth_++;
}
~RecursionScope() { serializer_->recursion_depth_--; }
bool ExceedsMaximum() const {
return serializer_->recursion_depth_ > kMaxRecursionDepth;
}
int ExceedsMaximumBy() const {
return serializer_->recursion_depth_ - kMaxRecursionDepth;
}
private:
static const int kMaxRecursionDepth = 32;
Serializer* serializer_;
};
V8_INLINE bool IsNotMappedSymbol(Tagged<HeapObject> obj) const;
void SerializeDeferredObjects();
void SerializeObject(Handle<HeapObject> o, SlotType slot_type);
virtual void SerializeObjectImpl(Handle<HeapObject> o,
SlotType slot_type) = 0;
virtual bool MustBeDeferred(Tagged<HeapObject> object);
void VisitRootPointers(Root root, const char* description,
FullObjectSlot start, FullObjectSlot end) override;
void SerializeRootObject(FullObjectSlot slot);
void PutRoot(RootIndex root_index);
void PutSmiRoot(FullObjectSlot slot);
void PutBackReference(Tagged<HeapObject> object,
SerializerReference reference);
void PutAttachedReference(SerializerReference reference);
void PutNextChunk(SnapshotSpace space);
void PutRepeatRoot(int repeat_count, RootIndex root_index);
void PutPendingForwardReference(PendingObjectReferences& ref);
void ResolvePendingForwardReference(int obj);
bool SerializeRoot(Tagged<HeapObject> obj);
bool SerializeHotObject(Tagged<HeapObject> obj);
bool SerializeBackReference(Tagged<HeapObject> obj);
bool SerializePendingObject(Tagged<HeapObject> obj);
bool ObjectIsBytecodeHandler(Tagged<HeapObject> obj) const;
ExternalReferenceEncoder::Value EncodeExternalReference(Address addr);
Maybe<ExternalReferenceEncoder::Value> TryEncodeExternalReference(
Address addr) {
return external_reference_encoder_.TryEncode(addr);
}
bool SerializeReadOnlyObjectReference(Tagged<HeapObject> obj,
SnapshotByteSink* sink);
void Pad(int padding_offset = 0);
void InitializeCodeAddressMap();
Tagged<InstructionStream> CopyCode(Tagged<InstructionStream> istream);
void QueueDeferredObject(Tagged<HeapObject> obj) {
DCHECK_NULL(reference_map_.LookupReference(obj));
deferred_objects_.Push(obj);
}
void RegisterObjectIsPending(Tagged<HeapObject> obj);
void ResolvePendingObject(Tagged<HeapObject> obj);
void OutputStatistics(const char* name);
void CountAllocation(Tagged<Map> map, int size, SnapshotSpace space);
#ifdef DEBUG
void PushStack(DirectHandle<HeapObject> o) { stack_.Push(*o); }
void PopStack();
void PrintStack();
void PrintStack(std::ostream&);
#endif
SerializerReferenceMap* reference_map() { return &reference_map_; }
const RootIndexMap* root_index_map() const { return &root_index_map_; }
SnapshotByteSink sink_;
bool allow_unknown_external_references_for_testing() const {
return (flags_ & Snapshot::kAllowUnknownExternalReferencesForTesting) != 0;
}
bool allow_active_isolate_for_testing() const {
return (flags_ & Snapshot::kAllowActiveIsolateForTesting) != 0;
}
bool reconstruct_read_only_and_shared_object_caches_for_testing() const {
return (flags_ &
Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting) != 0;
}
bool deferred_objects_empty() { return deferred_objects_.size() == 0; }
protected:
bool serializer_tracks_serialization_statistics() const {
return serializer_tracks_serialization_statistics_;
}
void set_serializer_tracks_serialization_statistics(bool v) {
serializer_tracks_serialization_statistics_ = v;
}
private:
class HotObjectsList {
public:
explicit HotObjectsList(Heap* heap);
~HotObjectsList();
HotObjectsList(const HotObjectsList&) = delete;
HotObjectsList& operator=(const HotObjectsList&) = delete;
void Add(Tagged<HeapObject> object) {
circular_queue_[index_] = object.ptr();
index_ = (index_ + 1) & kSizeMask;
}
static const int kNotFound = -1;
int Find(Tagged<HeapObject> object) {
DCHECK(!AllowGarbageCollection::IsAllowed());
for (int i = 0; i < kSize; i++) {
if (circular_queue_[i] == object.ptr()) {
return i;
}
}
return kNotFound;
}
private:
static const int kSize = kHotObjectCount;
static const int kSizeMask = kSize - 1;
static_assert(base::bits::IsPowerOfTwo(kSize));
Heap* heap_;
StrongRootsEntry* strong_roots_entry_;
Address circular_queue_[kSize] = {kNullAddress};
int index_ = 0;
};
DISALLOW_GARBAGE_COLLECTION(no_gc_)
Isolate* isolate_;
#if V8_COMPRESS_POINTERS
const PtrComprCageBase cage_base_;
#endif
HotObjectsList hot_objects_;
SerializerReferenceMap reference_map_;
ExternalReferenceEncoder external_reference_encoder_;
RootIndexMap root_index_map_;
std::unique_ptr<CodeAddressMap> code_address_map_;
std::vector<uint8_t> code_buffer_;
GlobalHandleVector<HeapObject>
deferred_objects_;
int num_back_refs_ = 0;
std::unordered_map<JSDispatchHandle, uint32_t> dispatch_handle_map_;
int next_forward_ref_id_ = 0;
int unresolved_forward_refs_ = 0;
IdentityMap<PendingObjectReferences, base::DefaultAllocationPolicy>
forward_refs_per_pending_object_;
uint32_t seen_backing_stores_index_ = 1;
int recursion_depth_ = 0;
const Snapshot::SerializerFlags flags_;
bool serializer_tracks_serialization_statistics_ = true;
size_t allocation_size_[kNumberOfSnapshotSpaces] = {0};
#ifdef OBJECT_PRINT
#define VERBOSE_SERIALIZATION_STATISTICS
#endif
#ifdef VERBOSE_SERIALIZATION_STATISTICS
static constexpr int kInstanceTypes = LAST_TYPE + 1;
std::unique_ptr<int[]> instance_type_count_[kNumberOfSnapshotSpaces];
std::unique_ptr<size_t[]> instance_type_size_[kNumberOfSnapshotSpaces];
#endif
#ifdef DEBUG
GlobalHandleVector<HeapObject> back_refs_;
GlobalHandleVector<HeapObject> stack_;
#endif
};
class Serializer::ObjectSerializer : public ObjectVisitor {
public:
ObjectSerializer(Serializer* serializer, Handle<HeapObject> obj,
SnapshotByteSink* sink)
: isolate_(serializer->isolate()),
serializer_(serializer),
object_(obj),
sink_(sink),
bytes_processed_so_far_(0) {
#ifdef DEBUG
serializer_->PushStack(obj);
#endif
}
~ObjectSerializer() override {
#ifdef DEBUG
serializer_->PopStack();
#endif
}
void Serialize(SlotType slot_type);
void SerializeObject();
void SerializeDeferred();
void VisitPointers(Tagged<HeapObject> host, ObjectSlot start,
ObjectSlot end) override;
void VisitPointers(Tagged<HeapObject> host, MaybeObjectSlot start,
MaybeObjectSlot end) override;
void VisitInstructionStreamPointer(Tagged<Code> host,
InstructionStreamSlot slot) override;
void VisitEmbeddedPointer(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitExternalReference(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitInternalReference(Tagged<InstructionStream> host,
RelocInfo* rinfo) override;
void VisitCodeTarget(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitOffHeapTarget(Tagged<InstructionStream> host,
RelocInfo* target) override;
void VisitExternalPointer(Tagged<HeapObject> host,
ExternalPointerSlot slot) override;
void VisitIndirectPointer(Tagged<HeapObject> host, IndirectPointerSlot slot,
IndirectPointerMode mode) override;
void VisitTrustedPointerTableEntry(Tagged<HeapObject> host,
IndirectPointerSlot slot) override;
void VisitProtectedPointer(Tagged<TrustedObject> host,
ProtectedPointerSlot slot) override;
void VisitProtectedPointer(Tagged<TrustedObject> host,
ProtectedMaybeObjectSlot slot) override;
void VisitCppHeapPointer(Tagged<HeapObject> host,
CppHeapPointerSlot slot) override;
void VisitJSDispatchTableEntry(Tagged<HeapObject> host,
JSDispatchHandle handle) override;
Isolate* isolate() { return isolate_; }
private:
void SerializePrologue(SnapshotSpace space, int size, Tagged<Map> map);
void SerializeContent(Tagged<Map> map, int size);
void OutputExternalReference(Address target, int target_size, bool sandboxify,
ExternalPointerTag tag);
void OutputRawData(Address up_to);
uint32_t SerializeBackingStore(void* backing_store, uint32_t byte_length,
Maybe<uint32_t> max_byte_length);
void SerializeJSTypedArray();
void SerializeJSArrayBuffer();
void SerializeNativeContext();
void SerializeExternalString();
void SerializeExternalStringAsSequentialString();
Isolate* isolate_;
Serializer* serializer_;
Handle<HeapObject> object_;
SnapshotByteSink* sink_;
int bytes_processed_so_far_;
};
}
}
#endif