Filter out nested classes in non-public outer classes.

This commit is contained in:
Ilya Gorbunov
2016-03-16 21:25:16 +03:00
parent a9fd1de263
commit 88a038a0df
@@ -13,8 +13,7 @@ fun main(args: Array<String>) {
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<String>)
data class ClassBinarySignature(val name: String, val outerName: String?, val signature: String, val memberSignatures: List<String>, val isPublic: Boolean)
class ApiDumper(val visibilityMap: Map<String, ClassVisibility>) {
fun getBinaryAPI(jar: JarFile): List<ClassBinarySignature> = 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<String, ClassVisibility>): List<ClassBinarySignature> = 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<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature> {
val classByName = associateBy { it.name }
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
return filter { it.isPublicAndAccessible() }
}
fun dumpBinaryAPI(signatures: List<ClassBinarySignature>) {
signatures.forEach {
println(it.signature)
it.memberSignatures.forEach { println(it) }
println("------------------\n")
}
fun List<ClassBinarySignature>.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<String> = 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<Any>.annotationValue(key: String): Any? {
return null
}
val ClassNode.outerClassName: String?
get() = innerClasses.singleOrNull { it.name == name }?.outerName