-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathasmUtils.kt
173 lines (132 loc) · 7.19 KB
/
asmUtils.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.tools
import org.objectweb.asm.*
import org.objectweb.asm.tree.*
val ACCESS_NAMES = mapOf(
Opcodes.ACC_PUBLIC to "public",
Opcodes.ACC_PROTECTED to "protected",
Opcodes.ACC_PRIVATE to "private",
Opcodes.ACC_STATIC to "static",
Opcodes.ACC_FINAL to "final",
Opcodes.ACC_ABSTRACT to "abstract",
Opcodes.ACC_SYNTHETIC to "synthetic",
Opcodes.ACC_INTERFACE to "interface",
Opcodes.ACC_ANNOTATION to "annotation")
data class ClassBinarySignature(
val name: String,
val superName: String,
val outerName: String?,
val supertypes: List<String>,
val memberSignatures: List<MemberBinarySignature>,
val access: AccessFlags,
val isEffectivelyPublic: Boolean,
val isNotUsedWhenEmpty: Boolean) {
val signature: String
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}"
}
interface MemberBinarySignature {
val name: String
val desc: String
val access: AccessFlags
val isPublishedApi: Boolean
fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
= access.isPublic && !(access.isProtected && classAccess.isFinal)
&& (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true)
fun findMemberVisibility(classVisibility: ClassVisibility?)
= classVisibility?.members?.get(MemberSignature(name, desc))
val signature: String
}
data class MethodBinarySignature(
override val name: String,
override val desc: String,
override val isPublishedApi: Boolean,
override val access: AccessFlags) : MemberBinarySignature {
override val signature: String
get() = "${access.getModifierString()} fun $name $desc"
override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
= super.isEffectivelyPublic(classAccess, classVisibility)
&& !isAccessOrAnnotationsMethod()
private fun isAccessOrAnnotationsMethod() = access.isSynthetic && (name.startsWith("access\$") || name.endsWith("\$annotations"))
}
data class FieldBinarySignature(
override val name: String,
override val desc: String,
override val isPublishedApi: Boolean,
override val access: AccessFlags) : MemberBinarySignature {
override val signature: String
get() = "${access.getModifierString()} field $name $desc"
override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
val fieldVisibility = super.findMemberVisibility(classVisibility) ?: return null
// good case for 'satisfying': fieldVisibility.satisfying { it.isLateInit() }?.let { classVisibility?.findSetterForProperty(it) }
if (fieldVisibility.isLateInit()) {
classVisibility?.findSetterForProperty(fieldVisibility)?.let { return it }
}
return fieldVisibility
}
}
val MemberBinarySignature.kind: Int get() = when (this) {
is FieldBinarySignature -> 1
is MethodBinarySignature -> 2
else -> error("Unsupported $this")
}
val MEMBER_SORT_ORDER = compareBy<MemberBinarySignature>(
{ it.kind },
{ it.name },
{ it.desc }
)
data class AccessFlags(val access: Int) {
val isPublic: Boolean get() = isPublic(access)
val isProtected: Boolean get() = isProtected(access)
val isStatic: Boolean get() = isStatic(access)
val isFinal: Boolean get() = isFinal(access)
val isSynthetic: Boolean get() = isSynthetic(access)
fun getModifiers(): List<String> = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null }
fun getModifierString(): String = getModifiers().joinToString(" ")
}
fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0
fun isProtected(access: Int) = access and Opcodes.ACC_PROTECTED != 0
fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
fun isFinal(access: Int) = access and Opcodes.ACC_FINAL != 0
fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic(access)
&& !isLocal()
&& !isWhenMappings()
&& (classVisibility?.isPublic(isPublishedApi()) ?: true)
val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false
fun ClassNode.isInner() = innerClassNode != null
fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access
val ClassNode.outerClassName: String? get() = innerClassNode?.outerName
const val publishedApiAnnotationName = "kotlin/PublishedApi"
fun ClassNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
fun MethodNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
fun FieldNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
private object KotlinClassKind {
const val FILE = 2
const val SYNTHETIC_CLASS = 3
const val MULTIPART_FACADE = 4
val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE)
}
fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS }
fun ClassNode.isDefaultImpls() = isInner() && name.endsWith("\$DefaultImpls") && kotlinClassKind == KotlinClassKind.SYNTHETIC_CLASS
val ClassNode.kotlinClassKind: Int?
get() = findAnnotation("kotlin/Metadata", false)?.get("k") as Int?
fun ClassNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
fun MethodNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
fun FieldNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
operator fun AnnotationNode.get(key: String): Any? = values.annotationValue(key)
private fun List<Any>.annotationValue(key: String): Any? {
for (index in (0 .. size / 2 - 1)) {
if (this[index*2] == key)
return this[index*2 + 1]
}
return null
}
private fun findAnnotation(annotationName: String, visibleAnnotations: List<AnnotationNode>?, invisibleAnnotations: List<AnnotationNode>?, includeInvisible: Boolean): AnnotationNode? =
visibleAnnotations?.firstOrNull { it.refersToName(annotationName) } ?:
if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null
fun AnnotationNode.refersToName(name: String) = desc.startsWith('L') && desc.endsWith(';') && desc.regionMatches(1, name, 0, name.length)