main

square/leakcanary

Last updated at: 29/12/2023 09:38

ClassReferenceReader.kt

TLDR

This file contains the implementation of the ClassReferenceReader class, which is responsible for reading references to heap objects of type HeapClass. It takes a HeapGraph and a list of ReferenceMatcher objects as input.

Classes

ClassReferenceReader

The ClassReferenceReader class is responsible for reading references to HeapClass objects in the heap graph. It implements the ReferenceReader interface and takes a HeapGraph and a list of ReferenceMatcher objects as input. The class provides a read method that takes a HeapClass object as input and returns a sequence of Reference objects representing the references to the static fields of the given HeapClass. The class also has an init block that initializes a mapping of static field names to reference matchers based on the provided referenceMatchers.

package shark

import shark.HeapObject.HeapClass
import shark.Reference.LazyDetails
import shark.ReferenceLocationType.STATIC_FIELD
import shark.ReferencePattern.StaticFieldPattern
import shark.ValueHolder.ReferenceHolder

class ClassReferenceReader(
  graph: HeapGraph,
  referenceMatchers: List<ReferenceMatcher>
) : ReferenceReader<HeapClass> {
  private val staticFieldNameByClassName: Map<String, Map<String, ReferenceMatcher>>

  init {
    val staticFieldNameByClassName = mutableMapOf<String, MutableMap<String, ReferenceMatcher>>()
    referenceMatchers.filterFor(graph).forEach { referenceMatcher ->
      val pattern = referenceMatcher.pattern
      if (pattern is StaticFieldPattern) {
        val mapOrNull = staticFieldNameByClassName[pattern.className]
        val map = if (mapOrNull != null) mapOrNull else {
          val newMap = mutableMapOf<String, ReferenceMatcher>()
          staticFieldNameByClassName[pattern.className] = newMap
          newMap
        }
        map[pattern.fieldName] = referenceMatcher
      }
    }
    this.staticFieldNameByClassName = staticFieldNameByClassName
  }

  override fun read(source: HeapClass): Sequence<Reference> {
    val ignoredStaticFields = staticFieldNameByClassName[source.name] ?: emptyMap()

    return source.readStaticFields().mapNotNull {  staticField ->
      // not non null: no null + no primitives.
      if (!staticField.value.isNonNullReference) {
        return@mapNotNull null
      }
      val fieldName = staticField.name
      if (fieldName == "\$staticOverhead" || fieldName == "\$classOverhead") {
        return@mapNotNull null
      }

      // Note: instead of calling staticField.value.asObjectId!! we cast holder to ReferenceHolder
      // and access value directly. This allows us to avoid unnecessary boxing of Long.
      val valueObjectId = (staticField.value.holder as ReferenceHolder).value
      val referenceMatcher = ignoredStaticFields[fieldName]

      if (referenceMatcher is IgnoredReferenceMatcher) {
        null
      } else {
        val sourceObjectId = source.objectId
        Reference(
          valueObjectId = valueObjectId,
          isLowPriority = referenceMatcher != null,
          lazyDetailsResolver = {
            LazyDetails(
              name = fieldName,
              locationClassObjectId = sourceObjectId,
              locationType = STATIC_FIELD,
              isVirtual = false,
              matchedLibraryLeak = referenceMatcher as LibraryLeakReferenceMatcher?,
            )
          }
        )
      }
    }
  }
}