• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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