-
-
Notifications
You must be signed in to change notification settings - Fork 31
/
ConstantPoolHelpers.kt
86 lines (81 loc) · 3.21 KB
/
ConstantPoolHelpers.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package io.sentry.android.gradle.instrumentation.util
import java.lang.reflect.Field
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
/**
* Looks up for the original [ClassWriter] up the visitor chain by looking at the private `cv` field
* of the [ClassVisitor].
*/
internal fun ClassVisitor.findClassWriter(): ClassWriter? {
var classWriter: ClassVisitor = this
while (!ClassWriter::class.java.isAssignableFrom(classWriter::class.java)) {
val cvField: Field = try {
classWriter::class.java.allFields.find { it.name == "cv" } ?: return null
} catch (e: Throwable) {
return null
}
cvField.isAccessible = true
classWriter = (cvField.get(classWriter) as? ClassVisitor) ?: return null
}
return classWriter as ClassWriter
}
/**
* Looks up for [ClassReader] of the [ClassWriter] through intermediate SymbolTable field.
*/
internal fun ClassWriter.findClassReader(): ClassReader? {
val clazz: Class<out ClassWriter> = this::class.java
val symbolTableField: Field = try {
clazz.allFields.find { it.name == "symbolTable" } ?: return null
} catch (e: Throwable) {
return null
}
symbolTableField.isAccessible = true
val symbolTable = symbolTableField.get(this)
val classReaderField: Field = try {
symbolTable::class.java.getDeclaredField("sourceClassReader")
} catch (e: Throwable) {
return null
}
classReaderField.isAccessible = true
return (classReaderField.get(symbolTable) as? ClassReader)
}
/**
* Looks at the constant pool entries and searches for R8 markers
*/
internal fun ClassReader.isMinifiedClass(): Boolean {
val charBuffer = CharArray(maxStringLength)
// R8 marker is usually in the first 3-5 entries, so we limit it at 10 to speed it up
// (constant pool size can be huge otherwise)
val poolSize = minOf(10, itemCount)
for (i in 1 until poolSize) {
try {
val constantPoolEntry = readConst(i, charBuffer)
if (constantPoolEntry is String && "~~R8" in constantPoolEntry) {
// ~~R8 is a marker in the class' constant pool, which r8 itself is looking at when
// parsing a .class file. See here -> https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/dex/Marker.java#53
return true
}
} catch (e: Throwable) {
// we ignore exceptions here, because some constant pool entries are nulls and the
// readConst method throws IllegalArgumentException when trying to read those
}
}
return false
}
/**
* Gets all fields of the given class and its parents (if any).
*
* Adapted from https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/reflect/FieldUtils.java
*/
private val Class<*>.allFields: List<Field>
get() {
val allFields = mutableListOf<Field>()
var currentClass: Class<*>? = this
while (currentClass != null) {
val declaredFields = currentClass.declaredFields
allFields += declaredFields
currentClass = currentClass.superclass
}
return allFields
}