Skip to content

Commit 01e2561

Browse files
committed
bcv: cleanup the old way of reading kotlin visibilities
1 parent 502d73c commit 01e2561

File tree

6 files changed

+104
-137
lines changed

6 files changed

+104
-137
lines changed

src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
1+
/*
2+
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
3+
* that can be found in the license/LICENSE.txt file.
4+
*/
5+
16
package org.jetbrains.kotlin.tools
27

38
import org.objectweb.asm.*
49
import org.objectweb.asm.tree.*
5-
import java.io.File
610
import java.io.InputStream
711
import java.util.jar.JarFile
812

913
fun main(args: Array<String>) {
10-
var src = args[0]
14+
val src = args[0]
1115
println(src)
1216
println("------------------\n");
13-
val visibilities = readKotlinVisibilities(File("""stdlib/target/stdlib-declarations.json"""))
14-
getBinaryAPI(JarFile(src), visibilities).filterOutNonPublic().dump()
17+
getBinaryAPI(JarFile(src)).filterOutNonPublic().dump()
1518
}
1619

1720

1821
fun JarFile.classEntries() = Sequence { entries().iterator() }.filter {
1922
!it.isDirectory && it.name.endsWith(".class") && !it.name.startsWith("META-INF/")
2023
}
2124

22-
fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> =
23-
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap, visibilityFilter)
25+
fun getBinaryAPI(jar: JarFile, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> =
26+
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter)
2427

25-
fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String, ClassVisibility>, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature>
26-
{
28+
fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> {
2729
val classNodes = classStreams.map { it.use { stream ->
2830
val classNode = ClassNode()
2931
ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
@@ -34,7 +36,6 @@ fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String,
3436

3537
return classNodes
3638
.map { with(it) {
37-
val classVisibility = visibilityMap[name]
3839
val metadata = kotlinMetadata
3940
val mVisibility = visibilityMapNew[name]
4041
val classAccess = AccessFlags(effectiveAccess and Opcodes.ACC_STATIC.inv())
@@ -63,14 +64,14 @@ fun List<ClassBinarySignature>.filterOutNonPublic(nonPublicPackages: List<String
6364
val classByName = associateBy { it.name }
6465

6566
fun ClassBinarySignature.isInNonPublicPackage() =
66-
nonPublicPaths.any { name.startsWith(it) }
67+
nonPublicPaths.any { name.startsWith(it) }
6768

6869
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
69-
isEffectivelyPublic &&
70-
(outerName == null || classByName[outerName]?.let { outerClass ->
71-
!(this.access.isProtected && outerClass.access.isFinal)
72-
&& outerClass.isPublicAndAccessible()
73-
} ?: true)
70+
isEffectivelyPublic &&
71+
(outerName == null || classByName[outerName]?.let { outerClass ->
72+
!(this.access.isProtected && outerClass.access.isFinal)
73+
&& outerClass.isPublicAndAccessible()
74+
} ?: true)
7475

7576
fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] })
7677

@@ -87,15 +88,15 @@ fun List<ClassBinarySignature>.filterOutNonPublic(nonPublicPackages: List<String
8788
}
8889

8990
return filter { !it.isInNonPublicPackage() && it.isPublicAndAccessible() }
90-
.map { it.flattenNonPublicBases() }
91-
.filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty()}
91+
.map { it.flattenNonPublicBases() }
92+
.filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty()}
9293
}
9394

9495
fun List<ClassBinarySignature>.dump() = dump(to = System.out)
9596

96-
fun <T: Appendable> List<ClassBinarySignature>.dump(to: T): T = to.apply { this@dump.forEach {
97-
append(it.signature).appendln(" {")
98-
it.memberSignatures.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) }
99-
appendln("}\n")
97+
fun <T : Appendable> List<ClassBinarySignature>.dump(to: T): T = to.apply { this@dump.forEach {
98+
append(it.signature).appendln(" {")
99+
it.memberSignatures.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) }
100+
appendln("}\n")
100101
}}
101102

