| // Copyright (c) 2012, 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/raw_object.h" |
| |
| #include "vm/class_table.h" |
| #include "vm/dart.h" |
| #include "vm/heap/become.h" |
| #include "vm/heap/freelist.h" |
| #include "vm/isolate.h" |
| #include "vm/isolate_reload.h" |
| #include "vm/object.h" |
| #include "vm/runtime_entry.h" |
| #include "vm/visitor.h" |
| |
| namespace dart { |
| |
| bool ObjectLayout::InVMIsolateHeap() const { |
| // All "vm-isolate" objects are pre-marked and in old space |
| // (see [Object::FinalizeVMIsolate]). |
| if (!IsOldObject() || !IsMarked()) return false; |
| |
| auto heap = Dart::vm_isolate()->heap(); |
| ASSERT(heap->UsedInWords(Heap::kNew) == 0); |
| return heap->old_space()->ContainsUnsafe(ToAddr(this)); |
| } |
| |
| void ObjectPtr::Validate(IsolateGroup* isolate_group) const { |
| // All Smi values are valid. |
| if (!IsHeapObject()) { |
| return; |
| } |
| // Slightly more readable than a segfault. |
| if (tagged_pointer_ == kHeapObjectTag) { |
| FATAL("RAW_NULL encountered"); |
| } |
| ptr()->Validate(isolate_group); |
| } |
| |
| void ObjectLayout::Validate(IsolateGroup* isolate_group) const { |
| if (static_cast<uword>(Object::void_class_) == kHeapObjectTag) { |
| // Validation relies on properly initialized class classes. Skip if the |
| // VM is still being initialized. |
| return; |
| } |
| // Validate that the tags_ field is sensible. |
| uint32_t tags = tags_; |
| if (IsNewObject()) { |
| if (!NewBit::decode(tags)) { |
| FATAL1("New object missing kNewBit: %x\n", tags); |
| } |
| if (OldBit::decode(tags)) { |
| FATAL1("New object has kOldBit: %x\n", tags); |
| } |
| if (OldAndNotMarkedBit::decode(tags)) { |
| FATAL1("New object has kOldAndNotMarkedBit: %x\n", tags); |
| } |
| if (OldAndNotRememberedBit::decode(tags)) { |
| FATAL1("New object has kOldAndNotRememberedBit: %x\n", tags); |
| } |
| } else { |
| if (NewBit::decode(tags)) { |
| FATAL1("Old object has kNewBit: %x\n", tags); |
| } |
| if (!OldBit::decode(tags)) { |
| FATAL1("Old object missing kOldBit: %x\n", tags); |
| } |
| } |
| const intptr_t class_id = ClassIdTag::decode(tags); |
| if (!isolate_group->shared_class_table()->IsValidIndex(class_id)) { |
| FATAL1("Invalid class id encountered %" Pd "\n", class_id); |
| } |
| if (class_id == kNullCid && |
| isolate_group->shared_class_table()->HasValidClassAt(class_id)) { |
| // Null class not yet initialized; skip. |
| return; |
| } |
| intptr_t size_from_tags = SizeTag::decode(tags); |
| intptr_t size_from_class = HeapSizeFromClass(tags); |
| if ((size_from_tags != 0) && (size_from_tags != size_from_class)) { |
| FATAL3( |
| "Inconsistent size encountered " |
| "cid: %" Pd ", size_from_tags: %" Pd ", size_from_class: %" Pd "\n", |
| class_id, size_from_tags, size_from_class); |
| } |
| } |
| |
| // Can't look at the class object because it can be called during |
| // compaction when the class objects are moving. Can use the class |
| // id in the header and the sizes in the Class Table. |
| // Cannot deference ptr()->tags_. May dereference other parts of the object. |
| intptr_t ObjectLayout::HeapSizeFromClass(uint32_t tags) const { |
| intptr_t class_id = ClassIdTag::decode(tags); |
| intptr_t instance_size = 0; |
| switch (class_id) { |
| case kCodeCid: { |
| const CodePtr raw_code = static_cast<const CodePtr>(this); |
| intptr_t pointer_offsets_length = |
| Code::PtrOffBits::decode(raw_code->ptr()->state_bits_); |
| instance_size = Code::InstanceSize(pointer_offsets_length); |
| break; |
| } |
| case kInstructionsCid: { |
| const InstructionsPtr raw_instructions = |
| static_cast<const InstructionsPtr>(this); |
| intptr_t instructions_size = Instructions::Size(raw_instructions); |
| instance_size = Instructions::InstanceSize(instructions_size); |
| break; |
| } |
| case kInstructionsSectionCid: { |
| const InstructionsSectionPtr raw_section = |
| static_cast<const InstructionsSectionPtr>(this); |
| intptr_t section_size = InstructionsSection::Size(raw_section); |
| instance_size = InstructionsSection::InstanceSize(section_size); |
| break; |
| } |
| case kContextCid: { |
| const ContextPtr raw_context = static_cast<const ContextPtr>(this); |
| intptr_t num_variables = raw_context->ptr()->num_variables_; |
| instance_size = Context::InstanceSize(num_variables); |
| break; |
| } |
| case kContextScopeCid: { |
| const ContextScopePtr raw_context_scope = |
| static_cast<const ContextScopePtr>(this); |
| intptr_t num_variables = raw_context_scope->ptr()->num_variables_; |
| instance_size = ContextScope::InstanceSize(num_variables); |
| break; |
| } |
| case kOneByteStringCid: { |
| const OneByteStringPtr raw_string = |
| static_cast<const OneByteStringPtr>(this); |
| intptr_t string_length = Smi::Value(raw_string->ptr()->length_); |
| instance_size = OneByteString::InstanceSize(string_length); |
| break; |
| } |
| case kTwoByteStringCid: { |
| const TwoByteStringPtr raw_string = |
| static_cast<const TwoByteStringPtr>(this); |
| intptr_t string_length = Smi::Value(raw_string->ptr()->length_); |
| instance_size = TwoByteString::InstanceSize(string_length); |
| break; |
| } |
| case kArrayCid: |
| case kImmutableArrayCid: { |
| const ArrayPtr raw_array = static_cast<const ArrayPtr>(this); |
| intptr_t array_length = Smi::Value(raw_array->ptr()->length_); |
| instance_size = Array::InstanceSize(array_length); |
| break; |
| } |
| case kObjectPoolCid: { |
| const ObjectPoolPtr raw_object_pool = |
| static_cast<const ObjectPoolPtr>(this); |
| intptr_t len = raw_object_pool->ptr()->length_; |
| instance_size = ObjectPool::InstanceSize(len); |
| break; |
| } |
| #define SIZE_FROM_CLASS(clazz) case kTypedData##clazz##Cid: |
| CLASS_LIST_TYPED_DATA(SIZE_FROM_CLASS) { |
| const TypedDataPtr raw_obj = static_cast<const TypedDataPtr>(this); |
| intptr_t array_len = Smi::Value(raw_obj->ptr()->length_); |
| intptr_t lengthInBytes = |
| array_len * TypedData::ElementSizeInBytes(class_id); |
| instance_size = TypedData::InstanceSize(lengthInBytes); |
| break; |
| } |
| #undef SIZE_FROM_CLASS |
| case kFfiPointerCid: |
| instance_size = Pointer::InstanceSize(); |
| break; |
| case kTypeArgumentsCid: { |
| const TypeArgumentsPtr raw_array = |
| static_cast<const TypeArgumentsPtr>(this); |
| intptr_t array_length = Smi::Value(raw_array->ptr()->length_); |
| instance_size = TypeArguments::InstanceSize(array_length); |
| break; |
| } |
| case kPcDescriptorsCid: { |
| const PcDescriptorsPtr raw_descriptors = |
| static_cast<const PcDescriptorsPtr>(this); |
| intptr_t length = raw_descriptors->ptr()->length_; |
| instance_size = PcDescriptors::InstanceSize(length); |
| break; |
| } |
| case kCodeSourceMapCid: { |
| const CodeSourceMapPtr raw_code_source_map = |
| static_cast<const CodeSourceMapPtr>(this); |
| intptr_t length = raw_code_source_map->ptr()->length_; |
| instance_size = CodeSourceMap::InstanceSize(length); |
| break; |
| } |
| case kCompressedStackMapsCid: { |
| const CompressedStackMapsPtr maps = |
| static_cast<const CompressedStackMapsPtr>(this); |
| intptr_t length = CompressedStackMaps::PayloadSizeOf(maps); |
| instance_size = CompressedStackMaps::InstanceSize(length); |
| break; |
| } |
| case kLocalVarDescriptorsCid: { |
| const LocalVarDescriptorsPtr raw_descriptors = |
| static_cast<const LocalVarDescriptorsPtr>(this); |
| intptr_t num_descriptors = raw_descriptors->ptr()->num_entries_; |
| instance_size = LocalVarDescriptors::InstanceSize(num_descriptors); |
| break; |
| } |
| case kExceptionHandlersCid: { |
| const ExceptionHandlersPtr raw_handlers = |
| static_cast<const ExceptionHandlersPtr>(this); |
| intptr_t num_handlers = raw_handlers->ptr()->num_entries_; |
| instance_size = ExceptionHandlers::InstanceSize(num_handlers); |
| break; |
| } |
| case kFreeListElement: { |
| uword addr = ObjectLayout::ToAddr(this); |
| FreeListElement* element = reinterpret_cast<FreeListElement*>(addr); |
| instance_size = element->HeapSize(); |
| break; |
| } |
| case kForwardingCorpse: { |
| uword addr = ObjectLayout::ToAddr(this); |
| ForwardingCorpse* element = reinterpret_cast<ForwardingCorpse*>(addr); |
| instance_size = element->HeapSize(); |
| break; |
| } |
| case kWeakSerializationReferenceCid: { |
| instance_size = WeakSerializationReference::InstanceSize(); |
| break; |
| } |
| default: { |
| // Get the (constant) instance size out of the class object. |
| // TODO(koda): Add Size(ClassTable*) interface to allow caching in loops. |
| auto isolate_group = IsolateGroup::Current(); |
| #if defined(DEBUG) |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| auto reload_context = isolate_group->reload_context(); |
| const bool use_saved_class_table = |
| reload_context != nullptr ? reload_context->UseSavedSizeTableForGC() |
| : false; |
| #else |
| const bool use_saved_class_table = false; |
| #endif |
| |
| auto class_table = isolate_group->shared_class_table(); |
| ASSERT(use_saved_class_table || class_table->SizeAt(class_id) > 0); |
| if (!class_table->IsValidIndex(class_id) || |
| (!class_table->HasValidClassAt(class_id) && !use_saved_class_table)) { |
| FATAL3("Invalid cid: %" Pd ", obj: %p, tags: %x. Corrupt heap?", |
| class_id, this, static_cast<uint32_t>(tags)); |
| } |
| #endif // DEBUG |
| instance_size = isolate_group->GetClassSizeForHeapWalkAt(class_id); |
| } |
| } |
| ASSERT(instance_size != 0); |
| #if defined(DEBUG) |
| intptr_t tags_size = SizeTag::decode(tags); |
| if ((class_id == kArrayCid) && (instance_size > tags_size && tags_size > 0)) { |
| // TODO(22501): Array::MakeFixedLength could be in the process of shrinking |
| // the array (see comment therein), having already updated the tags but not |
| // yet set the new length. Wait a millisecond and try again. |
| int retries_remaining = 1000; // ... but not forever. |
| do { |
| OS::Sleep(1); |
| const ArrayPtr raw_array = static_cast<const ArrayPtr>(this); |
| intptr_t array_length = Smi::Value(raw_array->ptr()->length_); |
| instance_size = Array::InstanceSize(array_length); |
| } while ((instance_size > tags_size) && (--retries_remaining > 0)); |
| } |
| if ((instance_size != tags_size) && (tags_size != 0)) { |
| FATAL3("Size mismatch: %" Pd " from class vs %" Pd " from tags %x\n", |
| instance_size, tags_size, tags); |
| } |
| #endif // DEBUG |
| return instance_size; |
| } |
| |
| intptr_t ObjectLayout::VisitPointersPredefined(ObjectPointerVisitor* visitor, |
| intptr_t class_id) { |
| ASSERT(class_id < kNumPredefinedCids); |
| |
| intptr_t size = 0; |
| |
| switch (class_id) { |
| #define RAW_VISITPOINTERS(clazz) \ |
| case k##clazz##Cid: { \ |
| clazz##Ptr raw_obj = static_cast<clazz##Ptr>(this); \ |
| size = clazz##Layout::Visit##clazz##Pointers(raw_obj, visitor); \ |
| break; \ |
| } |
| CLASS_LIST_NO_OBJECT(RAW_VISITPOINTERS) |
| #undef RAW_VISITPOINTERS |
| #define RAW_VISITPOINTERS(clazz) case kTypedData##clazz##Cid: |
| CLASS_LIST_TYPED_DATA(RAW_VISITPOINTERS) { |
| TypedDataPtr raw_obj = static_cast<TypedDataPtr>(this); |
| size = TypedDataLayout::VisitTypedDataPointers(raw_obj, visitor); |
| break; |
| } |
| #undef RAW_VISITPOINTERS |
| #define RAW_VISITPOINTERS(clazz) case kExternalTypedData##clazz##Cid: |
| CLASS_LIST_TYPED_DATA(RAW_VISITPOINTERS) { |
| auto raw_obj = static_cast<ExternalTypedDataPtr>(this); |
| size = ExternalTypedDataLayout::VisitExternalTypedDataPointers(raw_obj, |
| visitor); |
| break; |
| } |
| #undef RAW_VISITPOINTERS |
| case kByteDataViewCid: |
| #define RAW_VISITPOINTERS(clazz) case kTypedData##clazz##ViewCid: |
| CLASS_LIST_TYPED_DATA(RAW_VISITPOINTERS) { |
| auto raw_obj = static_cast<TypedDataViewPtr>(this); |
| size = |
| TypedDataViewLayout::VisitTypedDataViewPointers(raw_obj, visitor); |
| break; |
| } |
| #undef RAW_VISITPOINTERS |
| case kByteBufferCid: { |
| InstancePtr raw_obj = static_cast<InstancePtr>(this); |
| size = InstanceLayout::VisitInstancePointers(raw_obj, visitor); |
| break; |
| } |
| case kFfiPointerCid: { |
| PointerPtr raw_obj = static_cast<PointerPtr>(this); |
| size = PointerLayout::VisitPointerPointers(raw_obj, visitor); |
| break; |
| } |
| case kFfiDynamicLibraryCid: { |
| DynamicLibraryPtr raw_obj = static_cast<DynamicLibraryPtr>(this); |
| size = |
| DynamicLibraryLayout::VisitDynamicLibraryPointers(raw_obj, visitor); |
| break; |
| } |
| #define RAW_VISITPOINTERS(clazz) case kFfi##clazz##Cid: |
| CLASS_LIST_FFI_TYPE_MARKER(RAW_VISITPOINTERS) { |
| // NativeType do not have any fields or type arguments. |
| size = HeapSize(); |
| break; |
| } |
| #undef RAW_VISITPOINTERS |
| #define RAW_VISITPOINTERS(clazz) case k##clazz##Cid: |
| CLASS_LIST_WASM(RAW_VISITPOINTERS) { |
| // These wasm types do not have any fields or type arguments. |
| size = HeapSize(); |
| break; |
| } |
| #undef RAW_VISITPOINTERS |
| case kFreeListElement: { |
| uword addr = ObjectLayout::ToAddr(this); |
| FreeListElement* element = reinterpret_cast<FreeListElement*>(addr); |
| size = element->HeapSize(); |
| break; |
| } |
| case kForwardingCorpse: { |
| uword addr = ObjectLayout::ToAddr(this); |
| ForwardingCorpse* forwarder = reinterpret_cast<ForwardingCorpse*>(addr); |
| size = forwarder->HeapSize(); |
| break; |
| } |
| case kNullCid: |
| case kNeverCid: |
| size = HeapSize(); |
| break; |
| default: |
| FATAL3("Invalid cid: %" Pd ", obj: %p, tags: %x. Corrupt heap?", class_id, |
| this, static_cast<uint32_t>(tags_)); |
| break; |
| } |
| |
| #if defined(DEBUG) |
| ASSERT(size != 0); |
| const intptr_t expected_size = HeapSize(); |
| |
| // In general we expect that visitors return exactly the same size that |
| // HeapSize would compute. However in case of Arrays we might have a |
| // discrepancy when concurrently visiting an array that is being shrunk with |
| // Array::MakeFixedLength: the visitor might have visited the full array while |
| // here we are observing a smaller HeapSize(). |
| ASSERT(size == expected_size || |
| (class_id == kArrayCid && size > expected_size)); |
| return size; // Prefer larger size. |
| #else |
| return size; |
| #endif |
| } |
| |
| void ObjectLayout::VisitPointersPrecise(Isolate* isolate, |
| ObjectPointerVisitor* visitor) { |
| intptr_t class_id = GetClassId(); |
| if (class_id < kNumPredefinedCids) { |
| VisitPointersPredefined(visitor, class_id); |
| return; |
| } |
| |
| // N.B.: Not using the heap size! |
| uword next_field_offset = isolate->GetClassForHeapWalkAt(class_id) |
| ->ptr() |
| ->host_next_field_offset_in_words_ |
| << kWordSizeLog2; |
| ASSERT(next_field_offset > 0); |
| uword obj_addr = ObjectLayout::ToAddr(this); |
| uword from = obj_addr + sizeof(ObjectLayout); |
| uword to = obj_addr + next_field_offset - kWordSize; |
| const auto first = reinterpret_cast<ObjectPtr*>(from); |
| const auto last = reinterpret_cast<ObjectPtr*>(to); |
| |
| #if defined(SUPPORT_UNBOXED_INSTANCE_FIELDS) |
| const auto unboxed_fields_bitmap = |
| visitor->shared_class_table()->GetUnboxedFieldsMapAt(class_id); |
| |
| if (!unboxed_fields_bitmap.IsEmpty()) { |
| intptr_t bit = sizeof(ObjectLayout) / kWordSize; |
| for (ObjectPtr* current = first; current <= last; current++) { |
| if (!unboxed_fields_bitmap.Get(bit++)) { |
| visitor->VisitPointer(current); |
| } |
| } |
| } else { |
| visitor->VisitPointers(first, last); |
| } |
| #else |
| visitor->VisitPointers(first, last); |
| #endif // defined(SUPPORT_UNBOXED_INSTANCE_FIELDS) |
| } |
| |
| bool ObjectLayout::FindObject(FindObjectVisitor* visitor) { |
| ASSERT(visitor != NULL); |
| return visitor->FindObject(static_cast<ObjectPtr>(this)); |
| } |
| |
| // Most objects are visited with this function. It calls the from() and to() |
| // methods on the raw object to get the first and last cells that need |
| // visiting. |
| #define REGULAR_VISITOR(Type) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| /* Make sure that we got here with the tagged pointer as this. */ \ |
| ASSERT(raw_obj->IsHeapObject()); \ |
| ASSERT_UNCOMPRESSED(Type); \ |
| visitor->VisitPointers(raw_obj->ptr()->from(), raw_obj->ptr()->to()); \ |
| return Type::InstanceSize(); \ |
| } |
| |
| // It calls the from() and to() methods on the raw object to get the first and |
| // last cells that need visiting. |
| // |
| // Though as opposed to Similar to [REGULAR_VISITOR] this visitor will call the |
| // specializd VisitTypedDataViewPointers |
| #define TYPED_DATA_VIEW_VISITOR(Type) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| /* Make sure that we got here with the tagged pointer as this. */ \ |
| ASSERT(raw_obj->IsHeapObject()); \ |
| ASSERT_UNCOMPRESSED(Type); \ |
| visitor->VisitTypedDataViewPointers(raw_obj, raw_obj->ptr()->from(), \ |
| raw_obj->ptr()->to()); \ |
| return Type::InstanceSize(); \ |
| } |
| |
| // For variable length objects. get_length is a code snippet that gets the |
| // length of the object, which is passed to InstanceSize and the to() method. |
| #define VARIABLE_VISITOR(Type, get_length) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| /* Make sure that we got here with the tagged pointer as this. */ \ |
| ASSERT(raw_obj->IsHeapObject()); \ |
| intptr_t length = get_length; \ |
| visitor->VisitPointers(raw_obj->ptr()->from(), \ |
| raw_obj->ptr()->to(length)); \ |
| return Type::InstanceSize(length); \ |
| } |
| |
| // For now there are no compressed pointers: |
| #define COMPRESSED_VISITOR(Type) REGULAR_VISITOR(Type) |
| #define VARIABLE_COMPRESSED_VISITOR(Type, get_length) \ |
| VARIABLE_VISITOR(Type, get_length) |
| |
| // For fixed-length objects that don't have any pointers that need visiting. |
| #define NULL_VISITOR(Type) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| /* Make sure that we got here with the tagged pointer as this. */ \ |
| ASSERT(raw_obj->IsHeapObject()); \ |
| ASSERT_NOTHING_TO_VISIT(Type); \ |
| return Type::InstanceSize(); \ |
| } |
| |
| // For objects that don't have any pointers that need visiting, but have a |
| // variable length. |
| #define VARIABLE_NULL_VISITOR(Type, get_length) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| /* Make sure that we got here with the tagged pointer as this. */ \ |
| ASSERT(raw_obj->IsHeapObject()); \ |
| ASSERT_NOTHING_TO_VISIT(Type); \ |
| intptr_t length = get_length; \ |
| return Type::InstanceSize(length); \ |
| } |
| |
| // For objects that are never instantiated on the heap. |
| #define UNREACHABLE_VISITOR(Type) \ |
| intptr_t Type##Layout::Visit##Type##Pointers( \ |
| Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ |
| UNREACHABLE(); \ |
| return 0; \ |
| } |
| |
| REGULAR_VISITOR(Class) |
| REGULAR_VISITOR(Bytecode) |
| REGULAR_VISITOR(Type) |
| REGULAR_VISITOR(TypeRef) |
| REGULAR_VISITOR(TypeParameter) |
| REGULAR_VISITOR(PatchClass) |
| REGULAR_VISITOR(Function) |
| COMPRESSED_VISITOR(Closure) |
| REGULAR_VISITOR(ClosureData) |
| REGULAR_VISITOR(SignatureData) |
| REGULAR_VISITOR(RedirectionData) |
| REGULAR_VISITOR(FfiTrampolineData) |
| REGULAR_VISITOR(Field) |
| REGULAR_VISITOR(Script) |
| REGULAR_VISITOR(Library) |
| REGULAR_VISITOR(LibraryPrefix) |
| REGULAR_VISITOR(Namespace) |
| REGULAR_VISITOR(ParameterTypeCheck) |
| REGULAR_VISITOR(SingleTargetCache) |
| REGULAR_VISITOR(UnlinkedCall) |
| REGULAR_VISITOR(MonomorphicSmiableCall) |
| REGULAR_VISITOR(ICData) |
| REGULAR_VISITOR(MegamorphicCache) |
| REGULAR_VISITOR(ApiError) |
| REGULAR_VISITOR(LanguageError) |
| REGULAR_VISITOR(UnhandledException) |
| REGULAR_VISITOR(UnwindError) |
| REGULAR_VISITOR(ExternalOneByteString) |
| REGULAR_VISITOR(ExternalTwoByteString) |
| COMPRESSED_VISITOR(GrowableObjectArray) |
| COMPRESSED_VISITOR(LinkedHashMap) |
| COMPRESSED_VISITOR(ExternalTypedData) |
| TYPED_DATA_VIEW_VISITOR(TypedDataView) |
| REGULAR_VISITOR(ReceivePort) |
| REGULAR_VISITOR(StackTrace) |
| REGULAR_VISITOR(RegExp) |
| REGULAR_VISITOR(WeakProperty) |
| REGULAR_VISITOR(MirrorReference) |
| REGULAR_VISITOR(UserTag) |
| REGULAR_VISITOR(SubtypeTestCache) |
| REGULAR_VISITOR(LoadingUnit) |
| REGULAR_VISITOR(KernelProgramInfo) |
| VARIABLE_VISITOR(TypeArguments, Smi::Value(raw_obj->ptr()->length_)) |
| VARIABLE_VISITOR(LocalVarDescriptors, raw_obj->ptr()->num_entries_) |
| VARIABLE_VISITOR(ExceptionHandlers, raw_obj->ptr()->num_entries_) |
| VARIABLE_VISITOR(Context, raw_obj->ptr()->num_variables_) |
| VARIABLE_COMPRESSED_VISITOR(Array, Smi::Value(raw_obj->ptr()->length_)) |
| VARIABLE_COMPRESSED_VISITOR( |
| TypedData, |
| TypedData::ElementSizeInBytes(raw_obj->GetClassId()) * |
| Smi::Value(raw_obj->ptr()->length_)) |
| VARIABLE_VISITOR(ContextScope, raw_obj->ptr()->num_variables_) |
| NULL_VISITOR(Mint) |
| NULL_VISITOR(Double) |
| NULL_VISITOR(Float32x4) |
| NULL_VISITOR(Int32x4) |
| NULL_VISITOR(Float64x2) |
| NULL_VISITOR(Bool) |
| NULL_VISITOR(Capability) |
| NULL_VISITOR(SendPort) |
| NULL_VISITOR(TransferableTypedData) |
| REGULAR_VISITOR(Pointer) |
| NULL_VISITOR(DynamicLibrary) |
| VARIABLE_NULL_VISITOR(Instructions, Instructions::Size(raw_obj)) |
| VARIABLE_NULL_VISITOR(InstructionsSection, InstructionsSection::Size(raw_obj)) |
| VARIABLE_NULL_VISITOR(PcDescriptors, raw_obj->ptr()->length_) |
| VARIABLE_NULL_VISITOR(CodeSourceMap, raw_obj->ptr()->length_) |
| VARIABLE_NULL_VISITOR(CompressedStackMaps, |
| CompressedStackMaps::PayloadSizeOf(raw_obj)) |
| VARIABLE_NULL_VISITOR(OneByteString, Smi::Value(raw_obj->ptr()->length_)) |
| VARIABLE_NULL_VISITOR(TwoByteString, Smi::Value(raw_obj->ptr()->length_)) |
| // Abstract types don't have their visitor called. |
| UNREACHABLE_VISITOR(AbstractType) |
| UNREACHABLE_VISITOR(CallSiteData) |
| UNREACHABLE_VISITOR(TypedDataBase) |
| UNREACHABLE_VISITOR(Error) |
| UNREACHABLE_VISITOR(Number) |
| UNREACHABLE_VISITOR(Integer) |
| UNREACHABLE_VISITOR(String) |
| UNREACHABLE_VISITOR(FutureOr) |
| // Smi has no heap representation. |
| UNREACHABLE_VISITOR(Smi) |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| NULL_VISITOR(WeakSerializationReference) |
| #else |
| REGULAR_VISITOR(WeakSerializationReference) |
| #endif |
| |
| bool CodeLayout::ContainsPC(const ObjectPtr raw_obj, uword pc) { |
| if (!raw_obj->IsCode()) return false; |
| auto const raw_code = static_cast<const CodePtr>(raw_obj); |
| const uword start = Code::PayloadStartOf(raw_code); |
| const uword size = Code::PayloadSizeOf(raw_code); |
| return (pc - start) <= size; // pc may point just past last instruction. |
| } |
| |
| intptr_t CodeLayout::VisitCodePointers(CodePtr raw_obj, |
| ObjectPointerVisitor* visitor) { |
| visitor->VisitPointers(raw_obj->ptr()->from(), raw_obj->ptr()->to()); |
| |
| CodeLayout* obj = raw_obj->ptr(); |
| intptr_t length = Code::PtrOffBits::decode(obj->state_bits_); |
| #if defined(TARGET_ARCH_IA32) |
| // On IA32 only we embed pointers to objects directly in the generated |
| // instructions. The variable portion of a Code object describes where to |
| // find those pointers for tracing. |
| if (Code::AliveBit::decode(obj->state_bits_)) { |
| uword entry_point = Code::PayloadStartOf(raw_obj); |
| for (intptr_t i = 0; i < length; i++) { |
| int32_t offset = obj->data()[i]; |
| visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(entry_point + offset)); |
| } |
| } |
| return Code::InstanceSize(length); |
| #else |
| // On all other architectures, objects are referenced indirectly through |
| // either an ObjectPool or Thread. |
| ASSERT(length == 0); |
| return Code::InstanceSize(0); |
| #endif |
| } |
| |
| bool BytecodeLayout::ContainsPC(ObjectPtr raw_obj, uword pc) { |
| if (raw_obj->IsBytecode()) { |
| BytecodePtr raw_bytecode = static_cast<BytecodePtr>(raw_obj); |
| uword start = raw_bytecode->ptr()->instructions_; |
| uword size = raw_bytecode->ptr()->instructions_size_; |
| return (pc - start) <= size; // pc may point past last instruction. |
| } |
| return false; |
| } |
| |
| intptr_t ObjectPoolLayout::VisitObjectPoolPointers( |
| ObjectPoolPtr raw_obj, |
| ObjectPointerVisitor* visitor) { |
| const intptr_t length = raw_obj->ptr()->length_; |
| ObjectPoolLayout::Entry* entries = raw_obj->ptr()->data(); |
| uint8_t* entry_bits = raw_obj->ptr()->entry_bits(); |
| for (intptr_t i = 0; i < length; ++i) { |
| ObjectPool::EntryType entry_type = |
| ObjectPool::TypeBits::decode(entry_bits[i]); |
| if ((entry_type == ObjectPool::EntryType::kTaggedObject) || |
| (entry_type == ObjectPool::EntryType::kNativeEntryData)) { |
| visitor->VisitPointer(&entries[i].raw_obj_); |
| } |
| } |
| return ObjectPool::InstanceSize(length); |
| } |
| |
| bool InstructionsLayout::ContainsPC(const InstructionsPtr raw_instr, uword pc) { |
| const uword start = Instructions::PayloadStart(raw_instr); |
| const uword size = Instructions::Size(raw_instr); |
| // We use <= instead of < here because the saved-pc can be outside the |
| // instruction stream if the last instruction is a call we don't expect to |
| // return (e.g. because it throws an exception). |
| return (pc - start) <= size; |
| } |
| |
| intptr_t InstanceLayout::VisitInstancePointers(InstancePtr raw_obj, |
| ObjectPointerVisitor* visitor) { |
| // Make sure that we got here with the tagged pointer as this. |
| ASSERT(raw_obj->IsHeapObject()); |
| uint32_t tags = raw_obj->ptr()->tags_; |
| intptr_t instance_size = SizeTag::decode(tags); |
| if (instance_size == 0) { |
| instance_size = visitor->isolate_group()->GetClassSizeForHeapWalkAt( |
| raw_obj->GetClassId()); |
| } |
| |
| // Calculate the first and last raw object pointer fields. |
| uword obj_addr = ObjectLayout::ToAddr(raw_obj); |
| uword from = obj_addr + sizeof(ObjectLayout); |
| uword to = obj_addr + instance_size - kWordSize; |
| visitor->VisitPointers(reinterpret_cast<ObjectPtr*>(from), |
| reinterpret_cast<ObjectPtr*>(to)); |
| return instance_size; |
| } |
| |
| intptr_t ImmutableArrayLayout::VisitImmutableArrayPointers( |
| ImmutableArrayPtr raw_obj, |
| ObjectPointerVisitor* visitor) { |
| return ArrayLayout::VisitArrayPointers(raw_obj, visitor); |
| } |
| |
| void ObjectLayout::RememberCard(ObjectPtr const* slot) { |
| OldPage::Of(static_cast<ObjectPtr>(this))->RememberCard(slot); |
| } |
| |
| DEFINE_LEAF_RUNTIME_ENTRY(void, |
| RememberCard, |
| 2, |
| uword /*ObjectPtr*/ object_in, |
| ObjectPtr* slot) { |
| ObjectPtr object = static_cast<ObjectPtr>(object_in); |
| ASSERT(object->IsOldObject()); |
| ASSERT(object->ptr()->IsCardRemembered()); |
| OldPage::Of(object)->RememberCard(slot); |
| } |
| END_LEAF_RUNTIME_ENTRY |
| |
| const char* PcDescriptorsLayout::KindToCString(Kind k) { |
| switch (k) { |
| #define ENUM_CASE(name, init) \ |
| case Kind::k##name: \ |
| return #name; |
| FOR_EACH_RAW_PC_DESCRIPTOR(ENUM_CASE) |
| #undef ENUM_CASE |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool PcDescriptorsLayout::ParseKind(const char* cstr, Kind* out) { |
| ASSERT(cstr != nullptr && out != nullptr); |
| #define ENUM_CASE(name, init) \ |
| if (strcmp(#name, cstr) == 0) { \ |
| *out = Kind::k##name; \ |
| return true; \ |
| } |
| FOR_EACH_RAW_PC_DESCRIPTOR(ENUM_CASE) |
| #undef ENUM_CASE |
| return false; |
| } |
| #undef PREFIXED_NAME |
| |
| } // namespace dart |