Support mixed compilation of new default with non-default or @JvmDefault

This commit is contained in:
Mikhael Bogdanov
2020-02-06 09:43:47 +01:00
committed by Mikhail Bogdanov
parent a3f930d2e4
commit e45a892499
8 changed files with 142 additions and 6 deletions
@@ -222,8 +222,11 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtPureClassOrObject
for (Map.Entry<FunctionDescriptor, FunctionDescriptor> entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) {
FunctionDescriptor interfaceFun = entry.getKey();
//skip java 8 default methods
if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) && !JvmAnnotationUtilKt
.isCompiledToJvmDefaultIfNoAbstract(interfaceFun, state.getJvmDefaultMode())) {
if (!CodegenUtilKt.isDefinitelyNotDefaultImplsMethod(interfaceFun) &&
!JvmAnnotationUtilKt.isCallableMemberCompiledToJvmDefaultIfNoAbstract(
DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(interfaceFun), state.getJvmDefaultMode()
)
) {
generateDelegationToDefaultImpl(interfaceFun, entry.getValue());
}
}
@@ -732,7 +732,9 @@ public class FunctionCodegen {
@NotNull JvmDefaultMode jvmDefaultMode
) {
return OwnerKind.DEFAULT_IMPLS == context.getContextKind() &&
JvmAnnotationUtilKt.isCompiledToJvmDefaultIfNoAbstract(functionDescriptor, jvmDefaultMode) &&
JvmAnnotationUtilKt
.isCompiledToJvmDefaultIfNoAbstract(DescriptorUtils.unwrapFakeOverrideToAnyDeclaration(functionDescriptor),
jvmDefaultMode) &&
jvmDefaultMode.isCompatibility();
}
@@ -1540,7 +1540,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
return if (descriptor is AccessorForConstructorDescriptor) false else isAccessor(descriptor)
}
private fun findAnyDeclaration(function: FunctionDescriptor): FunctionDescriptor {
internal fun findAnyDeclaration(function: FunctionDescriptor): FunctionDescriptor {
return if (function.kind == CallableMemberDescriptor.Kind.DECLARATION) {
function
} else findBaseDeclaration(function)
@@ -8,13 +8,17 @@ package org.jetbrains.kotlin.resolve.jvm.annotations
import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
val JVM_DEFAULT_FQ_NAME = FqName("kotlin.jvm.JvmDefault")
val JVM_OVERLOADS_FQ_NAME = FqName("kotlin.jvm.JvmOverloads")
@@ -44,8 +48,15 @@ fun DeclarationDescriptor.hasJvmFieldAnnotation(): Boolean =
fun DeclarationDescriptor.isCallableMemberCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean =
this is CallableMemberDescriptor && isCompiledToJvmDefaultIfNoAbstract(jvmDefault)
fun CallableMemberDescriptor.isCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean =
jvmDefault.forAllMehtodsWithBody || DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)
fun CallableMemberDescriptor.isCompiledToJvmDefaultIfNoAbstract(jvmDefault: JvmDefaultMode): Boolean {
val directMember = DescriptorUtils.getDirectMember(this)
val clazz = directMember.containingDeclaration
if (directMember.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)) return true
if (clazz !is DeserializedClassDescriptor) return jvmDefault.forAllMethodsWithBody
return JvmProtoBufUtil.isNewPlaceForBodyGeneration(clazz.classProto)
}
fun CallableMemberDescriptor.hasJvmDefaultAnnotation(): Boolean =
DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)
@@ -0,0 +1,36 @@
// FULL_JDK
// FILE: 1.kt
interface KInterface {
fun call(): List<String> {
return Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}
fun superCall() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}
// FILE: main.kt
// !JVM_DEFAULT_MODE: all-compatibility
// JVM_TARGET: 1.8
interface KInterface2 : KInterface {
override fun superCall() = super.superCall()
}
class Foo: KInterface2 {
fun superCall2() = super<KInterface2>.superCall()
}
fun box(): String {
var result = Foo().call()
if (result[1] != "KInterface\$DefaultImpls.call") return "fail 1: ${result[1]}"
if (result[2] != "KInterface2\$DefaultImpls.call") return "fail 2: ${result[2]}"
if (result[3] != "Foo.call") return "fail 3: ${result[3]}"
if (result[4] != "MainKt.box") return "fail 4: ${result[4]}"
result = Foo().superCall2()
if (result[1] != "KInterface\$DefaultImpls.superCall") return "fail 1: ${result[1]}"
if (result[2] != "KInterface2.superCall") return "fail 2: ${result[2]}"
if (result[3] != "Foo.superCall2") return "fail 3: ${result[3]}"
if (result[4] != "MainKt.box") return "fail 4: ${result[4]}"
return "OK"
}
@@ -0,0 +1,38 @@
// FULL_JDK
// WITH_RUNTIME
// JVM_TARGET: 1.8
// FILE: 1.kt
// !JVM_DEFAULT_MODE: enable
interface KInterface {
@JvmDefault
fun call(): List<String> {
return Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}
fun superCall() = Thread.currentThread().getStackTrace().map { it.className + "." + it.methodName }
}
// FILE: main.kt
// !JVM_DEFAULT_MODE: all
interface KInterface2 : KInterface {
override fun superCall() = super.superCall()
}
class Foo: KInterface2 {
fun superCall2() = super<KInterface2>.superCall()
}
fun box(): String {
var result = Foo().call()
if (result[1] != "KInterface.call") return "fail 1: ${result[1]}"
if (result[2] != "MainKt.box") return "fail 2: ${result[2]}"
result = Foo().superCall2()
if (result[1] != "KInterface\$DefaultImpls.superCall") return "fail 1: ${result[1]}"
if (result[2] != "KInterface2.superCall") return "fail 2: ${result[2]}"
if (result[3] != "Foo.superCall2") return "fail 3: ${result[3]}"
if (result[4] != "MainKt.box") return "fail 4: ${result[4]}"
return "OK"
}
@@ -459,6 +459,29 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/allCompatibility/superPropAccessFromInterface2.kt");
}
}
@TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Interop extends AbstractCompileKotlinAgainstKotlinTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInterop() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("newAndOldSchemes.kt")
public void testNewAndOldSchemes() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt");
}
@TestMetadata("newSchemeWithJvmDefault.kt")
public void testNewSchemeWithJvmDefault() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt");
}
}
}
@TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/jvm8against6")
@@ -454,6 +454,29 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/allCompatibility/superPropAccessFromInterface2.kt");
}
}
@TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Interop extends AbstractIrCompileKotlinAgainstKotlinTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInInterop() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("newAndOldSchemes.kt")
public void testNewAndOldSchemes() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newAndOldSchemes.kt");
}
@TestMetadata("newSchemeWithJvmDefault.kt")
public void testNewSchemeWithJvmDefault() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvm8/defaults/interop/newSchemeWithJvmDefault.kt");
}
}
}
@TestMetadata("compiler/testData/compileKotlinAgainstKotlin/jvm8/jvm8against6")