Lines changed: 56 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/*
2+
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
3+
* that can be found in the license/LICENSE.txt file.
4+
*/
5+
16
package org.jetbrains.kotlin.tools
27

38
import kotlinx.metadata.jvm.KotlinClassMetadata
@@ -6,43 +11,41 @@ import org.objectweb.asm.tree.*
611
import kotlin.comparisons.*
712

813
val ACCESS_NAMES = mapOf(
9-
Opcodes.ACC_PUBLIC to "public",
10-
Opcodes.ACC_PROTECTED to "protected",
11-
Opcodes.ACC_PRIVATE to "private",
12-
Opcodes.ACC_STATIC to "static",
13-
Opcodes.ACC_FINAL to "final",
14-
Opcodes.ACC_ABSTRACT to "abstract",
15-
Opcodes.ACC_SYNTHETIC to "synthetic",
16-
Opcodes.ACC_INTERFACE to "interface",
17-
Opcodes.ACC_ANNOTATION to "annotation")
14+
Opcodes.ACC_PUBLIC to "public",
15+
Opcodes.ACC_PROTECTED to "protected",
16+
Opcodes.ACC_PRIVATE to "private",
17+
Opcodes.ACC_STATIC to "static",
18+
Opcodes.ACC_FINAL to "final",
19+
Opcodes.ACC_ABSTRACT to "abstract",
20+
Opcodes.ACC_SYNTHETIC to "synthetic",
21+
Opcodes.ACC_INTERFACE to "interface",
22+
Opcodes.ACC_ANNOTATION to "annotation"
23+
)
1824

1925
data class ClassBinarySignature(
20-
val name: String,
21-
val superName: String,
22-
val outerName: String?,
23-
val supertypes: List<String>,
24-
val memberSignatures: List<MemberBinarySignature>,
25-
val access: AccessFlags,
26-
val isEffectivelyPublic: Boolean,
27-
val isNotUsedWhenEmpty: Boolean) {
28-
26+
val name: String,
27+
val superName: String,
28+
val outerName: String?,
29+
val supertypes: List<String>,
30+
val memberSignatures: List<MemberBinarySignature>,
31+
val access: AccessFlags,
32+
val isEffectivelyPublic: Boolean,
33+
val isNotUsedWhenEmpty: Boolean
34+
) {
2935
val signature: String
3036
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}"
31-
3237
}
3338

34-
fun ClassVisibility.findMember(signature: MemberSignature): MemberVisibility? =
35-
members[signature] ?: partVisibilities.mapNotNull { it.members[signature] }.firstOrNull()
3639

3740
interface MemberBinarySignature {
3841
val name: String
3942
val desc: String
4043
val access: AccessFlags
4144
val isPublishedApi: Boolean
4245

43-
fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
44-
= access.isPublic && !(access.isProtected && classAccess.isFinal)
45-
&& (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true)
46+
fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) =
47+
access.isPublic && !(access.isProtected && classAccess.isFinal)
48+
&& (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true)
4649

4750
fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
4851
return classVisibility?.findMember(MemberSignature(name, desc))
@@ -52,10 +55,11 @@ interface MemberBinarySignature {
5255
}
5356

