blob: 607a56eca8256c8c0a97119a887595be6d75ea66 [file] [log] [blame]
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03001/*
2 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
Vsevolod Tolstopyatov74bcc922018-05-03 20:07:54 +03005package kotlinx.coroutines.experimental.tools
6
7import org.objectweb.asm.*
8import org.objectweb.asm.tree.*
9
10val ACCESS_NAMES = mapOf(
11 Opcodes.ACC_PUBLIC to "public",
12 Opcodes.ACC_PROTECTED to "protected",
13 Opcodes.ACC_PRIVATE to "private",
14 Opcodes.ACC_STATIC to "static",
15 Opcodes.ACC_FINAL to "final",
16 Opcodes.ACC_ABSTRACT to "abstract",
17 Opcodes.ACC_SYNTHETIC to "synthetic",
18 Opcodes.ACC_INTERFACE to "interface",
19 Opcodes.ACC_ANNOTATION to "annotation")
20
21data class ClassBinarySignature(
22 val name: String,
23 val superName: String,
24 val outerName: String?,
25 val supertypes: List<String>,
26 val memberSignatures: List<MemberBinarySignature>,
27 val access: AccessFlags,
28 val isEffectivelyPublic: Boolean,
29 val isNotUsedWhenEmpty: Boolean) {
30
31 val signature: String
32 get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}"
33
34}
35
36
37interface MemberBinarySignature {
38 val name: String
39 val desc: String
40 val access: AccessFlags
41 val isPublishedApi: Boolean
42
43 fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
44 = access.isPublic && !(access.isProtected && classAccess.isFinal)
45 && (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true)
46
47 fun findMemberVisibility(classVisibility: ClassVisibility?)
48 = classVisibility?.members?.get(MemberSignature(name, desc))
49
50 val signature: String
51}
52
53data class MethodBinarySignature(
54 override val name: String,
55 override val desc: String,
56 override val isPublishedApi: Boolean,
57 override val access: AccessFlags) : MemberBinarySignature {
58 override val signature: String
59 get() = "${access.getModifierString()} fun $name $desc"
60
61 override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
62 = super.isEffectivelyPublic(classAccess, classVisibility)
63 && !isAccessOrAnnotationsMethod()
64
65 private fun isAccessOrAnnotationsMethod() = access.isSynthetic && (name.startsWith("access\$") || name.endsWith("\$annotations"))
66}
67
68data class FieldBinarySignature(
69 override val name: String,
70 override val desc: String,
71 override val isPublishedApi: Boolean,
72 override val access: AccessFlags) : MemberBinarySignature {
73 override val signature: String
74 get() = "${access.getModifierString()} field $name $desc"
75
76 override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
77 val fieldVisibility = super.findMemberVisibility(classVisibility) ?: return null
78
79 // good case for 'satisfying': fieldVisibility.satisfying { it.isLateInit() }?.let { classVisibility?.findSetterForProperty(it) }
80 if (fieldVisibility.isLateInit()) {
81 classVisibility?.findSetterForProperty(fieldVisibility)?.let { return it }
82 }
83 return fieldVisibility
84 }
85}
86
87val MemberBinarySignature.kind: Int get() = when (this) {
88 is FieldBinarySignature -> 1
89 is MethodBinarySignature -> 2
90 else -> error("Unsupported $this")
91}
92
93val MEMBER_SORT_ORDER = compareBy<MemberBinarySignature>(
94 { it.kind },
95 { it.name },
96 { it.desc }
97)
98
99
100data class AccessFlags(val access: Int) {
101 val isPublic: Boolean get() = isPublic(access)
102 val isProtected: Boolean get() = isProtected(access)
103 val isStatic: Boolean get() = isStatic(access)
104 val isFinal: Boolean get() = isFinal(access)
105 val isSynthetic: Boolean get() = isSynthetic(access)
106
107 fun getModifiers(): List<String> = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null }
108 fun getModifierString(): String = getModifiers().joinToString(" ")
109}
110
111fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0
112fun isProtected(access: Int) = access and Opcodes.ACC_PROTECTED != 0
113fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
114fun isFinal(access: Int) = access and Opcodes.ACC_FINAL != 0
115fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0
116
117
118fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
119 isPublic(access)
120 && !isLocal()
121 && !isWhenMappings()
122 && (classVisibility?.isPublic(isPublishedApi()) ?: true)
123
124
125val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
126fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false
127fun ClassNode.isInner() = innerClassNode != null
128fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
129
130val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access
131val ClassNode.outerClassName: String? get() = innerClassNode?.outerName
132
133
134const val publishedApiAnnotationName = "kotlin/PublishedApi"
135fun ClassNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
136fun MethodNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
137fun FieldNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
138
139
140private object KotlinClassKind {
141 const val FILE = 2
142 const val SYNTHETIC_CLASS = 3
143 const val MULTIPART_FACADE = 4
144
145 val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE)
146}
147
148fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS }
149fun ClassNode.isDefaultImpls() = isInner() && name.endsWith("\$DefaultImpls") && kotlinClassKind == KotlinClassKind.SYNTHETIC_CLASS
150
151
152val ClassNode.kotlinClassKind: Int?
153 get() = findAnnotation("kotlin/Metadata", false)?.get("k") as Int?
154
155fun ClassNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
156fun MethodNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
157fun FieldNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
158
159operator fun AnnotationNode.get(key: String): Any? = values.annotationValue(key)
160
161private fun List<Any>.annotationValue(key: String): Any? {
162 for (index in (0 .. size / 2 - 1)) {
163 if (this[index*2] == key)
164 return this[index*2 + 1]
165 }
166 return null
167}
168
169private fun findAnnotation(annotationName: String, visibleAnnotations: List<AnnotationNode>?, invisibleAnnotations: List<AnnotationNode>?, includeInvisible: Boolean): AnnotationNode? =
170 visibleAnnotations?.firstOrNull { it.refersToName(annotationName) } ?:
171 if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null
172
173fun AnnotationNode.refersToName(name: String) = desc.startsWith('L') && desc.endsWith(';') && desc.regionMatches(1, name, 0, name.length)