| // Copyright (c) 2018, 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/compiler/backend/slot.h" | 
 |  | 
 | #include "vm/compiler/backend/flow_graph_compiler.h" | 
 | #include "vm/compiler/backend/il.h" | 
 | #include "vm/compiler/compiler_state.h" | 
 | #include "vm/hash_map.h" | 
 | #include "vm/parser.h" | 
 | #include "vm/scopes.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | // Canonicalization cache for Slot objects. | 
 | // | 
 | // This cache is attached to the CompilerState to ensure that we preserve | 
 | // identity of Slot objects during each individual compilation. | 
 | class SlotCache : public ZoneAllocated { | 
 |  public: | 
 |   // Returns an instance of SlotCache for the current compilation. | 
 |   static SlotCache& Instance(Thread* thread) { | 
 |     auto result = thread->compiler_state().slot_cache(); | 
 |     if (result == nullptr) { | 
 |       result = new (thread->zone()) SlotCache(thread); | 
 |       thread->compiler_state().set_slot_cache(result); | 
 |     } | 
 |     return *result; | 
 |   } | 
 |  | 
 |   const Slot& Canonicalize(const Slot& value) { | 
 |     auto result = fields_.LookupValue(&value); | 
 |     if (result == nullptr) { | 
 |       result = new (zone_) Slot(value); | 
 |       fields_.Insert(result); | 
 |     } | 
 |     return *result; | 
 |   } | 
 |  | 
 |  private: | 
 |   explicit SlotCache(Thread* thread) | 
 |       : zone_(thread->zone()), fields_(thread->zone()) {} | 
 |  | 
 |   Zone* const zone_; | 
 |   PointerSet<const Slot> fields_; | 
 | }; | 
 |  | 
 | static classid_t GetUnboxedNativeSlotCid(Representation rep) { | 
 |   // Currently we only support integer unboxed fields. | 
 |   if (RepresentationUtils::IsUnboxedInteger(rep)) { | 
 |     return Boxing::BoxCid(rep); | 
 |   } | 
 |   UNREACHABLE(); | 
 |   return kIllegalCid; | 
 | } | 
 |  | 
 | AcqRelAtomic<Slot*> Slot::native_fields_(nullptr); | 
 |  | 
 | enum NativeSlotsEnumeration { | 
 | #define DECLARE_KIND(CN, __, FN, ___, ____) k##CN##_##FN, | 
 |   NATIVE_SLOTS_LIST(DECLARE_KIND) | 
 | #undef DECLARE_KIND | 
 |       kNativeSlotsCount | 
 | }; | 
 |  | 
 | const Slot& Slot::GetNativeSlot(Kind kind) { | 
 |   if (native_fields_.load() == nullptr) { | 
 |     Slot* new_value = new Slot[kNativeSlotsCount]{ | 
 | #define NULLABLE_FIELD_FINAL(ClassName)                                        \ | 
 |   (IsNullableBit::encode(true) | IsImmutableBit::encode(true) |                \ | 
 |    IsCompressedBit::encode(ClassName::ContainsCompressedPointers())) | 
 | #define NULLABLE_FIELD_VAR(ClassName)                                          \ | 
 |   (IsNullableBit::encode(true) |                                               \ | 
 |    IsCompressedBit::encode(ClassName::ContainsCompressedPointers())) | 
 | #define DEFINE_NULLABLE_BOXED_NATIVE_FIELD(ClassName, UnderlyingType,          \ | 
 |                                            FieldName, cid, mutability)         \ | 
 |   Slot(Kind::k##ClassName##_##FieldName,                                       \ | 
 |        NULLABLE_FIELD_##mutability(ClassName), k##cid##Cid,                    \ | 
 |        compiler::target::ClassName::FieldName##_offset(),                      \ | 
 |        #ClassName "." #FieldName, nullptr, kTagged), | 
 |  | 
 |         NULLABLE_BOXED_NATIVE_SLOTS_LIST(DEFINE_NULLABLE_BOXED_NATIVE_FIELD) | 
 |  | 
 | #undef DEFINE_NULLABLE_BOXED_NATIVE_FIELD | 
 | #undef NULLABLE_FIELD_FINAL | 
 | #undef NULLABLE_FIELD_VAR | 
 |  | 
 | #define NONNULLABLE_FIELD_FINAL(ClassName)                                     \ | 
 |   (Slot::IsImmutableBit::encode(true) |                                        \ | 
 |    IsCompressedBit::encode(ClassName::ContainsCompressedPointers())) | 
 | #define NONNULLABLE_FIELD_VAR(ClassName)                                       \ | 
 |   (IsCompressedBit::encode(ClassName::ContainsCompressedPointers())) | 
 | #define DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD(ClassName, UnderlyingType,       \ | 
 |                                               FieldName, cid, mutability)      \ | 
 |   Slot(Kind::k##ClassName##_##FieldName,                                       \ | 
 |        NONNULLABLE_FIELD_##mutability(ClassName), k##cid##Cid,                 \ | 
 |        compiler::target::ClassName::FieldName##_offset(),                      \ | 
 |        #ClassName "." #FieldName, nullptr, kTagged), | 
 |  | 
 |             NONNULLABLE_BOXED_NATIVE_SLOTS_LIST( | 
 |                 DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD) | 
 |  | 
 | #undef DEFINE_NONNULLABLE_BOXED_NATIVE_FIELD | 
 | #undef NONNULLABLE_FIELD_VAR | 
 | #undef NONNULLABLE_FIELD_FINAL | 
 |  | 
 | #define UNBOXED_FIELD_FINAL (Slot::IsImmutableBit::encode(true)) | 
 | #define UNBOXED_FIELD_VAR (0) | 
 | #define DEFINE_UNBOXED_NATIVE_FIELD(ClassName, UnderlyingType, FieldName,      \ | 
 |                                     representation, mutability)                \ | 
 |   Slot(Kind::k##ClassName##_##FieldName, UNBOXED_FIELD_##mutability,           \ | 
 |        GetUnboxedNativeSlotCid(kUnboxed##representation),                      \ | 
 |        compiler::target::ClassName::FieldName##_offset(),                      \ | 
 |        #ClassName "." #FieldName, nullptr, kUnboxed##representation), | 
 |  | 
 |                 UNBOXED_NATIVE_SLOTS_LIST(DEFINE_UNBOXED_NATIVE_FIELD) | 
 |  | 
 | #undef DEFINE_UNBOXED_NATIVE_FIELD | 
 | #undef UNBOXED_FIELD_VAR | 
 | #undef UNBOXED_FIELD_FINAL | 
 |     }; | 
 |     Slot* old_value = nullptr; | 
 |     if (!native_fields_.compare_exchange_strong(old_value, new_value)) { | 
 |       delete[] new_value; | 
 |     } | 
 |   } | 
 |  | 
 |   ASSERT(static_cast<uint8_t>(kind) < kNativeSlotsCount); | 
 |   return native_fields_.load()[static_cast<uint8_t>(kind)]; | 
 | } | 
 |  | 
 | bool Slot::IsImmutableLengthSlot() const { | 
 |   switch (kind()) { | 
 |     case Slot::Kind::kArray_length: | 
 |     case Slot::Kind::kTypedDataBase_length: | 
 |     case Slot::Kind::kString_length: | 
 |     case Slot::Kind::kTypeArguments_length: | 
 |       return true; | 
 |     case Slot::Kind::kGrowableObjectArray_length: | 
 |       return false; | 
 |  | 
 |       // Not length loads. | 
 | #define UNBOXED_NATIVE_SLOT_CASE(Class, Untagged, Field, Rep, IsFinal)         \ | 
 |   case Slot::Kind::k##Class##_##Field: | 
 |       UNBOXED_NATIVE_SLOTS_LIST(UNBOXED_NATIVE_SLOT_CASE) | 
 | #undef UNBOXED_NATIVE_SLOT_CASE | 
 |     case Slot::Kind::kLinkedHashBase_index: | 
 |     case Slot::Kind::kImmutableLinkedHashBase_index: | 
 |     case Slot::Kind::kLinkedHashBase_data: | 
 |     case Slot::Kind::kImmutableLinkedHashBase_data: | 
 |     case Slot::Kind::kLinkedHashBase_hash_mask: | 
 |     case Slot::Kind::kLinkedHashBase_used_data: | 
 |     case Slot::Kind::kLinkedHashBase_deleted_keys: | 
 |     case Slot::Kind::kArgumentsDescriptor_type_args_len: | 
 |     case Slot::Kind::kArgumentsDescriptor_positional_count: | 
 |     case Slot::Kind::kArgumentsDescriptor_count: | 
 |     case Slot::Kind::kArgumentsDescriptor_size: | 
 |     case Slot::Kind::kArrayElement: | 
 |     case Slot::Kind::kInstance_native_fields_array: | 
 |     case Slot::Kind::kTypeArguments: | 
 |     case Slot::Kind::kTypedDataView_offset_in_bytes: | 
 |     case Slot::Kind::kTypedDataView_typed_data: | 
 |     case Slot::Kind::kGrowableObjectArray_data: | 
 |     case Slot::Kind::kArray_type_arguments: | 
 |     case Slot::Kind::kContext_parent: | 
 |     case Slot::Kind::kClosure_context: | 
 |     case Slot::Kind::kClosure_delayed_type_arguments: | 
 |     case Slot::Kind::kClosure_function: | 
 |     case Slot::Kind::kClosure_function_type_arguments: | 
 |     case Slot::Kind::kClosure_instantiator_type_arguments: | 
 |     case Slot::Kind::kClosure_hash: | 
 |     case Slot::Kind::kCapturedVariable: | 
 |     case Slot::Kind::kDartField: | 
 |     case Slot::Kind::kFinalizer_callback: | 
 |     case Slot::Kind::kFinalizer_type_arguments: | 
 |     case Slot::Kind::kFinalizerBase_all_entries: | 
 |     case Slot::Kind::kFinalizerBase_detachments: | 
 |     case Slot::Kind::kFinalizerBase_entries_collected: | 
 |     case Slot::Kind::kFinalizerEntry_detach: | 
 |     case Slot::Kind::kFinalizerEntry_finalizer: | 
 |     case Slot::Kind::kFinalizerEntry_next: | 
 |     case Slot::Kind::kFinalizerEntry_token: | 
 |     case Slot::Kind::kFinalizerEntry_value: | 
 |     case Slot::Kind::kNativeFinalizer_callback: | 
 |     case Slot::Kind::kFunction_data: | 
 |     case Slot::Kind::kFunction_signature: | 
 |     case Slot::Kind::kFunctionType_named_parameter_names: | 
 |     case Slot::Kind::kFunctionType_parameter_types: | 
 |     case Slot::Kind::kFunctionType_type_parameters: | 
 |     case Slot::Kind::kRecordField: | 
 |     case Slot::Kind::kRecord_field_names: | 
 |     case Slot::Kind::kSuspendState_function_data: | 
 |     case Slot::Kind::kSuspendState_then_callback: | 
 |     case Slot::Kind::kSuspendState_error_callback: | 
 |     case Slot::Kind::kType_arguments: | 
 |     case Slot::Kind::kTypeArgumentsIndex: | 
 |     case Slot::Kind::kTypeParameters_names: | 
 |     case Slot::Kind::kTypeParameters_flags: | 
 |     case Slot::Kind::kTypeParameters_bounds: | 
 |     case Slot::Kind::kTypeParameters_defaults: | 
 |     case Slot::Kind::kTypeParameter_bound: | 
 |     case Slot::Kind::kUnhandledException_exception: | 
 |     case Slot::Kind::kUnhandledException_stacktrace: | 
 |     case Slot::Kind::kWeakProperty_key: | 
 |     case Slot::Kind::kWeakProperty_value: | 
 |     case Slot::Kind::kWeakReference_target: | 
 |     case Slot::Kind::kWeakReference_type_arguments: | 
 |       return false; | 
 |   } | 
 |   UNREACHABLE(); | 
 |   return false; | 
 | } | 
 |  | 
 | // Note: should only be called with cids of array-like classes. | 
 | const Slot& Slot::GetLengthFieldForArrayCid(intptr_t array_cid) { | 
 |   if (IsExternalTypedDataClassId(array_cid) || IsTypedDataClassId(array_cid) || | 
 |       IsTypedDataViewClassId(array_cid) || | 
 |       IsUnmodifiableTypedDataViewClassId(array_cid)) { | 
 |     return GetNativeSlot(Kind::kTypedDataBase_length); | 
 |   } | 
 |   switch (array_cid) { | 
 |     case kGrowableObjectArrayCid: | 
 |       return GetNativeSlot(Kind::kGrowableObjectArray_length); | 
 |  | 
 |     case kOneByteStringCid: | 
 |     case kTwoByteStringCid: | 
 |     case kExternalOneByteStringCid: | 
 |     case kExternalTwoByteStringCid: | 
 |       return GetNativeSlot(Kind::kString_length); | 
 |  | 
 |     case kArrayCid: | 
 |     case kImmutableArrayCid: | 
 |       return GetNativeSlot(Kind::kArray_length); | 
 |  | 
 |     case kTypeArgumentsCid: | 
 |       return GetNativeSlot(Kind::kTypeArguments_length); | 
 |  | 
 |     default: | 
 |       UNREACHABLE(); | 
 |       return GetNativeSlot(Kind::kArray_length); | 
 |   } | 
 | } | 
 |  | 
 | const Slot& Slot::GetTypeArgumentsSlotFor(Thread* thread, const Class& cls) { | 
 |   if (cls.id() == kArrayCid || cls.id() == kImmutableArrayCid) { | 
 |     return Slot::Array_type_arguments(); | 
 |   } | 
 |   const intptr_t offset = | 
 |       compiler::target::Class::TypeArgumentsFieldOffset(cls); | 
 |   ASSERT(offset != Class::kNoTypeArguments); | 
 |   return GetCanonicalSlot( | 
 |       thread, Kind::kTypeArguments, | 
 |       IsImmutableBit::encode(true) | | 
 |           IsCompressedBit::encode( | 
 |               compiler::target::Class::HasCompressedPointers(cls)), | 
 |       kTypeArgumentsCid, offset, ":type_arguments", | 
 |       /*static_type=*/nullptr, kTagged); | 
 | } | 
 |  | 
 | const Slot& Slot::GetContextVariableSlotFor(Thread* thread, | 
 |                                             const LocalVariable& variable) { | 
 |   ASSERT(variable.is_captured()); | 
 |   return GetCanonicalSlot( | 
 |       thread, Kind::kCapturedVariable, | 
 |       IsImmutableBit::encode(variable.is_final() && !variable.is_late()) | | 
 |           IsNullableBit::encode(true) | | 
 |           IsCompressedBit::encode(Context::ContainsCompressedPointers()) | | 
 |           IsSentinelVisibleBit::encode(variable.is_late()), | 
 |       kDynamicCid, | 
 |       compiler::target::Context::variable_offset(variable.index().value()), | 
 |       &variable.name(), &variable.type(), kTagged); | 
 | } | 
 |  | 
 | const Slot& Slot::GetTypeArgumentsIndexSlot(Thread* thread, intptr_t index) { | 
 |   const intptr_t offset = | 
 |       compiler::target::TypeArguments::type_at_offset(index); | 
 |   return GetCanonicalSlot( | 
 |       thread, Kind::kTypeArgumentsIndex, | 
 |       IsImmutableBit::encode(true) | | 
 |           IsCompressedBit::encode(TypeArguments::ContainsCompressedPointers()), | 
 |       kDynamicCid, offset, ":argument", /*static_type=*/nullptr, kTagged); | 
 | } | 
 |  | 
 | const Slot& Slot::GetArrayElementSlot(Thread* thread, | 
 |                                       intptr_t offset_in_bytes) { | 
 |   return GetCanonicalSlot( | 
 |       thread, Kind::kArrayElement, | 
 |       IsNullableBit::encode(true) | | 
 |           IsCompressedBit::encode(Array::ContainsCompressedPointers()), | 
 |       kDynamicCid, offset_in_bytes, ":array_element", | 
 |       /*static_type=*/nullptr, kTagged); | 
 | } | 
 |  | 
 | const Slot& Slot::GetRecordFieldSlot(Thread* thread, intptr_t offset_in_bytes) { | 
 |   return GetCanonicalSlot( | 
 |       thread, Kind::kRecordField, | 
 |       IsNullableBit::encode(true) | | 
 |           IsCompressedBit::encode(Record::ContainsCompressedPointers()), | 
 |       kDynamicCid, offset_in_bytes, ":record_field", | 
 |       /*static_type=*/nullptr, kTagged); | 
 | } | 
 |  | 
 | const Slot& Slot::GetCanonicalSlot(Thread* thread, | 
 |                                    Slot::Kind kind, | 
 |                                    int8_t flags, | 
 |                                    ClassIdTagType cid, | 
 |                                    intptr_t offset_in_bytes, | 
 |                                    const void* data, | 
 |                                    const AbstractType* static_type, | 
 |                                    Representation representation, | 
 |                                    const FieldGuardState& field_guard_state) { | 
 |   const Slot& slot = Slot(kind, flags, cid, offset_in_bytes, data, static_type, | 
 |                           representation, field_guard_state); | 
 |   return SlotCache::Instance(thread).Canonicalize(slot); | 
 | } | 
 |  | 
 | FieldGuardState::FieldGuardState(const Field& field) | 
 |     : state_(GuardedCidBits::encode(field.guarded_cid()) | | 
 |              IsNullableBit::encode(field.is_nullable())) {} | 
 |  | 
 | Representation Slot::UnboxedRepresentation() const { | 
 |   switch (field_guard_state().guarded_cid()) { | 
 |     case kDoubleCid: | 
 |       return kUnboxedDouble; | 
 |     case kFloat32x4Cid: | 
 |       return kUnboxedFloat32x4; | 
 |     case kFloat64x2Cid: | 
 |       return kUnboxedFloat64x2; | 
 |     default: | 
 |       return kUnboxedInt64; | 
 |   } | 
 | } | 
 |  | 
 | const Slot& Slot::Get(const Field& field, | 
 |                       const ParsedFunction* parsed_function) { | 
 |   Thread* thread = Thread::Current(); | 
 |   Zone* zone = thread->zone(); | 
 |   Representation rep = kTagged; | 
 |   intptr_t nullable_cid = kDynamicCid; | 
 |   bool is_nullable = true; | 
 |  | 
 |   if (field.has_pragma()) { | 
 |     const intptr_t cid = MethodRecognizer::ResultCidFromPragma(field); | 
 |     if (cid != kDynamicCid) { | 
 |       nullable_cid = cid; | 
 |       is_nullable = false; | 
 |     } else if (MethodRecognizer::HasNonNullableResultTypeFromPragma(field)) { | 
 |       is_nullable = false; | 
 |     } | 
 |   } | 
 |  | 
 |   AbstractType& type = AbstractType::ZoneHandle(zone, field.type()); | 
 |   if (type.IsStrictlyNonNullable()) { | 
 |     is_nullable = false; | 
 |   } | 
 |  | 
 |   FieldGuardState field_guard_state(field); | 
 |  | 
 |   bool used_guarded_state = false; | 
 |   if (field_guard_state.guarded_cid() != kIllegalCid && | 
 |       field_guard_state.guarded_cid() != kDynamicCid) { | 
 |     // Use guarded state if it is more precise then what we already have. | 
 |     if (nullable_cid == kDynamicCid) { | 
 |       nullable_cid = field_guard_state.guarded_cid(); | 
 |       used_guarded_state = true; | 
 |     } | 
 |  | 
 |     if (is_nullable && !field_guard_state.is_nullable()) { | 
 |       is_nullable = false; | 
 |       used_guarded_state = true; | 
 |     } | 
 |   } | 
 |  | 
 |   if (field.needs_load_guard()) { | 
 |     // Should be kept in sync with LoadStaticFieldInstr::ComputeType. | 
 |     type = Type::DynamicType(); | 
 |     nullable_cid = kDynamicCid; | 
 |     is_nullable = true; | 
 |     used_guarded_state = false; | 
 |   } | 
 |  | 
 |   const bool is_unboxed = field.is_unboxed(); | 
 |   if (is_unboxed) { | 
 |     is_nullable = false; | 
 |     switch (field_guard_state.guarded_cid()) { | 
 |       case kDoubleCid: | 
 |         rep = kUnboxedDouble; | 
 |         break; | 
 |       case kFloat32x4Cid: | 
 |         rep = kUnboxedFloat32x4; | 
 |         break; | 
 |       case kFloat64x2Cid: | 
 |         rep = kUnboxedFloat64x2; | 
 |         break; | 
 |       default: | 
 |         rep = kUnboxedInt64; | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   Class& owner = Class::Handle(zone, field.Owner()); | 
 |   const Slot& slot = GetCanonicalSlot( | 
 |       thread, Kind::kDartField, | 
 |       IsImmutableBit::encode((field.is_final() && !field.is_late()) || | 
 |                              field.is_const()) | | 
 |           IsNullableBit::encode(is_nullable) | | 
 |           IsGuardedBit::encode(used_guarded_state) | | 
 |           IsCompressedBit::encode( | 
 |               compiler::target::Class::HasCompressedPointers(owner)) | | 
 |           IsSentinelVisibleBit::encode(field.is_late() && field.is_final() && | 
 |                                        !field.has_initializer()) | | 
 |           IsUnboxedBit::encode(is_unboxed), | 
 |       nullable_cid, compiler::target::Field::OffsetOf(field), &field, &type, | 
 |       rep, field_guard_state); | 
 |  | 
 |   // If properties of this slot were based on the guarded state make sure | 
 |   // to add the field to the list of guarded fields. Note that during background | 
 |   // compilation we might have two field clones that have incompatible guarded | 
 |   // state - however both of these clones would correspond to the same slot. | 
 |   // That is why we check the is_guarded_field() property of the slot rather | 
 |   // than look at the current guarded state of the field, because current | 
 |   // guarded state of the field might be set to kDynamicCid, while it was | 
 |   // set to something more concrete when the slot was created. | 
 |   // Note that we could have created this slot during an unsuccessful inlining | 
 |   // attempt where we built and discarded the graph, in this case guarded | 
 |   // fields associated with that graph are also discarded. However the slot | 
 |   // itself stays behind in the compilation global cache. Thus we must always | 
 |   // try to add it to the list of guarded fields of the current function. | 
 |   if (slot.is_guarded_field()) { | 
 |     if (thread->isolate_group()->use_field_guards()) { | 
 |       ASSERT(parsed_function != nullptr); | 
 |       parsed_function->AddToGuardedFields(&slot.field()); | 
 |     } else { | 
 |       // In precompiled mode we use guarded_cid field for type information | 
 |       // inferred by TFA. | 
 |       ASSERT(CompilerState::Current().is_aot()); | 
 |     } | 
 |   } | 
 |  | 
 |   return slot; | 
 | } | 
 |  | 
 | CompileType Slot::ComputeCompileType() const { | 
 |   // If we unboxed the slot, we may know a more precise type. | 
 |   switch (representation()) { | 
 | #if defined(TARGET_ARCH_IS_32_BIT) | 
 |     // Int32/Uint32 values are not guaranteed to fit in a Smi. | 
 |     case kUnboxedInt32: | 
 |     case kUnboxedUint32: | 
 | #endif | 
 |     case kUnboxedInt64: | 
 |       if (nullable_cid() == kDynamicCid) { | 
 |         return CompileType::Int(); | 
 |       } | 
 |       break; | 
 | #if defined(TARGET_ARCH_IS_64_BIT) | 
 |     // Int32/Uint32 values are guaranteed to fit in a Smi. | 
 |     case kUnboxedInt32: | 
 |     case kUnboxedUint32: | 
 | #endif | 
 |     case kUnboxedUint8: | 
 |       return CompileType::Smi(); | 
 |     case kUnboxedDouble: | 
 |       return CompileType::FromCid(kDoubleCid); | 
 |     case kUnboxedInt32x4: | 
 |       return CompileType::FromCid(kInt32x4Cid); | 
 |     case kUnboxedFloat32x4: | 
 |       return CompileType::FromCid(kFloat32x4Cid); | 
 |     case kUnboxedFloat64x2: | 
 |       return CompileType::FromCid(kFloat64x2Cid); | 
 |     default: | 
 |       break; | 
 |   } | 
 |  | 
 |   return CompileType(is_nullable(), is_sentinel_visible(), nullable_cid(), | 
 |                      nullable_cid() == kDynamicCid ? static_type_ : nullptr); | 
 | } | 
 |  | 
 | const AbstractType& Slot::static_type() const { | 
 |   return static_type_ != nullptr ? *static_type_ : Object::null_abstract_type(); | 
 | } | 
 |  | 
 | const char* Slot::Name() const { | 
 |   if (IsLocalVariable()) { | 
 |     return DataAs<const String>()->ToCString(); | 
 |   } else if (IsDartField()) { | 
 |     return String::Handle(field().name()).ToCString(); | 
 |   } else { | 
 |     return DataAs<const char>(); | 
 |   } | 
 | } | 
 |  | 
 | bool Slot::Equals(const Slot& other) const { | 
 |   if (kind_ != other.kind_ || offset_in_bytes_ != other.offset_in_bytes_) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   switch (kind_) { | 
 |     case Kind::kTypeArguments: | 
 |     case Kind::kTypeArgumentsIndex: | 
 |     case Kind::kArrayElement: | 
 |     case Kind::kRecordField: | 
 |       return true; | 
 |  | 
 |     case Kind::kCapturedVariable: | 
 |       return (flags_ == other.flags_) && | 
 |              (DataAs<const String>()->ptr() == | 
 |               other.DataAs<const String>()->ptr()) && | 
 |              static_type_->Equals(*(other.static_type_)); | 
 |  | 
 |     case Kind::kDartField: | 
 |       return other.DataAs<const Field>()->Original() == | 
 |              DataAs<const Field>()->Original(); | 
 |  | 
 |     default: | 
 |       UNREACHABLE(); | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | uword Slot::Hash() const { | 
 |   uword result = (static_cast<int8_t>(kind_) * 63 + offset_in_bytes_) * 31; | 
 |   if (IsDartField()) { | 
 |     result += String::Handle(DataAs<const Field>()->name()).Hash(); | 
 |   } else if (IsLocalVariable()) { | 
 |     result += DataAs<const String>()->Hash(); | 
 |   } | 
 |   return result; | 
 | } | 
 |  | 
 | }  // namespace dart |