// 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/stack_frame.h" #include "vm/visitor.h" namespace dart { 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"); } untag()->Validate(isolate_group); } void UntaggedObject::Validate(IsolateGroup* isolate_group) const { // Validate that the tags_ field is sensible. uword tags = tags_; if (IsNewObject()) { if (!NewOrEvacuationCandidateBit::decode(tags)) { FATAL("New object missing kNewBit: %" Px "\n", tags); } if (OldAndNotRememberedBit::decode(tags)) { FATAL("New object has kOldAndNotRememberedBit: %" Px "\n", tags); } } const intptr_t class_id = ClassIdTag::decode(tags); if (!isolate_group->class_table()->IsValidIndex(class_id)) { FATAL("Invalid class id encountered %" Pd "\n", class_id); } if (class_id == kNullCid && isolate_group->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)) { FATAL( "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 UntaggedObject::HeapSizeFromClass(uword 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(this); intptr_t pointer_offsets_length = Code::PtrOffBits::decode(raw_code->untag()->state_bits_); instance_size = Code::InstanceSize(pointer_offsets_length); break; } case kInstructionsCid: { const InstructionsPtr raw_instructions = static_cast(this); intptr_t instructions_size = Instructions::Size(raw_instructions); instance_size = Instructions::InstanceSize(instructions_size); break; } case kInstructionsSectionCid: { const InstructionsSectionPtr raw_section = static_cast(this); intptr_t section_size = InstructionsSection::Size(raw_section); instance_size = InstructionsSection::InstanceSize(section_size); break; } case kClosureCid: { const ClosurePtr raw_closure = static_cast(this); intptr_t num_elements = UntaggedClosure::LengthBits::decode( Smi::Value(raw_closure->untag()->length_and_flags())); instance_size = Closure::InstanceSize(num_elements); break; } case kContextCid: { const ContextPtr raw_context = static_cast(this); intptr_t num_variables = raw_context->untag()->num_variables_; instance_size = Context::InstanceSize(num_variables); break; } case kContextScopeCid: { const ContextScopePtr raw_context_scope = static_cast(this); intptr_t num_variables = raw_context_scope->untag()->num_variables_; instance_size = ContextScope::InstanceSize(num_variables); break; } case kOneByteStringCid: { const OneByteStringPtr raw_string = static_cast(this); intptr_t string_length = Smi::Value(raw_string->untag()->length()); instance_size = OneByteString::InstanceSize(string_length); break; } case kTwoByteStringCid: { const TwoByteStringPtr raw_string = static_cast(this); intptr_t string_length = Smi::Value(raw_string->untag()->length()); instance_size = TwoByteString::InstanceSize(string_length); break; } case kArrayCid: case kImmutableArrayCid: { const ArrayPtr raw_array = static_cast(this); intptr_t array_length = Smi::Value(raw_array->untag()->length()); instance_size = Array::InstanceSize(array_length); break; } case kWeakArrayCid: { const WeakArrayPtr raw_array = static_cast(this); intptr_t array_length = Smi::Value(raw_array->untag()->length()); instance_size = WeakArray::InstanceSize(array_length); break; } case kObjectPoolCid: { const ObjectPoolPtr raw_object_pool = static_cast(this); intptr_t len = raw_object_pool->untag()->length_; instance_size = ObjectPool::InstanceSize(len); break; } case kRecordCid: { const RecordPtr raw_record = static_cast(this); intptr_t num_fields = RecordShape(raw_record->untag()->shape()).num_fields(); instance_size = Record::InstanceSize(num_fields); break; } #define SIZE_FROM_CLASS(clazz) case kTypedData##clazz##Cid: CLASS_LIST_TYPED_DATA(SIZE_FROM_CLASS) { const TypedDataPtr raw_obj = static_cast(this); intptr_t array_len = Smi::Value(raw_obj->untag()->length()); intptr_t lengthInBytes = array_len * TypedData::ElementSizeInBytes(class_id); instance_size = TypedData::InstanceSize(lengthInBytes); break; } #undef SIZE_FROM_CLASS case kPointerCid: instance_size = Pointer::InstanceSize(); break; case kSuspendStateCid: { const SuspendStatePtr raw_suspend_state = static_cast(this); intptr_t frame_capacity = raw_suspend_state->untag()->frame_capacity(); instance_size = SuspendState::InstanceSize(frame_capacity); break; } case kTypeArgumentsCid: { const TypeArgumentsPtr raw_array = static_cast(this); intptr_t array_length = Smi::Value(raw_array->untag()->length()); instance_size = TypeArguments::InstanceSize(array_length); break; } case kPcDescriptorsCid: { const PcDescriptorsPtr raw_descriptors = static_cast(this); intptr_t length = raw_descriptors->untag()->length_; instance_size = PcDescriptors::InstanceSize(length); break; } case kCodeSourceMapCid: { const CodeSourceMapPtr raw_code_source_map = static_cast(this); intptr_t length = raw_code_source_map->untag()->length_; instance_size = CodeSourceMap::InstanceSize(length); break; } case kCompressedStackMapsCid: { const CompressedStackMapsPtr maps = static_cast(this); intptr_t length = CompressedStackMaps::PayloadSizeOf(maps); instance_size = CompressedStackMaps::InstanceSize(length); break; } case kLocalVarDescriptorsCid: { const LocalVarDescriptorsPtr raw_descriptors = static_cast(this); intptr_t num_descriptors = raw_descriptors->untag()->num_entries_; instance_size = LocalVarDescriptors::InstanceSize(num_descriptors); break; } case kExceptionHandlersCid: { const ExceptionHandlersPtr raw_handlers = static_cast(this); intptr_t num_handlers = raw_handlers->untag()->num_entries(); instance_size = ExceptionHandlers::InstanceSize(num_handlers); break; } case kFreeListElement: { uword addr = UntaggedObject::ToAddr(this); FreeListElement* element = reinterpret_cast(addr); instance_size = element->HeapSize(tags); break; } case kForwardingCorpse: { uword addr = UntaggedObject::ToAddr(this); ForwardingCorpse* element = reinterpret_cast(addr); instance_size = element->HeapSize(tags); 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) auto class_table = isolate_group->heap_walk_class_table(); if (!class_table->IsValidIndex(class_id) || !class_table->HasValidClassAt(class_id)) { FATAL("Invalid cid: %" Pd ", obj: %p, tags: %x. Corrupt heap?", class_id, this, static_cast(tags)); } ASSERT(class_table->SizeAt(class_id) > 0); #endif // DEBUG instance_size = isolate_group->heap_walk_class_table()->SizeAt(class_id); } } ASSERT(instance_size != 0); #if defined(DEBUG) intptr_t tags_size = SizeTag::decode(tags); if ((instance_size != tags_size) && (tags_size != 0)) { // Array::Truncate could be in the process of shrinking the array. // Unfortunately, we cannot do a sanity check by reload tags here expecting // to eventually see consistent sizes because another scavenge worker may // have already replaced the tags with a forwarding pointer. But the // truncation should be ensuring the heap remains iterable no matter what // intermediate state we see, so instead check there appears to be a filler // covering the gap between the two sizes. if (class_id == kArrayCid) { intptr_t smaller_size = Utils::Minimum(tags_size, instance_size); intptr_t larger_size = Utils::Maximum(tags_size, instance_size); ObjectPtr filler = FromAddr(ToAddr(this) + smaller_size); intptr_t filler_size = filler->untag()->HeapSize(); ASSERT(smaller_size + filler_size == larger_size); } else { FATAL("Size mismatch: %" Pd " from class vs %" Pd " from tags %" Px "\n", instance_size, tags_size, tags); } } #endif // DEBUG return instance_size; } intptr_t UntaggedObject::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(this); \ size = Untagged##clazz::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(this); size = UntaggedTypedData::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(this); size = UntaggedExternalTypedData::VisitExternalTypedDataPointers(raw_obj, visitor); break; } #undef RAW_VISITPOINTERS case kByteDataViewCid: case kUnmodifiableByteDataViewCid: #define RAW_VISITPOINTERS(clazz) \ case kTypedData##clazz##ViewCid: \ case kUnmodifiableTypedData##clazz##ViewCid: CLASS_LIST_TYPED_DATA(RAW_VISITPOINTERS) { auto raw_obj = static_cast(this); size = UntaggedTypedDataView::VisitTypedDataViewPointers(raw_obj, visitor); break; } #undef RAW_VISITPOINTERS case kByteBufferCid: { InstancePtr raw_obj = static_cast(this); size = UntaggedInstance::VisitInstancePointers(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 case kFreeListElement: { uword addr = UntaggedObject::ToAddr(this); FreeListElement* element = reinterpret_cast(addr); size = element->HeapSize(); break; } case kForwardingCorpse: { uword addr = UntaggedObject::ToAddr(this); ForwardingCorpse* forwarder = reinterpret_cast(addr); size = forwarder->HeapSize(); break; } case kNullCid: case kNeverCid: size = HeapSize(); break; default: FATAL("Invalid cid: %" Pd ", obj: %p, tags: %x. Corrupt heap?", class_id, this, static_cast(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 UntaggedObject::VisitPointersPrecise(ObjectPointerVisitor* visitor) { intptr_t class_id = GetClassId(); if ((class_id != kInstanceCid) && (class_id < kNumPredefinedCids)) { VisitPointersPredefined(visitor, class_id); return; } // N.B.: Not using the heap size! uword next_field_offset = visitor->class_table() ->At(class_id) ->untag() ->host_next_field_offset_in_words_ << kCompressedWordSizeLog2; ASSERT(next_field_offset > 0); uword obj_addr = UntaggedObject::ToAddr(this); uword from = obj_addr + sizeof(UntaggedObject); uword to = obj_addr + next_field_offset - kCompressedWordSize; const auto first = reinterpret_cast(from); const auto last = reinterpret_cast(to); const auto unboxed_fields_bitmap = visitor->class_table()->GetUnboxedFieldsMapAt(class_id); if (!unboxed_fields_bitmap.IsEmpty()) { intptr_t bit = sizeof(UntaggedObject) / kCompressedWordSize; for (CompressedObjectPtr* current = first; current <= last; current++) { if (!unboxed_fields_bitmap.Get(bit++)) { visitor->VisitCompressedPointers(heap_base(), current, current); } } } else { visitor->VisitCompressedPointers(heap_base(), first, last); } } // 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 Untagged##Type::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->untag()->from(), raw_obj->untag()->to()); \ return Type::InstanceSize(); \ } #if !defined(DART_COMPRESSED_POINTERS) #define COMPRESSED_VISITOR(Type) REGULAR_VISITOR(Type) #else #define COMPRESSED_VISITOR(Type) \ intptr_t Untagged##Type::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_COMPRESSED(Type); \ visitor->VisitCompressedPointers(raw_obj->heap_base(), \ raw_obj->untag()->from(), \ raw_obj->untag()->to()); \ return Type::InstanceSize(); \ } #endif // 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 // specialized VisitTypedDataViewPointers #define TYPED_DATA_VIEW_VISITOR(Type) \ intptr_t Untagged##Type::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_COMPRESSED(Type); \ visitor->VisitTypedDataViewPointers(raw_obj, raw_obj->untag()->from(), \ raw_obj->untag()->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 Untagged##Type::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->untag()->from(), \ raw_obj->untag()->to(length)); \ return Type::InstanceSize(length); \ } #if !defined(DART_COMPRESSED_POINTERS) #define VARIABLE_COMPRESSED_VISITOR(Type, get_length) \ VARIABLE_VISITOR(Type, get_length) #else #define VARIABLE_COMPRESSED_VISITOR(Type, get_length) \ intptr_t Untagged##Type::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->VisitCompressedPointers(raw_obj->heap_base(), \ raw_obj->untag()->from(), \ raw_obj->untag()->to(length)); \ return Type::InstanceSize(length); \ } #endif // For fixed-length objects that don't have any pointers that need visiting. #define NULL_VISITOR(Type) \ intptr_t Untagged##Type::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 Untagged##Type::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 Untagged##Type::Visit##Type##Pointers( \ Type##Ptr raw_obj, ObjectPointerVisitor* visitor) { \ UNREACHABLE(); \ return 0; \ } COMPRESSED_VISITOR(Class) COMPRESSED_VISITOR(PatchClass) COMPRESSED_VISITOR(ClosureData) COMPRESSED_VISITOR(FfiTrampolineData) COMPRESSED_VISITOR(Script) COMPRESSED_VISITOR(Library) COMPRESSED_VISITOR(Namespace) COMPRESSED_VISITOR(KernelProgramInfo) COMPRESSED_VISITOR(WeakSerializationReference) VARIABLE_COMPRESSED_VISITOR(WeakArray, Smi::Value(raw_obj->untag()->length())) COMPRESSED_VISITOR(Type) COMPRESSED_VISITOR(FunctionType) COMPRESSED_VISITOR(RecordType) COMPRESSED_VISITOR(TypeParameter) COMPRESSED_VISITOR(Function) // Use relaxed atomic to allow concurrent marker access // objects filled in CopyMutableObjectGraph. VARIABLE_COMPRESSED_VISITOR( Closure, UntaggedClosure::LengthBits::decode(Smi::Value( raw_obj->untag()->length_and_flags()))) COMPRESSED_VISITOR(LibraryPrefix) COMPRESSED_VISITOR(Bytecode) REGULAR_VISITOR(SingleTargetCache) REGULAR_VISITOR(UnlinkedCall) NULL_VISITOR(MonomorphicSmiableCall) REGULAR_VISITOR(ICData) REGULAR_VISITOR(MegamorphicCache) COMPRESSED_VISITOR(ApiError) COMPRESSED_VISITOR(LanguageError) COMPRESSED_VISITOR(UnhandledException) COMPRESSED_VISITOR(UnwindError) COMPRESSED_VISITOR(GrowableObjectArray) COMPRESSED_VISITOR(Map) COMPRESSED_VISITOR(Set) COMPRESSED_VISITOR(ExternalTypedData) TYPED_DATA_VIEW_VISITOR(TypedDataView) COMPRESSED_VISITOR(ReceivePort) COMPRESSED_VISITOR(StackTrace) COMPRESSED_VISITOR(RegExp) COMPRESSED_VISITOR(WeakProperty) COMPRESSED_VISITOR(WeakReference) COMPRESSED_VISITOR(Finalizer) COMPRESSED_VISITOR(FinalizerEntry) COMPRESSED_VISITOR(NativeFinalizer) COMPRESSED_VISITOR(MirrorReference) COMPRESSED_VISITOR(UserTag) REGULAR_VISITOR(SubtypeTestCache) COMPRESSED_VISITOR(LoadingUnit) COMPRESSED_VISITOR(TypeParameters) VARIABLE_COMPRESSED_VISITOR(TypeArguments, Smi::Value(raw_obj->untag()->length())) VARIABLE_COMPRESSED_VISITOR(LocalVarDescriptors, raw_obj->untag()->num_entries_) VARIABLE_COMPRESSED_VISITOR(ExceptionHandlers, raw_obj->untag()->num_entries()) VARIABLE_COMPRESSED_VISITOR(Context, raw_obj->untag()->num_variables_) // relaxed: for Array truncation VARIABLE_COMPRESSED_VISITOR( Array, Smi::Value(raw_obj->untag()->length())) VARIABLE_COMPRESSED_VISITOR( TypedData, TypedData::ElementSizeInBytes(raw_obj->GetClassIdOfHeapObject()) * Smi::Value(raw_obj->untag()->length())) VARIABLE_COMPRESSED_VISITOR(ContextScope, raw_obj->untag()->num_variables_) VARIABLE_COMPRESSED_VISITOR(Record, RecordShape(raw_obj->untag()->shape()).num_fields()) NULL_VISITOR(Sentinel) REGULAR_VISITOR(InstructionsTable) 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) COMPRESSED_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->untag()->length_) VARIABLE_NULL_VISITOR(CodeSourceMap, raw_obj->untag()->length_) VARIABLE_NULL_VISITOR(CompressedStackMaps, CompressedStackMaps::PayloadSizeOf(raw_obj)) VARIABLE_NULL_VISITOR(OneByteString, Smi::Value(raw_obj->untag()->length())) VARIABLE_NULL_VISITOR(TwoByteString, Smi::Value(raw_obj->untag()->length())) // Abstract types don't have their visitor called. UNREACHABLE_VISITOR(AbstractType) UNREACHABLE_VISITOR(CallSiteData) UNREACHABLE_VISITOR(TypedDataBase) UNREACHABLE_VISITOR(Error) UNREACHABLE_VISITOR(FinalizerBase) UNREACHABLE_VISITOR(Number) UNREACHABLE_VISITOR(Integer) UNREACHABLE_VISITOR(String) UNREACHABLE_VISITOR(FutureOr) // Smi has no heap representation. UNREACHABLE_VISITOR(Smi) intptr_t UntaggedField::VisitFieldPointers(FieldPtr raw_obj, ObjectPointerVisitor* visitor) { ASSERT(raw_obj->IsHeapObject()); ASSERT_COMPRESSED(Field); visitor->VisitCompressedPointers( raw_obj->heap_base(), raw_obj->untag()->from(), raw_obj->untag()->to()); if (visitor->trace_values_through_fields()) { if (Field::StaticBit::decode(raw_obj->untag()->kind_bits_)) { visitor->isolate_group()->ForEachIsolate( [&](Isolate* isolate) { intptr_t index = Smi::Value(raw_obj->untag()->host_offset_or_field_id()); visitor->VisitPointer(&isolate->field_table()->table()[index]); }, /*at_safepoint=*/true); } } return Field::InstanceSize(); } intptr_t UntaggedSuspendState::VisitSuspendStatePointers( SuspendStatePtr raw_obj, ObjectPointerVisitor* visitor) { ASSERT(raw_obj->IsHeapObject()); ASSERT_COMPRESSED(SuspendState); if (visitor->CanVisitSuspendStatePointers(raw_obj)) { visitor->VisitCompressedPointers( raw_obj->heap_base(), raw_obj->untag()->from(), raw_obj->untag()->to()); const uword pc = raw_obj->untag()->pc_; if (pc != 0) { Thread* thread = Thread::Current(); ASSERT(thread != nullptr); ASSERT(thread->isolate_group() == visitor->isolate_group()); const uword sp = reinterpret_cast(raw_obj->untag()->payload()); StackFrame frame(thread); frame.pc_ = pc; frame.sp_ = sp; frame.fp_ = sp + raw_obj->untag()->frame_size_; frame.VisitObjectPointers(visitor); } } return SuspendState::InstanceSize(raw_obj->untag()->frame_capacity()); } bool UntaggedCode::ContainsPC(const ObjectPtr raw_obj, uword pc) { if (!raw_obj->IsCode()) return false; auto const raw_code = static_cast(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 UntaggedCode::VisitCodePointers(CodePtr raw_obj, ObjectPointerVisitor* visitor) { visitor->VisitPointers(raw_obj->untag()->from(), raw_obj->untag()->to()); UntaggedCode* obj = raw_obj->untag(); 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(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 UntaggedBytecode::ContainsPC(ObjectPtr raw_obj, uword pc) { if (raw_obj->IsBytecode()) { BytecodePtr raw_bytecode = static_cast(raw_obj); uword start = raw_bytecode->untag()->instructions_; uword size = raw_bytecode->untag()->instructions_size_; return (pc - start) <= size; // pc may point past last instruction. } return false; } intptr_t UntaggedObjectPool::VisitObjectPoolPointers( ObjectPoolPtr raw_obj, ObjectPointerVisitor* visitor) { const intptr_t length = raw_obj->untag()->length_; UntaggedObjectPool::Entry* entries = raw_obj->untag()->data(); uint8_t* entry_bits = raw_obj->untag()->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) { visitor->VisitPointer(&entries[i].raw_obj_); } } return ObjectPool::InstanceSize(length); } bool UntaggedInstructions::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 UntaggedInstance::VisitInstancePointers( InstancePtr raw_obj, ObjectPointerVisitor* visitor) { // Make sure that we got here with the tagged pointer as this. ASSERT(raw_obj->IsHeapObject()); uword tags = raw_obj->untag()->tags_; intptr_t instance_size = SizeTag::decode(tags); if (instance_size == 0) { instance_size = visitor->class_table()->SizeAt(raw_obj->GetClassIdOfHeapObject()); } // Calculate the first and last raw object pointer fields. uword obj_addr = UntaggedObject::ToAddr(raw_obj); uword from = obj_addr + sizeof(UntaggedObject); uword to = obj_addr + instance_size - kCompressedWordSize; visitor->VisitCompressedPointers(raw_obj->heap_base(), reinterpret_cast(from), reinterpret_cast(to)); return instance_size; } intptr_t UntaggedImmutableArray::VisitImmutableArrayPointers( ImmutableArrayPtr raw_obj, ObjectPointerVisitor* visitor) { return UntaggedArray::VisitArrayPointers(raw_obj, visitor); } intptr_t UntaggedConstMap::VisitConstMapPointers( ConstMapPtr raw_obj, ObjectPointerVisitor* visitor) { return UntaggedMap::VisitMapPointers(raw_obj, visitor); } intptr_t UntaggedConstSet::VisitConstSetPointers( ConstSetPtr raw_obj, ObjectPointerVisitor* visitor) { return UntaggedSet::VisitSetPointers(raw_obj, visitor); } void UntaggedObject::RememberCard(ObjectPtr const* slot) { Page::Of(static_cast(this))->RememberCard(slot); } #if defined(DART_COMPRESSED_POINTERS) void UntaggedObject::RememberCard(CompressedObjectPtr const* slot) { Page::Of(static_cast(this))->RememberCard(slot); } #endif const char* UntaggedPcDescriptors::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 UntaggedPcDescriptors::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