57b27a7b44
- Updated conditional compilation flags throughout the runtime codebase to transition from DART_DYNAMIC_MODULES to DART_BYTECODE_INTERPRETER. - Adjusted logic in various files including object_graph_copy.cc, object_reload.cc, profiler.cc, and others to ensure compatibility with the new interpreter model. - Ensured that all references to dynamic modules are replaced with bytecode interpreter checks, maintaining functionality for interpreted code execution. - Modified stack frame handling and service-related code to align with the new interpreter architecture. - Updated tests and service implementations to reflect the changes in the runtime environment. Signed-off-by: Tony <tonylu@tony-cloud.com>
10374 lines
366 KiB
C++
10374 lines
366 KiB
C++
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "vm/app_snapshot.h"
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/bootstrap.h"
|
|
#include "vm/bss_relocs.h"
|
|
#include "vm/canonical_tables.h"
|
|
#include "vm/class_id.h"
|
|
#include "vm/code_observers.h"
|
|
#include "vm/compiler/api/print_filter.h"
|
|
#include "vm/compiler/assembler/disassembler.h"
|
|
#include "vm/dart.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/dispatch_table.h"
|
|
#include "vm/flag_list.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/heap/heap.h"
|
|
#include "vm/image_snapshot.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/program_visitor.h"
|
|
#include "vm/raw_object_fields.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/timeline.h"
|
|
#include "vm/v8_snapshot_writer.h"
|
|
#include "vm/version.h"
|
|
#include "vm/zone_text_buffer.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#include "vm/compiler/backend/code_statistics.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/relocation.h"
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
DEFINE_FLAG(bool,
|
|
print_cluster_information,
|
|
false,
|
|
"Print information about clusters written to snapshot");
|
|
#endif
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
DEFINE_FLAG(charp,
|
|
write_v8_snapshot_profile_to,
|
|
nullptr,
|
|
"Write a snapshot profile in V8 format to a file.");
|
|
DEFINE_FLAG(bool,
|
|
print_array_optimization_candidates,
|
|
false,
|
|
"Print information about how many array are candidates for Smi and "
|
|
"ROData optimizations.");
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
// Forward declarations.
|
|
class Serializer;
|
|
class Deserializer;
|
|
|
|
namespace {
|
|
|
|
// Serialized clusters are identified by their CID. So to insert custom clusters
|
|
// we need to assign them a CID that is otherwise never serialized.
|
|
static constexpr intptr_t kDeltaEncodedTypedDataCid = kNativePointer;
|
|
|
|
// StorageTrait for HashTable which allows to create hash tables backed by
|
|
// zone memory. Used to compute cluster order for canonical clusters.
|
|
struct GrowableArrayStorageTraits {
|
|
class Array : public ZoneObject {
|
|
public:
|
|
Array(Zone* zone, intptr_t length)
|
|
: length_(length), array_(zone->Alloc<ObjectPtr>(length)) {}
|
|
|
|
intptr_t Length() const { return length_; }
|
|
void SetAt(intptr_t index, const Object& value) const {
|
|
array_[index] = value.ptr();
|
|
}
|
|
ObjectPtr At(intptr_t index) const { return array_[index]; }
|
|
|
|
private:
|
|
intptr_t length_ = 0;
|
|
ObjectPtr* array_ = nullptr;
|
|
DISALLOW_COPY_AND_ASSIGN(Array);
|
|
};
|
|
|
|
using ArrayPtr = Array*;
|
|
class ArrayHandle : public ZoneObject {
|
|
public:
|
|
explicit ArrayHandle(ArrayPtr ptr) : ptr_(ptr) {}
|
|
ArrayHandle() {}
|
|
|
|
void SetFrom(const ArrayHandle& other) { ptr_ = other.ptr_; }
|
|
void Clear() { ptr_ = nullptr; }
|
|
bool IsNull() const { return ptr_ == nullptr; }
|
|
ArrayPtr ptr() { return ptr_; }
|
|
|
|
intptr_t Length() const { return ptr_->Length(); }
|
|
void SetAt(intptr_t index, const Object& value) const {
|
|
ptr_->SetAt(index, value);
|
|
}
|
|
ObjectPtr At(intptr_t index) const { return ptr_->At(index); }
|
|
|
|
private:
|
|
ArrayPtr ptr_ = nullptr;
|
|
DISALLOW_COPY_AND_ASSIGN(ArrayHandle);
|
|
};
|
|
|
|
static ArrayHandle& PtrToHandle(ArrayPtr ptr) {
|
|
return *new ArrayHandle(ptr);
|
|
}
|
|
|
|
static void SetHandle(ArrayHandle& dst, const ArrayHandle& src) { // NOLINT
|
|
dst.SetFrom(src);
|
|
}
|
|
|
|
static void ClearHandle(ArrayHandle& dst) { // NOLINT
|
|
dst.Clear();
|
|
}
|
|
|
|
static ArrayPtr New(Zone* zone, intptr_t length, Heap::Space space) {
|
|
return new (zone) Array(zone, length);
|
|
}
|
|
|
|
static bool IsImmutable(const ArrayHandle& handle) { return false; }
|
|
|
|
static ObjectPtr At(ArrayHandle* array, intptr_t index) {
|
|
return array->At(index);
|
|
}
|
|
|
|
static void SetAt(ArrayHandle* array, intptr_t index, const Object& value) {
|
|
array->SetAt(index, value);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
|
|
static void RelocateCodeObjects(
|
|
GrowableArray<CodePtr>* code_objects,
|
|
GrowableArray<ImageWriterCommand>* image_writer_commands) {
|
|
auto thread = Thread::Current();
|
|
auto isolate_group = thread->isolate_group();
|
|
|
|
WritableCodePages writable_code_pages(thread, isolate_group);
|
|
CodeRelocator::Relocate(thread, code_objects, image_writer_commands);
|
|
}
|
|
|
|
#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
|
|
class SerializationCluster : public ZoneObject {
|
|
public:
|
|
static constexpr intptr_t kSizeVaries = -1;
|
|
SerializationCluster(const char* name,
|
|
intptr_t cid,
|
|
intptr_t target_instance_size = kSizeVaries,
|
|
bool is_canonical = false,
|
|
bool is_deeply_immutable = false)
|
|
: name_(name),
|
|
cid_(cid),
|
|
target_instance_size_(target_instance_size),
|
|
is_canonical_(is_canonical),
|
|
is_deeply_immutable_(is_canonical_ ||
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(cid)) {
|
|
ASSERT(target_instance_size == kSizeVaries || target_instance_size >= 0);
|
|
}
|
|
virtual ~SerializationCluster() {}
|
|
|
|
// Add [object] to the cluster and push its outgoing references.
|
|
virtual void Trace(Serializer* serializer, ObjectPtr object) = 0;
|
|
|
|
// Write the cluster type and information needed to allocate the cluster's
|
|
// objects. For fixed sized objects, this is just the object count. For
|
|
// variable sized objects, this is the object count and length of each object.
|
|
virtual void WriteAlloc(Serializer* serializer) = 0;
|
|
|
|
// Write the byte and reference data of the cluster's objects.
|
|
virtual void WriteFill(Serializer* serializer) = 0;
|
|
|
|
void WriteAndMeasureAlloc(Serializer* serializer);
|
|
void WriteAndMeasureFill(Serializer* serializer);
|
|
|
|
const char* name() const { return name_; }
|
|
intptr_t cid() const { return cid_; }
|
|
bool is_canonical() const { return is_canonical_; }
|
|
bool is_deeply_immutable() const { return is_deeply_immutable_; }
|
|
intptr_t size() const { return size_; }
|
|
intptr_t num_objects() const { return num_objects_; }
|
|
|
|
// Returns number of bytes needed for deserialized objects in
|
|
// this cluster. Printed in --print_snapshot_sizes_verbose statistics.
|
|
//
|
|
// In order to calculate this size, clusters of fixed-size objects
|
|
// can pass instance size as [target_instance_size] constructor parameter.
|
|
// Otherwise clusters should count [target_memory_size] in
|
|
// their [WriteAlloc] methods.
|
|
intptr_t target_memory_size() const { return target_memory_size_; }
|
|
|
|
protected:
|
|
const char* const name_;
|
|
const intptr_t cid_;
|
|
const intptr_t target_instance_size_;
|
|
const bool is_canonical_;
|
|
const bool is_deeply_immutable_;
|
|
intptr_t size_ = 0;
|
|
intptr_t num_objects_ = 0;
|
|
intptr_t target_memory_size_ = 0;
|
|
};
|
|
|
|
class DeserializationCluster : public ZoneObject {
|
|
public:
|
|
explicit DeserializationCluster(const char* name,
|
|
bool is_canonical = false,
|
|
bool is_deeply_immutable = false)
|
|
: name_(name),
|
|
is_canonical_(is_canonical),
|
|
is_deeply_immutable_(is_deeply_immutable),
|
|
start_index_(-1),
|
|
stop_index_(-1) {
|
|
// All canonical should be deeply-immutable
|
|
ASSERT(is_deeply_immutable_ || !is_canonical_);
|
|
}
|
|
virtual ~DeserializationCluster() {}
|
|
|
|
// Allocate memory for all objects in the cluster and write their addresses
|
|
// into the ref array. Do not touch this memory.
|
|
virtual void ReadAlloc(Deserializer* deserializer) = 0;
|
|
|
|
// Initialize the cluster's objects. Do not touch the memory of other objects.
|
|
virtual void ReadFill(Deserializer* deserializer) = 0;
|
|
|
|
// Complete any action that requires the full graph to be deserialized, such
|
|
// as rehashing.
|
|
virtual void PostLoad(Deserializer* deserializer, const Array& refs) {
|
|
// We only need to worry about how canonical values are handled during
|
|
// deserialization if there may be multiple loading units, which only
|
|
// happens in the precompiled runtime.
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (is_canonical()) {
|
|
FATAL("%s needs canonicalization but doesn't define PostLoad", name());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const char* name() const { return name_; }
|
|
bool is_canonical() const { return is_canonical_; }
|
|
bool is_deeply_immutable() const { return is_deeply_immutable_; }
|
|
|
|
protected:
|
|
void ReadAllocFixedSize(Deserializer* deserializer, intptr_t instance_size);
|
|
|
|
const char* const name_;
|
|
const bool is_canonical_;
|
|
const bool is_deeply_immutable_;
|
|
// The range of the ref array that belongs to this cluster.
|
|
intptr_t start_index_;
|
|
intptr_t stop_index_;
|
|
};
|
|
|
|
class SerializationRoots {
|
|
public:
|
|
virtual ~SerializationRoots() {}
|
|
virtual void AddBaseObjects(Serializer* serializer) = 0;
|
|
virtual void PushRoots(Serializer* serializer) = 0;
|
|
virtual void WriteRoots(Serializer* serializer) = 0;
|
|
|
|
virtual const CompressedStackMaps& canonicalized_stack_map_entries() const;
|
|
};
|
|
|
|
class DeserializationRoots {
|
|
public:
|
|
virtual ~DeserializationRoots() {}
|
|
virtual void AddBaseObjects(Deserializer* deserializer) = 0;
|
|
virtual void ReadRoots(Deserializer* deserializer) = 0;
|
|
virtual void PostLoad(Deserializer* deserializer, const Array& refs) = 0;
|
|
};
|
|
|
|
// Reference value for objects that either are not reachable from the roots or
|
|
// should never have a reference in the snapshot (because they are dropped,
|
|
// for example). Should be the default value for Heap::GetObjectId.
|
|
static constexpr intptr_t kUnreachableReference = 0;
|
|
COMPILE_ASSERT(kUnreachableReference == WeakTable::kNoValue);
|
|
static constexpr intptr_t kFirstReference = 1;
|
|
|
|
// Reference value for traced objects that have not been allocated their final
|
|
// reference ID.
|
|
static constexpr intptr_t kUnallocatedReference = -1;
|
|
|
|
static constexpr bool IsAllocatedReference(intptr_t ref) {
|
|
return ref > kUnreachableReference;
|
|
}
|
|
|
|
static constexpr bool IsArtificialReference(intptr_t ref) {
|
|
return ref < kUnallocatedReference;
|
|
}
|
|
|
|
static constexpr bool IsReachableReference(intptr_t ref) {
|
|
return ref == kUnallocatedReference || IsAllocatedReference(ref);
|
|
}
|
|
|
|
class CodeSerializationCluster;
|
|
|
|
class Serializer : public ThreadStackResource {
|
|
public:
|
|
Serializer(Thread* thread,
|
|
Snapshot::Kind kind,
|
|
NonStreamingWriteStream* stream,
|
|
ImageWriter* image_writer_,
|
|
V8SnapshotProfileWriter* profile_writer = nullptr);
|
|
~Serializer();
|
|
|
|
void AddBaseObject(ObjectPtr base_object,
|
|
const char* type = nullptr,
|
|
const char* name = nullptr);
|
|
|
|
intptr_t AssignRef(ObjectPtr object);
|
|
intptr_t AssignArtificialRef(ObjectPtr object = nullptr);
|
|
|
|
intptr_t GetCodeIndex(CodePtr code);
|
|
|
|
void Push(ObjectPtr object, intptr_t cid_override = kIllegalCid);
|
|
void PushWeak(ObjectPtr object);
|
|
|
|
void AddUntracedRef() { num_written_objects_++; }
|
|
|
|
void Trace(ObjectPtr object, intptr_t cid_override);
|
|
|
|
void UnexpectedObject(ObjectPtr object, const char* message);
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
ObjectPtr ParentOf(ObjectPtr object) const;
|
|
ObjectPtr ParentOf(const Object& object) const;
|
|
#endif
|
|
|
|
SerializationCluster* NewClusterForClass(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable);
|
|
|
|
void ReserveHeader() {
|
|
// Make room for recording snapshot buffer size.
|
|
stream_->SetPosition(Snapshot::kHeaderSize);
|
|
}
|
|
|
|
void FillHeader(Snapshot::Kind kind) {
|
|
Snapshot* header = reinterpret_cast<Snapshot*>(stream_->buffer());
|
|
header->set_magic();
|
|
header->set_length(stream_->bytes_written());
|
|
header->set_kind(kind);
|
|
}
|
|
|
|
void WriteVersionAndFeatures();
|
|
|
|
void CreateEarlyClusters();
|
|
ZoneGrowableArray<Object*>* Serialize(SerializationRoots* roots);
|
|
void PrintSnapshotSizes();
|
|
|
|
NonStreamingWriteStream* stream() { return stream_; }
|
|
intptr_t bytes_written() { return stream_->bytes_written(); }
|
|
intptr_t bytes_heap_allocated() { return bytes_heap_allocated_; }
|
|
|
|
class WritingObjectScope : ValueObject {
|
|
public:
|
|
WritingObjectScope(Serializer* serializer,
|
|
const char* type,
|
|
ObjectPtr object,
|
|
StringPtr name)
|
|
: WritingObjectScope(
|
|
serializer,
|
|
ReserveId(serializer,
|
|
type,
|
|
object,
|
|
String::ToCString(serializer->thread(), name)),
|
|
object) {}
|
|
|
|
WritingObjectScope(Serializer* serializer,
|
|
const char* type,
|
|
ObjectPtr object,
|
|
const char* name)
|
|
: WritingObjectScope(serializer,
|
|
ReserveId(serializer, type, object, name),
|
|
object) {}
|
|
|
|
WritingObjectScope(Serializer* serializer,
|
|
const V8SnapshotProfileWriter::ObjectId& id,
|
|
ObjectPtr object = nullptr);
|
|
|
|
WritingObjectScope(Serializer* serializer, ObjectPtr object)
|
|
: WritingObjectScope(serializer,
|
|
serializer->GetProfileId(object),
|
|
object) {}
|
|
|
|
~WritingObjectScope();
|
|
|
|
private:
|
|
static V8SnapshotProfileWriter::ObjectId ReserveId(Serializer* serializer,
|
|
const char* type,
|
|
ObjectPtr object,
|
|
const char* name);
|
|
|
|
private:
|
|
Serializer* const serializer_;
|
|
const ObjectPtr old_object_;
|
|
const V8SnapshotProfileWriter::ObjectId old_id_;
|
|
const classid_t old_cid_;
|
|
};
|
|
|
|
// Writes raw data to the stream (basic type).
|
|
// sizeof(T) must be in {1,2,4,8}.
|
|
template <typename T>
|
|
void Write(T value) {
|
|
BaseWriteStream::Raw<sizeof(T), T>::Write(stream_, value);
|
|
}
|
|
void WriteRefId(intptr_t value) { stream_->WriteRefId(value); }
|
|
void WriteUnsigned(intptr_t value) { stream_->WriteUnsigned(value); }
|
|
void WriteUnsigned64(uint64_t value) { stream_->WriteUnsigned(value); }
|
|
|
|
void WriteWordWith32BitWrites(uword value) {
|
|
stream_->WriteWordWith32BitWrites(value);
|
|
}
|
|
|
|
void WriteBytes(const void* addr, intptr_t len) {
|
|
stream_->WriteBytes(addr, len);
|
|
}
|
|
void Align(intptr_t alignment, intptr_t offset = 0) {
|
|
stream_->Align(alignment, offset);
|
|
}
|
|
|
|
V8SnapshotProfileWriter::ObjectId GetProfileId(ObjectPtr object) const;
|
|
V8SnapshotProfileWriter::ObjectId GetProfileId(intptr_t ref) const;
|
|
|
|
void WriteRootRef(ObjectPtr object, const char* name = nullptr) {
|
|
intptr_t id = RefId(object);
|
|
WriteRefId(id);
|
|
if (profile_writer_ != nullptr) {
|
|
profile_writer_->AddRoot(GetProfileId(object), name);
|
|
}
|
|
}
|
|
|
|
// Record a reference from the currently written object to the given object
|
|
// and return reference id for the given object.
|
|
void AttributeReference(ObjectPtr object,
|
|
const V8SnapshotProfileWriter::Reference& reference);
|
|
|
|
void AttributeElementRef(ObjectPtr object, intptr_t index) {
|
|
AttributeReference(object,
|
|
V8SnapshotProfileWriter::Reference::Element(index));
|
|
}
|
|
|
|
void WriteElementRef(ObjectPtr object, intptr_t index) {
|
|
AttributeElementRef(object, index);
|
|
WriteRefId(RefId(object));
|
|
}
|
|
|
|
void AttributePropertyRef(ObjectPtr object, const char* property) {
|
|
AttributeReference(object,
|
|
V8SnapshotProfileWriter::Reference::Property(property));
|
|
}
|
|
|
|
void WritePropertyRef(ObjectPtr object, const char* property) {
|
|
AttributePropertyRef(object, property);
|
|
WriteRefId(RefId(object));
|
|
}
|
|
|
|
void WriteOffsetRef(ObjectPtr object, intptr_t offset) {
|
|
intptr_t id = RefId(object);
|
|
WriteRefId(id);
|
|
if (profile_writer_ != nullptr) {
|
|
if (auto const property = offsets_table_->FieldNameForOffset(
|
|
object_currently_writing_.cid_, offset)) {
|
|
AttributePropertyRef(object, property);
|
|
} else {
|
|
AttributeElementRef(object, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T, typename... P>
|
|
void WriteFromTo(T obj, P&&... args) {
|
|
auto* from = obj->untag()->from();
|
|
auto* to = obj->untag()->to_snapshot(kind(), args...);
|
|
WriteRange(obj, from, to);
|
|
}
|
|
|
|
template <typename T>
|
|
DART_NOINLINE void WriteRange(ObjectPtr obj, T from, T to) {
|
|
for (auto* p = from; p <= to; p++) {
|
|
WriteOffsetRef(
|
|
p->Decompress(obj->heap_base()),
|
|
reinterpret_cast<uword>(p) - reinterpret_cast<uword>(obj->untag()));
|
|
}
|
|
}
|
|
|
|
template <typename T, typename... P>
|
|
void PushFromTo(T obj, P&&... args) {
|
|
auto* from = obj->untag()->from();
|
|
auto* to = obj->untag()->to_snapshot(kind(), args...);
|
|
PushRange(obj, from, to);
|
|
}
|
|
|
|
template <typename T>
|
|
DART_NOINLINE void PushRange(ObjectPtr obj, T from, T to) {
|
|
for (auto* p = from; p <= to; p++) {
|
|
Push(p->Decompress(obj->heap_base()));
|
|
}
|
|
}
|
|
|
|
void WriteTokenPosition(TokenPosition pos) { Write(pos.Serialize()); }
|
|
|
|
void WriteCid(intptr_t cid) {
|
|
COMPILE_ASSERT(UntaggedObject::kClassIdTagSize <= 32);
|
|
Write<int32_t>(cid);
|
|
}
|
|
|
|
// Sorts Code objects and reorders instructions before writing snapshot.
|
|
// Builds binary search table for stack maps.
|
|
void PrepareInstructions(const CompressedStackMaps& canonical_smap);
|
|
|
|
void WriteInstructions(InstructionsPtr instr,
|
|
uint32_t unchecked_offset,
|
|
CodePtr code,
|
|
bool deferred);
|
|
uint32_t GetDataOffset(ObjectPtr object) const;
|
|
void TraceDataOffset(uint32_t offset);
|
|
intptr_t GetDataSize() const;
|
|
|
|
void WriteDispatchTable(const Array& entries);
|
|
|
|
Heap* heap() const { return heap_; }
|
|
Zone* zone() const { return zone_; }
|
|
Snapshot::Kind kind() const { return kind_; }
|
|
intptr_t next_ref_index() const { return next_ref_index_; }
|
|
|
|
void DumpCombinedCodeStatistics();
|
|
|
|
V8SnapshotProfileWriter* profile_writer() const { return profile_writer_; }
|
|
|
|
// If the given [obj] was not included into the snapshot and have not
|
|
// yet gotten an artificial node created for it create an artificial node
|
|
// in the profile representing this object.
|
|
// Returns true if [obj] has an artificial profile node associated with it.
|
|
bool CreateArtificialNodeIfNeeded(ObjectPtr obj);
|
|
|
|
bool InCurrentLoadingUnitOrRoot(ObjectPtr obj);
|
|
void RecordDeferredCode(CodePtr ptr);
|
|
GrowableArray<LoadingUnitSerializationData*>* loading_units() const {
|
|
return loading_units_;
|
|
}
|
|
void set_loading_units(GrowableArray<LoadingUnitSerializationData*>* units) {
|
|
loading_units_ = units;
|
|
}
|
|
intptr_t current_loading_unit_id() const { return current_loading_unit_id_; }
|
|
void set_current_loading_unit_id(intptr_t id) {
|
|
current_loading_unit_id_ = id;
|
|
}
|
|
|
|
// Returns the reference ID for the object. Fails for objects that have not
|
|
// been allocated a reference ID yet, so should be used only after all
|
|
// WriteAlloc calls.
|
|
intptr_t RefId(ObjectPtr object) const;
|
|
|
|
// Same as RefId, but allows artificial and unreachable references. Still
|
|
// fails for unallocated references.
|
|
intptr_t UnsafeRefId(ObjectPtr object) const;
|
|
|
|
// Whether the object is reachable.
|
|
bool IsReachable(ObjectPtr object) const {
|
|
return IsReachableReference(heap_->GetObjectId(object));
|
|
}
|
|
// Whether the object has an allocated reference.
|
|
bool HasRef(ObjectPtr object) const {
|
|
return IsAllocatedReference(heap_->GetObjectId(object));
|
|
}
|
|
// Whether the object only appears in the V8 snapshot profile.
|
|
bool HasArtificialRef(ObjectPtr object) const {
|
|
return IsArtificialReference(heap_->GetObjectId(object));
|
|
}
|
|
// Whether a node for the object already has been added to the V8 snapshot
|
|
// profile.
|
|
bool HasProfileNode(ObjectPtr object) const {
|
|
ASSERT(profile_writer_ != nullptr);
|
|
return profile_writer_->HasId(GetProfileId(object));
|
|
}
|
|
bool IsWritten(ObjectPtr object) const {
|
|
return heap_->GetObjectId(object) > num_base_objects_;
|
|
}
|
|
|
|
private:
|
|
const char* ReadOnlyObjectType(intptr_t cid);
|
|
void FlushProfile();
|
|
|
|
Heap* heap_;
|
|
Zone* zone_;
|
|
Snapshot::Kind kind_;
|
|
NonStreamingWriteStream* stream_;
|
|
ImageWriter* image_writer_;
|
|
SerializationCluster** canonical_clusters_by_cid_;
|
|
SerializationCluster** immutable_clusters_by_cid_;
|
|
SerializationCluster** clusters_by_cid_;
|
|
CodeSerializationCluster* code_cluster_ = nullptr;
|
|
GrowableArray<SerializationCluster*> clusters_;
|
|
|
|
struct StackEntry {
|
|
ObjectPtr obj;
|
|
intptr_t cid_override;
|
|
};
|
|
GrowableArray<StackEntry> stack_;
|
|
|
|
intptr_t num_cids_;
|
|
intptr_t num_tlc_cids_;
|
|
intptr_t num_base_objects_;
|
|
intptr_t num_written_objects_;
|
|
intptr_t next_ref_index_;
|
|
|
|
intptr_t dispatch_table_size_ = 0;
|
|
intptr_t bytes_heap_allocated_ = 0;
|
|
intptr_t instructions_table_len_ = 0;
|
|
intptr_t instructions_table_rodata_offset_ = 0;
|
|
|
|
V8SnapshotProfileWriter* profile_writer_ = nullptr;
|
|
struct ProfilingObject {
|
|
ObjectPtr object_ = nullptr;
|
|
// Unless within a WritingObjectScope, any bytes written are attributed to
|
|
// the artificial root.
|
|
V8SnapshotProfileWriter::ObjectId id_ =
|
|
V8SnapshotProfileWriter::kArtificialRootId;
|
|
intptr_t last_stream_position_ = 0;
|
|
intptr_t cid_ = -1;
|
|
} object_currently_writing_;
|
|
OffsetsTable* offsets_table_ = nullptr;
|
|
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
ObjectPtr current_parent_;
|
|
#endif
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
IntMap<intptr_t> deduped_instructions_sources_;
|
|
IntMap<intptr_t> code_index_;
|
|
#endif
|
|
|
|
intptr_t current_loading_unit_id_ = 0;
|
|
GrowableArray<LoadingUnitSerializationData*>* loading_units_ = nullptr;
|
|
ZoneGrowableArray<Object*>* objects_ = new ZoneGrowableArray<Object*>();
|
|
|
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Serializer);
|
|
};
|
|
|
|
#define AutoTraceObject(obj) \
|
|
Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, nullptr)
|
|
|
|
#define AutoTraceObjectName(obj, str) \
|
|
Serializer::WritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
|
|
|
|
#define WriteFieldValue(field, value) s->WritePropertyRef(value, #field);
|
|
|
|
#define WriteFromTo(obj, ...) s->WriteFromTo(obj, ##__VA_ARGS__);
|
|
|
|
#define PushFromTo(obj, ...) s->PushFromTo(obj, ##__VA_ARGS__);
|
|
|
|
#define WriteField(obj, field) s->WritePropertyRef(obj->untag()->field, #field)
|
|
#define WriteCompressedField(obj, name) \
|
|
s->WritePropertyRef(obj->untag()->name(), #name "_")
|
|
|
|
class Deserializer : public ThreadStackResource {
|
|
public:
|
|
Deserializer(Thread* thread,
|
|
Snapshot::Kind kind,
|
|
const uint8_t* buffer,
|
|
intptr_t size,
|
|
const uint8_t* data_buffer,
|
|
const uint8_t* instructions_buffer,
|
|
bool is_non_root_unit,
|
|
intptr_t offset = 0);
|
|
~Deserializer();
|
|
|
|
// Verifies the image alignment.
|
|
//
|
|
// On success, returns nullptr. On failure, returns an error message that the
|
|
// caller must free.
|
|
char* VerifyImageAlignment();
|
|
|
|
ObjectPtr Allocate(intptr_t size);
|
|
static void InitializeHeader(ObjectPtr raw, intptr_t cid, intptr_t size) {
|
|
InitializeHeader(raw, cid, size, /*is_canonical=*/false,
|
|
/*is_deeply_immutable=*/false);
|
|
}
|
|
static void InitializeHeader(ObjectPtr raw,
|
|
intptr_t cid,
|
|
intptr_t size,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable);
|
|
|
|
// Reads raw data (for basic types).
|
|
// sizeof(T) must be in {1,2,4,8}.
|
|
template <typename T>
|
|
T Read() {
|
|
return ReadStream::Raw<sizeof(T), T>::Read(&stream_);
|
|
}
|
|
intptr_t ReadRefId() { return stream_.ReadRefId(); }
|
|
intptr_t ReadUnsigned() { return stream_.ReadUnsigned(); }
|
|
uint64_t ReadUnsigned64() { return stream_.ReadUnsigned<uint64_t>(); }
|
|
void ReadBytes(uint8_t* addr, intptr_t len) { stream_.ReadBytes(addr, len); }
|
|
|
|
uword ReadWordWith32BitReads() { return stream_.ReadWordWith32BitReads(); }
|
|
|
|
intptr_t position() const { return stream_.Position(); }
|
|
void set_position(intptr_t p) { stream_.SetPosition(p); }
|
|
const uint8_t* AddressOfCurrentPosition() const {
|
|
return stream_.AddressOfCurrentPosition();
|
|
}
|
|
|
|
void Advance(intptr_t value) { stream_.Advance(value); }
|
|
void Align(intptr_t alignment, intptr_t offset = 0) {
|
|
stream_.Align(alignment, offset);
|
|
}
|
|
|
|
void AddBaseObject(ObjectPtr base_object) { AssignRef(base_object); }
|
|
|
|
void AssignRef(ObjectPtr object) {
|
|
ASSERT(next_ref_index_ <= num_objects_);
|
|
refs_->untag()->data()[next_ref_index_] = object;
|
|
next_ref_index_++;
|
|
}
|
|
|
|
ObjectPtr Ref(intptr_t index) const {
|
|
ASSERT(index > 0);
|
|
ASSERT(index <= num_objects_);
|
|
return refs_->untag()->element(index);
|
|
}
|
|
|
|
CodePtr GetCodeByIndex(intptr_t code_index, uword* entry_point) const;
|
|
uword GetEntryPointByCodeIndex(intptr_t code_index) const;
|
|
|
|
// If |code_index| corresponds to a non-discarded Code object returns
|
|
// index within the code cluster that corresponds to this Code object.
|
|
// Otherwise, if |code_index| corresponds to the discarded Code then
|
|
// returns -1.
|
|
static intptr_t CodeIndexToClusterIndex(const InstructionsTable& table,
|
|
intptr_t code_index);
|
|
|
|
ObjectPtr ReadRef() { return Ref(ReadRefId()); }
|
|
|
|
TokenPosition ReadTokenPosition() {
|
|
return TokenPosition::Deserialize(Read<int32_t>());
|
|
}
|
|
|
|
intptr_t ReadCid() {
|
|
COMPILE_ASSERT(UntaggedObject::kClassIdTagSize <= 32);
|
|
return Read<int32_t>();
|
|
}
|
|
|
|
void ReadInstructions(CodePtr code, bool deferred);
|
|
void EndInstructions();
|
|
ObjectPtr GetObjectAt(uint32_t offset) const;
|
|
|
|
void Deserialize(DeserializationRoots* roots);
|
|
|
|
DeserializationCluster* ReadCluster();
|
|
|
|
void ReadDispatchTable() {
|
|
ReadDispatchTable(&stream_, /*deferred=*/false, InstructionsTable::Handle(),
|
|
-1, -1);
|
|
}
|
|
void ReadDispatchTable(ReadStream* stream,
|
|
bool deferred,
|
|
const InstructionsTable& root_instruction_table,
|
|
intptr_t deferred_code_start_index,
|
|
intptr_t deferred_code_end_index);
|
|
|
|
intptr_t next_index() const { return next_ref_index_; }
|
|
Heap* heap() const { return heap_; }
|
|
Zone* zone() const { return zone_; }
|
|
Snapshot::Kind kind() const {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Snapshot::kFullAOT;
|
|
#else
|
|
return kind_;
|
|
#endif
|
|
}
|
|
bool is_non_root_unit() const { return is_non_root_unit_; }
|
|
void set_code_start_index(intptr_t value) { code_start_index_ = value; }
|
|
intptr_t code_start_index() const { return code_start_index_; }
|
|
void set_code_stop_index(intptr_t value) { code_stop_index_ = value; }
|
|
intptr_t code_stop_index() const { return code_stop_index_; }
|
|
void set_unknown_dart_code_index(intptr_t value) {
|
|
unknown_dart_code_index_ = value;
|
|
}
|
|
intptr_t unknown_dart_code_index() const { return unknown_dart_code_index_; }
|
|
void set_lazy_compile_index(intptr_t value) { lazy_compile_index_ = value; }
|
|
intptr_t lazy_compile_index() const { return lazy_compile_index_; }
|
|
const InstructionsTable& instructions_table() const {
|
|
return instructions_table_;
|
|
}
|
|
intptr_t num_base_objects() const { return num_base_objects_; }
|
|
|
|
// This serves to make the snapshot cursor, ref table and null be locals
|
|
// during ReadFill, which allows the C compiler to see they are not aliased
|
|
// and can be kept in registers.
|
|
class Local : public ReadStream {
|
|
public:
|
|
explicit Local(Deserializer* d)
|
|
: ReadStream(d->stream_.buffer_, d->stream_.current_, d->stream_.end_),
|
|
d_(d),
|
|
refs_(d->refs_),
|
|
null_(Object::null()) {
|
|
#if defined(DEBUG)
|
|
// Can't mix use of Deserializer::Read*.
|
|
d->stream_.current_ = nullptr;
|
|
#endif
|
|
}
|
|
~Local() { d_->stream_.current_ = current_; }
|
|
|
|
ObjectPtr Ref(intptr_t index) const {
|
|
ASSERT(index > 0);
|
|
ASSERT(index <= d_->num_objects_);
|
|
return refs_->untag()->element(index);
|
|
}
|
|
|
|
template <typename T>
|
|
T Read() {
|
|
return ReadStream::Raw<sizeof(T), T>::Read(this);
|
|
}
|
|
uint64_t ReadUnsigned64() { return ReadUnsigned<uint64_t>(); }
|
|
|
|
ObjectPtr ReadRef() { return Ref(ReadRefId()); }
|
|
TokenPosition ReadTokenPosition() {
|
|
return TokenPosition::Deserialize(Read<int32_t>());
|
|
}
|
|
|
|
intptr_t ReadCid() {
|
|
COMPILE_ASSERT(UntaggedObject::kClassIdTagSize <= 32);
|
|
return Read<int32_t>();
|
|
}
|
|
|
|
template <typename T, typename... P>
|
|
DART_FORCE_INLINE void ReadFromTo(T obj, P&&... params) {
|
|
auto* from = obj->untag()->from();
|
|
auto* to_snapshot = obj->untag()->to_snapshot(d_->kind(), params...);
|
|
auto* to = obj->untag()->to(params...);
|
|
for (auto* p = from; p <= to_snapshot; p++) {
|
|
*p = ReadRef();
|
|
}
|
|
// This is necessary because, unlike Object::Allocate, the clustered
|
|
// deserializer allocates object without null-initializing them. Instead,
|
|
// each deserialization cluster is responsible for initializing every
|
|
// field, ensuring that every field is written to exactly once.
|
|
for (auto* p = to_snapshot + 1; p <= to; p++) {
|
|
*p = null_;
|
|
}
|
|
}
|
|
|
|
ObjectPtr null() const { return null_; }
|
|
|
|
private:
|
|
Deserializer* const d_;
|
|
const ArrayPtr refs_;
|
|
const ObjectPtr null_;
|
|
};
|
|
|
|
private:
|
|
Heap* heap_;
|
|
PageSpace* old_space_;
|
|
FreeList* freelist_;
|
|
Zone* zone_;
|
|
Snapshot::Kind kind_;
|
|
ReadStream stream_;
|
|
ImageReader* image_reader_;
|
|
intptr_t num_base_objects_;
|
|
intptr_t num_objects_;
|
|
intptr_t num_clusters_;
|
|
ArrayPtr refs_;
|
|
intptr_t next_ref_index_;
|
|
intptr_t code_start_index_ = 0;
|
|
intptr_t code_stop_index_ = 0;
|
|
intptr_t lazy_compile_index_ = 0;
|
|
intptr_t unknown_dart_code_index_ = 0;
|
|
intptr_t instructions_index_ = 0;
|
|
DeserializationCluster** clusters_;
|
|
const bool is_non_root_unit_;
|
|
InstructionsTable& instructions_table_;
|
|
};
|
|
|
|
DART_FORCE_INLINE
|
|
ObjectPtr Deserializer::Allocate(intptr_t size) {
|
|
return UntaggedObject::FromAddr(
|
|
old_space_->AllocateSnapshotLocked(freelist_, size));
|
|
}
|
|
|
|
void Deserializer::InitializeHeader(ObjectPtr raw,
|
|
intptr_t class_id,
|
|
intptr_t size,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable) {
|
|
ASSERT(Utils::IsAligned(size, kObjectAlignment));
|
|
uword tags = 0;
|
|
tags = UntaggedObject::ClassIdTag::update(class_id, tags);
|
|
tags = UntaggedObject::SizeTag::update(size, tags);
|
|
tags = UntaggedObject::CanonicalBit::update(is_canonical, tags);
|
|
tags = UntaggedObject::AlwaysSetBit::update(true, tags);
|
|
tags = UntaggedObject::NotMarkedBit::update(true, tags);
|
|
tags = UntaggedObject::OldAndNotRememberedBit::update(true, tags);
|
|
tags = UntaggedObject::NewOrEvacuationCandidateBit::update(false, tags);
|
|
tags = UntaggedObject::ShallowImmutableBit::update(
|
|
Object::ShouldHaveShallowImmutabilityBitSet(class_id), tags);
|
|
tags = UntaggedObject::DeeplyImmutableBit::update(is_deeply_immutable, tags);
|
|
raw->untag()->tags_ = tags;
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void SerializationCluster::WriteAndMeasureAlloc(Serializer* serializer) {
|
|
intptr_t start_size = serializer->bytes_written();
|
|
intptr_t start_data = serializer->GetDataSize();
|
|
intptr_t start_objects = serializer->next_ref_index();
|
|
uint32_t tags =
|
|
UntaggedObject::ClassIdTag::encode(cid_) |
|
|
UntaggedObject::CanonicalBit::encode(is_canonical()) |
|
|
UntaggedObject::DeeplyImmutableBit::encode(is_deeply_immutable());
|
|
serializer->Write<uint32_t>(tags);
|
|
WriteAlloc(serializer);
|
|
intptr_t stop_size = serializer->bytes_written();
|
|
intptr_t stop_data = serializer->GetDataSize();
|
|
intptr_t stop_objects = serializer->next_ref_index();
|
|
if (FLAG_print_cluster_information) {
|
|
OS::PrintErr("Snapshot 0x%" Pp " (%" Pd "), ", start_size,
|
|
stop_size - start_size);
|
|
OS::PrintErr("Data 0x%" Pp " (%" Pd "): ", start_data,
|
|
stop_data - start_data);
|
|
OS::PrintErr("Alloc %s (%" Pd ")\n", name(), stop_objects - start_objects);
|
|
}
|
|
size_ += (stop_size - start_size) + (stop_data - start_data);
|
|
num_objects_ += (stop_objects - start_objects);
|
|
if (target_instance_size_ != kSizeVaries) {
|
|
target_memory_size_ += num_objects_ * target_instance_size_;
|
|
}
|
|
}
|
|
|
|
void SerializationCluster::WriteAndMeasureFill(Serializer* serializer) {
|
|
intptr_t start = serializer->bytes_written();
|
|
WriteFill(serializer);
|
|
intptr_t stop = serializer->bytes_written();
|
|
if (FLAG_print_cluster_information) {
|
|
OS::PrintErr("Snapshot 0x%" Pp " (%" Pd "): Fill %s\n", start, stop - start,
|
|
name());
|
|
}
|
|
size_ += (stop - start);
|
|
}
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
DART_NOINLINE
|
|
void DeserializationCluster::ReadAllocFixedSize(Deserializer* d,
|
|
intptr_t instance_size) {
|
|
start_index_ = d->next_index();
|
|
intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
d->AssignRef(d->Allocate(instance_size));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
static UnboxedFieldBitmap CalculateTargetUnboxedFieldsBitmap(
|
|
Serializer* s,
|
|
intptr_t class_id) {
|
|
const auto unboxed_fields_bitmap_host =
|
|
s->isolate_group()->class_table()->GetUnboxedFieldsMapAt(class_id);
|
|
|
|
UnboxedFieldBitmap unboxed_fields_bitmap;
|
|
if (unboxed_fields_bitmap_host.IsEmpty() ||
|
|
kWordSize == compiler::target::kWordSize) {
|
|
unboxed_fields_bitmap = unboxed_fields_bitmap_host;
|
|
} else {
|
|
ASSERT(kWordSize == 8 && compiler::target::kWordSize == 4);
|
|
// A new bitmap is built if the word sizes in the target and
|
|
// host are different
|
|
unboxed_fields_bitmap.Reset();
|
|
intptr_t target_i = 0, host_i = 0;
|
|
|
|
while (host_i < UnboxedFieldBitmap::Length()) {
|
|
// Each unboxed field has constant length, therefore the number of
|
|
// words used by it should double when compiling from 64-bit to 32-bit.
|
|
if (unboxed_fields_bitmap_host.Get(host_i++)) {
|
|
unboxed_fields_bitmap.Set(target_i++);
|
|
unboxed_fields_bitmap.Set(target_i++);
|
|
} else {
|
|
// For object pointers, the field is always one word length
|
|
target_i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return unboxed_fields_bitmap;
|
|
}
|
|
|
|
class ClassSerializationCluster : public SerializationCluster {
|
|
public:
|
|
explicit ClassSerializationCluster(intptr_t num_cids)
|
|
: SerializationCluster("Class",
|
|
kClassCid,
|
|
compiler::target::Class::InstanceSize()),
|
|
objects_(num_cids) {}
|
|
~ClassSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ClassPtr cls = Class::RawCast(object);
|
|
intptr_t class_id = cls->untag()->id_;
|
|
|
|
if (class_id == kIllegalCid) {
|
|
// Classes expected to be dropped by the precompiler should not be traced.
|
|
s->UnexpectedObject(cls, "Class with illegal cid");
|
|
}
|
|
|
|
objects_.Add(cls);
|
|
PushFromTo(cls);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ClassPtr cls = objects_[i];
|
|
s->AssignRef(cls);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WriteClass(s, objects_[i]);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void WriteClass(Serializer* s, ClassPtr cls) {
|
|
AutoTraceObjectName(cls, cls->untag()->name());
|
|
WriteFromTo(cls);
|
|
intptr_t class_id = cls->untag()->id_;
|
|
if (class_id == kIllegalCid) {
|
|
s->UnexpectedObject(cls, "Class with illegal cid");
|
|
}
|
|
s->WriteCid(class_id);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
s->Write<uint32_t>(cls->untag()->kernel_offset_);
|
|
}
|
|
if (s->kind() == Snapshot::kFullAOT) {
|
|
s->Write<int32_t>(Class::target_instance_size_in_words(cls));
|
|
s->Write<int32_t>(Class::target_next_field_offset_in_words(cls));
|
|
s->Write<int32_t>(
|
|
Class::target_type_arguments_field_offset_in_words(cls));
|
|
} else {
|
|
s->Write<int32_t>(Class::host_instance_size_in_words(cls));
|
|
s->Write<int32_t>(Class::host_next_field_offset_in_words(cls));
|
|
s->Write<int32_t>(Class::host_type_arguments_field_offset_in_words(cls));
|
|
}
|
|
s->Write<int16_t>(cls->untag()->num_type_arguments_);
|
|
s->Write<uint16_t>(cls->untag()->num_native_fields_);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
s->WriteTokenPosition(cls->untag()->token_pos_);
|
|
s->WriteTokenPosition(cls->untag()->end_token_pos_);
|
|
s->WriteCid(cls->untag()->implementor_cid_);
|
|
}
|
|
s->Write<uint32_t>(cls->untag()->state_bits_);
|
|
|
|
if (!ClassTable::IsTopLevelCid(class_id)) {
|
|
const auto unboxed_fields_map =
|
|
CalculateTargetUnboxedFieldsBitmap(s, class_id);
|
|
s->WriteUnsigned64(unboxed_fields_map.Value());
|
|
}
|
|
}
|
|
|
|
GrowableArray<ClassPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ClassDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ClassDeserializationCluster() : DeserializationCluster("Class") {}
|
|
~ClassDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Class::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ClassTable* table = d_->isolate_group()->class_table();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ClassPtr cls = static_cast<ClassPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(cls, kClassCid, Class::InstanceSize());
|
|
d.ReadFromTo(cls);
|
|
|
|
intptr_t class_id = d.ReadCid();
|
|
cls->untag()->id_ = class_id;
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d_->kind() != Snapshot::kFullAOT);
|
|
cls->untag()->kernel_offset_ = d.Read<uint32_t>();
|
|
#endif
|
|
cls->untag()->host_instance_size_in_words_ = d.Read<int32_t>();
|
|
cls->untag()->host_next_field_offset_in_words_ = d.Read<int32_t>();
|
|
cls->untag()->host_type_arguments_field_offset_in_words_ =
|
|
d.Read<int32_t>();
|
|
#if defined(DART_PRECOMPILER)
|
|
cls->untag()->target_instance_size_in_words_ =
|
|
cls->untag()->host_instance_size_in_words_;
|
|
cls->untag()->target_next_field_offset_in_words_ =
|
|
cls->untag()->host_next_field_offset_in_words_;
|
|
cls->untag()->target_type_arguments_field_offset_in_words_ =
|
|
cls->untag()->host_type_arguments_field_offset_in_words_;
|
|
#endif // defined(DART_PRECOMPILER)
|
|
cls->untag()->num_type_arguments_ = d.Read<int16_t>();
|
|
cls->untag()->num_native_fields_ = d.Read<uint16_t>();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d_->kind() != Snapshot::kFullAOT);
|
|
cls->untag()->token_pos_ = d.ReadTokenPosition();
|
|
cls->untag()->end_token_pos_ = d.ReadTokenPosition();
|
|
cls->untag()->implementor_cid_ = d.ReadCid();
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
cls->untag()->state_bits_ = d.Read<uint32_t>();
|
|
|
|
table->AllocateIndex(class_id);
|
|
table->SetAt(class_id, cls);
|
|
|
|
if (!ClassTable::IsTopLevelCid(class_id)) {
|
|
const UnboxedFieldBitmap unboxed_fields_map(d.ReadUnsigned64());
|
|
table->SetUnboxedFieldsMapAt(class_id, unboxed_fields_map);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Super classes for writing out clusters which contain objects grouped into
|
|
// a canonical set (e.g. String, Type, TypeArguments, etc).
|
|
// To save space in the snapshot we avoid writing such canonical sets
|
|
// explicitly as Array objects into the snapshot and instead utilize a different
|
|
// encoding: objects in a cluster representing a canonical set are sorted
|
|
// to appear in the same order they appear in the Array representing the set,
|
|
// and we additionally write out array of values describing gaps between
|
|
// objects.
|
|
//
|
|
// In some situations not all canonical objects of the some type need to
|
|
// be added to the resulting canonical set because they are cached in some
|
|
// special way (see Type::Canonicalize as an example, which caches declaration
|
|
// types in a special way). In this case subclass can set
|
|
// kAllCanonicalObjectsAreIncludedIntoSet to |false| and override
|
|
// IsInCanonicalSet filter.
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
template <typename SetType,
|
|
typename HandleType,
|
|
typename PointerType,
|
|
bool kAllCanonicalObjectsAreIncludedIntoSet = true>
|
|
class CanonicalSetSerializationCluster : public SerializationCluster {
|
|
protected:
|
|
CanonicalSetSerializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set,
|
|
const char* name,
|
|
intptr_t target_instance_size = 0)
|
|
: SerializationCluster(name,
|
|
cid,
|
|
target_instance_size,
|
|
is_canonical,
|
|
is_deeply_immutable),
|
|
represents_canonical_set_(represents_canonical_set) {}
|
|
|
|
virtual bool IsInCanonicalSet(Serializer* s, PointerType ptr) {
|
|
// Must override this function if kAllCanonicalObjectsAreIncludedIntoSet
|
|
// is set to |false|.
|
|
ASSERT(kAllCanonicalObjectsAreIncludedIntoSet);
|
|
return true;
|
|
}
|
|
|
|
void ReorderObjects(Serializer* s) {
|
|
if (!represents_canonical_set_) {
|
|
return;
|
|
}
|
|
|
|
// Sort objects before writing them out so that they appear in the same
|
|
// order as they would appear in a CanonicalStringSet.
|
|
using ZoneCanonicalSet =
|
|
HashTable<typename SetType::Traits, 0, 0, GrowableArrayStorageTraits>;
|
|
|
|
// Compute required capacity for the hashtable (to avoid overallocating).
|
|
intptr_t required_capacity = 0;
|
|
for (auto ptr : objects_) {
|
|
if (kAllCanonicalObjectsAreIncludedIntoSet || IsInCanonicalSet(s, ptr)) {
|
|
required_capacity++;
|
|
}
|
|
}
|
|
// Over-allocate capacity so a few inserts can happen at startup without
|
|
// causing a rehash.
|
|
const intptr_t kSpareCapacity = 32;
|
|
required_capacity = static_cast<intptr_t>(
|
|
static_cast<double>(required_capacity + kSpareCapacity) /
|
|
HashTables::kMaxLoadFactor);
|
|
|
|
intptr_t num_occupied = 0;
|
|
|
|
// Build canonical set out of objects that should belong to it.
|
|
// Objects that don't belong to it are copied to the prefix of objects_.
|
|
ZoneCanonicalSet table(
|
|
s->zone(), HashTables::New<ZoneCanonicalSet>(required_capacity));
|
|
HandleType& element = HandleType::Handle(s->zone());
|
|
for (auto ptr : objects_) {
|
|
if (kAllCanonicalObjectsAreIncludedIntoSet || IsInCanonicalSet(s, ptr)) {
|
|
element ^= ptr;
|
|
intptr_t entry = -1;
|
|
const bool present = table.FindKeyOrDeletedOrUnused(element, &entry);
|
|
ASSERT(!present);
|
|
table.InsertKey(entry, element);
|
|
} else {
|
|
objects_[num_occupied++] = ptr;
|
|
}
|
|
}
|
|
|
|
const auto prefix_length = num_occupied;
|
|
|
|
// Compute objects_ order and gaps based on canonical set layout.
|
|
auto& arr = table.Release();
|
|
intptr_t last_occupied = ZoneCanonicalSet::kFirstKeyIndex - 1;
|
|
for (intptr_t i = ZoneCanonicalSet::kFirstKeyIndex, length = arr.Length();
|
|
i < length; i++) {
|
|
ObjectPtr v = arr.At(i);
|
|
ASSERT(v != ZoneCanonicalSet::DeletedMarker().ptr());
|
|
if (v != ZoneCanonicalSet::UnusedMarker().ptr()) {
|
|
const intptr_t unused_run_length = (i - 1) - last_occupied;
|
|
gaps_.Add(unused_run_length);
|
|
objects_[num_occupied++] = static_cast<PointerType>(v);
|
|
last_occupied = i;
|
|
}
|
|
}
|
|
ASSERT(num_occupied == objects_.length());
|
|
ASSERT(prefix_length == (objects_.length() - gaps_.length()));
|
|
table_length_ = arr.Length();
|
|
}
|
|
|
|
void WriteCanonicalSetLayout(Serializer* s) {
|
|
if (represents_canonical_set_) {
|
|
s->WriteUnsigned(table_length_);
|
|
s->WriteUnsigned(objects_.length() - gaps_.length());
|
|
for (auto gap : gaps_) {
|
|
s->WriteUnsigned(gap);
|
|
}
|
|
target_memory_size_ +=
|
|
compiler::target::Array::InstanceSize(table_length_);
|
|
}
|
|
}
|
|
|
|
GrowableArray<PointerType> objects_;
|
|
|
|
private:
|
|
const bool represents_canonical_set_;
|
|
GrowableArray<intptr_t> gaps_;
|
|
intptr_t table_length_ = 0;
|
|
};
|
|
#endif
|
|
|
|
template <typename SetType, bool kAllCanonicalObjectsAreIncludedIntoSet = true>
|
|
class CanonicalSetDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
CanonicalSetDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit,
|
|
const char* name)
|
|
: DeserializationCluster(name, is_canonical, is_deeply_immutable),
|
|
is_root_unit_(is_root_unit),
|
|
table_(SetType::ArrayHandle::Handle()) {}
|
|
|
|
void BuildCanonicalSetFromLayout(Deserializer* d) {
|
|
if (!is_root_unit_ || !is_canonical()) {
|
|
return;
|
|
}
|
|
|
|
const auto table_length = d->ReadUnsigned();
|
|
first_element_ = d->ReadUnsigned();
|
|
const intptr_t count = stop_index_ - (start_index_ + first_element_);
|
|
auto table = StartDeserialization(d, table_length, count);
|
|
for (intptr_t i = start_index_ + first_element_; i < stop_index_; i++) {
|
|
table.FillGap(d->ReadUnsigned());
|
|
table.WriteElement(d, d->Ref(i));
|
|
}
|
|
table_ = table.Finish();
|
|
}
|
|
|
|
protected:
|
|
const bool is_root_unit_;
|
|
intptr_t first_element_;
|
|
typename SetType::ArrayHandle& table_;
|
|
|
|
void VerifyCanonicalSet(Deserializer* d,
|
|
const Array& refs,
|
|
const typename SetType::ArrayHandle& current_table) {
|
|
#if defined(DEBUG)
|
|
// First check that we are not overwriting a table and loosing information.
|
|
// TODO(bootstrap-only-in-gen-snapshot): Also assert for kFull.
|
|
if (!current_table.IsNull() && d->kind() != Snapshot::kFull) {
|
|
SetType current_set(d->zone(), current_table.ptr());
|
|
ASSERT(current_set.NumOccupied() == 0);
|
|
current_set.Release();
|
|
}
|
|
|
|
// Now check that manually created table behaves correctly as a canonical
|
|
// set.
|
|
SetType canonical_set(d->zone(), table_.ptr());
|
|
Object& key = Object::Handle();
|
|
for (intptr_t i = start_index_ + first_element_; i < stop_index_; i++) {
|
|
key = refs.At(i);
|
|
ASSERT(canonical_set.GetOrNull(key) != Object::null());
|
|
}
|
|
canonical_set.Release();
|
|
#endif // defined(DEBUG)
|
|
}
|
|
|
|
private:
|
|
struct DeserializationFinger {
|
|
typename SetType::ArrayPtr table;
|
|
intptr_t current_index;
|
|
ObjectPtr gap_element;
|
|
|
|
void FillGap(int length) {
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
table->untag()->data()[current_index + j] = gap_element;
|
|
}
|
|
current_index += length;
|
|
}
|
|
|
|
void WriteElement(Deserializer* d, ObjectPtr object) {
|
|
table->untag()->data()[current_index++] = object;
|
|
}
|
|
|
|
typename SetType::ArrayPtr Finish() {
|
|
if (table != SetType::ArrayHandle::null()) {
|
|
FillGap(Smi::Value(table->untag()->length()) - current_index);
|
|
}
|
|
auto result = table;
|
|
table = SetType::ArrayHandle::null();
|
|
return result;
|
|
}
|
|
};
|
|
|
|
static DeserializationFinger StartDeserialization(Deserializer* d,
|
|
intptr_t length,
|
|
intptr_t count) {
|
|
const intptr_t instance_size = SetType::ArrayHandle::InstanceSize(length);
|
|
typename SetType::ArrayPtr table =
|
|
static_cast<typename SetType::ArrayPtr>(d->Allocate(instance_size));
|
|
Deserializer::InitializeHeader(table, SetType::Storage::ArrayCid,
|
|
instance_size);
|
|
if ((SetType::Storage::ArrayCid == kArrayCid) &&
|
|
Array::UseCardMarkingForAllocation(length)) {
|
|
table->untag()->SetCardRememberedBitUnsynchronized();
|
|
Page::Of(table)->AllocateCardTable();
|
|
}
|
|
InitTypeArgsOrNext(table);
|
|
table->untag()->length_ = Smi::New(length);
|
|
for (intptr_t i = 0; i < SetType::kFirstKeyIndex; i++) {
|
|
table->untag()->data()[i] = Smi::New(0);
|
|
}
|
|
table->untag()->data()[SetType::kOccupiedEntriesIndex] = Smi::New(count);
|
|
return {table, SetType::kFirstKeyIndex, SetType::UnusedMarker().ptr()};
|
|
}
|
|
|
|
static void InitTypeArgsOrNext(ArrayPtr table) {
|
|
table->untag()->type_arguments_ = TypeArguments::null();
|
|
}
|
|
static void InitTypeArgsOrNext(WeakArrayPtr table) {
|
|
table->untag()->next_seen_by_gc_ = WeakArray::null();
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypeParametersSerializationCluster : public SerializationCluster {
|
|
public:
|
|
TypeParametersSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: SerializationCluster("TypeParameters",
|
|
kTypeParametersCid,
|
|
compiler::target::TypeParameters::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~TypeParametersSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypeParametersPtr type_params = TypeParameters::RawCast(object);
|
|
objects_.Add(type_params);
|
|
PushFromTo(type_params);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypeParametersPtr type_params = objects_[i];
|
|
s->AssignRef(type_params);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypeParametersPtr type_params = objects_[i];
|
|
AutoTraceObject(type_params);
|
|
WriteFromTo(type_params);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<TypeParametersPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypeParametersDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
TypeParametersDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: DeserializationCluster("TypeParameters",
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~TypeParametersDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, TypeParameters::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypeParametersPtr type_params = static_cast<TypeParametersPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(type_params, kTypeParametersCid,
|
|
TypeParameters::InstanceSize());
|
|
d.ReadFromTo(type_params);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypeArgumentsSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalTypeArgumentsSet,
|
|
TypeArguments,
|
|
TypeArgumentsPtr> {
|
|
public:
|
|
TypeArgumentsSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(kTypeArgumentsCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
represents_canonical_set,
|
|
"TypeArguments") {}
|
|
~TypeArgumentsSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypeArgumentsPtr type_args = TypeArguments::RawCast(object);
|
|
objects_.Add(type_args);
|
|
|
|
s->Push(type_args->untag()->instantiations());
|
|
const intptr_t length = Smi::Value(type_args->untag()->length());
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
s->Push(type_args->untag()->element(i));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypeArgumentsPtr type_args = objects_[i];
|
|
s->AssignRef(type_args);
|
|
AutoTraceObject(type_args);
|
|
const intptr_t length = Smi::Value(type_args->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::TypeArguments::InstanceSize(length);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypeArgumentsPtr type_args = objects_[i];
|
|
AutoTraceObject(type_args);
|
|
const intptr_t length = Smi::Value(type_args->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
intptr_t hash = Smi::Value(type_args->untag()->hash());
|
|
s->Write<int32_t>(hash);
|
|
const intptr_t nullability =
|
|
Smi::Value(type_args->untag()->nullability());
|
|
s->WriteUnsigned(nullability);
|
|
WriteField(type_args, instantiations());
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
s->WriteElementRef(type_args->untag()->element(j), j);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypeArgumentsDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalTypeArgumentsSet> {
|
|
public:
|
|
TypeArgumentsDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit,
|
|
"TypeArguments") {}
|
|
~TypeArgumentsDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(TypeArguments::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypeArgumentsPtr type_args = static_cast<TypeArgumentsPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(type_args, kTypeArgumentsCid,
|
|
TypeArguments::InstanceSize(length),
|
|
mark_canonical, is_deeply_immutable());
|
|
type_args->untag()->length_ = Smi::New(length);
|
|
type_args->untag()->hash_ = Smi::New(d.Read<int32_t>());
|
|
type_args->untag()->nullability_ = Smi::New(d.ReadUnsigned());
|
|
type_args->untag()->instantiations_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
type_args->untag()->types()[j] =
|
|
static_cast<AbstractTypePtr>(d.ReadRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(
|
|
d, refs, Array::Handle(object_store->canonical_type_arguments()));
|
|
object_store->set_canonical_type_arguments(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
TypeArguments& type_arg = TypeArguments::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
type_arg ^= refs.At(i);
|
|
type_arg = type_arg.Canonicalize(d->thread());
|
|
refs.SetAt(i, type_arg);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class PatchClassSerializationCluster : public SerializationCluster {
|
|
public:
|
|
PatchClassSerializationCluster()
|
|
: SerializationCluster("PatchClass",
|
|
kPatchClassCid,
|
|
compiler::target::PatchClass::InstanceSize()) {}
|
|
~PatchClassSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
PatchClassPtr cls = PatchClass::RawCast(object);
|
|
objects_.Add(cls);
|
|
PushFromTo(cls);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
PatchClassPtr cls = objects_[i];
|
|
s->AssignRef(cls);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
PatchClassPtr cls = objects_[i];
|
|
AutoTraceObject(cls);
|
|
WriteFromTo(cls);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
s->Write<int32_t>(cls->untag()->kernel_library_index_);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<PatchClassPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class PatchClassDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
PatchClassDeserializationCluster() : DeserializationCluster("PatchClass") {}
|
|
~PatchClassDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, PatchClass::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
PatchClassPtr cls = static_cast<PatchClassPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(cls, kPatchClassCid,
|
|
PatchClass::InstanceSize());
|
|
d.ReadFromTo(cls);
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d_->kind() != Snapshot::kFullAOT);
|
|
cls->untag()->kernel_library_index_ = d.Read<int32_t>();
|
|
#endif
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class FunctionSerializationCluster : public SerializationCluster {
|
|
public:
|
|
FunctionSerializationCluster()
|
|
: SerializationCluster("Function",
|
|
kFunctionCid,
|
|
compiler::target::Function::InstanceSize()) {}
|
|
~FunctionSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
Snapshot::Kind kind = s->kind();
|
|
FunctionPtr func = Function::RawCast(object);
|
|
objects_.Add(func);
|
|
|
|
PushFromTo(func);
|
|
if (kind == Snapshot::kFullAOT) {
|
|
s->Push(func->untag()->code());
|
|
} else if (kind == Snapshot::kFullJIT) {
|
|
NOT_IN_PRECOMPILED(s->Push(func->untag()->unoptimized_code()));
|
|
s->Push(func->untag()->code());
|
|
s->Push(func->untag()->ic_data_array_or_bytecode());
|
|
}
|
|
if (kind != Snapshot::kFullAOT) {
|
|
NOT_IN_PRECOMPILED(s->Push(func->untag()->positional_parameter_names()));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FunctionPtr func = objects_[i];
|
|
s->AssignRef(func);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
Snapshot::Kind kind = s->kind();
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FunctionPtr func = objects_[i];
|
|
AutoTraceObjectName(func, MakeDisambiguatedFunctionName(s, func));
|
|
WriteFromTo(func);
|
|
if (kind == Snapshot::kFullAOT) {
|
|
#if defined(DART_PRECOMPILER)
|
|
CodePtr code = func->untag()->code();
|
|
const auto code_index = s->GetCodeIndex(code);
|
|
s->WriteUnsigned(code_index);
|
|
s->AttributePropertyRef(code, "code_");
|
|
#else
|
|
UNREACHABLE();
|
|
#endif
|
|
} else if (s->kind() == Snapshot::kFullJIT) {
|
|
NOT_IN_PRECOMPILED(WriteCompressedField(func, unoptimized_code));
|
|
WriteCompressedField(func, code);
|
|
WriteCompressedField(func, ic_data_array_or_bytecode);
|
|
}
|
|
|
|
if (kind != Snapshot::kFullAOT) {
|
|
NOT_IN_PRECOMPILED(
|
|
WriteCompressedField(func, positional_parameter_names));
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER) && !defined(PRODUCT)
|
|
TokenPosition token_pos = func->untag()->token_pos_;
|
|
if (kind == Snapshot::kFullAOT) {
|
|
// We use then token_pos property to store the line number
|
|
// in AOT snapshots.
|
|
intptr_t line = -1;
|
|
const Function& function = Function::Handle(func);
|
|
const Script& script = Script::Handle(function.script());
|
|
if (!script.IsNull()) {
|
|
script.GetTokenLocation(token_pos, &line, nullptr);
|
|
}
|
|
token_pos = line == -1 ? TokenPosition::kNoSource
|
|
: TokenPosition::Deserialize(line);
|
|
}
|
|
s->WriteTokenPosition(token_pos);
|
|
#else
|
|
if (kind != Snapshot::kFullAOT) {
|
|
s->WriteTokenPosition(func->untag()->token_pos_);
|
|
}
|
|
#endif
|
|
if (kind != Snapshot::kFullAOT) {
|
|
s->WriteTokenPosition(func->untag()->end_token_pos_);
|
|
s->Write<uint32_t>(func->untag()->kernel_offset_);
|
|
s->Write<bool>(func->untag()->is_optimizable_);
|
|
}
|
|
s->Write<uint32_t>(func->untag()->kind_tag_);
|
|
}
|
|
}
|
|
|
|
static const char* MakeDisambiguatedFunctionName(Serializer* s,
|
|
FunctionPtr f) {
|
|
if (s->profile_writer() == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
REUSABLE_FUNCTION_HANDLESCOPE(s->thread());
|
|
Function& fun = reused_function_handle.Handle();
|
|
fun = f;
|
|
ZoneTextBuffer printer(s->thread()->zone());
|
|
fun.PrintName(NameFormattingParams::DisambiguatedUnqualified(
|
|
Object::NameVisibility::kInternalName),
|
|
&printer);
|
|
return printer.buffer();
|
|
}
|
|
|
|
private:
|
|
GrowableArray<FunctionPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
template <bool need_entry_point_for_non_discarded>
|
|
DART_FORCE_INLINE static CodePtr GetCodeAndEntryPointByIndex(
|
|
const Deserializer* d,
|
|
intptr_t code_index,
|
|
uword* entry_point) {
|
|
code_index -= 1; // 0 is reserved for LazyCompile stub.
|
|
|
|
// In root unit and VM isolate snapshot code_indices are self-contained
|
|
// they point into instruction table and/or into the code cluster.
|
|
// In non-root units we might also refer to code objects from the
|
|
// parent unit which means code_index is biased by num_base_objects_
|
|
const intptr_t base = d->is_non_root_unit() ? d->num_base_objects() : 0;
|
|
if (code_index < base) {
|
|
CodePtr code = static_cast<CodePtr>(d->Ref(code_index));
|
|
if (need_entry_point_for_non_discarded) {
|
|
*entry_point = Code::EntryPointOf(code);
|
|
}
|
|
return code;
|
|
}
|
|
code_index -= base;
|
|
|
|
// At this point code_index is referring to a code object which is either
|
|
// discarded or exists in the Code cluster. Non-discarded Code objects
|
|
// are associated with the tail of the instruction table and have the
|
|
// same order there and in the Code cluster. This means that
|
|
// subtracting first_entry_with_code yields index into the Code cluster.
|
|
// This also works for deferred code objects in root unit's snapshot
|
|
// due to the choice of encoding (see Serializer::GetCodeIndex).
|
|
const intptr_t first_entry_with_code =
|
|
d->instructions_table().rodata()->first_entry_with_code;
|
|
if (code_index < first_entry_with_code) {
|
|
*entry_point = d->instructions_table().EntryPointAt(code_index);
|
|
const intptr_t ref = d->unknown_dart_code_index();
|
|
ASSERT(ref != 0);
|
|
return static_cast<CodePtr>(d->Ref(ref));
|
|
} else {
|
|
const intptr_t cluster_index = code_index - first_entry_with_code;
|
|
CodePtr code =
|
|
static_cast<CodePtr>(d->Ref(d->code_start_index() + cluster_index));
|
|
if (need_entry_point_for_non_discarded) {
|
|
*entry_point = Code::EntryPointOf(code);
|
|
}
|
|
return code;
|
|
}
|
|
}
|
|
|
|
CodePtr Deserializer::GetCodeByIndex(intptr_t code_index,
|
|
uword* entry_point) const {
|
|
// See Serializer::GetCodeIndex for how code_index is encoded.
|
|
if (code_index == 0) {
|
|
const intptr_t ref = lazy_compile_index();
|
|
ASSERT(ref != 0);
|
|
return static_cast<CodePtr>(Ref(ref));
|
|
} else if (FLAG_precompiled_mode) {
|
|
return GetCodeAndEntryPointByIndex<
|
|
/*need_entry_point_for_non_discarded=*/false>(this, code_index,
|
|
entry_point);
|
|
} else {
|
|
// -1 below because 0 is reserved for LazyCompile stub.
|
|
const intptr_t ref = code_start_index_ + code_index - 1;
|
|
ASSERT(code_start_index_ <= ref && ref < code_stop_index_);
|
|
return static_cast<CodePtr>(Ref(ref));
|
|
}
|
|
}
|
|
|
|
intptr_t Deserializer::CodeIndexToClusterIndex(const InstructionsTable& table,
|
|
intptr_t code_index) {
|
|
// Note: code indices we are interpreting here originate from the root
|
|
// loading unit which means base is equal to 0.
|
|
// See comments which clarify the connection between code_index and
|
|
// index into the Code cluster.
|
|
ASSERT(FLAG_precompiled_mode);
|
|
const intptr_t first_entry_with_code = table.rodata()->first_entry_with_code;
|
|
return code_index - 1 - first_entry_with_code;
|
|
}
|
|
|
|
uword Deserializer::GetEntryPointByCodeIndex(intptr_t code_index) const {
|
|
// See Deserializer::GetCodeByIndex which this code repeats.
|
|
ASSERT(FLAG_precompiled_mode);
|
|
uword entry_point = 0;
|
|
GetCodeAndEntryPointByIndex</*need_entry_point_for_non_discarded=*/true>(
|
|
this, code_index, &entry_point);
|
|
return entry_point;
|
|
}
|
|
|
|
class FunctionDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
FunctionDeserializationCluster() : DeserializationCluster("Function") {}
|
|
~FunctionDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Function::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
Snapshot::Kind kind = d_->kind();
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
FunctionPtr func = static_cast<FunctionPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(func, kFunctionCid,
|
|
Function::InstanceSize());
|
|
d.ReadFromTo(func);
|
|
|
|
#if defined(DEBUG)
|
|
func->untag()->entry_point_ = 0;
|
|
func->untag()->unchecked_entry_point_ = 0;
|
|
#endif
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(kind == Snapshot::kFullAOT);
|
|
const intptr_t code_index = d.ReadUnsigned();
|
|
uword entry_point = 0;
|
|
CodePtr code = d_->GetCodeByIndex(code_index, &entry_point);
|
|
func->untag()->code_ = code;
|
|
if (entry_point != 0) {
|
|
func->untag()->entry_point_ = entry_point;
|
|
func->untag()->unchecked_entry_point_ = entry_point;
|
|
}
|
|
#else
|
|
ASSERT(kind != Snapshot::kFullAOT);
|
|
if (kind == Snapshot::kFullJIT) {
|
|
func->untag()->unoptimized_code_ = static_cast<CodePtr>(d.ReadRef());
|
|
func->untag()->code_ = static_cast<CodePtr>(d.ReadRef());
|
|
func->untag()->ic_data_array_or_bytecode_ = d.ReadRef();
|
|
}
|
|
#endif
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(kind != Snapshot::kFullAOT);
|
|
func->untag()->positional_parameter_names_ =
|
|
static_cast<ArrayPtr>(d.ReadRef());
|
|
#endif
|
|
#if !defined(DART_PRECOMPILED_RUNTIME) || \
|
|
(defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT))
|
|
func->untag()->token_pos_ = d.ReadTokenPosition();
|
|
#endif
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
func->untag()->end_token_pos_ = d.ReadTokenPosition();
|
|
func->untag()->kernel_offset_ = d.Read<uint32_t>();
|
|
func->untag()->unboxed_parameters_info_.Reset();
|
|
func->untag()->is_optimizable_ = d.Read<bool>();
|
|
#endif
|
|
|
|
func->untag()->kind_tag_ = d.Read<uint32_t>();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
func->untag()->usage_counter_ = 0;
|
|
func->untag()->optimized_instruction_count_ = 0;
|
|
func->untag()->optimized_call_site_count_ = 0;
|
|
func->untag()->deoptimization_counter_ = 0;
|
|
func->untag()->state_bits_ = 0;
|
|
func->untag()->inlining_depth_ = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (d->kind() == Snapshot::kFullAOT) {
|
|
Function& func = Function::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
func ^= refs.At(i);
|
|
auto const code = func.ptr()->untag()->code();
|
|
ASSERT(code->IsCode());
|
|
if (!Code::IsUnknownDartCode(code)) {
|
|
uword entry_point = code->untag()->entry_point_;
|
|
ASSERT(entry_point != 0);
|
|
func.ptr()->untag()->entry_point_ = entry_point;
|
|
uword unchecked_entry_point = code->untag()->unchecked_entry_point_;
|
|
ASSERT(unchecked_entry_point != 0);
|
|
func.ptr()->untag()->unchecked_entry_point_ = unchecked_entry_point;
|
|
}
|
|
}
|
|
} else if (d->kind() == Snapshot::kFullJIT) {
|
|
Function& func = Function::Handle(d->zone());
|
|
Code& code = Code::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
func ^= refs.At(i);
|
|
code = func.CurrentCode();
|
|
if (func.HasCode() && !code.IsDisabled()) {
|
|
func.SetInstructionsSafe(code); // Set entrypoint.
|
|
func.SetWasCompiled(true);
|
|
} else {
|
|
func.ClearCodeSafe(); // Set code and entrypoint to lazy compile stub
|
|
}
|
|
}
|
|
} else {
|
|
Function& func = Function::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
func ^= refs.At(i);
|
|
func.ClearCodeSafe(); // Set code and entrypoint to lazy compile stub.
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ClosureDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ClosureDataSerializationCluster()
|
|
: SerializationCluster("ClosureData",
|
|
kClosureDataCid,
|
|
compiler::target::ClosureData::InstanceSize()) {}
|
|
~ClosureDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ClosureDataPtr data = ClosureData::RawCast(object);
|
|
objects_.Add(data);
|
|
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
s->Push(data->untag()->context_scope());
|
|
}
|
|
s->Push(data->untag()->parent_function());
|
|
s->Push(data->untag()->closure());
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ClosureDataPtr data = objects_[i];
|
|
s->AssignRef(data);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ClosureDataPtr data = objects_[i];
|
|
AutoTraceObject(data);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
WriteCompressedField(data, context_scope);
|
|
}
|
|
WriteCompressedField(data, parent_function);
|
|
WriteCompressedField(data, closure);
|
|
s->WriteUnsigned(static_cast<uint32_t>(data->untag()->packed_fields_));
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ClosureDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ClosureDataDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ClosureDataDeserializationCluster() : DeserializationCluster("ClosureData") {}
|
|
~ClosureDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, ClosureData::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ClosureDataPtr data = static_cast<ClosureDataPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(data, kClosureDataCid,
|
|
ClosureData::InstanceSize());
|
|
if (d_->kind() == Snapshot::kFullAOT) {
|
|
data->untag()->context_scope_ = ContextScope::null();
|
|
} else {
|
|
data->untag()->context_scope_ =
|
|
static_cast<ContextScopePtr>(d.ReadRef());
|
|
}
|
|
data->untag()->parent_function_ = static_cast<FunctionPtr>(d.ReadRef());
|
|
data->untag()->closure_ = static_cast<ClosurePtr>(d.ReadRef());
|
|
data->untag()->packed_fields_ = d.ReadUnsigned<uint32_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class FfiTrampolineDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
FfiTrampolineDataSerializationCluster()
|
|
: SerializationCluster(
|
|
"FfiTrampolineData",
|
|
kFfiTrampolineDataCid,
|
|
compiler::target::FfiTrampolineData::InstanceSize()) {}
|
|
~FfiTrampolineDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
FfiTrampolineDataPtr data = FfiTrampolineData::RawCast(object);
|
|
objects_.Add(data);
|
|
PushFromTo(data);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
s->AssignRef(objects_[i]);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FfiTrampolineDataPtr const data = objects_[i];
|
|
AutoTraceObject(data);
|
|
WriteFromTo(data);
|
|
s->Write<int32_t>(data->untag()->callback_id_);
|
|
s->Write<uint8_t>(data->untag()->ffi_function_kind_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<FfiTrampolineDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class FfiTrampolineDataDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
FfiTrampolineDataDeserializationCluster()
|
|
: DeserializationCluster("FfiTrampolineData") {}
|
|
~FfiTrampolineDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, FfiTrampolineData::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
FfiTrampolineDataPtr data = static_cast<FfiTrampolineDataPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(data, kFfiTrampolineDataCid,
|
|
FfiTrampolineData::InstanceSize());
|
|
d.ReadFromTo(data);
|
|
data->untag()->callback_id_ = d.Read<int32_t>();
|
|
data->untag()->ffi_function_kind_ = d.Read<uint8_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class FieldSerializationCluster : public SerializationCluster {
|
|
public:
|
|
FieldSerializationCluster()
|
|
: SerializationCluster("Field",
|
|
kFieldCid,
|
|
compiler::target::Field::InstanceSize()) {}
|
|
~FieldSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
FieldPtr field = Field::RawCast(object);
|
|
objects_.Add(field);
|
|
|
|
Snapshot::Kind kind = s->kind();
|
|
|
|
s->Push(field->untag()->name());
|
|
s->Push(field->untag()->owner());
|
|
s->Push(field->untag()->type());
|
|
// Write out the initializer function
|
|
s->Push(field->untag()->initializer_function());
|
|
|
|
if (kind != Snapshot::kFullAOT) {
|
|
s->Push(field->untag()->guarded_list_length());
|
|
}
|
|
if (kind == Snapshot::kFullJIT) {
|
|
s->Push(field->untag()->dependent_code());
|
|
}
|
|
// Write out either the initial static value or field offset.
|
|
if (Field::StaticBit::decode(field->untag()->kind_bits_)) {
|
|
s->Push(field->untag()->host_offset_or_field_id());
|
|
} else {
|
|
s->Push(Smi::New(Field::TargetOffsetOf(field)));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FieldPtr field = objects_[i];
|
|
s->AssignRef(field);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
Snapshot::Kind kind = s->kind();
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FieldPtr field = objects_[i];
|
|
AutoTraceObjectName(field, field->untag()->name());
|
|
|
|
WriteCompressedField(field, name);
|
|
WriteCompressedField(field, owner);
|
|
WriteCompressedField(field, type);
|
|
// Write out the initializer function and initial value if not in AOT.
|
|
WriteCompressedField(field, initializer_function);
|
|
if (kind != Snapshot::kFullAOT) {
|
|
WriteCompressedField(field, guarded_list_length);
|
|
}
|
|
if (kind == Snapshot::kFullJIT) {
|
|
WriteCompressedField(field, dependent_code);
|
|
}
|
|
|
|
if (kind != Snapshot::kFullAOT) {
|
|
s->WriteTokenPosition(field->untag()->token_pos_);
|
|
s->WriteTokenPosition(field->untag()->end_token_pos_);
|
|
s->WriteCid(field->untag()->guarded_cid_);
|
|
s->WriteCid(field->untag()->is_nullable_);
|
|
s->Write<int8_t>(field->untag()->static_type_exactness_state_);
|
|
s->Write<uint32_t>(field->untag()->kernel_offset_);
|
|
}
|
|
s->Write<uint32_t>(field->untag()->kind_bits_);
|
|
|
|
// Write out either the initial static value or field offset.
|
|
if (Field::StaticBit::decode(field->untag()->kind_bits_)) {
|
|
WriteFieldValue("id", field->untag()->host_offset_or_field_id());
|
|
} else {
|
|
WriteFieldValue("offset", Smi::New(Field::TargetOffsetOf(field)));
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<FieldPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class FieldDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
FieldDeserializationCluster() : DeserializationCluster("Field") {}
|
|
~FieldDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Field::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
Snapshot::Kind kind = d_->kind();
|
|
#endif
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
FieldPtr field = static_cast<FieldPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(field, kFieldCid, Field::InstanceSize());
|
|
d.ReadFromTo(field);
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d_->kind() != Snapshot::kFullAOT);
|
|
field->untag()->guarded_list_length_ = static_cast<SmiPtr>(d.ReadRef());
|
|
if (kind == Snapshot::kFullJIT) {
|
|
field->untag()->dependent_code_ =
|
|
static_cast<WeakArrayPtr>(d.ReadRef());
|
|
}
|
|
field->untag()->token_pos_ = d.ReadTokenPosition();
|
|
field->untag()->end_token_pos_ = d.ReadTokenPosition();
|
|
field->untag()->guarded_cid_ = d.ReadCid();
|
|
field->untag()->is_nullable_ = d.ReadCid();
|
|
const int8_t static_type_exactness_state = d.Read<int8_t>();
|
|
#if defined(TARGET_ARCH_X64)
|
|
field->untag()->static_type_exactness_state_ =
|
|
static_type_exactness_state;
|
|
#else
|
|
// We might produce core snapshots using X64 VM and then consume
|
|
// them in IA32 or ARM VM. In which case we need to simply ignore
|
|
// static type exactness state written into snapshot because non-X64
|
|
// builds don't have this feature enabled.
|
|
// TODO(dartbug.com/34170) Support other architectures.
|
|
USE(static_type_exactness_state);
|
|
field->untag()->static_type_exactness_state_ =
|
|
StaticTypeExactnessState::NotTracking().Encode();
|
|
#endif // defined(TARGET_ARCH_X64)
|
|
field->untag()->kernel_offset_ = d.Read<uint32_t>();
|
|
#endif
|
|
field->untag()->kind_bits_ = d.Read<uint32_t>();
|
|
|
|
field->untag()->host_offset_or_field_id_ =
|
|
static_cast<SmiPtr>(d.ReadRef());
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
field->untag()->target_offset_ =
|
|
Smi::Value(field->untag()->host_offset_or_field_id());
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Field& field = Field::Handle(d->zone());
|
|
if (!d->isolate_group()->use_field_guards()) {
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
field ^= refs.At(i);
|
|
field.set_guarded_cid_unsafe(kDynamicCid);
|
|
field.set_is_nullable_unsafe(true);
|
|
field.set_guarded_list_length_unsafe(Field::kNoFixedLength);
|
|
field.set_guarded_list_length_in_object_offset_unsafe(
|
|
Field::kUnknownLengthOffset);
|
|
field.set_static_type_exactness_state_unsafe(
|
|
StaticTypeExactnessState::NotTracking());
|
|
}
|
|
} else {
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
field ^= refs.At(i);
|
|
field.InitializeGuardedListLengthInObjectOffset(/*unsafe=*/true);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ScriptSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ScriptSerializationCluster()
|
|
: SerializationCluster("Script",
|
|
kScriptCid,
|
|
compiler::target::Script::InstanceSize()) {}
|
|
~ScriptSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ScriptPtr script = Script::RawCast(object);
|
|
objects_.Add(script);
|
|
auto* from = script->untag()->from();
|
|
auto* to = script->untag()->to_snapshot(s->kind());
|
|
for (auto* p = from; p <= to; p++) {
|
|
const intptr_t offset =
|
|
reinterpret_cast<uword>(p) - reinterpret_cast<uword>(script->untag());
|
|
const ObjectPtr obj = p->Decompress(script->heap_base());
|
|
if (offset == Script::line_starts_offset()) {
|
|
// Line starts are delta encoded.
|
|
s->Push(obj, kDeltaEncodedTypedDataCid);
|
|
} else {
|
|
s->Push(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ScriptPtr script = objects_[i];
|
|
s->AssignRef(script);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ScriptPtr script = objects_[i];
|
|
AutoTraceObjectName(script, script->untag()->url());
|
|
WriteFromTo(script);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
// Clear out the max position cache in snapshots to ensure no
|
|
// differences in the snapshot due to triggering caching vs. not.
|
|
int32_t written_flags =
|
|
UntaggedScript::CachedMaxPositionBitField::update(
|
|
0, script->untag()->flags_and_max_position_);
|
|
written_flags = UntaggedScript::HasCachedMaxPositionBit::update(
|
|
false, written_flags);
|
|
s->Write<int32_t>(written_flags);
|
|
}
|
|
s->Write<int32_t>(script->untag()->kernel_script_index_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ScriptPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ScriptDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ScriptDeserializationCluster() : DeserializationCluster("Script") {}
|
|
~ScriptDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Script::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ScriptPtr script = static_cast<ScriptPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(script, kScriptCid,
|
|
Script::InstanceSize());
|
|
d.ReadFromTo(script);
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
script->untag()->flags_and_max_position_ = d.Read<int32_t>();
|
|
#endif
|
|
script->untag()->kernel_script_index_ = d.Read<int32_t>();
|
|
script->untag()->load_timestamp_ = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class LibrarySerializationCluster : public SerializationCluster {
|
|
public:
|
|
LibrarySerializationCluster()
|
|
: SerializationCluster("Library",
|
|
kLibraryCid,
|
|
compiler::target::Library::InstanceSize()) {}
|
|
~LibrarySerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
LibraryPtr lib = Library::RawCast(object);
|
|
objects_.Add(lib);
|
|
PushFromTo(lib);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LibraryPtr lib = objects_[i];
|
|
s->AssignRef(lib);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LibraryPtr lib = objects_[i];
|
|
AutoTraceObjectName(lib, lib->untag()->url());
|
|
WriteFromTo(lib);
|
|
s->Write<int32_t>(lib->untag()->index_);
|
|
s->Write<uint16_t>(lib->untag()->num_imports_);
|
|
s->Write<int8_t>(lib->untag()->load_state_);
|
|
s->Write<uint8_t>(lib->untag()->flags_);
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
s->Write<uint32_t>(lib->untag()->kernel_library_index_);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<LibraryPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class LibraryDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
LibraryDeserializationCluster() : DeserializationCluster("Library") {}
|
|
~LibraryDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Library::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
LibraryPtr lib = static_cast<LibraryPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(lib, kLibraryCid, Library::InstanceSize());
|
|
d.ReadFromTo(lib);
|
|
lib->untag()->native_entry_resolver_ = nullptr;
|
|
lib->untag()->native_entry_symbol_resolver_ = nullptr;
|
|
lib->untag()->ffi_native_resolver_ = nullptr;
|
|
lib->untag()->index_ = d.Read<int32_t>();
|
|
lib->untag()->num_imports_ = d.Read<uint16_t>();
|
|
lib->untag()->load_state_ = d.Read<int8_t>();
|
|
lib->untag()->flags_ = d.Read<uint8_t>();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d_->kind() != Snapshot::kFullAOT);
|
|
lib->untag()->kernel_library_index_ = d.Read<uint32_t>();
|
|
#endif
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class NamespaceSerializationCluster : public SerializationCluster {
|
|
public:
|
|
NamespaceSerializationCluster()
|
|
: SerializationCluster("Namespace",
|
|
kNamespaceCid,
|
|
compiler::target::Namespace::InstanceSize()) {}
|
|
~NamespaceSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
NamespacePtr ns = Namespace::RawCast(object);
|
|
objects_.Add(ns);
|
|
PushFromTo(ns);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
NamespacePtr ns = objects_[i];
|
|
s->AssignRef(ns);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
NamespacePtr ns = objects_[i];
|
|
AutoTraceObject(ns);
|
|
WriteFromTo(ns);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<NamespacePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class NamespaceDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
NamespaceDeserializationCluster() : DeserializationCluster("Namespace") {}
|
|
~NamespaceDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Namespace::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
NamespacePtr ns = static_cast<NamespacePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(ns, kNamespaceCid,
|
|
Namespace::InstanceSize());
|
|
d.ReadFromTo(ns);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
// KernelProgramInfo objects are not written into a full AOT snapshot.
|
|
class KernelProgramInfoSerializationCluster : public SerializationCluster {
|
|
public:
|
|
KernelProgramInfoSerializationCluster()
|
|
: SerializationCluster(
|
|
"KernelProgramInfo",
|
|
kKernelProgramInfoCid,
|
|
compiler::target::KernelProgramInfo::InstanceSize()) {}
|
|
~KernelProgramInfoSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
KernelProgramInfoPtr info = KernelProgramInfo::RawCast(object);
|
|
objects_.Add(info);
|
|
PushFromTo(info);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
KernelProgramInfoPtr info = objects_[i];
|
|
s->AssignRef(info);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
KernelProgramInfoPtr info = objects_[i];
|
|
AutoTraceObject(info);
|
|
WriteFromTo(info);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<KernelProgramInfoPtr> objects_;
|
|
};
|
|
|
|
// Since KernelProgramInfo objects are not written into full AOT snapshots,
|
|
// one will never need to read them from a full AOT snapshot.
|
|
class KernelProgramInfoDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
KernelProgramInfoDeserializationCluster()
|
|
: DeserializationCluster("KernelProgramInfo") {}
|
|
~KernelProgramInfoDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, KernelProgramInfo::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
KernelProgramInfoPtr info = static_cast<KernelProgramInfoPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(info, kKernelProgramInfoCid,
|
|
KernelProgramInfo::InstanceSize());
|
|
d.ReadFromTo(info);
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Array& array = Array::Handle(d->zone());
|
|
KernelProgramInfo& info = KernelProgramInfo::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
info ^= refs.At(id);
|
|
array = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
|
|
info.set_libraries_cache(array);
|
|
array = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
|
|
info.set_classes_cache(array);
|
|
}
|
|
}
|
|
};
|
|
|
|
class CodeSerializationCluster : public SerializationCluster {
|
|
public:
|
|
explicit CodeSerializationCluster(Heap* heap)
|
|
: SerializationCluster("Code", kCodeCid), array_(Array::Handle()) {}
|
|
~CodeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
CodePtr code = Code::RawCast(object);
|
|
|
|
const bool is_deferred = !s->InCurrentLoadingUnitOrRoot(code);
|
|
if (is_deferred) {
|
|
s->RecordDeferredCode(code);
|
|
} else {
|
|
objects_.Add(code);
|
|
}
|
|
|
|
// Even if this code object is itself deferred we still need to scan
|
|
// the pool for references to other code objects (which might reside
|
|
// in the current loading unit).
|
|
ObjectPoolPtr pool = code->untag()->object_pool_;
|
|
if (s->kind() == Snapshot::kFullAOT) {
|
|
TracePool(s, pool, /*only_call_targets=*/is_deferred);
|
|
} else {
|
|
if (s->InCurrentLoadingUnitOrRoot(pool)) {
|
|
s->Push(pool);
|
|
} else {
|
|
TracePool(s, pool, /*only_call_targets=*/true);
|
|
}
|
|
}
|
|
|
|
if (s->kind() == Snapshot::kFullJIT) {
|
|
s->Push(code->untag()->deopt_info_array_);
|
|
s->Push(code->untag()->static_calls_target_table_);
|
|
s->Push(code->untag()->compressed_stackmaps_);
|
|
} else if (s->kind() == Snapshot::kFullAOT) {
|
|
// Note: we don't trace compressed_stackmaps_ because we are going to emit
|
|
// a separate mapping table into RO data which is not going to be a real
|
|
// heap object.
|
|
#if defined(DART_PRECOMPILER)
|
|
auto const calls_array = code->untag()->static_calls_target_table_;
|
|
if (calls_array != Array::null()) {
|
|
#if defined(DART_SHOREBIRD_INTERPRETER)
|
|
// Keep the full table in Shorebird interpreter snapshots. Runtime
|
|
// static-call resolution needs Function targets, not just Code reachability.
|
|
s->Push(calls_array);
|
|
#else
|
|
// Some Code entries in the static calls target table may only be
|
|
// accessible via here, so push the Code objects.
|
|
array_ = calls_array;
|
|
for (auto entry : StaticCallsTable(array_)) {
|
|
auto kind = Code::KindField::decode(
|
|
Smi::Value(entry.Get<Code::kSCallTableKindAndOffset>()));
|
|
switch (kind) {
|
|
case Code::kCallViaCode:
|
|
// Code object in the pool.
|
|
continue;
|
|
case Code::kPcRelativeTTSCall:
|
|
// TTS will be reachable through type object which itself is
|
|
// in the pool.
|
|
continue;
|
|
case Code::kPcRelativeCall:
|
|
case Code::kPcRelativeTailCall:
|
|
auto destination = entry.Get<Code::kSCallTableCodeOrTypeTarget>();
|
|
ASSERT(destination->IsHeapObject() && destination->IsCode());
|
|
s->Push(destination);
|
|
}
|
|
}
|
|
#endif // defined(DART_SHOREBIRD_INTERPRETER)
|
|
}
|
|
#else
|
|
UNREACHABLE();
|
|
#endif
|
|
}
|
|
|
|
if (Code::IsDiscarded(code)) {
|
|
ASSERT(s->kind() == Snapshot::kFullAOT && FLAG_dwarf_stack_traces_mode &&
|
|
!FLAG_retain_code_objects);
|
|
// Only object pool and static call table entries and the compressed
|
|
// stack maps should be pushed.
|
|
return;
|
|
}
|
|
|
|
s->Push(code->untag()->owner_);
|
|
s->Push(code->untag()->exception_handlers_);
|
|
s->Push(code->untag()->pc_descriptors_);
|
|
s->Push(code->untag()->catch_entry_);
|
|
if (!FLAG_precompiled_mode || !FLAG_dwarf_stack_traces_mode) {
|
|
s->Push(code->untag()->inlined_id_to_function_);
|
|
if (s->InCurrentLoadingUnitOrRoot(code->untag()->code_source_map_)) {
|
|
s->Push(code->untag()->code_source_map_);
|
|
}
|
|
}
|
|
#if !defined(PRODUCT)
|
|
s->Push(code->untag()->return_address_metadata_);
|
|
if (s->isolate_group()->code_comments()) {
|
|
s->Push(code->untag()->comments_);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void TracePool(Serializer* s, ObjectPoolPtr pool, bool only_call_targets) {
|
|
if (pool == ObjectPool::null()) {
|
|
return;
|
|
}
|
|
|
|
const intptr_t length = pool->untag()->length_;
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
|
|
if (entry_type == ObjectPool::EntryType::kTaggedObject) {
|
|
const ObjectPtr target = pool->untag()->data()[i].raw_obj_;
|
|
// A field is a call target because its initializer may be called
|
|
// indirectly by passing the field to the runtime. A const closure
|
|
// is a call target because its function may be called indirectly
|
|
// via a closure call.
|
|
intptr_t cid = target->GetClassId();
|
|
if (!only_call_targets || (cid == kCodeCid) || (cid == kFunctionCid) ||
|
|
(cid == kFieldCid) || (cid == kClosureCid)) {
|
|
s->Push(target);
|
|
} else if (cid >= kNumPredefinedCids) {
|
|
s->Push(s->isolate_group()->class_table()->At(cid));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CodeOrderInfo {
|
|
CodePtr code;
|
|
intptr_t not_discarded; // 1 if this code was not discarded and
|
|
// 0 otherwise.
|
|
intptr_t instructions_id;
|
|
};
|
|
|
|
// We sort code objects in such a way that code objects with the same
|
|
// instructions are grouped together and ensure that all instructions
|
|
// without associated code objects are grouped together at the beginning of
|
|
// the code section. InstructionsTable encoding assumes that all
|
|
// instructions with non-discarded Code objects are grouped at the end.
|
|
//
|
|
// Note that in AOT mode we expect that all Code objects pointing to
|
|
// the same instructions are deduplicated, as in bare instructions mode
|
|
// there is no way to identify which specific Code object (out of those
|
|
// which point to the specific instructions range) actually corresponds
|
|
// to a particular frame.
|
|
static int CompareCodeOrderInfo(CodeOrderInfo const* a,
|
|
CodeOrderInfo const* b) {
|
|
if (a->not_discarded < b->not_discarded) return -1;
|
|
if (a->not_discarded > b->not_discarded) return 1;
|
|
if (a->instructions_id < b->instructions_id) return -1;
|
|
if (a->instructions_id > b->instructions_id) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void Insert(Serializer* s,
|
|
GrowableArray<CodeOrderInfo>* order_list,
|
|
IntMap<intptr_t>* order_map,
|
|
CodePtr code) {
|
|
InstructionsPtr instr = code->untag()->instructions_;
|
|
intptr_t key = static_cast<intptr_t>(instr);
|
|
intptr_t instructions_id = 0;
|
|
|
|
if (order_map->HasKey(key)) {
|
|
// We are expected to merge code objects which point to the same
|
|
// instructions in the precompiled mode.
|
|
RELEASE_ASSERT(!FLAG_precompiled_mode);
|
|
instructions_id = order_map->Lookup(key);
|
|
} else {
|
|
instructions_id = order_map->Length() + 1;
|
|
order_map->Insert(key, instructions_id);
|
|
}
|
|
CodeOrderInfo info;
|
|
info.code = code;
|
|
info.instructions_id = instructions_id;
|
|
info.not_discarded = Code::IsDiscarded(code) ? 0 : 1;
|
|
order_list->Add(info);
|
|
}
|
|
|
|
static void Sort(Serializer* s, GrowableArray<CodePtr>* codes) {
|
|
GrowableArray<CodeOrderInfo> order_list;
|
|
IntMap<intptr_t> order_map;
|
|
for (intptr_t i = 0; i < codes->length(); i++) {
|
|
Insert(s, &order_list, &order_map, (*codes)[i]);
|
|
}
|
|
order_list.Sort(CompareCodeOrderInfo);
|
|
ASSERT(order_list.length() == codes->length());
|
|
for (intptr_t i = 0; i < order_list.length(); i++) {
|
|
(*codes)[i] = order_list[i].code;
|
|
}
|
|
}
|
|
|
|
static void Sort(Serializer* s, GrowableArray<Code*>* codes) {
|
|
GrowableArray<CodeOrderInfo> order_list;
|
|
IntMap<intptr_t> order_map;
|
|
for (intptr_t i = 0; i < codes->length(); i++) {
|
|
Insert(s, &order_list, &order_map, (*codes)[i]->ptr());
|
|
}
|
|
order_list.Sort(CompareCodeOrderInfo);
|
|
ASSERT(order_list.length() == codes->length());
|
|
for (intptr_t i = 0; i < order_list.length(); i++) {
|
|
*(*codes)[i] = order_list[i].code;
|
|
}
|
|
}
|
|
|
|
intptr_t NonDiscardedCodeCount() {
|
|
intptr_t count = 0;
|
|
for (auto code : objects_) {
|
|
if (!Code::IsDiscarded(code)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t non_discarded_count = NonDiscardedCodeCount();
|
|
const intptr_t count = objects_.length();
|
|
ASSERT(count == non_discarded_count || (s->kind() == Snapshot::kFullAOT));
|
|
|
|
first_ref_ = s->next_ref_index();
|
|
s->WriteUnsigned(non_discarded_count);
|
|
for (auto code : objects_) {
|
|
if (!Code::IsDiscarded(code)) {
|
|
WriteAlloc(s, code);
|
|
} else {
|
|
// Mark discarded code unreachable, so that we could later
|
|
// assign artificial references to it.
|
|
s->heap()->SetObjectId(code, kUnreachableReference);
|
|
}
|
|
}
|
|
|
|
s->WriteUnsigned(deferred_objects_.length());
|
|
first_deferred_ref_ = s->next_ref_index();
|
|
for (auto code : deferred_objects_) {
|
|
ASSERT(!Code::IsDiscarded(code));
|
|
WriteAlloc(s, code);
|
|
}
|
|
last_ref_ = s->next_ref_index() - 1;
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s, CodePtr code) {
|
|
ASSERT(!Code::IsDiscarded(code));
|
|
s->AssignRef(code);
|
|
AutoTraceObjectName(code, MakeDisambiguatedCodeName(s, code));
|
|
const int32_t state_bits = code->untag()->state_bits_;
|
|
s->Write<int32_t>(state_bits);
|
|
target_memory_size_ += compiler::target::Code::InstanceSize(0);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
s->WriteRefId(s->RefId(StubCode::LazyCompile().ptr()));
|
|
s->WriteRefId(s->RefId(StubCode::UnknownDartCode().ptr()));
|
|
|
|
Snapshot::Kind kind = s->kind();
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
CodePtr code = objects_[i];
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr &&
|
|
Code::IsDiscarded(code)) {
|
|
s->CreateArtificialNodeIfNeeded(code);
|
|
}
|
|
#endif
|
|
// Note: for discarded code this function will not write anything out
|
|
// it is only called to produce information into snapshot profile.
|
|
WriteFill(s, kind, code, /*deferred=*/false);
|
|
}
|
|
const intptr_t deferred_count = deferred_objects_.length();
|
|
for (intptr_t i = 0; i < deferred_count; i++) {
|
|
CodePtr code = deferred_objects_[i];
|
|
WriteFill(s, kind, code, /*deferred=*/true);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s,
|
|
Snapshot::Kind kind,
|
|
CodePtr code,
|
|
bool deferred) {
|
|
const intptr_t bytes_written = s->bytes_written();
|
|
AutoTraceObjectName(code, MakeDisambiguatedCodeName(s, code));
|
|
|
|
intptr_t pointer_offsets_length =
|
|
Code::PtrOffBits::decode(code->untag()->state_bits_);
|
|
if (pointer_offsets_length != 0) {
|
|
FATAL("Cannot serialize code with embedded pointers");
|
|
}
|
|
if (kind == Snapshot::kFullAOT && Code::IsDisabled(code)) {
|
|
// Disabled code is fatal in AOT since we cannot recompile.
|
|
s->UnexpectedObject(code, "Disabled code");
|
|
}
|
|
|
|
s->WriteInstructions(code->untag()->instructions_,
|
|
code->untag()->unchecked_offset_, code, deferred);
|
|
if (kind == Snapshot::kFullJIT) {
|
|
// TODO(rmacnak): Fix references to disabled code before serializing.
|
|
// For now, we may write the FixCallersTarget or equivalent stub. This
|
|
// will cause a fixup if this code is called.
|
|
const uint32_t active_unchecked_offset =
|
|
code->untag()->unchecked_entry_point_ - code->untag()->entry_point_;
|
|
s->WriteInstructions(code->untag()->active_instructions_,
|
|
active_unchecked_offset, code, deferred);
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
// If we are writing V8 snapshot profile then attribute references going
|
|
// through the object pool and static calls to the code object itself.
|
|
if (kind == Snapshot::kFullAOT &&
|
|
code->untag()->object_pool_ != ObjectPool::null()) {
|
|
ObjectPoolPtr pool = code->untag()->object_pool_;
|
|
// Non-empty per-code object pools should not be reachable in this mode.
|
|
ASSERT(!s->HasRef(pool) || pool == Object::empty_object_pool().ptr());
|
|
s->CreateArtificialNodeIfNeeded(pool);
|
|
s->AttributePropertyRef(pool, "object_pool_");
|
|
}
|
|
if (kind != Snapshot::kFullJIT &&
|
|
code->untag()->static_calls_target_table_ != Array::null()) {
|
|
auto const table = code->untag()->static_calls_target_table_;
|
|
// Non-empty static call target tables shouldn't be reachable in this
|
|
// mode.
|
|
ASSERT(!s->HasRef(table) || table == Object::empty_array().ptr());
|
|
s->CreateArtificialNodeIfNeeded(table);
|
|
s->AttributePropertyRef(table, "static_calls_target_table_");
|
|
}
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
if (Code::IsDiscarded(code)) {
|
|
// No bytes should be written to represent this code.
|
|
ASSERT(s->bytes_written() == bytes_written);
|
|
// Only write instructions, compressed stackmaps and state bits
|
|
// for the discarded Code objects.
|
|
ASSERT(kind == Snapshot::kFullAOT && FLAG_dwarf_stack_traces_mode &&
|
|
!FLAG_retain_code_objects);
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
// Keep the owner as a (possibly artificial) node for snapshot analysis.
|
|
const auto& owner = code->untag()->owner_;
|
|
s->CreateArtificialNodeIfNeeded(owner);
|
|
s->AttributePropertyRef(owner, "owner_");
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// No need to write object pool out if we are producing full AOT
|
|
// snapshot with bare instructions.
|
|
if (kind != Snapshot::kFullAOT) {
|
|
if (s->InCurrentLoadingUnitOrRoot(code->untag()->object_pool_)) {
|
|
WriteField(code, object_pool_);
|
|
} else {
|
|
WriteFieldValue(object_pool_, ObjectPool::null());
|
|
}
|
|
}
|
|
WriteField(code, owner_);
|
|
WriteField(code, exception_handlers_);
|
|
WriteField(code, pc_descriptors_);
|
|
WriteField(code, catch_entry_);
|
|
if (s->kind() == Snapshot::kFullJIT) {
|
|
WriteField(code, compressed_stackmaps_);
|
|
}
|
|
if (FLAG_precompiled_mode && FLAG_dwarf_stack_traces_mode) {
|
|
WriteFieldValue(inlined_id_to_function_, Array::null());
|
|
WriteFieldValue(code_source_map_, CodeSourceMap::null());
|
|
} else {
|
|
WriteField(code, inlined_id_to_function_);
|
|
if (s->InCurrentLoadingUnitOrRoot(code->untag()->code_source_map_)) {
|
|
WriteField(code, code_source_map_);
|
|
} else {
|
|
WriteFieldValue(code_source_map_, CodeSourceMap::null());
|
|
}
|
|
}
|
|
if (kind == Snapshot::kFullJIT) {
|
|
WriteField(code, deopt_info_array_);
|
|
WriteField(code, static_calls_target_table_);
|
|
#if defined(DART_SHOREBIRD_INTERPRETER)
|
|
} else if (kind == Snapshot::kFullAOT) {
|
|
WriteField(code, static_calls_target_table_);
|
|
#endif
|
|
}
|
|
|
|
#if !defined(PRODUCT)
|
|
WriteField(code, return_address_metadata_);
|
|
if (s->isolate_group()->code_comments()) {
|
|
WriteField(code, comments_);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
GrowableArray<CodePtr>* objects() { return &objects_; }
|
|
GrowableArray<CodePtr>* deferred_objects() { return &deferred_objects_; }
|
|
|
|
static const char* MakeDisambiguatedCodeName(Serializer* s, CodePtr c) {
|
|
if (s->profile_writer() == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
REUSABLE_CODE_HANDLESCOPE(s->thread());
|
|
Code& code = reused_code_handle.Handle();
|
|
code = c;
|
|
return code.QualifiedName(
|
|
NameFormattingParams::DisambiguatedWithoutClassName(
|
|
Object::NameVisibility::kInternalName));
|
|
}
|
|
|
|
intptr_t first_ref() const { return first_ref_; }
|
|
intptr_t first_deferred_ref() const { return first_deferred_ref_; }
|
|
intptr_t last_ref() const { return last_ref_; }
|
|
|
|
private:
|
|
intptr_t first_ref_;
|
|
intptr_t first_deferred_ref_;
|
|
intptr_t last_ref_;
|
|
GrowableArray<CodePtr> objects_;
|
|
GrowableArray<CodePtr> deferred_objects_;
|
|
Array& array_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class CodeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
CodeDeserializationCluster() : DeserializationCluster("Code") {}
|
|
~CodeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
d->set_code_start_index(start_index_);
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ReadAllocOneCode(d);
|
|
}
|
|
stop_index_ = d->next_index();
|
|
d->set_code_stop_index(stop_index_);
|
|
deferred_start_index_ = d->next_index();
|
|
const intptr_t deferred_count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < deferred_count; i++) {
|
|
ReadAllocOneCode(d);
|
|
}
|
|
deferred_stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadAllocOneCode(Deserializer* d) {
|
|
const int32_t state_bits = d->Read<int32_t>();
|
|
ASSERT(!Code::DiscardedBit::decode(state_bits));
|
|
auto code = static_cast<CodePtr>(d->Allocate(Code::InstanceSize(0)));
|
|
d->AssignRef(code);
|
|
code->untag()->state_bits_ = state_bits;
|
|
}
|
|
|
|
void ReadFill(Deserializer* d) override {
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
d->set_lazy_compile_index(d->ReadRefId());
|
|
d->set_unknown_dart_code_index(d->ReadRefId());
|
|
ReadFill(d, start_index_, stop_index_, false);
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
ReadFill(d, deferred_start_index_, deferred_stop_index_, true);
|
|
#else
|
|
ASSERT(deferred_start_index_ == deferred_stop_index_);
|
|
#endif
|
|
}
|
|
|
|
void ReadFill(Deserializer* d,
|
|
intptr_t start_index,
|
|
intptr_t stop_index,
|
|
bool deferred) {
|
|
for (intptr_t id = start_index, n = stop_index; id < n; id++) {
|
|
auto const code = static_cast<CodePtr>(d->Ref(id));
|
|
|
|
Deserializer::InitializeHeader(code, kCodeCid, Code::InstanceSize(0));
|
|
ASSERT(!Code::IsDiscarded(code));
|
|
|
|
d->ReadInstructions(code, deferred);
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d->kind() == Snapshot::kFullJIT);
|
|
code->untag()->object_pool_ = static_cast<ObjectPoolPtr>(d->ReadRef());
|
|
#else
|
|
ASSERT(d->kind() == Snapshot::kFullAOT);
|
|
// There is a single global pool.
|
|
code->untag()->object_pool_ = ObjectPool::null();
|
|
#endif
|
|
code->untag()->owner_ = d->ReadRef();
|
|
code->untag()->exception_handlers_ =
|
|
static_cast<ExceptionHandlersPtr>(d->ReadRef());
|
|
code->untag()->pc_descriptors_ =
|
|
static_cast<PcDescriptorsPtr>(d->ReadRef());
|
|
code->untag()->catch_entry_ = d->ReadRef();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d->kind() == Snapshot::kFullJIT);
|
|
code->untag()->compressed_stackmaps_ =
|
|
static_cast<CompressedStackMapsPtr>(d->ReadRef());
|
|
#else
|
|
ASSERT(d->kind() == Snapshot::kFullAOT);
|
|
code->untag()->compressed_stackmaps_ = CompressedStackMaps::null();
|
|
#endif
|
|
code->untag()->inlined_id_to_function_ =
|
|
static_cast<ArrayPtr>(d->ReadRef());
|
|
code->untag()->code_source_map_ =
|
|
static_cast<CodeSourceMapPtr>(d->ReadRef());
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(d->kind() == Snapshot::kFullJIT);
|
|
code->untag()->deopt_info_array_ = static_cast<ArrayPtr>(d->ReadRef());
|
|
code->untag()->static_calls_target_table_ =
|
|
static_cast<ArrayPtr>(d->ReadRef());
|
|
#elif defined(DART_SHOREBIRD_INTERPRETER)
|
|
ASSERT(d->kind() == Snapshot::kFullAOT);
|
|
code->untag()->static_calls_target_table_ =
|
|
static_cast<ArrayPtr>(d->ReadRef());
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
#if !defined(PRODUCT)
|
|
code->untag()->return_address_metadata_ = d->ReadRef();
|
|
code->untag()->var_descriptors_ = LocalVarDescriptors::null();
|
|
code->untag()->comments_ = d->isolate_group()->code_comments()
|
|
? static_cast<ArrayPtr>(d->ReadRef())
|
|
: Array::null();
|
|
code->untag()->compile_timestamp_ = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
d->EndInstructions();
|
|
|
|
#if !defined(PRODUCT)
|
|
if (!CodeObservers::AreActive() && !FLAG_support_disassembler) return;
|
|
#endif
|
|
Code& code = Code::Handle(d->zone());
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
Object& owner = Object::Handle(d->zone());
|
|
#endif
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
code ^= refs.At(id);
|
|
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(PRODUCT)
|
|
if (CodeObservers::AreActive()) {
|
|
Code::NotifyCodeObservers(code, code.is_optimized());
|
|
}
|
|
#endif
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
owner = code.owner();
|
|
if (owner.IsFunction()) {
|
|
if ((FLAG_disassemble ||
|
|
(code.is_optimized() && FLAG_disassemble_optimized)) &&
|
|
compiler::PrintFilter::ShouldPrint(Function::Cast(owner))) {
|
|
Disassembler::DisassembleCode(Function::Cast(owner), code,
|
|
code.is_optimized());
|
|
}
|
|
} else if (FLAG_disassemble_stubs) {
|
|
Disassembler::DisassembleStub(code.Name(), code);
|
|
}
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
}
|
|
}
|
|
|
|
private:
|
|
intptr_t deferred_start_index_;
|
|
intptr_t deferred_stop_index_;
|
|
};
|
|
|
|
#if defined(DART_BYTECODE_INTERPRETER)
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class BytecodeSerializationCluster : public SerializationCluster {
|
|
public:
|
|
BytecodeSerializationCluster()
|
|
: SerializationCluster("Bytecode",
|
|
kBytecodeCid,
|
|
compiler::target::Bytecode::InstanceSize()) {}
|
|
~BytecodeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
BytecodePtr code = Bytecode::RawCast(object);
|
|
objects_.Add(code);
|
|
PushFromTo(code);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
BytecodePtr code = objects_[i];
|
|
s->AssignRef(code);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
BytecodePtr code = objects_[i];
|
|
AutoTraceObject(code);
|
|
WriteFromTo(code);
|
|
|
|
uint8_t* data = reinterpret_cast<uint8_t*>(code->untag()->instructions_);
|
|
intptr_t length = code->untag()->instructions_size_;
|
|
s->WriteUnsigned(length);
|
|
s->WriteBytes(data, length);
|
|
}
|
|
}
|
|
|
|
GrowableArray<BytecodePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class BytecodeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
BytecodeDeserializationCluster() : DeserializationCluster("Bytecode") {}
|
|
~BytecodeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Bytecode::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
BytecodePtr code = static_cast<BytecodePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(code, kBytecodeCid,
|
|
Bytecode::InstanceSize());
|
|
d.ReadFromTo(code);
|
|
|
|
intptr_t length = d.ReadUnsigned();
|
|
code->untag()->instructions_ =
|
|
reinterpret_cast<uword>(d.AddressOfCurrentPosition());
|
|
code->untag()->instructions_size_ = length;
|
|
code->untag()->instructions_binary_offset_ = -1;
|
|
code->untag()->code_offset_ = 0;
|
|
code->untag()->source_positions_binary_offset_ = 0;
|
|
d.Advance(length);
|
|
}
|
|
}
|
|
};
|
|
#endif // defined(DART_BYTECODE_INTERPRETER)
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ObjectPoolSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ObjectPoolSerializationCluster()
|
|
: SerializationCluster("ObjectPool", kObjectPoolCid) {}
|
|
~ObjectPoolSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ObjectPoolPtr pool = ObjectPool::RawCast(object);
|
|
objects_.Add(pool);
|
|
|
|
if (s->kind() != Snapshot::kFullAOT) {
|
|
const intptr_t length = pool->untag()->length_;
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
|
|
if (entry_type == ObjectPool::EntryType::kTaggedObject) {
|
|
s->Push(pool->untag()->data()[i].raw_obj_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ObjectPoolPtr pool = objects_[i];
|
|
s->AssignRef(pool);
|
|
AutoTraceObject(pool);
|
|
const intptr_t length = pool->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ += compiler::target::ObjectPool::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
bool weak = s->kind() == Snapshot::kFullAOT;
|
|
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ObjectPoolPtr pool = objects_[i];
|
|
AutoTraceObject(pool);
|
|
const intptr_t length = pool->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
uint8_t bits = entry_bits[j];
|
|
ObjectPool::EntryType type = ObjectPool::TypeBits::decode(bits);
|
|
auto snapshot_behavior = ObjectPool::SnapshotBehaviorBits::decode(bits);
|
|
ASSERT(snapshot_behavior !=
|
|
ObjectPool::SnapshotBehavior::kNotSnapshotable);
|
|
s->Write<uint8_t>(bits);
|
|
if (snapshot_behavior != ObjectPool::SnapshotBehavior::kSnapshotable) {
|
|
// The deserializer will reset this to a specific value, no need to
|
|
// write anything.
|
|
continue;
|
|
}
|
|
switch (type) {
|
|
case ObjectPool::EntryType::kTaggedObject: {
|
|
if (weak && !s->HasRef(entry.raw_obj_)) {
|
|
// Any value will do, but null has the shortest id.
|
|
s->WriteElementRef(Object::null(), j);
|
|
} else {
|
|
s->WriteElementRef(entry.raw_obj_, j);
|
|
}
|
|
break;
|
|
}
|
|
case ObjectPool::EntryType::kImmediate: {
|
|
s->Write<intptr_t>(entry.raw_value_);
|
|
break;
|
|
}
|
|
case ObjectPool::EntryType::kNativeFunction: {
|
|
// Write nothing. Will initialize with the lazy link entry.
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ObjectPoolPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ObjectPoolDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ObjectPoolDeserializationCluster() : DeserializationCluster("ObjectPool") {}
|
|
~ObjectPoolDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(ObjectPool::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
fill_position_ = d.Position();
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
const intptr_t length = d.ReadUnsigned();
|
|
ObjectPoolPtr pool = static_cast<ObjectPoolPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(pool, kObjectPoolCid,
|
|
ObjectPool::InstanceSize(length));
|
|
pool->untag()->length_ = length;
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
const uint8_t entry_bits = d.Read<uint8_t>();
|
|
pool->untag()->entry_bits()[j] = entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
#if defined(DEBUG)
|
|
entry.raw_value_ = 0xabababab;
|
|
#endif
|
|
const auto snapshot_behavior =
|
|
ObjectPool::SnapshotBehaviorBits::decode(entry_bits);
|
|
ASSERT(snapshot_behavior !=
|
|
ObjectPool::SnapshotBehavior::kNotSnapshotable);
|
|
switch (snapshot_behavior) {
|
|
case ObjectPool::SnapshotBehavior::kSnapshotable:
|
|
// Handled below.
|
|
break;
|
|
case ObjectPool::SnapshotBehavior::kResetToBootstrapNative:
|
|
entry.raw_obj_ = 0; // Stub not yet available.
|
|
continue;
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
case ObjectPool::SnapshotBehavior::
|
|
kResetToSwitchableCallMissEntryPoint:
|
|
entry.raw_value_ = 0; // Stub not yet available.
|
|
continue;
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
case ObjectPool::SnapshotBehavior::kSetToZero:
|
|
entry.raw_value_ = 0;
|
|
continue;
|
|
default:
|
|
FATAL("Unexpected snapshot behavior: %d\n", snapshot_behavior);
|
|
}
|
|
switch (ObjectPool::TypeBits::decode(entry_bits)) {
|
|
case ObjectPool::EntryType::kTaggedObject:
|
|
entry.raw_obj_ = d.ReadRef();
|
|
break;
|
|
case ObjectPool::EntryType::kImmediate:
|
|
entry.raw_value_ = d.Read<intptr_t>();
|
|
break;
|
|
case ObjectPool::EntryType::kNativeFunction: {
|
|
// Read nothing. Initialize with the lazy link entry.
|
|
uword new_entry = NativeEntry::LinkNativeCallEntry();
|
|
entry.raw_value_ = static_cast<intptr_t>(new_entry);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
const Code& call_bootstrap_native = StubCode::CallBootstrapNative();
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
const uword switchable_call_miss_entry_point =
|
|
StubCode::SwitchableCallMiss().MonomorphicEntryPoint();
|
|
#endif
|
|
ObjectPool& pool = ObjectPool::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
pool ^= refs.At(i);
|
|
intptr_t length = pool.Length();
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
switch (pool.SnapshotBehaviorAt(j)) {
|
|
case ObjectPool::SnapshotBehavior::kResetToBootstrapNative:
|
|
pool.SetObjectAt(j, call_bootstrap_native);
|
|
break;
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
case ObjectPool::SnapshotBehavior::
|
|
kResetToSwitchableCallMissEntryPoint:
|
|
pool.SetTypeAt(j, ObjectPool::EntryType::kImmediate,
|
|
ObjectPool::Patchability::kPatchable,
|
|
ObjectPool::SnapshotBehavior::kSnapshotable);
|
|
pool.SetRawValueAt(j, switchable_call_miss_entry_point);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME) && \
|
|
(!defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER))
|
|
if (FLAG_disassemble) {
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
pool ^= refs.At(i);
|
|
pool.DebugPrint();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
intptr_t fill_position_ = 0;
|
|
};
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
class WeakSerializationReferenceSerializationCluster
|
|
: public SerializationCluster {
|
|
public:
|
|
WeakSerializationReferenceSerializationCluster()
|
|
: SerializationCluster(
|
|
"WeakSerializationReference",
|
|
compiler::target::WeakSerializationReference::InstanceSize()) {}
|
|
~WeakSerializationReferenceSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ASSERT(s->kind() == Snapshot::kFullAOT);
|
|
objects_.Add(WeakSerializationReference::RawCast(object));
|
|
}
|
|
|
|
void RetraceEphemerons(Serializer* s) {
|
|
for (intptr_t i = 0; i < objects_.length(); i++) {
|
|
WeakSerializationReferencePtr weak = objects_[i];
|
|
if (!s->IsReachable(weak->untag()->target())) {
|
|
s->Push(weak->untag()->replacement());
|
|
}
|
|
}
|
|
}
|
|
|
|
intptr_t Count(Serializer* s) { return objects_.length(); }
|
|
|
|
void CreateArtificialTargetNodesIfNeeded(Serializer* s) {
|
|
for (intptr_t i = 0; i < objects_.length(); i++) {
|
|
WeakSerializationReferencePtr weak = objects_[i];
|
|
s->CreateArtificialNodeIfNeeded(weak->untag()->target());
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
UNREACHABLE(); // No WSRs are serialized, and so this cluster is not added.
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
UNREACHABLE(); // No WSRs are serialized, and so this cluster is not added.
|
|
}
|
|
|
|
private:
|
|
GrowableArray<WeakSerializationReferencePtr> objects_;
|
|
};
|
|
#endif
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class PcDescriptorsSerializationCluster : public SerializationCluster {
|
|
public:
|
|
PcDescriptorsSerializationCluster()
|
|
: SerializationCluster("PcDescriptors", kPcDescriptorsCid) {}
|
|
~PcDescriptorsSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
PcDescriptorsPtr desc = PcDescriptors::RawCast(object);
|
|
objects_.Add(desc);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
PcDescriptorsPtr desc = objects_[i];
|
|
s->AssignRef(desc);
|
|
AutoTraceObject(desc);
|
|
const intptr_t length = desc->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::PcDescriptors::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
PcDescriptorsPtr desc = objects_[i];
|
|
AutoTraceObject(desc);
|
|
const intptr_t length = desc->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(desc->untag()->data());
|
|
s->WriteBytes(cdata, length);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<PcDescriptorsPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class PcDescriptorsDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
PcDescriptorsDeserializationCluster()
|
|
: DeserializationCluster("PcDescriptors") {}
|
|
~PcDescriptorsDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(PcDescriptors::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
const intptr_t length = d.ReadUnsigned();
|
|
PcDescriptorsPtr desc = static_cast<PcDescriptorsPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(desc, kPcDescriptorsCid,
|
|
PcDescriptors::InstanceSize(length));
|
|
desc->untag()->length_ = length;
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(desc->untag()->data());
|
|
d.ReadBytes(cdata, length);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class CodeSourceMapSerializationCluster : public SerializationCluster {
|
|
public:
|
|
CodeSourceMapSerializationCluster()
|
|
: SerializationCluster("CodeSourceMap", kCodeSourceMapCid) {}
|
|
~CodeSourceMapSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
CodeSourceMapPtr map = CodeSourceMap::RawCast(object);
|
|
objects_.Add(map);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
CodeSourceMapPtr map = objects_[i];
|
|
s->AssignRef(map);
|
|
AutoTraceObject(map);
|
|
const intptr_t length = map->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::PcDescriptors::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
CodeSourceMapPtr map = objects_[i];
|
|
AutoTraceObject(map);
|
|
const intptr_t length = map->untag()->length_;
|
|
s->WriteUnsigned(length);
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(map->untag()->data());
|
|
s->WriteBytes(cdata, length);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<CodeSourceMapPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class CodeSourceMapDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
CodeSourceMapDeserializationCluster()
|
|
: DeserializationCluster("CodeSourceMap") {}
|
|
~CodeSourceMapDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(CodeSourceMap::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
const intptr_t length = d.ReadUnsigned();
|
|
CodeSourceMapPtr map = static_cast<CodeSourceMapPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(map, kPcDescriptorsCid,
|
|
CodeSourceMap::InstanceSize(length));
|
|
map->untag()->length_ = length;
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(map->untag()->data());
|
|
d.ReadBytes(cdata, length);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class CompressedStackMapsSerializationCluster : public SerializationCluster {
|
|
public:
|
|
CompressedStackMapsSerializationCluster()
|
|
: SerializationCluster("CompressedStackMaps", kCompressedStackMapsCid) {}
|
|
~CompressedStackMapsSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
CompressedStackMapsPtr desc = CompressedStackMaps::RawCast(object);
|
|
objects_.Add(desc);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
CompressedStackMapsPtr map = objects_[i];
|
|
s->AssignRef(map);
|
|
AutoTraceObject(map);
|
|
const intptr_t length = UntaggedCompressedStackMaps::SizeField::decode(
|
|
map->untag()->payload()->flags_and_size());
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::CompressedStackMaps::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
CompressedStackMapsPtr map = objects_[i];
|
|
AutoTraceObject(map);
|
|
s->WriteUnsigned(map->untag()->payload()->flags_and_size());
|
|
const intptr_t length = UntaggedCompressedStackMaps::SizeField::decode(
|
|
map->untag()->payload()->flags_and_size());
|
|
uint8_t* cdata =
|
|
reinterpret_cast<uint8_t*>(map->untag()->payload()->data());
|
|
s->WriteBytes(cdata, length);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<CompressedStackMapsPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class CompressedStackMapsDeserializationCluster
|
|
: public DeserializationCluster {
|
|
public:
|
|
CompressedStackMapsDeserializationCluster()
|
|
: DeserializationCluster("CompressedStackMaps") {}
|
|
~CompressedStackMapsDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(CompressedStackMaps::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
const intptr_t flags_and_size = d.ReadUnsigned();
|
|
const intptr_t length =
|
|
UntaggedCompressedStackMaps::SizeField::decode(flags_and_size);
|
|
CompressedStackMapsPtr map =
|
|
static_cast<CompressedStackMapsPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(map, kCompressedStackMapsCid,
|
|
CompressedStackMaps::InstanceSize(length));
|
|
map->untag()->payload()->set_flags_and_size(flags_and_size);
|
|
uint8_t* cdata =
|
|
reinterpret_cast<uint8_t*>(map->untag()->payload()->data());
|
|
d.ReadBytes(cdata, length);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_COMPRESSED_POINTERS)
|
|
// PcDescriptor, CompressedStackMaps, OneByteString, TwoByteString
|
|
class RODataSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalStringSet,
|
|
String,
|
|
ObjectPtr> {
|
|
public:
|
|
RODataSerializationCluster(Zone* zone,
|
|
const char* type,
|
|
intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: CanonicalSetSerializationCluster(
|
|
cid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_canonical && IsStringClassId(cid),
|
|
ImageWriter::TagObjectTypeAsReadOnly(zone, type)),
|
|
type_(type) {}
|
|
~RODataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
// A string's hash must already be computed when we write it because it
|
|
// will be loaded into read-only memory. Extra bytes due to allocation
|
|
// rounding need to be deterministically set for reliable deduplication in
|
|
// shared images.
|
|
if (s->heap()->old_space()->IsObjectFromImagePages(object)) {
|
|
// This object is already read-only.
|
|
} else {
|
|
Object::FinalizeReadOnlyObject(object);
|
|
}
|
|
|
|
objects_.Add(object);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const bool is_string_cluster = IsStringClassId(cid_);
|
|
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
|
|
uint32_t running_offset = 0;
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ObjectPtr object = objects_[i];
|
|
s->AssignRef(object);
|
|
const StringPtr name =
|
|
is_string_cluster ? String::RawCast(object) : nullptr;
|
|
Serializer::WritingObjectScope scope(s, type_, object, name);
|
|
uint32_t offset = s->GetDataOffset(object);
|
|
s->TraceDataOffset(offset);
|
|
ASSERT(Utils::IsAligned(
|
|
offset, compiler::target::ObjectAlignment::kObjectAlignment));
|
|
ASSERT(offset > running_offset);
|
|
s->WriteUnsigned((offset - running_offset) >>
|
|
compiler::target::ObjectAlignment::kObjectAlignmentLog2);
|
|
running_offset = offset;
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
// No-op.
|
|
}
|
|
|
|
private:
|
|
const char* const type_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME && !DART_COMPRESSED_POINTERS
|
|
|
|
#if !defined(DART_COMPRESSED_POINTERS)
|
|
class RODataDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalStringSet> {
|
|
public:
|
|
RODataDeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit,
|
|
"ROData"),
|
|
cid_(cid) {}
|
|
~RODataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
intptr_t count = d->ReadUnsigned();
|
|
uint32_t running_offset = 0;
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
running_offset += d->ReadUnsigned() << kObjectAlignmentLog2;
|
|
ObjectPtr object = d->GetObjectAt(running_offset);
|
|
d->AssignRef(object);
|
|
}
|
|
stop_index_ = d->next_index();
|
|
if (cid_ == kStringCid) {
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
ASSERT(cid_ == kStringCid);
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(d, refs,
|
|
WeakArray::Handle(object_store->symbol_table()));
|
|
object_store->set_symbol_table(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
FATAL("Cannot recanonicalize RO objects.");
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
#endif // !DART_COMPRESSED_POINTERS
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class LocalVarDescriptorsSerializationCluster : public SerializationCluster {
|
|
public:
|
|
LocalVarDescriptorsSerializationCluster()
|
|
: SerializationCluster("LocalVarDescriptors", kLocalVarDescriptorsCid) {}
|
|
~LocalVarDescriptorsSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
LocalVarDescriptorsPtr desc = LocalVarDescriptors::RawCast(object);
|
|
objects_.Add(desc);
|
|
|
|
const intptr_t length = desc->untag()->num_entries_;
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
s->Push(desc->untag()->name(i));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LocalVarDescriptorsPtr desc = objects_[i];
|
|
s->AssignRef(desc);
|
|
AutoTraceObject(desc);
|
|
const intptr_t length = desc->untag()->num_entries_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::LocalVarDescriptors::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LocalVarDescriptorsPtr desc = objects_[i];
|
|
AutoTraceObject(desc);
|
|
const intptr_t length = desc->untag()->num_entries_;
|
|
s->WriteUnsigned(length);
|
|
WriteFromTo(desc, length);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
UntaggedLocalVarDescriptors::VarInfo& entry = desc->untag()->data()[j];
|
|
s->Write<int32_t>(entry.index_kind);
|
|
s->WriteTokenPosition(entry.declaration_pos);
|
|
s->WriteTokenPosition(entry.begin_pos);
|
|
s->WriteTokenPosition(entry.end_pos);
|
|
s->Write<int64_t>(entry.scope_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<LocalVarDescriptorsPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class LocalVarDescriptorsDeserializationCluster
|
|
: public DeserializationCluster {
|
|
public:
|
|
LocalVarDescriptorsDeserializationCluster()
|
|
: DeserializationCluster("LocalVarDescriptors") {}
|
|
~LocalVarDescriptorsDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(LocalVarDescriptors::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
LocalVarDescriptorsPtr desc =
|
|
static_cast<LocalVarDescriptorsPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(desc, kLocalVarDescriptorsCid,
|
|
LocalVarDescriptors::InstanceSize(length));
|
|
desc->untag()->num_entries_ = length;
|
|
d.ReadFromTo(desc, length);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
UntaggedLocalVarDescriptors::VarInfo& entry = desc->untag()->data()[j];
|
|
entry.index_kind = d.Read<int32_t>();
|
|
entry.declaration_pos = d.ReadTokenPosition();
|
|
entry.begin_pos = d.ReadTokenPosition();
|
|
entry.end_pos = d.ReadTokenPosition();
|
|
entry.scope_id = d.Read<int64_t>();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ExceptionHandlersSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ExceptionHandlersSerializationCluster()
|
|
: SerializationCluster("ExceptionHandlers", kExceptionHandlersCid) {}
|
|
~ExceptionHandlersSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ExceptionHandlersPtr handlers = ExceptionHandlers::RawCast(object);
|
|
objects_.Add(handlers);
|
|
|
|
s->Push(handlers->untag()->handled_types_data());
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ExceptionHandlersPtr handlers = objects_[i];
|
|
s->AssignRef(handlers);
|
|
AutoTraceObject(handlers);
|
|
const intptr_t length = handlers->untag()->num_entries();
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::ExceptionHandlers::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ExceptionHandlersPtr handlers = objects_[i];
|
|
AutoTraceObject(handlers);
|
|
const intptr_t packed_fields = handlers->untag()->packed_fields_;
|
|
const intptr_t length =
|
|
UntaggedExceptionHandlers::NumEntriesBits::decode(packed_fields);
|
|
s->WriteUnsigned(packed_fields);
|
|
WriteCompressedField(handlers, handled_types_data);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
const ExceptionHandlerInfo& info = handlers->untag()->data()[j];
|
|
s->Write<uint32_t>(info.handler_pc_offset);
|
|
s->Write<int16_t>(info.outer_try_index);
|
|
s->Write<int8_t>(info.needs_stacktrace);
|
|
s->Write<int8_t>(info.has_catch_all);
|
|
s->Write<int8_t>(info.is_generated);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ExceptionHandlersPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ExceptionHandlersDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ExceptionHandlersDeserializationCluster()
|
|
: DeserializationCluster("ExceptionHandlers") {}
|
|
~ExceptionHandlersDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(ExceptionHandlers::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ExceptionHandlersPtr handlers =
|
|
static_cast<ExceptionHandlersPtr>(d.Ref(id));
|
|
const intptr_t packed_fields = d.ReadUnsigned();
|
|
const intptr_t length =
|
|
UntaggedExceptionHandlers::NumEntriesBits::decode(packed_fields);
|
|
Deserializer::InitializeHeader(handlers, kExceptionHandlersCid,
|
|
ExceptionHandlers::InstanceSize(length));
|
|
handlers->untag()->packed_fields_ = packed_fields;
|
|
handlers->untag()->handled_types_data_ =
|
|
static_cast<ArrayPtr>(d.ReadRef());
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
ExceptionHandlerInfo& info = handlers->untag()->data()[j];
|
|
info.handler_pc_offset = d.Read<uint32_t>();
|
|
info.outer_try_index = d.Read<int16_t>();
|
|
info.needs_stacktrace = d.Read<int8_t>();
|
|
info.has_catch_all = d.Read<int8_t>();
|
|
info.is_generated = d.Read<int8_t>();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ContextSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ContextSerializationCluster()
|
|
: SerializationCluster("Context", kContextCid) {}
|
|
~ContextSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ContextPtr context = Context::RawCast(object);
|
|
objects_.Add(context);
|
|
|
|
s->Push(context->untag()->parent());
|
|
const intptr_t length = context->untag()->num_variables_;
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
s->Push(context->untag()->element(i));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ContextPtr context = objects_[i];
|
|
s->AssignRef(context);
|
|
AutoTraceObject(context);
|
|
const intptr_t length = context->untag()->num_variables_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ += compiler::target::Context::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ContextPtr context = objects_[i];
|
|
AutoTraceObject(context);
|
|
const intptr_t length = context->untag()->num_variables_;
|
|
s->WriteUnsigned(length);
|
|
WriteField(context, parent());
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
s->WriteElementRef(context->untag()->element(j), j);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ContextPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ContextDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ContextDeserializationCluster() : DeserializationCluster("Context") {}
|
|
~ContextDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(Context::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ContextPtr context = static_cast<ContextPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(context, kContextCid,
|
|
Context::InstanceSize(length));
|
|
context->untag()->num_variables_ = length;
|
|
context->untag()->parent_ = static_cast<ContextPtr>(d.ReadRef());
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
context->untag()->data()[j] = d.ReadRef();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ContextScopeSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ContextScopeSerializationCluster()
|
|
: SerializationCluster("ContextScope", kContextScopeCid) {}
|
|
~ContextScopeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ContextScopePtr scope = ContextScope::RawCast(object);
|
|
objects_.Add(scope);
|
|
|
|
const intptr_t length = scope->untag()->num_variables_;
|
|
PushFromTo(scope, length);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ContextScopePtr scope = objects_[i];
|
|
s->AssignRef(scope);
|
|
AutoTraceObject(scope);
|
|
const intptr_t length = scope->untag()->num_variables_;
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::ContextScope::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ContextScopePtr scope = objects_[i];
|
|
AutoTraceObject(scope);
|
|
const intptr_t length = scope->untag()->num_variables_;
|
|
s->WriteUnsigned(length);
|
|
s->Write<bool>(scope->untag()->is_implicit_);
|
|
WriteFromTo(scope, length);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ContextScopePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ContextScopeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ContextScopeDeserializationCluster()
|
|
: DeserializationCluster("ContextScope") {}
|
|
~ContextScopeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(ContextScope::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ContextScopePtr scope = static_cast<ContextScopePtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(scope, kContextScopeCid,
|
|
ContextScope::InstanceSize(length));
|
|
scope->untag()->num_variables_ = length;
|
|
scope->untag()->is_implicit_ = d.Read<bool>();
|
|
d.ReadFromTo(scope, length);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class UnlinkedCallSerializationCluster : public SerializationCluster {
|
|
public:
|
|
UnlinkedCallSerializationCluster()
|
|
: SerializationCluster("UnlinkedCall",
|
|
kUnlinkedCallCid,
|
|
compiler::target::UnlinkedCall::InstanceSize()) {}
|
|
~UnlinkedCallSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
UnlinkedCallPtr unlinked = UnlinkedCall::RawCast(object);
|
|
objects_.Add(unlinked);
|
|
PushFromTo(unlinked);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnlinkedCallPtr unlinked = objects_[i];
|
|
s->AssignRef(unlinked);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnlinkedCallPtr unlinked = objects_[i];
|
|
AutoTraceObjectName(unlinked, unlinked->untag()->target_name_);
|
|
WriteFromTo(unlinked);
|
|
s->Write<bool>(unlinked->untag()->can_patch_to_monomorphic_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<UnlinkedCallPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class UnlinkedCallDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
UnlinkedCallDeserializationCluster()
|
|
: DeserializationCluster("UnlinkedCall") {}
|
|
~UnlinkedCallDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, UnlinkedCall::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
UnlinkedCallPtr unlinked = static_cast<UnlinkedCallPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(unlinked, kUnlinkedCallCid,
|
|
UnlinkedCall::InstanceSize());
|
|
d.ReadFromTo(unlinked);
|
|
unlinked->untag()->can_patch_to_monomorphic_ = d.Read<bool>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ICDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ICDataSerializationCluster()
|
|
: SerializationCluster("ICData",
|
|
kICDataCid,
|
|
compiler::target::ICData::InstanceSize()) {}
|
|
~ICDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ICDataPtr ic = ICData::RawCast(object);
|
|
objects_.Add(ic);
|
|
PushFromTo(ic);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ICDataPtr ic = objects_[i];
|
|
s->AssignRef(ic);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
Snapshot::Kind kind = s->kind();
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ICDataPtr ic = objects_[i];
|
|
AutoTraceObjectName(ic, ic->untag()->target_name_);
|
|
WriteFromTo(ic);
|
|
if (kind != Snapshot::kFullAOT) {
|
|
NOT_IN_PRECOMPILED(s->Write<int32_t>(ic->untag()->deopt_id_));
|
|
}
|
|
s->Write<uint32_t>(ic->untag()->state_bits_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ICDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ICDataDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ICDataDeserializationCluster() : DeserializationCluster("ICData") {}
|
|
~ICDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, ICData::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ICDataPtr ic = static_cast<ICDataPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(ic, kICDataCid, ICData::InstanceSize());
|
|
d.ReadFromTo(ic);
|
|
NOT_IN_PRECOMPILED(ic->untag()->deopt_id_ = d.Read<int32_t>());
|
|
ic->untag()->state_bits_ = d.Read<int32_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class MegamorphicCacheSerializationCluster : public SerializationCluster {
|
|
public:
|
|
MegamorphicCacheSerializationCluster()
|
|
: SerializationCluster(
|
|
"MegamorphicCache",
|
|
kMegamorphicCacheCid,
|
|
compiler::target::MegamorphicCache::InstanceSize()) {}
|
|
~MegamorphicCacheSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
MegamorphicCachePtr cache = MegamorphicCache::RawCast(object);
|
|
objects_.Add(cache);
|
|
PushFromTo(cache);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
MegamorphicCachePtr cache = objects_[i];
|
|
s->AssignRef(cache);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
MegamorphicCachePtr cache = objects_[i];
|
|
AutoTraceObjectName(cache, cache->untag()->target_name_);
|
|
WriteFromTo(cache);
|
|
s->Write<int32_t>(cache->untag()->filled_entry_count_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<MegamorphicCachePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class MegamorphicCacheDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
MegamorphicCacheDeserializationCluster()
|
|
: DeserializationCluster("MegamorphicCache") {}
|
|
~MegamorphicCacheDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, MegamorphicCache::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
MegamorphicCachePtr cache = static_cast<MegamorphicCachePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(cache, kMegamorphicCacheCid,
|
|
MegamorphicCache::InstanceSize());
|
|
d.ReadFromTo(cache);
|
|
cache->untag()->filled_entry_count_ = d.Read<int32_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class SubtypeTestCacheSerializationCluster : public SerializationCluster {
|
|
public:
|
|
SubtypeTestCacheSerializationCluster()
|
|
: SerializationCluster(
|
|
"SubtypeTestCache",
|
|
kSubtypeTestCacheCid,
|
|
compiler::target::SubtypeTestCache::InstanceSize()) {}
|
|
~SubtypeTestCacheSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
SubtypeTestCachePtr cache = SubtypeTestCache::RawCast(object);
|
|
objects_.Add(cache);
|
|
s->Push(cache->untag()->cache_);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
SubtypeTestCachePtr cache = objects_[i];
|
|
s->AssignRef(cache);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
SubtypeTestCachePtr cache = objects_[i];
|
|
AutoTraceObject(cache);
|
|
WriteField(cache, cache_);
|
|
s->Write<uint32_t>(cache->untag()->num_inputs_);
|
|
s->Write<uint32_t>(cache->untag()->num_occupied_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<SubtypeTestCachePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class SubtypeTestCacheDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
SubtypeTestCacheDeserializationCluster()
|
|
: DeserializationCluster("SubtypeTestCache") {}
|
|
~SubtypeTestCacheDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, SubtypeTestCache::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
SubtypeTestCachePtr cache = static_cast<SubtypeTestCachePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(cache, kSubtypeTestCacheCid,
|
|
SubtypeTestCache::InstanceSize());
|
|
cache->untag()->cache_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
cache->untag()->num_inputs_ = d.Read<uint32_t>();
|
|
cache->untag()->num_occupied_ = d.Read<uint32_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class LoadingUnitSerializationCluster : public SerializationCluster {
|
|
public:
|
|
LoadingUnitSerializationCluster()
|
|
: SerializationCluster("LoadingUnit",
|
|
kLoadingUnitCid,
|
|
compiler::target::LoadingUnit::InstanceSize()) {}
|
|
~LoadingUnitSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
LoadingUnitPtr unit = LoadingUnit::RawCast(object);
|
|
objects_.Add(unit);
|
|
s->Push(unit->untag()->parent());
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LoadingUnitPtr unit = objects_[i];
|
|
s->AssignRef(unit);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LoadingUnitPtr unit = objects_[i];
|
|
AutoTraceObject(unit);
|
|
WriteCompressedField(unit, parent);
|
|
s->Write<intptr_t>(
|
|
unit->untag()->packed_fields_.Read<UntaggedLoadingUnit::IdBits>());
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<LoadingUnitPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class LoadingUnitDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
LoadingUnitDeserializationCluster() : DeserializationCluster("LoadingUnit") {}
|
|
~LoadingUnitDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, LoadingUnit::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
LoadingUnitPtr unit = static_cast<LoadingUnitPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(unit, kLoadingUnitCid,
|
|
LoadingUnit::InstanceSize());
|
|
unit->untag()->parent_ = static_cast<LoadingUnitPtr>(d.ReadRef());
|
|
unit->untag()->base_objects_ = Array::null();
|
|
unit->untag()->instructions_image_ = nullptr;
|
|
unit->untag()->packed_fields_ =
|
|
UntaggedLoadingUnit::LoadStateBits::encode(
|
|
UntaggedLoadingUnit::kNotLoaded) |
|
|
UntaggedLoadingUnit::IdBits::encode(d.Read<intptr_t>());
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ApiErrorSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ApiErrorSerializationCluster()
|
|
: SerializationCluster("ApiError",
|
|
kApiErrorCid,
|
|
compiler::target::ApiError::InstanceSize()) {}
|
|
~ApiErrorSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ApiErrorPtr error = ApiError::RawCast(object);
|
|
objects_.Add(error);
|
|
PushFromTo(error);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ApiErrorPtr error = objects_[i];
|
|
s->AssignRef(error);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ApiErrorPtr error = objects_[i];
|
|
AutoTraceObject(error);
|
|
WriteFromTo(error);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ApiErrorPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ApiErrorDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ApiErrorDeserializationCluster() : DeserializationCluster("ApiError") {}
|
|
~ApiErrorDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, ApiError::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ApiErrorPtr error = static_cast<ApiErrorPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(error, kApiErrorCid,
|
|
ApiError::InstanceSize());
|
|
d.ReadFromTo(error);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class LanguageErrorSerializationCluster : public SerializationCluster {
|
|
public:
|
|
LanguageErrorSerializationCluster()
|
|
: SerializationCluster("LanguageError",
|
|
kLanguageErrorCid,
|
|
compiler::target::LanguageError::InstanceSize()) {}
|
|
~LanguageErrorSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
LanguageErrorPtr error = LanguageError::RawCast(object);
|
|
objects_.Add(error);
|
|
PushFromTo(error);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LanguageErrorPtr error = objects_[i];
|
|
s->AssignRef(error);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LanguageErrorPtr error = objects_[i];
|
|
AutoTraceObject(error);
|
|
WriteFromTo(error);
|
|
s->WriteTokenPosition(error->untag()->token_pos_);
|
|
s->Write<bool>(error->untag()->report_after_token_);
|
|
s->Write<int8_t>(error->untag()->kind_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<LanguageErrorPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class LanguageErrorDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
LanguageErrorDeserializationCluster()
|
|
: DeserializationCluster("LanguageError") {}
|
|
~LanguageErrorDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, LanguageError::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
LanguageErrorPtr error = static_cast<LanguageErrorPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(error, kLanguageErrorCid,
|
|
LanguageError::InstanceSize());
|
|
d.ReadFromTo(error);
|
|
error->untag()->token_pos_ = d.ReadTokenPosition();
|
|
error->untag()->report_after_token_ = d.Read<bool>();
|
|
error->untag()->kind_ = d.Read<int8_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class UnhandledExceptionSerializationCluster : public SerializationCluster {
|
|
public:
|
|
UnhandledExceptionSerializationCluster()
|
|
: SerializationCluster(
|
|
"UnhandledException",
|
|
kUnhandledExceptionCid,
|
|
compiler::target::UnhandledException::InstanceSize()) {}
|
|
~UnhandledExceptionSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
UnhandledExceptionPtr exception = UnhandledException::RawCast(object);
|
|
objects_.Add(exception);
|
|
PushFromTo(exception);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnhandledExceptionPtr exception = objects_[i];
|
|
s->AssignRef(exception);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnhandledExceptionPtr exception = objects_[i];
|
|
AutoTraceObject(exception);
|
|
WriteFromTo(exception);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<UnhandledExceptionPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class UnhandledExceptionDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
UnhandledExceptionDeserializationCluster()
|
|
: DeserializationCluster("UnhandledException") {}
|
|
~UnhandledExceptionDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, UnhandledException::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
UnhandledExceptionPtr exception =
|
|
static_cast<UnhandledExceptionPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(exception, kUnhandledExceptionCid,
|
|
UnhandledException::InstanceSize());
|
|
d.ReadFromTo(exception);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class UnwindErrorSerializationCluster : public SerializationCluster {
|
|
public:
|
|
UnwindErrorSerializationCluster()
|
|
: SerializationCluster("UnwindError",
|
|
kUnwindErrorCid,
|
|
compiler::target::UnwindError::InstanceSize()) {}
|
|
~UnwindErrorSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
UnwindErrorPtr error = UnwindError::RawCast(object);
|
|
objects_.Add(error);
|
|
PushFromTo(error);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnwindErrorPtr error = objects_[i];
|
|
s->AssignRef(error);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
UnwindErrorPtr error = objects_[i];
|
|
AutoTraceObject(error);
|
|
WriteFromTo(error);
|
|
s->Write<bool>(error->untag()->is_user_initiated_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<UnwindErrorPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class UnwindErrorDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
UnwindErrorDeserializationCluster() : DeserializationCluster("UnwindError") {}
|
|
~UnwindErrorDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, UnwindError::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
UnwindErrorPtr error = static_cast<UnwindErrorPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(error, kUnwindErrorCid,
|
|
UnwindError::InstanceSize());
|
|
d.ReadFromTo(error);
|
|
error->untag()->is_user_initiated_ = d.Read<bool>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class InstanceSerializationCluster : public SerializationCluster {
|
|
public:
|
|
InstanceSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
intptr_t cid)
|
|
: SerializationCluster("Instance",
|
|
cid,
|
|
kSizeVaries,
|
|
is_canonical,
|
|
is_deeply_immutable) {
|
|
ClassPtr cls = IsolateGroup::Current()->class_table()->At(cid);
|
|
host_next_field_offset_in_words_ =
|
|
cls->untag()->host_next_field_offset_in_words_;
|
|
ASSERT(host_next_field_offset_in_words_ > 0);
|
|
#if defined(DART_PRECOMPILER)
|
|
target_next_field_offset_in_words_ =
|
|
cls->untag()->target_next_field_offset_in_words_;
|
|
target_instance_size_in_words_ =
|
|
cls->untag()->target_instance_size_in_words_;
|
|
#else
|
|
target_next_field_offset_in_words_ =
|
|
cls->untag()->host_next_field_offset_in_words_;
|
|
target_instance_size_in_words_ = cls->untag()->host_instance_size_in_words_;
|
|
#endif // defined(DART_PRECOMPILER)
|
|
ASSERT(target_next_field_offset_in_words_ > 0);
|
|
ASSERT(target_instance_size_in_words_ > 0);
|
|
}
|
|
~InstanceSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
InstancePtr instance = Instance::RawCast(object);
|
|
objects_.Add(instance);
|
|
const intptr_t next_field_offset = host_next_field_offset_in_words_
|
|
<< kCompressedWordSizeLog2;
|
|
const auto unboxed_fields_bitmap =
|
|
s->isolate_group()->class_table()->GetUnboxedFieldsMapAt(cid_);
|
|
intptr_t offset = Instance::NextFieldOffset();
|
|
while (offset < next_field_offset) {
|
|
// Skips unboxed fields
|
|
if (!unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
|
|
ObjectPtr raw_obj =
|
|
reinterpret_cast<CompressedObjectPtr*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset)
|
|
->Decompress(instance->untag()->heap_base());
|
|
s->Push(raw_obj);
|
|
}
|
|
offset += kCompressedWordSize;
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
|
|
s->Write<int32_t>(target_next_field_offset_in_words_);
|
|
s->Write<int32_t>(target_instance_size_in_words_);
|
|
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
InstancePtr instance = objects_[i];
|
|
s->AssignRef(instance);
|
|
}
|
|
|
|
const intptr_t instance_size = compiler::target::RoundedAllocationSize(
|
|
target_instance_size_in_words_ * compiler::target::kCompressedWordSize);
|
|
target_memory_size_ += instance_size * count;
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t next_field_offset = host_next_field_offset_in_words_
|
|
<< kCompressedWordSizeLog2;
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned64(CalculateTargetUnboxedFieldsBitmap(s, cid_).Value());
|
|
const auto unboxed_fields_bitmap =
|
|
s->isolate_group()->class_table()->GetUnboxedFieldsMapAt(cid_);
|
|
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
InstancePtr instance = objects_[i];
|
|
AutoTraceObject(instance);
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
ClassPtr cls = s->isolate_group()->class_table()->At(cid_);
|
|
s->AttributePropertyRef(cls, "<class>");
|
|
}
|
|
#endif
|
|
intptr_t offset = Instance::NextFieldOffset();
|
|
while (offset < next_field_offset) {
|
|
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
|
|
// Writes 32 bits of the unboxed value at a time.
|
|
const compressed_uword value = *reinterpret_cast<compressed_uword*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset);
|
|
s->WriteWordWith32BitWrites(value);
|
|
} else {
|
|
ObjectPtr raw_obj =
|
|
reinterpret_cast<CompressedObjectPtr*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset)
|
|
->Decompress(instance->untag()->heap_base());
|
|
s->WriteElementRef(raw_obj, offset);
|
|
}
|
|
offset += kCompressedWordSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
intptr_t host_next_field_offset_in_words_;
|
|
intptr_t target_next_field_offset_in_words_;
|
|
intptr_t target_instance_size_in_words_;
|
|
GrowableArray<InstancePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class AbstractInstanceDeserializationCluster : public DeserializationCluster {
|
|
protected:
|
|
AbstractInstanceDeserializationCluster(const char* name,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: DeserializationCluster(name, is_canonical, is_deeply_immutable),
|
|
is_root_unit_(is_root_unit) {}
|
|
|
|
const bool is_root_unit_;
|
|
|
|
public:
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!is_root_unit_ && is_canonical()) {
|
|
SafepointMutexLocker ml(
|
|
d->isolate_group()->constant_canonicalization_mutex());
|
|
Instance& instance = Instance::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
instance ^= refs.At(i);
|
|
instance = instance.CanonicalizeLocked(d->thread());
|
|
refs.SetAt(i, instance);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
};
|
|
|
|
class InstanceDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
InstanceDeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Instance",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit),
|
|
cid_(cid) {}
|
|
~InstanceDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
next_field_offset_in_words_ = d->Read<int32_t>();
|
|
instance_size_in_words_ = d->Read<int32_t>();
|
|
intptr_t instance_size = Object::RoundedAllocationSize(
|
|
instance_size_in_words_ * kCompressedWordSize);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
d->AssignRef(d->Allocate(instance_size));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const intptr_t cid = cid_;
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
const bool is_deeply_immutable = is_deeply_immutable_;
|
|
intptr_t next_field_offset = next_field_offset_in_words_
|
|
<< kCompressedWordSizeLog2;
|
|
intptr_t instance_size = Object::RoundedAllocationSize(
|
|
instance_size_in_words_ * kCompressedWordSize);
|
|
const UnboxedFieldBitmap unboxed_fields_bitmap(d.ReadUnsigned64());
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
InstancePtr instance = static_cast<InstancePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(instance, cid, instance_size,
|
|
mark_canonical, is_deeply_immutable);
|
|
intptr_t offset = Instance::NextFieldOffset();
|
|
while (offset < next_field_offset) {
|
|
if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
|
|
compressed_uword* p = reinterpret_cast<compressed_uword*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset);
|
|
// Reads 32 bits of the unboxed value at a time
|
|
*p = d.ReadWordWith32BitReads();
|
|
} else {
|
|
CompressedObjectPtr* p = reinterpret_cast<CompressedObjectPtr*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset);
|
|
*p = d.ReadRef();
|
|
}
|
|
offset += kCompressedWordSize;
|
|
}
|
|
while (offset < instance_size) {
|
|
CompressedObjectPtr* p = reinterpret_cast<CompressedObjectPtr*>(
|
|
reinterpret_cast<uword>(instance->untag()) + offset);
|
|
*p = d.null();
|
|
offset += kCompressedWordSize;
|
|
}
|
|
ASSERT(offset == instance_size);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
intptr_t next_field_offset_in_words_;
|
|
intptr_t instance_size_in_words_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class LibraryPrefixSerializationCluster : public SerializationCluster {
|
|
public:
|
|
LibraryPrefixSerializationCluster()
|
|
: SerializationCluster("LibraryPrefix",
|
|
kLibraryPrefixCid,
|
|
compiler::target::LibraryPrefix::InstanceSize()) {}
|
|
~LibraryPrefixSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
LibraryPrefixPtr prefix = LibraryPrefix::RawCast(object);
|
|
objects_.Add(prefix);
|
|
PushFromTo(prefix);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LibraryPrefixPtr prefix = objects_[i];
|
|
s->AssignRef(prefix);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
LibraryPrefixPtr prefix = objects_[i];
|
|
AutoTraceObject(prefix);
|
|
WriteFromTo(prefix);
|
|
s->Write<uint16_t>(prefix->untag()->num_imports_);
|
|
s->Write<bool>(prefix->untag()->is_deferred_load_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<LibraryPrefixPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class LibraryPrefixDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
LibraryPrefixDeserializationCluster()
|
|
: DeserializationCluster("LibraryPrefix") {}
|
|
~LibraryPrefixDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, LibraryPrefix::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
LibraryPrefixPtr prefix = static_cast<LibraryPrefixPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(prefix, kLibraryPrefixCid,
|
|
LibraryPrefix::InstanceSize());
|
|
d.ReadFromTo(prefix);
|
|
prefix->untag()->num_imports_ = d.Read<uint16_t>();
|
|
prefix->untag()->is_deferred_load_ = d.Read<bool>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypeSerializationCluster
|
|
: public CanonicalSetSerializationCluster<
|
|
CanonicalTypeSet,
|
|
Type,
|
|
TypePtr,
|
|
/*kAllCanonicalObjectsAreIncludedIntoSet=*/false> {
|
|
public:
|
|
TypeSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(
|
|
kTypeCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
represents_canonical_set,
|
|
"Type",
|
|
compiler::target::Type::InstanceSize()) {}
|
|
~TypeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypePtr type = Type::RawCast(object);
|
|
objects_.Add(type);
|
|
|
|
PushFromTo(type);
|
|
|
|
ASSERT(type->untag()->type_class_id() != kIllegalCid);
|
|
ClassPtr type_class =
|
|
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
|
|
s->Push(type_class);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypePtr type = objects_[i];
|
|
s->AssignRef(type);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WriteType(s, objects_[i]);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Type& type_ = Type::Handle();
|
|
Class& cls_ = Class::Handle();
|
|
|
|
// Type::Canonicalize does not actually put all canonical Type objects into
|
|
// canonical_types set. Some of the canonical declaration types (but not all
|
|
// of them) are simply cached in UntaggedClass::declaration_type_ and are not
|
|
// inserted into the canonical_types set.
|
|
// Keep in sync with Type::Canonicalize.
|
|
virtual bool IsInCanonicalSet(Serializer* s, TypePtr type) {
|
|
ClassPtr type_class =
|
|
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
|
|
if (type_class->untag()->declaration_type() != type) {
|
|
return true;
|
|
}
|
|
|
|
type_ = type;
|
|
cls_ = type_class;
|
|
return !type_.IsDeclarationTypeOf(cls_);
|
|
}
|
|
|
|
void WriteType(Serializer* s, TypePtr type) {
|
|
AutoTraceObject(type);
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
ClassPtr type_class =
|
|
s->isolate_group()->class_table()->At(type->untag()->type_class_id());
|
|
s->AttributePropertyRef(type_class, "<type_class>");
|
|
}
|
|
#endif
|
|
WriteFromTo(type);
|
|
s->WriteUnsigned(type->untag()->flags());
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypeDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<
|
|
CanonicalTypeSet,
|
|
/*kAllCanonicalObjectsAreIncludedIntoSet=*/false> {
|
|
public:
|
|
TypeDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit,
|
|
"Type") {}
|
|
~TypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Type::InstanceSize());
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypePtr type = static_cast<TypePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(type, kTypeCid, Type::InstanceSize(),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(type);
|
|
type->untag()->set_flags(d.ReadUnsigned());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(d, refs,
|
|
Array::Handle(object_store->canonical_types()));
|
|
object_store->set_canonical_types(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
AbstractType& type = AbstractType::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
type ^= refs.At(i);
|
|
type = type.Canonicalize(d->thread());
|
|
refs.SetAt(i, type);
|
|
}
|
|
}
|
|
|
|
Type& type = Type::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
type.UpdateTypeTestingStubEntryPoint();
|
|
}
|
|
} else {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class FunctionTypeSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalFunctionTypeSet,
|
|
FunctionType,
|
|
FunctionTypePtr> {
|
|
public:
|
|
FunctionTypeSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(
|
|
kFunctionTypeCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
represents_canonical_set,
|
|
"FunctionType",
|
|
compiler::target::FunctionType::InstanceSize()) {}
|
|
~FunctionTypeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
FunctionTypePtr type = FunctionType::RawCast(object);
|
|
objects_.Add(type);
|
|
PushFromTo(type);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
FunctionTypePtr type = objects_[i];
|
|
s->AssignRef(type);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WriteFunctionType(s, objects_[i]);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void WriteFunctionType(Serializer* s, FunctionTypePtr type) {
|
|
AutoTraceObject(type);
|
|
WriteFromTo(type);
|
|
ASSERT(Utils::IsUint(8, type->untag()->flags()));
|
|
s->Write<uint8_t>(type->untag()->flags());
|
|
s->Write<uint32_t>(type->untag()->packed_parameter_counts_);
|
|
s->Write<uint16_t>(type->untag()->packed_type_parameter_counts_);
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class FunctionTypeDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalFunctionTypeSet> {
|
|
public:
|
|
FunctionTypeDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit,
|
|
"FunctionType") {}
|
|
~FunctionTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, FunctionType::InstanceSize());
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
FunctionTypePtr type = static_cast<FunctionTypePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(type, kFunctionTypeCid,
|
|
FunctionType::InstanceSize(),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(type);
|
|
type->untag()->set_flags(d.Read<uint8_t>());
|
|
type->untag()->packed_parameter_counts_ = d.Read<uint32_t>();
|
|
type->untag()->packed_type_parameter_counts_ = d.Read<uint16_t>();
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(
|
|
d, refs, Array::Handle(object_store->canonical_function_types()));
|
|
object_store->set_canonical_function_types(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
AbstractType& type = AbstractType::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
type ^= refs.At(i);
|
|
type = type.Canonicalize(d->thread());
|
|
refs.SetAt(i, type);
|
|
}
|
|
}
|
|
|
|
FunctionType& type = FunctionType::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
type.UpdateTypeTestingStubEntryPoint();
|
|
}
|
|
} else {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class RecordTypeSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalRecordTypeSet,
|
|
RecordType,
|
|
RecordTypePtr> {
|
|
public:
|
|
RecordTypeSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(
|
|
kRecordTypeCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
represents_canonical_set,
|
|
"RecordType",
|
|
compiler::target::RecordType::InstanceSize()) {}
|
|
~RecordTypeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
RecordTypePtr type = RecordType::RawCast(object);
|
|
objects_.Add(type);
|
|
PushFromTo(type);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
RecordTypePtr type = objects_[i];
|
|
s->AssignRef(type);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WriteRecordType(s, objects_[i]);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void WriteRecordType(Serializer* s, RecordTypePtr type) {
|
|
AutoTraceObject(type);
|
|
WriteFromTo(type);
|
|
ASSERT(Utils::IsUint(8, type->untag()->flags()));
|
|
s->Write<uint8_t>(type->untag()->flags());
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class RecordTypeDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalRecordTypeSet> {
|
|
public:
|
|
RecordTypeDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit,
|
|
"RecordType") {}
|
|
~RecordTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, RecordType::InstanceSize());
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
RecordTypePtr type = static_cast<RecordTypePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(type, kRecordTypeCid,
|
|
RecordType::InstanceSize(), mark_canonical,
|
|
is_deeply_immutable());
|
|
d.ReadFromTo(type);
|
|
type->untag()->set_flags(d.Read<uint8_t>());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(d, refs,
|
|
Array::Handle(object_store->canonical_record_types()));
|
|
object_store->set_canonical_record_types(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
AbstractType& type = AbstractType::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
type ^= refs.At(i);
|
|
type = type.Canonicalize(d->thread());
|
|
refs.SetAt(i, type);
|
|
}
|
|
}
|
|
|
|
RecordType& type = RecordType::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
type.UpdateTypeTestingStubEntryPoint();
|
|
}
|
|
} else {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypeParameterSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalTypeParameterSet,
|
|
TypeParameter,
|
|
TypeParameterPtr> {
|
|
public:
|
|
TypeParameterSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool cluster_represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(
|
|
kTypeParameterCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
cluster_represents_canonical_set,
|
|
"TypeParameter",
|
|
compiler::target::TypeParameter::InstanceSize()) {}
|
|
~TypeParameterSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypeParameterPtr type = TypeParameter::RawCast(object);
|
|
objects_.Add(type);
|
|
|
|
PushFromTo(type);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypeParameterPtr type = objects_[i];
|
|
s->AssignRef(type);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WriteTypeParameter(s, objects_[i]);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void WriteTypeParameter(Serializer* s, TypeParameterPtr type) {
|
|
AutoTraceObject(type);
|
|
WriteFromTo(type);
|
|
s->Write<uint16_t>(type->untag()->base_);
|
|
s->Write<uint16_t>(type->untag()->index_);
|
|
ASSERT(Utils::IsUint(8, type->untag()->flags()));
|
|
s->Write<uint8_t>(type->untag()->flags());
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypeParameterDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalTypeParameterSet> {
|
|
public:
|
|
TypeParameterDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit,
|
|
"TypeParameter") {}
|
|
~TypeParameterDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, TypeParameter::InstanceSize());
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypeParameterPtr type = static_cast<TypeParameterPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(type, kTypeParameterCid,
|
|
TypeParameter::InstanceSize(),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(type);
|
|
type->untag()->base_ = d.Read<uint16_t>();
|
|
type->untag()->index_ = d.Read<uint16_t>();
|
|
type->untag()->set_flags(d.Read<uint8_t>());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(
|
|
d, refs, Array::Handle(object_store->canonical_type_parameters()));
|
|
object_store->set_canonical_type_parameters(table_);
|
|
} else if (!is_root_unit_ && is_canonical()) {
|
|
TypeParameter& type_param = TypeParameter::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
type_param ^= refs.At(i);
|
|
type_param ^= type_param.Canonicalize(d->thread());
|
|
refs.SetAt(i, type_param);
|
|
}
|
|
}
|
|
|
|
TypeParameter& type_param = TypeParameter::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type_param ^= refs.At(id);
|
|
type_param.UpdateTypeTestingStubEntryPoint();
|
|
}
|
|
} else {
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type_param ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type_param);
|
|
type_param.InitializeTypeTestingStubNonAtomic(stub);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ClosureSerializationCluster : public SerializationCluster {
|
|
public:
|
|
ClosureSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("Closure",
|
|
kClosureCid,
|
|
kSizeVaries,
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~ClosureSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ClosurePtr closure = Closure::RawCast(object);
|
|
objects_.Add(closure);
|
|
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
|
Smi::Value(closure->untag()->length_and_flags()));
|
|
PushFromTo(closure, length);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ClosurePtr closure = objects_[i];
|
|
s->AssignRef(closure);
|
|
AutoTraceObject(closure);
|
|
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
|
Smi::Value(closure->untag()->length_and_flags()));
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ += compiler::target::Closure::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ClosurePtr closure = objects_[i];
|
|
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
|
Smi::Value(closure->untag()->length_and_flags()));
|
|
AutoTraceObject(closure);
|
|
s->WriteUnsigned(length);
|
|
WriteFromTo(closure, length);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ClosurePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ClosureDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
ClosureDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Closure",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit) {}
|
|
~ClosureDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(Closure::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ClosurePtr closure = static_cast<ClosurePtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(closure, kClosureCid,
|
|
Closure::InstanceSize(length),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(closure, length);
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
closure->untag()->entry_point_ = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
// We only cache the entry point in bare instructions mode (as we need
|
|
// to load the function anyway otherwise).
|
|
ASSERT(d->kind() == Snapshot::kFullAOT);
|
|
auto& closure = Closure::Handle(d->zone());
|
|
auto& func = Function::Handle(d->zone());
|
|
for (intptr_t i = start_index_, n = stop_index_; i < n; i++) {
|
|
closure ^= refs.At(i);
|
|
func = closure.function();
|
|
uword entry_point = func.entry_point();
|
|
ASSERT(entry_point != 0);
|
|
closure.ptr()->untag()->entry_point_ = entry_point;
|
|
}
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class MintSerializationCluster : public SerializationCluster {
|
|
public:
|
|
MintSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("int",
|
|
kMintCid,
|
|
kSizeVaries,
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~MintSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
if (!object->IsHeapObject()) {
|
|
SmiPtr smi = Smi::RawCast(object);
|
|
smis_.Add(smi);
|
|
} else {
|
|
MintPtr mint = Mint::RawCast(object);
|
|
mints_.Add(mint);
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
s->WriteUnsigned(smis_.length() + mints_.length());
|
|
for (intptr_t i = 0; i < smis_.length(); i++) {
|
|
SmiPtr smi = smis_[i];
|
|
s->AssignRef(smi);
|
|
AutoTraceObject(smi);
|
|
const int64_t value = Smi::Value(smi);
|
|
s->Write<int64_t>(value);
|
|
if (!Smi::IsValid(value)) {
|
|
// This Smi will become a Mint when loaded.
|
|
target_memory_size_ += compiler::target::Mint::InstanceSize();
|
|
}
|
|
}
|
|
for (intptr_t i = 0; i < mints_.length(); i++) {
|
|
MintPtr mint = mints_[i];
|
|
s->AssignRef(mint);
|
|
AutoTraceObject(mint);
|
|
s->Write<int64_t>(mint->untag()->value_);
|
|
// All Mints on the host should be Mints on the target.
|
|
ASSERT(!Smi::IsValid(mint->untag()->value_));
|
|
target_memory_size_ += compiler::target::Mint::InstanceSize();
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {}
|
|
|
|
private:
|
|
GrowableArray<SmiPtr> smis_;
|
|
GrowableArray<MintPtr> mints_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class MintDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
MintDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("int",
|
|
is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit) {}
|
|
~MintDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
const bool mark_canonical = is_canonical();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
int64_t value = d->Read<int64_t>();
|
|
if (Smi::IsValid(value)) {
|
|
d->AssignRef(Smi::New(value));
|
|
} else {
|
|
MintPtr mint = static_cast<MintPtr>(d->Allocate(Mint::InstanceSize()));
|
|
Deserializer::InitializeHeader(mint, kMintCid, Mint::InstanceSize(),
|
|
mark_canonical,
|
|
/*is_deeply_immutable=*/true);
|
|
mint->untag()->value_ = value;
|
|
d->AssignRef(mint);
|
|
}
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class DoubleSerializationCluster : public SerializationCluster {
|
|
public:
|
|
DoubleSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("double",
|
|
kDoubleCid,
|
|
compiler::target::Double::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~DoubleSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
DoublePtr dbl = Double::RawCast(object);
|
|
objects_.Add(dbl);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
DoublePtr dbl = objects_[i];
|
|
s->AssignRef(dbl);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
DoublePtr dbl = objects_[i];
|
|
AutoTraceObject(dbl);
|
|
s->Write<double>(dbl->untag()->value_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<DoublePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class DoubleDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
DoubleDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("double",
|
|
is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit) {
|
|
ASSERT(Object::ShouldHaveDeeplyImmutabilityBitSet(kDoubleCid));
|
|
}
|
|
~DoubleDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Double::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
DoublePtr dbl = static_cast<DoublePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(dbl, kDoubleCid, Double::InstanceSize(),
|
|
mark_canonical,
|
|
/*is_deeply_immutable=*/true);
|
|
dbl->untag()->value_ = d.Read<double>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class Simd128SerializationCluster : public SerializationCluster {
|
|
public:
|
|
Simd128SerializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: SerializationCluster("Simd128",
|
|
cid,
|
|
compiler::target::Int32x4::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {
|
|
ASSERT_EQUAL(compiler::target::Int32x4::InstanceSize(),
|
|
compiler::target::Float32x4::InstanceSize());
|
|
ASSERT_EQUAL(compiler::target::Int32x4::InstanceSize(),
|
|
compiler::target::Float64x2::InstanceSize());
|
|
}
|
|
~Simd128SerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) { objects_.Add(object); }
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ObjectPtr vector = objects_[i];
|
|
s->AssignRef(vector);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ObjectPtr vector = objects_[i];
|
|
AutoTraceObject(vector);
|
|
ASSERT_EQUAL(Int32x4::value_offset(), Float32x4::value_offset());
|
|
ASSERT_EQUAL(Int32x4::value_offset(), Float64x2::value_offset());
|
|
s->WriteBytes(&(static_cast<Int32x4Ptr>(vector)->untag()->value_),
|
|
sizeof(simd128_value_t));
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ObjectPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class Simd128DeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
Simd128DeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Simd128",
|
|
is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit),
|
|
cid_(cid) {}
|
|
~Simd128DeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ASSERT_EQUAL(Int32x4::InstanceSize(), Float32x4::InstanceSize());
|
|
ASSERT_EQUAL(Int32x4::InstanceSize(), Float64x2::InstanceSize());
|
|
ReadAllocFixedSize(d, Int32x4::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
const intptr_t cid = cid_;
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ObjectPtr vector = d.Ref(id);
|
|
Deserializer::InitializeHeader(vector, cid, Int32x4::InstanceSize(),
|
|
mark_canonical,
|
|
/*is_deeply_immutable=*/true);
|
|
d.ReadBytes(&(static_cast<Int32x4Ptr>(vector)->untag()->value_),
|
|
sizeof(simd128_value_t));
|
|
}
|
|
}
|
|
|
|
private:
|
|
intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class GrowableObjectArraySerializationCluster : public SerializationCluster {
|
|
public:
|
|
GrowableObjectArraySerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: SerializationCluster(
|
|
"GrowableObjectArray",
|
|
kGrowableObjectArrayCid,
|
|
compiler::target::GrowableObjectArray::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~GrowableObjectArraySerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
GrowableObjectArrayPtr array = GrowableObjectArray::RawCast(object);
|
|
objects_.Add(array);
|
|
PushFromTo(array);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
GrowableObjectArrayPtr array = objects_[i];
|
|
s->AssignRef(array);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
GrowableObjectArrayPtr array = objects_[i];
|
|
AutoTraceObject(array);
|
|
WriteFromTo(array);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<GrowableObjectArrayPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class GrowableObjectArrayDeserializationCluster
|
|
: public DeserializationCluster {
|
|
public:
|
|
GrowableObjectArrayDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable)
|
|
: DeserializationCluster("GrowableObjectArray",
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~GrowableObjectArrayDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, GrowableObjectArray::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
GrowableObjectArrayPtr list =
|
|
static_cast<GrowableObjectArrayPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(list, kGrowableObjectArrayCid,
|
|
GrowableObjectArray::InstanceSize());
|
|
d.ReadFromTo(list);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class RecordSerializationCluster : public SerializationCluster {
|
|
public:
|
|
RecordSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("Record",
|
|
kRecordCid,
|
|
kSizeVaries,
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~RecordSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
RecordPtr record = Record::RawCast(object);
|
|
objects_.Add(record);
|
|
|
|
const intptr_t num_fields = Record::NumFields(record);
|
|
for (intptr_t i = 0; i < num_fields; ++i) {
|
|
s->Push(record->untag()->field(i));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; ++i) {
|
|
RecordPtr record = objects_[i];
|
|
s->AssignRef(record);
|
|
AutoTraceObject(record);
|
|
const intptr_t num_fields = Record::NumFields(record);
|
|
s->WriteUnsigned(num_fields);
|
|
target_memory_size_ += compiler::target::Record::InstanceSize(num_fields);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; ++i) {
|
|
RecordPtr record = objects_[i];
|
|
AutoTraceObject(record);
|
|
const RecordShape shape(record->untag()->shape());
|
|
s->WriteUnsigned(shape.AsInt());
|
|
const intptr_t num_fields = shape.num_fields();
|
|
for (intptr_t j = 0; j < num_fields; ++j) {
|
|
s->WriteElementRef(record->untag()->field(j), j);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<RecordPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class RecordDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
RecordDeserializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Record",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit) {}
|
|
~RecordDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t num_fields = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(Record::InstanceSize(num_fields)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const bool stamp_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
RecordPtr record = static_cast<RecordPtr>(d.Ref(id));
|
|
const intptr_t shape = d.ReadUnsigned();
|
|
const intptr_t num_fields = RecordShape(shape).num_fields();
|
|
Deserializer::InitializeHeader(record, kRecordCid,
|
|
Record::InstanceSize(num_fields),
|
|
stamp_canonical, is_deeply_immutable());
|
|
record->untag()->shape_ = Smi::New(shape);
|
|
for (intptr_t j = 0; j < num_fields; ++j) {
|
|
record->untag()->data()[j] = d.ReadRef();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypedDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
explicit TypedDataSerializationCluster(intptr_t cid)
|
|
: SerializationCluster("TypedData", cid) {}
|
|
~TypedDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypedDataPtr data = TypedData::RawCast(object);
|
|
objects_.Add(data);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
const intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypedDataPtr data = objects_[i];
|
|
s->AssignRef(data);
|
|
AutoTraceObject(data);
|
|
const intptr_t length = Smi::Value(data->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ +=
|
|
compiler::target::TypedData::InstanceSize(length * element_size);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypedDataPtr data = objects_[i];
|
|
AutoTraceObject(data);
|
|
const intptr_t length = Smi::Value(data->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data());
|
|
s->WriteBytes(cdata, length * element_size);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<TypedDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypedDataDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit TypedDataDeserializationCluster(intptr_t cid)
|
|
: DeserializationCluster("TypedData"), cid_(cid) {}
|
|
~TypedDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(TypedData::InstanceSize(length * element_size)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
|
|
|
|
const intptr_t cid = cid_;
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypedDataPtr data = static_cast<TypedDataPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
const intptr_t length_in_bytes = length * element_size;
|
|
Deserializer::InitializeHeader(data, cid,
|
|
TypedData::InstanceSize(length_in_bytes));
|
|
data->untag()->length_ = Smi::New(length);
|
|
data->untag()->RecomputeDataField();
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data());
|
|
d.ReadBytes(cdata, length_in_bytes);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class TypedDataViewSerializationCluster : public SerializationCluster {
|
|
public:
|
|
explicit TypedDataViewSerializationCluster(intptr_t cid)
|
|
: SerializationCluster("TypedDataView",
|
|
cid,
|
|
compiler::target::TypedDataView::InstanceSize()) {}
|
|
~TypedDataViewSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypedDataViewPtr view = TypedDataView::RawCast(object);
|
|
objects_.Add(view);
|
|
|
|
PushFromTo(view);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypedDataViewPtr view = objects_[i];
|
|
s->AssignRef(view);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
TypedDataViewPtr view = objects_[i];
|
|
AutoTraceObject(view);
|
|
WriteFromTo(view);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<TypedDataViewPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class TypedDataViewDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit TypedDataViewDeserializationCluster(intptr_t cid)
|
|
: DeserializationCluster("TypedDataView"), cid_(cid) {}
|
|
~TypedDataViewDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, TypedDataView::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const intptr_t cid = cid_;
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypedDataViewPtr view = static_cast<TypedDataViewPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(view, cid, TypedDataView::InstanceSize());
|
|
d.ReadFromTo(view);
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
auto& view = TypedDataView::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
view ^= refs.At(id);
|
|
view.RecomputeDataField();
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ExternalTypedDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
explicit ExternalTypedDataSerializationCluster(intptr_t cid)
|
|
: SerializationCluster(
|
|
"ExternalTypedData",
|
|
cid,
|
|
compiler::target::ExternalTypedData::InstanceSize()) {}
|
|
~ExternalTypedDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ExternalTypedDataPtr data = ExternalTypedData::RawCast(object);
|
|
objects_.Add(data);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ExternalTypedDataPtr data = objects_[i];
|
|
s->AssignRef(data);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ExternalTypedDataPtr data = objects_[i];
|
|
AutoTraceObject(data);
|
|
const intptr_t length = Smi::Value(data->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data_);
|
|
s->Align(ExternalTypedData::kDataSerializationAlignment);
|
|
s->WriteBytes(cdata, length * element_size);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ExternalTypedDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ExternalTypedDataDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit ExternalTypedDataDeserializationCluster(intptr_t cid)
|
|
: DeserializationCluster("ExternalTypedData"), cid_(cid) {}
|
|
~ExternalTypedDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, ExternalTypedData::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
const intptr_t cid = cid_;
|
|
intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid);
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ExternalTypedDataPtr data = static_cast<ExternalTypedDataPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(data, cid,
|
|
ExternalTypedData::InstanceSize());
|
|
data->untag()->length_ = Smi::New(length);
|
|
d.Align(ExternalTypedData::kDataSerializationAlignment);
|
|
data->untag()->data_ = const_cast<uint8_t*>(d.AddressOfCurrentPosition());
|
|
d.Advance(length * element_size);
|
|
// No finalizer / external size 0.
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class DeltaEncodedTypedDataSerializationCluster : public SerializationCluster {
|
|
public:
|
|
DeltaEncodedTypedDataSerializationCluster()
|
|
: SerializationCluster("DeltaEncodedTypedData",
|
|
kDeltaEncodedTypedDataCid) {}
|
|
~DeltaEncodedTypedDataSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
TypedDataPtr data = TypedData::RawCast(object);
|
|
objects_.Add(data);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const TypedDataPtr data = objects_[i];
|
|
const intptr_t element_size =
|
|
TypedData::ElementSizeInBytes(data->GetClassIdOfHeapObject());
|
|
s->AssignRef(data);
|
|
AutoTraceObject(data);
|
|
const intptr_t length_in_bytes =
|
|
Smi::Value(data->untag()->length()) * element_size;
|
|
s->WriteUnsigned(length_in_bytes);
|
|
target_memory_size_ +=
|
|
compiler::target::TypedData::InstanceSize(length_in_bytes);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
TypedData& typed_data = TypedData::Handle(s->zone());
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const TypedDataPtr data = objects_[i];
|
|
AutoTraceObject(data);
|
|
const intptr_t cid = data->GetClassIdOfHeapObject();
|
|
// Only Uint16 and Uint32 typed data is supported at the moment. So encode
|
|
// which this is in the low bit of the length. Uint16 is 0, Uint32 is 1.
|
|
ASSERT(cid == kTypedDataUint16ArrayCid ||
|
|
cid == kTypedDataUint32ArrayCid);
|
|
const intptr_t cid_flag = cid == kTypedDataUint16ArrayCid ? 0 : 1;
|
|
const intptr_t length = Smi::Value(data->untag()->length());
|
|
const intptr_t encoded_length = (length << 1) | cid_flag;
|
|
s->WriteUnsigned(encoded_length);
|
|
intptr_t prev = 0;
|
|
typed_data = data;
|
|
for (intptr_t j = 0; j < length; ++j) {
|
|
const intptr_t value = (cid == kTypedDataUint16ArrayCid)
|
|
? typed_data.GetUint16(j << 1)
|
|
: typed_data.GetUint32(j << 2);
|
|
ASSERT(value >= prev);
|
|
s->WriteUnsigned(value - prev);
|
|
prev = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<TypedDataPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class DeltaEncodedTypedDataDeserializationCluster
|
|
: public DeserializationCluster {
|
|
public:
|
|
DeltaEncodedTypedDataDeserializationCluster()
|
|
: DeserializationCluster("DeltaEncodedTypedData") {}
|
|
~DeltaEncodedTypedDataDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length_in_bytes = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(TypedData::InstanceSize(length_in_bytes)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
TypedData& typed_data = TypedData::Handle(d_->zone());
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypedDataPtr data = static_cast<TypedDataPtr>(d.Ref(id));
|
|
const intptr_t encoded_length = d.ReadUnsigned();
|
|
const intptr_t length = encoded_length >> 1;
|
|
const intptr_t cid = (encoded_length & 0x1) == 0
|
|
? kTypedDataUint16ArrayCid
|
|
: kTypedDataUint32ArrayCid;
|
|
const intptr_t element_size = TypedData::ElementSizeInBytes(cid);
|
|
const intptr_t length_in_bytes = length * element_size;
|
|
Deserializer::InitializeHeader(data, cid,
|
|
TypedData::InstanceSize(length_in_bytes));
|
|
data->untag()->length_ = Smi::New(length);
|
|
data->untag()->RecomputeDataField();
|
|
intptr_t value = 0;
|
|
typed_data = data;
|
|
for (intptr_t j = 0; j < length; ++j) {
|
|
value += d.ReadUnsigned();
|
|
if (cid == kTypedDataUint16ArrayCid) {
|
|
typed_data.SetUint16(j << 1, static_cast<uint16_t>(value));
|
|
} else {
|
|
typed_data.SetUint32(j << 2, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class StackTraceSerializationCluster : public SerializationCluster {
|
|
public:
|
|
StackTraceSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("StackTrace",
|
|
kStackTraceCid,
|
|
compiler::target::StackTrace::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~StackTraceSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
StackTracePtr trace = StackTrace::RawCast(object);
|
|
objects_.Add(trace);
|
|
PushFromTo(trace);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
StackTracePtr trace = objects_[i];
|
|
s->AssignRef(trace);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
StackTracePtr trace = objects_[i];
|
|
AutoTraceObject(trace);
|
|
WriteFromTo(trace);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<StackTracePtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class StackTraceDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit StackTraceDeserializationCluster(bool is_deeply_immutable)
|
|
: DeserializationCluster("StackTrace",
|
|
/*is_canonical=*/false,
|
|
is_deeply_immutable) {}
|
|
~StackTraceDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, StackTrace::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
StackTracePtr trace = static_cast<StackTracePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(trace, kStackTraceCid,
|
|
StackTrace::InstanceSize());
|
|
d.ReadFromTo(trace);
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class RegExpSerializationCluster : public SerializationCluster {
|
|
public:
|
|
RegExpSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
|
: SerializationCluster("RegExp",
|
|
kRegExpCid,
|
|
compiler::target::RegExp::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~RegExpSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
RegExpPtr regexp = RegExp::RawCast(object);
|
|
objects_.Add(regexp);
|
|
PushFromTo(regexp);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
RegExpPtr regexp = objects_[i];
|
|
s->AssignRef(regexp);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
RegExpPtr regexp = objects_[i];
|
|
AutoTraceObject(regexp);
|
|
WriteFromTo(regexp);
|
|
s->Write<int32_t>(regexp->untag()->num_one_byte_registers_);
|
|
s->Write<int32_t>(regexp->untag()->num_two_byte_registers_);
|
|
s->Write<uint32_t>(regexp->untag()->flags_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<RegExpPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class RegExpDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit RegExpDeserializationCluster(bool is_deeply_immutable)
|
|
: DeserializationCluster("RegExp",
|
|
/*is_canonical=*/false,
|
|
is_deeply_immutable) {}
|
|
~RegExpDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, RegExp::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
RegExpPtr regexp = static_cast<RegExpPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(regexp, kRegExpCid,
|
|
RegExp::InstanceSize());
|
|
d.ReadFromTo(regexp);
|
|
regexp->untag()->num_one_byte_registers_ = d.Read<int32_t>();
|
|
regexp->untag()->num_two_byte_registers_ = d.Read<int32_t>();
|
|
regexp->untag()->flags_ = d.Read<uint32_t>();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class WeakPropertySerializationCluster : public SerializationCluster {
|
|
public:
|
|
WeakPropertySerializationCluster()
|
|
: SerializationCluster("WeakProperty",
|
|
kWeakPropertyCid,
|
|
compiler::target::WeakProperty::InstanceSize()) {}
|
|
~WeakPropertySerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
WeakPropertyPtr property = WeakProperty::RawCast(object);
|
|
objects_.Add(property);
|
|
|
|
s->PushWeak(property->untag()->key());
|
|
}
|
|
|
|
void RetraceEphemerons(Serializer* s) {
|
|
for (intptr_t i = 0; i < objects_.length(); i++) {
|
|
WeakPropertyPtr property = objects_[i];
|
|
if (s->IsReachable(property->untag()->key())) {
|
|
s->Push(property->untag()->value());
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WeakPropertyPtr property = objects_[i];
|
|
s->AssignRef(property);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WeakPropertyPtr property = objects_[i];
|
|
AutoTraceObject(property);
|
|
if (s->HasRef(property->untag()->key())) {
|
|
s->WriteOffsetRef(property->untag()->key(), WeakProperty::key_offset());
|
|
s->WriteOffsetRef(property->untag()->value(),
|
|
WeakProperty::value_offset());
|
|
} else {
|
|
s->WriteOffsetRef(Object::null(), WeakProperty::key_offset());
|
|
s->WriteOffsetRef(Object::null(), WeakProperty::value_offset());
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<WeakPropertyPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class WeakPropertyDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
WeakPropertyDeserializationCluster()
|
|
: DeserializationCluster("WeakProperty") {}
|
|
~WeakPropertyDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, WeakProperty::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
ASSERT(!is_canonical()); // Never canonical.
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
WeakPropertyPtr property = static_cast<WeakPropertyPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(property, kWeakPropertyCid,
|
|
WeakProperty::InstanceSize());
|
|
d.ReadFromTo(property);
|
|
property->untag()->next_seen_by_gc_ = WeakProperty::null();
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class MapSerializationCluster : public SerializationCluster {
|
|
public:
|
|
MapSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
intptr_t cid)
|
|
: SerializationCluster("Map",
|
|
cid,
|
|
compiler::target::Map::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~MapSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
MapPtr map = Map::RawCast(object);
|
|
// We never have mutable hashmaps in snapshots.
|
|
ASSERT(map->untag()->IsCanonical());
|
|
ASSERT_EQUAL(map.GetClassId(), kConstMapCid);
|
|
objects_.Add(map);
|
|
PushFromTo(map);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
MapPtr map = objects_[i];
|
|
s->AssignRef(map);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
MapPtr map = objects_[i];
|
|
AutoTraceObject(map);
|
|
WriteFromTo(map);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<MapPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class MapDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
MapDeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Map",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit),
|
|
cid_(cid) {}
|
|
~MapDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Map::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const intptr_t cid = cid_;
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
MapPtr map = static_cast<MapPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(map, cid, Map::InstanceSize(),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(map);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class SetSerializationCluster : public SerializationCluster {
|
|
public:
|
|
SetSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
intptr_t cid)
|
|
: SerializationCluster("Set",
|
|
cid,
|
|
compiler::target::Set::InstanceSize(),
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~SetSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
SetPtr set = Set::RawCast(object);
|
|
// We never have mutable hashsets in snapshots.
|
|
ASSERT(set->untag()->IsCanonical());
|
|
ASSERT_EQUAL(set.GetClassId(), kConstSetCid);
|
|
objects_.Add(set);
|
|
PushFromTo(set);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
SetPtr set = objects_[i];
|
|
s->AssignRef(set);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
SetPtr set = objects_[i];
|
|
AutoTraceObject(set);
|
|
WriteFromTo(set);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<SetPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class SetDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
SetDeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Set",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit),
|
|
cid_(cid) {}
|
|
~SetDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Set::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const intptr_t cid = cid_;
|
|
const bool mark_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
SetPtr set = static_cast<SetPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(set, cid, Set::InstanceSize(),
|
|
mark_canonical, is_deeply_immutable());
|
|
d.ReadFromTo(set);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ArraySerializationCluster : public SerializationCluster {
|
|
public:
|
|
ArraySerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
intptr_t cid)
|
|
: SerializationCluster("Array",
|
|
cid,
|
|
kSizeVaries,
|
|
is_canonical,
|
|
is_deeply_immutable) {}
|
|
~ArraySerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
ArrayPtr array = Array::RawCast(object);
|
|
objects_.Add(array);
|
|
|
|
s->Push(array->untag()->type_arguments());
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
s->Push(array->untag()->element(i));
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
static bool IsReadOnlyCid(intptr_t cid) {
|
|
switch (cid) {
|
|
case kPcDescriptorsCid:
|
|
case kCodeSourceMapCid:
|
|
case kCompressedStackMapsCid:
|
|
case kOneByteStringCid:
|
|
case kTwoByteStringCid:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_print_array_optimization_candidates) {
|
|
intptr_t array_count = objects_.length();
|
|
intptr_t array_count_allsmi = 0;
|
|
intptr_t array_count_allro = 0;
|
|
intptr_t array_count_empty = 0;
|
|
intptr_t element_count = 0;
|
|
intptr_t element_count_allsmi = 0;
|
|
intptr_t element_count_allro = 0;
|
|
for (intptr_t i = 0; i < array_count; i++) {
|
|
ArrayPtr array = objects_[i];
|
|
bool allsmi = true;
|
|
bool allro = true;
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
ObjectPtr element = array->untag()->element(i);
|
|
intptr_t cid = element->GetClassId();
|
|
if (!IsReadOnlyCid(cid)) allro = false;
|
|
if (cid != kSmiCid) allsmi = false;
|
|
}
|
|
element_count += length;
|
|
if (length == 0) {
|
|
array_count_empty++;
|
|
} else if (allsmi) {
|
|
array_count_allsmi++;
|
|
element_count_allsmi += length;
|
|
} else if (allro) {
|
|
array_count_allro++;
|
|
element_count_allro += length;
|
|
}
|
|
}
|
|
OS::PrintErr("Arrays\n");
|
|
OS::PrintErr(" total: %" Pd ", % " Pd " elements\n", array_count,
|
|
element_count);
|
|
OS::PrintErr(" smi-only:%" Pd ", % " Pd " elements\n",
|
|
array_count_allsmi, element_count_allsmi);
|
|
OS::PrintErr(" ro-only:%" Pd " , % " Pd " elements\n", array_count_allro,
|
|
element_count_allro);
|
|
OS::PrintErr(" empty:%" Pd "\n", array_count_empty);
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ArrayPtr array = objects_[i];
|
|
s->AssignRef(array);
|
|
AutoTraceObject(array);
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ += compiler::target::Array::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
ArrayPtr array = objects_[i];
|
|
AutoTraceObject(array);
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
WriteCompressedField(array, type_arguments);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
s->WriteElementRef(array->untag()->element(j), j);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<ArrayPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ArrayDeserializationCluster
|
|
: public AbstractInstanceDeserializationCluster {
|
|
public:
|
|
ArrayDeserializationCluster(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool is_root_unit)
|
|
: AbstractInstanceDeserializationCluster("Array",
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
is_root_unit),
|
|
cid_(cid) {}
|
|
~ArrayDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(Array::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
const intptr_t cid = cid_;
|
|
const bool stamp_canonical = is_root_unit_ && is_canonical();
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
ArrayPtr array = static_cast<ArrayPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(array, cid, Array::InstanceSize(length),
|
|
stamp_canonical, is_deeply_immutable());
|
|
if (Array::UseCardMarkingForAllocation(length)) [[unlikely]] {
|
|
array->untag()->SetCardRememberedBitUnsynchronized();
|
|
Page::Of(array)->AllocateCardTable();
|
|
}
|
|
array->untag()->type_arguments_ =
|
|
static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
array->untag()->length_ = Smi::New(length);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
array->untag()->data()[j] = d.ReadRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
const intptr_t cid_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class WeakArraySerializationCluster : public SerializationCluster {
|
|
public:
|
|
WeakArraySerializationCluster()
|
|
: SerializationCluster("WeakArray", kWeakArrayCid, kSizeVaries) {}
|
|
~WeakArraySerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
WeakArrayPtr array = WeakArray::RawCast(object);
|
|
objects_.Add(array);
|
|
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
s->PushWeak(array->untag()->element(i));
|
|
}
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WeakArrayPtr array = objects_[i];
|
|
s->AssignRef(array);
|
|
AutoTraceObject(array);
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
target_memory_size_ += compiler::target::WeakArray::InstanceSize(length);
|
|
}
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
WeakArrayPtr array = objects_[i];
|
|
AutoTraceObject(array);
|
|
const intptr_t length = Smi::Value(array->untag()->length());
|
|
s->WriteUnsigned(length);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
if (s->HasRef(array->untag()->element(j))) {
|
|
s->WriteElementRef(array->untag()->element(j), j);
|
|
} else {
|
|
s->WriteElementRef(Object::null(), j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
GrowableArray<WeakArrayPtr> objects_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class WeakArrayDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
WeakArrayDeserializationCluster() : DeserializationCluster("WeakArray") {}
|
|
~WeakArrayDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(WeakArray::InstanceSize(length)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
WeakArrayPtr array = static_cast<WeakArrayPtr>(d.Ref(id));
|
|
const intptr_t length = d.ReadUnsigned();
|
|
Deserializer::InitializeHeader(array, kWeakArrayCid,
|
|
WeakArray::InstanceSize(length));
|
|
array->untag()->next_seen_by_gc_ = WeakArray::null();
|
|
array->untag()->length_ = Smi::New(length);
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
array->untag()->data()[j] = d.ReadRef();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class StringSerializationCluster
|
|
: public CanonicalSetSerializationCluster<CanonicalStringSet,
|
|
String,
|
|
StringPtr> {
|
|
public:
|
|
// To distinguish one and two byte strings, we put a bit in the length to
|
|
// indicate which it is. The length is an unsigned SMI, so we actually have
|
|
// two spare bits available. Keep in sync with DecodeLengthAndCid.
|
|
static intptr_t EncodeLengthAndCid(intptr_t length, intptr_t cid) {
|
|
ASSERT(cid == kOneByteStringCid || cid == kTwoByteStringCid);
|
|
ASSERT(length <= compiler::target::kSmiMax);
|
|
return (length << 1) | (cid == kTwoByteStringCid ? 0x1 : 0x0);
|
|
}
|
|
|
|
StringSerializationCluster(bool is_canonical,
|
|
bool is_deeply_immutable,
|
|
bool represents_canonical_set)
|
|
: CanonicalSetSerializationCluster(kStringCid,
|
|
is_canonical,
|
|
is_deeply_immutable,
|
|
represents_canonical_set,
|
|
"String",
|
|
kSizeVaries) {}
|
|
~StringSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) {
|
|
StringPtr str = static_cast<StringPtr>(object);
|
|
objects_.Add(str);
|
|
}
|
|
|
|
void WriteAlloc(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
s->WriteUnsigned(count);
|
|
ReorderObjects(s);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
StringPtr str = objects_[i];
|
|
s->AssignRef(str);
|
|
AutoTraceObject(str);
|
|
const intptr_t cid = str->GetClassIdOfHeapObject();
|
|
const intptr_t length = Smi::Value(str->untag()->length());
|
|
const intptr_t encoded = EncodeLengthAndCid(length, cid);
|
|
s->WriteUnsigned(encoded);
|
|
target_memory_size_ +=
|
|
cid == kOneByteStringCid
|
|
? compiler::target::OneByteString::InstanceSize(length)
|
|
: compiler::target::TwoByteString::InstanceSize(length);
|
|
}
|
|
WriteCanonicalSetLayout(s);
|
|
}
|
|
|
|
void WriteFill(Serializer* s) {
|
|
const intptr_t count = objects_.length();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
StringPtr str = objects_[i];
|
|
AutoTraceObject(str);
|
|
const intptr_t cid = str->GetClassIdOfHeapObject();
|
|
const intptr_t length = Smi::Value(str->untag()->length());
|
|
const intptr_t encoded = EncodeLengthAndCid(length, cid);
|
|
s->WriteUnsigned(encoded);
|
|
if (cid == kOneByteStringCid) {
|
|
s->WriteBytes(static_cast<OneByteStringPtr>(str)->untag()->data(),
|
|
length);
|
|
} else {
|
|
s->WriteBytes(reinterpret_cast<uint8_t*>(
|
|
static_cast<TwoByteStringPtr>(str)->untag()->data()),
|
|
length * 2);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class StringDeserializationCluster
|
|
: public CanonicalSetDeserializationCluster<CanonicalStringSet> {
|
|
public:
|
|
static intptr_t DecodeLengthAndCid(intptr_t encoded, intptr_t* out_cid) {
|
|
*out_cid = (encoded & 0x1) != 0 ? kTwoByteStringCid : kOneByteStringCid;
|
|
return encoded >> 1;
|
|
}
|
|
|
|
static intptr_t InstanceSize(intptr_t length, intptr_t cid) {
|
|
return cid == kOneByteStringCid ? OneByteString::InstanceSize(length)
|
|
: TwoByteString::InstanceSize(length);
|
|
}
|
|
|
|
StringDeserializationCluster(bool is_canonical, bool is_root_unit)
|
|
: CanonicalSetDeserializationCluster(is_canonical,
|
|
/*is_deeply_immutable=*/true,
|
|
is_root_unit,
|
|
"String") {}
|
|
~StringDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t encoded = d->ReadUnsigned();
|
|
intptr_t cid = 0;
|
|
const intptr_t length = DecodeLengthAndCid(encoded, &cid);
|
|
d->AssignRef(d->Allocate(InstanceSize(length, cid)));
|
|
}
|
|
stop_index_ = d->next_index();
|
|
BuildCanonicalSetFromLayout(d);
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
StringPtr str = static_cast<StringPtr>(d.Ref(id));
|
|
const intptr_t encoded = d.ReadUnsigned();
|
|
intptr_t cid = 0;
|
|
const intptr_t length = DecodeLengthAndCid(encoded, &cid);
|
|
const intptr_t instance_size = InstanceSize(length, cid);
|
|
// Clean up last two words of the string object to simplify future
|
|
// string comparisons.
|
|
// Objects are rounded up to two-word size boundary.
|
|
*reinterpret_cast<word*>(reinterpret_cast<uint8_t*>(str->untag()) +
|
|
instance_size - 1 * kWordSize) = 0;
|
|
*reinterpret_cast<word*>(reinterpret_cast<uint8_t*>(str->untag()) +
|
|
instance_size - 2 * kWordSize) = 0;
|
|
Deserializer::InitializeHeader(str, cid, instance_size, is_canonical(),
|
|
/*is_deeply_immutable=*/true);
|
|
#if DART_COMPRESSED_POINTERS
|
|
// Gap caused by less-than-a-word length_ smi sitting before data_.
|
|
const intptr_t length_offset =
|
|
reinterpret_cast<intptr_t>(&str->untag()->length_);
|
|
const intptr_t data_offset =
|
|
cid == kOneByteStringCid
|
|
? reinterpret_cast<intptr_t>(
|
|
static_cast<OneByteStringPtr>(str)->untag()->data())
|
|
: reinterpret_cast<intptr_t>(
|
|
static_cast<TwoByteStringPtr>(str)->untag()->data());
|
|
const intptr_t length_with_gap = data_offset - length_offset;
|
|
ASSERT(length_with_gap > kCompressedWordSize);
|
|
ASSERT(length_with_gap == kWordSize);
|
|
memset(reinterpret_cast<void*>(length_offset), 0, length_with_gap);
|
|
#endif
|
|
str->untag()->length_ = Smi::New(length);
|
|
|
|
StringHasher hasher;
|
|
if (cid == kOneByteStringCid) {
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
uint8_t code_unit = d.Read<uint8_t>();
|
|
static_cast<OneByteStringPtr>(str)->untag()->data()[j] = code_unit;
|
|
hasher.Add(code_unit);
|
|
}
|
|
|
|
} else {
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
uint16_t code_unit = d.Read<uint8_t>();
|
|
code_unit = code_unit | (d.Read<uint8_t>() << 8);
|
|
static_cast<TwoByteStringPtr>(str)->untag()->data()[j] = code_unit;
|
|
hasher.Add(code_unit);
|
|
}
|
|
}
|
|
String::SetCachedHash(str, hasher.Finalize());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
if (!table_.IsNull()) {
|
|
// TODO(bootstrap-only-in-gen-snapshot)
|
|
// This table is incomplete because the cluster only includes written
|
|
// strings, missing the strings that were base objects.
|
|
if (!Snapshot::IncludesCode(d->kind())) return;
|
|
|
|
auto object_store = d->isolate_group()->object_store();
|
|
VerifyCanonicalSet(d, refs,
|
|
WeakArray::Handle(object_store->symbol_table()));
|
|
object_store->set_symbol_table(table_);
|
|
#if defined(DEBUG)
|
|
Symbols::New(Thread::Current(), ":some:new:symbol:");
|
|
ASSERT(object_store->symbol_table() == table_.ptr()); // Did not rehash.
|
|
#endif
|
|
}
|
|
}
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class FakeSerializationCluster : public SerializationCluster {
|
|
public:
|
|
FakeSerializationCluster(const char* name,
|
|
intptr_t num_objects,
|
|
intptr_t size,
|
|
intptr_t target_memory_size = 0)
|
|
: SerializationCluster(name, -1) {
|
|
num_objects_ = num_objects;
|
|
size_ = size;
|
|
target_memory_size_ = target_memory_size;
|
|
}
|
|
~FakeSerializationCluster() {}
|
|
|
|
void Trace(Serializer* s, ObjectPtr object) { UNREACHABLE(); }
|
|
void WriteAlloc(Serializer* s) { UNREACHABLE(); }
|
|
void WriteFill(Serializer* s) { UNREACHABLE(); }
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
static bool IsAbsentCid(intptr_t cid) {
|
|
switch (cid) {
|
|
case kObjectCid:
|
|
case kErrorCid:
|
|
case kCallSiteDataCid:
|
|
|
|
case kAbstractTypeCid:
|
|
case kFinalizerBaseCid:
|
|
case kTypedDataBaseCid:
|
|
case kTypedDataCid:
|
|
case kExternalTypedDataCid:
|
|
case kTypedDataViewCid:
|
|
case kStringCid:
|
|
case kFfiStructCid:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class ProgramSerializationRoots : public SerializationRoots {
|
|
public:
|
|
#define RESET_ROOT_LIST(V) \
|
|
ONLY_IF_CODE( \
|
|
V(symbol_table, WeakArray, HashTables::New<CanonicalStringSet>(4))) \
|
|
V(canonical_types, Array, HashTables::New<CanonicalTypeSet>(4)) \
|
|
V(canonical_function_types, Array, \
|
|
HashTables::New<CanonicalFunctionTypeSet>(4)) \
|
|
V(canonical_record_types, Array, HashTables::New<CanonicalRecordTypeSet>(4)) \
|
|
V(canonical_type_arguments, Array, \
|
|
HashTables::New<CanonicalTypeArgumentsSet>(4)) \
|
|
V(canonical_type_parameters, Array, \
|
|
HashTables::New<CanonicalTypeParameterSet>(4)) \
|
|
ONLY_IN_PRODUCT(ONLY_IN_AOT( \
|
|
V(closure_functions, GrowableObjectArray, GrowableObjectArray::null()))) \
|
|
ONLY_IN_AOT(V(closure_functions_table, Array, Array::null())) \
|
|
ONLY_IN_AOT(V(canonicalized_stack_map_entries, CompressedStackMaps, \
|
|
CompressedStackMaps::null()))
|
|
|
|
ProgramSerializationRoots(ObjectStore* object_store,
|
|
Snapshot::Kind snapshot_kind)
|
|
: object_store_(object_store), snapshot_kind_(snapshot_kind) {
|
|
#define ONLY_IF_CODE(code) \
|
|
if (Snapshot::IncludesCode(snapshot_kind_)) { \
|
|
code \
|
|
}
|
|
#define ONLY_IN_AOT(code) \
|
|
if (snapshot_kind_ == Snapshot::kFullAOT) { \
|
|
code \
|
|
}
|
|
#define SAVE_AND_RESET_ROOT(name, Type, init) \
|
|
do { \
|
|
saved_##name##_ = object_store->name(); \
|
|
object_store->set_##name(Type::Handle(init)); \
|
|
} while (0);
|
|
|
|
RESET_ROOT_LIST(SAVE_AND_RESET_ROOT)
|
|
#undef SAVE_AND_RESET_ROOT
|
|
#undef ONLY_IN_AOT
|
|
#undef ONLY_IF_CODE
|
|
}
|
|
~ProgramSerializationRoots() {
|
|
#define ONLY_IF_CODE(code) \
|
|
if (Snapshot::IncludesCode(snapshot_kind_)) { \
|
|
code \
|
|
}
|
|
#define ONLY_IN_AOT(code) \
|
|
if (snapshot_kind_ == Snapshot::kFullAOT) { \
|
|
code \
|
|
}
|
|
#define RESTORE_ROOT(name, Type, init) \
|
|
object_store_->set_##name(saved_##name##_);
|
|
RESET_ROOT_LIST(RESTORE_ROOT)
|
|
#undef RESTORE_ROOT
|
|
#undef ONLY_IN_AOT
|
|
#undef ONLY_IF_CODE
|
|
}
|
|
|
|
void AddBaseObjects(Serializer* s) {
|
|
// TODO(bootstrap-only-in-gen-snapshot): Always case 1.
|
|
if (Snapshot::IncludesCode(s->kind())) {
|
|
s->AddBaseObject(Roots::null_obj(), "Null", "null");
|
|
s->AddBaseObject(Roots::false_obj(), "bool", "false");
|
|
s->AddBaseObject(Roots::true_obj(), "bool", "true");
|
|
s->AddBaseObject(Roots::sentinel_obj(), "Sentinel", "sentinel");
|
|
s->AddBaseObject(Roots::unknown_constant_obj(), "Sentinel",
|
|
"unknown constant");
|
|
s->AddBaseObject(Roots::non_constant_obj(), "Sentinel", "non constant");
|
|
s->AddBaseObject(Roots::optimized_out_obj(), "Sentinel",
|
|
"<optimized out>");
|
|
} else {
|
|
Roots* roots = Roots::Current();
|
|
for (ObjectPtr* ptr = roots->from(); ptr <= roots->to(); ptr++) {
|
|
s->AddBaseObject(*ptr, "", "<raw root>");
|
|
}
|
|
for (auto* handle = roots->fromh(); handle <= roots->toh(); handle++) {
|
|
s->AddBaseObject(handle->ptr, "", "<handle root>");
|
|
}
|
|
ClassTable* table = s->isolate_group()->class_table();
|
|
for (intptr_t cid = kObjectCid; cid < kInstanceCid; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
ASSERT(table->HasValidClassAt(cid));
|
|
s->AddBaseObject(
|
|
table->At(cid), "Class",
|
|
Class::Handle(table->At(cid))
|
|
.NameCString(Object::NameVisibility::kInternalName));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PushRoots(Serializer* s) {
|
|
// TODO(bootstrap-only-in-gen-snapshot): Always case 1.
|
|
if (Snapshot::IncludesCode(s->kind())) {
|
|
// Place popular objects into early clusters so their refs will have short
|
|
// encodings.
|
|
// TODO(rmacnak): Should we handle these like null instead?
|
|
s->Push(Object::empty_array().ptr());
|
|
s->Push(Object::empty_instantiations_cache_array().ptr());
|
|
s->Push(Object::empty_subtype_test_cache_array().ptr());
|
|
s->Push(Object::empty_context_scope().ptr());
|
|
s->Push(Object::empty_object_pool().ptr());
|
|
s->Push(Object::empty_compressed_stackmaps().ptr());
|
|
s->Push(Object::empty_descriptors().ptr());
|
|
s->Push(Object::empty_var_descriptors().ptr());
|
|
s->Push(Object::empty_exception_handlers().ptr());
|
|
s->Push(Object::empty_async_exception_handlers().ptr());
|
|
s->CreateEarlyClusters();
|
|
|
|
Roots* roots = Roots::Current();
|
|
for (ObjectPtr* ptr = roots->from(); ptr <= roots->to(); ptr++) {
|
|
s->Push(*ptr);
|
|
}
|
|
for (auto* handle = roots->fromh(); handle <= roots->toh(); handle++) {
|
|
s->Push(handle->ptr);
|
|
}
|
|
for (ObjectPtr* ptr = roots->fromah(); ptr <= roots->toah(); ptr++) {
|
|
s->Push(*ptr);
|
|
}
|
|
ClassTable* table = s->isolate_group()->class_table();
|
|
for (intptr_t cid = kObjectCid; cid < kInstanceCid; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
s->Push(table->At(cid));
|
|
}
|
|
|
|
for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
ASSERT(table->HasValidClassAt(cid));
|
|
s->Push(table->At(cid));
|
|
}
|
|
}
|
|
|
|
ObjectPtr* from = object_store_->from();
|
|
ObjectPtr* to = object_store_->to_snapshot(s->kind());
|
|
for (ObjectPtr* p = from; p <= to; p++) {
|
|
s->Push(*p);
|
|
}
|
|
|
|
FieldTable* initial_field_table =
|
|
s->thread()->isolate_group()->initial_field_table();
|
|
for (intptr_t i = 0, n = initial_field_table->NumFieldIds(); i < n; i++) {
|
|
s->Push(initial_field_table->At(i));
|
|
}
|
|
|
|
FieldTable* shared_initial_field_table =
|
|
s->thread()->isolate_group()->shared_initial_field_table();
|
|
for (intptr_t i = 0, n = shared_initial_field_table->NumFieldIds(); i < n;
|
|
i++) {
|
|
s->Push(shared_initial_field_table->At(i));
|
|
}
|
|
|
|
dispatch_table_entries_ = object_store_->dispatch_table_code_entries();
|
|
// We should only have a dispatch table in precompiled mode.
|
|
ASSERT(dispatch_table_entries_.IsNull() || s->kind() == Snapshot::kFullAOT);
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
// We treat the dispatch table as a root object and trace the Code objects
|
|
// it references. Otherwise, a non-empty entry could be invalid on
|
|
// deserialization if the corresponding Code object was not reachable from
|
|
// the existing snapshot roots.
|
|
if (!dispatch_table_entries_.IsNull()) {
|
|
for (intptr_t i = 0; i < dispatch_table_entries_.Length(); i++) {
|
|
s->Push(dispatch_table_entries_.At(i));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void WriteRoots(Serializer* s) {
|
|
// TODO(bootstrap-only-in-gen-snapshot): Always case 1.
|
|
if (Snapshot::IncludesCode(s->kind())) {
|
|
Roots* roots = Roots::Current();
|
|
for (ObjectPtr* ptr = roots->from(); ptr <= roots->to(); ptr++) {
|
|
s->WriteRootRef(*ptr, "<raw root>");
|
|
}
|
|
for (auto* handle = roots->fromh(); handle <= roots->toh(); handle++) {
|
|
s->WriteRootRef(handle->ptr, "<handle root>");
|
|
}
|
|
for (ObjectPtr* ptr = roots->fromah(); ptr <= roots->toah(); ptr++) {
|
|
s->WriteRootRef(*ptr, "<api handle root>");
|
|
}
|
|
|
|
// Just to appease v8_snapshot_profile_writer_test.
|
|
ClassTable* table = s->isolate_group()->class_table();
|
|
for (intptr_t cid = kObjectCid; cid < kInstanceCid; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
s->WriteRootRef(table->At(cid), "<class table root>");
|
|
}
|
|
for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
ASSERT(table->HasValidClassAt(cid));
|
|
s->WriteRootRef(table->At(cid), "<class table root>");
|
|
}
|
|
}
|
|
|
|
ObjectPtr* from = object_store_->from();
|
|
ObjectPtr* to = object_store_->to_snapshot(s->kind());
|
|
// A strtab is smaller than an array of strings.
|
|
static const char* const names = ""
|
|
#define EMIT_FIELD_NAME(type, name) #name "_\0"
|
|
OBJECT_STORE_FIELD_LIST(
|
|
EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME,
|
|
EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME,
|
|
EMIT_FIELD_NAME)
|
|
#undef EMIT_FIELD_NAME
|
|
; // NOLINT
|
|
const char* name = names;
|
|
for (ObjectPtr* p = from; p <= to; p++) {
|
|
s->WriteRootRef(*p, name);
|
|
name += strlen(name) + 1;
|
|
}
|
|
|
|
FieldTable* initial_field_table =
|
|
s->thread()->isolate_group()->initial_field_table();
|
|
intptr_t n = initial_field_table->NumFieldIds();
|
|
s->WriteUnsigned(n);
|
|
for (intptr_t i = 0; i < n; i++) {
|
|
s->WriteRootRef(initial_field_table->At(i), "some-static-field");
|
|
}
|
|
|
|
FieldTable* shared_initial_field_table =
|
|
s->thread()->isolate_group()->shared_initial_field_table();
|
|
intptr_t n_shared = shared_initial_field_table->NumFieldIds();
|
|
s->WriteUnsigned(n_shared);
|
|
for (intptr_t i = 0; i < n_shared; i++) {
|
|
s->WriteRootRef(shared_initial_field_table->At(i),
|
|
"some-shared-static-field");
|
|
}
|
|
|
|
// The dispatch table is serialized only for precompiled snapshots.
|
|
s->WriteDispatchTable(dispatch_table_entries_);
|
|
}
|
|
|
|
virtual const CompressedStackMaps& canonicalized_stack_map_entries() const {
|
|
return saved_canonicalized_stack_map_entries_;
|
|
}
|
|
|
|
private:
|
|
ObjectStore* const object_store_;
|
|
const Snapshot::Kind snapshot_kind_;
|
|
Array& dispatch_table_entries_ = Array::Handle();
|
|
|
|
#define ONLY_IF_CODE(code) code
|
|
#define ONLY_IN_AOT(code) code
|
|
#define DECLARE_FIELD(name, Type, init) Type& saved_##name##_ = Type::Handle();
|
|
RESET_ROOT_LIST(DECLARE_FIELD)
|
|
#undef DECLARE_FIELD
|
|
#undef ONLY_IN_AOT
|
|
#undef ONLY_IF_CODE
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class ProgramDeserializationRoots : public DeserializationRoots {
|
|
public:
|
|
explicit ProgramDeserializationRoots(ObjectStore* object_store)
|
|
: object_store_(object_store) {}
|
|
|
|
void AddBaseObjects(Deserializer* d) override {
|
|
// TODO(bootstrap-only-in-gen-snapshot): Always case 1.
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
d->AddBaseObject(Roots::null_obj());
|
|
d->AddBaseObject(Roots::false_obj());
|
|
d->AddBaseObject(Roots::true_obj());
|
|
d->AddBaseObject(Roots::sentinel_obj());
|
|
d->AddBaseObject(Roots::unknown_constant_obj());
|
|
d->AddBaseObject(Roots::non_constant_obj());
|
|
d->AddBaseObject(Roots::optimized_out_obj());
|
|
} else {
|
|
Roots* roots = Roots::Current();
|
|
for (ObjectPtr* ptr = roots->from(); ptr <= roots->to(); ptr++) {
|
|
d->AddBaseObject(*ptr);
|
|
}
|
|
for (auto* handle = roots->fromh(); handle <= roots->toh(); handle++) {
|
|
d->AddBaseObject(handle->ptr);
|
|
}
|
|
ClassTable* table = d->isolate_group()->class_table();
|
|
for (intptr_t cid = kObjectCid; cid < kInstanceCid; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
ASSERT(table->HasValidClassAt(cid));
|
|
d->AddBaseObject(table->At(cid));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReadRoots(Deserializer* d) override {
|
|
// TODO(bootstrap-only-in-gen-snapshot): Always case 1.
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
Roots* roots = Roots::Current();
|
|
for (ObjectPtr* ptr = roots->from(); ptr <= roots->to(); ptr++) {
|
|
*ptr = d->ReadRef();
|
|
}
|
|
for (auto* handle = roots->fromh(); handle <= roots->toh(); handle++) {
|
|
handle->ptr = d->ReadRef();
|
|
}
|
|
for (ObjectPtr* ptr = roots->fromah(); ptr <= roots->toah(); ptr++) {
|
|
*ptr = d->ReadRef();
|
|
}
|
|
roots->InitVTables();
|
|
|
|
// Just to appease v8_snapshot_profile_writer_test.
|
|
ClassTable* table = d->isolate_group()->class_table();
|
|
for (intptr_t cid = kObjectCid; cid < kInstanceCid; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
table->SetAt(cid, (ClassPtr)d->ReadRef());
|
|
}
|
|
for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
|
|
if (IsAbsentCid(cid)) continue;
|
|
table->SetAt(cid, (ClassPtr)d->ReadRef());
|
|
}
|
|
}
|
|
|
|
// Read roots.
|
|
ObjectPtr* from = object_store_->from();
|
|
ObjectPtr* to = object_store_->to_snapshot(d->kind());
|
|
for (ObjectPtr* p = from; p <= to; p++) {
|
|
*p = d->ReadRef();
|
|
}
|
|
|
|
{
|
|
FieldTable* initial_field_table =
|
|
d->thread()->isolate_group()->initial_field_table();
|
|
intptr_t n = d->ReadUnsigned();
|
|
initial_field_table->AllocateIndex(n - 1);
|
|
for (intptr_t i = 0; i < n; i++) {
|
|
initial_field_table->SetAt(i, d->ReadRef());
|
|
}
|
|
}
|
|
|
|
{
|
|
FieldTable* shared_initial_field_table =
|
|
d->thread()->isolate_group()->shared_initial_field_table();
|
|
intptr_t n_shared = d->ReadUnsigned();
|
|
if (n_shared > 0) {
|
|
shared_initial_field_table->AllocateIndex(n_shared - 1);
|
|
for (intptr_t i = 0; i < n_shared; i++) {
|
|
shared_initial_field_table->SetAt(i, d->ReadRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deserialize dispatch table (when applicable)
|
|
d->ReadDispatchTable();
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
auto isolate_group = d->isolate_group();
|
|
{
|
|
isolate_group->class_table()->CopySizesFromClassObjects();
|
|
}
|
|
d->heap()->old_space()->EvaluateAfterLoading();
|
|
|
|
if (Snapshot::IncludesCode(d->kind())) {
|
|
d->thread()->InitVMConstants();
|
|
}
|
|
|
|
auto object_store = isolate_group->object_store();
|
|
const Array& units = Array::Handle(object_store->loading_units());
|
|
if (!units.IsNull()) {
|
|
LoadingUnit& unit = LoadingUnit::Handle();
|
|
unit ^= units.At(LoadingUnit::kRootId);
|
|
unit.set_base_objects(refs);
|
|
}
|
|
|
|
// Setup native resolver for bootstrap impl.
|
|
Bootstrap::SetupNativeResolver();
|
|
}
|
|
|
|
private:
|
|
ObjectStore* object_store_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
class UnitSerializationRoots : public SerializationRoots {
|
|
public:
|
|
explicit UnitSerializationRoots(LoadingUnitSerializationData* unit)
|
|
: unit_(unit) {}
|
|
|
|
void AddBaseObjects(Serializer* s) {
|
|
ZoneGrowableArray<Object*>* objects = unit_->parent()->objects();
|
|
for (intptr_t i = 0; i < objects->length(); i++) {
|
|
s->AddBaseObject(objects->At(i)->ptr());
|
|
}
|
|
}
|
|
|
|
void PushRoots(Serializer* s) {
|
|
for (auto deferred_object : *unit_->deferred_objects()) {
|
|
ASSERT(deferred_object->IsCode());
|
|
CodePtr code = static_cast<CodePtr>(deferred_object->ptr());
|
|
ObjectPoolPtr pool = code->untag()->object_pool_;
|
|
if (pool != ObjectPool::null()) {
|
|
const intptr_t length = pool->untag()->length_;
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
|
|
if (entry_type == ObjectPool::EntryType::kTaggedObject) {
|
|
s->Push(pool->untag()->data()[i].raw_obj_);
|
|
}
|
|
}
|
|
}
|
|
s->Push(code->untag()->code_source_map_);
|
|
}
|
|
}
|
|
|
|
void WriteRoots(Serializer* s) {
|
|
#if defined(DART_PRECOMPILER)
|
|
intptr_t start_index = 0;
|
|
intptr_t num_deferred_objects = unit_->deferred_objects()->length();
|
|
if (num_deferred_objects != 0) {
|
|
start_index = s->RefId(unit_->deferred_objects()->At(0)->ptr());
|
|
ASSERT(start_index > 0);
|
|
}
|
|
s->WriteUnsigned(start_index);
|
|
s->WriteUnsigned(num_deferred_objects);
|
|
for (intptr_t i = 0; i < num_deferred_objects; i++) {
|
|
const Object* deferred_object = (*unit_->deferred_objects())[i];
|
|
ASSERT(deferred_object->IsCode());
|
|
CodePtr code = static_cast<CodePtr>(deferred_object->ptr());
|
|
ASSERT(s->RefId(code) == (start_index + i));
|
|
ASSERT(!Code::IsDiscarded(code));
|
|
s->WriteInstructions(code->untag()->instructions_,
|
|
code->untag()->unchecked_offset_, code, false);
|
|
s->WriteRootRef(code->untag()->code_source_map_, "deferred-code");
|
|
}
|
|
|
|
ObjectPoolPtr pool =
|
|
s->isolate_group()->object_store()->global_object_pool();
|
|
const intptr_t length = pool->untag()->length_;
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
intptr_t last_write = 0;
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
|
|
if (entry_type == ObjectPool::EntryType::kTaggedObject) {
|
|
if (s->IsWritten(pool->untag()->data()[i].raw_obj_)) {
|
|
intptr_t skip = i - last_write;
|
|
s->WriteUnsigned(skip);
|
|
s->WriteRootRef(pool->untag()->data()[i].raw_obj_,
|
|
"deferred-literal");
|
|
last_write = i;
|
|
}
|
|
}
|
|
}
|
|
s->WriteUnsigned(length - last_write);
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
LoadingUnitSerializationData* unit_;
|
|
};
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
|
|
class UnitDeserializationRoots : public DeserializationRoots {
|
|
public:
|
|
explicit UnitDeserializationRoots(const LoadingUnit& unit) : unit_(unit) {}
|
|
|
|
void AddBaseObjects(Deserializer* d) override {
|
|
const Array& base_objects =
|
|
Array::Handle(LoadingUnit::Handle(unit_.parent()).base_objects());
|
|
for (intptr_t i = kFirstReference; i < base_objects.Length(); i++) {
|
|
d->AddBaseObject(base_objects.At(i));
|
|
}
|
|
}
|
|
|
|
void ReadRoots(Deserializer* d) override {
|
|
deferred_start_index_ = d->ReadUnsigned();
|
|
deferred_stop_index_ = deferred_start_index_ + d->ReadUnsigned();
|
|
for (intptr_t id = deferred_start_index_; id < deferred_stop_index_; id++) {
|
|
CodePtr code = static_cast<CodePtr>(d->Ref(id));
|
|
ASSERT(!Code::IsUnknownDartCode(code));
|
|
d->ReadInstructions(code, /*deferred=*/false);
|
|
if (code->untag()->owner_->IsHeapObject() &&
|
|
code->untag()->owner_->IsFunction()) {
|
|
FunctionPtr func = static_cast<FunctionPtr>(code->untag()->owner_);
|
|
uword entry_point = code->untag()->entry_point_;
|
|
ASSERT(entry_point != 0);
|
|
func->untag()->entry_point_ = entry_point;
|
|
uword unchecked_entry_point = code->untag()->unchecked_entry_point_;
|
|
ASSERT(unchecked_entry_point != 0);
|
|
func->untag()->unchecked_entry_point_ = unchecked_entry_point;
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (func->untag()->data()->IsHeapObject() &&
|
|
func->untag()->data()->IsClosureData()) {
|
|
// For closure functions in bare instructions mode, also update the
|
|
// cache inside the static implicit closure object, if any.
|
|
auto data = static_cast<ClosureDataPtr>(func->untag()->data());
|
|
if (data->untag()->closure() != Closure::null()) {
|
|
// Closure functions only have one entry point.
|
|
ASSERT_EQUAL(entry_point, unchecked_entry_point);
|
|
data->untag()->closure()->untag()->entry_point_ = entry_point;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
code->untag()->code_source_map_ =
|
|
static_cast<CodeSourceMapPtr>(d->ReadRef());
|
|
}
|
|
|
|
ObjectPoolPtr pool =
|
|
d->isolate_group()->object_store()->global_object_pool();
|
|
const intptr_t length = pool->untag()->length_;
|
|
uint8_t* entry_bits = pool->untag()->entry_bits();
|
|
for (intptr_t i = d->ReadUnsigned(); i < length; i += d->ReadUnsigned()) {
|
|
auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
|
|
ASSERT(entry_type == ObjectPool::EntryType::kTaggedObject);
|
|
// The existing entry will usually be null, but it might also be an
|
|
// equivalent object that was duplicated in another loading unit.
|
|
pool->untag()->data()[i].raw_obj_ = d->ReadRef();
|
|
}
|
|
|
|
// Reinitialize the dispatch table by rereading the table's serialization
|
|
// in the root snapshot.
|
|
auto isolate_group = d->isolate_group();
|
|
if (isolate_group->dispatch_table_snapshot() != nullptr) {
|
|
ReadStream stream(isolate_group->dispatch_table_snapshot(),
|
|
isolate_group->dispatch_table_snapshot_size());
|
|
const GrowableObjectArray& tables = GrowableObjectArray::Handle(
|
|
isolate_group->object_store()->instructions_tables());
|
|
InstructionsTable& root_table = InstructionsTable::Handle();
|
|
root_table ^= tables.At(0);
|
|
d->ReadDispatchTable(&stream, /*deferred=*/true, root_table,
|
|
deferred_start_index_, deferred_stop_index_);
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
d->EndInstructions();
|
|
unit_.set_base_objects(refs);
|
|
}
|
|
|
|
private:
|
|
const LoadingUnit& unit_;
|
|
intptr_t deferred_start_index_;
|
|
intptr_t deferred_stop_index_;
|
|
};
|
|
|
|
#if defined(DEBUG)
|
|
static constexpr int32_t kSectionMarker = 0xABAB;
|
|
#endif
|
|
|
|
Serializer::Serializer(Thread* thread,
|
|
Snapshot::Kind kind,
|
|
NonStreamingWriteStream* stream,
|
|
ImageWriter* image_writer,
|
|
V8SnapshotProfileWriter* profile_writer)
|
|
: ThreadStackResource(thread),
|
|
heap_(thread->isolate_group()->heap()),
|
|
zone_(thread->zone()),
|
|
kind_(kind),
|
|
stream_(stream),
|
|
image_writer_(image_writer),
|
|
canonical_clusters_by_cid_(nullptr),
|
|
immutable_clusters_by_cid_(nullptr),
|
|
clusters_by_cid_(nullptr),
|
|
stack_(),
|
|
num_cids_(0),
|
|
num_tlc_cids_(0),
|
|
num_base_objects_(0),
|
|
num_written_objects_(0),
|
|
next_ref_index_(kFirstReference),
|
|
profile_writer_(profile_writer)
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
,
|
|
current_parent_(Object::null())
|
|
#endif
|
|
#if defined(DART_PRECOMPILER)
|
|
,
|
|
deduped_instructions_sources_(zone_)
|
|
#endif
|
|
{
|
|
num_cids_ = thread->isolate_group()->class_table()->NumCids();
|
|
num_tlc_cids_ = thread->isolate_group()->class_table()->NumTopLevelCids();
|
|
canonical_clusters_by_cid_ = new SerializationCluster*[num_cids_];
|
|
for (intptr_t i = 0; i < num_cids_; i++) {
|
|
canonical_clusters_by_cid_[i] = nullptr;
|
|
}
|
|
immutable_clusters_by_cid_ = new SerializationCluster*[num_cids_];
|
|
for (intptr_t i = 0; i < num_cids_; i++) {
|
|
immutable_clusters_by_cid_[i] = nullptr;
|
|
}
|
|
clusters_by_cid_ = new SerializationCluster*[num_cids_];
|
|
for (intptr_t i = 0; i < num_cids_; i++) {
|
|
clusters_by_cid_[i] = nullptr;
|
|
}
|
|
if (profile_writer_ != nullptr) {
|
|
offsets_table_ = new (zone_) OffsetsTable(zone_);
|
|
}
|
|
}
|
|
|
|
Serializer::~Serializer() {
|
|
delete[] canonical_clusters_by_cid_;
|
|
delete[] immutable_clusters_by_cid_;
|
|
delete[] clusters_by_cid_;
|
|
}
|
|
|
|
void Serializer::AddBaseObject(ObjectPtr base_object,
|
|
const char* type,
|
|
const char* name) {
|
|
// Don't assign references to the discarded code.
|
|
const bool is_discarded_code = base_object->IsHeapObject() &&
|
|
base_object->IsCode() &&
|
|
Code::IsDiscarded(Code::RawCast(base_object));
|
|
if (!is_discarded_code) {
|
|
AssignRef(base_object);
|
|
}
|
|
num_base_objects_++;
|
|
|
|
if ((profile_writer_ != nullptr) && (type != nullptr)) {
|
|
const auto& profile_id = GetProfileId(base_object);
|
|
profile_writer_->SetObjectTypeAndName(profile_id, type, name);
|
|
profile_writer_->AddRoot(profile_id);
|
|
}
|
|
}
|
|
|
|
intptr_t Serializer::AssignRef(ObjectPtr object) {
|
|
ASSERT(IsAllocatedReference(next_ref_index_));
|
|
|
|
// The object id weak table holds image offsets for Instructions instead
|
|
// of ref indices.
|
|
ASSERT(!object->IsHeapObject() || !object->IsInstructions());
|
|
heap_->SetObjectId(object, next_ref_index_);
|
|
ASSERT(heap_->GetObjectId(object) == next_ref_index_);
|
|
|
|
objects_->Add(&Object::ZoneHandle(object));
|
|
|
|
return next_ref_index_++;
|
|
}
|
|
|
|
intptr_t Serializer::AssignArtificialRef(ObjectPtr object) {
|
|
const intptr_t ref = -(next_ref_index_++);
|
|
ASSERT(IsArtificialReference(ref));
|
|
if (object != nullptr) {
|
|
ASSERT(!object.IsHeapObject() || !object.IsInstructions());
|
|
ASSERT(heap_->GetObjectId(object) == kUnreachableReference);
|
|
heap_->SetObjectId(object, ref);
|
|
ASSERT(heap_->GetObjectId(object) == ref);
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
void Serializer::FlushProfile() {
|
|
if (profile_writer_ == nullptr) return;
|
|
const intptr_t bytes =
|
|
stream_->Position() - object_currently_writing_.last_stream_position_;
|
|
profile_writer_->AttributeBytesTo(object_currently_writing_.id_, bytes);
|
|
object_currently_writing_.last_stream_position_ = stream_->Position();
|
|
}
|
|
|
|
V8SnapshotProfileWriter::ObjectId Serializer::GetProfileId(
|
|
ObjectPtr object) const {
|
|
// Instructions are handled separately.
|
|
ASSERT(!object->IsHeapObject() || !object->IsInstructions());
|
|
return GetProfileId(UnsafeRefId(object));
|
|
}
|
|
|
|
V8SnapshotProfileWriter::ObjectId Serializer::GetProfileId(
|
|
intptr_t heap_id) const {
|
|
if (IsArtificialReference(heap_id)) {
|
|
return {IdSpace::kArtificial, -heap_id};
|
|
}
|
|
ASSERT(IsAllocatedReference(heap_id));
|
|
return {IdSpace::kSnapshot, heap_id};
|
|
}
|
|
|
|
void Serializer::AttributeReference(
|
|
ObjectPtr object,
|
|
const V8SnapshotProfileWriter::Reference& reference) {
|
|
if (profile_writer_ == nullptr) return;
|
|
const auto& object_id = GetProfileId(object);
|
|
#if defined(DART_PRECOMPILER)
|
|
if (object->IsHeapObject() && object->IsWeakSerializationReference()) {
|
|
auto const wsr = WeakSerializationReference::RawCast(object);
|
|
auto const target = wsr->untag()->target();
|
|
const auto& target_id = GetProfileId(target);
|
|
if (object_id != target_id) {
|
|
const auto& replacement_id = GetProfileId(wsr->untag()->replacement());
|
|
ASSERT(object_id == replacement_id);
|
|
// The target of the WSR will be replaced in the snapshot, so write
|
|
// attributions for both the dropped target and for the replacement.
|
|
profile_writer_->AttributeDroppedReferenceTo(
|
|
object_currently_writing_.id_, reference, target_id, replacement_id);
|
|
return;
|
|
}
|
|
// The replacement isn't used for this WSR in the snapshot, as either the
|
|
// target is strongly referenced or the WSR itself is unreachable, so fall
|
|
// through to attributing a reference to the WSR (which shares the profile
|
|
// ID of the target).
|
|
}
|
|
#endif
|
|
profile_writer_->AttributeReferenceTo(object_currently_writing_.id_,
|
|
reference, object_id);
|
|
}
|
|
|
|
Serializer::WritingObjectScope::WritingObjectScope(
|
|
Serializer* serializer,
|
|
const V8SnapshotProfileWriter::ObjectId& id,
|
|
ObjectPtr object)
|
|
: serializer_(serializer),
|
|
old_object_(serializer->object_currently_writing_.object_),
|
|
old_id_(serializer->object_currently_writing_.id_),
|
|
old_cid_(serializer->object_currently_writing_.cid_) {
|
|
if (serializer_->profile_writer_ == nullptr) return;
|
|
// The ID should correspond to one already added appropriately to the
|
|
// profile writer.
|
|
ASSERT(serializer_->profile_writer_->HasId(id));
|
|
serializer_->FlushProfile();
|
|
serializer_->object_currently_writing_.object_ = object;
|
|
serializer_->object_currently_writing_.id_ = id;
|
|
serializer_->object_currently_writing_.cid_ =
|
|
object == nullptr ? -1 : object->GetClassId();
|
|
}
|
|
|
|
Serializer::WritingObjectScope::~WritingObjectScope() {
|
|
if (serializer_->profile_writer_ == nullptr) return;
|
|
serializer_->FlushProfile();
|
|
serializer_->object_currently_writing_.object_ = old_object_;
|
|
serializer_->object_currently_writing_.id_ = old_id_;
|
|
serializer_->object_currently_writing_.cid_ = old_cid_;
|
|
}
|
|
|
|
V8SnapshotProfileWriter::ObjectId Serializer::WritingObjectScope::ReserveId(
|
|
Serializer* s,
|
|
const char* type,
|
|
ObjectPtr obj,
|
|
const char* name) {
|
|
if (s->profile_writer_ == nullptr) {
|
|
return V8SnapshotProfileWriter::kArtificialRootId;
|
|
}
|
|
if (name == nullptr) {
|
|
// Handle some cases where there are obvious names to assign.
|
|
switch (obj->GetClassId()) {
|
|
case kSmiCid: {
|
|
name = OS::SCreate(s->zone(), "%" Pd "", Smi::Value(Smi::RawCast(obj)));
|
|
break;
|
|
}
|
|
case kMintCid: {
|
|
name = OS::SCreate(s->zone(), "%" Pd64 "",
|
|
Mint::RawCast(obj)->untag()->value_);
|
|
break;
|
|
}
|
|
case kOneByteStringCid:
|
|
case kTwoByteStringCid: {
|
|
name = String::ToCString(s->thread(), String::RawCast(obj));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const auto& obj_id = s->GetProfileId(obj);
|
|
s->profile_writer_->SetObjectTypeAndName(obj_id, type, name);
|
|
return obj_id;
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
bool Serializer::CreateArtificialNodeIfNeeded(ObjectPtr obj) {
|
|
ASSERT(profile_writer() != nullptr);
|
|
|
|
// UnsafeRefId will do lazy reference allocation for WSRs.
|
|
intptr_t id = UnsafeRefId(obj);
|
|
ASSERT(id != kUnallocatedReference);
|
|
if (id != kUnreachableReference) {
|
|
return IsArtificialReference(id);
|
|
}
|
|
if (obj->IsHeapObject() && obj->IsWeakSerializationReference()) {
|
|
auto const target =
|
|
WeakSerializationReference::RawCast(obj)->untag()->target();
|
|
CreateArtificialNodeIfNeeded(target);
|
|
// Since the WSR is unreachable, we can replace its id with whatever the
|
|
// ID of the target is, whether real or artificial.
|
|
id = heap_->GetObjectId(target);
|
|
heap_->SetObjectId(obj, id);
|
|
return IsArtificialReference(id);
|
|
}
|
|
|
|
const char* type = nullptr;
|
|
const char* name = nullptr;
|
|
GrowableArray<std::pair<ObjectPtr, V8SnapshotProfileWriter::Reference>> links;
|
|
const classid_t cid = obj->GetClassId();
|
|
switch (cid) {
|
|
// For profiling static call target tables in AOT mode.
|
|
case kSmiCid: {
|
|
type = "Smi";
|
|
break;
|
|
}
|
|
// For profiling per-code object pools in bare instructions mode.
|
|
case kObjectPoolCid: {
|
|
type = "ObjectPool";
|
|
auto const pool = ObjectPool::RawCast(obj);
|
|
for (intptr_t i = 0; i < pool->untag()->length_; i++) {
|
|
uint8_t bits = pool->untag()->entry_bits()[i];
|
|
if (ObjectPool::TypeBits::decode(bits) ==
|
|
ObjectPool::EntryType::kTaggedObject) {
|
|
auto const elem = pool->untag()->data()[i].raw_obj_;
|
|
// Elements should be reachable from the global object pool.
|
|
ASSERT(HasRef(elem));
|
|
links.Add({elem, V8SnapshotProfileWriter::Reference::Element(i)});
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// For profiling static call target tables and the dispatch table in AOT.
|
|
case kImmutableArrayCid:
|
|
case kArrayCid: {
|
|
type = "Array";
|
|
auto const array = Array::RawCast(obj);
|
|
for (intptr_t i = 0, n = Smi::Value(array->untag()->length()); i < n;
|
|
i++) {
|
|
ObjectPtr elem = array->untag()->element(i);
|
|
links.Add({elem, V8SnapshotProfileWriter::Reference::Element(i)});
|
|
}
|
|
break;
|
|
}
|
|
// For profiling the dispatch table.
|
|
case kCodeCid: {
|
|
type = "Code";
|
|
auto const code = Code::RawCast(obj);
|
|
name = CodeSerializationCluster::MakeDisambiguatedCodeName(this, code);
|
|
links.Add({code->untag()->owner(),
|
|
V8SnapshotProfileWriter::Reference::Property("owner_")});
|
|
break;
|
|
}
|
|
case kFunctionCid: {
|
|
FunctionPtr func = static_cast<FunctionPtr>(obj);
|
|
type = "Function";
|
|
name = FunctionSerializationCluster::MakeDisambiguatedFunctionName(this,
|
|
func);
|
|
links.Add({func->untag()->owner(),
|
|
V8SnapshotProfileWriter::Reference::Property("owner_")});
|
|
ObjectPtr data = func->untag()->data();
|
|
if (data->GetClassId() == kClosureDataCid) {
|
|
links.Add(
|
|
{data, V8SnapshotProfileWriter::Reference::Property("data_")});
|
|
}
|
|
break;
|
|
}
|
|
case kClosureDataCid: {
|
|
auto data = static_cast<ClosureDataPtr>(obj);
|
|
type = "ClosureData";
|
|
links.Add(
|
|
{data->untag()->parent_function(),
|
|
V8SnapshotProfileWriter::Reference::Property("parent_function_")});
|
|
break;
|
|
}
|
|
case kClassCid: {
|
|
ClassPtr cls = static_cast<ClassPtr>(obj);
|
|
type = "Class";
|
|
name = String::ToCString(thread(), cls->untag()->name());
|
|
links.Add({cls->untag()->library(),
|
|
V8SnapshotProfileWriter::Reference::Property("library_")});
|
|
break;
|
|
}
|
|
case kPatchClassCid: {
|
|
PatchClassPtr patch_cls = static_cast<PatchClassPtr>(obj);
|
|
type = "PatchClass";
|
|
links.Add(
|
|
{patch_cls->untag()->wrapped_class(),
|
|
V8SnapshotProfileWriter::Reference::Property("wrapped_class_")});
|
|
break;
|
|
}
|
|
case kLibraryCid: {
|
|
LibraryPtr lib = static_cast<LibraryPtr>(obj);
|
|
type = "Library";
|
|
name = String::ToCString(thread(), lib->untag()->url());
|
|
break;
|
|
}
|
|
case kFunctionTypeCid: {
|
|
type = "FunctionType";
|
|
break;
|
|
};
|
|
case kRecordTypeCid: {
|
|
type = "RecordType";
|
|
break;
|
|
};
|
|
// Can appear in weakened constant hash tables.
|
|
case kMintCid: {
|
|
type = "Mint";
|
|
break;
|
|
}
|
|
default:
|
|
FATAL("Request to create artificial node for object with cid %d", cid);
|
|
}
|
|
|
|
id = AssignArtificialRef(obj);
|
|
Serializer::WritingObjectScope scope(this, type, obj, name);
|
|
for (const auto& link : links) {
|
|
CreateArtificialNodeIfNeeded(link.first);
|
|
AttributeReference(link.first, link.second);
|
|
}
|
|
return true;
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
intptr_t Serializer::RefId(ObjectPtr object) const {
|
|
auto const id = UnsafeRefId(object);
|
|
if (IsAllocatedReference(id)) {
|
|
return id;
|
|
}
|
|
ASSERT(id == kUnreachableReference || IsArtificialReference(id));
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread());
|
|
auto& handle = thread()->ObjectHandle();
|
|
handle = object;
|
|
FATAL("Reference to unreachable object %s", handle.ToCString());
|
|
}
|
|
|
|
intptr_t Serializer::UnsafeRefId(ObjectPtr object) const {
|
|
// The object id weak table holds image offsets for Instructions instead
|
|
// of ref indices.
|
|
ASSERT(!object->IsHeapObject() || !object->IsInstructions());
|
|
if (!Snapshot::IncludesCode(kind_) && object->GetClassId() == kCodeCid) {
|
|
return RefId(Object::null());
|
|
}
|
|
auto id = heap_->GetObjectId(object);
|
|
if (id != kUnallocatedReference) {
|
|
return id;
|
|
}
|
|
// This is the only case where we may still see unallocated references after
|
|
// WriteAlloc is finished.
|
|
if (object->IsWeakSerializationReference()) {
|
|
// Lazily set the object ID of the WSR to the object which will replace
|
|
// it in the snapshot.
|
|
auto const wsr = static_cast<WeakSerializationReferencePtr>(object);
|
|
// Either the target or the replacement must be allocated, since the
|
|
// WSR is reachable.
|
|
id = HasRef(wsr->untag()->target()) ? RefId(wsr->untag()->target())
|
|
: RefId(wsr->untag()->replacement());
|
|
heap_->SetObjectId(wsr, id);
|
|
return id;
|
|
}
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread());
|
|
auto& handle = thread()->ObjectHandle();
|
|
handle = object;
|
|
FATAL("Reference for object %s is unallocated", handle.ToCString());
|
|
}
|
|
|
|
const char* Serializer::ReadOnlyObjectType(intptr_t cid) {
|
|
switch (cid) {
|
|
case kPcDescriptorsCid:
|
|
return "PcDescriptors";
|
|
case kCodeSourceMapCid:
|
|
return "CodeSourceMap";
|
|
case kCompressedStackMapsCid:
|
|
return "CompressedStackMaps";
|
|
case kStringCid:
|
|
return current_loading_unit_id_ <= LoadingUnit::kRootId
|
|
? "CanonicalString"
|
|
: nullptr;
|
|
case kOneByteStringCid:
|
|
return current_loading_unit_id_ <= LoadingUnit::kRootId
|
|
? "OneByteStringCid"
|
|
: nullptr;
|
|
case kTwoByteStringCid:
|
|
return current_loading_unit_id_ <= LoadingUnit::kRootId
|
|
? "TwoByteStringCid"
|
|
: nullptr;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
SerializationCluster* Serializer::NewClusterForClass(intptr_t cid,
|
|
bool is_canonical,
|
|
bool is_deeply_immutable) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
UNREACHABLE();
|
|
return nullptr;
|
|
#else
|
|
Zone* Z = zone_;
|
|
if (cid >= kNumPredefinedCids || cid == kInstanceCid) {
|
|
Push(isolate_group()->class_table()->At(cid));
|
|
return new (Z)
|
|
InstanceSerializationCluster(is_canonical, is_deeply_immutable, cid);
|
|
}
|
|
if (IsTypedDataViewClassId(cid)) {
|
|
return new (Z) TypedDataViewSerializationCluster(cid);
|
|
}
|
|
if (IsExternalTypedDataClassId(cid)) {
|
|
return new (Z) ExternalTypedDataSerializationCluster(cid);
|
|
}
|
|
if (IsTypedDataClassId(cid)) {
|
|
return new (Z) TypedDataSerializationCluster(cid);
|
|
}
|
|
|
|
#if !defined(DART_COMPRESSED_POINTERS)
|
|
// Sometimes we write memory images for read-only objects that contain no
|
|
// pointers. These can be mmapped directly, needing no relocation, and added
|
|
// to the list of heap pages. This gives us lazy/demand paging from the OS.
|
|
// We do not do this for snapshots without code to keep snapshots portable
|
|
// between machines with different word sizes. We do not do this when we use
|
|
// compressed pointers because we cannot always control the load address of
|
|
// the memory image, and it might be outside the 4GB region addressable by
|
|
// compressed pointers.
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
if (auto const type = ReadOnlyObjectType(cid)) {
|
|
return new (Z) RODataSerializationCluster(Z, type, cid, is_canonical,
|
|
is_deeply_immutable);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
const bool cluster_represents_canonical_set =
|
|
current_loading_unit_id_ <= LoadingUnit::kRootId && is_canonical;
|
|
|
|
switch (cid) {
|
|
case kClassCid:
|
|
return new (Z) ClassSerializationCluster(num_cids_ + num_tlc_cids_);
|
|
case kTypeParametersCid:
|
|
return new (Z)
|
|
TypeParametersSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kTypeArgumentsCid:
|
|
return new (Z) TypeArgumentsSerializationCluster(
|
|
is_canonical, is_deeply_immutable, cluster_represents_canonical_set);
|
|
case kPatchClassCid:
|
|
return new (Z) PatchClassSerializationCluster();
|
|
case kFunctionCid:
|
|
return new (Z) FunctionSerializationCluster();
|
|
case kClosureDataCid:
|
|
return new (Z) ClosureDataSerializationCluster();
|
|
case kFfiTrampolineDataCid:
|
|
return new (Z) FfiTrampolineDataSerializationCluster();
|
|
case kFieldCid:
|
|
return new (Z) FieldSerializationCluster();
|
|
case kScriptCid:
|
|
return new (Z) ScriptSerializationCluster();
|
|
case kLibraryCid:
|
|
return new (Z) LibrarySerializationCluster();
|
|
case kNamespaceCid:
|
|
return new (Z) NamespaceSerializationCluster();
|
|
case kKernelProgramInfoCid:
|
|
return new (Z) KernelProgramInfoSerializationCluster();
|
|
case kCodeCid:
|
|
return new (Z) CodeSerializationCluster(heap_);
|
|
#if defined(DART_BYTECODE_INTERPRETER)
|
|
case kBytecodeCid:
|
|
return new (Z) BytecodeSerializationCluster();
|
|
#endif
|
|
case kObjectPoolCid:
|
|
return new (Z) ObjectPoolSerializationCluster();
|
|
case kPcDescriptorsCid:
|
|
return new (Z) PcDescriptorsSerializationCluster();
|
|
case kCodeSourceMapCid:
|
|
return new (Z) CodeSourceMapSerializationCluster();
|
|
case kCompressedStackMapsCid:
|
|
return new (Z) CompressedStackMapsSerializationCluster();
|
|
case kLocalVarDescriptorsCid:
|
|
return new (Z) LocalVarDescriptorsSerializationCluster();
|
|
case kExceptionHandlersCid:
|
|
return new (Z) ExceptionHandlersSerializationCluster();
|
|
case kContextCid:
|
|
return new (Z) ContextSerializationCluster();
|
|
case kContextScopeCid:
|
|
return new (Z) ContextScopeSerializationCluster();
|
|
case kUnlinkedCallCid:
|
|
return new (Z) UnlinkedCallSerializationCluster();
|
|
case kICDataCid:
|
|
return new (Z) ICDataSerializationCluster();
|
|
case kMegamorphicCacheCid:
|
|
return new (Z) MegamorphicCacheSerializationCluster();
|
|
case kSubtypeTestCacheCid:
|
|
return new (Z) SubtypeTestCacheSerializationCluster();
|
|
case kLoadingUnitCid:
|
|
return new (Z) LoadingUnitSerializationCluster();
|
|
case kApiErrorCid:
|
|
return new (Z) ApiErrorSerializationCluster();
|
|
case kLanguageErrorCid:
|
|
return new (Z) LanguageErrorSerializationCluster();
|
|
case kUnhandledExceptionCid:
|
|
return new (Z) UnhandledExceptionSerializationCluster();
|
|
case kUnwindErrorCid:
|
|
return new (Z) UnwindErrorSerializationCluster();
|
|
case kLibraryPrefixCid:
|
|
return new (Z) LibraryPrefixSerializationCluster();
|
|
case kTypeCid:
|
|
return new (Z) TypeSerializationCluster(is_canonical, is_deeply_immutable,
|
|
cluster_represents_canonical_set);
|
|
case kFunctionTypeCid:
|
|
return new (Z) FunctionTypeSerializationCluster(
|
|
is_canonical, is_deeply_immutable, cluster_represents_canonical_set);
|
|
case kRecordTypeCid:
|
|
return new (Z) RecordTypeSerializationCluster(
|
|
is_canonical, is_deeply_immutable, cluster_represents_canonical_set);
|
|
case kTypeParameterCid:
|
|
return new (Z) TypeParameterSerializationCluster(
|
|
is_canonical, is_deeply_immutable, cluster_represents_canonical_set);
|
|
case kClosureCid:
|
|
return new (Z)
|
|
ClosureSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kMintCid:
|
|
return new (Z)
|
|
MintSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kDoubleCid:
|
|
return new (Z)
|
|
DoubleSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kInt32x4Cid:
|
|
case kFloat32x4Cid:
|
|
case kFloat64x2Cid:
|
|
return new (Z)
|
|
Simd128SerializationCluster(cid, is_canonical, is_deeply_immutable);
|
|
case kGrowableObjectArrayCid:
|
|
return new (Z) GrowableObjectArraySerializationCluster(
|
|
is_canonical, is_deeply_immutable);
|
|
case kRecordCid:
|
|
return new (Z)
|
|
RecordSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kStackTraceCid:
|
|
return new (Z)
|
|
StackTraceSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kRegExpCid:
|
|
return new (Z)
|
|
RegExpSerializationCluster(is_canonical, is_deeply_immutable);
|
|
case kWeakPropertyCid:
|
|
return new (Z) WeakPropertySerializationCluster();
|
|
case kMapCid:
|
|
// We do not have mutable hash maps in snapshots.
|
|
UNREACHABLE();
|
|
case kConstMapCid:
|
|
return new (Z) MapSerializationCluster(is_canonical, is_deeply_immutable,
|
|
kConstMapCid);
|
|
case kSetCid:
|
|
// We do not have mutable hash sets in snapshots.
|
|
UNREACHABLE();
|
|
case kConstSetCid:
|
|
return new (Z) SetSerializationCluster(is_canonical, is_deeply_immutable,
|
|
kConstSetCid);
|
|
case kArrayCid:
|
|
return new (Z) ArraySerializationCluster(is_canonical,
|
|
is_deeply_immutable, kArrayCid);
|
|
case kImmutableArrayCid:
|
|
return new (Z) ArraySerializationCluster(
|
|
is_canonical, is_deeply_immutable, kImmutableArrayCid);
|
|
case kWeakArrayCid:
|
|
return new (Z) WeakArraySerializationCluster();
|
|
case kStringCid:
|
|
return new (Z) StringSerializationCluster(
|
|
is_canonical, is_deeply_immutable, cluster_represents_canonical_set);
|
|
#define CASE_FFI_CID(name) case kFfi##name##Cid:
|
|
CLASS_LIST_FFI_TYPE_MARKER(CASE_FFI_CID)
|
|
#undef CASE_FFI_CID
|
|
return new (Z)
|
|
InstanceSerializationCluster(is_canonical, is_deeply_immutable, cid);
|
|
case kDeltaEncodedTypedDataCid:
|
|
return new (Z) DeltaEncodedTypedDataSerializationCluster();
|
|
case kWeakSerializationReferenceCid:
|
|
#if defined(DART_PRECOMPILER)
|
|
ASSERT(kind_ == Snapshot::kFullAOT);
|
|
return new (Z) WeakSerializationReferenceSerializationCluster();
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// The caller will check for nullptr and provide an error with more context
|
|
// than is available here.
|
|
return nullptr;
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
}
|
|
|
|
bool Serializer::InCurrentLoadingUnitOrRoot(ObjectPtr obj) {
|
|
if (loading_units_ == nullptr) return true;
|
|
|
|
intptr_t unit_id = heap_->GetLoadingUnit(obj);
|
|
if (unit_id == WeakTable::kNoValue) {
|
|
FATAL("Missing loading unit assignment: %s\n",
|
|
Object::Handle(obj).ToCString());
|
|
}
|
|
return unit_id == LoadingUnit::kRootId || unit_id == current_loading_unit_id_;
|
|
}
|
|
|
|
void Serializer::RecordDeferredCode(CodePtr code) {
|
|
const intptr_t unit_id = heap_->GetLoadingUnit(code);
|
|
ASSERT(unit_id != WeakTable::kNoValue && unit_id != LoadingUnit::kRootId);
|
|
(*loading_units_)[unit_id]->AddDeferredObject(code);
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#if defined(DART_PRECOMPILER)
|
|
// We use the following encoding schemes when encoding references to Code
|
|
// objects.
|
|
//
|
|
// In AOT mode:
|
|
//
|
|
// 0 -- LazyCompile stub
|
|
// 1 -+
|
|
// | for non-root-unit/non-VM snapshots
|
|
// ... > reference into parent snapshot objects
|
|
// | (base is num_base_objects_ in this case, 0 otherwise).
|
|
// base -+
|
|
// base + 1 -+
|
|
// | for non-deferred Code objects (those with instructions)
|
|
// > index in into the instructions table (code_index_).
|
|
// | (L is code_index_.Length()).
|
|
// base + L -+
|
|
// ... -+
|
|
// | for deferred Code objects (those without instructions)
|
|
// > index of this Code object in the deferred part of the
|
|
// | Code cluster.
|
|
//
|
|
// Note that this encoding has the following property: non-discarded
|
|
// non-deferred Code objects form the tail of the instruction table
|
|
// which makes indices assigned to non-discarded non-deferred Code objects
|
|
// and deferred Code objects continuous. This means when decoding
|
|
// code_index - (base + 1) - first_entry_with_code yields an index of the
|
|
// Code object in the Code cluster both for non-deferred and deferred
|
|
// Code objects.
|
|
//
|
|
// For JIT snapshots we do:
|
|
//
|
|
// 0 -- LazyCompile stub
|
|
// 1 -+
|
|
// |
|
|
// ... > index of the Code object in the Code cluster.
|
|
// |
|
|
//
|
|
intptr_t Serializer::GetCodeIndex(CodePtr code) {
|
|
// In the precompiled mode Code object is uniquely identified by its
|
|
// instructions (because ProgramVisitor::DedupInstructions will dedup Code
|
|
// objects with the same instructions).
|
|
if (code == StubCode::LazyCompile().ptr()) {
|
|
return 0;
|
|
} else if (FLAG_precompiled_mode) {
|
|
const intptr_t ref = heap_->GetObjectId(code);
|
|
ASSERT(!IsReachableReference(ref) == Code::IsDiscarded(code));
|
|
|
|
const intptr_t base = (current_loading_unit_id() == LoadingUnit::kRootId)
|
|
? 0
|
|
: num_base_objects_;
|
|
|
|
// Check if we are referring to the Code object which originates from the
|
|
// parent loading unit. In this case we write out the reference of this
|
|
// object.
|
|
if (!Code::IsDiscarded(code) && ref < base) {
|
|
RELEASE_ASSERT(current_loading_unit_id() != LoadingUnit::kRootId);
|
|
return 1 + ref;
|
|
}
|
|
|
|
// Otherwise the code object must either be discarded or originate from
|
|
// the Code cluster.
|
|
ASSERT(Code::IsDiscarded(code) || (code_cluster_->first_ref() <= ref &&
|
|
ref <= code_cluster_->last_ref()));
|
|
|
|
// If Code object is non-deferred then simply write out the index of the
|
|
// entry point, otherwise write out the index of the deferred code object.
|
|
if (ref < code_cluster_->first_deferred_ref()) {
|
|
const intptr_t key = static_cast<intptr_t>(code->untag()->instructions_);
|
|
ASSERT(code_index_.HasKey(key));
|
|
const intptr_t result = code_index_.Lookup(key);
|
|
ASSERT(0 < result && result <= code_index_.Length());
|
|
// Note: result already has + 1.
|
|
return base + result;
|
|
} else {
|
|
// Note: only root snapshot can have deferred Code objects in the
|
|
// cluster.
|
|
const intptr_t cluster_index = ref - code_cluster_->first_deferred_ref();
|
|
return 1 + base + code_index_.Length() + cluster_index;
|
|
}
|
|
} else {
|
|
const intptr_t ref = heap_->GetObjectId(code);
|
|
ASSERT(IsAllocatedReference(ref));
|
|
ASSERT(code_cluster_->first_ref() <= ref &&
|
|
ref <= code_cluster_->last_ref());
|
|
return 1 + (ref - code_cluster_->first_ref());
|
|
}
|
|
}
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
void Serializer::PrepareInstructions(
|
|
const CompressedStackMaps& canonical_stack_map_entries) {
|
|
if (!Snapshot::IncludesCode(kind())) return;
|
|
|
|
// Code objects that have identical/duplicate instructions must be adjacent in
|
|
// the order that Code objects are written because the encoding of the
|
|
// reference from the Code to the Instructions assumes monotonically
|
|
// increasing offsets as part of a delta encoding. Also the code order table
|
|
// that allows for mapping return addresses back to Code objects depends on
|
|
// this sorting.
|
|
if (code_cluster_ != nullptr) {
|
|
CodeSerializationCluster::Sort(this, code_cluster_->objects());
|
|
}
|
|
if ((loading_units_ != nullptr) &&
|
|
(current_loading_unit_id_ == LoadingUnit::kRootId)) {
|
|
for (intptr_t i = LoadingUnit::kRootId + 1; i < loading_units_->length();
|
|
i++) {
|
|
auto unit_objects = loading_units_->At(i)->deferred_objects();
|
|
CodeSerializationCluster::Sort(this, unit_objects);
|
|
ASSERT(unit_objects->length() == 0 || code_cluster_ != nullptr);
|
|
for (intptr_t j = 0; j < unit_objects->length(); j++) {
|
|
code_cluster_->deferred_objects()->Add(unit_objects->At(j)->ptr());
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
if (kind() == Snapshot::kFullAOT) {
|
|
// Group the code objects whose instructions are not being deferred in this
|
|
// snapshot unit in the order they will be written: first the code objects
|
|
// encountered for this first time in this unit being written by the
|
|
// CodeSerializationCluster, then code object previously deferred whose
|
|
// instructions are now written by UnitSerializationRoots. This order needs
|
|
// to be known to finalize bare-instructions-mode's PC-relative calls.
|
|
GrowableArray<CodePtr> code_objects;
|
|
if (code_cluster_ != nullptr) {
|
|
auto in = code_cluster_->objects();
|
|
for (intptr_t i = 0; i < in->length(); i++) {
|
|
code_objects.Add(in->At(i));
|
|
}
|
|
}
|
|
if (loading_units_ != nullptr) {
|
|
auto in =
|
|
loading_units_->At(current_loading_unit_id_)->deferred_objects();
|
|
for (intptr_t i = 0; i < in->length(); i++) {
|
|
code_objects.Add(in->At(i)->ptr());
|
|
}
|
|
}
|
|
|
|
GrowableArray<ImageWriterCommand> writer_commands;
|
|
RelocateCodeObjects(&code_objects, &writer_commands);
|
|
image_writer_->PrepareForSerialization(&writer_commands);
|
|
|
|
if (code_objects.length() == 0) {
|
|
return;
|
|
}
|
|
|
|
// Build UntaggedInstructionsTable::Data object to be added to the
|
|
// read-only data section of the snapshot. It contains:
|
|
//
|
|
// - a binary search table mapping an Instructions entry point to its
|
|
// stack maps (by offset from the beginning of the Data object);
|
|
// - followed by stack maps bytes;
|
|
// - followed by canonical stack map entries.
|
|
//
|
|
struct StackMapInfo : public ZoneObject {
|
|
CompressedStackMapsPtr map;
|
|
intptr_t use_count;
|
|
uint32_t offset;
|
|
};
|
|
|
|
GrowableArray<StackMapInfo*> stack_maps;
|
|
IntMap<StackMapInfo*> stack_maps_info;
|
|
|
|
// Build code_index_ (which maps Instructions object to the order in
|
|
// which they appear in the code section in the end) and collect all
|
|
// stack maps.
|
|
// We also find the first Instructions object which is going to have
|
|
// Code object associated with it. This will allow to reduce the binary
|
|
// search space when searching specifically for the code object in runtime.
|
|
uint32_t total = 0;
|
|
intptr_t not_discarded_count = 0;
|
|
uint32_t first_entry_with_code = 0;
|
|
for (auto& cmd : writer_commands) {
|
|
if (cmd.op == ImageWriterCommand::InsertInstructionOfCode) {
|
|
RELEASE_ASSERT(code_objects[total] ==
|
|
cmd.insert_instruction_of_code.code);
|
|
ASSERT(!Code::IsDiscarded(cmd.insert_instruction_of_code.code) ||
|
|
(not_discarded_count == 0));
|
|
if (!Code::IsDiscarded(cmd.insert_instruction_of_code.code)) {
|
|
if (not_discarded_count == 0) {
|
|
first_entry_with_code = total;
|
|
}
|
|
not_discarded_count++;
|
|
}
|
|
total++;
|
|
|
|
// Update code_index_.
|
|
{
|
|
const intptr_t instr = static_cast<intptr_t>(
|
|
cmd.insert_instruction_of_code.code->untag()->instructions_);
|
|
ASSERT(!code_index_.HasKey(instr));
|
|
code_index_.Insert(instr, total);
|
|
}
|
|
|
|
// Collect stack maps.
|
|
CompressedStackMapsPtr stack_map =
|
|
cmd.insert_instruction_of_code.code->untag()->compressed_stackmaps_;
|
|
const intptr_t key = static_cast<intptr_t>(stack_map);
|
|
|
|
if (stack_maps_info.HasKey(key)) {
|
|
stack_maps_info.Lookup(key)->use_count++;
|
|
} else {
|
|
auto info = new StackMapInfo();
|
|
info->map = stack_map;
|
|
info->use_count = 1;
|
|
stack_maps.Add(info);
|
|
stack_maps_info.Insert(key, info);
|
|
}
|
|
}
|
|
}
|
|
ASSERT(static_cast<intptr_t>(total) == code_index_.Length());
|
|
instructions_table_len_ = not_discarded_count;
|
|
|
|
// Sort stack maps by usage so that most commonly used stack maps are
|
|
// together at the start of the Data object.
|
|
stack_maps.Sort([](StackMapInfo* const* a, StackMapInfo* const* b) {
|
|
if ((*a)->use_count < (*b)->use_count) return 1;
|
|
if ((*a)->use_count > (*b)->use_count) return -1;
|
|
return 0;
|
|
});
|
|
|
|
// Build Data object.
|
|
MallocWriteStream pc_mapping(4 * KB);
|
|
|
|
// Write the header out.
|
|
{
|
|
UntaggedInstructionsTable::Data header;
|
|
memset(&header, 0, sizeof(header));
|
|
header.length = total;
|
|
header.first_entry_with_code = first_entry_with_code;
|
|
pc_mapping.WriteFixed<UntaggedInstructionsTable::Data>(header);
|
|
}
|
|
|
|
// Reserve space for the binary search table.
|
|
for (auto& cmd : writer_commands) {
|
|
if (cmd.op == ImageWriterCommand::InsertInstructionOfCode) {
|
|
pc_mapping.WriteFixed<UntaggedInstructionsTable::DataEntry>({0, 0});
|
|
}
|
|
}
|
|
|
|
// Now write collected stack maps after the binary search table.
|
|
auto write_stack_map = [&](CompressedStackMapsPtr smap) {
|
|
const auto flags_and_size = smap->untag()->payload()->flags_and_size();
|
|
const auto payload_size =
|
|
UntaggedCompressedStackMaps::SizeField::decode(flags_and_size);
|
|
pc_mapping.WriteFixed<uint32_t>(flags_and_size);
|
|
pc_mapping.WriteBytes(smap->untag()->payload()->data(), payload_size);
|
|
};
|
|
|
|
for (auto sm : stack_maps) {
|
|
sm->offset = pc_mapping.bytes_written();
|
|
write_stack_map(sm->map);
|
|
}
|
|
|
|
// Write canonical entries (if any).
|
|
if (!canonical_stack_map_entries.IsNull()) {
|
|
auto header = reinterpret_cast<UntaggedInstructionsTable::Data*>(
|
|
pc_mapping.buffer());
|
|
header->canonical_stack_map_entries_offset = pc_mapping.bytes_written();
|
|
write_stack_map(canonical_stack_map_entries.ptr());
|
|
}
|
|
const auto total_bytes = pc_mapping.bytes_written();
|
|
|
|
// Now that we have offsets to all stack maps we can write binary
|
|
// search table.
|
|
pc_mapping.SetPosition(
|
|
sizeof(UntaggedInstructionsTable::Data)); // Skip the header.
|
|
for (auto& cmd : writer_commands) {
|
|
if (cmd.op == ImageWriterCommand::InsertInstructionOfCode) {
|
|
CompressedStackMapsPtr smap =
|
|
cmd.insert_instruction_of_code.code->untag()->compressed_stackmaps_;
|
|
const auto offset =
|
|
stack_maps_info.Lookup(static_cast<intptr_t>(smap))->offset;
|
|
const auto entry = image_writer_->GetTextOffsetFor(
|
|
Code::InstructionsOf(cmd.insert_instruction_of_code.code),
|
|
cmd.insert_instruction_of_code.code);
|
|
|
|
pc_mapping.WriteFixed<UntaggedInstructionsTable::DataEntry>(
|
|
{static_cast<uint32_t>(entry), offset});
|
|
}
|
|
}
|
|
// Restore position so that Steal does not truncate the buffer.
|
|
pc_mapping.SetPosition(total_bytes);
|
|
|
|
intptr_t length = 0;
|
|
uint8_t* bytes = pc_mapping.Steal(&length);
|
|
|
|
instructions_table_rodata_offset_ =
|
|
image_writer_->AddBytesToData(bytes, length);
|
|
// Attribute all bytes in this object to the root for simplicity.
|
|
if (profile_writer_ != nullptr) {
|
|
const auto offset_space = IdSpace::kIsolateData;
|
|
profile_writer_->AttributeReferenceTo(
|
|
V8SnapshotProfileWriter::kArtificialRootId,
|
|
V8SnapshotProfileWriter::Reference::Property(
|
|
"<instructions-table-rodata>"),
|
|
{offset_space, instructions_table_rodata_offset_});
|
|
}
|
|
}
|
|
#endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
|
}
|
|
|
|
void Serializer::WriteInstructions(InstructionsPtr instr,
|
|
uint32_t unchecked_offset,
|
|
CodePtr code,
|
|
bool deferred) {
|
|
ASSERT(code != Code::null());
|
|
|
|
ASSERT(InCurrentLoadingUnitOrRoot(code) != deferred);
|
|
if (deferred) {
|
|
return;
|
|
}
|
|
|
|
const intptr_t offset = image_writer_->GetTextOffsetFor(instr, code);
|
|
#if defined(DART_PRECOMPILER)
|
|
if (profile_writer_ != nullptr) {
|
|
ASSERT(object_currently_writing_.id_ !=
|
|
V8SnapshotProfileWriter::kArtificialRootId);
|
|
const auto offset_space = IdSpace::kIsolateText;
|
|
profile_writer_->AttributeReferenceTo(
|
|
object_currently_writing_.id_,
|
|
V8SnapshotProfileWriter::Reference::Property("<instructions>"),
|
|
{offset_space, offset});
|
|
}
|
|
|
|
if (Code::IsDiscarded(code)) {
|
|
return;
|
|
}
|
|
|
|
if (FLAG_precompiled_mode) {
|
|
const uint32_t payload_info =
|
|
(unchecked_offset << 1) | (Code::HasMonomorphicEntry(code) ? 0x1 : 0x0);
|
|
WriteUnsigned(payload_info);
|
|
return;
|
|
}
|
|
#endif
|
|
Write<uint32_t>(offset);
|
|
WriteUnsigned(unchecked_offset);
|
|
}
|
|
|
|
void Serializer::TraceDataOffset(uint32_t offset) {
|
|
if (profile_writer_ == nullptr) return;
|
|
// ROData cannot be roots.
|
|
ASSERT(object_currently_writing_.id_ !=
|
|
V8SnapshotProfileWriter::kArtificialRootId);
|
|
auto offset_space = IdSpace::kIsolateData;
|
|
// TODO(sjindel): Give this edge a more appropriate type than element
|
|
// (internal, maybe?).
|
|
profile_writer_->AttributeReferenceTo(
|
|
object_currently_writing_.id_,
|
|
V8SnapshotProfileWriter::Reference::Element(0), {offset_space, offset});
|
|
}
|
|
|
|
uint32_t Serializer::GetDataOffset(ObjectPtr object) const {
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
return image_writer_->GetDataOffsetFor(object, ParentOf(object));
|
|
#else
|
|
return image_writer_->GetDataOffsetFor(object);
|
|
#endif
|
|
}
|
|
|
|
intptr_t Serializer::GetDataSize() const {
|
|
if (image_writer_ == nullptr) {
|
|
return 0;
|
|
}
|
|
return image_writer_->data_size();
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
void Serializer::Push(ObjectPtr object, intptr_t cid_override) {
|
|
const bool is_code = object->IsHeapObject() && object->IsCode();
|
|
if (is_code && !Snapshot::IncludesCode(kind_)) {
|
|
return; // Do not trace, will write null.
|
|
}
|
|
|
|
intptr_t id = heap_->GetObjectId(object);
|
|
if (id == kUnreachableReference) {
|
|
// When discovering the transitive closure of objects reachable from the
|
|
// roots we do not trace references, e.g. inside [RawCode], to
|
|
// [RawInstructions], since [RawInstructions] doesn't contain any references
|
|
// and the serialization code uses an [ImageWriter] for those.
|
|
if (object->IsHeapObject() && object->IsInstructions()) {
|
|
UnexpectedObject(object,
|
|
"Instructions should only be reachable from Code");
|
|
}
|
|
|
|
heap_->SetObjectId(object, kUnallocatedReference);
|
|
ASSERT(IsReachableReference(heap_->GetObjectId(object)));
|
|
stack_.Add({object, cid_override});
|
|
if (!(is_code && Code::IsDiscarded(Code::RawCast(object)))) {
|
|
num_written_objects_++;
|
|
}
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
heap()->SetSnapshotParent(object, &Object::Handle(zone_, current_parent_));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Serializer::PushWeak(ObjectPtr object) {
|
|
// The GC considers immediate objects to always be alive. This doesn't happen
|
|
// automatically in the serializer because the serializer does not have
|
|
// immediate objects: it handles Smis as ref indices like all other objects.
|
|
// This visit causes the serializer to reproduce the GC's semantics for
|
|
// weakness, which in particular allows the templates in hash_table.h to work
|
|
// with weak arrays because the metadata Smis always survive.
|
|
if (!object->IsHeapObject()) {
|
|
Push(object);
|
|
}
|
|
}
|
|
|
|
void Serializer::Trace(ObjectPtr object, intptr_t cid_override) {
|
|
intptr_t cid;
|
|
bool is_canonical;
|
|
bool is_deeply_immutable;
|
|
if (!object->IsHeapObject()) {
|
|
// Smis are merged into the Mint cluster because Smis for the writer might
|
|
// become Mints for the reader and vice versa.
|
|
cid = kMintCid;
|
|
is_canonical = true;
|
|
is_deeply_immutable = true;
|
|
} else {
|
|
cid = object->GetClassIdOfHeapObject();
|
|
is_canonical = object->untag()->IsCanonical();
|
|
is_deeply_immutable = object->untag()->IsDeeplyImmutable();
|
|
}
|
|
// if is_canonical then has to be is_deeply_immutable
|
|
ASSERT(is_deeply_immutable || !is_canonical);
|
|
if (cid_override != kIllegalCid) {
|
|
cid = cid_override;
|
|
} else if (IsStringClassId(cid)) {
|
|
cid = kStringCid;
|
|
}
|
|
|
|
SerializationCluster** cluster_ref =
|
|
is_canonical ? &canonical_clusters_by_cid_[cid]
|
|
: (is_deeply_immutable ? &immutable_clusters_by_cid_[cid]
|
|
: &clusters_by_cid_[cid]);
|
|
if (*cluster_ref == nullptr) {
|
|
*cluster_ref = NewClusterForClass(cid, is_canonical, is_deeply_immutable);
|
|
if (*cluster_ref == nullptr) {
|
|
UnexpectedObject(object, "No serialization cluster defined");
|
|
}
|
|
}
|
|
SerializationCluster* cluster = *cluster_ref;
|
|
ASSERT(cluster != nullptr);
|
|
if (cluster->is_canonical() != is_canonical) {
|
|
FATAL("cluster for %s (cid %" Pd ") %s as canonical, but %s",
|
|
cluster->name(), cid,
|
|
cluster->is_canonical() ? "marked" : "not marked",
|
|
is_canonical ? "should be" : "should not be");
|
|
}
|
|
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
current_parent_ = object;
|
|
#endif
|
|
|
|
cluster->Trace(this, object);
|
|
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
current_parent_ = Object::null();
|
|
#endif
|
|
}
|
|
|
|
void Serializer::UnexpectedObject(ObjectPtr raw_object, const char* message) {
|
|
// Exit the no safepoint scope so we can allocate while printing.
|
|
while (thread()->no_safepoint_scope_depth() > 0) {
|
|
thread()->DecrementNoSafepointScopeDepth();
|
|
}
|
|
Object& object = Object::Handle(raw_object);
|
|
OS::PrintErr("Unexpected object (%s, %s): 0x%" Px " %s\n", message,
|
|
Snapshot::KindToCString(kind_), static_cast<uword>(object.ptr()),
|
|
object.ToCString());
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
while (!object.IsNull()) {
|
|
object = ParentOf(object);
|
|
OS::PrintErr("referenced by 0x%" Px " %s\n",
|
|
static_cast<uword>(object.ptr()), object.ToCString());
|
|
}
|
|
#endif
|
|
OS::Abort();
|
|
}
|
|
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
ObjectPtr Serializer::ParentOf(ObjectPtr object) const {
|
|
Object* parent = heap()->GetSnapshotParent(object);
|
|
if (parent != nullptr) {
|
|
return parent->ptr();
|
|
}
|
|
return Object::null();
|
|
}
|
|
|
|
ObjectPtr Serializer::ParentOf(const Object& object) const {
|
|
Object* parent = heap()->GetSnapshotParent(object.ptr());
|
|
if (parent != nullptr) {
|
|
return parent->ptr();
|
|
}
|
|
return Object::null();
|
|
}
|
|
#endif // SNAPSHOT_BACKTRACE
|
|
|
|
void Serializer::WriteVersionAndFeatures() {
|
|
const char* expected_version = Version::SnapshotString();
|
|
ASSERT(expected_version != nullptr);
|
|
const intptr_t version_len = strlen(expected_version);
|
|
WriteBytes(reinterpret_cast<const uint8_t*>(expected_version), version_len);
|
|
|
|
char* expected_features = Dart::FeaturesString(isolate_group(), kind_);
|
|
ASSERT(expected_features != nullptr);
|
|
const intptr_t features_len = strlen(expected_features);
|
|
WriteBytes(reinterpret_cast<const uint8_t*>(expected_features),
|
|
features_len + 1);
|
|
free(expected_features);
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
static int CompareClusters(SerializationCluster* const* a,
|
|
SerializationCluster* const* b) {
|
|
if ((*a)->size() > (*b)->size()) {
|
|
return -1;
|
|
} else if ((*a)->size() < (*b)->size()) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define CID_CLUSTER(Type) \
|
|
reinterpret_cast<Type##SerializationCluster*>(clusters_by_cid_[k##Type##Cid])
|
|
|
|
const CompressedStackMaps& SerializationRoots::canonicalized_stack_map_entries()
|
|
const {
|
|
return CompressedStackMaps::Handle();
|
|
}
|
|
|
|
void Serializer::CreateEarlyClusters() {
|
|
while (stack_.length() > 0) {
|
|
StackEntry entry = stack_.RemoveLast();
|
|
Trace(entry.obj, entry.cid_override);
|
|
}
|
|
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = canonical_clusters_by_cid_[cid]) {
|
|
clusters_.Add(cluster);
|
|
canonical_clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = immutable_clusters_by_cid_[cid]) {
|
|
clusters_.Add(cluster);
|
|
immutable_clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = clusters_by_cid_[cid]) {
|
|
clusters_.Add(clusters_by_cid_[cid]);
|
|
clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
ZoneGrowableArray<Object*>* Serializer::Serialize(SerializationRoots* roots) {
|
|
// While object_currently_writing_ is initialized to the artificial root, we
|
|
// set up a scope to ensure proper flushing to the profile.
|
|
Serializer::WritingObjectScope scope(
|
|
this, V8SnapshotProfileWriter::kArtificialRootId);
|
|
roots->AddBaseObjects(this);
|
|
|
|
NoSafepointScope no_safepoint;
|
|
|
|
roots->PushRoots(this);
|
|
|
|
// Resolving WeakSerializationReferences and WeakProperties may cause new
|
|
// objects to be pushed on the stack, and handling the changes to the stack
|
|
// may cause the targets of WeakSerializationReferences and keys of
|
|
// WeakProperties to become reachable, so we do this as a fixed point
|
|
// computation. Note that reachability is computed monotonically (an object
|
|
// can change from not reachable to reachable, but never the reverse), which
|
|
// is technically a conservative approximation for WSRs, but doing a strict
|
|
// analysis that allows non-monotonic reachability may not halt.
|
|
//
|
|
// To see this, take a WSR whose replacement causes the target of another WSR
|
|
// to become reachable, which then causes the target of the first WSR to
|
|
// become reachable, but the only way to reach the target is through the
|
|
// target of the second WSR, which was only reachable via the replacement
|
|
// the first.
|
|
//
|
|
// In practice, this case doesn't come up as replacements tend to be either
|
|
// null, smis, or singleton objects that do not contain WSRs currently.
|
|
while (stack_.length() > 0) {
|
|
// Strong references.
|
|
while (stack_.length() > 0) {
|
|
StackEntry entry = stack_.RemoveLast();
|
|
Trace(entry.obj, entry.cid_override);
|
|
}
|
|
|
|
// Ephemeron references.
|
|
#if defined(DART_PRECOMPILER)
|
|
if (auto const cluster = CID_CLUSTER(WeakSerializationReference)) {
|
|
cluster->RetraceEphemerons(this);
|
|
}
|
|
#endif
|
|
if (auto const cluster = CID_CLUSTER(WeakProperty)) {
|
|
cluster->RetraceEphemerons(this);
|
|
}
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
auto const wsr_cluster = CID_CLUSTER(WeakSerializationReference);
|
|
if (wsr_cluster != nullptr) {
|
|
// Now that we have computed the reachability fixpoint, we remove the
|
|
// count of now-reachable WSRs as they are not actually serialized.
|
|
num_written_objects_ -= wsr_cluster->Count(this);
|
|
// We don't need to write this cluster, so remove it from consideration.
|
|
clusters_by_cid_[kWeakSerializationReferenceCid] = nullptr;
|
|
}
|
|
ASSERT(clusters_by_cid_[kWeakSerializationReferenceCid] == nullptr);
|
|
#endif
|
|
|
|
code_cluster_ = CID_CLUSTER(Code);
|
|
|
|
// The order that PostLoad runs matters for some classes because of
|
|
// assumptions during canonicalization, read filling, or post-load filling of
|
|
// some classes about what has already been read and/or canonicalized.
|
|
// Explicitly add these clusters first, then add the rest ordered by class id.
|
|
#define ADD_CANONICAL_NEXT(cid) \
|
|
if (auto const cluster = canonical_clusters_by_cid_[cid]) { \
|
|
clusters_.Add(cluster); \
|
|
canonical_clusters_by_cid_[cid] = nullptr; \
|
|
}
|
|
#define ADD_NON_CANONICAL_NEXT(cid) \
|
|
if (auto const cluster = immutable_clusters_by_cid_[cid]) { \
|
|
clusters_.Add(cluster); \
|
|
immutable_clusters_by_cid_[cid] = nullptr; \
|
|
} \
|
|
if (auto const cluster = clusters_by_cid_[cid]) { \
|
|
clusters_.Add(cluster); \
|
|
clusters_by_cid_[cid] = nullptr; \
|
|
}
|
|
ADD_CANONICAL_NEXT(kOneByteStringCid)
|
|
ADD_CANONICAL_NEXT(kTwoByteStringCid)
|
|
ADD_CANONICAL_NEXT(kStringCid)
|
|
ADD_CANONICAL_NEXT(kMintCid)
|
|
ADD_CANONICAL_NEXT(kDoubleCid)
|
|
ADD_CANONICAL_NEXT(kTypeParameterCid)
|
|
ADD_CANONICAL_NEXT(kTypeCid)
|
|
ADD_CANONICAL_NEXT(kTypeArgumentsCid)
|
|
// Code cluster should be deserialized before Function as
|
|
// FunctionDeserializationCluster::ReadFill uses instructions table
|
|
// which is filled in CodeDeserializationCluster::ReadFill.
|
|
// Code cluster should also precede ObjectPool as its ReadFill uses
|
|
// entry points of stubs.
|
|
ADD_NON_CANONICAL_NEXT(kCodeCid)
|
|
// The function cluster should be deserialized before any closures, as
|
|
// PostLoad for closures caches the entry point found in the function.
|
|
ADD_NON_CANONICAL_NEXT(kFunctionCid)
|
|
ADD_CANONICAL_NEXT(kClosureCid)
|
|
#undef ADD_CANONICAL_NEXT
|
|
#undef ADD_NON_CANONICAL_NEXT
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = canonical_clusters_by_cid_[cid]) {
|
|
clusters_.Add(cluster);
|
|
canonical_clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = immutable_clusters_by_cid_[cid]) {
|
|
clusters_.Add(cluster);
|
|
immutable_clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
for (intptr_t cid = 0; cid < num_cids_; cid++) {
|
|
if (auto const cluster = clusters_by_cid_[cid]) {
|
|
clusters_.Add(clusters_by_cid_[cid]);
|
|
clusters_by_cid_[cid] = nullptr;
|
|
}
|
|
}
|
|
|
|
PrepareInstructions(roots->canonicalized_stack_map_entries());
|
|
|
|
intptr_t num_objects = num_base_objects_ + num_written_objects_;
|
|
#if defined(ARCH_IS_64_BIT)
|
|
if (!Utils::IsInt(32, num_objects)) {
|
|
FATAL("Ref overflow");
|
|
}
|
|
#endif
|
|
|
|
WriteUnsigned(num_base_objects_);
|
|
WriteUnsigned(num_objects);
|
|
WriteUnsigned(clusters_.length());
|
|
ASSERT((instructions_table_len_ == 0) || FLAG_precompiled_mode);
|
|
WriteUnsigned(instructions_table_len_);
|
|
WriteUnsigned(instructions_table_rodata_offset_);
|
|
|
|
for (SerializationCluster* cluster : clusters_) {
|
|
cluster->WriteAndMeasureAlloc(this);
|
|
bytes_heap_allocated_ += cluster->target_memory_size();
|
|
#if defined(DEBUG)
|
|
Write<int32_t>(next_ref_index_);
|
|
#endif
|
|
}
|
|
|
|
// We should have assigned a ref to every object we pushed.
|
|
ASSERT((next_ref_index_ - 1) == num_objects);
|
|
// And recorded them all in [objects_].
|
|
ASSERT(objects_->length() == num_objects);
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
if (profile_writer_ != nullptr && wsr_cluster != nullptr) {
|
|
// Post-WriteAlloc, we eagerly create artificial nodes for any unreachable
|
|
// targets in reachable WSRs if writing a v8 snapshot profile, since they
|
|
// will be used in AttributeReference().
|
|
//
|
|
// Unreachable WSRs may also need artificial nodes, as they may be members
|
|
// of other unreachable objects that have artificial nodes in the profile,
|
|
// but they are instead lazily handled in CreateArtificialNodeIfNeeded().
|
|
wsr_cluster->CreateArtificialTargetNodesIfNeeded(this);
|
|
}
|
|
#endif
|
|
|
|
for (SerializationCluster* cluster : clusters_) {
|
|
cluster->WriteAndMeasureFill(this);
|
|
#if defined(DEBUG)
|
|
Write<int32_t>(kSectionMarker);
|
|
#endif
|
|
}
|
|
|
|
roots->WriteRoots(this);
|
|
|
|
#if defined(DEBUG)
|
|
Write<int32_t>(kSectionMarker);
|
|
#endif
|
|
|
|
PrintSnapshotSizes();
|
|
|
|
heap()->ResetObjectIdTable();
|
|
#if defined(SNAPSHOT_BACKTRACE)
|
|
heap()->ResetSnapshotParentTable();
|
|
#endif
|
|
|
|
return objects_;
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
|
|
// The serialized format of the dispatch table is a sequence of variable-length
|
|
// integers (the built-in variable-length integer encoding/decoding of
|
|
// the stream). Each encoded integer e is interpreted thus:
|
|
// -kRecentCount .. -1 Pick value from the recent values buffer at index -1-e.
|
|
// 0 Empty (unused) entry.
|
|
// 1 .. kMaxRepeat Repeat previous entry e times.
|
|
// kIndexBase or higher Pick entry point from the object at index e-kIndexBase
|
|
// in the snapshot code cluster. Also put it in the recent
|
|
// values buffer at the next round-robin index.
|
|
|
|
// Constants for serialization format. Chosen such that repeats and recent
|
|
// values are encoded as single bytes in SLEB128 encoding.
|
|
static constexpr intptr_t kDispatchTableSpecialEncodingBits = 6;
|
|
static constexpr intptr_t kDispatchTableRecentCount =
|
|
1 << kDispatchTableSpecialEncodingBits;
|
|
static constexpr intptr_t kDispatchTableRecentMask =
|
|
(1 << kDispatchTableSpecialEncodingBits) - 1;
|
|
static constexpr intptr_t kDispatchTableMaxRepeat =
|
|
(1 << kDispatchTableSpecialEncodingBits) - 1;
|
|
static constexpr intptr_t kDispatchTableIndexBase = kDispatchTableMaxRepeat + 1;
|
|
#endif // defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
void Serializer::WriteDispatchTable(const Array& entries) {
|
|
#if defined(DART_PRECOMPILER)
|
|
if (kind() != Snapshot::kFullAOT) return;
|
|
|
|
// Create an artificial node to which the bytes should be attributed. We
|
|
// don't attribute them to entries.ptr(), as we don't want to attribute the
|
|
// bytes for printing out a length of 0 to Object::null() when the dispatch
|
|
// table is empty.
|
|
const intptr_t profile_ref = AssignArtificialRef();
|
|
const auto& dispatch_table_profile_id = GetProfileId(profile_ref);
|
|
if (profile_writer_ != nullptr) {
|
|
profile_writer_->SetObjectTypeAndName(dispatch_table_profile_id,
|
|
"DispatchTable", "dispatch_table");
|
|
profile_writer_->AddRoot(dispatch_table_profile_id);
|
|
}
|
|
WritingObjectScope scope(this, dispatch_table_profile_id);
|
|
if (profile_writer_ != nullptr) {
|
|
// We'll write the Array object as a property of the artificial dispatch
|
|
// table node, so Code objects otherwise unreferenced will have it as an
|
|
// ancestor.
|
|
CreateArtificialNodeIfNeeded(entries.ptr());
|
|
AttributePropertyRef(entries.ptr(), "<code entries>");
|
|
}
|
|
|
|
const intptr_t bytes_before = bytes_written();
|
|
const intptr_t table_length = entries.IsNull() ? 0 : entries.Length();
|
|
|
|
ASSERT(table_length <= compiler::target::kWordMax);
|
|
WriteUnsigned(table_length);
|
|
if (table_length == 0) {
|
|
dispatch_table_size_ = bytes_written() - bytes_before;
|
|
return;
|
|
}
|
|
|
|
ASSERT(code_cluster_ != nullptr);
|
|
// If instructions can be deduped, the code order table in the deserializer
|
|
// may not contain all Code objects in the snapshot. Thus, we write the ID
|
|
// for the first code object here so we can retrieve it during deserialization
|
|
// and calculate the snapshot ID for Code objects from the cluster index.
|
|
//
|
|
// We could just use the snapshot reference ID of the Code object itself
|
|
// instead of the cluster index and avoid this. However, since entries are
|
|
// SLEB128 encoded, the size delta for serializing the first ID once is less
|
|
// than the size delta of serializing the ID plus kIndexBase for each entry,
|
|
// even when Code objects are allocated before all other non-base objects.
|
|
//
|
|
// We could also map Code objects to the first Code object in the cluster with
|
|
// the same entry point and serialize that ID instead, but that loses
|
|
// information about which Code object was originally referenced.
|
|
WriteUnsigned(code_cluster_->first_ref());
|
|
|
|
CodePtr previous_code = nullptr;
|
|
CodePtr recent[kDispatchTableRecentCount] = {nullptr};
|
|
intptr_t recent_index = 0;
|
|
intptr_t repeat_count = 0;
|
|
for (intptr_t i = 0; i < table_length; i++) {
|
|
auto const code = Code::RawCast(entries.At(i));
|
|
// First, see if we're repeating the previous entry (invalid, recent, or
|
|
// encoded).
|
|
if (code == previous_code) {
|
|
if (++repeat_count == kDispatchTableMaxRepeat) {
|
|
Write(kDispatchTableMaxRepeat);
|
|
repeat_count = 0;
|
|
}
|
|
continue;
|
|
}
|
|
// Emit any outstanding repeat count before handling the new code value.
|
|
if (repeat_count > 0) {
|
|
Write(repeat_count);
|
|
repeat_count = 0;
|
|
}
|
|
previous_code = code;
|
|
// The invalid entry can be repeated, but is never part of the recent list
|
|
// since it already encodes to a single byte..
|
|
if (code == Code::null()) {
|
|
Write(0);
|
|
continue;
|
|
}
|
|
// Check against the recent entries, and write an encoded reference to
|
|
// the recent entry if found.
|
|
intptr_t found_index = 0;
|
|
for (; found_index < kDispatchTableRecentCount; found_index++) {
|
|
if (recent[found_index] == code) break;
|
|
}
|
|
if (found_index < kDispatchTableRecentCount) {
|
|
Write(~found_index);
|
|
continue;
|
|
}
|
|
// We have a non-repeated, non-recent entry, so encode the reference ID of
|
|
// the code object and emit that.
|
|
auto const code_index = GetCodeIndex(code);
|
|
// Use the index in the code cluster, not in the snapshot..
|
|
auto const encoded = kDispatchTableIndexBase + code_index;
|
|
ASSERT(encoded <= compiler::target::kWordMax);
|
|
Write(encoded);
|
|
recent[recent_index] = code;
|
|
recent_index = (recent_index + 1) & kDispatchTableRecentMask;
|
|
}
|
|
if (repeat_count > 0) {
|
|
Write(repeat_count);
|
|
}
|
|
dispatch_table_size_ = bytes_written() - bytes_before;
|
|
#endif // defined(DART_PRECOMPILER)
|
|
}
|
|
|
|
void Serializer::PrintSnapshotSizes() {
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (FLAG_print_snapshot_sizes_verbose) {
|
|
TextBuffer buffer(1024);
|
|
// Header, using format sizes matching those below to ensure alignment.
|
|
buffer.Printf("%25s", "Cluster");
|
|
buffer.Printf(" %6s", "Objs");
|
|
buffer.Printf(" %8s", "Size");
|
|
buffer.Printf(" %8s", "Fraction");
|
|
buffer.Printf(" %10s", "Cumulative");
|
|
buffer.Printf(" %8s", "HeapSize");
|
|
buffer.Printf(" %5s", "Cid");
|
|
buffer.Printf(" %9s", "Canonical");
|
|
buffer.AddString("\n");
|
|
GrowableArray<SerializationCluster*> clusters_by_size;
|
|
for (intptr_t cid = 1; cid < num_cids_; cid++) {
|
|
if (auto const cluster = canonical_clusters_by_cid_[cid]) {
|
|
clusters_by_size.Add(cluster);
|
|
}
|
|
if (auto const cluster = immutable_clusters_by_cid_[cid]) {
|
|
clusters_by_size.Add(cluster);
|
|
}
|
|
if (auto const cluster = clusters_by_cid_[cid]) {
|
|
clusters_by_size.Add(cluster);
|
|
}
|
|
}
|
|
intptr_t text_size = 0;
|
|
if (image_writer_ != nullptr) {
|
|
auto const text_object_count = image_writer_->GetTextObjectCount();
|
|
text_size = image_writer_->text_size();
|
|
intptr_t trampoline_count, trampoline_size;
|
|
image_writer_->GetTrampolineInfo(&trampoline_count, &trampoline_size);
|
|
auto const instructions_count = text_object_count - trampoline_count;
|
|
auto const instructions_size = text_size - trampoline_size;
|
|
clusters_by_size.Add(new (zone_) FakeSerializationCluster(
|
|
ImageWriter::TagObjectTypeAsReadOnly(zone_, "Instructions"),
|
|
instructions_count, instructions_size));
|
|
if (trampoline_size > 0) {
|
|
clusters_by_size.Add(new (zone_) FakeSerializationCluster(
|
|
ImageWriter::TagObjectTypeAsReadOnly(zone_, "Trampoline"),
|
|
trampoline_count, trampoline_size));
|
|
}
|
|
}
|
|
// The dispatch_table_size_ will be 0 if the snapshot did not include a
|
|
// dispatch table (i.e., the VM snapshot). For a precompiled isolate
|
|
// snapshot, we always serialize at least _one_ byte for the DispatchTable.
|
|
if (dispatch_table_size_ > 0) {
|
|
const auto& dispatch_table_entries = Array::Handle(
|
|
zone_,
|
|
isolate_group()->object_store()->dispatch_table_code_entries());
|
|
auto const entry_count =
|
|
dispatch_table_entries.IsNull() ? 0 : dispatch_table_entries.Length();
|
|
clusters_by_size.Add(new (zone_) FakeSerializationCluster(
|
|
"DispatchTable", entry_count, dispatch_table_size_));
|
|
}
|
|
if (instructions_table_len_ > 0) {
|
|
const intptr_t memory_size =
|
|
compiler::target::InstructionsTable::InstanceSize() +
|
|
compiler::target::Array::InstanceSize(instructions_table_len_);
|
|
clusters_by_size.Add(new (zone_) FakeSerializationCluster(
|
|
"InstructionsTable", instructions_table_len_, 0, memory_size));
|
|
}
|
|
clusters_by_size.Sort(CompareClusters);
|
|
double total_size =
|
|
static_cast<double>(bytes_written() + GetDataSize() + text_size);
|
|
double cumulative_fraction = 0.0;
|
|
for (intptr_t i = 0; i < clusters_by_size.length(); i++) {
|
|
SerializationCluster* cluster = clusters_by_size[i];
|
|
double fraction = static_cast<double>(cluster->size()) / total_size;
|
|
cumulative_fraction += fraction;
|
|
buffer.Printf("%25s", cluster->name());
|
|
buffer.Printf(" %6" Pd "", cluster->num_objects());
|
|
buffer.Printf(" %8" Pd "", cluster->size());
|
|
buffer.Printf(" %1.6lf", fraction);
|
|
buffer.Printf(" %1.8lf", cumulative_fraction);
|
|
buffer.Printf(" %8" Pd "", cluster->target_memory_size());
|
|
if (cluster->cid() != -1) {
|
|
buffer.Printf(" %5" Pd "", cluster->cid());
|
|
} else {
|
|
buffer.Printf(" %5s", "");
|
|
}
|
|
if (cluster->is_canonical()) {
|
|
buffer.Printf(" %9s", "canonical");
|
|
} else {
|
|
buffer.Printf(" %9s", "");
|
|
}
|
|
buffer.AddString("\n");
|
|
}
|
|
OS::PrintErr("%s", buffer.buffer());
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
Deserializer::Deserializer(Thread* thread,
|
|
Snapshot::Kind kind,
|
|
const uint8_t* buffer,
|
|
intptr_t size,
|
|
const uint8_t* data_buffer,
|
|
const uint8_t* instructions_buffer,
|
|
bool is_non_root_unit,
|
|
intptr_t offset)
|
|
: ThreadStackResource(thread),
|
|
heap_(thread->isolate_group()->heap()),
|
|
old_space_(heap_->old_space()),
|
|
freelist_(old_space_->DataFreeList()),
|
|
zone_(thread->zone()),
|
|
kind_(kind),
|
|
stream_(buffer, size),
|
|
image_reader_(nullptr),
|
|
refs_(nullptr),
|
|
next_ref_index_(kFirstReference),
|
|
clusters_(nullptr),
|
|
is_non_root_unit_(is_non_root_unit),
|
|
instructions_table_(InstructionsTable::Handle(thread->zone())) {
|
|
if (Snapshot::IncludesCode(kind)) {
|
|
ASSERT(instructions_buffer != nullptr);
|
|
ASSERT(data_buffer != nullptr);
|
|
image_reader_ = new (zone_) ImageReader(data_buffer, instructions_buffer);
|
|
}
|
|
stream_.SetPosition(offset);
|
|
}
|
|
|
|
Deserializer::~Deserializer() {
|
|
delete[] clusters_;
|
|
}
|
|
|
|
DeserializationCluster* Deserializer::ReadCluster() {
|
|
const uint32_t tags = Read<uint32_t>();
|
|
const intptr_t cid = UntaggedObject::ClassIdTag::decode(tags);
|
|
const bool is_canonical = UntaggedObject::CanonicalBit::decode(tags);
|
|
const bool is_deeply_immutable =
|
|
UntaggedObject::DeeplyImmutableBit::decode(tags);
|
|
Zone* Z = zone_;
|
|
if (cid >= kNumPredefinedCids || cid == kInstanceCid) {
|
|
return new (Z) InstanceDeserializationCluster(
|
|
cid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
}
|
|
if (IsTypedDataViewClassId(cid)) {
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) TypedDataViewDeserializationCluster(cid);
|
|
}
|
|
if (IsExternalTypedDataClassId(cid)) {
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ExternalTypedDataDeserializationCluster(cid);
|
|
}
|
|
if (IsTypedDataClassId(cid)) {
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) TypedDataDeserializationCluster(cid);
|
|
}
|
|
|
|
#if !defined(DART_COMPRESSED_POINTERS)
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
switch (cid) {
|
|
case kPcDescriptorsCid:
|
|
case kCodeSourceMapCid:
|
|
case kCompressedStackMapsCid:
|
|
return new (Z) RODataDeserializationCluster(
|
|
cid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kOneByteStringCid:
|
|
case kTwoByteStringCid:
|
|
case kStringCid:
|
|
if (!is_non_root_unit_) {
|
|
return new (Z) RODataDeserializationCluster(
|
|
cid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
switch (cid) {
|
|
case kClassCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ClassDeserializationCluster();
|
|
case kTypeParametersCid:
|
|
return new (Z) TypeParametersDeserializationCluster(is_canonical,
|
|
is_deeply_immutable);
|
|
case kTypeArgumentsCid:
|
|
return new (Z) TypeArgumentsDeserializationCluster(
|
|
is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kPatchClassCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) PatchClassDeserializationCluster();
|
|
case kFunctionCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) FunctionDeserializationCluster();
|
|
case kClosureDataCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ClosureDataDeserializationCluster();
|
|
case kFfiTrampolineDataCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) FfiTrampolineDataDeserializationCluster();
|
|
case kFieldCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) FieldDeserializationCluster();
|
|
case kScriptCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ScriptDeserializationCluster();
|
|
case kLibraryCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) LibraryDeserializationCluster();
|
|
case kNamespaceCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) NamespaceDeserializationCluster();
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
case kKernelProgramInfoCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) KernelProgramInfoDeserializationCluster();
|
|
#endif // !DART_PRECOMPILED_RUNTIME
|
|
case kCodeCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) CodeDeserializationCluster();
|
|
#if defined(DART_BYTECODE_INTERPRETER)
|
|
case kBytecodeCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) BytecodeDeserializationCluster();
|
|
#endif
|
|
case kObjectPoolCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ObjectPoolDeserializationCluster();
|
|
case kPcDescriptorsCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) PcDescriptorsDeserializationCluster();
|
|
case kCodeSourceMapCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) CodeSourceMapDeserializationCluster();
|
|
case kCompressedStackMapsCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) CompressedStackMapsDeserializationCluster();
|
|
case kLocalVarDescriptorsCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) LocalVarDescriptorsDeserializationCluster();
|
|
case kExceptionHandlersCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ExceptionHandlersDeserializationCluster();
|
|
case kContextCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ContextDeserializationCluster();
|
|
case kContextScopeCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ContextScopeDeserializationCluster();
|
|
case kUnlinkedCallCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) UnlinkedCallDeserializationCluster();
|
|
case kICDataCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ICDataDeserializationCluster();
|
|
case kMegamorphicCacheCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) MegamorphicCacheDeserializationCluster();
|
|
case kSubtypeTestCacheCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) SubtypeTestCacheDeserializationCluster();
|
|
case kLoadingUnitCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) LoadingUnitDeserializationCluster();
|
|
case kApiErrorCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) ApiErrorDeserializationCluster();
|
|
case kLanguageErrorCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) LanguageErrorDeserializationCluster();
|
|
case kUnhandledExceptionCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) UnhandledExceptionDeserializationCluster();
|
|
case kUnwindErrorCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) UnwindErrorDeserializationCluster();
|
|
case kLibraryPrefixCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) LibraryPrefixDeserializationCluster();
|
|
case kTypeCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
TypeDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kFunctionTypeCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
FunctionTypeDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kRecordTypeCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
RecordTypeDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kTypeParameterCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
TypeParameterDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kClosureCid:
|
|
return new (Z) ClosureDeserializationCluster(
|
|
is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kMintCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
MintDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kDoubleCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
DoubleDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
case kInt32x4Cid:
|
|
case kFloat32x4Cid:
|
|
case kFloat64x2Cid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
Simd128DeserializationCluster(cid, is_canonical, !is_non_root_unit_);
|
|
case kGrowableObjectArrayCid:
|
|
ASSERT(!is_canonical);
|
|
return new (Z) GrowableObjectArrayDeserializationCluster(
|
|
is_canonical, is_deeply_immutable);
|
|
case kRecordCid:
|
|
return new (Z) RecordDeserializationCluster(
|
|
is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kStackTraceCid:
|
|
ASSERT(!is_canonical);
|
|
return new (Z) StackTraceDeserializationCluster(is_deeply_immutable);
|
|
case kRegExpCid:
|
|
ASSERT(!is_canonical);
|
|
return new (Z) RegExpDeserializationCluster(is_deeply_immutable);
|
|
case kWeakPropertyCid:
|
|
ASSERT(!is_canonical);
|
|
ASSERT(!is_deeply_immutable);
|
|
return new (Z) WeakPropertyDeserializationCluster();
|
|
case kMapCid:
|
|
// We do not have mutable hash maps in snapshots.
|
|
UNREACHABLE();
|
|
case kConstMapCid:
|
|
return new (Z) MapDeserializationCluster(
|
|
kConstMapCid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kSetCid:
|
|
// We do not have mutable hash sets in snapshots.
|
|
UNREACHABLE();
|
|
case kConstSetCid:
|
|
return new (Z) SetDeserializationCluster(
|
|
kConstSetCid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kArrayCid:
|
|
return new (Z) ArrayDeserializationCluster(
|
|
kArrayCid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kImmutableArrayCid:
|
|
return new (Z)
|
|
ArrayDeserializationCluster(kImmutableArrayCid, is_canonical,
|
|
is_deeply_immutable, !is_non_root_unit_);
|
|
case kWeakArrayCid:
|
|
return new (Z) WeakArrayDeserializationCluster();
|
|
case kStringCid:
|
|
ASSERT(is_deeply_immutable);
|
|
return new (Z)
|
|
StringDeserializationCluster(is_canonical, !is_non_root_unit_);
|
|
#define CASE_FFI_CID(name) case kFfi##name##Cid:
|
|
CLASS_LIST_FFI_TYPE_MARKER(CASE_FFI_CID)
|
|
#undef CASE_FFI_CID
|
|
return new (Z) InstanceDeserializationCluster(
|
|
cid, is_canonical, is_deeply_immutable, !is_non_root_unit_);
|
|
case kDeltaEncodedTypedDataCid:
|
|
return new (Z) DeltaEncodedTypedDataDeserializationCluster();
|
|
default:
|
|
break;
|
|
}
|
|
FATAL("No cluster defined for cid %" Pd, cid);
|
|
return nullptr;
|
|
}
|
|
|
|
void Deserializer::ReadDispatchTable(
|
|
ReadStream* stream,
|
|
bool deferred,
|
|
const InstructionsTable& root_instruction_table,
|
|
intptr_t deferred_code_start_index,
|
|
intptr_t deferred_code_end_index) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
const uint8_t* table_snapshot_start = stream->AddressOfCurrentPosition();
|
|
const intptr_t length = stream->ReadUnsigned();
|
|
if (length == 0) return;
|
|
|
|
const intptr_t first_code_id = stream->ReadUnsigned();
|
|
deferred_code_start_index -= first_code_id;
|
|
deferred_code_end_index -= first_code_id;
|
|
|
|
auto const IG = isolate_group();
|
|
auto& code = StubCode::DispatchTableNullError();
|
|
ASSERT(!code.IsNull());
|
|
uword null_entry = Code::EntryPointOf(code.ptr());
|
|
|
|
DispatchTable* table;
|
|
if (deferred) {
|
|
table = IG->dispatch_table();
|
|
ASSERT(table != nullptr && table->length() == length);
|
|
} else {
|
|
ASSERT(IG->dispatch_table() == nullptr);
|
|
table = new DispatchTable(length);
|
|
}
|
|
auto const array = table->array();
|
|
uword value = 0;
|
|
uword recent[kDispatchTableRecentCount] = {0};
|
|
intptr_t recent_index = 0;
|
|
intptr_t repeat_count = 0;
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
if (repeat_count > 0) {
|
|
array[i] = value;
|
|
repeat_count--;
|
|
continue;
|
|
}
|
|
auto const encoded = stream->Read<intptr_t>();
|
|
if (encoded == 0) {
|
|
value = null_entry;
|
|
} else if (encoded < 0) {
|
|
intptr_t r = ~encoded;
|
|
ASSERT(r < kDispatchTableRecentCount);
|
|
value = recent[r];
|
|
} else if (encoded <= kDispatchTableMaxRepeat) {
|
|
repeat_count = encoded - 1;
|
|
} else {
|
|
const intptr_t code_index = encoded - kDispatchTableIndexBase;
|
|
if (deferred) {
|
|
const intptr_t code_id =
|
|
CodeIndexToClusterIndex(root_instruction_table, code_index);
|
|
if ((deferred_code_start_index <= code_id) &&
|
|
(code_id < deferred_code_end_index)) {
|
|
auto code = static_cast<CodePtr>(Ref(first_code_id + code_id));
|
|
value = Code::EntryPointOf(code);
|
|
} else {
|
|
// Reuse old value from the dispatch table.
|
|
value = array[i];
|
|
}
|
|
} else {
|
|
value = GetEntryPointByCodeIndex(code_index);
|
|
}
|
|
recent[recent_index] = value;
|
|
recent_index = (recent_index + 1) & kDispatchTableRecentMask;
|
|
}
|
|
array[i] = value;
|
|
}
|
|
ASSERT(repeat_count == 0);
|
|
|
|
if (!deferred) {
|
|
IG->set_dispatch_table(table);
|
|
intptr_t table_snapshot_size =
|
|
stream->AddressOfCurrentPosition() - table_snapshot_start;
|
|
IG->set_dispatch_table_snapshot(table_snapshot_start);
|
|
IG->set_dispatch_table_snapshot_size(table_snapshot_size);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
char* Deserializer::VerifyImageAlignment() {
|
|
if (image_reader_ != nullptr) {
|
|
return image_reader_->VerifyAlignment();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
char* SnapshotHeaderReader::VerifyVersionAndFeatures(
|
|
IsolateGroup* isolate_group,
|
|
intptr_t* offset) {
|
|
char* error = VerifyVersion();
|
|
if (error == nullptr) {
|
|
error = VerifyFeatures(isolate_group);
|
|
}
|
|
if (error == nullptr) {
|
|
*offset = stream_.Position();
|
|
}
|
|
return error;
|
|
}
|
|
|
|
char* SnapshotHeaderReader::VerifyVersion() {
|
|
// If the version string doesn't match, return an error.
|
|
// Note: New things are allocated only if we're going to return an error.
|
|
|
|
const char* expected_version = Version::SnapshotString();
|
|
ASSERT(expected_version != nullptr);
|
|
const intptr_t version_len = strlen(expected_version);
|
|
if (stream_.PendingBytes() < version_len) {
|
|
const intptr_t kMessageBufferSize = 128;
|
|
char message_buffer[kMessageBufferSize];
|
|
Utils::SNPrint(message_buffer, kMessageBufferSize,
|
|
"No full snapshot version found, expected '%s'",
|
|
expected_version);
|
|
return BuildError(message_buffer);
|
|
}
|
|
|
|
const char* version =
|
|
reinterpret_cast<const char*>(stream_.AddressOfCurrentPosition());
|
|
ASSERT(version != nullptr);
|
|
if (strncmp(version, expected_version, version_len) != 0) {
|
|
const intptr_t kMessageBufferSize = 256;
|
|
char message_buffer[kMessageBufferSize];
|
|
char* actual_version = Utils::StrNDup(version, version_len);
|
|
Utils::SNPrint(message_buffer, kMessageBufferSize,
|
|
"Wrong %s snapshot version, expected '%s' found '%s'",
|
|
(Snapshot::IsFull(kind_)) ? "full" : "script",
|
|
expected_version, actual_version);
|
|
free(actual_version);
|
|
return BuildError(message_buffer);
|
|
}
|
|
stream_.Advance(version_len);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
char* SnapshotHeaderReader::VerifyFeatures(IsolateGroup* isolate_group) {
|
|
const char* expected_features = Dart::FeaturesString(isolate_group, kind_);
|
|
ASSERT(expected_features != nullptr);
|
|
const intptr_t expected_len = strlen(expected_features);
|
|
|
|
const char* features = nullptr;
|
|
intptr_t features_length = 0;
|
|
|
|
auto error = ReadFeatures(&features, &features_length);
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
if (features_length != expected_len ||
|
|
(strncmp(features, expected_features, expected_len) != 0)) {
|
|
const intptr_t kMessageBufferSize = 1024;
|
|
char message_buffer[kMessageBufferSize];
|
|
char* actual_features = Utils::StrNDup(
|
|
features, features_length < 1024 ? features_length : 1024);
|
|
Utils::SNPrint(message_buffer, kMessageBufferSize,
|
|
"Snapshot not compatible with the current VM configuration: "
|
|
"the snapshot requires '%s' but the VM has '%s'",
|
|
actual_features, expected_features);
|
|
free(const_cast<char*>(expected_features));
|
|
free(actual_features);
|
|
return BuildError(message_buffer);
|
|
}
|
|
free(const_cast<char*>(expected_features));
|
|
return nullptr;
|
|
}
|
|
|
|
char* SnapshotHeaderReader::ReadFeatures(const char** features,
|
|
intptr_t* features_length) {
|
|
const char* cursor =
|
|
reinterpret_cast<const char*>(stream_.AddressOfCurrentPosition());
|
|
const intptr_t length = Utils::StrNLen(cursor, stream_.PendingBytes());
|
|
if (length == stream_.PendingBytes()) {
|
|
return BuildError(
|
|
"The features string in the snapshot was not '\\0'-terminated.");
|
|
}
|
|
*features = cursor;
|
|
*features_length = length;
|
|
stream_.Advance(length + 1);
|
|
return nullptr;
|
|
}
|
|
|
|
char* SnapshotHeaderReader::BuildError(const char* message) {
|
|
return Utils::StrDup(message);
|
|
}
|
|
|
|
void Deserializer::ReadInstructions(CodePtr code, bool deferred) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (deferred) {
|
|
// TODO(rmacnak): Fill in for better errors.
|
|
// uword entry_point = StubCode::NotLoaded().EntryPoint();
|
|
uword entry_point = 1;
|
|
// There are no serialized RawInstructions objects in this mode.
|
|
code->untag()->instructions_ = Instructions::null();
|
|
code->untag()->entry_point_ = entry_point;
|
|
code->untag()->unchecked_entry_point_ = entry_point;
|
|
code->untag()->monomorphic_entry_point_ = entry_point;
|
|
code->untag()->monomorphic_unchecked_entry_point_ = entry_point;
|
|
code->untag()->instructions_length_ = 0;
|
|
return;
|
|
}
|
|
|
|
const uword payload_start = instructions_table_.EntryPointAt(
|
|
instructions_table_.rodata()->first_entry_with_code +
|
|
instructions_index_);
|
|
const uint32_t payload_info = ReadUnsigned();
|
|
const uint32_t unchecked_offset = payload_info >> 1;
|
|
const bool has_monomorphic_entrypoint = (payload_info & 0x1) == 0x1;
|
|
|
|
const uword entry_offset =
|
|
has_monomorphic_entrypoint ? Instructions::kPolymorphicEntryOffsetAOT : 0;
|
|
const uword monomorphic_entry_offset =
|
|
has_monomorphic_entrypoint ? Instructions::kMonomorphicEntryOffsetAOT : 0;
|
|
|
|
const uword entry_point = payload_start + entry_offset;
|
|
const uword monomorphic_entry_point =
|
|
payload_start + monomorphic_entry_offset;
|
|
|
|
instructions_table_.SetCodeAt(instructions_index_++, code);
|
|
|
|
// There are no serialized RawInstructions objects in this mode.
|
|
code->untag()->instructions_ = Instructions::null();
|
|
code->untag()->entry_point_ = entry_point;
|
|
code->untag()->unchecked_entry_point_ = entry_point + unchecked_offset;
|
|
code->untag()->monomorphic_entry_point_ = monomorphic_entry_point;
|
|
code->untag()->monomorphic_unchecked_entry_point_ =
|
|
monomorphic_entry_point + unchecked_offset;
|
|
#else
|
|
ASSERT(!deferred);
|
|
InstructionsPtr instr = image_reader_->GetInstructionsAt(Read<uint32_t>());
|
|
uint32_t unchecked_offset = ReadUnsigned();
|
|
code->untag()->instructions_ = instr;
|
|
code->untag()->unchecked_offset_ = unchecked_offset;
|
|
ASSERT(kind() == Snapshot::kFullJIT);
|
|
const uint32_t active_offset = Read<uint32_t>();
|
|
instr = image_reader_->GetInstructionsAt(active_offset);
|
|
unchecked_offset = ReadUnsigned();
|
|
code->untag()->active_instructions_ = instr;
|
|
Code::InitializeCachedEntryPointsFrom(code, instr, unchecked_offset);
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
void Deserializer::EndInstructions() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (instructions_table_.IsNull()) {
|
|
ASSERT(instructions_index_ == 0);
|
|
return;
|
|
}
|
|
|
|
const auto& code_objects =
|
|
Array::Handle(instructions_table_.ptr()->untag()->code_objects());
|
|
ASSERT(code_objects.Length() == instructions_index_);
|
|
|
|
uword previous_end = image_reader_->GetBareInstructionsEnd();
|
|
for (intptr_t i = instructions_index_ - 1; i >= 0; --i) {
|
|
CodePtr code = Code::RawCast(code_objects.At(i));
|
|
uword start = Code::PayloadStartOf(code);
|
|
ASSERT(start <= previous_end);
|
|
code->untag()->instructions_length_ = previous_end - start;
|
|
previous_end = start;
|
|
}
|
|
|
|
ObjectStore* object_store = isolate_group()->object_store();
|
|
GrowableObjectArray& tables =
|
|
GrowableObjectArray::Handle(zone_, object_store->instructions_tables());
|
|
if (tables.IsNull()) {
|
|
tables = GrowableObjectArray::New(Heap::kOld);
|
|
object_store->set_instructions_tables(tables);
|
|
}
|
|
if ((tables.Length() == 0) ||
|
|
(tables.At(tables.Length() - 1) != instructions_table_.ptr())) {
|
|
ASSERT((!is_non_root_unit_ && tables.Length() == 0) ||
|
|
(is_non_root_unit_ && tables.Length() > 0));
|
|
tables.Add(instructions_table_, Heap::kOld);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ObjectPtr Deserializer::GetObjectAt(uint32_t offset) const {
|
|
return image_reader_->GetObjectAt(offset);
|
|
}
|
|
|
|
class HeapLocker : public StackResource {
|
|
public:
|
|
HeapLocker(Thread* thread, PageSpace* page_space)
|
|
: StackResource(thread),
|
|
page_space_(page_space),
|
|
freelist_(page_space->DataFreeList()) {
|
|
page_space_->AcquireLock(freelist_);
|
|
}
|
|
~HeapLocker() { page_space_->ReleaseLock(freelist_); }
|
|
|
|
private:
|
|
PageSpace* page_space_;
|
|
FreeList* freelist_;
|
|
};
|
|
|
|
void Deserializer::Deserialize(DeserializationRoots* roots) {
|
|
const void* clustered_start = AddressOfCurrentPosition();
|
|
|
|
Array& refs = Array::Handle(zone_);
|
|
num_base_objects_ = ReadUnsigned();
|
|
num_objects_ = ReadUnsigned();
|
|
num_clusters_ = ReadUnsigned();
|
|
const intptr_t instructions_table_len = ReadUnsigned();
|
|
const uint32_t instruction_table_data_offset = ReadUnsigned();
|
|
USE(instruction_table_data_offset);
|
|
|
|
clusters_ = new DeserializationCluster*[num_clusters_];
|
|
refs = Array::New(num_objects_ + kFirstReference, Heap::kOld);
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (instructions_table_len > 0) {
|
|
ASSERT(FLAG_precompiled_mode);
|
|
const uword start_pc = image_reader_->GetBareInstructionsAt(0);
|
|
const uword end_pc = image_reader_->GetBareInstructionsEnd();
|
|
uword instruction_table_data = 0;
|
|
if (instruction_table_data_offset != 0) {
|
|
// NoSafepointScope to satisfy assertion in DataStart. InstructionsTable
|
|
// data resides in RO memory and is immovable and immortal making it
|
|
// safe to use DataStart result outside of NoSafepointScope.
|
|
NoSafepointScope no_safepoint;
|
|
instruction_table_data = reinterpret_cast<uword>(
|
|
OneByteString::DataStart(String::Handle(static_cast<StringPtr>(
|
|
image_reader_->GetObjectAt(instruction_table_data_offset)))));
|
|
}
|
|
instructions_table_ = InstructionsTable::New(
|
|
instructions_table_len, start_pc, end_pc, instruction_table_data);
|
|
}
|
|
#else
|
|
ASSERT(instructions_table_len == 0);
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
{
|
|
// The deserializer initializes objects without using the write barrier,
|
|
// partly for speed since we know all the deserialized objects will be
|
|
// long-lived and partly because the target objects can be not yet
|
|
// initialized at the time of the write. To make this safe, we must ensure
|
|
// there are no other threads mutating this heap, and that incremental
|
|
// marking is not in progress. This is normally the case anyway for the
|
|
// main snapshot being deserialized at isolate load, but needs checks for
|
|
// loading secondary snapshots are part of deferred loading.
|
|
HeapIterationScope iter(thread());
|
|
// For bump-pointer allocation in old-space.
|
|
HeapLocker hl(thread(), heap_->old_space());
|
|
// Must not perform any other type of allocation, which might trigger GC
|
|
// while there are still uninitialized objects.
|
|
NoSafepointScope no_safepoint;
|
|
refs_ = refs.ptr();
|
|
|
|
roots->AddBaseObjects(this);
|
|
|
|
if (num_base_objects_ != (next_ref_index_ - kFirstReference)) {
|
|
FATAL("Snapshot expects %" Pd
|
|
" base objects, but deserializer provided %" Pd,
|
|
num_base_objects_, next_ref_index_ - kFirstReference);
|
|
}
|
|
|
|
{
|
|
TIMELINE_DURATION(thread(), Isolate, "ReadAlloc");
|
|
for (intptr_t i = 0; i < num_clusters_; i++) {
|
|
clusters_[i] = ReadCluster();
|
|
clusters_[i]->ReadAlloc(this);
|
|
#if defined(DEBUG)
|
|
intptr_t serializers_next_ref_index_ = Read<int32_t>();
|
|
ASSERT_EQUAL(serializers_next_ref_index_, next_ref_index_);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// We should have completely filled the ref array.
|
|
ASSERT_EQUAL(next_ref_index_ - kFirstReference, num_objects_);
|
|
|
|
{
|
|
TIMELINE_DURATION(thread(), Isolate, "ReadFill");
|
|
for (intptr_t i = 0; i < num_clusters_; i++) {
|
|
clusters_[i]->ReadFill(this);
|
|
#if defined(DEBUG)
|
|
int32_t section_marker = Read<int32_t>();
|
|
ASSERT(section_marker == kSectionMarker);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
roots->ReadRoots(this);
|
|
|
|
#if defined(DEBUG)
|
|
int32_t section_marker = Read<int32_t>();
|
|
ASSERT(section_marker == kSectionMarker);
|
|
#endif
|
|
|
|
refs_ = nullptr;
|
|
}
|
|
|
|
roots->PostLoad(this, refs);
|
|
|
|
auto isolate_group = thread()->isolate_group();
|
|
#if defined(DEBUG)
|
|
isolate_group->ValidateClassTable();
|
|
isolate_group->heap()->Verify("Deserializer::Deserialize");
|
|
#endif
|
|
|
|
{
|
|
TIMELINE_DURATION(thread(), Isolate, "PostLoad");
|
|
for (intptr_t i = 0; i < num_clusters_; i++) {
|
|
clusters_[i]->PostLoad(this, refs);
|
|
}
|
|
}
|
|
|
|
if (isolate_group->snapshot_is_dontneed_safe()) {
|
|
size_t clustered_length =
|
|
reinterpret_cast<uword>(AddressOfCurrentPosition()) -
|
|
reinterpret_cast<uword>(clustered_start);
|
|
VirtualMemory::DontNeed(const_cast<void*>(clustered_start),
|
|
clustered_length);
|
|
}
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
FullSnapshotWriter::FullSnapshotWriter(Snapshot::Kind kind,
|
|
NonStreamingWriteStream* snapshot_data,
|
|
ImageWriter* image_writer)
|
|
: thread_(Thread::Current()),
|
|
kind_(kind),
|
|
snapshot_data_(snapshot_data),
|
|
image_writer_(image_writer) {
|
|
ASSERT(isolate_group() != nullptr);
|
|
ASSERT(heap() != nullptr);
|
|
ObjectStore* object_store = isolate_group()->object_store();
|
|
ASSERT(object_store != nullptr);
|
|
|
|
#if defined(DEBUG)
|
|
isolate_group()->ValidateClassTable();
|
|
#endif // DEBUG
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
profile_writer_ = new (zone()) V8SnapshotProfileWriter(zone());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FullSnapshotWriter::~FullSnapshotWriter() {}
|
|
|
|
void FullSnapshotWriter::WriteProgramSnapshot(
|
|
GrowableArray<LoadingUnitSerializationData*>* units) {
|
|
TIMELINE_DURATION(thread(), Isolate, "WriteProgramSnapshot");
|
|
|
|
ASSERT(snapshot_data_ != nullptr);
|
|
Serializer serializer(thread(), kind_, snapshot_data_, image_writer_,
|
|
profile_writer_);
|
|
serializer.set_loading_units(units);
|
|
serializer.set_current_loading_unit_id(LoadingUnit::kRootId);
|
|
ObjectStore* object_store = isolate_group()->object_store();
|
|
ASSERT(object_store != nullptr);
|
|
|
|
// These type arguments must always be retained.
|
|
ASSERT(object_store->type_argument_int()->untag()->IsCanonical());
|
|
ASSERT(object_store->type_argument_double()->untag()->IsCanonical());
|
|
ASSERT(object_store->type_argument_string()->untag()->IsCanonical());
|
|
ASSERT(object_store->type_argument_string_dynamic()->untag()->IsCanonical());
|
|
ASSERT(object_store->type_argument_string_string()->untag()->IsCanonical());
|
|
|
|
serializer.ReserveHeader();
|
|
serializer.WriteVersionAndFeatures();
|
|
ProgramSerializationRoots roots(object_store, kind_);
|
|
ZoneGrowableArray<Object*>* objects = serializer.Serialize(&roots);
|
|
if (units != nullptr) {
|
|
(*units)[LoadingUnit::kRootId]->set_objects(objects);
|
|
}
|
|
serializer.FillHeader(serializer.kind());
|
|
clustered_isolate_size_ = serializer.bytes_written();
|
|
heap_isolate_size_ = serializer.bytes_heap_allocated();
|
|
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
image_writer_->SetProfileWriter(profile_writer_);
|
|
image_writer_->Write(serializer.stream(), false);
|
|
#if defined(DART_PRECOMPILER)
|
|
image_writer_->DumpStatistics();
|
|
#endif
|
|
|
|
mapped_data_size_ += image_writer_->data_size();
|
|
mapped_text_size_ += image_writer_->text_size();
|
|
image_writer_->ResetOffsets();
|
|
image_writer_->ClearProfileWriter();
|
|
}
|
|
}
|
|
|
|
void FullSnapshotWriter::WriteUnitSnapshot(
|
|
GrowableArray<LoadingUnitSerializationData*>* units,
|
|
LoadingUnitSerializationData* unit,
|
|
uint32_t program_hash) {
|
|
TIMELINE_DURATION(thread(), Isolate, "WriteUnitSnapshot");
|
|
|
|
Serializer serializer(thread(), kind_, snapshot_data_, image_writer_,
|
|
profile_writer_);
|
|
serializer.set_loading_units(units);
|
|
serializer.set_current_loading_unit_id(unit->id());
|
|
|
|
serializer.ReserveHeader();
|
|
serializer.WriteVersionAndFeatures();
|
|
serializer.Write(program_hash);
|
|
|
|
UnitSerializationRoots roots(unit);
|
|
unit->set_objects(serializer.Serialize(&roots));
|
|
|
|
serializer.FillHeader(serializer.kind());
|
|
clustered_isolate_size_ = serializer.bytes_written();
|
|
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
image_writer_->SetProfileWriter(profile_writer_);
|
|
image_writer_->Write(serializer.stream(), false);
|
|
#if defined(DART_PRECOMPILER)
|
|
image_writer_->DumpStatistics();
|
|
#endif
|
|
|
|
mapped_data_size_ += image_writer_->data_size();
|
|
mapped_text_size_ += image_writer_->text_size();
|
|
image_writer_->ResetOffsets();
|
|
image_writer_->ClearProfileWriter();
|
|
}
|
|
}
|
|
|
|
void FullSnapshotWriter::WriteFullSnapshot(
|
|
GrowableArray<LoadingUnitSerializationData*>* data) {
|
|
WriteProgramSnapshot(data);
|
|
|
|
if (FLAG_print_snapshot_sizes) {
|
|
OS::Print("VMIsolate(CodeSize): %" Pd "\n", clustered_vm_size_);
|
|
OS::Print("Isolate(CodeSize): %" Pd "\n", clustered_isolate_size_);
|
|
OS::Print("ReadOnlyData(CodeSize): %" Pd "\n", mapped_data_size_);
|
|
OS::Print("Instructions(CodeSize): %" Pd "\n", mapped_text_size_);
|
|
OS::Print("Total(CodeSize): %" Pd "\n",
|
|
clustered_vm_size_ + clustered_isolate_size_ + mapped_data_size_ +
|
|
mapped_text_size_);
|
|
OS::Print("VMIsolate(HeapSize): %" Pd "\n", heap_vm_size_);
|
|
OS::Print("Isolate(HeapSize): %" Pd "\n", heap_isolate_size_);
|
|
OS::Print("Total(HeapSize): %" Pd "\n", heap_vm_size_ + heap_isolate_size_);
|
|
}
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
|
|
profile_writer_->Write(FLAG_write_v8_snapshot_profile_to);
|
|
}
|
|
#endif
|
|
}
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
FullSnapshotReader::FullSnapshotReader(const Snapshot* snapshot,
|
|
const uint8_t* instructions_buffer,
|
|
Thread* thread)
|
|
: kind_(snapshot->kind()),
|
|
thread_(thread),
|
|
buffer_(snapshot->Addr()),
|
|
size_(snapshot->length()),
|
|
data_image_(snapshot->DataImage()),
|
|
instructions_image_(instructions_buffer) {}
|
|
|
|
char* SnapshotHeaderReader::InitializeIsolateGroupFlagsFromSnapshot(
|
|
const Snapshot* snapshot) {
|
|
SnapshotHeaderReader header_reader(snapshot);
|
|
|
|
char* error = header_reader.VerifyVersion();
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
const char* features = nullptr;
|
|
intptr_t features_length = 0;
|
|
error = header_reader.ReadFeatures(&features, &features_length);
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
IsolateGroup* group = IsolateGroup::Current();
|
|
|
|
ASSERT(features[features_length] == '\0');
|
|
const char* cursor = features;
|
|
while (*cursor != '\0') {
|
|
while (*cursor == ' ') {
|
|
cursor++;
|
|
}
|
|
|
|
const char* end = strstr(cursor, " ");
|
|
if (end == nullptr) {
|
|
end = features + features_length;
|
|
}
|
|
|
|
#define SET_FLAG(name, isolate_flag) \
|
|
if (strncmp(cursor, #name, end - cursor) == 0) { \
|
|
group->set_##isolate_flag(true); \
|
|
cursor = end; \
|
|
continue; \
|
|
} \
|
|
if (strncmp(cursor, "no-" #name, end - cursor) == 0) { \
|
|
group->set_##isolate_flag(false); \
|
|
cursor = end; \
|
|
continue; \
|
|
}
|
|
|
|
#define ISOLATE_GROUP_FLAG_LIST(V) \
|
|
V(asserts, asserts) \
|
|
V(use_field_guards, use_field_guards) \
|
|
V(use_osr, use_osr) \
|
|
V(branch_coverage, branch_coverage) \
|
|
V(coverage, coverage) \
|
|
V(code_comments, code_comments) \
|
|
V(dwarf_stack_traces, dwarf_stack_traces)
|
|
|
|
ISOLATE_GROUP_FLAG_LIST(SET_FLAG)
|
|
#undef ISOLATE_GROUP_FLAG_LIST
|
|
|
|
cursor = end;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
char* FullSnapshotReader::ReadProgramSnapshot() {
|
|
SnapshotHeaderReader header_reader(kind_, buffer_, size_);
|
|
intptr_t offset = 0;
|
|
char* error =
|
|
header_reader.VerifyVersionAndFeatures(thread_->isolate_group(), &offset);
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
// Even though there's no concurrent threads we have to guard agains, some
|
|
// logic we do in deserialization triggers common code that asserts the
|
|
// program lock is held.
|
|
SafepointWriteRwLocker ml(thread_, isolate_group()->program_lock());
|
|
|
|
Deserializer deserializer(thread_, kind_, buffer_, size_, data_image_,
|
|
instructions_image_, /*is_non_root_unit=*/false,
|
|
offset);
|
|
error = deserializer.VerifyImageAlignment();
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
ASSERT(data_image_ != nullptr);
|
|
thread_->isolate_group()->SetupImagePage(data_image_,
|
|
/* is_executable */ false);
|
|
ASSERT(instructions_image_ != nullptr);
|
|
thread_->isolate_group()->SetupImagePage(instructions_image_,
|
|
/* is_executable */ true);
|
|
}
|
|
|
|
ProgramDeserializationRoots roots(thread_->isolate_group()->object_store());
|
|
deserializer.Deserialize(&roots);
|
|
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
const auto& units = Array::Handle(
|
|
thread_->isolate_group()->object_store()->loading_units());
|
|
if (!units.IsNull()) {
|
|
const auto& unit = LoadingUnit::Handle(
|
|
LoadingUnit::RawCast(units.At(LoadingUnit::kRootId)));
|
|
// Unlike other units, we don't explicitly load the root loading unit,
|
|
// so we mark it as loaded here, setting the instructions image as well.
|
|
unit.set_load_outstanding();
|
|
unit.set_instructions_image(instructions_image_);
|
|
unit.set_loaded(true);
|
|
}
|
|
}
|
|
|
|
InitializeBSS();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
char* FullSnapshotReader::ReadUnitSnapshot(const LoadingUnit& unit) {
|
|
SnapshotHeaderReader header_reader(kind_, buffer_, size_);
|
|
intptr_t offset = 0;
|
|
char* error =
|
|
header_reader.VerifyVersionAndFeatures(thread_->isolate_group(), &offset);
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
Deserializer deserializer(
|
|
thread_, kind_, buffer_, size_, data_image_, instructions_image_,
|
|
/*is_non_root_unit=*/unit.id() != LoadingUnit::kRootId, offset);
|
|
error = deserializer.VerifyImageAlignment();
|
|
if (error != nullptr) {
|
|
return nullptr;
|
|
}
|
|
{
|
|
Array& units =
|
|
Array::Handle(isolate_group()->object_store()->loading_units());
|
|
uint32_t main_program_hash = Smi::Value(Smi::RawCast(units.At(0)));
|
|
uint32_t unit_program_hash = deserializer.Read<uint32_t>();
|
|
if (main_program_hash != unit_program_hash) {
|
|
return Utils::StrDup(
|
|
"Deferred loading unit is from a different "
|
|
"program than the main loading unit");
|
|
}
|
|
}
|
|
|
|
if (Snapshot::IncludesCode(kind_)) {
|
|
ASSERT(data_image_ != nullptr);
|
|
thread_->isolate_group()->SetupImagePage(data_image_,
|
|
/* is_executable */ false);
|
|
ASSERT(instructions_image_ != nullptr);
|
|
thread_->isolate_group()->SetupImagePage(instructions_image_,
|
|
/* is_executable */ true);
|
|
unit.set_instructions_image(instructions_image_);
|
|
}
|
|
|
|
UnitDeserializationRoots roots(unit);
|
|
deserializer.Deserialize(&roots);
|
|
|
|
InitializeBSS();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FullSnapshotReader::InitializeBSS() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
// Initialize entries in the isolate portion of the BSS segment.
|
|
ASSERT(Snapshot::IncludesCode(kind_));
|
|
Image image(instructions_image_);
|
|
if (auto const bss = image.bss()) {
|
|
BSS::Initialize(thread_, bss, /*vm=*/false);
|
|
}
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
} // namespace dart
|