blob: 07b74a2cb8d7811a88e61976f6de0284c033940f [file] [log] [blame]
// 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 {
bool UntaggedObject::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_group()->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");
}
untag()->Validate(isolate_group);
}
void UntaggedObject::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.
uword tags = tags_;
if (IsNewObject()) {
if (!NewBit::decode(tags)) {
FATAL1("New object missing kNewBit: %" Px "\n", tags);
}
if (OldBit::decode(tags)) {
FATAL1("New object has kOldBit: %" Px "\n", tags);
}
if (OldAndNotMarkedBit::decode(tags)) {
FATAL1("New object has kOldAndNotMarkedBit: %" Px "\n", tags);
}
if (OldAndNotRememberedBit::decode(tags)) {
FATAL1("New object has kOldAndNotRememberedBit: %" Px "\n", tags);
}
} else {
if (NewBit::decode(tags)) {
FATAL1("Old object has kNewBit: %" Px "\n", tags);
}
if (!OldBit::decode(tags)) {
FATAL1("Old object missing kOldBit: %" Px "\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 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<const CodePtr>(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<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->untag()->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->untag()->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->untag()->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->untag()->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->untag()->length<std::memory_order_acquire>());
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->untag()->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->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<const SuspendStatePtr>(this);
intptr_t frame_size = raw_suspend_state->untag()->frame_size_;
instance_size = SuspendState::InstanceSize(frame_size);
break;
}
case kTypeArgumentsCid: {
const TypeArgumentsPtr raw_array =
static_cast<const TypeArgumentsPtr>(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<const PcDescriptorsPtr>(this);
intptr_t length = raw_descriptors->untag()->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->untag()->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->untag()->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->untag()->num_entries();
instance_size = ExceptionHandlers::InstanceSize(num_handlers);
break;
}
case kFreeListElement: {
uword addr = UntaggedObject::ToAddr(this);
FreeListElement* element = reinterpret_cast<FreeListElement*>(addr);
instance_size = element->HeapSize(tags);
break;
}
case kForwardingCorpse: {
uword addr = UntaggedObject::ToAddr(this);
ForwardingCorpse* element = reinterpret_cast<ForwardingCorpse*>(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)
#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->untag()->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 %" 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<clazz##Ptr>(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<TypedDataPtr>(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<ExternalTypedDataPtr>(this);
size = UntaggedExternalTypedData::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 =
UntaggedTypedDataView::VisitTypedDataViewPointers(raw_obj, visitor);
break;
}
#undef RAW_VISITPOINTERS
case kByteBufferCid: {
InstancePtr raw_obj = static_cast<InstancePtr>(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<FreeListElement*>(addr);
size = element->HeapSize();
break;
}
case kForwardingCorpse: {
uword addr = UntaggedObject::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 UntaggedObject::VisitPointersPrecise(IsolateGroup* isolate_group,
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 = isolate_group->GetClassForHeapWalkAt(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<CompressedObjectPtr*>(from);
const auto last = reinterpret_cast<CompressedObjectPtr*>(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(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);
}
#else
visitor->VisitCompressedPointers(heap_base(), first, last);
#endif // defined(SUPPORT_UNBOXED_INSTANCE_FIELDS)
}
bool UntaggedObject::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 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
// specializd 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)
COMPRESSED_VISITOR(Type)
COMPRESSED_VISITOR(FunctionType)
COMPRESSED_VISITOR(TypeRef)
COMPRESSED_VISITOR(TypeParameter)
COMPRESSED_VISITOR(Function)
COMPRESSED_VISITOR(Closure)
COMPRESSED_VISITOR(LibraryPrefix)
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(ExternalOneByteString)
COMPRESSED_VISITOR(ExternalTwoByteString)
COMPRESSED_VISITOR(GrowableObjectArray)
COMPRESSED_VISITOR(LinkedHashMap)
COMPRESSED_VISITOR(LinkedHashSet)
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_)
VARIABLE_COMPRESSED_VISITOR(Array, Smi::Value(raw_obj->untag()->length()))
VARIABLE_COMPRESSED_VISITOR(
TypedData,
TypedData::ElementSizeInBytes(raw_obj->GetClassId()) *
Smi::Value(raw_obj->untag()->length()))
VARIABLE_COMPRESSED_VISITOR(ContextScope, raw_obj->untag()->num_variables_)
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<uword>(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_size_);
}
bool UntaggedCode::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 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<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
}
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->isolate_group()->GetClassSizeForHeapWalkAt(
raw_obj->GetClassId());
}
// 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<CompressedObjectPtr*>(from),
reinterpret_cast<CompressedObjectPtr*>(to));
return instance_size;
}
intptr_t UntaggedImmutableArray::VisitImmutableArrayPointers(
ImmutableArrayPtr raw_obj,
ObjectPointerVisitor* visitor) {
return UntaggedArray::VisitArrayPointers(raw_obj, visitor);
}
intptr_t UntaggedImmutableLinkedHashMap::VisitImmutableLinkedHashMapPointers(
ImmutableLinkedHashMapPtr raw_obj,
ObjectPointerVisitor* visitor) {
return UntaggedLinkedHashMap::VisitLinkedHashMapPointers(raw_obj, visitor);
}
intptr_t UntaggedImmutableLinkedHashSet::VisitImmutableLinkedHashSetPointers(
ImmutableLinkedHashSetPtr raw_obj,
ObjectPointerVisitor* visitor) {
return UntaggedLinkedHashSet::VisitLinkedHashSetPointers(raw_obj, visitor);
}
void UntaggedObject::RememberCard(ObjectPtr const* slot) {
OldPage::Of(static_cast<ObjectPtr>(this))->RememberCard(slot);
}
#if defined(DART_COMPRESSED_POINTERS)
void UntaggedObject::RememberCard(CompressedObjectPtr const* slot) {
OldPage::Of(static_cast<ObjectPtr>(this))->RememberCard(slot);
}
#endif
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->untag()->IsCardRemembered());
OldPage::Of(object)->RememberCard(slot);
}
END_LEAF_RUNTIME_ENTRY
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