5457
data class MethodBinarySignature(
55-
override val name: String,
56-
override val desc: String,
57-
override val isPublishedApi: Boolean,
58-
override val access: AccessFlags) : MemberBinarySignature {
58+
override val name: String,
59+
override val desc: String,
60+
override val isPublishedApi: Boolean,
61+
override val access: AccessFlags
62+
) : MemberBinarySignature {
5963
override val signature: String
6064
get() = "${access.getModifierString()} fun $name $desc"
6165

@@ -92,35 +96,31 @@ data class MethodBinarySignature(
9296
}
9397

9498
data class FieldBinarySignature(
95-
override val name: String,
96-
override val desc: String,
97-
override val isPublishedApi: Boolean,
98-
override val access: AccessFlags) : MemberBinarySignature {
99+
override val name: String,
100+
override val desc: String,
101+
override val isPublishedApi: Boolean,
102+
override val access: AccessFlags
103+
) : MemberBinarySignature {
99104
override val signature: String
100105
get() = "${access.getModifierString()} field $name $desc"
101106

102107
override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
103-
val fieldVisibility = super.findMemberVisibility(classVisibility)
108+
return super.findMemberVisibility(classVisibility)
104109
?: takeIf { access.isStatic }?.let { super.findMemberVisibility(classVisibility?.companionVisibilities) }
105-
?: return null
106-
107-
if (fieldVisibility.isLateInit()) {
108-
classVisibility?.findSetterForProperty(fieldVisibility)?.let { return it }
109-
}
110-
return fieldVisibility
111110
}
112111
}
113112

114-
val MemberBinarySignature.kind: Int get() = when (this) {
115-
is FieldBinarySignature -> 1
116-
is MethodBinarySignature -> 2
117-
else -> error("Unsupported $this")
118-
}
113+
private val MemberBinarySignature.kind: Int
114+
get() = when (this) {
115+
is FieldBinarySignature -> 1
116+
is MethodBinarySignature -> 2
117+
else -> error("Unsupported $this")
118+
}
119119

120120
val MEMBER_SORT_ORDER = compareBy<MemberBinarySignature>(
121-
{ it.kind },
122-
{ it.name },
123-
{ it.desc }
121+
{ it.kind },
122+
{ it.name },
123+
{ it.desc }
124124
)
125125

126126

@@ -143,10 +143,10 @@ fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0
143143

144144

145145
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
146-
isPublic(access)
147-
&& !isLocal()
148-
&& !isWhenMappings()
149-
&& (classVisibility?.isPublic(isPublishedApi()) ?: true)
146+
isPublic(access)
147+
&& !isLocal()
148+
&& !isWhenMappings()
149+
&& (classVisibility?.isPublic(isPublishedApi()) ?: true)
150150

151151

152152
val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
@@ -164,37 +164,25 @@ fun MethodNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, inc
164164
fun FieldNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
165165

166166

167-
private object KotlinClassKind {
168-
const val FILE = 2
169-
const val SYNTHETIC_CLASS = 3
170-
const val MULTIPART_FACADE = 4
171-
172-
val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE)
173-
}
174-
175-
fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS }
176167
fun ClassNode.isDefaultImpls(metadata: KotlinClassMetadata?) = isInner() && name.endsWith("\$DefaultImpls") && metadata.isSyntheticClass()
177168

178169

179-
val ClassNode.kotlinClassKind: Int?
180-
get() = findAnnotation("kotlin/Metadata", false)?.get("k") as Int?
181-
182170
fun ClassNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
183171
fun MethodNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
184172
fun FieldNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
185173

186174
operator fun AnnotationNode.get(key: String): Any? = values.annotationValue(key)
187175

