xref: /aosp_15_r20/external/leakcanary2/shark-graph/src/main/java/shark/internal/ClassFieldsReader.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
1 package shark.internal
2 
3 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.FieldRecord
4 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.StaticFieldRecord
5 import shark.PrimitiveType
6 import shark.PrimitiveType.BOOLEAN
7 import shark.PrimitiveType.BYTE
8 import shark.PrimitiveType.CHAR
9 import shark.PrimitiveType.DOUBLE
10 import shark.PrimitiveType.FLOAT
11 import shark.PrimitiveType.INT
12 import shark.PrimitiveType.LONG
13 import shark.PrimitiveType.SHORT
14 import shark.ValueHolder
15 import shark.ValueHolder.BooleanHolder
16 import shark.ValueHolder.ByteHolder
17 import shark.ValueHolder.CharHolder
18 import shark.ValueHolder.DoubleHolder
19 import shark.ValueHolder.FloatHolder
20 import shark.ValueHolder.IntHolder
21 import shark.ValueHolder.LongHolder
22 import shark.ValueHolder.ReferenceHolder
23 import shark.ValueHolder.ShortHolder
24 import shark.internal.IndexedObject.IndexedClass
25 
26 internal class ClassFieldsReader(
27   private val identifierByteSize: Int,
28   private val classFieldBytes: ByteArray
29 ) {
30 
classDumpStaticFieldsnull31   fun classDumpStaticFields(indexedClass: IndexedClass): List<StaticFieldRecord> {
32     return read(initialPosition = indexedClass.fieldsIndex) {
33       val staticFieldCount = readUnsignedShort()
34       val staticFields = ArrayList<StaticFieldRecord>(staticFieldCount)
35       for (i in 0 until staticFieldCount) {
36         val nameStringId = readId()
37         val type = readUnsignedByte()
38         val value = readValue(type)
39         staticFields.add(
40           StaticFieldRecord(
41             nameStringId = nameStringId,
42             type = type,
43             value = value
44           )
45         )
46       }
47       staticFields
48     }
49   }
50 
classDumpFieldsnull51   fun classDumpFields(indexedClass: IndexedClass): List<FieldRecord> {
52     return read(initialPosition = indexedClass.fieldsIndex) {
53       skipStaticFields()
54 
55       val fieldCount = readUnsignedShort()
56       val fields = ArrayList<FieldRecord>(fieldCount)
57       for (i in 0 until fieldCount) {
58         fields.add(FieldRecord(nameStringId = readId(), type = readUnsignedByte()))
59       }
60       fields
61     }
62   }
63 
classDumpHasReferenceFieldsnull64   fun classDumpHasReferenceFields(indexedClass: IndexedClass): Boolean {
65     return read(initialPosition = indexedClass.fieldsIndex) {
66       skipStaticFields()
67       val fieldCount = readUnsignedShort()
68       for (i in 0 until fieldCount) {
69         position += identifierByteSize
70         val type = readUnsignedByte()
71         if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
72           return@read true
73         }
74       }
75       return@read false
76     }
77   }
78 
readnull79   private fun <R> read(
80     initialPosition: Int,
81     block: ReadInFlight.() -> R
82   ): R {
83     val readInFlight = ReadInFlight()
84     readInFlight.position = initialPosition
85     return readInFlight.run(block)
86   }
87 
88   private inner class ReadInFlight {
89     var position = 0
90 
skipStaticFieldsnull91     fun skipStaticFields() {
92       val staticFieldCount = readUnsignedShort()
93       for (i in 0 until staticFieldCount) {
94         position += identifierByteSize
95         val type = readUnsignedByte()
96         position += if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
97           identifierByteSize
98         } else {
99           PrimitiveType.byteSizeByHprofType.getValue(type)
100         }
101       }
102     }
103 
readValuenull104     fun readValue(type: Int): ValueHolder {
105       return when (type) {
106         PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
107         BOOLEAN_TYPE -> BooleanHolder(readBoolean())
108         CHAR_TYPE -> CharHolder(readChar())
109         FLOAT_TYPE -> FloatHolder(readFloat())
110         DOUBLE_TYPE -> DoubleHolder(readDouble())
111         BYTE_TYPE -> ByteHolder(readByte())
112         SHORT_TYPE -> ShortHolder(readShort())
113         INT_TYPE -> IntHolder(readInt())
114         LONG_TYPE -> LongHolder(readLong())
115         else -> throw IllegalStateException("Unknown type $type")
116       }
117     }
118 
readBytenull119     fun readByte(): Byte {
120       return classFieldBytes[position++]
121     }
122 
readIntnull123     fun readInt(): Int {
124       return (classFieldBytes[position++].toInt() and 0xff shl 24) or
125         (classFieldBytes[position++].toInt() and 0xff shl 16) or
126         (classFieldBytes[position++].toInt() and 0xff shl 8) or
127         (classFieldBytes[position++].toInt() and 0xff)
128     }
129 
readLongnull130     fun readLong(): Long {
131       return (classFieldBytes[position++].toLong() and 0xff shl 56) or
132         (classFieldBytes[position++].toLong() and 0xff shl 48) or
133         (classFieldBytes[position++].toLong() and 0xff shl 40) or
134         (classFieldBytes[position++].toLong() and 0xff shl 32) or
135         (classFieldBytes[position++].toLong() and 0xff shl 24) or
136         (classFieldBytes[position++].toLong() and 0xff shl 16) or
137         (classFieldBytes[position++].toLong() and 0xff shl 8) or
138         (classFieldBytes[position++].toLong() and 0xff)
139     }
140 
readShortnull141     fun readShort(): Short {
142       return ((classFieldBytes[position++].toInt() and 0xff shl 8) or
143         (classFieldBytes[position++].toInt() and 0xff)).toShort()
144     }
145 
readUnsignedShortnull146     fun readUnsignedShort(): Int {
147       return readShort().toInt() and 0xFFFF
148     }
149 
readUnsignedBytenull150     fun readUnsignedByte(): Int {
151       return readByte().toInt() and 0xFF
152     }
153 
readIdnull154     fun readId(): Long {
155       // As long as we don't interpret IDs, reading signed values here is fine.
156       return when (identifierByteSize) {
157         1 -> readByte().toLong()
158         2 -> readShort().toLong()
159         4 -> readInt().toLong()
160         8 -> readLong()
161         else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
162       }
163     }
164 
readBooleannull165     fun readBoolean(): Boolean {
166       return readByte()
167         .toInt() != 0
168     }
169 
readCharnull170     fun readChar(): Char {
171       return readShort().toChar()
172     }
173 
readFloatnull174     fun readFloat(): Float {
175       return Float.fromBits(readInt())
176     }
177 
readDoublenull178     fun readDouble(): Double {
179       return Double.fromBits(readLong())
180     }
181   }
182 
183   companion object {
184     private val BOOLEAN_TYPE = BOOLEAN.hprofType
185     private val CHAR_TYPE = CHAR.hprofType
186     private val FLOAT_TYPE = FLOAT.hprofType
187     private val DOUBLE_TYPE = DOUBLE.hprofType
188     private val BYTE_TYPE = BYTE.hprofType
189     private val SHORT_TYPE = SHORT.hprofType
190     private val INT_TYPE = INT.hprofType
191     private val LONG_TYPE = LONG.hprofType
192   }
193 }
194