7df4ef0cf1
TEST=ci Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: Iaa38ca6c62f9899d2c3f0363c484dbe15e0a9859 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509523 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
1759 lines
62 KiB
C++
1759 lines
62 KiB
C++
// Copyright (c) 2025, 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.
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "vm/module_snapshot.h"
|
|
|
|
#include "platform/assert.h"
|
|
#include "vm/bootstrap.h"
|
|
#include "vm/canonical_tables.h"
|
|
#include "vm/class_id.h"
|
|
#include "vm/closure_functions_cache.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/resolver.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/timeline.h"
|
|
#include "vm/version.h"
|
|
#include "vm/zone_text_buffer.h"
|
|
|
|
namespace dart {
|
|
namespace module_snapshot {
|
|
|
|
class ModuleSnapshot : public AllStatic {
|
|
public:
|
|
// Version of module snapshot format.
|
|
// Should match Snapshot.moduleSnapshotFormatVersion
|
|
// constant declared in pkg/native_compiler/lib/snapshot/snapshot.dart.
|
|
static constexpr intptr_t kFormatVersion = 1;
|
|
|
|
// Predefined clusters in the module snapshot.
|
|
// Should match PredefinedClusters enum
|
|
// declared in pkg/native_compiler/lib/snapshot/snapshot.dart.
|
|
enum PredefinedClusters {
|
|
kOneByteStrings,
|
|
kTwoByteStrings,
|
|
kLibraryRefs,
|
|
kPrivateNames,
|
|
kClassRefs,
|
|
kFieldRefs,
|
|
kFunctionRefs,
|
|
kClosureFunctionRefs,
|
|
kClosureRefs,
|
|
kArgumentsDescriptorRefs,
|
|
kRecordShapeRefs,
|
|
kInts,
|
|
kDoubles,
|
|
kLists,
|
|
kMaps,
|
|
kSets,
|
|
kRecords,
|
|
kInstantiatedClosures,
|
|
kTypeParameters,
|
|
kInterfaceTypes,
|
|
kFunctionTypes,
|
|
kRecordTypes,
|
|
kTypeParameterTypes,
|
|
kTypeArguments,
|
|
kCodes,
|
|
kICDatas,
|
|
kSubtypeTestCaches,
|
|
kObjectPools,
|
|
kInstances,
|
|
};
|
|
|
|
// Function kinds in the module snapshot.
|
|
// Should match FunctionKind enum
|
|
// declared in pkg/native_compiler/lib/snapshot/snapshot.dart.
|
|
enum FunctionKind {
|
|
kRegular,
|
|
kGetter,
|
|
kSetter,
|
|
kGenerativeConstructor,
|
|
kFactoryConstructor,
|
|
kImplicitGetter,
|
|
kImplicitSetter,
|
|
kFieldInitializer,
|
|
kMethodExtractor,
|
|
};
|
|
|
|
// Object pool entry kinds in the module snapshots.
|
|
// Should match ObjectPoolEntryKind enum
|
|
// declared in pkg/native_compiler/lib/snapshot/snapshot.dart.
|
|
enum ObjectPoolEntryKind {
|
|
kObjectRef,
|
|
kNewObjectTags,
|
|
kStaticFieldOffset,
|
|
kInterfaceCall,
|
|
kDynamicCall,
|
|
kUnboxedInt,
|
|
kUnboxedDouble,
|
|
};
|
|
};
|
|
|
|
class Deserializer;
|
|
|
|
class DeserializationCluster : public ZoneObject {
|
|
public:
|
|
explicit DeserializationCluster(const char* name,
|
|
bool is_deeply_immutable = false)
|
|
: name_(name),
|
|
is_deeply_immutable_(is_deeply_immutable),
|
|
start_index_(-1),
|
|
stop_index_(-1) {}
|
|
virtual ~DeserializationCluster() {}
|
|
|
|
// Read references to base objects.
|
|
virtual void PreLoad(Deserializer* deserializer) {}
|
|
|
|
// 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) {}
|
|
|
|
// Initialize the cluster's objects. Do not touch the memory of other objects.
|
|
virtual void ReadFill(Deserializer* deserializer) {}
|
|
|
|
// Complete any action that requires the full graph to be deserialized, such
|
|
// as rehashing.
|
|
virtual void PostLoad(Deserializer* deserializer, const Array& refs) {}
|
|
|
|
const char* name() const { return name_; }
|
|
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_deeply_immutable_;
|
|
// The range of the ref array that belongs to this cluster.
|
|
intptr_t start_index_;
|
|
intptr_t stop_index_;
|
|
};
|
|
|
|
static constexpr intptr_t kFirstReference = 1;
|
|
|
|
class Deserializer : public ThreadStackResource {
|
|
public:
|
|
Deserializer(Thread* thread,
|
|
const uint8_t* buffer,
|
|
intptr_t size,
|
|
const uint8_t* instructions_buffer);
|
|
~Deserializer();
|
|
|
|
char* VerifyVersionAndFeatures();
|
|
|
|
ObjectPtr Allocate(intptr_t size);
|
|
static void InitializeHeader(ObjectPtr raw,
|
|
intptr_t cid,
|
|
intptr_t size,
|
|
bool is_deeply_immutable = false);
|
|
|
|
// 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); }
|
|
|
|
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) { stream_.Align(alignment); }
|
|
|
|
void AddBaseObject(const Object& object) { AssignRefPreLoad(object); }
|
|
|
|
void AssignRefPreLoad(const Object& object) {
|
|
refs_array_.SetAt(next_ref_index_, object);
|
|
next_ref_index_++;
|
|
}
|
|
|
|
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_array_.At(index);
|
|
}
|
|
|
|
ObjectPtr ReadRef() { return Ref(ReadRefId()); }
|
|
|
|
void Deserialize();
|
|
|
|
DeserializationCluster* ReadCluster();
|
|
|
|
uword instructions() const {
|
|
return reinterpret_cast<uword>(instructions_buffer_);
|
|
}
|
|
intptr_t next_index() const { return next_ref_index_; }
|
|
Heap* heap() const { return heap_; }
|
|
Zone* zone() const { return zone_; }
|
|
|
|
// 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()); }
|
|
|
|
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_;
|
|
ReadStream stream_;
|
|
const uint8_t* instructions_buffer_;
|
|
intptr_t num_base_objects_ = 0;
|
|
intptr_t num_objects_ = 0;
|
|
intptr_t num_clusters_ = 0;
|
|
Array& refs_array_;
|
|
ArrayPtr refs_;
|
|
intptr_t next_ref_index_ = kFirstReference;
|
|
DeserializationCluster** clusters_ = nullptr;
|
|
};
|
|
|
|
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_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(false, 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;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
class OneByteStringDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit OneByteStringDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("OneByteString"),
|
|
string_(String::Handle(zone)) {}
|
|
~OneByteStringDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t len = d->ReadUnsigned();
|
|
string_ =
|
|
Symbols::FromLatin1(d->thread(), d->AddressOfCurrentPosition(), len);
|
|
d->Advance(len);
|
|
d->AssignRefPreLoad(string_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
String& string_;
|
|
};
|
|
|
|
class TwoByteStringDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit TwoByteStringDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("TwoByteString"),
|
|
string_(String::Handle(zone)) {}
|
|
~TwoByteStringDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t len = d->ReadUnsigned();
|
|
d->Align(TwoByteString::kBytesPerElement);
|
|
string_ = Symbols::FromUTF16(
|
|
d->thread(),
|
|
reinterpret_cast<const uint16_t*>(d->AddressOfCurrentPosition()),
|
|
len);
|
|
d->Advance(len << 1);
|
|
d->AssignRefPreLoad(string_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
String& string_;
|
|
};
|
|
|
|
class LibraryRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit LibraryRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("LibraryRef"),
|
|
uri_(String::Handle(zone)),
|
|
library_(Library::Handle(zone)) {}
|
|
~LibraryRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
uri_ = static_cast<StringPtr>(d->ReadRef());
|
|
library_ = Library::LookupLibrary(d->thread(), uri_);
|
|
if (library_.IsNull()) {
|
|
FATAL("Unable to find library %s", uri_.ToCString());
|
|
}
|
|
d->AssignRefPreLoad(library_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
String& uri_;
|
|
Library& library_;
|
|
};
|
|
|
|
class ClassRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit ClassRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("ClassRef"),
|
|
library_(Library::Handle(zone)),
|
|
class_name_(String::Handle(zone)),
|
|
class_(Class::Handle(zone)) {}
|
|
~ClassRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
library_ = static_cast<LibraryPtr>(d->ReadRef());
|
|
class_name_ = static_cast<StringPtr>(d->ReadRef());
|
|
class_ = library_.LookupClass(class_name_);
|
|
if (class_.IsNull()) {
|
|
FATAL("Unable to find class %s in %s", class_name_.ToCString(),
|
|
library_.ToCString());
|
|
}
|
|
d->AssignRefPreLoad(class_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Library& library_;
|
|
String& class_name_;
|
|
Class& class_;
|
|
};
|
|
|
|
class PrivateNameDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit PrivateNameDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("PrivateName"),
|
|
library_(Library::Handle(zone)),
|
|
name_(String::Handle(zone)) {}
|
|
~PrivateNameDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
library_ = static_cast<LibraryPtr>(d->ReadRef());
|
|
name_ = static_cast<StringPtr>(d->ReadRef());
|
|
name_ = library_.PrivateName(name_);
|
|
d->AssignRefPreLoad(name_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Library& library_;
|
|
String& name_;
|
|
};
|
|
|
|
class FieldRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit FieldRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("FieldRef"),
|
|
owner_(Object::Handle(zone)),
|
|
field_name_(String::Handle(zone)),
|
|
field_(Field::Handle(zone)) {}
|
|
~FieldRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
owner_ = d->ReadRef();
|
|
field_name_ = static_cast<StringPtr>(d->ReadRef());
|
|
if (owner_.IsLibrary()) {
|
|
owner_ = Library::Cast(owner_).toplevel_class();
|
|
}
|
|
field_ = Class::Cast(owner_).LookupField(field_name_);
|
|
if (field_.IsNull()) {
|
|
FATAL("Unable to find field %s in %s", field_name_.ToCString(),
|
|
owner_.ToCString());
|
|
}
|
|
d->AssignRefPreLoad(field_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Object& owner_;
|
|
String& field_name_;
|
|
Field& field_;
|
|
};
|
|
|
|
class FunctionRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit FunctionRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("FunctionRef"),
|
|
zone_(zone),
|
|
owner_(Object::Handle(zone)),
|
|
class_name_(String::Handle(zone)),
|
|
function_name_(String::Handle(zone)),
|
|
function_(Function::Handle(zone)),
|
|
field_(Field::Handle(zone)) {}
|
|
~FunctionRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const auto kind =
|
|
static_cast<ModuleSnapshot::FunctionKind>(d->ReadUnsigned());
|
|
owner_ = d->ReadRef();
|
|
if (owner_.IsLibrary()) {
|
|
owner_ = Library::Cast(owner_).toplevel_class();
|
|
}
|
|
function_name_ = static_cast<StringPtr>(d->ReadRef());
|
|
switch (kind) {
|
|
case ModuleSnapshot::kRegular:
|
|
case ModuleSnapshot::kMethodExtractor:
|
|
break;
|
|
case ModuleSnapshot::kGetter:
|
|
case ModuleSnapshot::kImplicitGetter:
|
|
function_name_ = Field::GetterName(function_name_);
|
|
break;
|
|
case ModuleSnapshot::kSetter:
|
|
case ModuleSnapshot::kImplicitSetter:
|
|
function_name_ = Field::SetterName(function_name_);
|
|
break;
|
|
case ModuleSnapshot::kFieldInitializer:
|
|
field_ = Class::Cast(owner_).LookupField(function_name_);
|
|
if (field_.IsNull()) {
|
|
FATAL("Unable to find field %s in %s", function_name_.ToCString(),
|
|
owner_.ToCString());
|
|
}
|
|
function_ = field_.EnsureInitializerFunction();
|
|
ASSERT(!function_.IsNull());
|
|
break;
|
|
case ModuleSnapshot::kGenerativeConstructor:
|
|
case ModuleSnapshot::kFactoryConstructor: {
|
|
class_name_ = Class::Cast(owner_).Name();
|
|
GrowableHandlePtrArray<const String> pieces(zone_, 3);
|
|
pieces.Add(class_name_);
|
|
pieces.Add(Symbols::Dot());
|
|
pieces.Add(function_name_);
|
|
function_name_ = Symbols::FromConcatAll(d->thread(), pieces);
|
|
} break;
|
|
}
|
|
if (kind != ModuleSnapshot::kFieldInitializer) {
|
|
function_ = Resolver::ResolveFunction(zone_, Class::Cast(owner_),
|
|
function_name_);
|
|
if (function_.IsNull()) {
|
|
FATAL("Unable to find function %s in %s", function_name_.ToCString(),
|
|
owner_.ToCString());
|
|
}
|
|
if (kind == ModuleSnapshot::kMethodExtractor) {
|
|
function_name_ = Field::GetterName(function_name_);
|
|
function_ = function_.GetMethodExtractor(function_name_);
|
|
}
|
|
}
|
|
d->AssignRefPreLoad(function_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
Object& owner_;
|
|
String& class_name_;
|
|
String& function_name_;
|
|
Function& function_;
|
|
Field& field_;
|
|
};
|
|
|
|
class ClosureFunctionRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit ClosureFunctionRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("ClosureFunctionRef"),
|
|
function_(Function::Handle(zone)) {}
|
|
~ClosureFunctionRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
function_ = static_cast<FunctionPtr>(d->ReadRef());
|
|
const intptr_t local_function_id = d->ReadUnsigned();
|
|
if (local_function_id == 0) {
|
|
function_ = function_.ImplicitClosureFunction();
|
|
} else {
|
|
function_ = ClosureFunctionsCache::LookupClosureFunction(
|
|
function_, local_function_id);
|
|
RELEASE_ASSERT(!function_.IsNull());
|
|
}
|
|
d->AssignRefPreLoad(function_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Function& function_;
|
|
};
|
|
|
|
class ClosureRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit ClosureRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("ClosureRef"),
|
|
function_(Function::Handle(zone)),
|
|
closure_(Closure::Handle(zone)) {}
|
|
~ClosureRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
function_ = static_cast<FunctionPtr>(d->ReadRef());
|
|
closure_ = function_.ImplicitStaticClosure();
|
|
d->AssignRefPreLoad(closure_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Function& function_;
|
|
Closure& closure_;
|
|
};
|
|
|
|
class ArgumentsDescriptorRefDeserializationCluster
|
|
: public DeserializationCluster {
|
|
public:
|
|
explicit ArgumentsDescriptorRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("ArgumentsDescriptorRef"),
|
|
name_(String::Handle(zone)),
|
|
named_(Array::Handle(zone)),
|
|
args_descriptor_(Array::Handle(zone)) {}
|
|
~ArgumentsDescriptorRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t num_type_args = d->ReadUnsigned();
|
|
const intptr_t num_positional = d->ReadUnsigned();
|
|
const intptr_t num_named = d->ReadUnsigned();
|
|
const intptr_t total_args = num_positional + num_named;
|
|
// TODO(alexmarkov): support unboxed parameters.
|
|
if (num_named > 0) {
|
|
named_ = Array::New(num_named, Heap::kOld);
|
|
for (intptr_t i = 0; i < num_named; ++i) {
|
|
name_ ^= d->ReadRef();
|
|
named_.SetAt(i, name_);
|
|
}
|
|
args_descriptor_ = ArgumentsDescriptor::New(num_type_args, total_args,
|
|
total_args, named_);
|
|
} else {
|
|
args_descriptor_ =
|
|
ArgumentsDescriptor::New(num_type_args, total_args, total_args);
|
|
}
|
|
d->AssignRefPreLoad(args_descriptor_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
String& name_;
|
|
Array& named_;
|
|
Array& args_descriptor_;
|
|
};
|
|
|
|
class RecordShapeRefDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit RecordShapeRefDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("RecordShapeRef"),
|
|
name_(String::Handle(zone)),
|
|
named_(Array::Handle(zone)),
|
|
shape_(Smi::Handle(zone)) {}
|
|
~RecordShapeRefDeserializationCluster() {}
|
|
|
|
void PreLoad(Deserializer* d) override {
|
|
const intptr_t count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
const intptr_t num_positional = d->ReadUnsigned();
|
|
const intptr_t num_named = d->ReadUnsigned();
|
|
const intptr_t num_fields = num_positional + num_named;
|
|
const Array* field_names = &Array::empty_array();
|
|
if (num_named > 0) {
|
|
named_ = Array::New(num_named, Heap::kOld);
|
|
for (intptr_t i = 0; i < num_named; ++i) {
|
|
name_ ^= d->ReadRef();
|
|
named_.SetAt(i, name_);
|
|
}
|
|
named_.MakeImmutable();
|
|
field_names = &named_;
|
|
}
|
|
shape_ =
|
|
RecordShape::Register(d->thread(), num_fields, *field_names).AsSmi();
|
|
d->AssignRefPreLoad(shape_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
String& name_;
|
|
Array& named_;
|
|
Smi& shape_;
|
|
};
|
|
class IntDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
IntDeserializationCluster()
|
|
: DeserializationCluster("Int", /*is_deeply_immutable=*/true) {
|
|
ASSERT(Object::ShouldHaveDeeplyImmutabilityBitSet(kMintCid));
|
|
}
|
|
~IntDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
const intptr_t smi_count = d->ReadUnsigned();
|
|
for (intptr_t i = 0; i < smi_count; i++) {
|
|
int64_t value = d->Read<int64_t>();
|
|
ASSERT(Smi::IsValid(value));
|
|
d->AssignRef(Smi::New(value));
|
|
}
|
|
for (intptr_t i = smi_count; i < count; i++) {
|
|
int64_t value = d->Read<int64_t>();
|
|
ASSERT(!Smi::IsValid(value));
|
|
MintPtr mint = static_cast<MintPtr>(d->Allocate(Mint::InstanceSize()));
|
|
Deserializer::InitializeHeader(mint, kMintCid, Mint::InstanceSize(),
|
|
/*is_deeply_immutable=*/true);
|
|
mint->untag()->value_ = value;
|
|
d->AssignRef(mint);
|
|
}
|
|
stop_index_ = d->next_index();
|
|
}
|
|
|
|
void ReadFill(Deserializer*) override {}
|
|
};
|
|
|
|
class DoubleDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
DoubleDeserializationCluster()
|
|
: DeserializationCluster("Double", /*is_deeply_immutable=*/true) {
|
|
ASSERT(Object::ShouldHaveDeeplyImmutabilityBitSet(kDoubleCid));
|
|
}
|
|
~DoubleDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Double::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
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(),
|
|
/*is_deeply_immutable=*/true);
|
|
dbl->untag()->value_ = d.Read<double>();
|
|
}
|
|
}
|
|
};
|
|
|
|
class ListDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
ListDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"List",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kImmutableArrayCid)) {}
|
|
~ListDeserializationCluster() {}
|
|
|
|
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_);
|
|
|
|
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, kImmutableArrayCid,
|
|
Array::InstanceSize(length),
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class MapDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
MapDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"Map",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kConstMapCid)) {}
|
|
~MapDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Map::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
MapPtr map = static_cast<MapPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(map, kConstMapCid, Map::InstanceSize(),
|
|
is_deeply_immutable());
|
|
map->untag()->type_arguments_ =
|
|
static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
map->untag()->hash_mask_ = Smi::New(0);
|
|
map->untag()->data_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
map->untag()->used_data_ = Smi::New(d.ReadUnsigned());
|
|
map->untag()->deleted_keys_ = Smi::New(0);
|
|
map->untag()->index_ = static_cast<TypedDataPtr>(d.null());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Map& map = Map::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
map ^= refs.At(id);
|
|
map.ComputeAndSetHashMask();
|
|
}
|
|
}
|
|
};
|
|
|
|
class SetDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
SetDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"Set",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kConstSetCid)) {}
|
|
~SetDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Set::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
SetPtr set = static_cast<SetPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(set, kConstSetCid, Set::InstanceSize(),
|
|
is_deeply_immutable());
|
|
set->untag()->type_arguments_ =
|
|
static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
set->untag()->hash_mask_ = Smi::New(0);
|
|
set->untag()->data_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
set->untag()->used_data_ = Smi::New(d.ReadUnsigned());
|
|
set->untag()->deleted_keys_ = Smi::New(0);
|
|
set->untag()->index_ = static_cast<TypedDataPtr>(d.null());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Set& set = Set::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
set ^= refs.At(id);
|
|
set.ComputeAndSetHashMask();
|
|
}
|
|
}
|
|
};
|
|
|
|
class RecordDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
RecordDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"Record",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kRecordCid)) {}
|
|
~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 length = d->ReadUnsigned();
|
|
d->AssignRef(d->Allocate(Record::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++) {
|
|
RecordPtr record = static_cast<RecordPtr>(d.Ref(id));
|
|
SmiPtr shape = static_cast<SmiPtr>(d.ReadRef());
|
|
const intptr_t num_fields = RecordShape(shape).num_fields();
|
|
Deserializer::InitializeHeader(record, kRecordCid,
|
|
Record::InstanceSize(num_fields),
|
|
is_deeply_immutable());
|
|
record->untag()->shape_ = shape;
|
|
for (intptr_t j = 0; j < num_fields; j++) {
|
|
record->untag()->data()[j] = d.ReadRef();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class InstanceDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit InstanceDeserializationCluster(const Class& cls)
|
|
: DeserializationCluster(
|
|
"List",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(cls.id())),
|
|
class_(cls) {}
|
|
~InstanceDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
start_index_ = d->next_index();
|
|
const intptr_t count = d->ReadUnsigned();
|
|
next_field_offset_ = d->ReadUnsigned();
|
|
const intptr_t instance_size = instance_size_ = d->ReadUnsigned();
|
|
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 = class_.id();
|
|
intptr_t next_field_offset = next_field_offset_;
|
|
intptr_t instance_size = instance_size_;
|
|
|
|
// TODO(alexmarkov): support unboxed fields
|
|
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,
|
|
is_deeply_immutable());
|
|
|
|
intptr_t offset = Instance::NextFieldOffset();
|
|
while (offset < next_field_offset) {
|
|
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 Class& class_;
|
|
intptr_t next_field_offset_ = 0;
|
|
intptr_t instance_size_ = 0;
|
|
};
|
|
|
|
class TypeParametersDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
TypeParametersDeserializationCluster()
|
|
: DeserializationCluster("TypeParameters") {}
|
|
~TypeParametersDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, TypeParameters::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypeParametersPtr tps = static_cast<TypeParametersPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(tps, kTypeParametersCid,
|
|
TypeParameters::InstanceSize());
|
|
tps->untag()->names_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
tps->untag()->flags_ = static_cast<ArrayPtr>(d.null());
|
|
tps->untag()->bounds_ = static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
tps->untag()->defaults_ = static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
}
|
|
}
|
|
};
|
|
|
|
class TypeArgumentsDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
TypeArgumentsDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"TypeArguments",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kTypeArgumentsCid)) {}
|
|
~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();
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
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),
|
|
is_deeply_immutable());
|
|
type_args->untag()->length_ = Smi::New(length);
|
|
type_args->untag()->hash_ = Smi::New(0);
|
|
type_args->untag()->nullability_ = Smi::New(0);
|
|
type_args->untag()->instantiations_ =
|
|
Object::empty_instantiations_cache_array().ptr();
|
|
for (intptr_t j = 0; j < length; j++) {
|
|
type_args->untag()->types()[j] =
|
|
static_cast<AbstractTypePtr>(d.ReadRef());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class FunctionTypeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
FunctionTypeDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"FunctionType",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kFunctionTypeCid)) {}
|
|
~FunctionTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, FunctionType::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
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(),
|
|
is_deeply_immutable());
|
|
type->untag()->type_test_stub_entry_point_.store(
|
|
0, std::memory_order_relaxed);
|
|
const intptr_t nullability = static_cast<intptr_t>(
|
|
(d.ReadUnsigned() != 0) ? Nullability::kNullable
|
|
: Nullability::kNonNullable);
|
|
const intptr_t flags = UntaggedAbstractType::NullabilityBit::update(
|
|
nullability, UntaggedAbstractType::TypeStateBits::encode(
|
|
UntaggedAbstractType::kAllocated));
|
|
type->untag()->set_flags(flags);
|
|
type->untag()->type_test_stub_ = static_cast<CodePtr>(d.null());
|
|
type->untag()->hash_ = Smi::New(0);
|
|
type->untag()->type_parameters_ =
|
|
static_cast<TypeParametersPtr>(d.ReadRef());
|
|
type->untag()->result_type_ = static_cast<AbstractTypePtr>(d.ReadRef());
|
|
type->untag()->parameter_types_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
type->untag()->named_parameter_names_ =
|
|
static_cast<ArrayPtr>(d.ReadRef());
|
|
const intptr_t num_fixed_params = d.ReadUnsigned();
|
|
type->untag()->packed_parameter_counts_ =
|
|
UntaggedFunctionType::PackedNumImplicitParameters::update(
|
|
1 /* implicit closure parameter */,
|
|
UntaggedFunctionType::PackedNumFixedParameters::encode(
|
|
num_fixed_params));
|
|
type->untag()->packed_type_parameter_counts_ =
|
|
0; // TODO(alexmarkov): set type parameter counts.
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
FunctionType& type = FunctionType::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
const intptr_t num_params = Array::LengthOf(type.parameter_types());
|
|
const intptr_t num_named_params =
|
|
Array::LengthOf(type.named_parameter_names());
|
|
const intptr_t num_fixed_params = type.num_fixed_parameters();
|
|
type.set_num_implicit_parameters(1); // Implicit closure parameter.
|
|
if (num_named_params != 0) {
|
|
ASSERT(num_params - num_named_params == num_fixed_params);
|
|
type.SetNumOptionalParameters(num_named_params,
|
|
/* are_optional_positional=*/false);
|
|
} else if (num_fixed_params != num_params) {
|
|
type.SetNumOptionalParameters(num_params - num_fixed_params,
|
|
/* are_optional_positional=*/true);
|
|
}
|
|
ASSERT(type.NumParameters() == num_params);
|
|
type.SetIsFinalized();
|
|
}
|
|
}
|
|
};
|
|
|
|
class RecordTypeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
RecordTypeDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"RecordType",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kRecordTypeCid)) {}
|
|
~RecordTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, RecordType::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
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(),
|
|
is_deeply_immutable());
|
|
type->untag()->type_test_stub_entry_point_.store(
|
|
0, std::memory_order_relaxed);
|
|
const intptr_t nullability = static_cast<intptr_t>(
|
|
(d.ReadUnsigned() != 0) ? Nullability::kNullable
|
|
: Nullability::kNonNullable);
|
|
const intptr_t flags = UntaggedAbstractType::NullabilityBit::update(
|
|
nullability, UntaggedAbstractType::TypeStateBits::encode(
|
|
UntaggedAbstractType::kAllocated));
|
|
type->untag()->set_flags(flags);
|
|
type->untag()->type_test_stub_ = static_cast<CodePtr>(d.null());
|
|
type->untag()->hash_ = Smi::New(0);
|
|
type->untag()->shape_ = static_cast<SmiPtr>(d.ReadRef());
|
|
type->untag()->field_types_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
RecordType& type = RecordType::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
type.SetIsFinalized();
|
|
}
|
|
}
|
|
};
|
|
|
|
class InterfaceTypeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
InterfaceTypeDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"InterfaceType",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kTypeCid)) {}
|
|
~InterfaceTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Type::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
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(),
|
|
is_deeply_immutable());
|
|
type->untag()->type_test_stub_entry_point_.store(
|
|
0, std::memory_order_relaxed);
|
|
ClassPtr type_class = static_cast<ClassPtr>(d.ReadRef());
|
|
const intptr_t nullability = static_cast<intptr_t>(
|
|
(d.ReadUnsigned() != 0) ? Nullability::kNullable
|
|
: Nullability::kNonNullable);
|
|
const intptr_t flags = UntaggedType::TypeClassIdBits::update(
|
|
type_class->untag()->id(),
|
|
UntaggedAbstractType::NullabilityBit::update(
|
|
nullability, UntaggedAbstractType::TypeStateBits::encode(
|
|
UntaggedAbstractType::kAllocated)));
|
|
type->untag()->set_flags(flags);
|
|
type->untag()->type_test_stub_ = static_cast<CodePtr>(d.null());
|
|
type->untag()->hash_ = Smi::New(0);
|
|
type->untag()->arguments_ = static_cast<TypeArgumentsPtr>(d.ReadRef());
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Type& type = Type::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
type.SetIsFinalized();
|
|
}
|
|
}
|
|
};
|
|
|
|
class TypeParameterTypeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
TypeParameterTypeDeserializationCluster()
|
|
: DeserializationCluster(
|
|
"TypeParameterType",
|
|
Object::ShouldHaveDeeplyImmutabilityBitSet(kTypeParameterCid)) {}
|
|
~TypeParameterTypeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, TypeParameter::InstanceSize());
|
|
}
|
|
|
|
void ReadFill(Deserializer* d_) override {
|
|
Deserializer::Local d(d_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
TypeParameterPtr tp = static_cast<TypeParameterPtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(tp, kTypeParameterCid,
|
|
TypeParameter::InstanceSize(),
|
|
is_deeply_immutable());
|
|
tp->untag()->type_test_stub_entry_point_.store(0,
|
|
std::memory_order_relaxed);
|
|
const intptr_t nullability = static_cast<intptr_t>(
|
|
(d.ReadUnsigned() != 0) ? Nullability::kNullable
|
|
: Nullability::kNonNullable);
|
|
ObjectPtr owner = d.ReadRef();
|
|
intptr_t flags = UntaggedAbstractType::NullabilityBit::update(
|
|
nullability, UntaggedAbstractType::TypeStateBits::encode(
|
|
UntaggedAbstractType::kAllocated));
|
|
if (owner->IsClass()) {
|
|
owner = Smi::New(static_cast<ClassPtr>(owner)->untag()->id());
|
|
} else {
|
|
if (owner->IsFunction()) {
|
|
owner = Function::RawCast(owner)->untag()->signature();
|
|
}
|
|
flags =
|
|
UntaggedTypeParameter::IsFunctionTypeParameter::update(true, flags);
|
|
}
|
|
tp->untag()->set_flags(flags);
|
|
tp->untag()->type_test_stub_ = static_cast<CodePtr>(d.null());
|
|
tp->untag()->hash_ = Smi::New(0);
|
|
tp->untag()->owner_ = owner;
|
|
tp->untag()->base_ = 0;
|
|
tp->untag()->index_ = d.ReadUnsigned();
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
TypeParameter& type = TypeParameter::Handle(d->zone());
|
|
Code& stub = Code::Handle(d->zone());
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
type ^= refs.At(id);
|
|
stub = TypeTestingStubGenerator::DefaultCodeForType(type);
|
|
type.InitializeTypeTestingStubNonAtomic(stub);
|
|
type.SetIsFinalized();
|
|
}
|
|
}
|
|
};
|
|
|
|
class CodeDeserializationCluster : public DeserializationCluster {
|
|
public:
|
|
explicit CodeDeserializationCluster(Zone* zone)
|
|
: DeserializationCluster("Code"),
|
|
code_source_map_(CodeSourceMap::Handle(zone, CodeSourceMap::New(0))) {}
|
|
~CodeDeserializationCluster() {}
|
|
|
|
void ReadAlloc(Deserializer* d) override {
|
|
ReadAllocFixedSize(d, Code::InstanceSize(0));
|
|
}
|
|
|
|
void ReadFill(Deserializer* d) override {
|
|
uword instructions = d->instructions();
|
|
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));
|
|
|
|
const uword entry_point = instructions;
|
|
code->untag()->entry_point_ = entry_point;
|
|
code->untag()->monomorphic_entry_point_ = entry_point;
|
|
code->untag()->unchecked_entry_point_ = entry_point;
|
|
code->untag()->monomorphic_unchecked_entry_point_ = entry_point;
|
|
code->untag()->object_pool_ = static_cast<ObjectPoolPtr>(d->ReadRef());
|
|
code->untag()->instructions_ = Instructions::null();
|
|
code->untag()->owner_ = d->ReadRef();
|
|
code->untag()->exception_handlers_ =
|
|
Object::empty_exception_handlers().ptr();
|
|
code->untag()->pc_descriptors_ = Object::empty_descriptors().ptr();
|
|
code->untag()->catch_entry_ = Object::null();
|
|
code->untag()->compressed_stackmaps_ = CompressedStackMaps::null();
|
|
code->untag()->inlined_id_to_function_ = Array::null();
|
|
code->untag()->code_source_map_ = code_source_map_.ptr();
|
|
code->untag()->active_instructions_ = Instructions::null();
|
|
code->untag()->deopt_info_array_ = Array::null();
|
|
code->untag()->static_calls_target_table_ = Array::null();
|
|
|
|
#if !defined(PRODUCT)
|
|
code->untag()->return_address_metadata_ = Object::null();
|
|
code->untag()->var_descriptors_ = LocalVarDescriptors::null();
|
|
code->untag()->comments_ = Array::null();
|
|
code->untag()->compile_timestamp_ = 0;
|
|
#endif
|
|
|
|
code->untag()->state_bits_ = Code::OptimizedBit::update(true, 0);
|
|
code->untag()->unchecked_offset_ = 0;
|
|
|
|
const uword instr_size = d->ReadUnsigned();
|
|
instructions += instr_size;
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
Code& code = Code::Handle(d->zone());
|
|
Object& owner = Object::Handle(d->zone());
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
code ^= refs.At(id);
|
|
owner = code.owner();
|
|
|
|
if (owner.IsFunction()) {
|
|
Function::Cast(owner).SetInstructionsSafe(code);
|
|
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
if ((FLAG_disassemble ||
|
|
(code.is_optimized() && FLAG_disassemble_optimized)) &&
|
|
compiler::PrintFilter::ShouldPrint(Function::Cast(owner))) {
|
|
Disassembler::DisassembleCode(Function::Cast(owner), code,
|
|
code.is_optimized());
|
|
}
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
} else {
|
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
if (FLAG_disassemble_stubs) {
|
|
Disassembler::DisassembleStub("", code);
|
|
}
|
|
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
}
|
|
|
|
#if !defined(PRODUCT)
|
|
if (CodeObservers::AreActive()) {
|
|
Code::NotifyCodeObservers(code, code.is_optimized());
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private:
|
|
const CodeSourceMap& code_source_map_;
|
|
};
|
|
|
|
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_);
|
|
|
|
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());
|
|
ic->untag()->target_name_ = static_cast<StringPtr>(d.ReadRef());
|
|
ic->untag()->args_descriptor_ = static_cast<ArrayPtr>(d.ReadRef());
|
|
ic->untag()->entries_ = ICData::CachedEmptyICDataArray(
|
|
/*num_args_tested=*/1, /*tracking_exactness=*/false);
|
|
ic->untag()->receivers_static_type_ =
|
|
static_cast<AbstractTypePtr>(d.null());
|
|
ic->untag()->owner_ = static_cast<FunctionPtr>(d.ReadRef());
|
|
ic->untag()->deopt_id_ = DeoptId::kNone;
|
|
ic->untag()->state_bits_ = ICData::NumArgsTestedBits::encode(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
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_);
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
SubtypeTestCachePtr stc = static_cast<SubtypeTestCachePtr>(d.Ref(id));
|
|
Deserializer::InitializeHeader(stc, kSubtypeTestCacheCid,
|
|
SubtypeTestCache::InstanceSize());
|
|
stc->untag()->cache_ = Object::empty_subtype_test_cache_array().ptr();
|
|
stc->untag()->num_inputs_ = d.ReadUnsigned();
|
|
stc->untag()->num_occupied_ = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
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_);
|
|
|
|
const uint8_t tagged_entry_bits =
|
|
ObjectPool::EncodeBits(ObjectPool::EntryType::kTaggedObject,
|
|
ObjectPool::Patchability::kNotPatchable,
|
|
ObjectPool::SnapshotBehavior::kNotSnapshotable);
|
|
const uint8_t immediate_entry_bits =
|
|
ObjectPool::EncodeBits(ObjectPool::EntryType::kImmediate,
|
|
ObjectPool::Patchability::kNotPatchable,
|
|
ObjectPool::SnapshotBehavior::kNotSnapshotable);
|
|
|
|
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 auto kind =
|
|
static_cast<ModuleSnapshot::ObjectPoolEntryKind>(d.ReadUnsigned());
|
|
switch (kind) {
|
|
case ModuleSnapshot::kObjectRef: {
|
|
pool->untag()->entry_bits()[j] = tagged_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_obj_ = d.ReadRef();
|
|
break;
|
|
}
|
|
case ModuleSnapshot::kNewObjectTags: {
|
|
ClassPtr cls = static_cast<ClassPtr>(d.ReadRef());
|
|
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_value_ = compiler::target::MakeTagWordForNewSpaceObject(
|
|
cls->untag()->id_,
|
|
Object::RoundedAllocationSize(Class::host_instance_size(cls) *
|
|
kCompressedWordSize));
|
|
break;
|
|
}
|
|
case ModuleSnapshot::kStaticFieldOffset: {
|
|
FieldPtr field = static_cast<FieldPtr>(d.ReadRef());
|
|
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_value_ = FieldTable::FieldOffsetFor(
|
|
Smi::Value(field->untag()->host_offset_or_field_id()));
|
|
break;
|
|
}
|
|
case ModuleSnapshot::kInterfaceCall:
|
|
case ModuleSnapshot::kDynamicCall: {
|
|
pool->untag()->entry_bits()[j] = tagged_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_obj_ = d.ReadRef();
|
|
ASSERT(j < length);
|
|
++j;
|
|
pool->untag()->entry_bits()[j] = tagged_entry_bits;
|
|
UntaggedObjectPool::Entry& entry2 = pool->untag()->data()[j];
|
|
entry2.raw_obj_ = StubCode::OneArgOptimizedCheckInlineCache().ptr();
|
|
break;
|
|
}
|
|
case ModuleSnapshot::kUnboxedInt: {
|
|
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_value_ = d.Read<int64_t>();
|
|
break;
|
|
}
|
|
case ModuleSnapshot::kUnboxedDouble: {
|
|
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
|
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
|
entry.raw_value_ = bit_cast<int64_t>(d.Read<double>());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PostLoad(Deserializer* d, const Array& refs) override {
|
|
ObjectPool& pool = ObjectPool::Handle(d->zone());
|
|
Object& obj = Object::Handle(d->zone());
|
|
|
|
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
|
pool ^= refs.At(id);
|
|
|
|
for (intptr_t i = 0, length = pool.Length(); i < length; ++i) {
|
|
if (pool.TypeAt(i) != ObjectPool::EntryType::kTaggedObject) {
|
|
continue;
|
|
}
|
|
obj = pool.ObjectAt(i);
|
|
if (obj.IsInstance() && !obj.IsSmi()) {
|
|
obj = Instance::Cast(obj).Canonicalize(d->thread());
|
|
pool.SetObjectAt(i, obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Deserializer::Deserializer(Thread* thread,
|
|
const uint8_t* buffer,
|
|
intptr_t size,
|
|
const uint8_t* instructions_buffer)
|
|
: ThreadStackResource(thread),
|
|
heap_(thread->isolate_group()->heap()),
|
|
old_space_(heap_->old_space()),
|
|
freelist_(old_space_->DataFreeList()),
|
|
zone_(thread->zone()),
|
|
stream_(buffer, size),
|
|
instructions_buffer_(instructions_buffer),
|
|
refs_array_(Array::Handle(zone_)),
|
|
refs_(Array::null()) {}
|
|
|
|
Deserializer::~Deserializer() {
|
|
delete[] clusters_;
|
|
}
|
|
|
|
char* Deserializer::VerifyVersionAndFeatures() {
|
|
stream_.SetPosition(Snapshot::kHeaderSize);
|
|
|
|
const intptr_t format_version = stream_.ReadUnsigned();
|
|
if (format_version != ModuleSnapshot::kFormatVersion) {
|
|
return OS::SCreate(nullptr,
|
|
"Invalid module snapshot format version %" Pd
|
|
" (expected %" Pd ")",
|
|
format_version, ModuleSnapshot::kFormatVersion);
|
|
}
|
|
|
|
const char* features =
|
|
reinterpret_cast<const char*>(stream_.AddressOfCurrentPosition());
|
|
const intptr_t features_length =
|
|
Utils::StrNLen(features, stream_.PendingBytes());
|
|
if (features_length == stream_.PendingBytes()) {
|
|
return Utils::StrDup(
|
|
"The features string in the module snapshot was not zero-terminated.");
|
|
}
|
|
stream_.Advance(features_length + 1);
|
|
|
|
const char* expected_features = kHostArchitectureName;
|
|
if (strcmp(expected_features, features) != 0) {
|
|
return OS::SCreate(
|
|
nullptr, "Invalid module snapshot configuration '%s' (expected '%s')",
|
|
features, expected_features);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DeserializationCluster* Deserializer::ReadCluster() {
|
|
const intptr_t cluster_id = ReadUnsigned();
|
|
Zone* Z = zone_;
|
|
switch (cluster_id) {
|
|
case ModuleSnapshot::kOneByteStrings:
|
|
return new (Z) OneByteStringDeserializationCluster(Z);
|
|
case ModuleSnapshot::kTwoByteStrings:
|
|
return new (Z) TwoByteStringDeserializationCluster(Z);
|
|
case ModuleSnapshot::kLibraryRefs:
|
|
return new (Z) LibraryRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kPrivateNames:
|
|
return new (Z) PrivateNameDeserializationCluster(Z);
|
|
case ModuleSnapshot::kClassRefs:
|
|
return new (Z) ClassRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kFieldRefs:
|
|
return new (Z) FieldRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kFunctionRefs:
|
|
return new (Z) FunctionRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kClosureFunctionRefs:
|
|
return new (Z) ClosureFunctionRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kClosureRefs:
|
|
return new (Z) ClosureRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kArgumentsDescriptorRefs:
|
|
return new (Z) ArgumentsDescriptorRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kRecordShapeRefs:
|
|
return new (Z) RecordShapeRefDeserializationCluster(Z);
|
|
case ModuleSnapshot::kInts:
|
|
return new (Z) IntDeserializationCluster();
|
|
case ModuleSnapshot::kDoubles:
|
|
return new (Z) DoubleDeserializationCluster();
|
|
case ModuleSnapshot::kLists:
|
|
return new (Z) ListDeserializationCluster();
|
|
case ModuleSnapshot::kMaps:
|
|
return new (Z) MapDeserializationCluster();
|
|
case ModuleSnapshot::kSets:
|
|
return new (Z) SetDeserializationCluster();
|
|
case ModuleSnapshot::kRecords:
|
|
return new (Z) RecordDeserializationCluster();
|
|
case ModuleSnapshot::kInstantiatedClosures:
|
|
// return new (Z) InstantiatedClosureDeserializationCluster();
|
|
UNIMPLEMENTED();
|
|
return nullptr;
|
|
case ModuleSnapshot::kTypeParameters:
|
|
return new (Z) TypeParametersDeserializationCluster();
|
|
case ModuleSnapshot::kInterfaceTypes:
|
|
return new (Z) InterfaceTypeDeserializationCluster();
|
|
case ModuleSnapshot::kFunctionTypes:
|
|
return new (Z) FunctionTypeDeserializationCluster();
|
|
case ModuleSnapshot::kRecordTypes:
|
|
return new (Z) RecordTypeDeserializationCluster();
|
|
case ModuleSnapshot::kTypeParameterTypes:
|
|
return new (Z) TypeParameterTypeDeserializationCluster();
|
|
case ModuleSnapshot::kTypeArguments:
|
|
return new (Z) TypeArgumentsDeserializationCluster();
|
|
case ModuleSnapshot::kCodes:
|
|
return new (Z) CodeDeserializationCluster(Z);
|
|
case ModuleSnapshot::kICDatas:
|
|
return new (Z) ICDataDeserializationCluster();
|
|
case ModuleSnapshot::kSubtypeTestCaches:
|
|
return new (Z) SubtypeTestCacheDeserializationCluster();
|
|
case ModuleSnapshot::kObjectPools:
|
|
return new (Z) ObjectPoolDeserializationCluster();
|
|
case ModuleSnapshot::kInstances: {
|
|
const auto& cls = Class::Handle(Z, static_cast<ClassPtr>(ReadRef()));
|
|
return new (Z) InstanceDeserializationCluster(cls);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
FATAL("No cluster defined for cluster id %" Pd, cluster_id);
|
|
return nullptr;
|
|
}
|
|
|
|
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() {
|
|
const void* clustered_start = AddressOfCurrentPosition();
|
|
|
|
num_base_objects_ = ReadUnsigned();
|
|
num_objects_ = ReadUnsigned();
|
|
const uword instructions_size = ReadUnsigned();
|
|
num_clusters_ = ReadUnsigned();
|
|
|
|
// TODO(alexmarkov): register image pages
|
|
//
|
|
// heap_->SetupImagePage(const_cast<uint8_t*>(instructions_buffer_),
|
|
// instructions_size,
|
|
// /* is_executable */ true);
|
|
USE(instructions_size);
|
|
|
|
clusters_ = new DeserializationCluster*[num_clusters_];
|
|
refs_array_ = Array::New(num_objects_ + kFirstReference, Heap::kOld);
|
|
ObjectStore* object_store = thread()->isolate_group()->object_store();
|
|
|
|
AddBaseObject(Object::null_object());
|
|
AddBaseObject(Bool::True());
|
|
AddBaseObject(Bool::False());
|
|
AddBaseObject(Object::sentinel());
|
|
AddBaseObject(Object::dynamic_type());
|
|
AddBaseObject(Object::void_type());
|
|
AddBaseObject(Type::Handle(zone(), object_store->null_type()));
|
|
AddBaseObject(Type::Handle(zone(), object_store->never_type()));
|
|
AddBaseObject(Object::empty_array());
|
|
AddBaseObject(Object::optimized_out());
|
|
AddBaseObject(StubCode::Subtype1TestCache());
|
|
AddBaseObject(StubCode::Subtype2TestCache());
|
|
AddBaseObject(StubCode::Subtype3TestCache());
|
|
AddBaseObject(StubCode::Subtype4TestCache());
|
|
AddBaseObject(StubCode::Subtype6TestCache());
|
|
AddBaseObject(StubCode::InstantiateTypeArguments());
|
|
AddBaseObject(StubCode::InitAsync());
|
|
AddBaseObject(StubCode::InitAsyncStar());
|
|
AddBaseObject(StubCode::InitSyncStar());
|
|
AddBaseObject(StubCode::Await());
|
|
AddBaseObject(StubCode::AwaitWithTypeCheck());
|
|
AddBaseObject(StubCode::YieldAsyncStar());
|
|
AddBaseObject(StubCode::SuspendSyncStarAtStart());
|
|
AddBaseObject(StubCode::SuspendSyncStarAtYield());
|
|
AddBaseObject(StubCode::ReturnAsync());
|
|
AddBaseObject(StubCode::ReturnAsyncNotFuture());
|
|
AddBaseObject(StubCode::ReturnAsyncStar());
|
|
|
|
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, "PreLoad");
|
|
for (intptr_t i = 0; i < num_clusters_; i++) {
|
|
clusters_[i] = ReadCluster();
|
|
clusters_[i]->PreLoad(this);
|
|
}
|
|
}
|
|
|
|
{
|
|
// 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
|
|
// module snapshots being deserialized at isolate load.
|
|
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(thread());
|
|
refs_ = refs_array_.ptr();
|
|
|
|
{
|
|
TIMELINE_DURATION(thread(), Isolate, "ReadAlloc");
|
|
for (intptr_t i = 0; i < num_clusters_; i++) {
|
|
clusters_[i]->ReadAlloc(this);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
refs_ = nullptr;
|
|
}
|
|
|
|
auto isolate_group = thread()->isolate_group();
|
|
#if defined(DEBUG)
|
|
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_array_);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
char* ReadModuleSnapshot(Thread* thread,
|
|
const Snapshot* snapshot,
|
|
const uint8_t* instructions_buffer) {
|
|
ASSERT(snapshot->kind() == Snapshot::kModule);
|
|
|
|
Deserializer deserializer(thread, snapshot->Addr(), snapshot->length(),
|
|
instructions_buffer);
|
|
|
|
char* error = deserializer.VerifyVersionAndFeatures();
|
|
if (error != nullptr) {
|
|
return error;
|
|
}
|
|
|
|
deserializer.Deserialize();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace module_snapshot
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|