// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_BODY_DESCRIPTORS_INL_H_ #define V8_OBJECTS_BODY_DESCRIPTORS_INL_H_ #include "src/assembler-inl.h" #include "src/objects-body-descriptors.h" #include "src/transitions.h" namespace v8 { namespace internal { template int FlexibleBodyDescriptor::SizeOf(Map* map, HeapObject* object) { return object->SizeFromMap(map); } bool BodyDescriptorBase::IsValidSlotImpl(HeapObject* obj, int offset) { if (!FLAG_unbox_double_fields || obj->map()->HasFastPointerLayout()) { return true; } else { DCHECK(FLAG_unbox_double_fields); DCHECK(IsAligned(offset, kPointerSize)); LayoutDescriptorHelper helper(obj->map()); DCHECK(!helper.all_fields_tagged()); return helper.IsTagged(offset); } } template void BodyDescriptorBase::IterateBodyImpl(HeapObject* obj, int start_offset, int end_offset, ObjectVisitor* v) { if (!FLAG_unbox_double_fields || obj->map()->HasFastPointerLayout()) { IteratePointers(obj, start_offset, end_offset, v); } else { DCHECK(FLAG_unbox_double_fields); DCHECK(IsAligned(start_offset, kPointerSize) && IsAligned(end_offset, kPointerSize)); LayoutDescriptorHelper helper(obj->map()); DCHECK(!helper.all_fields_tagged()); for (int offset = start_offset; offset < end_offset;) { int end_of_region_offset; if (helper.IsTagged(offset, end_offset, &end_of_region_offset)) { IteratePointers(obj, offset, end_of_region_offset, v); } offset = end_of_region_offset; } } } template void BodyDescriptorBase::IterateBodyImpl(Heap* heap, HeapObject* obj, int start_offset, int end_offset) { if (!FLAG_unbox_double_fields || obj->map()->HasFastPointerLayout()) { IteratePointers(heap, obj, start_offset, end_offset); } else { DCHECK(FLAG_unbox_double_fields); DCHECK(IsAligned(start_offset, kPointerSize) && IsAligned(end_offset, kPointerSize)); LayoutDescriptorHelper helper(obj->map()); DCHECK(!helper.all_fields_tagged()); for (int offset = start_offset; offset < end_offset;) { int end_of_region_offset; if (helper.IsTagged(offset, end_offset, &end_of_region_offset)) { IteratePointers(heap, obj, offset, end_of_region_offset); } offset = end_of_region_offset; } } } template DISABLE_CFI_PERF void BodyDescriptorBase::IteratePointers(HeapObject* obj, int start_offset, int end_offset, ObjectVisitor* v) { v->VisitPointers(obj, HeapObject::RawField(obj, start_offset), HeapObject::RawField(obj, end_offset)); } template DISABLE_CFI_PERF void BodyDescriptorBase::IteratePointers(Heap* heap, HeapObject* obj, int start_offset, int end_offset) { StaticVisitor::VisitPointers(heap, obj, HeapObject::RawField(obj, start_offset), HeapObject::RawField(obj, end_offset)); } template void BodyDescriptorBase::IteratePointer(HeapObject* obj, int offset, ObjectVisitor* v) { v->VisitPointer(obj, HeapObject::RawField(obj, offset)); } template void BodyDescriptorBase::IteratePointer(Heap* heap, HeapObject* obj, int offset) { StaticVisitor::VisitPointer(heap, obj, HeapObject::RawField(obj, offset)); } class JSObject::BodyDescriptor final : public BodyDescriptorBase { public: static const int kStartOffset = JSReceiver::kPropertiesOffset; static bool IsValidSlot(HeapObject* obj, int offset) { if (offset < kStartOffset) return false; return IsValidSlotImpl(obj, offset); } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IterateBodyImpl(obj, kStartOffset, object_size, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); IterateBodyImpl(heap, obj, kStartOffset, object_size); } static inline int SizeOf(Map* map, HeapObject* object) { return map->instance_size(); } }; class JSObject::FastBodyDescriptor final : public BodyDescriptorBase { public: static const int kStartOffset = JSReceiver::kPropertiesOffset; static bool IsValidSlot(HeapObject* obj, int offset) { return offset >= kStartOffset; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IteratePointers(obj, kStartOffset, object_size, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); IteratePointers(heap, obj, kStartOffset, object_size); } static inline int SizeOf(Map* map, HeapObject* object) { return map->instance_size(); } }; // Iterates the function object according to the visiting policy. template class JSFunction::BodyDescriptorImpl final : public BodyDescriptorBase { public: STATIC_ASSERT(kNonWeakFieldsEndOffset == kCodeEntryOffset); STATIC_ASSERT(kCodeEntryOffset + kPointerSize == kNextFunctionLinkOffset); STATIC_ASSERT(kNextFunctionLinkOffset + kPointerSize == kSize); static bool IsValidSlot(HeapObject* obj, int offset) { if (offset < kSize) return true; return IsValidSlotImpl(obj, offset); } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IteratePointers(obj, kPropertiesOffset, kNonWeakFieldsEndOffset, v); if (body_visiting_policy & kVisitCodeEntry) { v->VisitCodeEntry(JSFunction::cast(obj), obj->address() + kCodeEntryOffset); } if (body_visiting_policy & kVisitNextFunction) { IteratePointers(obj, kNextFunctionLinkOffset, kSize, v); } IterateBodyImpl(obj, kSize, object_size, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); IteratePointers(heap, obj, kPropertiesOffset, kNonWeakFieldsEndOffset); if (body_visiting_policy & kVisitCodeEntry) { StaticVisitor::VisitCodeEntry(heap, obj, obj->address() + kCodeEntryOffset); } if (body_visiting_policy & kVisitNextFunction) { IteratePointers(heap, obj, kNextFunctionLinkOffset, kSize); } IterateBodyImpl(heap, obj, kSize, object_size); } static inline int SizeOf(Map* map, HeapObject* object) { return map->instance_size(); } }; class JSArrayBuffer::BodyDescriptor final : public BodyDescriptorBase { public: STATIC_ASSERT(kByteLengthOffset + kPointerSize == kBackingStoreOffset); STATIC_ASSERT(kAllocationLengthOffset + kPointerSize == kBitFieldSlot); STATIC_ASSERT(kBitFieldSlot + kPointerSize == kSize); static bool IsValidSlot(HeapObject* obj, int offset) { if (offset < kAllocationLengthOffset) return true; if (offset < kSize) return false; return IsValidSlotImpl(obj, offset); } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { // Array buffers contain raw pointers that the GC does not know about. These // are stored at kBackStoreOffset and later, so we do not iterate over // those. IteratePointers(obj, kPropertiesOffset, kBackingStoreOffset, v); IterateBodyImpl(obj, kSize, object_size, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); // Array buffers contain raw pointers that the GC does not know about. These // are stored at kBackStoreOffset and later, so we do not iterate over // those. IteratePointers(heap, obj, kPropertiesOffset, kBackingStoreOffset); IterateBodyImpl(heap, obj, kSize, object_size); } static inline int SizeOf(Map* map, HeapObject* object) { return map->instance_size(); } }; class ByteArray::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) {} template static inline void IterateBody(HeapObject* obj, int object_size) {} static inline int SizeOf(Map* map, HeapObject* obj) { return reinterpret_cast(obj)->ByteArraySize(); } }; class BytecodeArray::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return offset >= kConstantPoolOffset && offset <= kSourcePositionTableOffset; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IteratePointer(obj, kConstantPoolOffset, v); IteratePointer(obj, kHandlerTableOffset, v); IteratePointer(obj, kSourcePositionTableOffset, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); IteratePointer(heap, obj, kConstantPoolOffset); IteratePointer(heap, obj, kHandlerTableOffset); IteratePointer(heap, obj, kSourcePositionTableOffset); } static inline int SizeOf(Map* map, HeapObject* obj) { return reinterpret_cast(obj)->BytecodeArraySize(); } }; class FixedDoubleArray::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) {} template static inline void IterateBody(HeapObject* obj, int object_size) {} static inline int SizeOf(Map* map, HeapObject* obj) { return FixedDoubleArray::SizeFor( reinterpret_cast(obj)->length()); } }; class FixedTypedArrayBase::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return offset == kBasePointerOffset; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IteratePointer(obj, kBasePointerOffset, v); } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); IteratePointer(heap, obj, kBasePointerOffset); } static inline int SizeOf(Map* map, HeapObject* object) { return reinterpret_cast(object)->size(); } }; template class JSWeakCollection::BodyDescriptorImpl final : public BodyDescriptorBase { public: STATIC_ASSERT(kTableOffset + kPointerSize == kNextOffset); STATIC_ASSERT(kNextOffset + kPointerSize == kSize); static bool IsValidSlot(HeapObject* obj, int offset) { return IsValidSlotImpl(obj, offset); } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { if (body_visiting_policy == kVisitStrong) { IterateBodyImpl(obj, kPropertiesOffset, object_size, v); } else { IteratePointers(obj, kPropertiesOffset, kTableOffset, v); IterateBodyImpl(obj, kSize, object_size, v); } } template static inline void IterateBody(HeapObject* obj, int object_size) { Heap* heap = obj->GetHeap(); if (body_visiting_policy == kVisitStrong) { IterateBodyImpl(heap, obj, kPropertiesOffset, object_size); } else { IteratePointers(heap, obj, kPropertiesOffset, kTableOffset); IterateBodyImpl(heap, obj, kSize, object_size); } } static inline int SizeOf(Map* map, HeapObject* object) { return map->instance_size(); } }; class Foreign::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { v->VisitExternalReference(Foreign::cast(obj), reinterpret_cast(HeapObject::RawField( obj, kForeignAddressOffset))); } template static inline void IterateBody(HeapObject* obj, int object_size) { StaticVisitor::VisitExternalReference(reinterpret_cast( HeapObject::RawField(obj, kForeignAddressOffset))); } static inline int SizeOf(Map* map, HeapObject* object) { return kSize; } }; class ExternalOneByteString::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { } template static inline void IterateBody(HeapObject* obj, int object_size) { } static inline int SizeOf(Map* map, HeapObject* object) { return kSize; } }; class ExternalTwoByteString::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { } template static inline void IterateBody(HeapObject* obj, int object_size) { } static inline int SizeOf(Map* map, HeapObject* object) { return kSize; } }; class Code::BodyDescriptor final : public BodyDescriptorBase { public: STATIC_ASSERT(kRelocationInfoOffset + kPointerSize == kHandlerTableOffset); STATIC_ASSERT(kHandlerTableOffset + kPointerSize == kDeoptimizationDataOffset); STATIC_ASSERT(kDeoptimizationDataOffset + kPointerSize == kSourcePositionTableOffset); STATIC_ASSERT(kSourcePositionTableOffset + kPointerSize == kTypeFeedbackInfoOffset); STATIC_ASSERT(kTypeFeedbackInfoOffset + kPointerSize == kNextCodeLinkOffset); static bool IsValidSlot(HeapObject* obj, int offset) { // Slots in code can't be invalid because we never trim code objects. return true; } template static inline void IterateBody(HeapObject* obj, ObjectVisitor* v) { int mode_mask = RelocInfo::kCodeTargetMask | RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | RelocInfo::ModeMask(RelocInfo::CELL) | RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) | RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) | RelocInfo::kDebugBreakSlotMask; IteratePointers(obj, kRelocationInfoOffset, kNextCodeLinkOffset, v); v->VisitNextCodeLink(Code::cast(obj), HeapObject::RawField(obj, kNextCodeLinkOffset)); RelocIterator it(Code::cast(obj), mode_mask); Isolate* isolate = obj->GetIsolate(); for (; !it.done(); it.next()) { it.rinfo()->Visit(isolate, v); } } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) { IterateBody(obj, v); } template static inline void IterateBody(HeapObject* obj) { int mode_mask = RelocInfo::kCodeTargetMask | RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) | RelocInfo::ModeMask(RelocInfo::CELL) | RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) | RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY) | RelocInfo::kDebugBreakSlotMask; Heap* heap = obj->GetHeap(); IteratePointers(heap, obj, kRelocationInfoOffset, kNextCodeLinkOffset); StaticVisitor::VisitNextCodeLink( heap, HeapObject::RawField(obj, kNextCodeLinkOffset)); RelocIterator it(Code::cast(obj), mode_mask); for (; !it.done(); it.next()) { it.rinfo()->template Visit(heap); } } template static inline void IterateBody(HeapObject* obj, int object_size) { IterateBody(obj); } static inline int SizeOf(Map* map, HeapObject* object) { return reinterpret_cast(object)->CodeSize(); } }; class SeqOneByteString::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) {} template static inline void IterateBody(HeapObject* obj, int object_size) {} static inline int SizeOf(Map* map, HeapObject* obj) { SeqOneByteString* string = SeqOneByteString::cast(obj); return string->SizeFor(string->length()); } }; class SeqTwoByteString::BodyDescriptor final : public BodyDescriptorBase { public: static bool IsValidSlot(HeapObject* obj, int offset) { return false; } template static inline void IterateBody(HeapObject* obj, int object_size, ObjectVisitor* v) {} template static inline void IterateBody(HeapObject* obj, int object_size) {} static inline int SizeOf(Map* map, HeapObject* obj) { SeqTwoByteString* string = SeqTwoByteString::cast(obj); return string->SizeFor(string->length()); } }; template ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) { if (type < FIRST_NONSTRING_TYPE) { switch (type & kStringRepresentationMask) { case kSeqStringTag: return ReturnType(); case kConsStringTag: return Op::template apply(p1, p2, p3); case kThinStringTag: return Op::template apply(p1, p2, p3); case kSlicedStringTag: return Op::template apply(p1, p2, p3); case kExternalStringTag: if ((type & kStringEncodingMask) == kOneByteStringTag) { return Op::template apply( p1, p2, p3); } else { return Op::template apply( p1, p2, p3); } } UNREACHABLE(); return ReturnType(); } switch (type) { case FIXED_ARRAY_TYPE: return Op::template apply(p1, p2, p3); case FIXED_DOUBLE_ARRAY_TYPE: return ReturnType(); case TRANSITION_ARRAY_TYPE: return Op::template apply(p1, p2, p3); case JS_OBJECT_TYPE: case JS_ERROR_TYPE: case JS_ARGUMENTS_TYPE: case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE: case JS_PROMISE_CAPABILITY_TYPE: case JS_PROMISE_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_GENERATOR_OBJECT_TYPE: case JS_ASYNC_GENERATOR_OBJECT_TYPE: case JS_VALUE_TYPE: case JS_DATE_TYPE: case JS_ARRAY_TYPE: case JS_MODULE_NAMESPACE_TYPE: case JS_TYPED_ARRAY_TYPE: case JS_DATA_VIEW_TYPE: case JS_SET_TYPE: case JS_MAP_TYPE: case JS_SET_ITERATOR_TYPE: case JS_MAP_ITERATOR_TYPE: case JS_STRING_ITERATOR_TYPE: case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE: case JS_FAST_ARRAY_KEY_ITERATOR_TYPE: case JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE: case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_GENERIC_ARRAY_KEY_VALUE_ITERATOR_TYPE: case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE: case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE: case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE: case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE: case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE: case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE: case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE: case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE: case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE: case JS_GENERIC_ARRAY_VALUE_ITERATOR_TYPE: case JS_REGEXP_TYPE: case JS_GLOBAL_PROXY_TYPE: case JS_GLOBAL_OBJECT_TYPE: case JS_API_OBJECT_TYPE: case JS_SPECIAL_API_OBJECT_TYPE: case JS_MESSAGE_OBJECT_TYPE: case JS_BOUND_FUNCTION_TYPE: return Op::template apply(p1, p2, p3); case JS_WEAK_MAP_TYPE: case JS_WEAK_SET_TYPE: return Op::template apply(p1, p2, p3); case JS_ARRAY_BUFFER_TYPE: return Op::template apply(p1, p2, p3); case JS_FUNCTION_TYPE: return Op::template apply(p1, p2, p3); case ODDBALL_TYPE: return Op::template apply(p1, p2, p3); case JS_PROXY_TYPE: return Op::template apply(p1, p2, p3); case FOREIGN_TYPE: return Op::template apply(p1, p2, p3); case MAP_TYPE: return Op::template apply(p1, p2, p3); case CODE_TYPE: return Op::template apply(p1, p2, p3); case CELL_TYPE: return Op::template apply(p1, p2, p3); case PROPERTY_CELL_TYPE: return Op::template apply(p1, p2, p3); case WEAK_CELL_TYPE: return Op::template apply(p1, p2, p3); case SYMBOL_TYPE: return Op::template apply(p1, p2, p3); case BYTECODE_ARRAY_TYPE: return Op::template apply(p1, p2, p3); case HEAP_NUMBER_TYPE: case MUTABLE_HEAP_NUMBER_TYPE: case FILLER_TYPE: case BYTE_ARRAY_TYPE: case FREE_SPACE_TYPE: return ReturnType(); #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ case FIXED_##TYPE##_ARRAY_TYPE: \ return Op::template apply(p1, p2, p3); TYPED_ARRAYS(TYPED_ARRAY_CASE) #undef TYPED_ARRAY_CASE case SHARED_FUNCTION_INFO_TYPE: { return Op::template apply(p1, p2, p3); } #define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: STRUCT_LIST(MAKE_STRUCT_CASE) #undef MAKE_STRUCT_CASE if (type == ALLOCATION_SITE_TYPE) { return Op::template apply(p1, p2, p3); } else { return Op::template apply(p1, p2, p3); } default: PrintF("Unknown type: %d\n", type); UNREACHABLE(); return ReturnType(); } } template void HeapObject::IterateFast(ObjectVisitor* v) { BodyDescriptorBase::IteratePointer(this, kMapOffset, v); IterateBodyFast(v); } template void HeapObject::IterateBodyFast(ObjectVisitor* v) { Map* m = map(); IterateBodyFast(m->instance_type(), SizeFromMap(m), v); } struct CallIterateBody { template static void apply(HeapObject* obj, int object_size, ObjectVisitor* v) { BodyDescriptor::IterateBody(obj, object_size, v); } }; template void HeapObject::IterateBodyFast(InstanceType type, int object_size, ObjectVisitor* v) { BodyDescriptorApply(type, this, object_size, v); } } // namespace internal } // namespace v8 #endif // V8_OBJECTS_BODY_DESCRIPTORS_INL_H_