188176
private fun List<Any>.annotationValue(key: String): Any? {
189-
for (index in (0 .. size / 2 - 1)) {
190-
if (this[index*2] == key)
191-
return this[index*2 + 1]
177+
for (index in (0 until size / 2)) {
178+
if (this[index * 2] == key)
179+
return this[index * 2 + 1]
192180
}
193181
return null
194182
}
195183

196184
private fun findAnnotation(annotationName: String, visibleAnnotations: List<AnnotationNode>?, invisibleAnnotations: List<AnnotationNode>?, includeInvisible: Boolean): AnnotationNode? =
197-
visibleAnnotations?.firstOrNull { it.refersToName(annotationName) } ?:
198-
if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null
185+
visibleAnnotations?.firstOrNull { it.refersToName(annotationName) }
186+
?: if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null
199187

200188
fun AnnotationNode.refersToName(name: String) = desc.startsWith('L') && desc.endsWith(';') && desc.regionMatches(1, name, 0, name.length)

src/main/kotlin/org.jetbrains.kotlin.tools/kotlinMetadataVisibilities.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.objectweb.asm.tree.ClassNode
1212
val ClassNode.kotlinMetadata: KotlinClassMetadata?
1313
get() {
1414
val metadata = findAnnotation("kotlin/Metadata", false) ?: return null
15+
@Suppress("UNCHECKED_CAST")
1516
val header = with(metadata) {
1617
KotlinClassHeader(
1718
kind = get("k") as Int?,
Lines changed: 10 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,26 @@
1-
package org.jetbrains.kotlin.tools
1+
/*
2+
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
3+
* that can be found in the license/LICENSE.txt file.
4+
*/
25

3-
import com.google.gson.internal.Streams
4-
import com.google.gson.stream.JsonReader
5-
import java.io.File
6+
package org.jetbrains.kotlin.tools
67

78
class ClassVisibility(val name: String, val visibility: String?, val members: Map<MemberSignature, MemberVisibility>, val isCompanion: Boolean = false, val facadeClassName: String? = null) {
89
var companionVisibilities: ClassVisibility? = null
910
val partVisibilities = mutableListOf<ClassVisibility>()
1011
}
12+
13+
fun ClassVisibility.findMember(signature: MemberSignature): MemberVisibility? =
14+
members[signature] ?: partVisibilities.mapNotNull { it.members[signature] }.firstOrNull()
15+
16+
1117
data class MemberVisibility(val member: MemberSignature, val declaration: String?, val visibility: String?)
1218
data class MemberSignature(val name: String, val desc: String)
1319

1420
private fun isPublic(visibility: String?, isPublishedApi: Boolean) = visibility == null || visibility == "public" || visibility == "protected" || (isPublishedApi && visibility == "internal")
1521
fun ClassVisibility.isPublic(isPublishedApi: Boolean) = isPublic(visibility, isPublishedApi)
1622
fun MemberVisibility.isPublic(isPublishedApi: Boolean) = isPublic(visibility, isPublishedApi)
1723

18-
fun MemberVisibility.isLateInit() = declaration != null && "lateinit var " in declaration
19-
20-
private val varValPrefix = Regex("va[lr]\\s+")
21-
fun ClassVisibility.findSetterForProperty(property: MemberVisibility): MemberVisibility? {
22-
// ad-hoc solution:
23-
val declaration = property.declaration ?: return null
24-
val match = varValPrefix.find(declaration) ?: return null
25-
val name = declaration.substring(match.range.endInclusive + 1).substringBefore(':')
26-
val setterName = "<set-$name>"
27-
return members.values.find { it.declaration?.contains(setterName) ?: false }
28-
}
2924

30-
fun readKotlinVisibilities(declarationFile: File): Map<String, ClassVisibility> {
31-
val result = mutableListOf<ClassVisibility>()
32-
declarationFile.bufferedReader().use { reader ->
33-
val jsonReader = JsonReader(reader)
34-
jsonReader.beginArray()
35-
while (jsonReader.hasNext()) {
36-
val classObject = Streams.parse(jsonReader).asJsonObject
37-
result += with (classObject) {
38-
val name = getAsJsonPrimitive("class").asString
39-
val visibility = getAsJsonPrimitive("visibility")?.asString
40-
val members = getAsJsonArray("members").map { it ->
41-
with(it.asJsonObject) {
42-
val name = getAsJsonPrimitive("name").asString
43-
val desc = getAsJsonPrimitive("desc").asString
44-
val declaration = getAsJsonPrimitive("declaration")?.asString
45-
val visibility = getAsJsonPrimitive("visibility")?.asString
46-
MemberVisibility(MemberSignature(name, desc), declaration, visibility)
47-
}
48-
}
49-
ClassVisibility(name, visibility, members.associateByTo(hashMapOf()) { it.member })
50-
}
51-
}
52-
jsonReader.endArray()
53-
}
54-
55-
return result.associateByTo(hashMapOf()) { it.name }
56-
}
5725

5826

0 commit comments

Comments
 (0)