Support bridges in interfaces. Fix for KT-12416: Missed bridges in js backend

#KT-12416 Fixed
This commit is contained in:
Mikhael Bogdanov
2016-05-19 15:55:18 +03:00
parent c778af518b
commit bb59638039
12 changed files with 235 additions and 22 deletions
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.backend.common.bridges
import org.jetbrains.kotlin.utils.DFS
import java.util.HashSet
import java.util.*
interface FunctionHandle {
val isDeclaration: Boolean
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.common.bridges
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -25,9 +26,10 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesi
fun <Signature> generateBridgesForFunctionDescriptor(
descriptor: FunctionDescriptor,
signature: (FunctionDescriptor) -> Signature
signature: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean
): Set<Bridge<Signature>> {
return generateBridges(DescriptorBasedFunctionHandle(descriptor), { signature(it.descriptor) })
return generateBridges(DescriptorBasedFunctionHandle(descriptor, isBodyOwner), { signature(it.descriptor) })
}
/**
@@ -47,8 +49,19 @@ fun <Signature> generateBridgesForFunctionDescriptor(
* can generate a bridge near an implementation (of course, in case it has a super-declaration with a different signature). Ultimately this
* eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated.
*/
data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : FunctionHandle {
private val overridden = descriptor.overriddenDescriptors.map { DescriptorBasedFunctionHandle(it.original) }
class DescriptorBasedFunctionHandle(
val descriptor: FunctionDescriptor,
/*
To generate proper bridges for non-abstract function
we should know if the function declaration and its definition in the target platform are the same or not.
For JS and JVM8 they are placed in interface classes and we need generate bridge for such function ('isAbstract' will return false).
For JVM6 target function body generated in separate place (DefaultImpl) and method in interface is abstract
For JVM6 target function body is generated in a separate place (DefaultImpls) and
the method in the interface is abstract so we must not generate bridges for such cases.
*/
isBodyOwner: (DeclarationDescriptor) -> Boolean
) : FunctionHandle {
private val overridden = descriptor.overriddenDescriptors.map { DescriptorBasedFunctionHandle(it.original, isBodyOwner) }
override val isDeclaration: Boolean =
descriptor.kind.isReal ||
@@ -56,9 +69,17 @@ data class DescriptorBasedFunctionHandle(val descriptor: FunctionDescriptor) : F
override val isAbstract: Boolean =
descriptor.modality == Modality.ABSTRACT ||
DescriptorUtils.isInterface(descriptor.containingDeclaration)
isBodyOwner(descriptor.containingDeclaration)
override fun getOverridden() = overridden
override fun hashCode(): Int {
return descriptor.hashCode()
}
override fun equals(other: Any?): Boolean {
return other is DescriptorBasedFunctionHandle && descriptor == other.descriptor
}
}
@@ -77,6 +77,8 @@ import java.util.Set;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isAnnotationOrJvm6Interface;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceMember;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*;
@@ -95,6 +97,13 @@ public class FunctionCodegen {
private final ClassBuilder v;
private final MemberCodegen<?> memberCodegen;
private final Function1<DeclarationDescriptor, Boolean> IS_PURE_INTERFACE_CHECKER = new Function1<DeclarationDescriptor, Boolean>() {
@Override
public Boolean invoke(DeclarationDescriptor descriptor) {
return JvmCodegenUtil.isAnnotationOrJvm6Interface(descriptor, state);
}
};
public FunctionCodegen(
@NotNull CodegenContext owner,
@NotNull ClassBuilder v,
@@ -565,7 +574,7 @@ public class FunctionCodegen {
public void generateBridges(@NotNull FunctionDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor) return;
if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) return;
if (isInterface(descriptor.getContainingDeclaration())) return;
if (isAnnotationOrJvm6Interface(descriptor.getContainingDeclaration(), state)) return;
// equals(Any?), hashCode(), toString() never need bridges
if (isMethodOfAny(descriptor)) return;
@@ -579,7 +588,8 @@ public class FunctionCodegen {
if (!isSpecial) {
bridgesToGenerate = ImplKt.generateBridgesForFunctionDescriptor(
descriptor,
getSignatureMapper(typeMapper)
getSignatureMapper(typeMapper),
IS_PURE_INTERFACE_CHECKER
);
if (!bridgesToGenerate.isEmpty()) {
PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
@@ -594,7 +604,8 @@ public class FunctionCodegen {
else {
Set<BridgeForBuiltinSpecial<Method>> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor,
getSignatureMapper(typeMapper)
getSignatureMapper(typeMapper),
IS_PURE_INTERFACE_CHECKER
);
if (!specials.isEmpty()) {
@@ -914,7 +925,12 @@ public class FunctionCodegen {
iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor());
}
else {
iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
if (isJvm8InterfaceMember(descriptor, state)) {
iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
}
else {
iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
}
}
StackValue.coerce(delegateTo.getReturnType(), bridge.getReturnType(), iv);
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.backend.common.bridges.DescriptorBasedFunctionHandle
import org.jetbrains.kotlin.backend.common.bridges.findAllReachableDeclarations
import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
@@ -46,14 +47,15 @@ class BridgeForBuiltinSpecial<out Signature : Any>(
object BuiltinSpecialBridgesUtil {
@JvmStatic fun <Signature : Any> generateBridgesForBuiltinSpecial(
function: FunctionDescriptor,
signatureByDescriptor: (FunctionDescriptor) -> Signature
signatureByDescriptor: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean
): Set<BridgeForBuiltinSpecial<Signature>> {
val functionHandle = DescriptorBasedFunctionHandle(function)
val functionHandle = DescriptorBasedFunctionHandle(function, isBodyOwner)
val fake = !functionHandle.isDeclaration
val overriddenBuiltin = function.getOverriddenBuiltinReflectingJvmDescriptor()!!
val reachableDeclarations = findAllReachableDeclarations(function)
val reachableDeclarations = findAllReachableDeclarations(function, isBodyOwner)
// e.g. `getSize()I`
val methodItself = signatureByDescriptor(function)
@@ -79,8 +81,8 @@ object BuiltinSpecialBridgesUtil {
if (fake) {
for (overridden in function.overriddenDescriptors.map { it.original }) {
if (!DescriptorBasedFunctionHandle(overridden).isAbstract) {
commonBridges.removeAll(findAllReachableDeclarations(overridden).map(signatureByDescriptor))
if (!DescriptorBasedFunctionHandle(overridden, isBodyOwner).isAbstract) {
commonBridges.removeAll(findAllReachableDeclarations(overridden, isBodyOwner).map(signatureByDescriptor))
}
}
}
@@ -89,7 +91,7 @@ object BuiltinSpecialBridgesUtil {
// Can be null if special builtin is final (e.g. 'name' in Enum)
// because there should be no stubs for override in subclasses
val superImplementationDescriptor = findSuperImplementationForStubDelegation(function, fake)
val superImplementationDescriptor = findSuperImplementationForStubDelegation(function, fake, isBodyOwner)
if (superImplementationDescriptor != null) {
bridges.add(BridgeForBuiltinSpecial(methodItself, signatureByDescriptor(superImplementationDescriptor), isDelegateToSuper = true))
}
@@ -121,16 +123,23 @@ object BuiltinSpecialBridgesUtil {
}
private fun findSuperImplementationForStubDelegation(function: FunctionDescriptor, fake: Boolean): FunctionDescriptor? {
private fun findSuperImplementationForStubDelegation(
function: FunctionDescriptor,
fake: Boolean,
isBodyOwner: (DeclarationDescriptor) -> Boolean
): FunctionDescriptor? {
if (function.modality != Modality.OPEN || !fake) return null
val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function)).descriptor
val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, isBodyOwner)).descriptor
if (DescriptorUtils.isInterface(implementation.containingDeclaration)) return null
return implementation
}
private fun findAllReachableDeclarations(functionDescriptor: FunctionDescriptor): MutableSet<FunctionDescriptor> =
findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor)).map { it.descriptor }.toMutableSet()
private fun findAllReachableDeclarations(
functionDescriptor: FunctionDescriptor,
isBodyOwner: (DeclarationDescriptor) -> Boolean
): MutableSet<FunctionDescriptor> =
findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor, isBodyOwner)).map { it.descriptor }.toMutableSet()
private fun <Signature> CallableMemberDescriptor.getSpecialBridgeSignatureIfExists(
signatureByDescriptor: (FunctionDescriptor) -> Signature
+41
View File
@@ -0,0 +1,41 @@
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
interface B<T, U> {
fun foo(t: T, u: U) = "B"
}
interface Z1 : A<String>, B<String, Int> {
override fun foo(t: String, u: Int) = "Z1"
}
interface Z2 : B<String, Int>, A<String> {
override fun foo(t: String, u: Int) = "Z2"
}
class Z1C : Z1 {
}
class Z2C : Z2 {
}
fun box(): String {
val z1 = Z1C()
val z2 = Z2C()
val z1a: A<String> = z1
val z1b: B<String, Int> = z1
val z2a: A<String> = z2
val z2b: B<String, Int> = z2
return when {
z1.foo("", 0) != "Z1" -> "Fail #1"
z1a.foo("", 0) != "Z1" -> "Fail #2"
z1b.foo("", 0) != "Z1" -> "Fail #3" //FAIL
z2.foo("", 0) != "Z2" -> "Fail #4"
z2a.foo("", 0) != "Z2" -> "Fail #5"
z2b.foo("", 0) != "Z2" -> "Fail #6"
else -> "OK"
}
}
@@ -0,0 +1,40 @@
interface A<T> {
fun foo(t: T, u: Int) = "A"
}
interface B<T, U> {
fun foo(t: T, u: U) = "B"
}
interface Z1 : A<String>, B<String, Int> {
override fun foo(t: String, u: Int) = "Z1"
}
interface Z2 : B<String, Int>, A<String> {
override fun foo(t: String, u: Int) = "Z2"
}
class Z1Class : Z1 {
}
class Z2Class : Z2 {
}
fun box(): String {
val z1 = Z1Class()
val z2 = Z2Class()
val z1a: A<String> = z1
val z1b: B<String, Int> = z1
val z2a: A<String> = z2
val z2b: B<String, Int> = z2
return when {
z1.foo("", 0) != "Z1" -> "Fail #1"
z1a.foo("", 0) != "Z1" -> "Fail #2"
z1b.foo("", 0) != "Z1" -> "Fail #3" //FAIL
z2.foo("", 0) != "Z2" -> "Fail #4"
z2a.foo("", 0) != "Z2" -> "Fail #5"
z2b.foo("", 0) != "Z2" -> "Fail #6"
else -> "OK"
}
}
@@ -0,0 +1,21 @@
// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET
interface Test<T> {
fun test(p: T): T {
return p
}
}
class TestClass : Test<String> {
override fun test(p: String): String {
return p + "K"
}
}
fun <T> execute(t: Test<T>, p: T): T {
return t.test(p)
}
fun box(): String {
return execute(TestClass(), "O")
}
@@ -0,0 +1,24 @@
// KOTLIN_CONFIGURATION_FLAGS: +JVM.JVM_8_TARGET
interface Test<T> {
fun test(p: T): T {
return p
}
}
interface Test2: Test<String> {
override fun test(p: String): String {
return p + "K"
}
}
class TestClass : Test2 {
}
fun <T> execute(t: Test<T>, p: T): T {
return t.test(p)
}
fun box(): String {
return execute(TestClass(), "O")
}
@@ -127,6 +127,18 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("bridgeInClass.kt")
public void testBridgeInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/bridgeInClass.kt");
doTest(fileName);
}
@TestMetadata("bridgeInInterface.kt")
public void testBridgeInInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/bridgeInInterface.kt");
doTest(fileName);
}
@TestMetadata("simpleCall.kt")
public void testSimpleCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/simpleCall.kt");
@@ -994,6 +994,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("kt12416.kt")
public void testKt12416() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/kt12416.kt");
doTest(fileName);
}
@TestMetadata("kt1939.kt")
public void testKt1939() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/kt1939.kt");
@@ -1162,6 +1168,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("twoParentsWithDifferentMethodsTwoBridges2.kt")
public void testTwoParentsWithDifferentMethodsTwoBridges2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt");
doTest(fileName);
}
@TestMetadata("twoParentsWithTheSameMethodOneBridge.kt")
public void testTwoParentsWithTheSameMethodOneBridge() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt");
@@ -125,6 +125,12 @@ public class BridgeTestGenerated extends AbstractBridgeTest {
doTest(fileName);
}
@TestMetadata("kt12416.kt")
public void testKt12416() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/kt12416.kt");
doTest(fileName);
}
@TestMetadata("kt1939.kt")
public void testKt1939() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/kt1939.kt");
@@ -287,6 +293,12 @@ public class BridgeTestGenerated extends AbstractBridgeTest {
doTest(fileName);
}
@TestMetadata("twoParentsWithDifferentMethodsTwoBridges2.kt")
public void testTwoParentsWithDifferentMethodsTwoBridges2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/twoParentsWithDifferentMethodsTwoBridges2.kt");
doTest(fileName);
}
@TestMetadata("twoParentsWithTheSameMethodOneBridge.kt")
public void testTwoParentsWithTheSameMethodOneBridge() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/twoParentsWithTheSameMethodOneBridge.kt");
@@ -77,6 +77,8 @@ class ClassTranslator private constructor(
private fun isTrait(): Boolean = descriptor.kind == ClassKind.INTERFACE
private fun isAnnotation(): Boolean = descriptor.kind == ClassKind.ANNOTATION_CLASS
private fun generateClassCreateInvocationArguments() {
val properties = SmartList<JsPropertyInitializer>()
val staticProperties = SmartList<JsPropertyInitializer>()
@@ -369,7 +371,7 @@ class ClassTranslator private constructor(
}
private fun generatedBridgeMethods(properties: MutableList<JsPropertyInitializer>) {
if (isTrait()) return
if (isAnnotation()) return
generateBridgesToTraitImpl(properties)
@@ -387,7 +389,10 @@ class ClassTranslator private constructor(
private fun generateOtherBridges(properties: MutableList<JsPropertyInitializer>) {
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
if (memberDescriptor is FunctionDescriptor) {
val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity())
val bridgesToGenerate = generateBridgesForFunctionDescriptor(memberDescriptor, identity()) {
//There is no DefaultImpls in js backend so if method non-abstract it should be recognized as non-abstract on bridges calculation
false
}
for (bridge in bridgesToGenerate) {
generateBridge(bridge, properties)