summaryrefslogtreecommitdiff
path: root/runtime/mirror/dex_cache.h
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/mirror/dex_cache.h')
-rw-r--r--runtime/mirror/dex_cache.h424
1 files changed, 192 insertions, 232 deletions
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 7c7b11f475..7af0fa2e87 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,15 +19,17 @@
#include "array.h"
#include "base/array_ref.h"
+#include "base/atomic_pair.h"
#include "base/bit_utils.h"
#include "base/locks.h"
+#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
#include "gc_root.h" // Note: must not use -inl here to avoid circular dependency.
+#include "linear_alloc.h"
#include "object.h"
#include "object_array.h"
namespace art {
-enum class LinearAllocKind : uint32_t;
namespace linker {
class ImageWriter;
@@ -46,6 +48,7 @@ namespace mirror {
class CallSite;
class Class;
class ClassLoader;
+class DexCache;
class MethodType;
class String;
@@ -115,20 +118,92 @@ template <typename T> struct PACKED(2 * __SIZEOF_POINTER__) NativeDexCachePair {
}
};
-using TypeDexCachePair = DexCachePair<Class>;
-using TypeDexCacheType = std::atomic<TypeDexCachePair>;
+template <typename T, size_t size> class NativeDexCachePairArray {
+ public:
+ NativeDexCachePairArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto pair = GetNativePair(entries_, SlotIndex(index));
+ return pair.GetObjectForIndex(index);
+ }
+
+ void Set(uint32_t index, T* value) {
+ NativeDexCachePair<T> pair(value, index);
+ SetNativePair(entries_, SlotIndex(index), pair);
+ }
+
+ NativeDexCachePair<T> GetNativePair(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetNativePair(entries_, SlotIndex(index));
+ }
+
+ void SetNativePair(uint32_t index, NativeDexCachePair<T> value) {
+ SetNativePair(entries_, SlotIndex(index), value);
+ }
+
+ private:
+ NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array, size_t idx) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> value = AtomicPairLoadAcquire(&array[idx]);
+ return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
+ }
+
+ void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ NativeDexCachePair<T> pair) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(pair.object), pair.index);
+ AtomicPairStoreRelease(&array[idx], v);
+ }
+
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
+
+ std::atomic<NativeDexCachePair<T>> entries_[0];
+
+ NativeDexCachePairArray(const NativeDexCachePairArray<T, size>&) = delete;
+ NativeDexCachePairArray& operator=(const NativeDexCachePairArray<T, size>&) = delete;
+};
+
+template <typename T, size_t size> class DexCachePairArray {
+ public:
+ DexCachePairArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetPair(index).GetObjectForIndex(index);
+ }
-using StringDexCachePair = DexCachePair<String>;
-using StringDexCacheType = std::atomic<StringDexCachePair>;
+ void Set(uint32_t index, T* value) {
+ SetPair(index, DexCachePair<T>(value, index));
+ }
+
+ DexCachePair<T> GetPair(uint32_t index) {
+ return entries_[SlotIndex(index)].load(std::memory_order_relaxed);
+ }
+
+ void SetPair(uint32_t index, DexCachePair<T> value) {
+ entries_[SlotIndex(index)].store(value, std::memory_order_relaxed);
+ }
+
+ void Clear(uint32_t index) {
+ uint32_t slot = SlotIndex(index);
+ // This is racy but should only be called from the transactional interpreter.
+ if (entries_[slot].load(std::memory_order_relaxed).index == index) {
+ DexCachePair<T> cleared(nullptr, DexCachePair<T>::InvalidIndexForSlot(slot));
+ entries_[slot].store(cleared, std::memory_order_relaxed);
+ }
+ }
-using FieldDexCachePair = NativeDexCachePair<ArtField>;
-using FieldDexCacheType = std::atomic<FieldDexCachePair>;
+ private:
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
-using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
-using MethodDexCacheType = std::atomic<MethodDexCachePair>;
+ std::atomic<DexCachePair<T>> entries_[0];
-using MethodTypeDexCachePair = DexCachePair<MethodType>;
-using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
+ DexCachePairArray(const DexCachePairArray<T, size>&) = delete;
+ DexCachePairArray& operator=(const DexCachePairArray<T, size>&) = delete;
+};
// C++ mirror of java.lang.DexCache.
class MANAGED DexCache final : public Object {
@@ -164,26 +239,6 @@ class MANAGED DexCache final : public Object {
static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
"MethodType dex cache size is not a power of 2.");
- static constexpr size_t StaticTypeSize() {
- return kDexCacheTypeCacheSize;
- }
-
- static constexpr size_t StaticStringSize() {
- return kDexCacheStringCacheSize;
- }
-
- static constexpr size_t StaticArtFieldSize() {
- return kDexCacheFieldCacheSize;
- }
-
- static constexpr size_t StaticMethodSize() {
- return kDexCacheMethodCacheSize;
- }
-
- static constexpr size_t StaticMethodTypeSize() {
- return kDexCacheMethodTypeCacheSize;
- }
-
// Size of an instance of java.lang.DexCache not including referenced values.
static constexpr uint32_t InstanceSize() {
return sizeof(DexCache);
@@ -196,7 +251,6 @@ class MANAGED DexCache final : public Object {
DexCachePair<Object>* pairs_end)
REQUIRES_SHARED(Locks::mutator_lock_);
-
void Initialize(const DexFile* dex_file, ObjPtr<ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::dex_lock_);
@@ -209,66 +263,6 @@ class MANAGED DexCache final : public Object {
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<String> GetLocation() REQUIRES_SHARED(Locks::mutator_lock_);
- static constexpr MemberOffset StringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
- }
-
- static constexpr MemberOffset PreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, preresolved_strings_);
- }
-
- static constexpr MemberOffset ResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
- }
-
- static constexpr MemberOffset ResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_);
- }
-
- static constexpr MemberOffset ResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
- }
-
- static constexpr MemberOffset ResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
- }
-
- static constexpr MemberOffset ResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
- }
-
- static constexpr MemberOffset NumStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
- }
-
- static constexpr MemberOffset NumPreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_preresolved_strings_);
- }
-
- static constexpr MemberOffset NumResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
- }
-
- static constexpr MemberOffset NumResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_fields_);
- }
-
- static constexpr MemberOffset NumResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
- }
-
- static constexpr MemberOffset NumResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
- }
-
- static constexpr MemberOffset NumResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
- }
-
- static constexpr size_t PreResolvedStringsAlignment() {
- return alignof(GcRoot<mirror::String>);
- }
-
String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -317,106 +311,6 @@ class MANAGED DexCache final : public Object {
ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
- }
-
- void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(StringsOffset(), strings);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
- }
-
- void SetResolvedTypes(TypeDexCacheType* resolved_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
- }
-
- MethodDexCacheType* GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<MethodDexCacheType*>(ResolvedMethodsOffset());
- }
-
- void SetResolvedMethods(MethodDexCacheType* resolved_methods)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods);
- }
-
- FieldDexCacheType* GetResolvedFields() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<FieldDexCacheType*>(ResolvedFieldsOffset());
- }
-
- void SetResolvedFields(FieldDexCacheType* resolved_fields)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- MethodTypeDexCacheType* GetResolvedMethodTypes()
- ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<MethodTypeDexCacheType*, kVerifyFlags>(ResolvedMethodTypesOffset());
- }
-
- void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- GcRoot<CallSite>* GetResolvedCallSites()
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<GcRoot<CallSite>*, kVerifyFlags>(ResolvedCallSitesOffset());
- }
-
- void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumPreResolvedStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumPreResolvedStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethods() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedFieldsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedCallSitesOffset());
- }
-
const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
}
@@ -427,25 +321,6 @@ class MANAGED DexCache final : public Object {
void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
- template <typename T>
- static NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx);
-
- template <typename T>
- static void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx,
- NativeDexCachePair<T> pair);
-
- static size_t PreResolvedStringsSize(size_t num_strings) {
- return sizeof(GcRoot<mirror::String>) * num_strings;
- }
-
- uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-
void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -458,10 +333,108 @@ class MANAGED DexCache final : public Object {
void VisitNativeRoots(const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+ // NOLINTBEGIN(bugprone-macro-parentheses)
+#define DEFINE_PAIR_ARRAY(name, pair_kind, getter_setter, type, size, ids, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ pair_kind ##Array<type, size>* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<pair_kind ##Array<type, size>*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(pair_kind ##Array<type, size>* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ pair_kind ##Array<type, size>* Allocate ##getter_setter() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<pair_kind ##Array<type, size>*>( \
+ AllocArray<std::atomic<pair_kind<type>>, size>( \
+ getter_setter ##Offset(), GetDexFile()->ids(), alloc_kind)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Get ##getter_setter() == nullptr ? 0u : std::min<size_t>(GetDexFile()->ids(), size); \
+ } \
+
+#define DEFINE_ARRAY(name, getter_setter, type, ids, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ type* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<type*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(type* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ type* Allocate ##getter_setter() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<type*>(AllocArray<type, std::numeric_limits<size_t>::max()>( \
+ getter_setter ##Offset(), GetDexFile()->ids(), alloc_kind)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetDexFile()->ids(); \
+ } \
+
+ DEFINE_ARRAY(resolved_call_sites_,
+ ResolvedCallSites,
+ GcRoot<CallSite>,
+ NumCallSiteIds,
+ LinearAllocKind::kGCRootArray)
+
+ DEFINE_PAIR_ARRAY(resolved_fields_,
+ NativeDexCachePair,
+ ResolvedFields,
+ ArtField,
+ kDexCacheFieldCacheSize,
+ NumFieldIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_PAIR_ARRAY(resolved_method_types_,
+ DexCachePair,
+ ResolvedMethodTypes,
+ mirror::MethodType,
+ kDexCacheMethodTypeCacheSize,
+ NumProtoIds,
+ LinearAllocKind::kDexCacheArray);
+
+ DEFINE_PAIR_ARRAY(resolved_methods_,
+ NativeDexCachePair,
+ ResolvedMethods,
+ ArtMethod,
+ kDexCacheMethodCacheSize,
+ NumMethodIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_PAIR_ARRAY(resolved_types_,
+ DexCachePair,
+ ResolvedTypes,
+ mirror::Class,
+ kDexCacheTypeCacheSize,
+ NumTypeIds,
+ LinearAllocKind::kDexCacheArray);
+
+ DEFINE_PAIR_ARRAY(strings_,
+ DexCachePair,
+ Strings,
+ mirror::String,
+ kDexCacheStringCacheSize,
+ NumStringIds,
+ LinearAllocKind::kDexCacheArray);
+
+// NOLINTEND(bugprone-macro-parentheses)
+
private:
// Allocate new array in linear alloc and save it in the given fields.
template<typename T, size_t kMaxCacheSize>
- T* AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num, LinearAllocKind kind)
+ T* AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind)
REQUIRES_SHARED(Locks::mutator_lock_);
// Visit instance fields of the dex cache as well as its associated arrays.
@@ -476,26 +449,13 @@ class MANAGED DexCache final : public Object {
HeapReference<String> location_;
uint64_t dex_file_; // const DexFile*
- uint64_t preresolved_strings_; // GcRoot<mirror::String*> array with num_preresolved_strings
- // elements.
- uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
- // elements.
- uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
- // num_resolved_fields_ elements.
- uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
- // num_resolved_method_types_ elements.
- uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
- uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
- uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
- // elements.
-
- uint32_t num_preresolved_strings_; // Number of elements in the preresolved_strings_ array.
- uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
- uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
- uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
- uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
- uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array.
- uint32_t num_strings_; // Number of elements in the strings_ array.
+
+ uint64_t resolved_call_sites_; // Array of call sites
+ uint64_t resolved_fields_; // NativeDexCacheArray holding ArtField's
+ uint64_t resolved_method_types_; // DexCacheArray holding mirror::MethodType's
+ uint64_t resolved_methods_; // NativeDexCacheArray holding ArtMethod's
+ uint64_t resolved_types_; // DexCacheArray holding mirror::Class's
+ uint64_t strings_; // DexCacheArray holding mirror::String's
friend struct art::DexCacheOffsets; // for verifying offset information
friend class linker::ImageWriter;