Binary compatibility validator: filter out empty facades *after* members are flattened from non-public part superclasses.

Use stable member sort order for members flattened from non-public superclasses, rewrite test to make order matter.
This commit is contained in:
Ilya Gorbunov
2016-03-30 20:30:59 +03:00
parent 8e7f259beb
commit 6ba98f7fe6
3 changed files with 23 additions and 15 deletions
@@ -5,7 +5,6 @@ import org.objectweb.asm.tree.*
import java.io.File
import java.io.InputStream
import java.util.jar.JarFile
import kotlin.comparisons.compareBy
fun main(args: Array<String>) {
var src = args[0]
@@ -36,19 +35,13 @@ fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String,
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
val memberSignatures = (
fields
.sortedBy { it.name }
.map { with(it) { FieldBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } } +
methods
.sortedWith(compareBy({ it.name }, { it.desc }))
.map { with(it) { MethodBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } }
fields.map { with(it) { FieldBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } } +
methods.map { with(it) { MethodBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } }
).filter {
it.isEffectivelyPublic(classAccess, classVisibility)
}
val isEffectivelyPublic = it.isEffectivelyPublic(classVisibility)
&& !(isFileOrMultipartFacade() && memberSignatures.isEmpty())
ClassBinarySignature(name, superName, outerClassName, supertypes, memberSignatures, classAccess, isEffectivelyPublic)
ClassBinarySignature(name, superName, outerClassName, supertypes, memberSignatures, classAccess, isEffectivelyPublic(classVisibility), isFileOrMultipartFacade())
}}
.asIterable()
.sortedBy { it.name }
@@ -79,14 +72,16 @@ fun List<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature>
return this.copy(memberSignatures = memberSignatures + inheritedStaticSignatures, supertypes = supertypes - superName)
}
return filter { it -> it.isPublicAndAccessible() }.map { it.flattenNonPublicBases() }
return filter { it.isPublicAndAccessible() }
.map { it.flattenNonPublicBases() }
.filterNot { it.isFileOrMultipartFacade && it.memberSignatures.isEmpty()}
}
fun List<ClassBinarySignature>.dump() = dump(to = System.out)
fun <T: Appendable> List<ClassBinarySignature>.dump(to: T): T = to.apply { this@dump.forEach {
append(it.signature).appendln(" {")
it.memberSignatures.forEach { append("\t").appendln(it.signature) }
it.memberSignatures.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) }
appendln("}\n")
}}
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.tools
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.*
import kotlin.comparisons.*
val ACCESS_NAMES = mapOf(
Opcodes.ACC_PUBLIC to "public",
@@ -21,7 +22,8 @@ data class ClassBinarySignature(
val supertypes: List<String>,
val memberSignatures: List<MemberBinarySignature>,
val access: AccessFlags,
val isEffectivelyPublic: Boolean) {
val isEffectivelyPublic: Boolean,
val isFileOrMultipartFacade: Boolean) {
val signature: String
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}"
@@ -79,6 +81,17 @@ data class FieldBinarySignature(
}
}
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) {
@@ -1,10 +1,10 @@
package cases.java;
class Part1 {
public static void publicMethod(String param) { }
public static void publicMethod(int param) { }
public static class Part2 extends Part1 {
public static void publicMethod(int param) { }
public static void publicMethod(String param) { }
}
}