Binary compatibility validator is now an external tool
Use its code from a dependency.
This commit is contained in:
@@ -6,13 +6,7 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':kotlin-stdlib')
|
compile("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.3")
|
||||||
compileOnly project(':kotlinx-metadata')
|
|
||||||
compileOnly project(':kotlinx-metadata-jvm')
|
|
||||||
compile 'org.ow2.asm:asm:6.0'
|
|
||||||
compile 'org.ow2.asm:asm-tree:6.0'
|
|
||||||
|
|
||||||
runtime project(path: ':kotlinx-metadata-jvm', configuration: 'runtime')
|
|
||||||
|
|
||||||
testCompile project(':kotlin-test:kotlin-test-junit')
|
testCompile project(':kotlin-test:kotlin-test-junit')
|
||||||
|
|
||||||
|
|||||||
-104
@@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.tools
|
|
||||||
|
|
||||||
import kotlinx.metadata.jvm.JvmFieldSignature
|
|
||||||
import kotlinx.metadata.jvm.JvmMethodSignature
|
|
||||||
import org.objectweb.asm.*
|
|
||||||
import org.objectweb.asm.tree.*
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.jar.JarFile
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
val src = args[0]
|
|
||||||
println(src)
|
|
||||||
println("------------------\n");
|
|
||||||
getBinaryAPI(JarFile(src)).filterOutNonPublic().dump()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun JarFile.classEntries() = Sequence { entries().iterator() }.filter {
|
|
||||||
!it.isDirectory && it.name.endsWith(".class") && !it.name.startsWith("META-INF/")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getBinaryAPI(jar: JarFile, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> =
|
|
||||||
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityFilter)
|
|
||||||
|
|
||||||
fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityFilter: (String) -> Boolean = { true }): List<ClassBinarySignature> {
|
|
||||||
val classNodes = classStreams.map { it.use { stream ->
|
|
||||||
val classNode = ClassNode()
|
|
||||||
ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
|
|
||||||
classNode
|
|
||||||
}}
|
|
||||||
|
|
||||||
val visibilityMapNew = classNodes.readKotlinVisibilities().filterKeys(visibilityFilter)
|
|
||||||
|
|
||||||
return classNodes
|
|
||||||
.map { with(it) {
|
|
||||||
val metadata = kotlinMetadata
|
|
||||||
val mVisibility = visibilityMapNew[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(JvmFieldSignature(name, desc), isPublishedApi(), AccessFlags(access)) } } +
|
|
||||||
methods.map { with(it) { MethodBinarySignature(JvmMethodSignature(name, desc), isPublishedApi(), AccessFlags(access)) } }
|
|
||||||
).filter {
|
|
||||||
it.isEffectivelyPublic(classAccess, mVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassBinarySignature(name, superName, outerClassName, supertypes, memberSignatures, classAccess,
|
|
||||||
isEffectivelyPublic(mVisibility), metadata.isFileOrMultipartFacade() || isDefaultImpls(metadata)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
.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")
|
|
||||||
}}
|
|
||||||
|
|
||||||
-189
@@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.tools
|
|
||||||
|
|
||||||
import kotlinx.metadata.jvm.*
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import org.objectweb.asm.tree.*
|
|
||||||
|
|
||||||
val ACCESS_NAMES = mapOf(
|
|
||||||
Opcodes.ACC_PUBLIC to "public",
|
|
||||||
Opcodes.ACC_PROTECTED to "protected",
|
|
||||||
Opcodes.ACC_PRIVATE to "private",
|
|
||||||
Opcodes.ACC_STATIC to "static",
|
|
||||||
Opcodes.ACC_FINAL to "final",
|
|
||||||
Opcodes.ACC_ABSTRACT to "abstract",
|
|
||||||
Opcodes.ACC_SYNTHETIC to "synthetic",
|
|
||||||
Opcodes.ACC_INTERFACE to "interface",
|
|
||||||
Opcodes.ACC_ANNOTATION to "annotation"
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ClassBinarySignature(
|
|
||||||
val name: String,
|
|
||||||
val superName: String,
|
|
||||||
val outerName: String?,
|
|
||||||
val supertypes: List<String>,
|
|
||||||
val memberSignatures: List<MemberBinarySignature>,
|
|
||||||
val access: AccessFlags,
|
|
||||||
val isEffectivelyPublic: Boolean,
|
|
||||||
val isNotUsedWhenEmpty: Boolean
|
|
||||||
) {
|
|
||||||
val signature: String
|
|
||||||
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface MemberBinarySignature {
|
|
||||||
val jvmMember: JvmMemberSignature
|
|
||||||
val name: String get() = jvmMember.name
|
|
||||||
val desc: String get() = jvmMember.desc
|
|
||||||
val access: AccessFlags
|
|
||||||
val isPublishedApi: Boolean
|
|
||||||
|
|
||||||
fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) =
|
|
||||||
access.isPublic && !(access.isProtected && classAccess.isFinal)
|
|
||||||
&& (findMemberVisibility(classVisibility)?.isPublic(isPublishedApi) ?: true)
|
|
||||||
|
|
||||||
fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
|
|
||||||
return classVisibility?.findMember(jvmMember)
|
|
||||||
}
|
|
||||||
|
|
||||||
val signature: String
|
|
||||||
}
|
|
||||||
|
|
||||||
data class MethodBinarySignature(
|
|
||||||
override val jvmMember: JvmMethodSignature,
|
|
||||||
override val isPublishedApi: Boolean,
|
|
||||||
override val access: AccessFlags
|
|
||||||
) : MemberBinarySignature {
|
|
||||||
override val signature: String
|
|
||||||
get() = "${access.getModifierString()} fun $name $desc"
|
|
||||||
|
|
||||||
override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) =
|
|
||||||
super.isEffectivelyPublic(classAccess, classVisibility)
|
|
||||||
&& !isAccessOrAnnotationsMethod()
|
|
||||||
&& !isDummyDefaultConstructor()
|
|
||||||
|
|
||||||
override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
|
|
||||||
return super.findMemberVisibility(classVisibility) ?: classVisibility?.let { alternateDefaultSignature(it.name)?.let(it::findMember) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isAccessOrAnnotationsMethod() = access.isSynthetic && (name.startsWith("access\$") || name.endsWith("\$annotations"))
|
|
||||||
|
|
||||||
private fun isDummyDefaultConstructor() = access.isSynthetic && name == "<init>" && desc == "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the signature of this method without default parameters
|
|
||||||
*
|
|
||||||
* Returns `null` if this method isn't an entry point of a function
|
|
||||||
* or a constructor with default parameters.
|
|
||||||
* Returns an incorrect result, if there are more than 31 default parameters.
|
|
||||||
*/
|
|
||||||
private fun alternateDefaultSignature(className: String): JvmMethodSignature? {
|
|
||||||
return when {
|
|
||||||
!access.isSynthetic -> null
|
|
||||||
name == "<init>" && "ILkotlin/jvm/internal/DefaultConstructorMarker;" in desc ->
|
|
||||||
JvmMethodSignature(name, desc.replace("ILkotlin/jvm/internal/DefaultConstructorMarker;", ""))
|
|
||||||
name.endsWith("\$default") && "ILjava/lang/Object;)" in desc ->
|
|
||||||
JvmMethodSignature(
|
|
||||||
name.removeSuffix("\$default"),
|
|
||||||
desc.replace("ILjava/lang/Object;)", ")").replace("(L$className;", "(")
|
|
||||||
)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class FieldBinarySignature(
|
|
||||||
override val jvmMember: JvmFieldSignature,
|
|
||||||
override val isPublishedApi: Boolean,
|
|
||||||
override val access: AccessFlags
|
|
||||||
) : MemberBinarySignature {
|
|
||||||
override val signature: String
|
|
||||||
get() = "${access.getModifierString()} field $name $desc"
|
|
||||||
|
|
||||||
override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? {
|
|
||||||
return super.findMemberVisibility(classVisibility)
|
|
||||||
?: takeIf { access.isStatic }?.let { super.findMemberVisibility(classVisibility?.companionVisibilities) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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) {
|
|
||||||
val isPublic: Boolean get() = isPublic(access)
|
|
||||||
val isProtected: Boolean get() = isProtected(access)
|
|
||||||
val isStatic: Boolean get() = isStatic(access)
|
|
||||||
val isFinal: Boolean get() = isFinal(access)
|
|
||||||
val isSynthetic: Boolean get() = isSynthetic(access)
|
|
||||||
|
|
||||||
fun getModifiers(): List<String> = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null }
|
|
||||||
fun getModifierString(): String = getModifiers().joinToString(" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0
|
|
||||||
fun isProtected(access: Int) = access and Opcodes.ACC_PROTECTED != 0
|
|
||||||
fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
|
|
||||||
fun isFinal(access: Int) = access and Opcodes.ACC_FINAL != 0
|
|
||||||
fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0
|
|
||||||
|
|
||||||
|
|
||||||
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
|
|
||||||
isPublic(access)
|
|
||||||
&& !isLocal()
|
|
||||||
&& !isWhenMappings()
|
|
||||||
&& (classVisibility?.isPublic(isPublishedApi()) ?: true)
|
|
||||||
|
|
||||||
|
|
||||||
val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
|
|
||||||
fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false
|
|
||||||
fun ClassNode.isInner() = innerClassNode != null
|
|
||||||
fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
|
|
||||||
|
|
||||||
val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access
|
|
||||||
val ClassNode.outerClassName: String? get() = innerClassNode?.outerName
|
|
||||||
|
|
||||||
|
|
||||||
const val publishedApiAnnotationName = "kotlin/PublishedApi"
|
|
||||||
fun ClassNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
|
|
||||||
fun MethodNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
|
|
||||||
fun FieldNode.isPublishedApi() = findAnnotation(publishedApiAnnotationName, includeInvisible = true) != null
|
|
||||||
|
|
||||||
|
|
||||||
fun ClassNode.isDefaultImpls(metadata: KotlinClassMetadata?) = isInner() && name.endsWith("\$DefaultImpls") && metadata.isSyntheticClass()
|
|
||||||
|
|
||||||
|
|
||||||
fun ClassNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
|
|
||||||
fun MethodNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
|
|
||||||
fun FieldNode.findAnnotation(annotationName: String, includeInvisible: Boolean = false) = findAnnotation(annotationName, visibleAnnotations, invisibleAnnotations, includeInvisible)
|
|
||||||
|
|
||||||
operator fun AnnotationNode.get(key: String): Any? = values.annotationValue(key)
|
|
||||||
|
|
||||||
private fun List<Any>.annotationValue(key: String): Any? {
|
|
||||||
for (index in (0 until size / 2)) {
|
|
||||||
if (this[index * 2] == key)
|
|
||||||
return this[index * 2 + 1]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findAnnotation(annotationName: String, visibleAnnotations: List<AnnotationNode>?, invisibleAnnotations: List<AnnotationNode>?, includeInvisible: Boolean): AnnotationNode? =
|
|
||||||
visibleAnnotations?.firstOrNull { it.refersToName(annotationName) }
|
|
||||||
?: if (includeInvisible) invisibleAnnotations?.firstOrNull { it.refersToName(annotationName) } else null
|
|
||||||
|
|
||||||
fun AnnotationNode.refersToName(name: String) = desc.startsWith('L') && desc.endsWith(';') && desc.regionMatches(1, name, 0, name.length)
|
|
||||||
-102
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.tools
|
|
||||||
|
|
||||||
import kotlinx.metadata.*
|
|
||||||
import kotlinx.metadata.jvm.*
|
|
||||||
import org.objectweb.asm.tree.ClassNode
|
|
||||||
|
|
||||||
val ClassNode.kotlinMetadata: KotlinClassMetadata?
|
|
||||||
get() {
|
|
||||||
val metadata = findAnnotation("kotlin/Metadata", false) ?: return null
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val header = with(metadata) {
|
|
||||||
KotlinClassHeader(
|
|
||||||
kind = get("k") as Int?,
|
|
||||||
metadataVersion = (get("mv") as List<Int>?)?.toIntArray(),
|
|
||||||
bytecodeVersion = (get("bv") as List<Int>?)?.toIntArray(),
|
|
||||||
data1 = (get("d1") as List<String>?)?.toTypedArray(),
|
|
||||||
data2 = (get("d2") as List<String>?)?.toTypedArray(),
|
|
||||||
extraString = get("xs") as String?,
|
|
||||||
packageName = get("pn") as String?,
|
|
||||||
extraInt = get("xi") as Int?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return KotlinClassMetadata.read(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun KotlinClassMetadata?.isFileOrMultipartFacade() =
|
|
||||||
this is KotlinClassMetadata.FileFacade || this is KotlinClassMetadata.MultiFileClassFacade
|
|
||||||
|
|
||||||
fun KotlinClassMetadata?.isSyntheticClass() = this is KotlinClassMetadata.SyntheticClass
|
|
||||||
|
|
||||||
fun KotlinClassMetadata.toClassVisibility(classNode: ClassNode): ClassVisibility? {
|
|
||||||
var flags: Flags? = null
|
|
||||||
var _facadeClassName: String? = null
|
|
||||||
val members = mutableListOf<MemberVisibility>()
|
|
||||||
|
|
||||||
fun addMember(signature: JvmMemberSignature?, flags: Flags, isReified: Boolean) {
|
|
||||||
if (signature != null) {
|
|
||||||
members.add(MemberVisibility(signature, flags, isReified))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val container: KmDeclarationContainer? = when (this) {
|
|
||||||
is KotlinClassMetadata.Class ->
|
|
||||||
toKmClass().also { klass ->
|
|
||||||
flags = klass.flags
|
|
||||||
|
|
||||||
for (constructor in klass.constructors) {
|
|
||||||
addMember(constructor.signature, constructor.flags, isReified = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is KotlinClassMetadata.FileFacade ->
|
|
||||||
toKmPackage()
|
|
||||||
is KotlinClassMetadata.MultiFileClassPart ->
|
|
||||||
toKmPackage().also { _facadeClassName = this.facadeClassName }
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (container != null) {
|
|
||||||
fun List<KmTypeParameter>.containsReified() = any { Flag.TypeParameter.IS_REIFIED(it.flags) }
|
|
||||||
|
|
||||||
for (function in container.functions) {
|
|
||||||
addMember(function.signature, function.flags, function.typeParameters.containsReified())
|
|
||||||
}
|
|
||||||
|
|
||||||
for (property in container.properties) {
|
|
||||||
val isReified = property.typeParameters.containsReified()
|
|
||||||
addMember(property.getterSignature, property.getterFlags, isReified)
|
|
||||||
addMember(property.setterSignature, property.setterFlags, isReified)
|
|
||||||
|
|
||||||
val fieldVisibility = when {
|
|
||||||
Flag.Property.IS_LATEINIT(property.flags) -> property.setterFlags
|
|
||||||
property.getterSignature == null && property.setterSignature == null -> property.flags // JvmField or const case
|
|
||||||
else -> flagsOf(Flag.IS_PRIVATE)
|
|
||||||
}
|
|
||||||
addMember(property.fieldSignature, fieldVisibility, isReified = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ClassVisibility(classNode.name, flags, members.associateBy { it.member }, _facadeClassName)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ClassNode.toClassVisibility() = kotlinMetadata?.toClassVisibility(this)
|
|
||||||
|
|
||||||
fun Sequence<ClassNode>.readKotlinVisibilities(): Map<String, ClassVisibility> =
|
|
||||||
mapNotNull { it.toClassVisibility() }
|
|
||||||
.associateBy { it.name }
|
|
||||||
.apply {
|
|
||||||
values.asSequence().filter { it.isCompanion }.forEach {
|
|
||||||
val containingClassName = it.name.substringBeforeLast('$')
|
|
||||||
getValue(containingClassName).companionVisibilities = it
|
|
||||||
}
|
|
||||||
|
|
||||||
values.asSequence().filter { it.facadeClassName != null }.forEach {
|
|
||||||
getValue(it.facadeClassName!!).partVisibilities.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jetbrains.kotlin.tools
|
|
||||||
|
|
||||||
import kotlinx.metadata.Flag
|
|
||||||
import kotlinx.metadata.Flags
|
|
||||||
import kotlinx.metadata.jvm.JvmMemberSignature
|
|
||||||
|
|
||||||
class ClassVisibility(
|
|
||||||
val name: String,
|
|
||||||
val flags: Flags?,
|
|
||||||
val members: Map<JvmMemberSignature, MemberVisibility>,
|
|
||||||
val facadeClassName: String? = null
|
|
||||||
) {
|
|
||||||
val visibility get() = flags
|
|
||||||
val isCompanion: Boolean get() = flags != null && Flag.Class.IS_COMPANION_OBJECT(flags)
|
|
||||||
|
|
||||||
var companionVisibilities: ClassVisibility? = null
|
|
||||||
val partVisibilities = mutableListOf<ClassVisibility>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ClassVisibility.findMember(signature: JvmMemberSignature): MemberVisibility? =
|
|
||||||
members[signature] ?: partVisibilities.mapNotNull { it.members[signature] }.firstOrNull()
|
|
||||||
|
|
||||||
|
|
||||||
data class MemberVisibility(val member: JvmMemberSignature, val visibility: Flags?, val isReified: Boolean)
|
|
||||||
|
|
||||||
private fun isPublic(visibility: Flags?, isPublishedApi: Boolean) =
|
|
||||||
visibility == null
|
|
||||||
|| Flag.IS_PUBLIC(visibility)
|
|
||||||
|| Flag.IS_PROTECTED(visibility)
|
|
||||||
|| (isPublishedApi && Flag.IS_INTERNAL(visibility))
|
|
||||||
|
|
||||||
fun ClassVisibility.isPublic(isPublishedApi: Boolean) = isPublic(visibility, isPublishedApi)
|
|
||||||
fun MemberVisibility.isPublic(isPublishedApi: Boolean) =
|
|
||||||
// Assuming isReified implies inline
|
|
||||||
!isReified && isPublic(visibility, isPublishedApi)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.tools.tests
|
package org.jetbrains.kotlin.tools.tests
|
||||||
|
|
||||||
import org.jetbrains.kotlin.tools.*
|
import kotlinx.validation.api.*
|
||||||
import org.junit.*
|
import org.junit.*
|
||||||
import org.junit.rules.TestName
|
import org.junit.rules.TestName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -58,7 +58,7 @@ class CasesPublicAPITest {
|
|||||||
|
|
||||||
val testClassStreams = testClasses.asSequence().filter { it.name.endsWith(".class") }.map { it.inputStream() }
|
val testClassStreams = testClasses.asSequence().filter { it.name.endsWith(".class") }.map { it.inputStream() }
|
||||||
|
|
||||||
val api = getBinaryAPI(testClassStreams).filterOutNonPublic()
|
val api = testClassStreams.loadApiFromJvmClasses().filterOutNonPublic()
|
||||||
|
|
||||||
val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt")
|
val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt")
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.tools.tests
|
package org.jetbrains.kotlin.tools.tests
|
||||||
|
|
||||||
import org.jetbrains.kotlin.tools.*
|
import kotlinx.validation.api.*
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TestName
|
import org.junit.rules.TestName
|
||||||
@@ -51,7 +51,7 @@ class RuntimePublicAPITest {
|
|||||||
val publicPackageFilter = { className: String -> publicPackagePrefixes.none { className.startsWith(it) } }
|
val publicPackageFilter = { className: String -> publicPackagePrefixes.none { className.startsWith(it) } }
|
||||||
|
|
||||||
println("Reading binary API from $jarFile")
|
println("Reading binary API from $jarFile")
|
||||||
val api = getBinaryAPI(JarFile(jarFile), publicPackageFilter).filterOutNonPublic(nonPublicPackages)
|
val api = JarFile(jarFile).loadApiFromJvmClasses(publicPackageFilter).filterOutNonPublic(nonPublicPackages)
|
||||||
|
|
||||||
val target = File("reference-public-api")
|
val target = File("reference-public-api")
|
||||||
.resolve(testName.methodName.replaceCamelCaseWithDashedLowerCase() + ".txt")
|
.resolve(testName.methodName.replaceCamelCaseWithDashedLowerCase() + ".txt")
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.tools.tests
|
package org.jetbrains.kotlin.tools.tests
|
||||||
|
|
||||||
import org.jetbrains.kotlin.tools.*
|
import kotlinx.validation.api.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.fail
|
import kotlin.test.fail
|
||||||
|
|||||||
Reference in New Issue
Block a user