ClassFieldsReader.kt
TLDR
The ClassFieldsReader
class in the file shark-graph/src/main/java/shark/internal/ClassFieldsReader.kt
is responsible for reading fields of a class in a heap dump. It provides methods for reading static fields, instance fields, and determining if a class has reference fields.
Methods
classDumpStaticFields
Reads and returns the static fields of a class.
classDumpFields
Reads and returns the instance fields of a class.
classDumpHasReferenceFields
Checks if a class has any reference fields and returns a boolean indicating the result.
Classes
ClassFieldsReader
The ClassFieldsReader
class is responsible for reading the fields of a class in a heap dump. It provides methods for reading static fields, instance fields, and determining if a class has reference fields.
package shark.internal
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.FieldRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.StaticFieldRecord
import shark.PrimitiveType
import shark.PrimitiveType.BOOLEAN
import shark.PrimitiveType.BYTE
import shark.PrimitiveType.CHAR
import shark.PrimitiveType.DOUBLE
import shark.PrimitiveType.FLOAT
import shark.PrimitiveType.INT
import shark.PrimitiveType.LONG
import shark.PrimitiveType.SHORT
import shark.ValueHolder
import shark.ValueHolder.BooleanHolder
import shark.ValueHolder.ByteHolder
import shark.ValueHolder.CharHolder
import shark.ValueHolder.DoubleHolder
import shark.ValueHolder.FloatHolder
import shark.ValueHolder.IntHolder
import shark.ValueHolder.LongHolder
import shark.ValueHolder.ReferenceHolder
import shark.ValueHolder.ShortHolder
import shark.internal.IndexedObject.IndexedClass
internal class ClassFieldsReader(
private val identifierByteSize: Int,
private val classFieldBytes: ByteArray
) {
fun classDumpStaticFields(indexedClass: IndexedClass): List<StaticFieldRecord> {
return read(initialPosition = indexedClass.fieldsIndex) {
val staticFieldCount = readUnsignedShort()
val staticFields = ArrayList<StaticFieldRecord>(staticFieldCount)
for (i in 0 until staticFieldCount) {
val nameStringId = readId()
val type = readUnsignedByte()
val value = readValue(type)
staticFields.add(
StaticFieldRecord(
nameStringId = nameStringId,
type = type,
value = value
)
)
}
staticFields
}
}
fun classDumpFields(indexedClass: IndexedClass): List<FieldRecord> {
return read(initialPosition = indexedClass.fieldsIndex) {
skipStaticFields()
val fieldCount = readUnsignedShort()
val fields = ArrayList<FieldRecord>(fieldCount)
for (i in 0 until fieldCount) {
fields.add(FieldRecord(nameStringId = readId(), type = readUnsignedByte()))
}
fields
}
}
fun classDumpHasReferenceFields(indexedClass: IndexedClass): Boolean {
return read(initialPosition = indexedClass.fieldsIndex) {
skipStaticFields()
val fieldCount = readUnsignedShort()
for (i in 0 until fieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
return@read true
}
}
return@read false
}
}
private fun <R> read(
initialPosition: Int,
block: ReadInFlight.() -> R
): R {
val readInFlight = ReadInFlight()
readInFlight.position = initialPosition
return readInFlight.run(block)
}
private inner class ReadInFlight {
var position = 0
fun skipStaticFields() {
val staticFieldCount = readUnsignedShort()
for (i in 0 until staticFieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
position += if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
identifierByteSize
} else {
PrimitiveType.byteSizeByHprofType.getValue(type)
}
}
}
fun readValue(type: Int): ValueHolder {
return when (type) {
PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
BOOLEAN_TYPE -> BooleanHolder(readBoolean())
CHAR_TYPE -> CharHolder(readChar())
FLOAT_TYPE -> FloatHolder(readFloat())
DOUBLE_TYPE -> DoubleHolder(readDouble())
BYTE_TYPE -> ByteHolder(readByte())
SHORT_TYPE -> ShortHolder(readShort())
INT_TYPE -> IntHolder(readInt())
LONG_TYPE -> LongHolder(readLong())
else -> throw IllegalStateException("Unknown type $type")
}
}
fun readByte(): Byte {
return classFieldBytes[position++]
}
fun readInt(): Int {
return (classFieldBytes[position++].toInt() and 0xff shl 24) or
(classFieldBytes[position++].toInt() and 0xff shl 16) or
(classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)
}
fun readLong(): Long {
return (classFieldBytes[position++].toLong() and 0xff shl 56) or
(classFieldBytes[position++].toLong() and 0xff shl 48) or
(classFieldBytes[position++].toLong() and 0xff shl 40) or
(classFieldBytes[position++].toLong() and 0xff shl 32) or
(classFieldBytes[position++].toLong() and 0xff shl 24) or
(classFieldBytes[position++].toLong() and 0xff shl 16) or
(classFieldBytes[position++].toLong() and 0xff shl 8) or
(classFieldBytes[position++].toLong() and 0xff)
}
fun readShort(): Short {
return ((classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)).toShort()
}
fun readUnsignedShort(): Int {
return readShort().toInt() and 0xFFFF
}
fun readUnsignedByte(): Int {
return readByte().toInt() and 0xFF
}
fun readId(): Long {
// As long as we don't interpret IDs, reading signed values here is fine.
return when (identifierByteSize) {
1 -> readByte().toLong()
2 -> readShort().toLong()
4 -> readInt().toLong()
8 -> readLong()
else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
}
}
fun readBoolean(): Boolean {
return readByte()
.toInt() != 0
}
fun readChar(): Char {
return readShort().toChar()
}
fun readFloat(): Float {
return Float.fromBits(readInt())
}
fun readDouble(): Double {
return Double.fromBits(readLong())
}
}
companion object {
private val BOOLEAN_TYPE = BOOLEAN.hprofType
private val CHAR_TYPE = CHAR.hprofType
private val FLOAT_TYPE = FLOAT.hprofType
private val DOUBLE_TYPE = DOUBLE.hprofType
private val BYTE_TYPE = BYTE.hprofType
private val SHORT_TYPE = SHORT.hprofType
private val INT_TYPE = INT.hprofType
private val LONG_TYPE = LONG.hprofType
}
}