| // 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 { | |
| static void ModularAOTModeHandler(bool value) { | |
| #if !defined(TARGET_ARCH_ARM64) | |
| FATAL("Modular AOT is supported only on ARM64"); | |
| #endif | |
| FLAG_background_compilation = false; | |
| FLAG_enable_mirrors = false; | |
| FLAG_link_natives_lazily = true; | |
| FLAG_optimization_counter_threshold = -1; | |
| FLAG_use_field_guards = false; | |
| FLAG_use_cha_deopt = false; | |
| #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) | |
| FLAG_deoptimize_alot = false; | |
| FLAG_deoptimize_every = 0; | |
| FLAG_use_osr = false; | |
| #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) | |
| FLAG_modular_aot_mode = true; | |
| } | |
| DEFINE_FLAG_HANDLER(ModularAOTModeHandler, | |
| modular_aot, | |
| "Modular AOT compiler mode"); | |
| 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, | |
| kRecords, | |
| kInstantiatedClosures, | |
| kTypeParameters, | |
| kInterfaceTypes, | |
| kFunctionTypes, | |
| kRecordTypes, | |
| kTypeParameterTypes, | |
| kTypeArguments, | |
| kClosureDatas, | |
| kFunctions, | |
| kLists, | |
| kMaps, | |
| kSets, | |
| kInstances, | |
| kCodes, | |
| kICDatas, | |
| kSubtypeTestCaches, | |
| kObjectPools, | |
| kExceptionHandlers, | |
| kPcDescriptors, | |
| kCatchEntryMoves, | |
| kCompressedStackMaps, | |
| kCodeSourceMap, | |
| }; | |
| // 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, | |
| kNativeFunction, | |
| }; | |
| }; | |
| 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(); | |
| void ReadRoots(); | |
| 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<UntaggedFunction::Kind>(d->ReadUnsigned()); | |
| owner_ = d->ReadRef(); | |
| if (owner_.IsLibrary()) { | |
| owner_ = Library::Cast(owner_).toplevel_class(); | |
| } | |
| function_name_ = static_cast<StringPtr>(d->ReadRef()); | |
| switch (kind) { | |
| case UntaggedFunction::kRegularFunction: | |
| case UntaggedFunction::kMethodExtractor: | |
| break; | |
| case UntaggedFunction::kGetterFunction: | |
| case UntaggedFunction::kImplicitGetter: | |
| case UntaggedFunction::kImplicitStaticGetter: | |
| function_name_ = Field::GetterName(function_name_); | |
| break; | |
| case UntaggedFunction::kSetterFunction: | |
| case UntaggedFunction::kImplicitSetter: | |
| function_name_ = Field::SetterName(function_name_); | |
| break; | |
| case UntaggedFunction::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 UntaggedFunction::kConstructor: { | |
| 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; | |
| default: | |
| FATAL("Unexpected function kind %s", Function::KindToCString(kind)); | |
| } | |
| if (kind != UntaggedFunction::kFieldInitializer) { | |
| ErrorPtr finalize_err = | |
| (kind == UntaggedFunction::kConstructor) | |
| ? Class::Cast(owner_).EnsureIsAllocateFinalized(d->thread()) | |
| : Class::Cast(owner_).EnsureIsFinalized(d->thread()); | |
| if (finalize_err != Error::null()) { | |
| FATAL( | |
| "Unable to finalize class %s%s: %s", | |
| Class::Cast(owner_).ToCString(), | |
| (kind == UntaggedFunction::kConstructor) ? " for allocation" : "", | |
| Error::Handle(zone_, finalize_err).ToErrorCString()); | |
| } | |
| 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 == UntaggedFunction::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 ClosureDataDeserializationCluster : public DeserializationCluster { | |
| public: | |
| ClosureDataDeserializationCluster() : DeserializationCluster("ClosureData") {} | |
| ~ClosureDataDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| ReadAllocFixedSize(d, ClosureData::InstanceSize()); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| ClosureDataPtr data = static_cast<ClosureDataPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(data, kClosureDataCid, | |
| ClosureData::InstanceSize()); | |
| data->untag()->context_scope_ = static_cast<ContextScopePtr>(d.null()); | |
| data->untag()->parent_function_ = static_cast<FunctionPtr>(d.ReadRef()); | |
| data->untag()->closure_ = static_cast<ClosurePtr>(d.null()); | |
| data->untag()->packed_fields_ = 0; | |
| } | |
| } | |
| }; | |
| class FunctionDeserializationCluster : public DeserializationCluster { | |
| public: | |
| FunctionDeserializationCluster() : DeserializationCluster("Function") {} | |
| ~FunctionDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| ReadAllocFixedSize(d, Function::InstanceSize()); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| FunctionPtr func = static_cast<FunctionPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(func, kFunctionCid, | |
| Function::InstanceSize()); | |
| func->untag()->entry_point_ = 0; | |
| func->untag()->unchecked_entry_point_ = 0; | |
| func->untag()->name_ = static_cast<StringPtr>(d.ReadRef()); | |
| ObjectPtr owner = d.ReadRef(); | |
| if (owner->IsLibrary()) { | |
| owner = static_cast<LibraryPtr>(owner)->untag()->toplevel_class(); | |
| } | |
| func->untag()->owner_ = owner; | |
| func->untag()->signature_ = static_cast<FunctionTypePtr>(d.ReadRef()); | |
| func->untag()->data_ = d.ReadRef(); | |
| func->untag()->positional_parameter_names_ = | |
| static_cast<ArrayPtr>(d.ReadRef()); | |
| func->untag()->unboxed_parameters_info_.Reset(); | |
| func->untag()->token_pos_ = | |
| TokenPosition::Deserialize(d.ReadUnsigned() - 1); | |
| func->untag()->end_token_pos_ = | |
| TokenPosition::Deserialize(d.ReadUnsigned() - 1); | |
| func->untag()->kind_tag_ = d.ReadUnsigned(); | |
| const intptr_t local_function_id = d.ReadUnsigned(); | |
| // Temporarily save local_function_id to kernel_offset until PostLoad. | |
| func->untag()->kernel_offset_ = local_function_id; | |
| func->untag()->usage_counter_ = 0; | |
| func->untag()->optimized_instruction_count_ = 0; | |
| func->untag()->optimized_call_site_count_ = 0; | |
| func->untag()->deoptimization_counter_ = 0; | |
| func->untag()->state_bits_ = 0; | |
| func->untag()->inlining_depth_ = 0; | |
| func->untag()->is_optimizable_ = false; | |
| } | |
| } | |
| void PostLoad(Deserializer* d, const Array& refs) override { | |
| Function& func = Function::Handle(d->zone()); | |
| FunctionType& signature = FunctionType::Handle(d->zone()); | |
| TypeArguments& type_args = TypeArguments::Handle(d->zone()); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| func ^= refs.At(id); | |
| signature = func.signature(); | |
| signature ^= ClassFinalizer::FinalizeType(signature); | |
| func.SetSignature(signature); | |
| if (func.IsClosureFunction()) { | |
| const intptr_t local_function_id = func.kernel_offset(); | |
| func.set_kernel_offset(0); | |
| ClosureFunctionsCache::AddClosureFunctionLocked(func, | |
| local_function_id); | |
| if (func.IsGeneric()) { | |
| type_args = func.DefaultTypeArguments(d->zone()); | |
| auto mode = type_args.GetInstantiationMode(d->zone(), &func); | |
| func.set_default_type_arguments_instantiation_mode(mode); | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| 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(); | |
| } | |
| } | |
| } | |
| void PostLoad(Deserializer* d, const Array& refs) override { | |
| Array& array = Array::Handle(d->zone()); | |
| TypeArguments& type_args = TypeArguments::Handle(d->zone()); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| array ^= refs.At(id); | |
| type_args = array.GetTypeArguments(); | |
| if (!type_args.IsNull() && !type_args.IsCanonical()) { | |
| type_args = type_args.Canonicalize(d->thread()); | |
| array.SetTypeArguments(type_args); | |
| } | |
| } | |
| } | |
| }; | |
| 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()); | |
| TypeArguments& type_args = TypeArguments::Handle(d->zone()); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| map ^= refs.At(id); | |
| type_args = map.GetTypeArguments(); | |
| if (!type_args.IsNull() && !type_args.IsCanonical()) { | |
| type_args = type_args.Canonicalize(d->thread()); | |
| map.SetTypeArguments(type_args); | |
| } | |
| 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()); | |
| TypeArguments& type_args = TypeArguments::Handle(d->zone()); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| set ^= refs.At(id); | |
| type_args = set.GetTypeArguments(); | |
| if (!type_args.IsNull() && !type_args.IsCanonical()) { | |
| type_args = type_args.Canonicalize(d->thread()); | |
| set.SetTypeArguments(type_args); | |
| } | |
| 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( | |
| "Instance", | |
| 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); | |
| } | |
| } | |
| void PostLoad(Deserializer* d, const Array& refs) override { | |
| if (class_.host_type_arguments_field_offset() == Class::kNoTypeArguments) { | |
| return; | |
| } | |
| Instance& instance = Instance::Handle(d->zone()); | |
| TypeArguments& type_args = TypeArguments::Handle(d->zone()); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| instance ^= refs.At(id); | |
| type_args = instance.GetTypeArguments(); | |
| if (!type_args.IsNull() && !type_args.IsCanonical()) { | |
| type_args = type_args.Canonicalize(d->thread()); | |
| instance.SetTypeArguments(type_args); | |
| } | |
| } | |
| } | |
| 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 { | |
| 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") {} | |
| ~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_ = | |
| static_cast<ExceptionHandlersPtr>(d->ReadRef()); | |
| code->untag()->pc_descriptors_ = | |
| static_cast<PcDescriptorsPtr>(d->ReadRef()); | |
| code->untag()->catch_entry_ = static_cast<TypedDataPtr>(d->ReadRef()); | |
| code->untag()->compressed_stackmaps_ = | |
| static_cast<CompressedStackMapsPtr>(d->ReadRef()); | |
| code->untag()->inlined_id_to_function_ = Array::null(); | |
| code->untag()->code_source_map_ = | |
| static_cast<CodeSourceMapPtr>(d->ReadRef()); | |
| 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 | |
| const uword instr_size = d->ReadUnsigned(); | |
| code->untag()->state_bits_ = Code::OptimizedBit::update(true, 0); | |
| code->untag()->instructions_length_ = instr_size; | |
| code->untag()->unchecked_offset_ = 0; | |
| 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 | |
| } | |
| } | |
| }; | |
| 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: | |
| explicit ObjectPoolDeserializationCluster(Zone* zone) | |
| : DeserializationCluster("ObjectPool"), class_(Class::Handle(zone)) {} | |
| ~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: { | |
| class_ = 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( | |
| class_.id(), compiler::target::Class::GetInstanceSize(class_)); | |
| 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_ = d.ReadRef(); | |
| 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; | |
| } | |
| case ModuleSnapshot::kNativeFunction: { | |
| pool->untag()->entry_bits()[j] = immediate_entry_bits; | |
| UntaggedObjectPool::Entry& entry = pool->untag()->data()[j]; | |
| entry.raw_value_ = NativeEntry::LinkNativeCallEntry(); | |
| 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.ptr() != Object::uninitialized_index().ptr()) && | |
| (obj.ptr() != Object::uninitialized_data().ptr())) { | |
| obj = Instance::Cast(obj).Canonicalize(d->thread()); | |
| pool.SetObjectAt(i, obj); | |
| } | |
| } | |
| } | |
| } | |
| private: | |
| Class& class_; | |
| }; | |
| class ExceptionHandlersDeserializationCluster : public DeserializationCluster { | |
| public: | |
| ExceptionHandlersDeserializationCluster() | |
| : DeserializationCluster("ExceptionHandlers") {} | |
| ~ExceptionHandlersDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| start_index_ = d->next_index(); | |
| const intptr_t count = d->ReadUnsigned(); | |
| for (intptr_t i = 0; i < count; i++) { | |
| const intptr_t length = d->ReadUnsigned(); | |
| d->AssignRef(d->Allocate(ExceptionHandlers::InstanceSize(length))); | |
| } | |
| stop_index_ = d->next_index(); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| ExceptionHandlersPtr handlers = | |
| static_cast<ExceptionHandlersPtr>(d.Ref(id)); | |
| const intptr_t length = d.ReadUnsigned(); | |
| const bool has_async_handler = (d.ReadUnsigned() != 0); | |
| Deserializer::InitializeHeader(handlers, kExceptionHandlersCid, | |
| ExceptionHandlers::InstanceSize(length)); | |
| handlers->untag()->packed_fields_ = | |
| UntaggedExceptionHandlers::AsyncHandlerBit::update( | |
| has_async_handler, | |
| UntaggedExceptionHandlers::NumEntriesBits::encode(length)); | |
| handlers->untag()->handled_types_data_ = | |
| static_cast<ArrayPtr>(d.ReadRef()); | |
| for (intptr_t j = 0; j < length; j++) { | |
| ExceptionHandlerInfo& info = handlers->untag()->data()[j]; | |
| info.handler_pc_offset = static_cast<uint32_t>(d.ReadUnsigned()); | |
| info.outer_try_index = static_cast<int16_t>(d.ReadUnsigned() - 1); | |
| info.needs_stacktrace = static_cast<int8_t>(d.ReadUnsigned()); | |
| info.has_catch_all = static_cast<int8_t>(d.ReadUnsigned()); | |
| info.is_generated = static_cast<int8_t>(d.ReadUnsigned()); | |
| } | |
| } | |
| } | |
| }; | |
| class PcDescriptorsDeserializationCluster : public DeserializationCluster { | |
| public: | |
| PcDescriptorsDeserializationCluster() | |
| : DeserializationCluster("PcDescriptors") {} | |
| ~PcDescriptorsDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| start_index_ = d->next_index(); | |
| const intptr_t count = d->ReadUnsigned(); | |
| for (intptr_t i = 0; i < count; i++) { | |
| const intptr_t length = d->ReadUnsigned(); | |
| d->AssignRef(d->Allocate(PcDescriptors::InstanceSize(length))); | |
| } | |
| stop_index_ = d->next_index(); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| const intptr_t length = d.ReadUnsigned(); | |
| PcDescriptorsPtr desc = static_cast<PcDescriptorsPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(desc, kPcDescriptorsCid, | |
| PcDescriptors::InstanceSize(length)); | |
| desc->untag()->length_ = length; | |
| uint8_t* cdata = reinterpret_cast<uint8_t*>(desc->untag()->data()); | |
| d.ReadBytes(cdata, length); | |
| } | |
| } | |
| }; | |
| class CatchEntryMovesDeserializationCluster : public DeserializationCluster { | |
| public: | |
| CatchEntryMovesDeserializationCluster() | |
| : DeserializationCluster("CatchEntryMoves") {} | |
| ~CatchEntryMovesDeserializationCluster() {} | |
| 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(TypedData::InstanceSize(length))); | |
| } | |
| stop_index_ = d->next_index(); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| const intptr_t length = d.ReadUnsigned(); | |
| TypedDataPtr data = static_cast<TypedDataPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(data, kTypedDataInt8ArrayCid, | |
| TypedData::InstanceSize(length)); | |
| data->untag()->length_ = Smi::New(length); | |
| data->untag()->RecomputeDataField(); | |
| uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data()); | |
| d.ReadBytes(cdata, length); | |
| } | |
| } | |
| }; | |
| class CompressedStackMapsDeserializationCluster | |
| : public DeserializationCluster { | |
| public: | |
| CompressedStackMapsDeserializationCluster() | |
| : DeserializationCluster("CompressedStackMaps") {} | |
| ~CompressedStackMapsDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| start_index_ = d->next_index(); | |
| const intptr_t count = d->ReadUnsigned(); | |
| for (intptr_t i = 0; i < count; i++) { | |
| const intptr_t length = d->ReadUnsigned(); | |
| d->AssignRef(d->Allocate(CompressedStackMaps::InstanceSize(length))); | |
| } | |
| stop_index_ = d->next_index(); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| const intptr_t length = d.ReadUnsigned(); | |
| CompressedStackMapsPtr map = | |
| static_cast<CompressedStackMapsPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(map, kCompressedStackMapsCid, | |
| CompressedStackMaps::InstanceSize(length)); | |
| map->untag()->payload()->set_flags_and_size( | |
| UntaggedCompressedStackMaps::SizeField::encode(length)); | |
| uint8_t* cdata = | |
| reinterpret_cast<uint8_t*>(map->untag()->payload()->data()); | |
| d.ReadBytes(cdata, length); | |
| } | |
| } | |
| }; | |
| class CodeSourceMapDeserializationCluster : public DeserializationCluster { | |
| public: | |
| CodeSourceMapDeserializationCluster() | |
| : DeserializationCluster("CodeSourceMap") {} | |
| ~CodeSourceMapDeserializationCluster() {} | |
| void ReadAlloc(Deserializer* d) override { | |
| start_index_ = d->next_index(); | |
| const intptr_t count = d->ReadUnsigned(); | |
| for (intptr_t i = 0; i < count; i++) { | |
| const intptr_t length = d->ReadUnsigned(); | |
| d->AssignRef(d->Allocate(CodeSourceMap::InstanceSize(length))); | |
| } | |
| stop_index_ = d->next_index(); | |
| } | |
| void ReadFill(Deserializer* d_) override { | |
| Deserializer::Local d(d_); | |
| for (intptr_t id = start_index_, n = stop_index_; id < n; id++) { | |
| const intptr_t length = d.ReadUnsigned(); | |
| CodeSourceMapPtr map = static_cast<CodeSourceMapPtr>(d.Ref(id)); | |
| Deserializer::InitializeHeader(map, kCodeSourceMapCid, | |
| CodeSourceMap::InstanceSize(length)); | |
| map->untag()->length_ = length; | |
| uint8_t* cdata = reinterpret_cast<uint8_t*>(map->untag()->data()); | |
| d.ReadBytes(cdata, length); | |
| } | |
| } | |
| }; | |
| 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::kClosureDatas: | |
| return new (Z) ClosureDataDeserializationCluster(); | |
| case ModuleSnapshot::kFunctions: | |
| return new (Z) FunctionDeserializationCluster(); | |
| 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(Z); | |
| case ModuleSnapshot::kExceptionHandlers: | |
| return new (Z) ExceptionHandlersDeserializationCluster(); | |
| case ModuleSnapshot::kPcDescriptors: | |
| return new (Z) PcDescriptorsDeserializationCluster(); | |
| case ModuleSnapshot::kCatchEntryMoves: | |
| return new (Z) CatchEntryMovesDeserializationCluster(); | |
| case ModuleSnapshot::kCompressedStackMaps: | |
| return new (Z) CompressedStackMapsDeserializationCluster(); | |
| case ModuleSnapshot::kCodeSourceMap: | |
| return new (Z) CodeSourceMapDeserializationCluster(); | |
| 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; | |
| } | |
| void Deserializer::ReadRoots() { | |
| Deserializer* d = this; | |
| const intptr_t stub_count = d->ReadUnsigned(); | |
| for (intptr_t i = 0; i < stub_count; ++i) { | |
| const intptr_t stub_index = d->ReadUnsigned(); | |
| CodePtr stub_code = static_cast<CodePtr>(d->ReadRef()); | |
| StubCode::EntryAtPut(stub_index, stub_code); | |
| } | |
| } | |
| 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(Object::empty_exception_handlers()); | |
| AddBaseObject(Object::empty_async_exception_handlers()); | |
| AddBaseObject(Object::empty_descriptors()); | |
| AddBaseObject(Object::uninitialized_index()); | |
| AddBaseObject(Object::uninitialized_data()); | |
| AddBaseObject(Object::mutable_empty_array()); | |
| 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()); | |
| AddBaseObject(StubCode::CloneSuspendState()); | |
| AddBaseObject(StubCode::CallBootstrapNative()); | |
| 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); | |
| } | |
| } | |
| ReadRoots(); | |
| 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); | |
| if (!FLAG_modular_aot_mode) { | |
| FATAL("Module snapshots can be loaded only if using --modular_aot"); | |
| } | |
| if (!thread->isolate_group()->modular_aot_mode()) { | |
| FATAL( | |
| "Module snapshots can be loaded only if " | |
| "Dart_IsolateFlags::modular_aot_mode was set"); | |
| } | |
| Deserializer deserializer(thread, snapshot->Addr(), snapshot->length(), | |
| instructions_buffer); | |
| char* error = deserializer.VerifyVersionAndFeatures(); | |
| if (error != nullptr) { | |
| return error; | |
| } | |
| SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); | |
| deserializer.Deserialize(); | |
| return nullptr; | |
| } | |
| } // namespace module_snapshot | |
| } // namespace dart | |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |