JVM IR: do not generate DefaultImpls delegate for collection fake overrides
In the newly added test, prior to this change, JVM IR was generating DefaultImpls classes with calls to things like `kotlin/collections/MutableList$DefaultImpls.spliterator` and other default methods present in JDK 8+. This obviously didn't make much sense. Although these weren't explicitly mentioned anywhere in the bytecode, they caused some validation tools to report errors (e.g. animalsniffer used in arrow).
This commit is contained in:
@@ -156,6 +156,7 @@ fun IrSimpleFunction.isCompiledToJvmDefault(jvmDefaultMode: JvmDefaultMode): Boo
|
|||||||
assert(!isFakeOverride && parentAsClass.isInterface && modality != Modality.ABSTRACT) {
|
assert(!isFakeOverride && parentAsClass.isInterface && modality != Modality.ABSTRACT) {
|
||||||
"`isCompiledToJvmDefault` should be called on non-fakeoverrides and non-abstract methods from interfaces ${ir2string(this)}"
|
"`isCompiledToJvmDefault` should be called on non-fakeoverrides and non-abstract methods from interfaces ${ir2string(this)}"
|
||||||
}
|
}
|
||||||
|
if (origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) return false
|
||||||
if (hasJvmDefault()) return true
|
if (hasJvmDefault()) return true
|
||||||
val parentDescriptor = propertyIfAccessor.parentAsClass.descriptor
|
val parentDescriptor = propertyIfAccessor.parentAsClass.descriptor
|
||||||
if (parentDescriptor !is DeserializedClassDescriptor) return jvmDefaultMode.forAllMethodsWithBody
|
if (parentDescriptor !is DeserializedClassDescriptor) return jvmDefaultMode.forAllMethodsWithBody
|
||||||
|
|||||||
+14
-12
@@ -171,19 +171,21 @@ private class InterfaceDefaultCallsLowering(val context: JvmBackendContext) : Ir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod(jvmDefaultMode: JvmDefaultMode): Boolean {
|
internal fun IrSimpleFunction.isDefinitelyNotDefaultImplsMethod(
|
||||||
if (resolveFakeOverride()?.run {
|
jvmDefaultMode: JvmDefaultMode,
|
||||||
origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB || isCompiledToJvmDefault(
|
implementation: IrSimpleFunction? = resolveFakeOverride()
|
||||||
jvmDefaultMode
|
): Boolean =
|
||||||
)
|
implementation == null ||
|
||||||
} != false) return true
|
implementation.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB ||
|
||||||
|
implementation.isCompiledToJvmDefault(jvmDefaultMode) ||
|
||||||
return origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||||
hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME) ||
|
hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME) ||
|
||||||
(name.asString() == "clone" &&
|
isCloneableClone()
|
||||||
parent.safeAs<IrClass>()?.fqNameWhenAvailable?.asString() == "kotlin.Cloneable" &&
|
|
||||||
valueParameters.isEmpty())
|
private fun IrSimpleFunction.isCloneableClone(): Boolean =
|
||||||
}
|
name.asString() == "clone" &&
|
||||||
|
parent.safeAs<IrClass>()?.fqNameWhenAvailable?.asString() == "kotlin.Cloneable" &&
|
||||||
|
valueParameters.isEmpty()
|
||||||
|
|
||||||
internal val interfaceObjectCallsPhase = makeIrFilePhase(
|
internal val interfaceObjectCallsPhase = makeIrFilePhase(
|
||||||
lowering = ::InterfaceObjectCallsLowering,
|
lowering = ::InterfaceObjectCallsLowering,
|
||||||
|
|||||||
+2
-3
@@ -105,18 +105,17 @@ internal class InterfaceLowering(val context: JvmBackendContext) : IrElementTran
|
|||||||
when {
|
when {
|
||||||
Visibilities.isPrivate(implementation.visibility) || implementation.isMethodOfAny() ->
|
Visibilities.isPrivate(implementation.visibility) || implementation.isMethodOfAny() ->
|
||||||
continue@loop
|
continue@loop
|
||||||
!implementation.isCompiledToJvmDefault(jvmDefaultMode) -> {
|
!function.isDefinitelyNotDefaultImplsMethod(jvmDefaultMode, implementation) -> {
|
||||||
val defaultImpl = createDefaultImpl(function)
|
val defaultImpl = createDefaultImpl(function)
|
||||||
val superImpl = firstSuperMethodFromKotlin(function, implementation)
|
val superImpl = firstSuperMethodFromKotlin(function, implementation)
|
||||||
context.declarationFactory.getDefaultImplsFunction(superImpl.owner).also {
|
context.declarationFactory.getDefaultImplsFunction(superImpl.owner).also {
|
||||||
defaultImpl.bridgeToStatic(it)
|
defaultImpl.bridgeToStatic(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmDefaultMode.isCompatibility -> {
|
jvmDefaultMode.isCompatibility && implementation.isCompiledToJvmDefault(jvmDefaultMode) -> {
|
||||||
val defaultImpl = createDefaultImpl(function)
|
val defaultImpl = createDefaultImpl(function)
|
||||||
defaultImpl.bridgeViaAccessorTo(function)
|
defaultImpl.bridgeViaAccessorTo(function)
|
||||||
}
|
}
|
||||||
// else -> Do nothing.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
// !JVM_DEFAULT_MODE: all-compatibility
|
||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// FULL_JDK
|
||||||
|
// FILE: J.java
|
||||||
|
|
||||||
|
public interface J {
|
||||||
|
default void foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: K.kt
|
||||||
|
|
||||||
|
interface K : J
|
||||||
|
|
||||||
|
interface MyList<T> : MutableList<T>
|
||||||
|
interface MySet<E> : MutableSet<E>
|
||||||
|
interface MyMap<K, V> : MutableMap<K, V>
|
||||||
|
|
||||||
|
interface MyMap2<X, Y> : MyMap<X, Y>
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public interface K
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyList
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyMap
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyMap2
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MySet
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// FULL_JDK
|
||||||
|
// FILE: J.java
|
||||||
|
|
||||||
|
public interface J {
|
||||||
|
default void foo() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: K.kt
|
||||||
|
|
||||||
|
interface K : J
|
||||||
|
|
||||||
|
interface MyList<T> : MutableList<T>
|
||||||
|
interface MySet<E> : MutableSet<E>
|
||||||
|
interface MyMap<K, V> : MutableMap<K, V>
|
||||||
|
|
||||||
|
interface MyMap2<X, Y> : MyMap<X, Y>
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public interface K
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyList
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyMap
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MyMap2
|
||||||
|
|
||||||
|
@kotlin.Metadata
|
||||||
|
public interface MySet
|
||||||
+49
@@ -433,6 +433,50 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Jvm8 extends AbstractBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInJvm8() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8/defaults")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Defaults extends AbstractBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInDefaults() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8/defaults"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class AllCompatibility extends AbstractBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInAllCompatibility() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noDefaultImplsOnEmptySubInterface.kt")
|
||||||
|
public void testNoDefaultImplsOnEmptySubInterface() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility/noDefaultImplsOnEmptySubInterface.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/main")
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/main")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -506,6 +550,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/contains.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/contains.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noDefaultImplsOnEmptySubInterface.kt")
|
||||||
|
public void testNoDefaultImplsOnEmptySubInterface() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noDefaultImplsOnEmptySubInterface.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noSpecialBridgeIfPresentInSuperClass.kt")
|
@TestMetadata("noSpecialBridgeIfPresentInSuperClass.kt")
|
||||||
public void testNoSpecialBridgeIfPresentInSuperClass() throws Exception {
|
public void testNoSpecialBridgeIfPresentInSuperClass() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noSpecialBridgeIfPresentInSuperClass.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noSpecialBridgeIfPresentInSuperClass.kt");
|
||||||
|
|||||||
+49
@@ -403,6 +403,50 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Jvm8 extends AbstractIrBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInJvm8() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8/defaults")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Defaults extends AbstractIrBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInDefaults() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8/defaults"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class AllCompatibility extends AbstractIrBytecodeListingTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInAllCompatibility() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noDefaultImplsOnEmptySubInterface.kt")
|
||||||
|
public void testNoDefaultImplsOnEmptySubInterface() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/jvm8/defaults/allCompatibility/noDefaultImplsOnEmptySubInterface.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/main")
|
@TestMetadata("compiler/testData/codegen/bytecodeListing/main")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -476,6 +520,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
|
|||||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/contains.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/contains.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noDefaultImplsOnEmptySubInterface.kt")
|
||||||
|
public void testNoDefaultImplsOnEmptySubInterface() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noDefaultImplsOnEmptySubInterface.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noSpecialBridgeIfPresentInSuperClass.kt")
|
@TestMetadata("noSpecialBridgeIfPresentInSuperClass.kt")
|
||||||
public void testNoSpecialBridgeIfPresentInSuperClass() throws Exception {
|
public void testNoSpecialBridgeIfPresentInSuperClass() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noSpecialBridgeIfPresentInSuperClass.kt");
|
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/noSpecialBridgeIfPresentInSuperClass.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user