diff --git a/build.gradle.kts b/build.gradle.kts index e75f2758..22bc1a99 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -109,6 +109,7 @@ kotlin { tasks.compileTestKotlin { compilerOptions { languageVersion.set(KotlinVersion.KOTLIN_1_9) + freeCompilerArgs.add("-Xjvm-default=all-compatibility") } } diff --git a/src/main/kotlin/api/KotlinMetadataSignature.kt b/src/main/kotlin/api/KotlinMetadataSignature.kt index d5b0fc72..0a531679 100644 --- a/src/main/kotlin/api/KotlinMetadataSignature.kt +++ b/src/main/kotlin/api/KotlinMetadataSignature.kt @@ -60,6 +60,7 @@ internal data class MethodBinarySignature( super.isEffectivelyPublic(classAccess, classVisibility) && !isAccessOrAnnotationsMethod() && !isDummyDefaultConstructor() + && !isSuspendImplMethod() override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? { return super.findMemberVisibility(classVisibility) @@ -71,6 +72,14 @@ internal data class MethodBinarySignature( private fun isDummyDefaultConstructor() = access.isSynthetic && name == "" && desc == "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V" + + /** + * Kotlin compiler emits special `$suspendImpl` methods for open + * suspendable functions. These synthetic functions could only be invoked from original function, + * or from a corresponding continuation. They don't constitute class's public ABI, but in some cases + * they might be declared public (namely, in case of default interface methods). + */ + private fun isSuspendImplMethod() = access.isSynthetic && name.endsWith("\$suspendImpl") } /** diff --git a/src/test/kotlin/cases/default/default.txt b/src/test/kotlin/cases/default/default.txt index 6e6eb8a9..2a095fb3 100644 --- a/src/test/kotlin/cases/default/default.txt +++ b/src/test/kotlin/cases/default/default.txt @@ -7,7 +7,9 @@ public class cases/default/ClassFunctions { public abstract interface class cases/default/InterfaceFunctions { public abstract fun withAllDefaults (ILjava/lang/String;)V + public static synthetic fun withAllDefaults$default (Lcases/default/InterfaceFunctions;ILjava/lang/String;ILjava/lang/Object;)V public abstract fun withSomeDefaults (ILjava/lang/String;)V + public static synthetic fun withSomeDefaults$default (Lcases/default/InterfaceFunctions;ILjava/lang/String;ILjava/lang/Object;)V } public final class cases/default/InterfaceFunctions$DefaultImpls { diff --git a/src/test/kotlin/cases/interfaces/interfaces.txt b/src/test/kotlin/cases/interfaces/interfaces.txt index 4f37b42f..0e4cecf0 100644 --- a/src/test/kotlin/cases/interfaces/interfaces.txt +++ b/src/test/kotlin/cases/interfaces/interfaces.txt @@ -1,5 +1,5 @@ public abstract interface class cases/interfaces/BaseWithImpl { - public abstract fun foo ()I + public fun foo ()I } public final class cases/interfaces/BaseWithImpl$DefaultImpls { @@ -7,7 +7,7 @@ public final class cases/interfaces/BaseWithImpl$DefaultImpls { } public abstract interface class cases/interfaces/DerivedWithImpl : cases/interfaces/BaseWithImpl { - public abstract fun foo ()I + public fun foo ()I } public final class cases/interfaces/DerivedWithImpl$DefaultImpls { diff --git a/src/test/kotlin/cases/suspend/suspend.kt b/src/test/kotlin/cases/suspend/suspend.kt new file mode 100644 index 00000000..b54b8825 --- /dev/null +++ b/src/test/kotlin/cases/suspend/suspend.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2016-2024 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package cases.suspend + +public interface I { + suspend fun openFunction(): Int = 42 +} + +public interface II : I { + override suspend fun openFunction(): Int { + return super.openFunction() + 1 + } +} + +public open class C : II { + override suspend fun openFunction(): Int { + return super.openFunction() + 2 + } +} diff --git a/src/test/kotlin/cases/suspend/suspend.txt b/src/test/kotlin/cases/suspend/suspend.txt new file mode 100644 index 00000000..295410d5 --- /dev/null +++ b/src/test/kotlin/cases/suspend/suspend.txt @@ -0,0 +1,21 @@ +public class cases/suspend/C : cases/suspend/II { + public fun ()V + public fun openFunction (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class cases/suspend/I { + public fun openFunction (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class cases/suspend/I$DefaultImpls { + public static fun openFunction (Lcases/suspend/I;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class cases/suspend/II : cases/suspend/I { + public fun openFunction (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class cases/suspend/II$DefaultImpls { + public static fun openFunction (Lcases/suspend/II;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/src/test/kotlin/tests/CasesPublicAPITest.kt b/src/test/kotlin/tests/CasesPublicAPITest.kt index d7949b0e..e97d95e3 100644 --- a/src/test/kotlin/tests/CasesPublicAPITest.kt +++ b/src/test/kotlin/tests/CasesPublicAPITest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 JetBrains s.r.o. + * Copyright 2016-2024 JetBrains s.r.o. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ @@ -58,6 +58,8 @@ class CasesPublicAPITest { @Test fun special() { snapshotAPIAndCompare(testName.methodName) } + @Test fun suspend() { snapshotAPIAndCompare(testName.methodName) } + @Test fun whenMappings() { snapshotAPIAndCompare(testName.methodName) } @Test fun enums() { snapshotAPIAndCompare(testName.methodName) }