| // Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #include "vm/message_snapshot.h" | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "platform/assert.h" | 
 | #include "platform/unicode.h" | 
 | #include "vm/class_finalizer.h" | 
 | #include "vm/class_id.h" | 
 | #include "vm/dart_api_message.h" | 
 | #include "vm/dart_api_state.h" | 
 | #include "vm/dart_entry.h" | 
 | #include "vm/flags.h" | 
 | #include "vm/growable_array.h" | 
 | #include "vm/heap/heap.h" | 
 | #include "vm/heap/weak_table.h" | 
 | #include "vm/longjump.h" | 
 | #include "vm/object.h" | 
 | #include "vm/object_graph_copy.h" | 
 | #include "vm/object_store.h" | 
 | #include "vm/symbols.h" | 
 | #include "vm/type_testing_stubs.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | static Dart_CObject cobj_sentinel = {Dart_CObject_kUnsupported, {false}}; | 
 | static Dart_CObject cobj_transition_sentinel = {Dart_CObject_kUnsupported, | 
 |                                                 {false}}; | 
 | static Dart_CObject cobj_dynamic_type = {Dart_CObject_kUnsupported, {false}}; | 
 | static Dart_CObject cobj_void_type = {Dart_CObject_kUnsupported, {false}}; | 
 | static Dart_CObject cobj_empty_type_arguments = {Dart_CObject_kUnsupported, | 
 |                                                  {false}}; | 
 | static Dart_CObject cobj_true = {Dart_CObject_kBool, {true}}; | 
 | static Dart_CObject cobj_false = {Dart_CObject_kBool, {false}}; | 
 |  | 
 | // Workaround for lack of designated initializers until we adopt c++20 | 
 | class PredefinedCObjects { | 
 |  public: | 
 |   static PredefinedCObjects& getInstance() { | 
 |     static PredefinedCObjects instance; | 
 |     return instance; | 
 |   } | 
 |  | 
 |   static Dart_CObject* cobj_null() { return &getInstance().cobj_null_; } | 
 |   static Dart_CObject* cobj_empty_array() { | 
 |     return &getInstance().cobj_empty_array_; | 
 |   } | 
 |  | 
 |  private: | 
 |   PredefinedCObjects() { | 
 |     cobj_null_.type = Dart_CObject_kNull; | 
 |     cobj_null_.value.as_int64 = 0; | 
 |     cobj_empty_array_.type = Dart_CObject_kArray; | 
 |     cobj_empty_array_.value.as_array = {0, nullptr}; | 
 |   } | 
 |  | 
 |   Dart_CObject cobj_null_; | 
 |   Dart_CObject cobj_empty_array_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(PredefinedCObjects); | 
 | }; | 
 |  | 
 | enum class MessagePhase { | 
 |   kBeforeTypes = 0, | 
 |   kTypes = 1, | 
 |   kCanonicalInstances = 2, | 
 |   kNonCanonicalInstances = 3, | 
 |  | 
 |   kNumPhases = 4, | 
 | }; | 
 |  | 
 | class MessageSerializer; | 
 | class MessageDeserializer; | 
 | class ApiMessageSerializer; | 
 | class ApiMessageDeserializer; | 
 |  | 
 | class MessageSerializationCluster : public ZoneAllocated { | 
 |  public: | 
 |   explicit MessageSerializationCluster(const char* name, | 
 |                                        MessagePhase phase, | 
 |                                        intptr_t cid, | 
 |                                        bool is_canonical = false) | 
 |       : name_(name), phase_(phase), cid_(cid), is_canonical_(is_canonical) {} | 
 |   virtual ~MessageSerializationCluster() {} | 
 |  | 
 |   virtual void Trace(MessageSerializer* s, Object* object) = 0; | 
 |   virtual void WriteNodes(MessageSerializer* s) = 0; | 
 |   virtual void WriteEdges(MessageSerializer* s) {} | 
 |  | 
 |   virtual void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) {} | 
 |   virtual void WriteNodesApi(ApiMessageSerializer* s) {} | 
 |   virtual void WriteEdgesApi(ApiMessageSerializer* s) {} | 
 |  | 
 |   const char* name() const { return name_; } | 
 |   MessagePhase phase() const { return phase_; } | 
 |   intptr_t cid() const { return cid_; } | 
 |   bool is_canonical() const { return is_canonical_; } | 
 |  | 
 |  protected: | 
 |   const char* const name_; | 
 |   const MessagePhase phase_; | 
 |   const intptr_t cid_; | 
 |   const bool is_canonical_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MessageSerializationCluster); | 
 | }; | 
 |  | 
 | class MessageDeserializationCluster : public ZoneAllocated { | 
 |  public: | 
 |   explicit MessageDeserializationCluster(const char* name, | 
 |                                          bool is_canonical = false) | 
 |       : name_(name), | 
 |         is_canonical_(is_canonical), | 
 |         start_index_(0), | 
 |         stop_index_(0) {} | 
 |   virtual ~MessageDeserializationCluster() {} | 
 |  | 
 |   virtual void ReadNodes(MessageDeserializer* d) = 0; | 
 |   virtual void ReadEdges(MessageDeserializer* d) {} | 
 |   virtual ObjectPtr PostLoad(MessageDeserializer* d) { return nullptr; } | 
 |   virtual void ReadNodesApi(ApiMessageDeserializer* d) {} | 
 |   virtual void ReadEdgesApi(ApiMessageDeserializer* d) {} | 
 |   virtual void PostLoadApi(ApiMessageDeserializer* d) {} | 
 |  | 
 |   void ReadNodesWrapped(MessageDeserializer* d); | 
 |   void ReadNodesWrappedApi(ApiMessageDeserializer* d); | 
 |  | 
 |   const char* name() const { return name_; } | 
 |   bool is_canonical() const { return is_canonical_; } | 
 |  | 
 |  protected: | 
 |   ObjectPtr PostLoadAbstractType(MessageDeserializer* d); | 
 |   ObjectPtr PostLoadLinkedHash(MessageDeserializer* d); | 
 |  | 
 |   const char* const name_; | 
 |   const bool is_canonical_; | 
 |   // The range of the ref array that belongs to this cluster. | 
 |   intptr_t start_index_; | 
 |   intptr_t stop_index_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MessageDeserializationCluster); | 
 | }; | 
 |  | 
 | class BaseSerializer : public StackResource { | 
 |  public: | 
 |   BaseSerializer(Thread* thread, Zone* zone); | 
 |   ~BaseSerializer(); | 
 |  | 
 |   // Writes raw data to the stream (basic type). | 
 |   // sizeof(T) must be in {1,2,4,8}. | 
 |   template <typename T> | 
 |   void Write(T value) { | 
 |     BaseWriteStream::Raw<sizeof(T), T>::Write(&stream_, value); | 
 |   } | 
 |   void WriteUnsigned(intptr_t value) { stream_.WriteUnsigned(value); } | 
 |   void WriteWordWith32BitWrites(uword value) { | 
 |     stream_.WriteWordWith32BitWrites(value); | 
 |   } | 
 |   void WriteBytes(const void* addr, intptr_t len) { | 
 |     stream_.WriteBytes(addr, len); | 
 |   } | 
 |   void WriteAscii(const String& str) { | 
 |     intptr_t len = str.Length(); | 
 |     WriteUnsigned(len); | 
 |     for (intptr_t i = 0; i < len; i++) { | 
 |       int64_t c = str.CharAt(i); | 
 |       ASSERT(c < 128); | 
 |       Write<uint8_t>(c); | 
 |     } | 
 |     Write<uint8_t>(0); | 
 |   } | 
 |  | 
 |   MessageSerializationCluster* NewClusterForClass(intptr_t cid, | 
 |                                                   bool is_canonical); | 
 |   void WriteCluster(MessageSerializationCluster* cluster); | 
 |  | 
 |   std::unique_ptr<Message> Finish(Dart_Port dest_port, | 
 |                                   Message::Priority priority) { | 
 |     MessageFinalizableData* finalizable_data = finalizable_data_; | 
 |     finalizable_data_ = nullptr; | 
 |     finalizable_data->SerializationSucceeded(); | 
 |     intptr_t size; | 
 |     uint8_t* buffer = stream_.Steal(&size); | 
 |     return Message::New(dest_port, buffer, size, finalizable_data, priority); | 
 |   } | 
 |  | 
 |   Zone* zone() const { return zone_; } | 
 |   MessageFinalizableData* finalizable_data() const { return finalizable_data_; } | 
 |   intptr_t next_ref_index() const { return next_ref_index_; } | 
 |  | 
 |  protected: | 
 |   Zone* const zone_; | 
 |   MallocWriteStream stream_; | 
 |   MessageFinalizableData* finalizable_data_; | 
 |   GrowableArray<MessageSerializationCluster*> clusters_; | 
 |   intptr_t num_base_objects_; | 
 |   intptr_t num_written_objects_; | 
 |   intptr_t next_ref_index_; | 
 | }; | 
 |  | 
 | class MessageSerializer : public BaseSerializer { | 
 |  public: | 
 |   explicit MessageSerializer(Thread* thread); | 
 |   ~MessageSerializer(); | 
 |  | 
 |   bool MarkObjectId(ObjectPtr object, intptr_t id) { | 
 |     ASSERT(id != WeakTable::kNoValue); | 
 |     WeakTable* table; | 
 |     if (object->IsImmediateOrOldObject()) { | 
 |       table = isolate()->forward_table_old(); | 
 |     } else { | 
 |       table = isolate()->forward_table_new(); | 
 |     } | 
 |     return table->MarkValueExclusive(object, id); | 
 |   } | 
 |  | 
 |   void SetObjectId(ObjectPtr object, intptr_t id) { | 
 |     ASSERT(id != WeakTable::kNoValue); | 
 |     WeakTable* table; | 
 |     if (object->IsImmediateOrOldObject()) { | 
 |       table = isolate()->forward_table_old(); | 
 |     } else { | 
 |       table = isolate()->forward_table_new(); | 
 |     } | 
 |     table->SetValueExclusive(object, id); | 
 |   } | 
 |  | 
 |   intptr_t GetObjectId(ObjectPtr object) const { | 
 |     const WeakTable* table; | 
 |     if (object->IsImmediateOrOldObject()) { | 
 |       table = isolate()->forward_table_old(); | 
 |     } else { | 
 |       table = isolate()->forward_table_new(); | 
 |     } | 
 |     return table->GetValueExclusive(object); | 
 |   } | 
 |  | 
 |   DART_NOINLINE void AddBaseObject(ObjectPtr base_object) { | 
 |     AssignRef(base_object); | 
 |     num_base_objects_++; | 
 |   } | 
 |   DART_NOINLINE void AssignRef(ObjectPtr object) { | 
 |     SetObjectId(object, next_ref_index_); | 
 |     next_ref_index_++; | 
 |   } | 
 |   void AssignRef(Object* object) { AssignRef(object->ptr()); } | 
 |  | 
 |   void Push(ObjectPtr object); | 
 |  | 
 |   void Trace(const Object& root, Object* object); | 
 |  | 
 |   void IllegalObject(const Object& object, const char* message); | 
 |  | 
 |   void AddBaseObjects(); | 
 |   void Serialize(const Object& root); | 
 |  | 
 |   DART_NOINLINE void WriteRef(ObjectPtr object) { | 
 |     intptr_t index = GetObjectId(object); | 
 |     ASSERT(index != WeakTable::kNoValue); | 
 |     WriteUnsigned(index); | 
 |   } | 
 |  | 
 |   Thread* thread() const { | 
 |     return static_cast<Thread*>(StackResource::thread()); | 
 |   } | 
 |   Isolate* isolate() const { return thread()->isolate(); } | 
 |   IsolateGroup* isolate_group() const { return thread()->isolate_group(); } | 
 |  | 
 |   bool HasRef(ObjectPtr object) const { | 
 |     return GetObjectId(object) != WeakTable::kNoValue; | 
 |   } | 
 |  | 
 |  private: | 
 |   WeakTable* forward_table_new_; | 
 |   WeakTable* forward_table_old_; | 
 |   GrowableArray<Object*> stack_; | 
 | }; | 
 |  | 
 | class ApiMessageSerializer : public BaseSerializer { | 
 |  public: | 
 |   explicit ApiMessageSerializer(Zone* zone); | 
 |   ~ApiMessageSerializer(); | 
 |  | 
 |   bool MarkObjectId(Dart_CObject* object, intptr_t id) { | 
 |     ASSERT(id != WeakTable::kNoValue); | 
 |     return forward_table_.MarkValueExclusive( | 
 |         static_cast<ObjectPtr>(reinterpret_cast<uword>(object)), id); | 
 |   } | 
 |  | 
 |   void SetObjectId(Dart_CObject* object, intptr_t id) { | 
 |     ASSERT(id != WeakTable::kNoValue); | 
 |     forward_table_.SetValueExclusive( | 
 |         static_cast<ObjectPtr>(reinterpret_cast<uword>(object)), id); | 
 |   } | 
 |  | 
 |   intptr_t GetObjectId(Dart_CObject* object) const { | 
 |     return forward_table_.GetValueExclusive( | 
 |         static_cast<ObjectPtr>(reinterpret_cast<uword>(object))); | 
 |   } | 
 |  | 
 |   DART_NOINLINE void AddBaseObject(Dart_CObject* base_object) { | 
 |     AssignRef(base_object); | 
 |     num_base_objects_++; | 
 |   } | 
 |   DART_NOINLINE intptr_t AssignRef(Dart_CObject* object) { | 
 |     SetObjectId(object, next_ref_index_); | 
 |     return next_ref_index_++; | 
 |   } | 
 |   void ForwardRef(Dart_CObject* old, Dart_CObject* nue) { | 
 |     intptr_t id = GetObjectId(nue); | 
 |     ASSERT(id != WeakTable::kNoValue); | 
 |     SetObjectId(old, id); | 
 |     num_written_objects_--; | 
 |   } | 
 |  | 
 |   void Push(Dart_CObject* object); | 
 |  | 
 |   bool Trace(Dart_CObject* object); | 
 |  | 
 |   void AddBaseObjects(); | 
 |   bool Serialize(Dart_CObject* root); | 
 |  | 
 |   void WriteRef(Dart_CObject* object) { | 
 |     intptr_t index = GetObjectId(object); | 
 |     ASSERT(index != WeakTable::kNoValue); | 
 |     WriteUnsigned(index); | 
 |   } | 
 |  | 
 |   bool Fail(const char* message) { | 
 |     exception_message_ = message; | 
 |     return false; | 
 |   } | 
 |  | 
 |  private: | 
 |   WeakTable forward_table_; | 
 |   GrowableArray<Dart_CObject*> stack_; | 
 |   const char* exception_message_; | 
 | }; | 
 |  | 
 | class BaseDeserializer : public ValueObject { | 
 |  public: | 
 |   BaseDeserializer(Zone* zone, Message* message); | 
 |   ~BaseDeserializer(); | 
 |  | 
 |   // 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 ReadUnsigned() { return stream_.ReadUnsigned(); } | 
 |   uword ReadWordWith32BitReads() { return stream_.ReadWordWith32BitReads(); } | 
 |   void ReadBytes(void* addr, intptr_t len) { stream_.ReadBytes(addr, len); } | 
 |   const char* ReadAscii() { | 
 |     intptr_t len = ReadUnsigned(); | 
 |     const char* result = reinterpret_cast<const char*>(CurrentBufferAddress()); | 
 |     Advance(len + 1); | 
 |     return result; | 
 |   } | 
 |  | 
 |   const uint8_t* CurrentBufferAddress() const { | 
 |     return stream_.AddressOfCurrentPosition(); | 
 |   } | 
 |  | 
 |   void Advance(intptr_t value) { stream_.Advance(value); } | 
 |  | 
 |   MessageDeserializationCluster* ReadCluster(); | 
 |  | 
 |   Zone* zone() const { return zone_; } | 
 |   intptr_t next_index() const { return next_ref_index_; } | 
 |   MessageFinalizableData* finalizable_data() const { return finalizable_data_; } | 
 |  | 
 |  protected: | 
 |   Zone* zone_; | 
 |   ReadStream stream_; | 
 |   MessageFinalizableData* finalizable_data_; | 
 |   intptr_t next_ref_index_; | 
 | }; | 
 |  | 
 | class MessageDeserializer : public BaseDeserializer { | 
 |  public: | 
 |   MessageDeserializer(Thread* thread, Message* message) | 
 |       : BaseDeserializer(thread->zone(), message), | 
 |         thread_(thread), | 
 |         refs_(Array::Handle(thread->zone())) {} | 
 |   ~MessageDeserializer() {} | 
 |  | 
 |   DART_NOINLINE void AddBaseObject(ObjectPtr base_object) { | 
 |     AssignRef(base_object); | 
 |   } | 
 |   void AssignRef(ObjectPtr object) { | 
 |     refs_.untag()->set_element(next_ref_index_, object); | 
 |     next_ref_index_++; | 
 |   } | 
 |  | 
 |   ObjectPtr Ref(intptr_t index) const { | 
 |     ASSERT(index > 0); | 
 |     ASSERT(index <= next_ref_index_); | 
 |     return refs_.At(index); | 
 |   } | 
 |   void UpdateRef(intptr_t index, const Object& new_object) { | 
 |     ASSERT(index > 0); | 
 |     ASSERT(index <= next_ref_index_); | 
 |     refs_.SetAt(index, new_object); | 
 |   } | 
 |  | 
 |   ObjectPtr ReadRef() { return Ref(ReadUnsigned()); } | 
 |  | 
 |   void AddBaseObjects(); | 
 |   ObjectPtr Deserialize(); | 
 |  | 
 |   Thread* thread() const { return thread_; } | 
 |   IsolateGroup* isolate_group() const { return thread_->isolate_group(); } | 
 |   ArrayPtr refs() const { return refs_.ptr(); } | 
 |  | 
 |  private: | 
 |   Thread* const thread_; | 
 |   Array& refs_; | 
 | }; | 
 |  | 
 | class ApiMessageDeserializer : public BaseDeserializer { | 
 |  public: | 
 |   ApiMessageDeserializer(Zone* zone, Message* message) | 
 |       : BaseDeserializer(zone, message), refs_(nullptr) {} | 
 |   ~ApiMessageDeserializer() {} | 
 |  | 
 |   void AddBaseObject(Dart_CObject* base_object) { AssignRef(base_object); } | 
 |   void AssignRef(Dart_CObject* object) { | 
 |     refs_[next_ref_index_] = object; | 
 |     next_ref_index_++; | 
 |   } | 
 |  | 
 |   Dart_CObject* Allocate(Dart_CObject_Type type) { | 
 |     Dart_CObject* result = zone()->Alloc<Dart_CObject>(1); | 
 |     result->type = type; | 
 |     return result; | 
 |   } | 
 |  | 
 |   Dart_CObject* Ref(intptr_t index) const { | 
 |     ASSERT(index > 0); | 
 |     ASSERT(index <= next_ref_index_); | 
 |     return refs_[index]; | 
 |   } | 
 |  | 
 |   Dart_CObject* ReadRef() { return Ref(ReadUnsigned()); } | 
 |  | 
 |   void AddBaseObjects(); | 
 |   Dart_CObject* Deserialize(); | 
 |  | 
 |  private: | 
 |   Dart_CObject** refs_; | 
 | }; | 
 |  | 
 | void MessageDeserializationCluster::ReadNodesWrapped(MessageDeserializer* d) { | 
 |   start_index_ = d->next_index(); | 
 |   this->ReadNodes(d); | 
 |   stop_index_ = d->next_index(); | 
 | } | 
 |  | 
 | void MessageDeserializationCluster::ReadNodesWrappedApi( | 
 |     ApiMessageDeserializer* d) { | 
 |   start_index_ = d->next_index(); | 
 |   this->ReadNodesApi(d); | 
 |   stop_index_ = d->next_index(); | 
 | } | 
 |  | 
 | ObjectPtr MessageDeserializationCluster::PostLoadAbstractType( | 
 |     MessageDeserializer* d) { | 
 |   ClassFinalizer::FinalizationKind finalization = | 
 |       is_canonical() ? ClassFinalizer::kCanonicalize | 
 |                      : ClassFinalizer::kFinalize; | 
 |   AbstractType& type = AbstractType::Handle(d->zone()); | 
 |   Code& code = Code::Handle(d->zone()); | 
 |   for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |     type ^= d->Ref(id); | 
 |  | 
 |     code = TypeTestingStubGenerator::DefaultCodeForType(type); | 
 |     type.InitializeTypeTestingStubNonAtomic(code); | 
 |  | 
 |     type ^= ClassFinalizer::FinalizeType(type, finalization); | 
 |     d->UpdateRef(id, type); | 
 |   } | 
 |   return nullptr; | 
 | } | 
 |  | 
 | ObjectPtr MessageDeserializationCluster::PostLoadLinkedHash( | 
 |     MessageDeserializer* d) { | 
 |   ASSERT(!is_canonical()); | 
 |   Array& maps = Array::Handle(d->zone(), d->refs()); | 
 |   maps = maps.Slice(start_index_, stop_index_ - start_index_, | 
 |                     /*with_type_argument=*/false); | 
 |   return DartLibraryCalls::RehashObjectsInDartCollection(d->thread(), maps); | 
 | } | 
 |  | 
 | class ClassMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   ClassMessageSerializationCluster() | 
 |       : MessageSerializationCluster("Class", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kClassCid), | 
 |         objects_() {} | 
 |   ~ClassMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Class* cls = static_cast<Class*>(object); | 
 |     objects_.Add(cls); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     Library& lib = Library::Handle(s->zone()); | 
 |     String& str = String::Handle(s->zone()); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Class* cls = objects_[i]; | 
 |       s->AssignRef(cls); | 
 |       intptr_t cid = cls->id(); | 
 |       if (cid < kNumPredefinedCids) { | 
 |         ASSERT(cid != 0); | 
 |         s->WriteUnsigned(cid); | 
 |       } else { | 
 |         s->WriteUnsigned(0); | 
 |         lib = cls->library(); | 
 |         str = lib.url(); | 
 |         s->WriteAscii(str); | 
 |         str = cls->Name(); | 
 |         s->WriteAscii(str); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Class*> objects_; | 
 | }; | 
 |  | 
 | class ClassMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   ClassMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("Class") {} | 
 |   ~ClassMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     auto* class_table = d->isolate_group()->class_table(); | 
 |     String& uri = String::Handle(d->zone()); | 
 |     Library& lib = Library::Handle(d->zone()); | 
 |     String& name = String::Handle(d->zone()); | 
 |     Class& cls = Class::Handle(d->zone()); | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t cid = d->ReadUnsigned(); | 
 |       if (cid != 0) { | 
 |         cls = class_table->At(cid); | 
 |       } else { | 
 |         uri = String::New(d->ReadAscii());   // Library URI. | 
 |         name = String::New(d->ReadAscii());  // Class name. | 
 |         lib = Library::LookupLibrary(d->thread(), uri); | 
 |         if (UNLIKELY(lib.IsNull())) { | 
 |           FATAL("Not found: %s %s\n", uri.ToCString(), name.ToCString()); | 
 |         } | 
 |         if (name.Equals(Symbols::TopLevel())) { | 
 |           cls = lib.toplevel_class(); | 
 |         } else { | 
 |           cls = lib.LookupClass(name); | 
 |         } | 
 |         if (UNLIKELY(cls.IsNull())) { | 
 |           FATAL("Not found: %s %s\n", uri.ToCString(), name.ToCString()); | 
 |         } | 
 |         cls.EnsureIsFinalized(d->thread()); | 
 |       } | 
 |       d->AssignRef(cls.ptr()); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t cid = d->ReadUnsigned(); | 
 |       if (cid == 0) { | 
 |         d->ReadAscii();  // Library URI. | 
 |         d->ReadAscii();  // Class name. | 
 |       } | 
 |       d->AssignRef(nullptr); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class TypeArgumentsMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit TypeArgumentsMessageSerializationCluster(bool is_canonical) | 
 |       : MessageSerializationCluster("TypeArguments", | 
 |                                     MessagePhase::kTypes, | 
 |                                     kTypeArgumentsCid, | 
 |                                     is_canonical) {} | 
 |   ~TypeArgumentsMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     TypeArguments* type_args = static_cast<TypeArguments*>(object); | 
 |     objects_.Add(type_args); | 
 |  | 
 |     s->Push(type_args->untag()->instantiations()); | 
 |     intptr_t length = Smi::Value(type_args->untag()->length()); | 
 |     for (intptr_t i = 0; i < length; i++) { | 
 |       s->Push(type_args->untag()->element(i)); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TypeArguments* type_args = objects_[i]; | 
 |       s->AssignRef(type_args); | 
 |       intptr_t length = Smi::Value(type_args->untag()->length()); | 
 |       s->WriteUnsigned(length); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TypeArguments* type_args = objects_[i]; | 
 |       intptr_t hash = Smi::Value(type_args->untag()->hash()); | 
 |       s->Write<int32_t>(hash); | 
 |       const intptr_t nullability = | 
 |           Smi::Value(type_args->untag()->nullability()); | 
 |       s->WriteUnsigned(nullability); | 
 |  | 
 |       intptr_t length = Smi::Value(type_args->untag()->length()); | 
 |       s->WriteUnsigned(length); | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         s->WriteRef(type_args->untag()->element(j)); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<TypeArguments*> objects_; | 
 | }; | 
 |  | 
 | class TypeArgumentsMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit TypeArgumentsMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("TypeArguments", is_canonical) {} | 
 |   ~TypeArgumentsMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       d->AssignRef(TypeArguments::New(length)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       TypeArgumentsPtr type_args = static_cast<TypeArgumentsPtr>(d->Ref(id)); | 
 |  | 
 |       type_args->untag()->hash_ = Smi::New(d->Read<int32_t>()); | 
 |       type_args->untag()->nullability_ = Smi::New(d->ReadUnsigned()); | 
 |  | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         type_args->untag()->types()[j] = | 
 |             static_cast<AbstractTypePtr>(d->ReadRef()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     if (is_canonical()) { | 
 |       TypeArguments& type_args = TypeArguments::Handle(d->zone()); | 
 |       for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |         type_args ^= d->Ref(id); | 
 |         type_args ^= type_args.Canonicalize(d->thread()); | 
 |         d->UpdateRef(id, type_args); | 
 |       } | 
 |     } | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->ReadUnsigned();  // Length. | 
 |       d->AssignRef(nullptr); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdgesApi(ApiMessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       d->Read<int32_t>();  // Hash. | 
 |       d->ReadUnsigned();   // Nullability. | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         d->ReadRef();  // Element. | 
 |       } | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class TypeMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit TypeMessageSerializationCluster(bool is_canonical) | 
 |       : MessageSerializationCluster("Type", | 
 |                                     MessagePhase::kTypes, | 
 |                                     kTypeCid, | 
 |                                     is_canonical) {} | 
 |   ~TypeMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Type* type = static_cast<Type*>(object); | 
 |  | 
 |     if (!type->IsTypeClassAllowedBySpawnUri()) { | 
 |       s->IllegalObject(*object, "is a Type"); | 
 |     } | 
 |  | 
 |     objects_.Add(type); | 
 |  | 
 |     s->Push(type->type_class()); | 
 |     s->Push(type->arguments()); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Type* type = objects_[i]; | 
 |       s->AssignRef(type); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Type* type = objects_[i]; | 
 |       s->WriteRef(type->type_class()); | 
 |       s->WriteRef(type->arguments()); | 
 |       s->Write<uint8_t>(static_cast<uint8_t>(type->nullability())); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Type*> objects_; | 
 | }; | 
 |  | 
 | class TypeMessageDeserializationCluster : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit TypeMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("Type", is_canonical) {} | 
 |   ~TypeMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->AssignRef(Type::New()); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     Class& cls = Class::Handle(d->zone()); | 
 |     Type& type = Type::Handle(d->zone()); | 
 |     TypeArguments& type_args = TypeArguments::Handle(d->zone()); | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       type ^= d->Ref(id); | 
 |       cls ^= d->ReadRef(); | 
 |       type.set_type_class(cls); | 
 |       type_args ^= d->ReadRef(); | 
 |       type.set_arguments(type_args); | 
 |       type.untag()->set_hash(Smi::New(0)); | 
 |       type.set_nullability(static_cast<Nullability>(d->Read<uint8_t>())); | 
 |       type.SetIsFinalized(); | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { return PostLoadAbstractType(d); } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->AssignRef(nullptr); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdgesApi(ApiMessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       d->ReadRef();        // Class. | 
 |       d->ReadRef();        // Type arguments. | 
 |       d->Read<uint8_t>();  // Nullability. | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class SmiMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit SmiMessageSerializationCluster(Zone* zone) | 
 |       : MessageSerializationCluster("Smi", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kSmiCid, | 
 |                                     true), | 
 |         objects_(zone, 0) {} | 
 |   ~SmiMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Smi* smi = static_cast<Smi*>(object); | 
 |     objects_.Add(smi); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Smi* smi = static_cast<Smi*>(objects_[i]); | 
 |       s->AssignRef(smi); | 
 |       s->Write<intptr_t>(smi->Value()); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<Smi*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* smi = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(smi); | 
 |       intptr_t value = smi->type == Dart_CObject_kInt32 ? smi->value.as_int32 | 
 |                                                         : smi->value.as_int64; | 
 |       s->Write<intptr_t>(value); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Smi*> objects_; | 
 | }; | 
 |  | 
 | class SmiMessageDeserializationCluster : public MessageDeserializationCluster { | 
 |  public: | 
 |   SmiMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("Smi", true) {} | 
 |   ~SmiMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->AssignRef(Smi::New(d->Read<intptr_t>())); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t value = d->Read<intptr_t>(); | 
 |       Dart_CObject* smi; | 
 |       if ((kMinInt32 <= value) && (value <= kMaxInt32)) { | 
 |         smi = d->Allocate(Dart_CObject_kInt32); | 
 |         smi->value.as_int32 = value; | 
 |       } else { | 
 |         smi = d->Allocate(Dart_CObject_kInt64); | 
 |         smi->value.as_int64 = value; | 
 |       } | 
 |       d->AssignRef(smi); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class MintMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit MintMessageSerializationCluster(Zone* zone, bool is_canonical) | 
 |       : MessageSerializationCluster("Mint", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kMintCid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~MintMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Mint* mint = static_cast<Mint*>(object); | 
 |     objects_.Add(mint); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Mint* mint = static_cast<Mint*>(objects_[i]); | 
 |       s->AssignRef(mint); | 
 |       s->Write<int64_t>(mint->value()); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<Mint*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* mint = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(mint); | 
 |       int64_t value = mint->type == Dart_CObject_kInt32 ? mint->value.as_int32 | 
 |                                                         : mint->value.as_int64; | 
 |       s->Write<int64_t>(value); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Mint*> objects_; | 
 | }; | 
 |  | 
 | class MintMessageDeserializationCluster : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit MintMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("int", is_canonical) {} | 
 |   ~MintMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       int64_t value = d->Read<int64_t>(); | 
 |       d->AssignRef(is_canonical() ? Mint::NewCanonical(value) | 
 |                                   : Mint::New(value)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       int64_t value = d->Read<int64_t>(); | 
 |       Dart_CObject* mint; | 
 |       if ((kMinInt32 <= value) && (value <= kMaxInt32)) { | 
 |         mint = d->Allocate(Dart_CObject_kInt32); | 
 |         mint->value.as_int32 = value; | 
 |       } else { | 
 |         mint = d->Allocate(Dart_CObject_kInt64); | 
 |         mint->value.as_int64 = value; | 
 |       } | 
 |       d->AssignRef(mint); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class DoubleMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit DoubleMessageSerializationCluster(Zone* zone, bool is_canonical) | 
 |       : MessageSerializationCluster("double", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kDoubleCid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |  | 
 |   ~DoubleMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Double* dbl = static_cast<Double*>(object); | 
 |     objects_.Add(dbl); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Double* dbl = objects_[i]; | 
 |       s->AssignRef(dbl); | 
 |       s->Write<double>(dbl->untag()->value_); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<Double*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* dbl = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(dbl); | 
 |       s->Write<double>(dbl->value.as_double); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Double*> objects_; | 
 | }; | 
 |  | 
 | class DoubleMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit DoubleMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("double", is_canonical) {} | 
 |   ~DoubleMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       double value = d->Read<double>(); | 
 |       d->AssignRef(is_canonical() ? Double::NewCanonical(value) | 
 |                                   : Double::New(value)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* dbl = d->Allocate(Dart_CObject_kDouble); | 
 |       dbl->value.as_double = d->Read<double>(); | 
 |       d->AssignRef(dbl); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class GrowableObjectArrayMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   GrowableObjectArrayMessageSerializationCluster() | 
 |       : MessageSerializationCluster("GrowableObjectArray", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     kGrowableObjectArrayCid) {} | 
 |   ~GrowableObjectArrayMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     GrowableObjectArray* array = static_cast<GrowableObjectArray*>(object); | 
 |     objects_.Add(array); | 
 |  | 
 |     // Compensation for bogus type prefix optimization. | 
 |     TypeArguments& args = | 
 |         TypeArguments::Handle(s->zone(), array->untag()->type_arguments()); | 
 |     if (!args.IsNull() && (args.Length() != 1)) { | 
 |       args = args.TruncatedTo(1); | 
 |       array->untag()->set_type_arguments(args.ptr()); | 
 |     } | 
 |  | 
 |     s->Push(array->untag()->type_arguments()); | 
 |     for (intptr_t i = 0, n = array->Length(); i < n; i++) { | 
 |       s->Push(array->At(i)); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       GrowableObjectArray* array = objects_[i]; | 
 |       s->WriteUnsigned(array->Length()); | 
 |       s->AssignRef(array); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       GrowableObjectArray* array = objects_[i]; | 
 |       s->WriteRef(array->untag()->type_arguments()); | 
 |       for (intptr_t i = 0, n = array->Length(); i < n; i++) { | 
 |         s->WriteRef(array->At(i)); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<GrowableObjectArray*> objects_; | 
 | }; | 
 |  | 
 | class GrowableObjectArrayMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   GrowableObjectArrayMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("GrowableObjectArray") {} | 
 |   ~GrowableObjectArrayMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     GrowableObjectArray& array = GrowableObjectArray::Handle(d->zone()); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       array = GrowableObjectArray::New(length);  // Here length is capacity. | 
 |       array.SetLength(length); | 
 |       d->AssignRef(array.ptr()); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     GrowableObjectArray& array = GrowableObjectArray::Handle(d->zone()); | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       array ^= d->Ref(id); | 
 |       array.untag()->set_type_arguments( | 
 |           static_cast<TypeArgumentsPtr>(d->ReadRef())); | 
 |       for (intptr_t i = 0, n = array.Length(); i < n; i++) { | 
 |         array.untag()->data()->untag()->set_element(i, d->ReadRef()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     ASSERT(!is_canonical()); | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* array = d->Allocate(Dart_CObject_kArray); | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       array->value.as_array.length = length; | 
 |       if (length > 0) { | 
 |         array->value.as_array.values = d->zone()->Alloc<Dart_CObject*>(length); | 
 |       } else { | 
 |         ASSERT(length == 0); | 
 |         array->value.as_array.values = nullptr; | 
 |       } | 
 |       d->AssignRef(array); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdgesApi(ApiMessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       Dart_CObject* array = d->Ref(id); | 
 |       intptr_t length = array->value.as_array.length; | 
 |       d->ReadRef();  // type_arguments | 
 |       for (intptr_t i = 0; i < length; i++) { | 
 |         array->value.as_array.values[i] = d->ReadRef(); | 
 |       } | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class TypedDataMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit TypedDataMessageSerializationCluster(Zone* zone, intptr_t cid) | 
 |       : MessageSerializationCluster("TypedData", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     cid), | 
 |         objects_(zone, 0) {} | 
 |   ~TypedDataMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     TypedData* data = static_cast<TypedData*>(object); | 
 |     objects_.Add(data); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     intptr_t element_size = TypedData::ElementSizeInBytes(cid_); | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TypedData* data = objects_[i]; | 
 |       s->AssignRef(data); | 
 |       intptr_t length = data->Length(); | 
 |       s->WriteUnsigned(length); | 
 |       NoSafepointScope no_safepoint; | 
 |       uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data()); | 
 |       s->WriteBytes(cdata, length * element_size); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<TypedData*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t element_size = TypedData::ElementSizeInBytes(cid_); | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(data); | 
 |       intptr_t length = data->value.as_external_typed_data.length; | 
 |       s->WriteUnsigned(length); | 
 |       const uint8_t* cdata = data->value.as_typed_data.values; | 
 |       s->WriteBytes(cdata, length * element_size); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<TypedData*> objects_; | 
 | }; | 
 |  | 
 | class TypedDataMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit TypedDataMessageDeserializationCluster(intptr_t cid) | 
 |       : MessageDeserializationCluster("TypedData"), cid_(cid) {} | 
 |   ~TypedDataMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     intptr_t element_size = TypedData::ElementSizeInBytes(cid_); | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     TypedData& data = TypedData::Handle(d->zone()); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       data = TypedData::New(cid_, length); | 
 |       d->AssignRef(data.ptr()); | 
 |       const intptr_t length_in_bytes = length * element_size; | 
 |       NoSafepointScope no_safepoint; | 
 |       d->ReadBytes(data.untag()->data(), length_in_bytes); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     Dart_TypedData_Type type; | 
 |     switch (cid_) { | 
 |       case kTypedDataInt8ArrayCid: | 
 |         type = Dart_TypedData_kInt8; | 
 |         break; | 
 |       case kTypedDataUint8ArrayCid: | 
 |         type = Dart_TypedData_kUint8; | 
 |         break; | 
 |       case kTypedDataUint8ClampedArrayCid: | 
 |         type = Dart_TypedData_kUint8Clamped; | 
 |         break; | 
 |       case kTypedDataInt16ArrayCid: | 
 |         type = Dart_TypedData_kInt16; | 
 |         break; | 
 |       case kTypedDataUint16ArrayCid: | 
 |         type = Dart_TypedData_kUint16; | 
 |         break; | 
 |       case kTypedDataInt32ArrayCid: | 
 |         type = Dart_TypedData_kInt32; | 
 |         break; | 
 |       case kTypedDataUint32ArrayCid: | 
 |         type = Dart_TypedData_kUint32; | 
 |         break; | 
 |       case kTypedDataInt64ArrayCid: | 
 |         type = Dart_TypedData_kInt64; | 
 |         break; | 
 |       case kTypedDataUint64ArrayCid: | 
 |         type = Dart_TypedData_kUint64; | 
 |         break; | 
 |       case kTypedDataFloat32ArrayCid: | 
 |         type = Dart_TypedData_kFloat32; | 
 |         break; | 
 |       case kTypedDataFloat64ArrayCid: | 
 |         type = Dart_TypedData_kFloat64; | 
 |         break; | 
 |       case kTypedDataInt32x4ArrayCid: | 
 |         type = Dart_TypedData_kInt32x4; | 
 |         break; | 
 |       case kTypedDataFloat32x4ArrayCid: | 
 |         type = Dart_TypedData_kFloat32x4; | 
 |         break; | 
 |       case kTypedDataFloat64x2ArrayCid: | 
 |         type = Dart_TypedData_kFloat64x2; | 
 |         break; | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |  | 
 |     intptr_t element_size = TypedData::ElementSizeInBytes(cid_); | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = d->Allocate(Dart_CObject_kTypedData); | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       data->value.as_typed_data.type = type; | 
 |       data->value.as_typed_data.length = length; | 
 |       if (length == 0) { | 
 |         data->value.as_typed_data.values = nullptr; | 
 |       } else { | 
 |         data->value.as_typed_data.values = d->CurrentBufferAddress(); | 
 |         d->Advance(length * element_size); | 
 |       } | 
 |       d->AssignRef(data); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | // This function's name can appear in Observatory. | 
 | static void IsolateMessageTypedDataFinalizer(void* isolate_callback_data, | 
 |                                              void* buffer) { | 
 |   free(buffer); | 
 | } | 
 |  | 
 | class ExternalTypedDataMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit ExternalTypedDataMessageSerializationCluster(Zone* zone, | 
 |                                                         intptr_t cid) | 
 |       : MessageSerializationCluster("ExternalTypedData", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     cid), | 
 |         objects_(zone, 0) {} | 
 |   ~ExternalTypedDataMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     ExternalTypedData* data = static_cast<ExternalTypedData*>(object); | 
 |     objects_.Add(data); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_); | 
 |  | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       ExternalTypedData* data = objects_[i]; | 
 |       s->AssignRef(data); | 
 |       intptr_t length = Smi::Value(data->untag()->length_); | 
 |       s->WriteUnsigned(length); | 
 |  | 
 |       intptr_t length_in_bytes = length * element_size; | 
 |       void* passed_data = malloc(length_in_bytes); | 
 |       memmove(passed_data, data->untag()->data_, length_in_bytes); | 
 |       s->finalizable_data()->Put(length_in_bytes, | 
 |                                  passed_data,  // data | 
 |                                  passed_data,  // peer, | 
 |                                  IsolateMessageTypedDataFinalizer); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<ExternalTypedData*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_); | 
 |  | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(data); | 
 |  | 
 |       intptr_t length = data->value.as_external_typed_data.length; | 
 |       s->WriteUnsigned(length); | 
 |  | 
 |       s->finalizable_data()->Put(length * element_size, | 
 |                                  data->value.as_external_typed_data.data, | 
 |                                  data->value.as_external_typed_data.peer, | 
 |                                  data->value.as_external_typed_data.callback); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<ExternalTypedData*> objects_; | 
 | }; | 
 |  | 
 | class ExternalTypedDataMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit ExternalTypedDataMessageDeserializationCluster(intptr_t cid) | 
 |       : MessageDeserializationCluster("ExternalTypedData"), cid_(cid) {} | 
 |   ~ExternalTypedDataMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_); | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     ExternalTypedData& data = ExternalTypedData::Handle(d->zone()); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       FinalizableData finalizable_data = d->finalizable_data()->Take(); | 
 |       data = ExternalTypedData::New( | 
 |           cid_, reinterpret_cast<uint8_t*>(finalizable_data.data), length); | 
 |       intptr_t external_size = length * element_size; | 
 |       data.AddFinalizer(finalizable_data.peer, finalizable_data.callback, | 
 |                         external_size); | 
 |       d->AssignRef(data.ptr()); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     Dart_TypedData_Type type; | 
 |     switch (cid_) { | 
 |       case kExternalTypedDataInt8ArrayCid: | 
 |         type = Dart_TypedData_kInt8; | 
 |         break; | 
 |       case kExternalTypedDataUint8ArrayCid: | 
 |         type = Dart_TypedData_kUint8; | 
 |         break; | 
 |       case kExternalTypedDataUint8ClampedArrayCid: | 
 |         type = Dart_TypedData_kUint8Clamped; | 
 |         break; | 
 |       case kExternalTypedDataInt16ArrayCid: | 
 |         type = Dart_TypedData_kInt16; | 
 |         break; | 
 |       case kExternalTypedDataUint16ArrayCid: | 
 |         type = Dart_TypedData_kUint16; | 
 |         break; | 
 |       case kExternalTypedDataInt32ArrayCid: | 
 |         type = Dart_TypedData_kInt32; | 
 |         break; | 
 |       case kExternalTypedDataUint32ArrayCid: | 
 |         type = Dart_TypedData_kUint32; | 
 |         break; | 
 |       case kExternalTypedDataInt64ArrayCid: | 
 |         type = Dart_TypedData_kInt64; | 
 |         break; | 
 |       case kExternalTypedDataUint64ArrayCid: | 
 |         type = Dart_TypedData_kUint64; | 
 |         break; | 
 |       case kExternalTypedDataFloat32ArrayCid: | 
 |         type = Dart_TypedData_kFloat32; | 
 |         break; | 
 |       case kExternalTypedDataFloat64ArrayCid: | 
 |         type = Dart_TypedData_kFloat64; | 
 |         break; | 
 |       case kExternalTypedDataInt32x4ArrayCid: | 
 |         type = Dart_TypedData_kInt32x4; | 
 |         break; | 
 |       case kExternalTypedDataFloat32x4ArrayCid: | 
 |         type = Dart_TypedData_kFloat32x4; | 
 |         break; | 
 |       case kExternalTypedDataFloat64x2ArrayCid: | 
 |         type = Dart_TypedData_kFloat64x2; | 
 |         break; | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |  | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = d->Allocate(Dart_CObject_kTypedData); | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       FinalizableData finalizable_data = d->finalizable_data()->Get(); | 
 |       data->value.as_typed_data.type = type; | 
 |       data->value.as_typed_data.length = length; | 
 |       data->value.as_typed_data.values = | 
 |           reinterpret_cast<uint8_t*>(finalizable_data.data); | 
 |       d->AssignRef(data); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | class NativePointerMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit NativePointerMessageSerializationCluster(Zone* zone) | 
 |       : MessageSerializationCluster("NativePointer", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     kNativePointer), | 
 |         objects_(zone, 0) {} | 
 |   ~NativePointerMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { UNREACHABLE(); } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { UNREACHABLE(); } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(object); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = objects_[i]; | 
 |       s->AssignRef(data); | 
 |  | 
 |       s->finalizable_data()->Put( | 
 |           data->value.as_native_pointer.size, | 
 |           reinterpret_cast<void*>(data->value.as_native_pointer.ptr), | 
 |           reinterpret_cast<void*>(data->value.as_native_pointer.ptr), | 
 |           data->value.as_native_pointer.callback); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Dart_CObject*> objects_; | 
 | }; | 
 |  | 
 | class NativePointerMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   NativePointerMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("NativePointer"), cid_(kNativePointer) {} | 
 |   ~NativePointerMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       FinalizableData finalizable_data = d->finalizable_data()->Take(); | 
 |       intptr_t ptr = reinterpret_cast<intptr_t>(finalizable_data.data); | 
 |       d->AssignRef(Integer::New(ptr)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { UNREACHABLE(); } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | enum TypedDataViewFormat { | 
 |   kTypedDataViewFromC, | 
 |   kTypedDataViewFromDart, | 
 | }; | 
 |  | 
 | class TypedDataViewMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit TypedDataViewMessageSerializationCluster(Zone* zone, intptr_t cid) | 
 |       : MessageSerializationCluster("TypedDataView", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     cid), | 
 |         objects_(zone, 0) {} | 
 |   ~TypedDataViewMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     TypedDataView* view = static_cast<TypedDataView*>(object); | 
 |     objects_.Add(view); | 
 |  | 
 |     s->Push(view->untag()->length()); | 
 |     s->Push(view->untag()->typed_data()); | 
 |     s->Push(view->untag()->offset_in_bytes()); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     s->Write<TypedDataViewFormat>(kTypedDataViewFromDart); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TypedDataView* view = objects_[i]; | 
 |       s->AssignRef(view); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TypedDataView* view = objects_[i]; | 
 |       s->WriteRef(view->untag()->length()); | 
 |       s->WriteRef(view->untag()->typed_data()); | 
 |       s->WriteRef(view->untag()->offset_in_bytes()); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     ASSERT(object->type == Dart_CObject_kUnmodifiableExternalTypedData); | 
 |     objects_.Add(reinterpret_cast<TypedDataView*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t element_size = TypedDataView::ElementSizeInBytes(cid_); | 
 |  | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     s->Write<TypedDataViewFormat>(kTypedDataViewFromC); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(data); | 
 |  | 
 |       intptr_t length = data->value.as_external_typed_data.length; | 
 |       s->WriteUnsigned(length); | 
 |  | 
 |       s->finalizable_data()->Put(length * element_size, | 
 |                                  data->value.as_external_typed_data.data, | 
 |                                  data->value.as_external_typed_data.peer, | 
 |                                  data->value.as_external_typed_data.callback); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<TypedDataView*> objects_; | 
 | }; | 
 |  | 
 | class TypedDataViewMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit TypedDataViewMessageDeserializationCluster(intptr_t cid) | 
 |       : MessageDeserializationCluster("TypedDataView"), cid_(cid) {} | 
 |   ~TypedDataViewMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     format_ = d->Read<TypedDataViewFormat>(); | 
 |     if (format_ == kTypedDataViewFromC) { | 
 |       intptr_t view_cid = cid_; | 
 |       ASSERT(IsUnmodifiableTypedDataViewClassId(view_cid)); | 
 |       intptr_t backing_cid = cid_ - kTypedDataCidRemainderUnmodifiable + | 
 |                              kTypedDataCidRemainderExternal; | 
 |       ASSERT(IsExternalTypedDataClassId(backing_cid)); | 
 |       intptr_t element_size = | 
 |           ExternalTypedData::ElementSizeInBytes(backing_cid); | 
 |       ExternalTypedData& data = ExternalTypedData::Handle(d->zone()); | 
 |       TypedDataView& view = TypedDataView::Handle(d->zone()); | 
 |       for (intptr_t i = 0; i < count; i++) { | 
 |         intptr_t length = d->ReadUnsigned(); | 
 |         FinalizableData finalizable_data = d->finalizable_data()->Take(); | 
 |         data = ExternalTypedData::New( | 
 |             backing_cid, reinterpret_cast<uint8_t*>(finalizable_data.data), | 
 |             length); | 
 |         data.SetImmutable();  // Can pass by reference. | 
 |         intptr_t external_size = length * element_size; | 
 |         data.AddFinalizer(finalizable_data.peer, finalizable_data.callback, | 
 |                           external_size); | 
 |         view = TypedDataView::New(view_cid, data, 0, length); | 
 |         d->AssignRef(data.ptr()); | 
 |       } | 
 |     } else { | 
 |       for (intptr_t i = 0; i < count; i++) { | 
 |         d->AssignRef(TypedDataView::New(cid_)); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     if (format_ == kTypedDataViewFromC) return; | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       TypedDataViewPtr view = static_cast<TypedDataViewPtr>(d->Ref(id)); | 
 |       view->untag()->set_length(static_cast<SmiPtr>(d->ReadRef())); | 
 |       view->untag()->set_typed_data( | 
 |           static_cast<TypedDataBasePtr>(d->ReadRef())); | 
 |       view->untag()->set_offset_in_bytes(static_cast<SmiPtr>(d->ReadRef())); | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     if (format_ == kTypedDataViewFromC) return nullptr; | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       TypedDataViewPtr view = static_cast<TypedDataViewPtr>(d->Ref(id)); | 
 |       view->untag()->RecomputeDataField(); | 
 |     } | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   struct Dart_CTypedDataView : public Dart_CObject { | 
 |     Dart_CObject* length; | 
 |     Dart_CObject* typed_data; | 
 |     Dart_CObject* offset_in_bytes; | 
 |   }; | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     format_ = d->Read<TypedDataViewFormat>(); | 
 |     if (format_ == kTypedDataViewFromC) { | 
 |       Dart_TypedData_Type type; | 
 |       switch (cid_) { | 
 |         case kUnmodifiableTypedDataInt8ArrayViewCid: | 
 |           type = Dart_TypedData_kInt8; | 
 |           break; | 
 |         case kUnmodifiableTypedDataUint8ArrayViewCid: | 
 |           type = Dart_TypedData_kUint8; | 
 |           break; | 
 |         case kUnmodifiableTypedDataUint8ClampedArrayViewCid: | 
 |           type = Dart_TypedData_kUint8Clamped; | 
 |           break; | 
 |         case kUnmodifiableTypedDataInt16ArrayViewCid: | 
 |           type = Dart_TypedData_kInt16; | 
 |           break; | 
 |         case kUnmodifiableTypedDataUint16ArrayViewCid: | 
 |           type = Dart_TypedData_kUint16; | 
 |           break; | 
 |         case kUnmodifiableTypedDataInt32ArrayViewCid: | 
 |           type = Dart_TypedData_kInt32; | 
 |           break; | 
 |         case kUnmodifiableTypedDataUint32ArrayViewCid: | 
 |           type = Dart_TypedData_kUint32; | 
 |           break; | 
 |         case kUnmodifiableTypedDataInt64ArrayViewCid: | 
 |           type = Dart_TypedData_kInt64; | 
 |           break; | 
 |         case kUnmodifiableTypedDataUint64ArrayViewCid: | 
 |           type = Dart_TypedData_kUint64; | 
 |           break; | 
 |         case kUnmodifiableTypedDataFloat32ArrayViewCid: | 
 |           type = Dart_TypedData_kFloat32; | 
 |           break; | 
 |         case kUnmodifiableTypedDataFloat64ArrayViewCid: | 
 |           type = Dart_TypedData_kFloat64; | 
 |           break; | 
 |         case kUnmodifiableTypedDataInt32x4ArrayViewCid: | 
 |           type = Dart_TypedData_kInt32x4; | 
 |           break; | 
 |         case kUnmodifiableTypedDataFloat32x4ArrayViewCid: | 
 |           type = Dart_TypedData_kFloat32x4; | 
 |           break; | 
 |         case kUnmodifiableTypedDataFloat64x2ArrayViewCid: | 
 |           type = Dart_TypedData_kFloat64x2; | 
 |           break; | 
 |         default: | 
 |           UNREACHABLE(); | 
 |       } | 
 |  | 
 |       Dart_CObject* data = | 
 |           d->Allocate(Dart_CObject_kUnmodifiableExternalTypedData); | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       FinalizableData finalizable_data = d->finalizable_data()->Get(); | 
 |       data->value.as_typed_data.type = type; | 
 |       data->value.as_typed_data.length = length; | 
 |       data->value.as_typed_data.values = | 
 |           reinterpret_cast<uint8_t*>(finalizable_data.data); | 
 |       d->AssignRef(data); | 
 |     } else { | 
 |       for (intptr_t i = 0; i < count; i++) { | 
 |         Dart_CTypedDataView* view = d->zone()->Alloc<Dart_CTypedDataView>(1); | 
 |         d->AssignRef(view); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdgesApi(ApiMessageDeserializer* d) { | 
 |     if (format_ == kTypedDataViewFromC) return; | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       Dart_CTypedDataView* view = static_cast<Dart_CTypedDataView*>(d->Ref(id)); | 
 |       view->length = d->ReadRef(); | 
 |       view->typed_data = d->ReadRef(); | 
 |       view->offset_in_bytes = d->ReadRef(); | 
 |     } | 
 |   } | 
 |  | 
 |   void PostLoadApi(ApiMessageDeserializer* d) { | 
 |     if (format_ == kTypedDataViewFromC) return; | 
 |     Dart_TypedData_Type type; | 
 |     switch (cid_) { | 
 |       case kTypedDataInt8ArrayViewCid: | 
 |       case kUnmodifiableTypedDataInt8ArrayViewCid: | 
 |         type = Dart_TypedData_kInt8; | 
 |         break; | 
 |       case kTypedDataUint8ArrayViewCid: | 
 |       case kUnmodifiableTypedDataUint8ArrayViewCid: | 
 |         type = Dart_TypedData_kUint8; | 
 |         break; | 
 |       case kTypedDataUint8ClampedArrayViewCid: | 
 |       case kUnmodifiableTypedDataUint8ClampedArrayViewCid: | 
 |         type = Dart_TypedData_kUint8Clamped; | 
 |         break; | 
 |       case kTypedDataInt16ArrayViewCid: | 
 |       case kUnmodifiableTypedDataInt16ArrayViewCid: | 
 |         type = Dart_TypedData_kInt16; | 
 |         break; | 
 |       case kTypedDataUint16ArrayViewCid: | 
 |       case kUnmodifiableTypedDataUint16ArrayViewCid: | 
 |         type = Dart_TypedData_kUint16; | 
 |         break; | 
 |       case kTypedDataInt32ArrayViewCid: | 
 |       case kUnmodifiableTypedDataInt32ArrayViewCid: | 
 |         type = Dart_TypedData_kInt32; | 
 |         break; | 
 |       case kTypedDataUint32ArrayViewCid: | 
 |       case kUnmodifiableTypedDataUint32ArrayViewCid: | 
 |         type = Dart_TypedData_kUint32; | 
 |         break; | 
 |       case kTypedDataInt64ArrayViewCid: | 
 |       case kUnmodifiableTypedDataInt64ArrayViewCid: | 
 |         type = Dart_TypedData_kInt64; | 
 |         break; | 
 |       case kTypedDataUint64ArrayViewCid: | 
 |       case kUnmodifiableTypedDataUint64ArrayViewCid: | 
 |         type = Dart_TypedData_kUint64; | 
 |         break; | 
 |       case kTypedDataFloat32ArrayViewCid: | 
 |       case kUnmodifiableTypedDataFloat32ArrayViewCid: | 
 |         type = Dart_TypedData_kFloat32; | 
 |         break; | 
 |       case kTypedDataFloat64ArrayViewCid: | 
 |       case kUnmodifiableTypedDataFloat64ArrayViewCid: | 
 |         type = Dart_TypedData_kFloat64; | 
 |         break; | 
 |       case kTypedDataInt32x4ArrayViewCid: | 
 |       case kUnmodifiableTypedDataInt32x4ArrayViewCid: | 
 |         type = Dart_TypedData_kInt32x4; | 
 |         break; | 
 |       case kTypedDataFloat32x4ArrayViewCid: | 
 |       case kUnmodifiableTypedDataFloat32x4ArrayViewCid: | 
 |         type = Dart_TypedData_kFloat32x4; | 
 |         break; | 
 |       case kTypedDataFloat64x2ArrayViewCid: | 
 |       case kUnmodifiableTypedDataFloat64x2ArrayViewCid: | 
 |         type = Dart_TypedData_kFloat64x2; | 
 |         break; | 
 |       default: | 
 |         UNREACHABLE(); | 
 |     } | 
 |  | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       Dart_CTypedDataView* view = static_cast<Dart_CTypedDataView*>(d->Ref(id)); | 
 |       if (view->typed_data->type == Dart_CObject_kTypedData) { | 
 |         view->type = Dart_CObject_kTypedData; | 
 |         view->value.as_typed_data.type = type; | 
 |         view->value.as_typed_data.length = view->length->value.as_int32; | 
 |         view->value.as_typed_data.values = | 
 |             view->typed_data->value.as_typed_data.values + | 
 |             view->offset_in_bytes->value.as_int32; | 
 |       } else { | 
 |         UNREACHABLE(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 |   TypedDataViewFormat format_; | 
 | }; | 
 |  | 
 | class TransferableTypedDataMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   TransferableTypedDataMessageSerializationCluster() | 
 |       : MessageSerializationCluster("TransferableTypedData", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     kTransferableTypedDataCid) {} | 
 |   ~TransferableTypedDataMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     TransferableTypedData* transferable = | 
 |         static_cast<TransferableTypedData*>(object); | 
 |     objects_.Add(transferable); | 
 |  | 
 |     void* peer = s->thread()->heap()->GetPeer(transferable->ptr()); | 
 |     // Assume that object's Peer is only used to track transferability state. | 
 |     ASSERT(peer != nullptr); | 
 |     TransferableTypedDataPeer* tpeer = | 
 |         reinterpret_cast<TransferableTypedDataPeer*>(peer); | 
 |     if (tpeer->data() == nullptr) { | 
 |       s->IllegalObject(*object, | 
 |                        "TransferableTypedData has been transferred already"); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       TransferableTypedData* transferable = objects_[i]; | 
 |       s->AssignRef(transferable); | 
 |  | 
 |       void* peer = s->thread()->heap()->GetPeer(transferable->ptr()); | 
 |       // Assume that object's Peer is only used to track transferability state. | 
 |       ASSERT(peer != nullptr); | 
 |       TransferableTypedDataPeer* tpeer = | 
 |           reinterpret_cast<TransferableTypedDataPeer*>(peer); | 
 |       intptr_t length = tpeer->length();  // In bytes. | 
 |       void* data = tpeer->data(); | 
 |       ASSERT(data != nullptr); | 
 |       s->WriteUnsigned(length); | 
 |       s->finalizable_data()->Put( | 
 |           length, data, tpeer, | 
 |           // Finalizer does nothing - in case of failure to serialize, | 
 |           // [data] remains wrapped in sender's [TransferableTypedData]. | 
 |           [](void* data, void* peer) {}, | 
 |           // This is invoked on successful serialization of the message | 
 |           [](void* data, void* peer) { | 
 |             TransferableTypedDataPeer* ttpeer = | 
 |                 reinterpret_cast<TransferableTypedDataPeer*>(peer); | 
 |             ttpeer->handle()->EnsureFreedExternal(IsolateGroup::Current()); | 
 |             ttpeer->ClearData(); | 
 |           }); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<TransferableTypedData*> objects_; | 
 | }; | 
 |  | 
 | class TransferableTypedDataMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   TransferableTypedDataMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("TransferableTypedData") {} | 
 |   ~TransferableTypedDataMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       const FinalizableData finalizable_data = d->finalizable_data()->Take(); | 
 |       d->AssignRef(TransferableTypedData::New( | 
 |           reinterpret_cast<uint8_t*>(finalizable_data.data), length)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* data = d->Allocate(Dart_CObject_kTypedData); | 
 |       data->value.as_typed_data.length = d->ReadUnsigned(); | 
 |       data->value.as_typed_data.type = Dart_TypedData_kUint8; | 
 |       FinalizableData finalizable_data = d->finalizable_data()->Get(); | 
 |       data->value.as_typed_data.values = | 
 |           reinterpret_cast<const uint8_t*>(finalizable_data.data); | 
 |       d->AssignRef(data); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class Simd128MessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit Simd128MessageSerializationCluster(intptr_t cid) | 
 |       : MessageSerializationCluster("Simd128", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     cid) {} | 
 |   ~Simd128MessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { objects_.Add(object); } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Object* vector = objects_[i]; | 
 |       s->AssignRef(vector); | 
 |       ASSERT_EQUAL(Int32x4::value_offset(), Float32x4::value_offset()); | 
 |       ASSERT_EQUAL(Int32x4::value_offset(), Float64x2::value_offset()); | 
 |       s->WriteBytes(&(static_cast<Int32x4Ptr>(vector->ptr())->untag()->value_), | 
 |                     sizeof(simd128_value_t)); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Object*> objects_; | 
 | }; | 
 |  | 
 | class Simd128MessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit Simd128MessageDeserializationCluster(intptr_t cid) | 
 |       : MessageDeserializationCluster("Simd128"), cid_(cid) { | 
 |     ASSERT(cid_ == kInt32x4Cid || cid_ == kFloat32x4Cid || | 
 |            cid_ == kFloat64x2Cid); | 
 | #if defined(DEBUG) | 
 |     // If not for Int32x4, check that all the Int32x4-specific arguments used in | 
 |     // ReadNodes match those for the actual class. | 
 |     if (cid_ == kFloat32x4Cid) { | 
 |       AssertSameStructure<Int32x4, Float32x4>(); | 
 |     } else if (cid_ == kFloat64x2Cid) { | 
 |       AssertSameStructure<Int32x4, Float64x2>(); | 
 |     } | 
 | #endif | 
 |   } | 
 |   ~Simd128MessageDeserializationCluster() {} | 
 |  | 
 | #if defined(DEBUG) | 
 |   template <typename Expected, typename Got> | 
 |   static void AssertSameStructure() { | 
 |     ASSERT_EQUAL(Got::InstanceSize(), Expected::InstanceSize()); | 
 |     ASSERT_EQUAL(Got::ContainsCompressedPointers(), | 
 |                  Expected::ContainsCompressedPointers()); | 
 |     ASSERT_EQUAL(Object::from_offset<Got>(), Object::from_offset<Expected>()); | 
 |     ASSERT_EQUAL(Object::to_offset<Got>(), Object::to_offset<Expected>()); | 
 |     ASSERT_EQUAL(Got::value_offset(), Expected::value_offset()); | 
 |   } | 
 | #endif | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       ObjectPtr vector = Object::Allocate( | 
 |           cid_, Int32x4::InstanceSize(), Heap::kNew, | 
 |           Int32x4::ContainsCompressedPointers(), Object::from_offset<Int32x4>(), | 
 |           Object::to_offset<Int32x4>()); | 
 |       d->AssignRef(vector); | 
 |       d->ReadBytes(&(static_cast<Int32x4Ptr>(vector)->untag()->value_), | 
 |                    sizeof(simd128_value_t)); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | class SendPortMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit SendPortMessageSerializationCluster(Zone* zone) | 
 |       : MessageSerializationCluster("SendPort", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     kSendPortCid), | 
 |         objects_(zone, 0) {} | 
 |   ~SendPortMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     SendPort* port = static_cast<SendPort*>(object); | 
 |     objects_.Add(port); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       SendPort* port = objects_[i]; | 
 |       s->AssignRef(port); | 
 |       s->Write<Dart_Port>(port->untag()->id_); | 
 |       s->Write<Dart_Port>(port->untag()->origin_id_); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<SendPort*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* port = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(port); | 
 |       s->Write<Dart_Port>(port->value.as_send_port.id); | 
 |       s->Write<Dart_Port>(port->value.as_send_port.origin_id); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<SendPort*> objects_; | 
 | }; | 
 |  | 
 | class SendPortMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   SendPortMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("SendPort") {} | 
 |   ~SendPortMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_Port id = d->Read<Dart_Port>(); | 
 |       Dart_Port origin_id = d->Read<Dart_Port>(); | 
 |       d->AssignRef(SendPort::New(id, origin_id)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* port = d->Allocate(Dart_CObject_kSendPort); | 
 |       port->value.as_send_port.id = d->Read<Dart_Port>(); | 
 |       port->value.as_send_port.origin_id = d->Read<Dart_Port>(); | 
 |       d->AssignRef(port); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class CapabilityMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit CapabilityMessageSerializationCluster(Zone* zone) | 
 |       : MessageSerializationCluster("Capability", | 
 |                                     MessagePhase::kNonCanonicalInstances, | 
 |                                     kCapabilityCid), | 
 |         objects_(zone, 0) {} | 
 |   ~CapabilityMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Capability* cap = static_cast<Capability*>(object); | 
 |     objects_.Add(cap); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Capability* cap = objects_[i]; | 
 |       s->AssignRef(cap); | 
 |       s->Write<uint64_t>(cap->untag()->id_); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<Capability*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* cap = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(cap); | 
 |       s->Write<Dart_Port>(cap->value.as_capability.id); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Capability*> objects_; | 
 | }; | 
 |  | 
 | class CapabilityMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   CapabilityMessageDeserializationCluster() | 
 |       : MessageDeserializationCluster("Capability") {} | 
 |   ~CapabilityMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       uint64_t id = d->Read<uint64_t>(); | 
 |       d->AssignRef(Capability::New(id)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* cap = d->Allocate(Dart_CObject_kCapability); | 
 |       cap->value.as_capability.id = d->Read<uint64_t>(); | 
 |       d->AssignRef(cap); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class MapMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   MapMessageSerializationCluster(Zone* zone, bool is_canonical, intptr_t cid) | 
 |       : MessageSerializationCluster("Map", | 
 |                                     is_canonical | 
 |                                         ? MessagePhase::kCanonicalInstances | 
 |                                         : MessagePhase::kNonCanonicalInstances, | 
 |                                     cid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~MapMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Map* map = static_cast<Map*>(object); | 
 |     objects_.Add(map); | 
 |  | 
 |     // Compensation for bogus type prefix optimization. | 
 |     TypeArguments& args = | 
 |         TypeArguments::Handle(s->zone(), map->untag()->type_arguments()); | 
 |     if (!args.IsNull() && (args.Length() != 2)) { | 
 |       args = args.TruncatedTo(2); | 
 |       map->untag()->set_type_arguments(args.ptr()); | 
 |     } | 
 |  | 
 |     s->Push(map->untag()->type_arguments()); | 
 |     s->Push(map->untag()->data()); | 
 |     s->Push(map->untag()->used_data()); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Map* map = objects_[i]; | 
 |       s->AssignRef(map); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Map* map = objects_[i]; | 
 |       s->WriteRef(map->untag()->type_arguments()); | 
 |       s->WriteRef(map->untag()->data()); | 
 |       s->WriteRef(map->untag()->used_data()); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Map*> objects_; | 
 | }; | 
 |  | 
 | class MapMessageDeserializationCluster : public MessageDeserializationCluster { | 
 |  public: | 
 |   MapMessageDeserializationCluster(bool is_canonical, intptr_t cid) | 
 |       : MessageDeserializationCluster("Map", is_canonical), cid_(cid) {} | 
 |   ~MapMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->AssignRef(Map::NewUninitialized(cid_)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       MapPtr map = static_cast<MapPtr>(d->Ref(id)); | 
 |       map->untag()->set_hash_mask(Smi::New(0)); | 
 |       map->untag()->set_type_arguments( | 
 |           static_cast<TypeArgumentsPtr>(d->ReadRef())); | 
 |       map->untag()->set_data(static_cast<ArrayPtr>(d->ReadRef())); | 
 |       map->untag()->set_used_data(static_cast<SmiPtr>(d->ReadRef())); | 
 |       map->untag()->set_deleted_keys(Smi::New(0)); | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     if (!is_canonical()) { | 
 |       ASSERT(cid_ == kMapCid); | 
 |       return PostLoadLinkedHash(d); | 
 |     } | 
 |  | 
 |     ASSERT(cid_ == kConstMapCid); | 
 |     SafepointMutexLocker ml( | 
 |         d->isolate_group()->constant_canonicalization_mutex()); | 
 |     Map& instance = Map::Handle(d->zone()); | 
 |     for (intptr_t i = start_index_; i < stop_index_; i++) { | 
 |       instance ^= d->Ref(i); | 
 |       instance ^= instance.CanonicalizeLocked(d->thread()); | 
 |       d->UpdateRef(i, instance); | 
 |     } | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | class SetMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   SetMessageSerializationCluster(Zone* zone, bool is_canonical, intptr_t cid) | 
 |       : MessageSerializationCluster("Set", | 
 |                                     is_canonical | 
 |                                         ? MessagePhase::kCanonicalInstances | 
 |                                         : MessagePhase::kNonCanonicalInstances, | 
 |                                     cid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~SetMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Set* set = static_cast<Set*>(object); | 
 |     objects_.Add(set); | 
 |  | 
 |     // Compensation for bogus type prefix optimization. | 
 |     TypeArguments& args = | 
 |         TypeArguments::Handle(s->zone(), set->untag()->type_arguments()); | 
 |     if (!args.IsNull() && (args.Length() != 1)) { | 
 |       args = args.TruncatedTo(1); | 
 |       set->untag()->set_type_arguments(args.ptr()); | 
 |     } | 
 |  | 
 |     s->Push(set->untag()->type_arguments()); | 
 |     s->Push(set->untag()->data()); | 
 |     s->Push(set->untag()->used_data()); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Set* set = objects_[i]; | 
 |       s->AssignRef(set); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Set* set = objects_[i]; | 
 |       s->WriteRef(set->untag()->type_arguments()); | 
 |       s->WriteRef(set->untag()->data()); | 
 |       s->WriteRef(set->untag()->used_data()); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Set*> objects_; | 
 | }; | 
 |  | 
 | class SetMessageDeserializationCluster : public MessageDeserializationCluster { | 
 |  public: | 
 |   SetMessageDeserializationCluster(bool is_canonical, intptr_t cid) | 
 |       : MessageDeserializationCluster("Set", is_canonical), cid_(cid) {} | 
 |   ~SetMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       d->AssignRef(Set::NewUninitialized(cid_)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       SetPtr map = static_cast<SetPtr>(d->Ref(id)); | 
 |       map->untag()->set_hash_mask(Smi::New(0)); | 
 |       map->untag()->set_type_arguments( | 
 |           static_cast<TypeArgumentsPtr>(d->ReadRef())); | 
 |       map->untag()->set_data(static_cast<ArrayPtr>(d->ReadRef())); | 
 |       map->untag()->set_used_data(static_cast<SmiPtr>(d->ReadRef())); | 
 |       map->untag()->set_deleted_keys(Smi::New(0)); | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     if (!is_canonical()) { | 
 |       ASSERT(cid_ == kSetCid); | 
 |       return PostLoadLinkedHash(d); | 
 |     } | 
 |  | 
 |     ASSERT(cid_ == kConstSetCid); | 
 |     SafepointMutexLocker ml( | 
 |         d->isolate_group()->constant_canonicalization_mutex()); | 
 |     Set& instance = Set::Handle(d->zone()); | 
 |     for (intptr_t i = start_index_; i < stop_index_; i++) { | 
 |       instance ^= d->Ref(i); | 
 |       instance ^= instance.CanonicalizeLocked(d->thread()); | 
 |       d->UpdateRef(i, instance); | 
 |     } | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | class ArrayMessageSerializationCluster : public MessageSerializationCluster { | 
 |  public: | 
 |   ArrayMessageSerializationCluster(Zone* zone, bool is_canonical, intptr_t cid) | 
 |       : MessageSerializationCluster("Array", | 
 |                                     is_canonical | 
 |                                         ? MessagePhase::kCanonicalInstances | 
 |                                         : MessagePhase::kNonCanonicalInstances, | 
 |                                     cid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~ArrayMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     Array* array = static_cast<Array*>(object); | 
 |     objects_.Add(array); | 
 |  | 
 |     // Compensation for bogus type prefix optimization. | 
 |     TypeArguments& args = | 
 |         TypeArguments::Handle(s->zone(), array->untag()->type_arguments()); | 
 |     if (!args.IsNull() && (args.Length() != 1)) { | 
 |       args = args.TruncatedTo(1); | 
 |       array->untag()->set_type_arguments(args.ptr()); | 
 |     } | 
 |  | 
 |     s->Push(array->untag()->type_arguments()); | 
 |     intptr_t length = Smi::Value(array->untag()->length()); | 
 |     for (intptr_t i = 0; i < length; i++) { | 
 |       s->Push(array->untag()->element(i)); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Array* array = objects_[i]; | 
 |       s->AssignRef(array); | 
 |       intptr_t length = Smi::Value(array->untag()->length()); | 
 |       s->WriteUnsigned(length); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdges(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Array* array = objects_[i]; | 
 |       intptr_t length = array->Length(); | 
 |       s->WriteRef(array->untag()->type_arguments()); | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         s->WriteRef(array->untag()->element(j)); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<Array*>(object)); | 
 |  | 
 |     for (intptr_t i = 0, n = object->value.as_array.length; i < n; i++) { | 
 |       s->Push(object->value.as_array.values[i]); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* array = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(array); | 
 |       s->WriteUnsigned(array->value.as_array.length); | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteEdgesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* array = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       intptr_t length = array->value.as_array.length; | 
 |       s->WriteRef(PredefinedCObjects::cobj_null());  // TypeArguments | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         s->WriteRef(array->value.as_array.values[j]); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<Array*> objects_; | 
 | }; | 
 |  | 
 | class ArrayMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit ArrayMessageDeserializationCluster(bool is_canonical, intptr_t cid) | 
 |       : MessageDeserializationCluster("Array", is_canonical), cid_(cid) {} | 
 |   ~ArrayMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       d->AssignRef(Array::NewUninitialized(cid_, length)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdges(MessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       ArrayPtr array = static_cast<ArrayPtr>(d->Ref(id)); | 
 |       intptr_t length = Smi::Value(array->untag()->length()); | 
 |       array->untag()->set_type_arguments( | 
 |           static_cast<TypeArgumentsPtr>(d->ReadRef())); | 
 |       for (intptr_t j = 0; j < length; j++) { | 
 |         array->untag()->set_element(j, d->ReadRef()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   ObjectPtr PostLoad(MessageDeserializer* d) { | 
 |     if (is_canonical()) { | 
 |       SafepointMutexLocker ml( | 
 |           d->isolate_group()->constant_canonicalization_mutex()); | 
 |       Instance& instance = Instance::Handle(d->zone()); | 
 |       for (intptr_t i = start_index_; i < stop_index_; i++) { | 
 |         instance ^= d->Ref(i); | 
 |         instance = instance.CanonicalizeLocked(d->thread()); | 
 |         d->UpdateRef(i, instance); | 
 |       } | 
 |     } | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* array = d->Allocate(Dart_CObject_kArray); | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       array->value.as_array.length = length; | 
 |       if (length == 0) { | 
 |         array->value.as_array.values = nullptr; | 
 |       } else { | 
 |         array->value.as_array.values = d->zone()->Alloc<Dart_CObject*>(length); | 
 |       } | 
 |       d->AssignRef(array); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadEdgesApi(ApiMessageDeserializer* d) { | 
 |     for (intptr_t id = start_index_; id < stop_index_; id++) { | 
 |       Dart_CObject* array = d->Ref(id); | 
 |       intptr_t length = array->value.as_array.length; | 
 |       d->ReadRef();  // type_arguments | 
 |       for (intptr_t i = 0; i < length; i++) { | 
 |         array->value.as_array.values[i] = d->ReadRef(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const intptr_t cid_; | 
 | }; | 
 |  | 
 | class OneByteStringMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit OneByteStringMessageSerializationCluster(Zone* zone, | 
 |                                                     bool is_canonical) | 
 |       : MessageSerializationCluster("OneByteString", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kOneByteStringCid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~OneByteStringMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     String* str = static_cast<String*>(object); | 
 |     objects_.Add(str); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       String* str = objects_[i]; | 
 |       s->AssignRef(str); | 
 |       intptr_t length = str->Length(); | 
 |       s->WriteUnsigned(length); | 
 |       NoSafepointScope no_safepoint; | 
 |       s->WriteBytes(OneByteString::DataStart(*str), length * sizeof(uint8_t)); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<String*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* str = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(str); | 
 |  | 
 |       const uint8_t* utf8_str = | 
 |           reinterpret_cast<const uint8_t*>(str->value.as_string); | 
 |       intptr_t utf8_len = strlen(str->value.as_string); | 
 |       Utf8::Type type = Utf8::kLatin1; | 
 |       intptr_t latin1_len = Utf8::CodeUnitCount(utf8_str, utf8_len, &type); | 
 |  | 
 |       uint8_t* latin1_str = reinterpret_cast<uint8_t*>( | 
 |           dart::malloc(latin1_len * sizeof(uint8_t))); | 
 |       bool success = | 
 |           Utf8::DecodeToLatin1(utf8_str, utf8_len, latin1_str, latin1_len); | 
 |       ASSERT(success); | 
 |       s->WriteUnsigned(latin1_len); | 
 |       s->WriteBytes(latin1_str, latin1_len); | 
 |       ::free(latin1_str); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<String*> objects_; | 
 | }; | 
 |  | 
 | class OneByteStringMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit OneByteStringMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("OneByteString", is_canonical) {} | 
 |   ~OneByteStringMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       const uint8_t* data = d->CurrentBufferAddress(); | 
 |       d->Advance(length * sizeof(uint8_t)); | 
 |       d->AssignRef(is_canonical() | 
 |                        ? Symbols::FromLatin1(d->thread(), data, length) | 
 |                        : String::FromLatin1(data, length)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* str = d->Allocate(Dart_CObject_kString); | 
 |       intptr_t latin1_length = d->ReadUnsigned(); | 
 |       const uint8_t* data = d->CurrentBufferAddress(); | 
 |  | 
 |       d->Advance(latin1_length * sizeof(uint8_t)); | 
 |  | 
 |       intptr_t utf8_len = 0; | 
 |       for (intptr_t i = 0; i < latin1_length; i++) { | 
 |         utf8_len += Utf8::Length(data[i]); | 
 |       } | 
 |       char* utf8_data = d->zone()->Alloc<char>(utf8_len + 1); | 
 |       str->value.as_string = utf8_data; | 
 |       for (intptr_t i = 0; i < latin1_length; i++) { | 
 |         utf8_data += Utf8::Encode(data[i], utf8_data); | 
 |       } | 
 |       *utf8_data = '\0'; | 
 |  | 
 |       d->AssignRef(str); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | class TwoByteStringMessageSerializationCluster | 
 |     : public MessageSerializationCluster { | 
 |  public: | 
 |   explicit TwoByteStringMessageSerializationCluster(Zone* zone, | 
 |                                                     bool is_canonical) | 
 |       : MessageSerializationCluster("TwoByteString", | 
 |                                     MessagePhase::kBeforeTypes, | 
 |                                     kTwoByteStringCid, | 
 |                                     is_canonical), | 
 |         objects_(zone, 0) {} | 
 |   ~TwoByteStringMessageSerializationCluster() {} | 
 |  | 
 |   void Trace(MessageSerializer* s, Object* object) { | 
 |     String* str = static_cast<String*>(object); | 
 |     objects_.Add(str); | 
 |   } | 
 |  | 
 |   void WriteNodes(MessageSerializer* s) { | 
 |     const intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       String* str = objects_[i]; | 
 |       s->AssignRef(str); | 
 |       intptr_t length = str->Length(); | 
 |       s->WriteUnsigned(length); | 
 |       NoSafepointScope no_safepoint; | 
 |       uint16_t* utf16 = TwoByteString::DataStart(*str); | 
 |       s->WriteBytes(reinterpret_cast<const uint8_t*>(utf16), | 
 |                     length * sizeof(uint16_t)); | 
 |     } | 
 |   } | 
 |  | 
 |   void TraceApi(ApiMessageSerializer* s, Dart_CObject* object) { | 
 |     objects_.Add(reinterpret_cast<String*>(object)); | 
 |   } | 
 |  | 
 |   void WriteNodesApi(ApiMessageSerializer* s) { | 
 |     intptr_t count = objects_.length(); | 
 |     s->WriteUnsigned(count); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       Dart_CObject* str = reinterpret_cast<Dart_CObject*>(objects_[i]); | 
 |       s->AssignRef(str); | 
 |  | 
 |       const uint8_t* utf8_str = | 
 |           reinterpret_cast<const uint8_t*>(str->value.as_string); | 
 |       intptr_t utf8_len = strlen(str->value.as_string); | 
 |       Utf8::Type type = Utf8::kLatin1; | 
 |       intptr_t utf16_len = Utf8::CodeUnitCount(utf8_str, utf8_len, &type); | 
 |  | 
 |       uint16_t* utf16_str = reinterpret_cast<uint16_t*>( | 
 |           dart::malloc(utf16_len * sizeof(uint16_t))); | 
 |       bool success = | 
 |           Utf8::DecodeToUTF16(utf8_str, utf8_len, utf16_str, utf16_len); | 
 |       ASSERT(success); | 
 |       s->WriteUnsigned(utf16_len); | 
 |       s->WriteBytes(reinterpret_cast<const uint8_t*>(utf16_str), | 
 |                     utf16_len * sizeof(uint16_t)); | 
 |       ::free(utf16_str); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   GrowableArray<String*> objects_; | 
 | }; | 
 |  | 
 | class TwoByteStringMessageDeserializationCluster | 
 |     : public MessageDeserializationCluster { | 
 |  public: | 
 |   explicit TwoByteStringMessageDeserializationCluster(bool is_canonical) | 
 |       : MessageDeserializationCluster("TwoByteString", is_canonical) {} | 
 |   ~TwoByteStringMessageDeserializationCluster() {} | 
 |  | 
 |   void ReadNodes(MessageDeserializer* d) { | 
 |     const intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t i = 0; i < count; i++) { | 
 |       intptr_t length = d->ReadUnsigned(); | 
 |       const uint16_t* data = | 
 |           reinterpret_cast<const uint16_t*>(d->CurrentBufferAddress()); | 
 |       d->Advance(length * sizeof(uint16_t)); | 
 |       d->AssignRef(is_canonical() | 
 |                        ? Symbols::FromUTF16(d->thread(), data, length) | 
 |                        : String::FromUTF16(data, length)); | 
 |     } | 
 |   } | 
 |  | 
 |   void ReadNodesApi(ApiMessageDeserializer* d) { | 
 |     intptr_t count = d->ReadUnsigned(); | 
 |     for (intptr_t j = 0; j < count; j++) { | 
 |       // Read all the UTF-16 code units. | 
 |       intptr_t utf16_length = d->ReadUnsigned(); | 
 |       const uint16_t* utf16 = | 
 |           reinterpret_cast<const uint16_t*>(d->CurrentBufferAddress()); | 
 |       d->Advance(utf16_length * sizeof(uint16_t)); | 
 |  | 
 |       // Calculate the UTF-8 length and check if the string can be | 
 |       // UTF-8 encoded. | 
 |       intptr_t utf8_len = 0; | 
 |       bool valid = true; | 
 |       intptr_t i = 0; | 
 |       while (i < utf16_length && valid) { | 
 |         int32_t ch = Utf16::Next(utf16, &i, utf16_length); | 
 |         utf8_len += Utf8::Length(ch); | 
 |         valid = !Utf16::IsSurrogate(ch); | 
 |       } | 
 |       if (!valid) { | 
 |         d->AssignRef(d->Allocate(Dart_CObject_kUnsupported)); | 
 |       } else { | 
 |         Dart_CObject* str = d->Allocate(Dart_CObject_kString); | 
 |         char* utf8 = d->zone()->Alloc<char>(utf8_len + 1); | 
 |         str->value.as_string = utf8; | 
 |         i = 0; | 
 |         while (i < utf16_length) { | 
 |           utf8 += Utf8::Encode(Utf16::Next(utf16, &i, utf16_length), utf8); | 
 |         } | 
 |         *utf8 = '\0'; | 
 |         d->AssignRef(str); | 
 |       } | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | static constexpr intptr_t kFirstReference = 1; | 
 | static constexpr intptr_t kUnallocatedReference = -1; | 
 |  | 
 | BaseSerializer::BaseSerializer(Thread* thread, Zone* zone) | 
 |     : StackResource(thread), | 
 |       zone_(zone), | 
 |       stream_(100), | 
 |       finalizable_data_(new MessageFinalizableData()), | 
 |       clusters_(zone, 0), | 
 |       num_base_objects_(0), | 
 |       num_written_objects_(0), | 
 |       next_ref_index_(kFirstReference) {} | 
 |  | 
 | BaseSerializer::~BaseSerializer() { | 
 |   delete finalizable_data_; | 
 | } | 
 |  | 
 | MessageSerializer::MessageSerializer(Thread* thread) | 
 |     : BaseSerializer(thread, thread->zone()), | 
 |       forward_table_new_(), | 
 |       forward_table_old_(), | 
 |       stack_(thread->zone(), 0) { | 
 |   isolate()->set_forward_table_new(new WeakTable()); | 
 |   isolate()->set_forward_table_old(new WeakTable()); | 
 | } | 
 |  | 
 | MessageSerializer::~MessageSerializer() { | 
 |   isolate()->set_forward_table_new(nullptr); | 
 |   isolate()->set_forward_table_old(nullptr); | 
 | } | 
 |  | 
 | ApiMessageSerializer::ApiMessageSerializer(Zone* zone) | 
 |     : BaseSerializer(nullptr, zone), forward_table_(), stack_(zone, 0) {} | 
 |  | 
 | ApiMessageSerializer::~ApiMessageSerializer() {} | 
 |  | 
 | void MessageSerializer::Push(ObjectPtr object) { | 
 |   if (MarkObjectId(object, kUnallocatedReference)) { | 
 |     stack_.Add(&Object::ZoneHandle(zone_, object)); | 
 |     num_written_objects_++; | 
 |   } | 
 | } | 
 |  | 
 | void ApiMessageSerializer::Push(Dart_CObject* object) { | 
 |   if (MarkObjectId(object, kUnallocatedReference)) { | 
 |     stack_.Add(object); | 
 |     num_written_objects_++; | 
 |   } | 
 | } | 
 |  | 
 | void MessageSerializer::Trace(const Object& root, Object* object) { | 
 |   intptr_t cid; | 
 |   bool is_canonical; | 
 |   if (!object->ptr()->IsHeapObject()) { | 
 |     cid = kSmiCid; | 
 |     is_canonical = true; | 
 |   } else { | 
 |     cid = object->GetClassId(); | 
 |     is_canonical = object->ptr()->untag()->IsCanonical(); | 
 |   } | 
 |  | 
 |   MessageSerializationCluster* cluster = nullptr; | 
 |   for (MessageSerializationCluster* c : clusters_) { | 
 |     if ((c->cid() == cid) && (c->is_canonical() == is_canonical)) { | 
 |       cluster = c; | 
 |       break; | 
 |     } | 
 |   } | 
 |   if (cluster == nullptr) { | 
 |     if (cid >= kNumPredefinedCids || cid == kInstanceCid) { | 
 |       // Will stomp over forward_table_new/old WeakTables, which should be ok, | 
 |       // as they are not going to be used again here. | 
 |       const char* message = OS::SCreate( | 
 |           zone_, "is a regular instance reachable via %s", | 
 |           FindRetainingPath(zone_, isolate(), root, *object, | 
 |                             TraversalRules::kExternalBetweenIsolateGroups)); | 
 |       IllegalObject(*object, message); | 
 |     } | 
 |  | 
 |     const char* illegal_cid_string = nullptr; | 
 |     // Keep the list in sync with the one in lib/isolate.cc, | 
 |     // vm/object_graph_copy.cc | 
 | #define ILLEGAL(type)                                                          \ | 
 |   case k##type##Cid:                                                           \ | 
 |     illegal_cid_string = #type;                                                \ | 
 |     break; | 
 |  | 
 |     switch (cid) { | 
 |       ILLEGAL(Closure) | 
 |       ILLEGAL(Finalizer) | 
 |       ILLEGAL(FinalizerEntry) | 
 |       ILLEGAL(FunctionType) | 
 |       ILLEGAL(MirrorReference) | 
 |       ILLEGAL(NativeFinalizer) | 
 |       ILLEGAL(ReceivePort) | 
 |       ILLEGAL(Record) | 
 |       ILLEGAL(RecordType) | 
 |       ILLEGAL(RegExp) | 
 |       ILLEGAL(StackTrace) | 
 |       ILLEGAL(SuspendState) | 
 |       ILLEGAL(UserTag) | 
 |       ILLEGAL(WeakProperty) | 
 |       ILLEGAL(WeakReference) | 
 |       ILLEGAL(WeakArray) | 
 |  | 
 |       // From "dart:ffi" we handle only Pointer/DynamicLibrary specially, since | 
 |       // those are the only non-abstract classes (so we avoid checking more cids | 
 |       // here that cannot happen in reality) | 
 |       ILLEGAL(DynamicLibrary) | 
 |       ILLEGAL(Pointer) | 
 |  | 
 | #undef ILLEGAL | 
 |     } | 
 |  | 
 |     if (illegal_cid_string != nullptr) { | 
 |       // Will stomp over forward_table_new/old WeakTables, which should be ok, | 
 |       // as they are not going to be used again here. | 
 |       const char* message = OS::SCreate( | 
 |           zone_, "is a %s reachable via %s", illegal_cid_string, | 
 |           FindRetainingPath(zone_, isolate(), root, *object, | 
 |                             TraversalRules::kExternalBetweenIsolateGroups)); | 
 |       IllegalObject(*object, message); | 
 |     } | 
 |  | 
 |     cluster = NewClusterForClass(cid, is_canonical); | 
 |     clusters_.Add(cluster); | 
 |   } | 
 |  | 
 |   cluster->Trace(this, object); | 
 | } | 
 |  | 
 | bool ApiMessageSerializer::Trace(Dart_CObject* object) { | 
 |   const bool is_canonical = false; | 
 |   intptr_t cid; | 
 |   switch (object->type) { | 
 |     case Dart_CObject_kNull: | 
 |       ForwardRef(object, PredefinedCObjects::cobj_null()); | 
 |       return true; | 
 |     case Dart_CObject_kBool: | 
 |       ForwardRef(object, object->value.as_bool ? &cobj_true : &cobj_false); | 
 |       return true; | 
 |     case Dart_CObject_kInt32: | 
 |       cid = Smi::IsValid(object->value.as_int32) ? kSmiCid : kMintCid; | 
 |       break; | 
 |     case Dart_CObject_kInt64: | 
 |       cid = Smi::IsValid(object->value.as_int64) ? kSmiCid : kMintCid; | 
 |       break; | 
 |     case Dart_CObject_kDouble: | 
 |       cid = kDoubleCid; | 
 |       break; | 
 |     case Dart_CObject_kString: { | 
 |       RELEASE_ASSERT(object->value.as_string != nullptr); | 
 |       const uint8_t* utf8_str = | 
 |           reinterpret_cast<const uint8_t*>(object->value.as_string); | 
 |       intptr_t utf8_len = strlen(object->value.as_string); | 
 |       if (!Utf8::IsValid(utf8_str, utf8_len)) { | 
 |         return Fail("invalid utf8"); | 
 |       } | 
 |       Utf8::Type type = Utf8::kLatin1; | 
 |       intptr_t len = Utf8::CodeUnitCount(utf8_str, utf8_len, &type); | 
 |       if (len > String::kMaxElements) { | 
 |         return Fail("invalid string length"); | 
 |       } | 
 |       cid = type == Utf8::kLatin1 ? kOneByteStringCid : kTwoByteStringCid; | 
 |       break; | 
 |     } | 
 |     case Dart_CObject_kArray: | 
 |       cid = kArrayCid; | 
 |       if (!Array::IsValidLength(object->value.as_array.length)) { | 
 |         return Fail("invalid array length"); | 
 |       } | 
 |       break; | 
 |     case Dart_CObject_kTypedData: | 
 |       switch (object->value.as_typed_data.type) { | 
 |         case Dart_TypedData_kInt8: | 
 |           cid = kTypedDataInt8ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8: | 
 |           cid = kTypedDataUint8ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8Clamped: | 
 |           cid = kTypedDataUint8ClampedArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt16: | 
 |           cid = kTypedDataInt16ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint16: | 
 |           cid = kTypedDataUint16ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32: | 
 |           cid = kTypedDataInt32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint32: | 
 |           cid = kTypedDataUint32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt64: | 
 |           cid = kTypedDataInt64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint64: | 
 |           cid = kTypedDataUint64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32: | 
 |           cid = kTypedDataFloat32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64: | 
 |           cid = kTypedDataFloat64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32x4: | 
 |           cid = kTypedDataInt32x4ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32x4: | 
 |           cid = kTypedDataFloat32x4ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64x2: | 
 |           cid = kTypedDataFloat64x2ArrayCid; | 
 |           break; | 
 |         default: | 
 |           return Fail("invalid TypedData type"); | 
 |       } | 
 |       { | 
 |         intptr_t len = object->value.as_typed_data.length; | 
 |         if (len < 0 || len > TypedData::MaxElements(cid)) { | 
 |           return Fail("invalid typeddata length"); | 
 |         } | 
 |       } | 
 |       break; | 
 |     case Dart_CObject_kExternalTypedData: | 
 |       switch (object->value.as_external_typed_data.type) { | 
 |         case Dart_TypedData_kInt8: | 
 |           cid = kExternalTypedDataInt8ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8: | 
 |           cid = kExternalTypedDataUint8ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8Clamped: | 
 |           cid = kExternalTypedDataUint8ClampedArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt16: | 
 |           cid = kExternalTypedDataInt16ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint16: | 
 |           cid = kExternalTypedDataUint16ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32: | 
 |           cid = kExternalTypedDataInt32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint32: | 
 |           cid = kExternalTypedDataUint32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt64: | 
 |           cid = kExternalTypedDataInt64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint64: | 
 |           cid = kExternalTypedDataUint64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32: | 
 |           cid = kExternalTypedDataFloat32ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64: | 
 |           cid = kExternalTypedDataFloat64ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32x4: | 
 |           cid = kExternalTypedDataInt32x4ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32x4: | 
 |           cid = kExternalTypedDataFloat32x4ArrayCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64x2: | 
 |           cid = kExternalTypedDataFloat64x2ArrayCid; | 
 |           break; | 
 |         default: | 
 |           return Fail("invalid TypedData type"); | 
 |       } | 
 |       { | 
 |         intptr_t len = object->value.as_typed_data.length; | 
 |         if (len < 0 || len > ExternalTypedData::MaxElements(cid)) { | 
 |           return Fail("invalid typeddata length"); | 
 |         } | 
 |       } | 
 |       break; | 
 |     case Dart_CObject_kUnmodifiableExternalTypedData: | 
 |       switch (object->value.as_external_typed_data.type) { | 
 |         case Dart_TypedData_kInt8: | 
 |           cid = kUnmodifiableTypedDataInt8ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8: | 
 |           cid = kUnmodifiableTypedDataUint8ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint8Clamped: | 
 |           cid = kUnmodifiableTypedDataUint8ClampedArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt16: | 
 |           cid = kUnmodifiableTypedDataInt16ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint16: | 
 |           cid = kUnmodifiableTypedDataUint16ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32: | 
 |           cid = kUnmodifiableTypedDataInt32ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint32: | 
 |           cid = kUnmodifiableTypedDataUint32ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt64: | 
 |           cid = kUnmodifiableTypedDataInt64ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kUint64: | 
 |           cid = kUnmodifiableTypedDataUint64ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32: | 
 |           cid = kUnmodifiableTypedDataFloat32ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64: | 
 |           cid = kUnmodifiableTypedDataFloat64ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kInt32x4: | 
 |           cid = kUnmodifiableTypedDataInt32x4ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat32x4: | 
 |           cid = kUnmodifiableTypedDataFloat32x4ArrayViewCid; | 
 |           break; | 
 |         case Dart_TypedData_kFloat64x2: | 
 |           cid = kUnmodifiableTypedDataFloat64x2ArrayViewCid; | 
 |           break; | 
 |         default: | 
 |           return Fail("invalid TypedData type"); | 
 |       } | 
 |       { | 
 |         intptr_t len = object->value.as_typed_data.length; | 
 |         if (len < 0 || len > TypedData::MaxElements( | 
 |                                  cid - kTypedDataCidRemainderUnmodifiable + | 
 |                                  kTypedDataCidRemainderInternal)) { | 
 |           return Fail("invalid typeddata length"); | 
 |         } | 
 |       } | 
 |       break; | 
 |     case Dart_CObject_kSendPort: | 
 |       cid = kSendPortCid; | 
 |       break; | 
 |     case Dart_CObject_kCapability: | 
 |       cid = kCapabilityCid; | 
 |       break; | 
 |     case Dart_CObject_kNativePointer: | 
 |       cid = kNativePointer; | 
 |       break; | 
 |     default: | 
 |       return Fail("invalid Dart_CObject type"); | 
 |   } | 
 |  | 
 |   MessageSerializationCluster* cluster = nullptr; | 
 |   for (MessageSerializationCluster* c : clusters_) { | 
 |     if (c->cid() == cid) { | 
 |       cluster = c; | 
 |       break; | 
 |     } | 
 |   } | 
 |   if (cluster == nullptr) { | 
 |     cluster = NewClusterForClass(cid, is_canonical); | 
 |     clusters_.Add(cluster); | 
 |   } | 
 |  | 
 |   cluster->TraceApi(this, object); | 
 |   return true; | 
 | } | 
 |  | 
 | void MessageSerializer::IllegalObject(const Object& object, | 
 |                                       const char* message) { | 
 |   const Array& args = Array::Handle(zone(), Array::New(3)); | 
 |   args.SetAt(0, object); | 
 |   args.SetAt(2, String::Handle(zone(), String::New(message))); | 
 |   Exceptions::ThrowByType(Exceptions::kArgumentValue, args); | 
 | } | 
 |  | 
 | BaseDeserializer::BaseDeserializer(Zone* zone, Message* message) | 
 |     : zone_(zone), | 
 |       stream_(message->snapshot(), message->snapshot_length()), | 
 |       finalizable_data_(message->finalizable_data()), | 
 |       next_ref_index_(kFirstReference) {} | 
 |  | 
 | BaseDeserializer::~BaseDeserializer() {} | 
 |  | 
 | MessageSerializationCluster* BaseSerializer::NewClusterForClass( | 
 |     intptr_t cid, | 
 |     bool is_canonical) { | 
 |   Zone* Z = zone_; | 
 |   if (IsTypedDataViewClassId(cid) || cid == kByteDataViewCid || | 
 |       IsUnmodifiableTypedDataViewClassId(cid) || | 
 |       cid == kUnmodifiableByteDataViewCid) { | 
 |     return new (Z) TypedDataViewMessageSerializationCluster(Z, cid); | 
 |   } | 
 |   if (IsExternalTypedDataClassId(cid)) { | 
 |     return new (Z) ExternalTypedDataMessageSerializationCluster(Z, cid); | 
 |   } | 
 |   if (IsTypedDataClassId(cid)) { | 
 |     return new (Z) TypedDataMessageSerializationCluster(Z, cid); | 
 |   } | 
 |  | 
 |   switch (cid) { | 
 |     case kNativePointer: | 
 |       return new (Z) NativePointerMessageSerializationCluster(Z); | 
 |     case kClassCid: | 
 |       return new (Z) ClassMessageSerializationCluster(); | 
 |     case kTypeArgumentsCid: | 
 |       return new (Z) TypeArgumentsMessageSerializationCluster(is_canonical); | 
 |     case kTypeCid: | 
 |       return new (Z) TypeMessageSerializationCluster(is_canonical); | 
 |     case kSmiCid: | 
 |       return new (Z) SmiMessageSerializationCluster(Z); | 
 |     case kMintCid: | 
 |       return new (Z) MintMessageSerializationCluster(Z, is_canonical); | 
 |     case kDoubleCid: | 
 |       return new (Z) DoubleMessageSerializationCluster(Z, is_canonical); | 
 |     case kGrowableObjectArrayCid: | 
 |       return new (Z) GrowableObjectArrayMessageSerializationCluster(); | 
 |     case kSendPortCid: | 
 |       return new (Z) SendPortMessageSerializationCluster(Z); | 
 |     case kCapabilityCid: | 
 |       return new (Z) CapabilityMessageSerializationCluster(Z); | 
 |     case kTransferableTypedDataCid: | 
 |       return new (Z) TransferableTypedDataMessageSerializationCluster(); | 
 |     case kMapCid: | 
 |     case kConstMapCid: | 
 |       return new (Z) MapMessageSerializationCluster(Z, is_canonical, cid); | 
 |     case kSetCid: | 
 |     case kConstSetCid: | 
 |       return new (Z) SetMessageSerializationCluster(Z, is_canonical, cid); | 
 |     case kArrayCid: | 
 |     case kImmutableArrayCid: | 
 |       return new (Z) ArrayMessageSerializationCluster(Z, is_canonical, cid); | 
 |     case kOneByteStringCid: | 
 |       return new (Z) OneByteStringMessageSerializationCluster(Z, is_canonical); | 
 |     case kTwoByteStringCid: | 
 |       return new (Z) TwoByteStringMessageSerializationCluster(Z, is_canonical); | 
 |     case kInt32x4Cid: | 
 |     case kFloat32x4Cid: | 
 |     case kFloat64x2Cid: | 
 |       return new (Z) Simd128MessageSerializationCluster(cid); | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   FATAL("No cluster defined for cid %" Pd, cid); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | void BaseSerializer::WriteCluster(MessageSerializationCluster* cluster) { | 
 |   uint64_t cid_and_canonical = (static_cast<uint64_t>(cluster->cid()) << 1) | | 
 |                                (cluster->is_canonical() ? 0x1 : 0x0); | 
 |   WriteUnsigned(cid_and_canonical); | 
 | } | 
 |  | 
 | MessageDeserializationCluster* BaseDeserializer::ReadCluster() { | 
 |   const uint64_t cid_and_canonical = ReadUnsigned(); | 
 |   const intptr_t cid = (cid_and_canonical >> 1) & kMaxUint32; | 
 |   const bool is_canonical = (cid_and_canonical & 0x1) == 0x1; | 
 |  | 
 |   Zone* Z = zone_; | 
 |   if (IsTypedDataViewClassId(cid) || cid == kByteDataViewCid || | 
 |       IsUnmodifiableTypedDataViewClassId(cid) || | 
 |       cid == kUnmodifiableByteDataViewCid) { | 
 |     ASSERT(!is_canonical); | 
 |     return new (Z) TypedDataViewMessageDeserializationCluster(cid); | 
 |   } | 
 |   if (IsExternalTypedDataClassId(cid)) { | 
 |     ASSERT(!is_canonical); | 
 |     return new (Z) ExternalTypedDataMessageDeserializationCluster(cid); | 
 |   } | 
 |   if (IsTypedDataClassId(cid)) { | 
 |     ASSERT(!is_canonical); | 
 |     return new (Z) TypedDataMessageDeserializationCluster(cid); | 
 |   } | 
 |  | 
 |   switch (cid) { | 
 |     case kNativePointer: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) NativePointerMessageDeserializationCluster(); | 
 |     case kClassCid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) ClassMessageDeserializationCluster(); | 
 |     case kTypeArgumentsCid: | 
 |       return new (Z) TypeArgumentsMessageDeserializationCluster(is_canonical); | 
 |     case kTypeCid: | 
 |       return new (Z) TypeMessageDeserializationCluster(is_canonical); | 
 |     case kSmiCid: | 
 |       ASSERT(is_canonical); | 
 |       return new (Z) SmiMessageDeserializationCluster(); | 
 |     case kMintCid: | 
 |       return new (Z) MintMessageDeserializationCluster(is_canonical); | 
 |     case kDoubleCid: | 
 |       return new (Z) DoubleMessageDeserializationCluster(is_canonical); | 
 |     case kGrowableObjectArrayCid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) GrowableObjectArrayMessageDeserializationCluster(); | 
 |     case kSendPortCid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) SendPortMessageDeserializationCluster(); | 
 |     case kCapabilityCid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) CapabilityMessageDeserializationCluster(); | 
 |     case kTransferableTypedDataCid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) TransferableTypedDataMessageDeserializationCluster(); | 
 |     case kMapCid: | 
 |     case kConstMapCid: | 
 |       return new (Z) MapMessageDeserializationCluster(is_canonical, cid); | 
 |     case kSetCid: | 
 |     case kConstSetCid: | 
 |       return new (Z) SetMessageDeserializationCluster(is_canonical, cid); | 
 |     case kArrayCid: | 
 |     case kImmutableArrayCid: | 
 |       return new (Z) ArrayMessageDeserializationCluster(is_canonical, cid); | 
 |     case kOneByteStringCid: | 
 |       return new (Z) OneByteStringMessageDeserializationCluster(is_canonical); | 
 |     case kTwoByteStringCid: | 
 |       return new (Z) TwoByteStringMessageDeserializationCluster(is_canonical); | 
 |     case kInt32x4Cid: | 
 |     case kFloat32x4Cid: | 
 |     case kFloat64x2Cid: | 
 |       ASSERT(!is_canonical); | 
 |       return new (Z) Simd128MessageDeserializationCluster(cid); | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   FATAL("No cluster defined for cid %" Pd, cid); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | void MessageSerializer::AddBaseObjects() { | 
 |   AddBaseObject(Object::null()); | 
 |   AddBaseObject(Object::sentinel().ptr()); | 
 |   AddBaseObject(Object::transition_sentinel().ptr()); | 
 |   AddBaseObject(Object::empty_array().ptr()); | 
 |   AddBaseObject(Object::dynamic_type().ptr()); | 
 |   AddBaseObject(Object::void_type().ptr()); | 
 |   AddBaseObject(Object::empty_type_arguments().ptr()); | 
 |   AddBaseObject(Bool::True().ptr()); | 
 |   AddBaseObject(Bool::False().ptr()); | 
 | } | 
 |  | 
 | void MessageDeserializer::AddBaseObjects() { | 
 |   AddBaseObject(Object::null()); | 
 |   AddBaseObject(Object::sentinel().ptr()); | 
 |   AddBaseObject(Object::transition_sentinel().ptr()); | 
 |   AddBaseObject(Object::empty_array().ptr()); | 
 |   AddBaseObject(Object::dynamic_type().ptr()); | 
 |   AddBaseObject(Object::void_type().ptr()); | 
 |   AddBaseObject(Object::empty_type_arguments().ptr()); | 
 |   AddBaseObject(Bool::True().ptr()); | 
 |   AddBaseObject(Bool::False().ptr()); | 
 | } | 
 |  | 
 | void ApiMessageSerializer::AddBaseObjects() { | 
 |   AddBaseObject(PredefinedCObjects::cobj_null()); | 
 |   AddBaseObject(&cobj_sentinel); | 
 |   AddBaseObject(&cobj_transition_sentinel); | 
 |   AddBaseObject(PredefinedCObjects::cobj_empty_array()); | 
 |   AddBaseObject(&cobj_dynamic_type); | 
 |   AddBaseObject(&cobj_void_type); | 
 |   AddBaseObject(&cobj_empty_type_arguments); | 
 |   AddBaseObject(&cobj_true); | 
 |   AddBaseObject(&cobj_false); | 
 | } | 
 |  | 
 | void ApiMessageDeserializer::AddBaseObjects() { | 
 |   AddBaseObject(PredefinedCObjects::cobj_null()); | 
 |   AddBaseObject(&cobj_sentinel); | 
 |   AddBaseObject(&cobj_transition_sentinel); | 
 |   AddBaseObject(PredefinedCObjects::cobj_empty_array()); | 
 |   AddBaseObject(&cobj_dynamic_type); | 
 |   AddBaseObject(&cobj_void_type); | 
 |   AddBaseObject(&cobj_empty_type_arguments); | 
 |   AddBaseObject(&cobj_true); | 
 |   AddBaseObject(&cobj_false); | 
 | } | 
 |  | 
 | void MessageSerializer::Serialize(const Object& root) { | 
 |   AddBaseObjects(); | 
 |  | 
 |   Push(root.ptr()); | 
 |  | 
 |   while (stack_.length() > 0) { | 
 |     Trace(root, stack_.RemoveLast()); | 
 |   } | 
 |  | 
 |   intptr_t num_objects = num_base_objects_ + num_written_objects_; | 
 |   WriteUnsigned(num_base_objects_); | 
 |   WriteUnsigned(num_objects); | 
 |  | 
 |   for (intptr_t i = 0; i < static_cast<intptr_t>(MessagePhase::kNumPhases); | 
 |        i++) { | 
 |     intptr_t num_clusters = 0; | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       num_clusters++; | 
 |     } | 
 |     WriteUnsigned(num_clusters); | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       WriteCluster(cluster); | 
 |       cluster->WriteNodes(this); | 
 |     } | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       cluster->WriteEdges(this); | 
 |     } | 
 |   } | 
 |  | 
 |   // We should have assigned a ref to every object we pushed. | 
 |   ASSERT((next_ref_index_ - 1) == num_objects); | 
 |  | 
 |   WriteRef(root.ptr()); | 
 | } | 
 |  | 
 | bool ApiMessageSerializer::Serialize(Dart_CObject* root) { | 
 |   AddBaseObjects(); | 
 |  | 
 |   Push(root); | 
 |  | 
 |   // Strong references only. | 
 |   while (stack_.length() > 0) { | 
 |     if (!Trace(stack_.RemoveLast())) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   intptr_t num_objects = num_base_objects_ + num_written_objects_; | 
 |   WriteUnsigned(num_base_objects_); | 
 |   WriteUnsigned(num_objects); | 
 |  | 
 |   for (intptr_t i = 0; i < static_cast<intptr_t>(MessagePhase::kNumPhases); | 
 |        i++) { | 
 |     intptr_t num_clusters = 0; | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       num_clusters++; | 
 |     } | 
 |     WriteUnsigned(num_clusters); | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       WriteCluster(cluster); | 
 |       cluster->WriteNodesApi(this); | 
 |     } | 
 |     for (MessageSerializationCluster* cluster : clusters_) { | 
 |       if (static_cast<intptr_t>(cluster->phase()) != i) continue; | 
 |       cluster->WriteEdgesApi(this); | 
 |     } | 
 |   } | 
 |  | 
 |   // We should have assigned a ref to every object we pushed. | 
 |   ASSERT((next_ref_index_ - 1) == num_objects); | 
 |  | 
 |   WriteRef(root); | 
 |   return true; | 
 | } | 
 |  | 
 | ObjectPtr MessageDeserializer::Deserialize() { | 
 |   intptr_t num_base_objects = ReadUnsigned(); | 
 |   intptr_t num_objects = ReadUnsigned(); | 
 |  | 
 |   refs_ = Array::New(num_objects + kFirstReference); | 
 |  | 
 |   AddBaseObjects(); | 
 |  | 
 |   // Writer and reader must agree on number of base objects. | 
 |   ASSERT_EQUAL(num_base_objects, (next_ref_index_ - kFirstReference)); | 
 |  | 
 |   Object& error = Object::Handle(zone()); | 
 |   for (intptr_t i = 0; i < static_cast<intptr_t>(MessagePhase::kNumPhases); | 
 |        i++) { | 
 |     intptr_t num_clusters = ReadUnsigned(); | 
 |     MessageDeserializationCluster** clusters = | 
 |         zone()->Alloc<MessageDeserializationCluster*>(num_clusters); | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       clusters[i] = ReadCluster(); | 
 |       clusters[i]->ReadNodesWrapped(this); | 
 |     } | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       clusters[i]->ReadEdges(this); | 
 |     } | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       error = clusters[i]->PostLoad(this); | 
 |       if (error.IsError()) { | 
 |         return error.ptr();  // E.g., an UnwindError during rehashing. | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // We should have completely filled the ref array. | 
 |   ASSERT_EQUAL(next_ref_index_ - kFirstReference, num_objects); | 
 |  | 
 |   return ReadRef(); | 
 | } | 
 |  | 
 | Dart_CObject* ApiMessageDeserializer::Deserialize() { | 
 |   intptr_t num_base_objects = ReadUnsigned(); | 
 |   intptr_t num_objects = ReadUnsigned(); | 
 |  | 
 |   refs_ = zone()->Alloc<Dart_CObject*>(num_objects + kFirstReference); | 
 |  | 
 |   AddBaseObjects(); | 
 |  | 
 |   // Writer and reader must agree on number of base objects. | 
 |   ASSERT_EQUAL(num_base_objects, (next_ref_index_ - kFirstReference)); | 
 |  | 
 |   for (intptr_t i = 0; i < static_cast<intptr_t>(MessagePhase::kNumPhases); | 
 |        i++) { | 
 |     intptr_t num_clusters = ReadUnsigned(); | 
 |     MessageDeserializationCluster** clusters = | 
 |         zone()->Alloc<MessageDeserializationCluster*>(num_clusters); | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       clusters[i] = ReadCluster(); | 
 |       clusters[i]->ReadNodesWrappedApi(this); | 
 |     } | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       clusters[i]->ReadEdgesApi(this); | 
 |     } | 
 |     for (intptr_t i = 0; i < num_clusters; i++) { | 
 |       clusters[i]->PostLoadApi(this); | 
 |     } | 
 |   } | 
 |  | 
 |   // We should have completely filled the ref array. | 
 |   ASSERT_EQUAL(next_ref_index_ - kFirstReference, num_objects); | 
 |  | 
 |   return ReadRef(); | 
 | } | 
 |  | 
 | std::unique_ptr<Message> WriteMessage(bool same_group, | 
 |                                       const Object& obj, | 
 |                                       Dart_Port dest_port, | 
 |                                       Message::Priority priority) { | 
 |   if (ApiObjectConverter::CanConvert(obj.ptr())) { | 
 |     return Message::New(dest_port, obj.ptr(), priority); | 
 |   } else if (same_group) { | 
 |     const Object& copy = Object::Handle(CopyMutableObjectGraph(obj)); | 
 |     auto handle = | 
 |         IsolateGroup::Current()->api_state()->AllocatePersistentHandle(); | 
 |     handle->set_ptr(copy.ptr()); | 
 |     return std::make_unique<Message>(dest_port, handle, priority); | 
 |   } | 
 |  | 
 |   Thread* thread = Thread::Current(); | 
 |   MessageSerializer serializer(thread); | 
 |   serializer.Serialize(obj); | 
 |   return serializer.Finish(dest_port, priority); | 
 | } | 
 |  | 
 | std::unique_ptr<Message> WriteApiMessage(Zone* zone, | 
 |                                          Dart_CObject* obj, | 
 |                                          Dart_Port dest_port, | 
 |                                          Message::Priority priority) { | 
 |   ApiMessageSerializer serializer(zone); | 
 |   if (!serializer.Serialize(obj)) { | 
 |     return nullptr; | 
 |   } | 
 |   return serializer.Finish(dest_port, priority); | 
 | } | 
 |  | 
 | ObjectPtr ReadObjectGraphCopyMessage(Thread* thread, PersistentHandle* handle) { | 
 |   // msg_array = [ | 
 |   //     <message>, | 
 |   //     <collection-lib-objects-to-rehash>, | 
 |   //     <core-lib-objects-to-rehash>, | 
 |   // ] | 
 |   Zone* zone = thread->zone(); | 
 |   Object& msg_obj = Object::Handle(zone); | 
 |   const auto& msg_array = Array::Handle(zone, Array::RawCast(handle->ptr())); | 
 |   ASSERT(msg_array.Length() == 3); | 
 |   msg_obj = msg_array.At(0); | 
 |   if (msg_array.At(1) != Object::null()) { | 
 |     const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(1)); | 
 |     auto& result = Object::Handle(zone); | 
 |     result = DartLibraryCalls::RehashObjectsInDartCollection(thread, | 
 |                                                              objects_to_rehash); | 
 |     if (result.ptr() != Object::null()) { | 
 |       msg_obj = result.ptr(); | 
 |     } | 
 |   } | 
 |   if (msg_array.At(2) != Object::null()) { | 
 |     const auto& objects_to_rehash = Object::Handle(zone, msg_array.At(2)); | 
 |     auto& result = Object::Handle(zone); | 
 |     result = | 
 |         DartLibraryCalls::RehashObjectsInDartCore(thread, objects_to_rehash); | 
 |     if (result.ptr() != Object::null()) { | 
 |       msg_obj = result.ptr(); | 
 |     } | 
 |   } | 
 |   return msg_obj.ptr(); | 
 | } | 
 |  | 
 | ObjectPtr ReadMessage(Thread* thread, Message* message) { | 
 |   if (message->IsRaw()) { | 
 |     return message->raw_obj(); | 
 |   } else if (message->IsFinalizerInvocationRequest()) { | 
 |     PersistentHandle* handle = message->persistent_handle(); | 
 |     Object& msg_obj = Object::Handle(thread->zone(), handle->ptr()); | 
 |     ASSERT(msg_obj.IsFinalizer() || msg_obj.IsNativeFinalizer()); | 
 |     return msg_obj.ptr(); | 
 |   } else if (message->IsPersistentHandle()) { | 
 |     return ReadObjectGraphCopyMessage(thread, message->persistent_handle()); | 
 |   } else { | 
 |     RELEASE_ASSERT(message->IsSnapshot()); | 
 |     LongJumpScope jump(thread); | 
 |     if (setjmp(*jump.Set()) == 0) { | 
 |       MessageDeserializer deserializer(thread, message); | 
 |       return deserializer.Deserialize(); | 
 |     } else { | 
 |       return thread->StealStickyError(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | Dart_CObject* ReadApiMessage(Zone* zone, Message* message) { | 
 |   if (message->IsRaw()) { | 
 |     Dart_CObject* result = zone->Alloc<Dart_CObject>(1); | 
 |     ApiObjectConverter::Convert(message->raw_obj(), result); | 
 |     return result; | 
 |   } else { | 
 |     RELEASE_ASSERT(message->IsSnapshot()); | 
 |     ApiMessageDeserializer deserializer(zone, message); | 
 |     return deserializer.Deserialize(); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace dart |