xref: /aosp_15_r20/external/leakcanary2/shark/src/main/java/shark/internal/ChainingInstanceReferenceReader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1 package shark.internal
2 
3 import shark.HeapGraph
4 import shark.HeapObject.HeapInstance
5 
6 /**
7  * A [ReferenceReader] that first delegates expanding to [virtualRefReaders] in order until one
8  * matches (or none), and then always proceeds with [fieldRefReader]. This means any
9  * synthetic ref will be on the shortest path, but we still explore the entire data structure so
10  * that we correctly track which objects have been visited and correctly compute dominators and
11  * retained size.
12  */
13 internal class ChainingInstanceReferenceReader(
14   private val virtualRefReaders: List<VirtualInstanceReferenceReader>,
15   private val fieldRefReader: FieldInstanceReferenceReader
16 ) : ReferenceReader<HeapInstance> {
17 
readnull18   override fun read(source: HeapInstance): Sequence<Reference> {
19     val virtualRefs = expandVirtualRefs(source)
20     // Note: always forwarding to fieldRefReader means we may navigate the structure twice
21     // which increases IO reads. However this is a trade-of that allows virtualRef impls to
22     // focus on a subset of references and more importantly it means we still get a proper
23     // calculation of retained size as we don't skip any instance.
24     val fieldRefs = fieldRefReader.read(source)
25     return virtualRefs + fieldRefs
26   }
27 
expandVirtualRefsnull28   private fun expandVirtualRefs(instance: HeapInstance): Sequence<Reference> {
29     for (expander in virtualRefReaders) {
30       if (expander.matches(instance)) {
31         return expander.read(instance)
32       }
33     }
34     return emptySequence()
35   }
36 
37   /**
38    * Same as [ReferenceReader] but [read] is only invoked when [matches] returns
39    * true. [matches] should return false if this [VirtualInstanceReferenceReader] implementation isn't
40    * able to expand the provided instance, in which case [ChainingInstanceReferenceReader] will delegate
41    * to the next [VirtualInstanceReferenceReader] implementation.
42    */
43   interface VirtualInstanceReferenceReader : ReferenceReader<HeapInstance> {
matchesnull44     fun matches(instance: HeapInstance): Boolean
45 
46     /**
47      * May create a new InstanceExpander, depending on what's in the heap graph.
48      * [OptionalFactory] implementations might return a different [ReferenceReader]
49      * depending on which version of a class is present in the heap dump, or they might return null if
50      * that class is missing.
51      */
52     fun interface OptionalFactory {
53       fun create(graph: HeapGraph): VirtualInstanceReferenceReader?
54     }
55   }
56 }
57