diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt index cae1d6581fb..bbe3f5e03ca 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/bridges/impl.kt @@ -17,20 +17,18 @@ 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.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized fun generateBridgesForFunctionDescriptor( - descriptor: FunctionDescriptor, - signature: (FunctionDescriptor) -> Signature, - isBodyOwner: (DeclarationDescriptor) -> Boolean + descriptor: FunctionDescriptor, + signature: (FunctionDescriptor) -> Signature, + areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean ): Set> { - return generateBridges(DescriptorBasedFunctionHandle(descriptor, isBodyOwner), { signature(it.descriptor) }) + return generateBridges(DescriptorBasedFunctionHandle(descriptor, areDeclarationAndDefinitionSame), { signature(it.descriptor) }) } /** @@ -51,26 +49,28 @@ fun generateBridgesForFunctionDescriptor( * eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated. */ 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 + 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 @JvmDefault JVM members they are placed in interface classes and + we need generate bridge for such function ('isAbstract' will return false). + For non-@JvmDefault function, its 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. + */ + areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean ) : FunctionHandle { - private val overridden = descriptor.overriddenDescriptors.map { DescriptorBasedFunctionHandle(it.original, isBodyOwner) } + private val overridden = descriptor.overriddenDescriptors.map { + DescriptorBasedFunctionHandle( + it.original, + areDeclarationAndDefinitionSame + ) + } - override val isDeclaration: Boolean = - descriptor.kind.isReal || - findInterfaceImplementation(descriptor) != null + override val isDeclaration: Boolean = descriptor.kind.isReal || findInterfaceImplementation(descriptor) != null override val isAbstract: Boolean = - descriptor.modality == Modality.ABSTRACT || - isBodyOwner(descriptor.containingDeclaration) + descriptor.modality == Modality.ABSTRACT || !areDeclarationAndDefinitionSame(descriptor) override val isInterfaceDeclaration: Boolean get() = DescriptorUtils.isInterface(descriptor.containingDeclaration) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index d9f7ebb3ba1..caeeb8fe093 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; +import org.jetbrains.kotlin.config.JvmTarget; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.Annotated; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; @@ -74,6 +75,7 @@ import static org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt.isEf import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE; +import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmDefaultAnnotation; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; @@ -85,10 +87,12 @@ public class FunctionCodegen { private final ClassBuilder v; private final MemberCodegen memberCodegen; - private final Function1 IS_PURE_INTERFACE_CHECKER = new Function1() { + private final Function1 DECLARATION_AND_DEFINITION_CHECKER = new Function1() { @Override - public Boolean invoke(DeclarationDescriptor descriptor) { - return JvmCodegenUtil.isInterfaceWithoutDefaults(descriptor, state); + public Boolean invoke(CallableMemberDescriptor descriptor) { + return !isInterface(descriptor.getContainingDeclaration()) || + (state.getTarget() != JvmTarget.JVM_1_6 && + hasJvmDefaultAnnotation(descriptor)); } }; @@ -938,14 +942,14 @@ public class FunctionCodegen { private boolean hasSpecialBridgeMethod(@NotNull FunctionDescriptor descriptor) { if (SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) == null) return false; return !BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( - descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER + descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER ).isEmpty(); } public void generateBridges(@NotNull FunctionDescriptor descriptor) { if (descriptor instanceof ConstructorDescriptor) return; if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) return; - if (IS_PURE_INTERFACE_CHECKER.invoke(descriptor.getContainingDeclaration())) return; + if (!DECLARATION_AND_DEFINITION_CHECKER.invoke(descriptor)) return; // equals(Any?), hashCode(), toString() never need bridges if (isMethodOfAny(descriptor)) return; @@ -955,7 +959,7 @@ public class FunctionCodegen { Set> bridgesToGenerate; if (!isSpecial) { bridgesToGenerate = - ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER); + ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER); if (!bridgesToGenerate.isEmpty()) { PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; boolean isSpecialBridge = @@ -968,7 +972,7 @@ public class FunctionCodegen { } else { Set> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( - descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER + descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER ); if (!specials.isEmpty()) { @@ -1310,7 +1314,7 @@ public class FunctionCodegen { iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false); } else { - if (CodegenUtilKt.hasJvmDefaultAnnotation(descriptor)) { + if (hasJvmDefaultAnnotation(descriptor)) { iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor()); } else { @@ -1478,7 +1482,7 @@ public class FunctionCodegen { assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " + containingDeclaration; - if (CodegenUtilKt.hasJvmDefaultAnnotation(memberDescriptor)) { + if (hasJvmDefaultAnnotation(memberDescriptor)) { return kind != OwnerKind.DEFAULT_IMPLS; } else { switch (kind) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index d44b2236afc..87578781c2b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -1404,7 +1404,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } private void generateTraitMethods() { - if (isInterfaceWithoutDefaults(descriptor, state)) return; + if (isInterface(descriptor)) return; for (Map.Entry entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) { FunctionDescriptor interfaceFun = entry.getKey(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index 8893f354c2b..df3a7f42e89 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -53,30 +53,6 @@ public class JvmCodegenUtil { private JvmCodegenUtil() { } - public static boolean isInterfaceWithoutDefaults(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) { - return isInterfaceWithoutDefaults(descriptor, state.isJvm8Target()); - } - - private static boolean isInterfaceWithoutDefaults( - @NotNull DeclarationDescriptor descriptor, - boolean isJvm8PlusTarget - ) { - if (!DescriptorUtils.isInterface(descriptor)) return false; - - if (descriptor instanceof DeserializedClassDescriptor) { - SourceElement source = ((DeserializedClassDescriptor) descriptor).getSource(); - if (source instanceof KotlinJvmBinarySourceElement) { - KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass(); - assert binaryClass instanceof FileBasedKotlinClass : - "KotlinJvmBinaryClass should be subclass of FileBasedKotlinClass, but " + binaryClass; - return ((FileBasedKotlinClass) binaryClass).getClassVersion() == Opcodes.V1_6; - } - } - //we can't determine is interface have default methods or not - //we need inspect all methods for jvm target 1.8+ - return !isJvm8PlusTarget; - } - public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor) { if (!isJvmInterface(descriptor.getContainingDeclaration())) { return false; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt index 20b2744c4ad..5aa3818e829 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/builtinSpecialBridges.kt @@ -20,7 +20,6 @@ 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 @@ -45,16 +44,16 @@ class BridgeForBuiltinSpecial( object BuiltinSpecialBridgesUtil { @JvmStatic fun generateBridgesForBuiltinSpecial( - function: FunctionDescriptor, - signatureByDescriptor: (FunctionDescriptor) -> Signature, - isBodyOwner: (DeclarationDescriptor) -> Boolean + function: FunctionDescriptor, + signatureByDescriptor: (FunctionDescriptor) -> Signature, + areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean ): Set> { - val functionHandle = DescriptorBasedFunctionHandle(function, isBodyOwner) + val functionHandle = DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame) val fake = !functionHandle.isDeclaration val overriddenBuiltin = function.getOverriddenBuiltinReflectingJvmDescriptor()!! - val reachableDeclarations = findAllReachableDeclarations(function, isBodyOwner) + val reachableDeclarations = findAllReachableDeclarations(function, areDeclarationAndDefinitionSame) // e.g. `getSize()I` val methodItself = signatureByDescriptor(function) @@ -78,8 +77,10 @@ object BuiltinSpecialBridgesUtil { if (fake) { for (overridden in function.overriddenDescriptors.map { it.original }) { - if (!DescriptorBasedFunctionHandle(overridden, isBodyOwner).isAbstract) { - commonBridges.removeAll(findAllReachableDeclarations(overridden, isBodyOwner).map(signatureByDescriptor)) + if (!DescriptorBasedFunctionHandle(overridden, areDeclarationAndDefinitionSame).isAbstract) { + commonBridges.removeAll(findAllReachableDeclarations(overridden, + areDeclarationAndDefinitionSame + ).map(signatureByDescriptor)) } } } @@ -88,7 +89,7 @@ object BuiltinSpecialBridgesUtil { val superImplementationDescriptor = if (specialBridge != null && fake && !functionHandle.isAbstract) - findSuperImplementationForStubDelegation(function, isBodyOwner, signatureByDescriptor) + findSuperImplementationForStubDelegation(function, areDeclarationAndDefinitionSame, signatureByDescriptor) else null @@ -137,11 +138,11 @@ object BuiltinSpecialBridgesUtil { * Also note that there is no special bridges for final declarations, thus no stubs either */ private fun findSuperImplementationForStubDelegation( - function: FunctionDescriptor, - isBodyOwner: (DeclarationDescriptor) -> Boolean, - signatureByDescriptor: (FunctionDescriptor) -> Signature + function: FunctionDescriptor, + areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean, + signatureByDescriptor: (FunctionDescriptor) -> Signature ): FunctionDescriptor? { - val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, isBodyOwner)).descriptor + val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame)).descriptor // Implementation from interface will be generated by common mechanism if (DescriptorUtils.isInterface(implementation.containingDeclaration)) return null @@ -157,10 +158,15 @@ private fun findSuperImplementationForStubDelegation( } private fun findAllReachableDeclarations( - functionDescriptor: FunctionDescriptor, - isBodyOwner: (DeclarationDescriptor) -> Boolean + functionDescriptor: FunctionDescriptor, + areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean ): MutableSet = - findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor, isBodyOwner)).map { it.descriptor }.toMutableSet() + findAllReachableDeclarations( + DescriptorBasedFunctionHandle( + functionDescriptor, + areDeclarationAndDefinitionSame + ) + ).map { it.descriptor }.toMutableSet() private fun CallableMemberDescriptor.getSpecialBridgeSignatureIfExists( signatureByDescriptor: (FunctionDescriptor) -> Signature diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index f9b0ca5e4c7..effdc65de3d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt @@ -34,11 +34,11 @@ import org.jetbrains.kotlin.codegen.AsmUtil.isAbstractMethod import org.jetbrains.kotlin.codegen.BuiltinSpecialBridgesUtil import org.jetbrains.kotlin.codegen.FunctionCodegen.isMethodOfAny import org.jetbrains.kotlin.codegen.FunctionCodegen.isThereOverriddenInKotlinClass -import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.codegen.JvmCodegenUtil.getDirectMember import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.descriptors.FileClassDescriptor import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION import org.jetbrains.kotlin.descriptors.annotations.Annotations @@ -60,7 +60,9 @@ import org.jetbrains.kotlin.load.java.getOverriddenBuiltinReflectingJvmDescripto import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE +import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes.* @@ -73,12 +75,12 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { private val typeMapper = state.typeMapper - private val IS_PURE_INTERFACE_CHECKER = fun(descriptor: DeclarationDescriptor): Boolean = - JvmCodegenUtil.isInterfaceWithoutDefaults(descriptor, state) + private val DECLARATION_AND_DEFINITION_CHECKER = fun(descriptor: CallableMemberDescriptor): Boolean = + !isInterface(descriptor.containingDeclaration) || state.target !== JvmTarget.JVM_1_6 && descriptor.hasJvmDefaultAnnotation() override fun lower(irClass: IrClass) { val classDescriptor = irClass.descriptor - if (IS_PURE_INTERFACE_CHECKER(classDescriptor) || classDescriptor is FileClassDescriptor) return + if (classDescriptor is FileClassDescriptor) return if (classDescriptor is DefaultImplsClassDescriptor) { return /*TODO?*/ @@ -133,7 +135,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { bridgesToGenerate = generateBridgesForFunctionDescriptor( descriptor, getSignatureMapper(typeMapper), - IS_PURE_INTERFACE_CHECKER + DECLARATION_AND_DEFINITION_CHECKER ) if (!bridgesToGenerate.isEmpty()) { val origin = if (descriptor.kind == DECLARATION) getSourceFromDescriptor(descriptor) else null @@ -148,7 +150,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass { val specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( descriptor, getSignatureMapper(typeMapper), - IS_PURE_INTERFACE_CHECKER + DECLARATION_AND_DEFINITION_CHECKER ) if (!specials.isEmpty()) { diff --git a/compiler/testData/codegen/box/bridges/bridgeInInterface.kt b/compiler/testData/codegen/box/bridges/bridgeInInterface.kt new file mode 100644 index 00000000000..2e5be92be41 --- /dev/null +++ b/compiler/testData/codegen/box/bridges/bridgeInInterface.kt @@ -0,0 +1,63 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FULL_JDK + +interface Test { + fun test(p: T): T { + return null!! + } + + fun foo(p: T): T { + return null!! + } +} + +interface Test2: Test { + override fun test(p: String): String { + return p + } + + override fun foo(p: String): String { + return p + } +} + +class TestClass : Test2 + +fun box(): String { + checkNoMethod(Test2::class.java, "foo", Any::class.java) + checkNoMethod(Test2::class.java, "test", Any::class.java) + + checkMethodExists(TestClass::class.java, "test", Any::class.java) + checkMethodExists(TestClass::class.java, "test", String::class.java) + checkMethodExists(TestClass::class.java, "foo", Any::class.java) + checkMethodExists(TestClass::class.java, "foo", String::class.java) + + + val test2DefaultImpls = java.lang.Class.forName("Test2\$DefaultImpls") + checkMethodExists(test2DefaultImpls, "test", Test2::class.java, String::class.java) + checkMethodExists(test2DefaultImpls, "foo", Test2::class.java, String::class.java) + + return "OK" +} + +fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + } + catch (e: NoSuchMethodException) { + return + } + throw AssertionError("fail: method $name was found in " + clazz) +} + +fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return + } + catch (e: NoSuchMethodException) { + throw AssertionError("fail: method $name was not found in " + clazz, e) + } + +} diff --git a/compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt b/compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt new file mode 100644 index 00000000000..ea962d80aa6 --- /dev/null +++ b/compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt @@ -0,0 +1,56 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FULL_JDK + +interface Test { + var T.foo: T + get() = null!! + set(value) { + null!! + } +} + +interface Test2 : Test { + + override var String.foo: String + get() = "" + set(value) {} +} + +class TestClass : Test2 + +fun box(): String { + checkNoMethod(Test2::class.java, "setFoo", Any::class.java, Any::class.java) + + checkMethodExists(TestClass::class.java, "getFoo", String::class.java) + checkMethodExists(TestClass::class.java, "getFoo", Any::class.java) + checkMethodExists(TestClass::class.java, "setFoo", Any::class.java, Any::class.java) + checkMethodExists(TestClass::class.java, "setFoo", String::class.java, String::class.java) + + val test2DefaultImpls = java.lang.Class.forName("Test2\$DefaultImpls") + checkMethodExists(test2DefaultImpls, "getFoo", Test2::class.java, String::class.java) + checkMethodExists(test2DefaultImpls, "setFoo", Test2::class.java, String::class.java, String::class.java) + + return "OK" +} + +fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + } + catch (e: NoSuchMethodException) { + return + } + throw AssertionError("fail: method $name was found in " + clazz) +} + +fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return + } + catch (e: NoSuchMethodException) { + throw AssertionError("fail: method $name was not found in " + clazz, e) + } + +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterface2.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterface2.kt new file mode 100644 index 00000000000..4252ed8feea --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterface2.kt @@ -0,0 +1,66 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK + +interface Test { + @kotlin.annotations.JvmDefault + fun test(p: T): T { + return null!! + } + + fun foo(p: T): T { + return null!! + } +} + +interface Test2: Test { + @kotlin.annotations.JvmDefault + override fun test(p: String): String { + return p + } + + override fun foo(p: String): String { + return p + } +} + +class TestClass : Test2 + +fun box(): String { + checkNoMethod(Test2::class.java, "foo", Any::class.java) + checkMethodExists(Test2::class.java, "test", Any::class.java) + + checkNoMethod(TestClass::class.java, "test", Any::class.java) + checkMethodExists(TestClass::class.java, "foo", String::class.java) + checkMethodExists(TestClass::class.java, "foo", Any::class.java) + + + val test2DefaultImpls = java.lang.Class.forName("Test2\$DefaultImpls") + checkNoMethod(test2DefaultImpls, "test", Any::class.java) + checkNoMethod(test2DefaultImpls, "test", String::class.java) + checkMethodExists(test2DefaultImpls, "foo", Test2::class.java, String::class.java) + + return "OK" +} + +fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + } + catch (e: NoSuchMethodException) { + return + } + throw AssertionError("fail: method $name was found in " + clazz) +} + +fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return + } + catch (e: NoSuchMethodException) { + throw AssertionError("fail: method $name was not found in " + clazz, e) + } + +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties.kt new file mode 100644 index 00000000000..07617f42623 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties.kt @@ -0,0 +1,33 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface Test { + @kotlin.annotations.JvmDefault + var test: T + get() = null!! + set(value) { + null!! + } +} +var result = "fail" + +interface Test2 : Test { + @kotlin.annotations.JvmDefault + override var test: String + get() = result + set(value) { + result = value + } +} + +class TestClass : Test2 + +fun execute(t: Test, p: T): T { + t.test = p + return t.test +} + +fun box(): String { + return execute(TestClass(), "OK") +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties2.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties2.kt new file mode 100644 index 00000000000..1542c929417 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties2.kt @@ -0,0 +1,80 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK + +interface Test { + @kotlin.annotations.JvmDefault + var T.test: T + get() = null!! + set(value) { + null!! + } + + var T.foo: T + get() = null!! + set(value) { + null!! + } +} + +interface Test2 : Test { + @kotlin.annotations.JvmDefault + override var String.test: String + get() = "" + set(value) {} + + override var String.foo: String + get() = "" + set(value) {} +} + +class TestClass : Test2 + +fun box(): String { + checkMethodExists(Test2::class.java, "getTest", String::class.java) + checkMethodExists(Test2::class.java, "getTest", Any::class.java) + checkMethodExists(Test2::class.java, "setTest", Any::class.java, Any::class.java) + checkMethodExists(Test2::class.java, "setTest", String::class.java, String::class.java) + + checkNoMethod(Test2::class.java, "setFoo", Any::class.java, Any::class.java) + + checkNoMethod(TestClass::class.java, "getTest", String::class.java) + checkNoMethod(TestClass::class.java, "getTest", Any::class.java) + checkNoMethod(TestClass::class.java, "setTest", Any::class.java, Any::class.java) + checkNoMethod(TestClass::class.java, "setTest", String::class.java, String::class.java) + + checkMethodExists(TestClass::class.java, "getFoo", String::class.java) + checkMethodExists(TestClass::class.java, "getFoo", Any::class.java) + checkMethodExists(TestClass::class.java, "setFoo", Any::class.java, Any::class.java) + checkMethodExists(TestClass::class.java, "setFoo", String::class.java, String::class.java) + + val test2DefaultImpls = java.lang.Class.forName("Test2\$DefaultImpls") + checkNoMethod(test2DefaultImpls, "getTest", String::class.java) + checkNoMethod(test2DefaultImpls, "getTest", Any::class.java) + checkNoMethod(test2DefaultImpls, "setTest", Any::class.java, Any::class.java) + checkNoMethod(test2DefaultImpls, "setTest", String::class.java, String::class.java) + + return "OK" +} + +fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + } + catch (e: NoSuchMethodException) { + return + } + throw AssertionError("fail: method $name was found in " + clazz) +} + +fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return + } + catch (e: NoSuchMethodException) { + throw AssertionError("fail: method $name was not found in " + clazz, e) + } + +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeWithJava.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeWithJava.kt new file mode 100644 index 00000000000..8a9fa88431c --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/bridgeWithJava.kt @@ -0,0 +1,58 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK +// FILE: Test.java +public interface Test { + default T test(T p) { + return null; + } +} + +// FILE: kotlin.kt + +interface Test2: Test { + @kotlin.annotations.JvmDefault + override fun test(p: String): String { + return p + } + + fun forDefaultImpls() {} +} + +class TestClass : Test2 + +fun box(): String { + checkMethodExists(Test2::class.java, "test", String::class.java) + checkMethodExists(Test2::class.java, "test", Any::class.java) + + checkNoMethod(TestClass::class.java, "test", String::class.java) + checkNoMethod(TestClass::class.java, "test", Any::class.java) + + val test2DefaultImpls = java.lang.Class.forName("Test2\$DefaultImpls") + checkNoMethod(test2DefaultImpls, "test", Any::class.java) + checkNoMethod(test2DefaultImpls, "test", String::class.java) + + return "OK" +} + +fun checkNoMethod(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + } + catch (e: NoSuchMethodException) { + return + } + throw AssertionError("fail: method $name was found in " + clazz) +} + +fun checkMethodExists(clazz: Class<*>, name: String, vararg parameterTypes: Class<*>) { + try { + clazz.getDeclaredMethod(name, *parameterTypes) + return + } + catch (e: NoSuchMethodException) { + throw AssertionError("fail: method $name was not found in " + clazz, e) + } + +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/diamond.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/diamond.kt index 4faedef46a4..6239907ba77 100644 --- a/compiler/testData/codegen/java8/box/jvm8/defaults/diamond.kt +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/diamond.kt @@ -43,7 +43,7 @@ fun box(): String { fun checkNoMethod(clazz: Class<*>, name: String) { try { - clazz.getDeclaredMethod("test") + clazz.getDeclaredMethod(name) } catch (e: NoSuchMethodException) { return diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ddf2c0372d6..3b7c2b08e73 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -1127,6 +1127,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("bridgeInInterface.kt") + public void testBridgeInInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterface.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeInInterfaceWithProperties.kt") + public void testBridgeInInterfaceWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt"); + doTest(fileName); + } + @TestMetadata("complexMultiInheritance.kt") public void testComplexMultiInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java index 81da6c5bcc8..63e4009d5d9 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java @@ -371,6 +371,30 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg doTest(fileName); } + @TestMetadata("bridgeInInterface2.kt") + public void testBridgeInInterface2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterface2.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeInInterfaceWithProperties.kt") + public void testBridgeInInterfaceWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeInInterfaceWithProperties2.kt") + public void testBridgeInInterfaceWithProperties2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/bridgeInInterfaceWithProperties2.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeWithJava.kt") + public void testBridgeWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/bridgeWithJava.kt"); + doTest(fileName); + } + @TestMetadata("callableReference.kt") public void testCallableReference() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/callableReference.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 72c4443df30..74355942c65 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -1127,6 +1127,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("bridgeInInterface.kt") + public void testBridgeInInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterface.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeInInterfaceWithProperties.kt") + public void testBridgeInInterfaceWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt"); + doTest(fileName); + } + @TestMetadata("complexMultiInheritance.kt") public void testComplexMultiInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index d2fa8ae7cf1..e1347f0ac49 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -1127,6 +1127,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("bridgeInInterface.kt") + public void testBridgeInInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterface.kt"); + doTest(fileName); + } + + @TestMetadata("bridgeInInterfaceWithProperties.kt") + public void testBridgeInInterfaceWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt"); + doTest(fileName); + } + @TestMetadata("complexMultiInheritance.kt") public void testComplexMultiInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index aea8d2c98fa..2d3737b8cef 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -1471,6 +1471,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/bridges"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("bridgeInInterfaceWithProperties.kt") + public void testBridgeInInterfaceWithProperties() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/bridgeInInterfaceWithProperties.kt"); + doTest(fileName); + } + @TestMetadata("complexMultiInheritance.kt") public void testComplexMultiInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt index 0342be4943a..cba6ccfb9fe 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt @@ -245,7 +245,7 @@ class ClassModelGenerator(val context: TranslationContext) { if (memberDescriptor is FunctionDescriptor) { 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 + true } for (bridge in bridgesToGenerate) {