1 package shark.internal 2 3 import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader.OptionalFactory 4 import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader 5 import shark.HeapGraph 6 import shark.HeapObject.HeapInstance 7 8 /** 9 * Defines [VirtualInstanceReferenceReader] factories for common Apache Harmony data structures. 10 * 11 * Note: the expanders target the direct classes and don't target subclasses, as these might 12 * include additional out going references that would be missed. 13 */ 14 internal enum class ApacheHarmonyInstanceRefReaders : OptionalFactory { 15 16 // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/LinkedList.java 17 LINKED_LIST { createnull18 override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { 19 val linkedListClass = graph.findClassByName("java.util.LinkedList") ?: return null 20 val isApacheHarmonyImpl = linkedListClass.readRecordFields() 21 .any { linkedListClass.instanceFieldName(it) == "voidLink" } 22 23 if (!isApacheHarmonyImpl) { 24 return null 25 } 26 return InternalSharedLinkedListReferenceReader( 27 classObjectId = linkedListClass.objectId, 28 headFieldName = "voidLink", 29 nodeClassName = "java.util.LinkedList\$Link", 30 nodeNextFieldName = "next", 31 nodeElementFieldName = "data", 32 ) 33 } 34 }, 35 36 // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/ArrayList.java 37 ARRAY_LIST { createnull38 override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { 39 val arrayListClass = graph.findClassByName("java.util.ArrayList") ?: return null 40 41 val isApacheHarmonyImpl = arrayListClass.readRecordFields() 42 .any { arrayListClass.instanceFieldName(it) == "array" } 43 44 if (!isApacheHarmonyImpl) { 45 return null 46 } 47 48 return InternalSharedArrayListReferenceReader( 49 className = "java.util.ArrayList", 50 classObjectId = arrayListClass.objectId, 51 elementArrayName = "array", 52 sizeFieldName = "size", 53 ) 54 } 55 }, 56 57 // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java 58 COPY_ON_WRITE_ARRAY_LIST { createnull59 override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { 60 val arrayListClass = 61 graph.findClassByName("java.util.concurrent.CopyOnWriteArrayList") ?: return null 62 63 val isApacheHarmonyImpl = arrayListClass.readRecordFields() 64 .any { arrayListClass.instanceFieldName(it) == "elements" } 65 66 if (!isApacheHarmonyImpl) { 67 return null 68 } 69 70 return InternalSharedArrayListReferenceReader( 71 className = "java.util.concurrent.CopyOnWriteArrayList", 72 classObjectId = arrayListClass.objectId, 73 elementArrayName = "elements", 74 sizeFieldName = null, 75 ) 76 } 77 }, 78 79 // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/HashMap.java 80 /** 81 * Handles HashMap & LinkedHashMap 82 */ 83 HASH_MAP { createnull84 override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { 85 val hashMapClass = graph.findClassByName("java.util.HashMap") ?: return null 86 87 // No loadFactor field in the Apache Harmony impl. 88 val isOpenJdkImpl = hashMapClass.readRecordFields() 89 .any { hashMapClass.instanceFieldName(it) == "loadFactor" } 90 91 if (isOpenJdkImpl) { 92 return null 93 } 94 val linkedHashMapClass = graph.findClassByName("java.util.LinkedHashMap") 95 96 val hashMapClassId = hashMapClass.objectId 97 val linkedHashMapClassId = linkedHashMapClass?.objectId ?: 0 98 99 return InternalSharedHashMapReferenceReader( 100 className = "java.util.HashMap", 101 tableFieldName = "table", 102 nodeClassName = "java.util.HashMap\$HashMapEntry", 103 nodeNextFieldName = "next", 104 nodeKeyFieldName = "key", 105 nodeValueFieldName = "value", 106 keyName = "key()", 107 keysOnly = false, 108 matches = { 109 val instanceClassId = it.instanceClassId 110 instanceClassId == hashMapClassId || instanceClassId == linkedHashMapClassId 111 }, 112 declaringClassId = { it.instanceClassId } 113 ) 114 } 115 }, 116 117 // https://cs.android.com/android/platform/superproject/+/android-6.0.1_r81:libcore/luni/src/main/java/java/util/HashSet.java 118 /** 119 * Handles HashSet & LinkedHashSet 120 */ 121 HASH_SET { createnull122 override fun create(graph: HeapGraph): VirtualInstanceReferenceReader? { 123 val hashSetClass = graph.findClassByName("java.util.HashSet") ?: return null 124 125 val isApacheHarmonyImpl = hashSetClass.readRecordFields() 126 .any { hashSetClass.instanceFieldName(it) == "backingMap" } 127 128 if (!isApacheHarmonyImpl) { 129 return null 130 } 131 132 val linkedHashSetClass = graph.findClassByName("java.util.LinkedHashSet") 133 val hashSetClassId = hashSetClass.objectId 134 val linkedHashSetClassId = linkedHashSetClass?.objectId ?: 0 135 return object : VirtualInstanceReferenceReader { 136 137 override fun matches(instance: HeapInstance): Boolean { 138 val instanceClassId = instance.instanceClassId 139 return (instanceClassId == hashSetClassId || instanceClassId == linkedHashSetClassId) 140 } 141 142 override fun read(instance: HeapInstance): Sequence<Reference> { 143 // "HashSet.backingMap" is never null. 144 val map = instance["java.util.HashSet", "backingMap"]!!.valueAsInstance!! 145 return InternalSharedHashMapReferenceReader( 146 className = "java.util.HashMap", 147 tableFieldName = "table", 148 nodeClassName = "java.util.HashMap\$HashMapEntry", 149 nodeNextFieldName = "next", 150 nodeKeyFieldName = "key", 151 nodeValueFieldName = "value", 152 keyName = "element()", 153 keysOnly = true, 154 matches = { true }, 155 declaringClassId = { instance.instanceClassId } 156 ).read(map) 157 } 158 } 159 } 160 } 161 162 ; 163 } 164