mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
JVM IR: fix incorrect detection of interface method impls in Java
The problem in the test case was that `JImpl.entrySet` was detected by
ReplaceDefaultImplsOverriddenSymbols as a class fake override, which
overrides non-abstract interface method. Thus, overriddenSymbols of
`MyMap.entrySet` were changed in
`ReplaceDefaultImplsOverriddenSymbols.visitSimpleFunction`.
Later, BridgeLowering tried to determine which special bridges to
generate for `MyMap.<get-entries>`, and for that it inspected existing
methods and their overrides.
Normally we would generate the following special bridge:
getEntries()Ljava/util/Set;
invokespecial JImpl.entrySet()Ljava/util/Set;
However, because of incorrect overrides, the generated class method was
selected here instead of the expected `Map.<get-entries>`:
06001fc091/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt (L282)
and since the JVM signature of the generated class method is the same as
that of the fake override in MyMap, we never got to generating the
special bridge.
The solution is to skip Java classes when looking for class methods
which override non-abstract interface methods. This logic only makes
sense for Kotlin code, because only Kotlin has DefaultImpls, and the
requirement to generate non-abstract fake overrides of interface methods
as actual methods in the bytecode, delegating to DefaultImpls.
#KT-48167 Fixed
This commit is contained in:
@@ -40578,6 +40578,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/irrelevantRemoveAtOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaMapWithCustomEntries.kt")
|
||||
public void testJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("mapGetOrDefault.kt")
|
||||
public void testMapGetOrDefault() throws Exception {
|
||||
|
||||
@@ -262,7 +262,9 @@ private class InterfaceObjectCallsLowering(val context: JvmBackendContext) : IrE
|
||||
*/
|
||||
internal fun IrSimpleFunction.findInterfaceImplementation(jvmDefaultMode: JvmDefaultMode): IrSimpleFunction? {
|
||||
if (!isFakeOverride) return null
|
||||
parent.let { if (it is IrClass && it.isJvmInterface) return null }
|
||||
|
||||
val parent = parent
|
||||
if (parent is IrClass && (parent.isJvmInterface || parent.isFromJava())) return null
|
||||
|
||||
val implementation = resolveFakeOverride(toSkip = ::isDefaultImplsBridge) ?: return null
|
||||
|
||||
|
||||
81
compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt
vendored
Normal file
81
compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// JVM_TARGET: 1.8
|
||||
// FILE: box.kt
|
||||
|
||||
class MyMap<K, V> : JImpl<K, V>()
|
||||
|
||||
fun box(): String {
|
||||
val a = MyMap<Int, String>()
|
||||
a.put(42, "OK")
|
||||
return a.entries.iterator().next().value
|
||||
}
|
||||
|
||||
// FILE: J.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface J<K, V> extends Map<K, V> {
|
||||
@Override
|
||||
default Set<Entry<K, V>> entrySet() {
|
||||
return myEntrySet();
|
||||
}
|
||||
|
||||
Set<Entry<K,V>> myEntrySet();
|
||||
}
|
||||
|
||||
// FILE: JImpl.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JImpl<K, V> implements J<K, V> {
|
||||
private final Map<K, V> delegate = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> myEntrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return delegate.keySet();
|
||||
}
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return delegate.values();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// WITH_SIGNATURES
|
||||
// JVM_TARGET: 1.8
|
||||
// FILE: test.kt
|
||||
|
||||
class MyMap<K, V> : JImpl<K, V>()
|
||||
|
||||
// FILE: J.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface J<K, V> extends Map<K, V> {
|
||||
@Override
|
||||
default Set<Entry<K, V>> entrySet() {
|
||||
return myEntrySet();
|
||||
}
|
||||
|
||||
Set<Entry<K,V>> myEntrySet();
|
||||
}
|
||||
|
||||
// FILE: JImpl.java
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JImpl<K, V> implements J<K, V> {
|
||||
private final Map<K, V> delegate = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> myEntrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return delegate.keySet();
|
||||
}
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return delegate.values();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@kotlin.Metadata
|
||||
public final class<<K:Ljava/lang/Object;V:Ljava/lang/Object;>LJImpl<TK;TV;>;> MyMap {
|
||||
// source: 'test.kt'
|
||||
public bridge final <()Ljava/util/Collection<TV;>;> method values(): java.util.Collection
|
||||
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;> method entrySet(): java.util.Set
|
||||
public bridge final <()Ljava/util/Set<TK;>;> method keySet(): java.util.Set
|
||||
public <null> method <init>(): void
|
||||
public bridge <null> method getEntries(): java.util.Set
|
||||
public bridge <null> method getKeys(): java.util.Set
|
||||
public bridge <null> method getSize(): int
|
||||
public bridge <null> method getValues(): java.util.Collection
|
||||
public bridge final <null> method size(): int
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@kotlin.Metadata
|
||||
public final class<<K:Ljava/lang/Object;V:Ljava/lang/Object;>LJImpl<TK;TV;>;> MyMap {
|
||||
// source: 'test.kt'
|
||||
public bridge <()Ljava/util/Collection<Ljava/lang/Object;>;> method getValues(): java.util.Collection
|
||||
public bridge final <()Ljava/util/Collection<TV;>;> method values(): java.util.Collection
|
||||
public bridge <()Ljava/util/Set<Ljava/lang/Object;>;> method getKeys(): java.util.Set
|
||||
public bridge <()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/Object;Ljava/lang/Object;>;>;> method getEntries(): java.util.Set
|
||||
public bridge final <()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;> method entrySet(): java.util.Set
|
||||
public bridge final <()Ljava/util/Set<TK;>;> method keySet(): java.util.Set
|
||||
public <null> method <init>(): void
|
||||
public bridge <null> method getSize(): int
|
||||
public bridge final <null> method size(): int
|
||||
}
|
||||
@@ -40416,6 +40416,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/irrelevantRemoveAtOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaMapWithCustomEntries.kt")
|
||||
public void testJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("mapGetOrDefault.kt")
|
||||
public void testMapGetOrDefault() throws Exception {
|
||||
|
||||
@@ -2255,6 +2255,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/signatures/implementsJavaMap.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("implementsJavaMapWithCustomEntries.kt")
|
||||
public void testImplementsJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/signatures/implementsJavaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("implementsMap.kt")
|
||||
public void testImplementsMap() throws Exception {
|
||||
|
||||
@@ -40578,6 +40578,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/irrelevantRemoveAtOverride.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("javaMapWithCustomEntries.kt")
|
||||
public void testJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("mapGetOrDefault.kt")
|
||||
public void testMapGetOrDefault() throws Exception {
|
||||
|
||||
@@ -2303,6 +2303,12 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
|
||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/signatures/implementsJavaMap.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("implementsJavaMapWithCustomEntries.kt")
|
||||
public void testImplementsJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/specialBridges/signatures/implementsJavaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("implementsMap.kt")
|
||||
public void testImplementsMap() throws Exception {
|
||||
|
||||
@@ -32423,6 +32423,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/irrelevantRemoveAtOverride.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("javaMapWithCustomEntries.kt")
|
||||
public void testJavaMapWithCustomEntries() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/javaMapWithCustomEntries.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("mapGetOrDefault.kt")
|
||||
public void testMapGetOrDefault() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/specialBuiltins/mapGetOrDefault.kt");
|
||||
|
||||
Reference in New Issue
Block a user