blob: 343df34b87e815cf7c459e490eb5755d22eaf9ab [file] [log] [blame]
/*
* 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")
}
}