From 0b3fb41eeb0946715a11827ac230cc07b248a95b Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 13 Oct 2016 09:36:21 +0300 Subject: [PATCH] Do not include $DefaultImpls inner classes in public API when they do not contain any public members (i.e. used only as a container to hold synthetic methods with annotations for properties or typealiases) --- .../PublicApiDump.kt | 4 +-- .../org.jetbrains.kotlin.tools/asmUtils.kt | 5 +++- .../interfaces/interfaceWithEmptyImpls.kt | 22 ++++++++++++++ .../cases/interfaces/interfaceWithImpls.kt | 30 +++++++++++++++++++ .../kotlin/cases/interfaces/interfaces.txt | 27 +++++++++++++++++ .../CasesPublicAPITest.kt | 2 ++ 6 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithEmptyImpls.kt create mode 100644 libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithImpls.kt create mode 100644 libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaces.txt diff --git a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt index 77706b11cc6..313efc3f6d0 100644 --- a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt +++ b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/PublicApiDump.kt @@ -41,7 +41,7 @@ fun getBinaryAPI(classStreams: Sequence, visibilityMap: Map.filterOutNonPublic(): List return filter { it.isPublicAndAccessible() } .map { it.flattenNonPublicBases() } - .filterNot { it.isFileOrMultipartFacade && it.memberSignatures.isEmpty()} + .filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty()} } fun List.dump() = dump(to = System.out) diff --git a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt index 749ec3b6502..b5ebfe4a31c 100644 --- a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt +++ b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt @@ -23,7 +23,7 @@ data class ClassBinarySignature( val memberSignatures: List, val access: AccessFlags, val isEffectivelyPublic: Boolean, - val isFileOrMultipartFacade: Boolean) { + val isNotUsedWhenEmpty: Boolean) { val signature: String get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else " : ${supertypes.joinToString()}" @@ -121,6 +121,7 @@ fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) = 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 @@ -134,12 +135,14 @@ fun FieldNode.isInlineExposed() = hasAnnotation(inlineExposedAnnotationName, inc private object KotlinClassKind { const val FILE = 2 + const val SYNTHETIC_CLASS = 3 const val MULTIPART_FACADE = 4 val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE) } fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS } +fun ClassNode.isDefaultImpls() = isInner() && name.endsWith("\$DefaultImpls") && kotlinClassKind == KotlinClassKind.SYNTHETIC_CLASS val ClassNode.kotlinClassKind: Int? diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithEmptyImpls.kt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithEmptyImpls.kt new file mode 100644 index 00000000000..c27d09549c5 --- /dev/null +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithEmptyImpls.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cases.interfaces + +public interface EmptyImpls { + @SinceKotlin("1.1") + val property: String +} diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithImpls.kt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithImpls.kt new file mode 100644 index 00000000000..3df99c1ee3b --- /dev/null +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaceWithImpls.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cases.interfaces + +public interface BaseWithImpl { + fun foo() = 42 +} + +public interface DerivedWithImpl : BaseWithImpl { + override fun foo(): Int { + return super.foo() + 1 + } +} + +public interface DerivedWithoutImpl : BaseWithImpl + diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaces.txt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaces.txt new file mode 100644 index 00000000000..4f37b42f3af --- /dev/null +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/cases/interfaces/interfaces.txt @@ -0,0 +1,27 @@ +public abstract interface class cases/interfaces/BaseWithImpl { + public abstract fun foo ()I +} + +public final class cases/interfaces/BaseWithImpl$DefaultImpls { + public static fun foo (Lcases/interfaces/BaseWithImpl;)I +} + +public abstract interface class cases/interfaces/DerivedWithImpl : cases/interfaces/BaseWithImpl { + public abstract fun foo ()I +} + +public final class cases/interfaces/DerivedWithImpl$DefaultImpls { + public static fun foo (Lcases/interfaces/DerivedWithImpl;)I +} + +public abstract interface class cases/interfaces/DerivedWithoutImpl : cases/interfaces/BaseWithImpl { +} + +public final class cases/interfaces/DerivedWithoutImpl$DefaultImpls { + public static fun foo (Lcases/interfaces/DerivedWithoutImpl;)I +} + +public abstract interface class cases/interfaces/EmptyImpls { + public abstract fun getProperty ()Ljava/lang/String; +} + diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/CasesPublicAPITest.kt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/CasesPublicAPITest.kt index ec6259db3be..ac9038db0e6 100644 --- a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/CasesPublicAPITest.kt +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/CasesPublicAPITest.kt @@ -20,6 +20,8 @@ class CasesPublicAPITest { @Test fun inline() { snapshotAPIAndCompare(testName.methodName) } + @Test fun interfaces() { snapshotAPIAndCompare(testName.methodName) } + @Test fun internal() { snapshotAPIAndCompare(testName.methodName) } @Test fun java() { snapshotAPIAndCompare(testName.methodName) }