From 88a038a0df244732157cc996206afc32667f451c Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 16 Mar 2016 21:25:16 +0300 Subject: [PATCH] Filter out nested classes in non-public outer classes. --- .../PublicApiDump.kt | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt index cbd16acac89..753e7a775cf 100644 --- a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt +++ b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt @@ -13,8 +13,7 @@ fun main(args: Array) { println(src) println("------------------\n"); val visibilities = readKotlinVisibilities(File("""stdlib/target/stdlib-declarations.json""")) - val apiDumper = ApiDumper(visibilities) - dumpBinaryAPI(apiDumper.getBinaryAPI(JarFile(src))) + getBinaryAPI(JarFile(src), visibilities).filterOutNonPublic().dump() } @@ -34,54 +33,58 @@ fun JarFile.classEntries() = entries().asSequence().filter { !it.isDirectory && it.name.endsWith(".class") } -data class ClassBinarySignature(val name: String, val signature: String, val memberSignatures: List) +data class ClassBinarySignature(val name: String, val outerName: String?, val signature: String, val memberSignatures: List, val isPublic: Boolean) -class ApiDumper(val visibilityMap: Map) { - fun getBinaryAPI(jar: JarFile): List = jar.classEntries() - .map { entry -> - jar.getInputStream(entry).use { stream -> - val classNode = ClassNode() - ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE) - classNode - } +fun getBinaryAPI(jar: JarFile, visibilityMap: Map): List = jar.classEntries() + .map { entry -> + jar.getInputStream(entry).use { stream -> + val classNode = ClassNode() + ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE) + classNode } - .mapNotNull { with(it) { - val classVisibility = visibilityMap[name] - if (!it.isEffectivelyPublic(classVisibility)) - return@mapNotNull null + } + .asIterable() + .sortedBy { it.name } + .mapNotNull { with(it) { + val classVisibility = visibilityMap[name] + val isPublic = it.isEffectivelyPublic(classVisibility) - // TODO: Inspect outer classes + val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted() - val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted() + val classSignature = "${getModifierString(access)} class $name" + + if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}" - val classSignature = "${getModifierString(access)} class $name" + - if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}" + val memberSignatures = + fields.filter { it.isPublic() } + .sortedBy { it.name } + .map { with(it) { "${getModifierString(access)} field $name $desc" } } + + methods.filter { it.isEffectivelyPublic(classVisibility) } + .sortedWith(compareBy({ it.name }, { it.desc })) + .map { with(it) { "${getModifierString(access)} fun $name $desc" } } - val memberSignatures = - fields.filter { it.isPublic() } - .sortedBy { it.name } - .map { with(it) { "${getModifierString(access)} field $name $desc" } } + - methods.filter { it.isEffectivelyPublic(classVisibility) } - .sortedWith(compareBy({ it.name }, { it.desc })) - .map { with(it) { "${getModifierString(access)} fun $name $desc" } } + ClassBinarySignature(name, outerClassName, classSignature, memberSignatures, isPublic) + }} - ClassBinarySignature(name, classSignature, memberSignatures) - }} - .asIterable() - .sortedBy { it.name } + +fun List.filterOutNonPublic(): List { + val classByName = associateBy { it.name } + + fun ClassBinarySignature.isPublicAndAccessible(): Boolean = + isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true) + + return filter { it.isPublicAndAccessible() } } -fun dumpBinaryAPI(signatures: List) { - signatures.forEach { - println(it.signature) - it.memberSignatures.forEach { println(it) } - println("------------------\n") - } +fun List.dump() = forEach { + println(it.signature) + it.memberSignatures.forEach { println(it) } + println("------------------\n") } + fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0 fun getModifiers(access: Int): List = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null } fun getModifierString(access: Int): String = getModifiers(access).joinToString(" ") @@ -137,6 +140,8 @@ private fun List.annotationValue(key: String): Any? { return null } +val ClassNode.outerClassName: String? + get() = innerClasses.singleOrNull { it.name == name }?.outerName