// 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 #include #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 T Read() { return ReadStream::Raw::Read(&stream_); } intptr_t ReadRefId() { return stream_.ReadRefId(); } intptr_t ReadUnsigned() { return stream_.ReadUnsigned(); } uint64_t ReadUnsigned64() { return stream_.ReadUnsigned(); } 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(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 T Read() { return ReadStream::Raw::Read(this); } uint64_t ReadUnsigned64() { return ReadUnsigned(); } 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(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(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(d->ReadRef()); class_name_ = static_cast(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(d->ReadRef()); name_ = static_cast(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(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(d->ReadUnsigned()); owner_ = d->ReadRef(); if (owner_.IsLibrary()) { owner_ = Library::Cast(owner_).toplevel_class(); } function_name_ = static_cast(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 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(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(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(); ASSERT(Smi::IsValid(value)); d->AssignRef(Smi::New(value)); } for (intptr_t i = smi_count; i < count; i++) { int64_t value = d->Read(); ASSERT(!Smi::IsValid(value)); MintPtr mint = static_cast(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(d.Ref(id)); Deserializer::InitializeHeader(dbl, kDoubleCid, Double::InstanceSize(), /*is_deeply_immutable=*/true); dbl->untag()->value_ = d.Read(); } } }; 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(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(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(d.Ref(id)); Deserializer::InitializeHeader(map, kConstMapCid, Map::InstanceSize(), is_deeply_immutable()); map->untag()->type_arguments_ = static_cast(d.ReadRef()); map->untag()->hash_mask_ = Smi::New(0); map->untag()->data_ = static_cast(d.ReadRef()); map->untag()->used_data_ = Smi::New(d.ReadUnsigned()); map->untag()->deleted_keys_ = Smi::New(0); map->untag()->index_ = static_cast(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(d.Ref(id)); Deserializer::InitializeHeader(set, kConstSetCid, Set::InstanceSize(), is_deeply_immutable()); set->untag()->type_arguments_ = static_cast(d.ReadRef()); set->untag()->hash_mask_ = Smi::New(0); set->untag()->data_ = static_cast(d.ReadRef()); set->untag()->used_data_ = Smi::New(d.ReadUnsigned()); set->untag()->deleted_keys_ = Smi::New(0); set->untag()->index_ = static_cast(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(d.Ref(id)); SmiPtr shape = static_cast(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(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( reinterpret_cast(instance->untag()) + offset); *p = d.ReadRef(); offset += kCompressedWordSize; } while (offset < instance_size) { CompressedObjectPtr* p = reinterpret_cast( reinterpret_cast(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 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(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(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(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( (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(d.null()); type->untag()->hash_ = Smi::New(0); type->untag()->type_parameters_ = static_cast(d.ReadRef()); type->untag()->result_type_ = static_cast(d.ReadRef()); type->untag()->parameter_types_ = static_cast(d.ReadRef()); type->untag()->named_parameter_names_ = static_cast(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) { type.SetNumOptionalParameters(num_params - 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); } 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(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( (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(d.null()); type->untag()->hash_ = Smi::New(0); type->untag()->shape_ = static_cast(d.ReadRef()); type->untag()->field_types_ = static_cast(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(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(d.ReadRef()); const intptr_t nullability = static_cast( (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(d.null()); type->untag()->hash_ = Smi::New(0); type->untag()->arguments_ = static_cast(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(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( (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(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(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(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(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(d.Ref(id)); Deserializer::InitializeHeader(ic, kICDataCid, ICData::InstanceSize()); ic->untag()->target_name_ = static_cast(d.ReadRef()); ic->untag()->args_descriptor_ = static_cast(d.ReadRef()); ic->untag()->entries_ = ICData::CachedEmptyICDataArray( /*num_args_tested=*/1, /*tracking_exactness=*/false); ic->untag()->receivers_static_type_ = static_cast(d.null()); ic->untag()->owner_ = static_cast(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(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(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(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(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(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(); break; } case ModuleSnapshot::kUnboxedDouble: { pool->untag()->entry_bits()[j] = immediate_entry_bits; UntaggedObjectPool::Entry& entry = pool->untag()->data()[j]; entry.raw_value_ = bit_cast(d.Read()); 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(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(); UNIMPLEMENTED(); return nullptr; 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(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(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(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(AddressOfCurrentPosition()) - reinterpret_cast(clustered_start); VirtualMemory::DontNeed(const_cast(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)