aboutsummaryrefslogtreecommitdiffstats
path: root/binary-compatibility-validator/src/PublicApiDump.kt
diff options
context:
space:
mode:
Diffstat (limited to 'binary-compatibility-validator/src/PublicApiDump.kt')
-rw-r--r--binary-compatibility-validator/src/PublicApiDump.kt120
1 files changed, 120 insertions, 0 deletions
diff --git a/binary-compatibility-validator/src/PublicApiDump.kt b/binary-compatibility-validator/src/PublicApiDump.kt
new file mode 100644
index 00000000..343df34b
--- /dev/null
+++ b/binary-compatibility-validator/src/PublicApiDump.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines.tools
+
+import org.objectweb.asm.*
+import org.objectweb.asm.tree.*
+import java.io.*
+import java.util.jar.*
+
+fun JarFile.classEntries() = entries().asSequence().filter {
+ !it.isDirectory && it.name.endsWith(".class") && !it.name.startsWith("META-INF/")
+}
+
+fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
+ getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap)
+
+fun getBinaryAPI(
+ classStreams: Sequence<InputStream>,
+ visibilityMap: Map<String, ClassVisibility>
+): List<ClassBinarySignature> =
+ classStreams.map {
+ it.use { stream ->
+ val classNode = ClassNode()
+ ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
+ classNode
+ }
+ }.map {
+ with(it) {
+ val classVisibility = visibilityMap[name]
+ val classAccess = AccessFlags(effectiveAccess and Opcodes.ACC_STATIC.inv())
+ val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
+
+ val memberSignatures = (
+ fields.map {
+ with(it) {
+ FieldBinarySignature(
+ name,
+ desc,
+ isPublishedApi(),
+ AccessFlags(access)
+ )
+ }
+ } +
+ methods.map {
+ with(it) {
+ MethodBinarySignature(
+ name,
+ desc,
+ isPublishedApi(),
+ AccessFlags(access)
+ )
+ }
+ }
+ ).filter {
+ it.isEffectivelyPublic(classAccess, classVisibility)
+ }
+
+ ClassBinarySignature(
+ name,
+ superName,
+ outerClassName,
+ supertypes,
+ memberSignatures,
+ classAccess,
+ isEffectivelyPublic(classVisibility),
+ isFileOrMultipartFacade() || isDefaultImpls()
+ )
+ }
+ }.asIterable().sortedBy { it.name }
+
+
+fun List<ClassBinarySignature>.filterOutNonPublic(nonPublicPackages: List<String> = emptyList()): List<ClassBinarySignature> {
+ val nonPublicPaths = nonPublicPackages.map { it.replace('.', '/') + '/' }
+ val classByName = associateBy { it.name }
+
+ fun ClassBinarySignature.isInNonPublicPackage() =
+ nonPublicPaths.any { name.startsWith(it) }
+
+ fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
+ isEffectivelyPublic &&
+ (outerName == null || classByName[outerName]?.let { outerClass ->
+ !(this.access.isProtected && outerClass.access.isFinal)
+ && outerClass.isPublicAndAccessible()
+ } ?: true)
+
+ fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] })
+
+ fun ClassBinarySignature.flattenNonPublicBases(): ClassBinarySignature {
+
+ val nonPublicSupertypes = supertypes(superName).takeWhile { !it.isPublicAndAccessible() }.toList()
+ if (nonPublicSupertypes.isEmpty())
+ return this
+
+ val inheritedStaticSignatures =
+ nonPublicSupertypes.flatMap { it.memberSignatures.filter { it.access.isStatic } }
+
+ // not covered the case when there is public superclass after chain of private superclasses
+ return this.copy(
+ memberSignatures = memberSignatures + inheritedStaticSignatures,
+ supertypes = supertypes - superName
+ )
+ }
+
+ return filter { !it.isInNonPublicPackage() && it.isPublicAndAccessible() }
+ .map { it.flattenNonPublicBases() }
+ .filterNot { it.isNotUsedWhenEmpty && 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.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) }
+ appendln("}\n")
+ }
+}
+