Repository: square/leakcanary
File: shark/src/main/java/shark/internal/PathFinder.kt (Kotlin)

Code before:
    }
  }

  private fun HeapInstance.readAllNonNullFieldsOfReferenceType(): MutableList<HeapField> {
    val hprofGraph = graph // DO NOT REMOVE! Getter performs typecast which is expensive!
    var fieldReader: FieldValuesReader? = null
    val result = mutableListOf<HeapField>()
    var skipBytesCount = 0
    for (heapClass in instanceClass.classHierarchy.toList()) {
      for (fieldRecord in heapClass.readRecord().fields) {
        if (fieldRecord.type != PrimitiveType.REFERENCE_HPROF_TYPE) {
          // Skip all fields that are not references. Track how many bytes to skip
          skipBytesCount += hprofGraph.getRecordSize(fieldRecord)
        } else {
          // Initialize field reader if it's not yet initialized. Replaces `lazy` without synchronization
          if (fieldReader == null) {
            fieldReader = hprofGraph.createFieldValuesReader(readRecord())
          }

          // Skip the accumulated bytes offset
          fieldReader.skipBytes(skipBytesCount)
          skipBytesCount = 0

          val fieldValue = ReferenceHolder(fieldReader.readId())
          if (!fieldValue.isNull) {
            val fieldName = hprofGraph.fieldName(heapClass.objectId, fieldRecord.nameStringId)
            result.add(HeapField(heapClass, fieldName, HeapValue(hprofGraph, fieldValue)))
          }
        }
      }
    }
    return result
  }

  private fun HeapGraph.getRecordSize(field: FieldRecord) =
    when (field.type) {
      PrimitiveType.REFERENCE_HPROF_TYPE -> identifierByteSize
      BOOLEAN.hprofType -> 1
      CHAR.hprofType -> 2
      FLOAT.hprofType -> 4
      DOUBLE.hprofType -> 8
      BYTE.hprofType -> 1
      SHORT.hprofType -> 2
      INT.hprofType -> 4
      LONG.hprofType -> 8
      else -> throw IllegalStateException("Unknown type ${field.type}")
    }

  private fun State.visitObjectArray(
    objectArray: HeapObjectArray,
    parent: ReferencePathNode

Review comment (suggestion):
Replace this with a new API: `HeapClass.fieldName(fieldRecord: FieldRecord)` or `HeapClass.fieldName(fieldIndex: Int)`

That way we don't have to make hprofGraph.fieldName public.

Code after:
            parent = parent,
            refFromParentType = INSTANCE_FIELD,
            refFromParentName = name
        )
        is LibraryLeakReferenceMatcher ->
          LibraryLeakChildNode(
              objectId = objectId,
              parent = parent,
              refFromParentType = INSTANCE_FIELD,
              refFromParentName = name,
              matcher = referenceMatcher
          )
        is IgnoredReferenceMatcher -> null
      }
      if (node != null) {
        enqueue(node)
      }
    }
  }

  private fun HeapInstance.readAllNonNullFieldsOfReferenceType(): MutableList<LongObjectPair<String>> {
    // Assigning to local variable to avoid repeated lookup and cast:
    // HeapInstance.graph casts HeapInstance.hprofGraph to HeapGraph in its getter
    val hprofGraph = graph
    var fieldReader: FieldIdReader? = null
    val result = mutableListOf<LongObjectPair<String>>()
    var skipBytesCount = 0

    for (heapClass in instanceClass.classHierarchyWithoutJavaLangObject()) {
      for (fieldRecord in heapClass.readRecord().fields) {
        if (fieldRecord.type != PrimitiveType.REFERENCE_HPROF_TYPE) {
          // Skip all fields that are not references. Track how many bytes to skip
          skipBytesCount += hprofGraph.getRecordSize(fieldRecord)
        } else {
          // Initialize id reader if it's not yet initialized. Replaces `lazy` without synchronization
          if (fieldReader == null) {
            fieldReader = FieldIdReader(readRecord(), hprofGraph.identifierByteSize)
          }

          // Skip the accumulated bytes offset
          fieldReader.skipBytes(skipBytesCount)
          skipBytesCount = 0

          val objectId = fieldReader.readId()
          if (objectId != 0L) {
            result.add(objectId to heapClass.instanceFieldName(fieldRecord))
          }
        }
      }
    }
    return result