<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