xref: /aosp_15_r20/external/leakcanary2/shark/src/main/java/shark/internal/InternalSharedExpanderHelpers.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)

<lambda>null1 package shark.internal
2 
3 import shark.internal.ChainingInstanceReferenceReader.VirtualInstanceReferenceReader
4 import shark.HeapObject.HeapInstance
5 import shark.HeapValue
6 import shark.internal.Reference.LazyDetails
7 import shark.internal.ReferenceLocationType.ARRAY_ENTRY
8 
9 internal class InternalSharedHashMapReferenceReader(
10   private val className: String,
11   private val tableFieldName: String,
12   private val nodeClassName: String,
13   private val nodeNextFieldName: String,
14   private val nodeKeyFieldName: String,
15   private val nodeValueFieldName: String,
16   private val keyName: String,
17   private val keysOnly: Boolean,
18   private val matches: (HeapInstance) -> Boolean,
19   private val declaringClassId: (HeapInstance) -> (Long)
20 ) : VirtualInstanceReferenceReader {
21   override fun matches(instance: HeapInstance): Boolean {
22     return matches.invoke(instance)
23   }
24 
25   override fun read(source: HeapInstance): Sequence<Reference> {
26     val table = source[className, tableFieldName]!!.valueAsObjectArray
27     return if (table != null) {
28       val entries = table.readElements().mapNotNull { entryRef ->
29         if (entryRef.isNonNullReference) {
30           val entry = entryRef.asObject!!.asInstance!!
31           generateSequence(entry) { node ->
32             node[nodeClassName, nodeNextFieldName]!!.valueAsInstance
33           }
34         } else {
35           null
36         }
37       }.flatten()
38 
39       val declaringClassId = declaringClassId(source)
40 
41       val createKeyRef: (HeapValue) -> Reference? = { key ->
42         if (key.isNonNullReference) {
43           Reference(
44             valueObjectId = key.asObjectId!!,
45             isLowPriority = false,
46             lazyDetailsResolver = {
47               LazyDetails(
48                 // All entries are represented by the same key name, e.g. "key()"
49                 name = keyName,
50                 locationClassObjectId = declaringClassId,
51                 locationType = ARRAY_ENTRY,
52                 isVirtual = true,
53                 matchedLibraryLeak = null
54               )
55             }
56           )
57         } else null
58       }
59 
60       if (keysOnly) {
61         entries.mapNotNull { entry ->
62           val key = entry[nodeClassName, nodeKeyFieldName]!!.value
63           createKeyRef(key)
64         }
65       } else {
66         entries.flatMap { entry ->
67           val key = entry[nodeClassName, nodeKeyFieldName]!!.value
68           val keyRef = createKeyRef(key)
69           val value = entry[nodeClassName, nodeValueFieldName]!!.value
70           val valueRef = if (value.isNonNullReference) {
71             Reference(
72               valueObjectId = value.asObjectId!!,
73               isLowPriority = false,
74               lazyDetailsResolver = {
75                 val keyAsString = key.asObject?.asInstance?.readAsJavaString()?.let { "\"$it\"" }
76                 val keyAsName =
77                   keyAsString ?: key.asObject?.toString() ?: "null"
78                 LazyDetails(
79                   name = keyAsName,
80                   locationClassObjectId = declaringClassId,
81                   locationType = ARRAY_ENTRY,
82                   isVirtual = true,
83                   matchedLibraryLeak = null
84                 )
85               }
86             )
87           } else null
88           if (keyRef != null && valueRef != null) {
89             sequenceOf(keyRef, valueRef)
90           } else if (keyRef != null) {
91             sequenceOf(keyRef)
92           } else if (valueRef != null) {
93             sequenceOf(valueRef)
94           } else {
95             emptySequence()
96           }
97         }
98       }
99     } else {
100       emptySequence()
101     }
102   }
103 }
104 
105 internal class InternalSharedArrayListReferenceReader(
106   private val className: String,
107   private val classObjectId: Long,
108   private val elementArrayName: String,
109   private val sizeFieldName: String?
110 ) : VirtualInstanceReferenceReader {
111 
matchesnull112   override fun matches(instance: HeapInstance): Boolean {
113     return instance.instanceClassId == classObjectId
114   }
115 
readnull116   override fun read(source: HeapInstance): Sequence<Reference> {
117     val instanceClassId = source.instanceClassId
118     val elementFieldRef =
119       source[className, elementArrayName]!!.valueAsObjectArray ?: return emptySequence()
120 
121     val elements = if (sizeFieldName != null) {
122       val size = source[className, sizeFieldName]!!.value.asInt!!
123       elementFieldRef.readElements().take(size)
124     } else {
125       elementFieldRef.readElements()
126     }
127     return elements.withIndex()
128       .mapNotNull { (index, elementValue) ->
129         if (elementValue.isNonNullReference) {
130           Reference(
131             valueObjectId = elementValue.asObjectId!!,
132             isLowPriority = false,
133             lazyDetailsResolver = {
134               LazyDetails(
135                 name = "$index",
136                 locationClassObjectId = instanceClassId,
137                 locationType = ARRAY_ENTRY,
138                 isVirtual = true,
139                 matchedLibraryLeak = null
140               )
141             }
142           )
143         } else {
144           null
145         }
146       }
147   }
148 }
149 
150 internal class InternalSharedLinkedListReferenceReader(
151   private val classObjectId: Long,
152   private val headFieldName: String,
153   private val nodeClassName: String,
154   private val nodeNextFieldName: String,
155   private val nodeElementFieldName: String
156 ) : VirtualInstanceReferenceReader {
157 
matchesnull158   override fun matches(instance: HeapInstance): Boolean {
159     return instance.instanceClassId == classObjectId
160   }
161 
readnull162   override fun read(source: HeapInstance): Sequence<Reference> {
163     val instanceClassId = source.instanceClassId
164     // head may be null, in that case we generate an empty sequence.
165     val firstNode = source["java.util.LinkedList", headFieldName]!!.valueAsInstance
166     val visitedNodes = mutableSetOf<Long>()
167     if (firstNode != null) {
168       visitedNodes += firstNode.objectId
169     }
170     return generateSequence(firstNode) { node ->
171       val nextNode = node[nodeClassName, nodeNextFieldName]!!.valueAsInstance
172       if (nextNode != null && visitedNodes.add(nextNode.objectId)) {
173         nextNode
174       } else {
175         null
176       }
177     }
178       .withIndex()
179       .mapNotNull { (index, node) ->
180         val itemObjectId = node[nodeClassName, nodeElementFieldName]!!.value.asObjectId
181         itemObjectId?.run {
182           Reference(
183             valueObjectId = this,
184             isLowPriority = false,
185             lazyDetailsResolver = {
186               LazyDetails(
187                 // All entries are represented by the same key name, e.g. "key()"
188                 name = "$index",
189                 locationClassObjectId = instanceClassId,
190                 locationType = ARRAY_ENTRY,
191                 isVirtual = true,
192                 matchedLibraryLeak = null
193               )
194             }
195           )
196         }
197       }
198   }
199 }
200