Check method abstractness during bridge generation per method not interface

This commit is contained in:
Mikhael Bogdanov
2018-03-06 12:13:19 +01:00
parent f290b325ee
commit 2a8041e77e
19 changed files with 490 additions and 80 deletions
@@ -17,20 +17,18 @@
package org.jetbrains.kotlin.backend.common.bridges package org.jetbrains.kotlin.backend.common.bridges
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized
fun <Signature> generateBridgesForFunctionDescriptor( fun <Signature> generateBridgesForFunctionDescriptor(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
signature: (FunctionDescriptor) -> Signature, signature: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): Set<Bridge<Signature>> { ): Set<Bridge<Signature>> {
return generateBridges(DescriptorBasedFunctionHandle(descriptor, isBodyOwner), { signature(it.descriptor) }) return generateBridges(DescriptorBasedFunctionHandle(descriptor, areDeclarationAndDefinitionSame), { signature(it.descriptor) })
} }
/** /**
@@ -51,26 +49,28 @@ fun <Signature> generateBridgesForFunctionDescriptor(
* eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated. * eases the process of determining what bridges are already generated in our supertypes and need to be inherited, not regenerated.
*/ */
class DescriptorBasedFunctionHandle( class DescriptorBasedFunctionHandle(
val descriptor: FunctionDescriptor, val descriptor: FunctionDescriptor,
/* /*
To generate proper bridges for non-abstract function 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. 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 JS and @JvmDefault JVM members they are placed in interface classes and
For JVM6 target function body generated in separate place (DefaultImpl) and method in interface is abstract we need generate bridge for such function ('isAbstract' will return false).
For JVM6 target function body is generated in a separate place (DefaultImpls) and 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. the method in the interface is abstract so we must not generate bridges for such cases.
*/ */
isBodyOwner: (DeclarationDescriptor) -> Boolean areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
) : FunctionHandle { ) : 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 = override val isDeclaration: Boolean = descriptor.kind.isReal || findInterfaceImplementation(descriptor) != null
descriptor.kind.isReal ||
findInterfaceImplementation(descriptor) != null
override val isAbstract: Boolean = override val isAbstract: Boolean =
descriptor.modality == Modality.ABSTRACT || descriptor.modality == Modality.ABSTRACT || !areDeclarationAndDefinitionSame(descriptor)
isBodyOwner(descriptor.containingDeclaration)
override val isInterfaceDeclaration: Boolean override val isInterfaceDeclaration: Boolean
get() = DescriptorUtils.isInterface(descriptor.containingDeclaration) get() = DescriptorUtils.isInterface(descriptor.containingDeclaration)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated; import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; 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.DescriptorToSourceUtils.getSourceFromDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE; 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.kotlin.types.expressions.ExpressionTypingUtils.*;
import static org.jetbrains.org.objectweb.asm.Opcodes.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -85,10 +87,12 @@ public class FunctionCodegen {
private final ClassBuilder v; private final ClassBuilder v;
private final MemberCodegen<?> memberCodegen; private final MemberCodegen<?> memberCodegen;
private final Function1<DeclarationDescriptor, Boolean> IS_PURE_INTERFACE_CHECKER = new Function1<DeclarationDescriptor, Boolean>() { private final Function1<CallableMemberDescriptor, Boolean> DECLARATION_AND_DEFINITION_CHECKER = new Function1<CallableMemberDescriptor, Boolean>() {
@Override @Override
public Boolean invoke(DeclarationDescriptor descriptor) { public Boolean invoke(CallableMemberDescriptor descriptor) {
return JvmCodegenUtil.isInterfaceWithoutDefaults(descriptor, state); 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) { private boolean hasSpecialBridgeMethod(@NotNull FunctionDescriptor descriptor) {
if (SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) == null) return false; if (SpecialBuiltinMembers.getOverriddenBuiltinReflectingJvmDescriptor(descriptor) == null) return false;
return !BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( return !BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER
).isEmpty(); ).isEmpty();
} }
public void generateBridges(@NotNull FunctionDescriptor descriptor) { public void generateBridges(@NotNull FunctionDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor) return; if (descriptor instanceof ConstructorDescriptor) return;
if (owner.getContextKind() == OwnerKind.DEFAULT_IMPLS) 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 // equals(Any?), hashCode(), toString() never need bridges
if (isMethodOfAny(descriptor)) return; if (isMethodOfAny(descriptor)) return;
@@ -955,7 +959,7 @@ public class FunctionCodegen {
Set<Bridge<Method>> bridgesToGenerate; Set<Bridge<Method>> bridgesToGenerate;
if (!isSpecial) { if (!isSpecial) {
bridgesToGenerate = bridgesToGenerate =
ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER); ImplKt.generateBridgesForFunctionDescriptor(descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER);
if (!bridgesToGenerate.isEmpty()) { if (!bridgesToGenerate.isEmpty()) {
PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null; PsiElement origin = descriptor.getKind() == DECLARATION ? getSourceFromDescriptor(descriptor) : null;
boolean isSpecialBridge = boolean isSpecialBridge =
@@ -968,7 +972,7 @@ public class FunctionCodegen {
} }
else { else {
Set<BridgeForBuiltinSpecial<Method>> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( Set<BridgeForBuiltinSpecial<Method>> specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor, typeMapper::mapAsmMethod, IS_PURE_INTERFACE_CHECKER descriptor, typeMapper::mapAsmMethod, DECLARATION_AND_DEFINITION_CHECKER
); );
if (!specials.isEmpty()) { if (!specials.isEmpty()) {
@@ -1310,7 +1314,7 @@ public class FunctionCodegen {
iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false); iv.invokespecial(parentInternalName, delegateTo.getName(), delegateTo.getDescriptor(), false);
} }
else { else {
if (CodegenUtilKt.hasJvmDefaultAnnotation(descriptor)) { if (hasJvmDefaultAnnotation(descriptor)) {
iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor()); iv.invokeinterface(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
} }
else { else {
@@ -1478,7 +1482,7 @@ public class FunctionCodegen {
assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " + assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " +
containingDeclaration; containingDeclaration;
if (CodegenUtilKt.hasJvmDefaultAnnotation(memberDescriptor)) { if (hasJvmDefaultAnnotation(memberDescriptor)) {
return kind != OwnerKind.DEFAULT_IMPLS; return kind != OwnerKind.DEFAULT_IMPLS;
} else { } else {
switch (kind) { switch (kind) {
@@ -1404,7 +1404,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
} }
private void generateTraitMethods() { private void generateTraitMethods() {
if (isInterfaceWithoutDefaults(descriptor, state)) return; if (isInterface(descriptor)) return;
for (Map.Entry<FunctionDescriptor, FunctionDescriptor> entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) { for (Map.Entry<FunctionDescriptor, FunctionDescriptor> entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) {
FunctionDescriptor interfaceFun = entry.getKey(); FunctionDescriptor interfaceFun = entry.getKey();
@@ -53,30 +53,6 @@ public class JvmCodegenUtil {
private 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) { public static boolean isNonDefaultInterfaceMember(@NotNull CallableMemberDescriptor descriptor) {
if (!isJvmInterface(descriptor.getContainingDeclaration())) { if (!isJvmInterface(descriptor.getContainingDeclaration())) {
return false; return false;
@@ -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.findAllReachableDeclarations
import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration import org.jetbrains.kotlin.backend.common.bridges.findConcreteSuperDeclaration
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
@@ -45,16 +44,16 @@ class BridgeForBuiltinSpecial<out Signature : Any>(
object BuiltinSpecialBridgesUtil { object BuiltinSpecialBridgesUtil {
@JvmStatic fun <Signature : Any> generateBridgesForBuiltinSpecial( @JvmStatic fun <Signature : Any> generateBridgesForBuiltinSpecial(
function: FunctionDescriptor, function: FunctionDescriptor,
signatureByDescriptor: (FunctionDescriptor) -> Signature, signatureByDescriptor: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): Set<BridgeForBuiltinSpecial<Signature>> { ): Set<BridgeForBuiltinSpecial<Signature>> {
val functionHandle = DescriptorBasedFunctionHandle(function, isBodyOwner) val functionHandle = DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame)
val fake = !functionHandle.isDeclaration val fake = !functionHandle.isDeclaration
val overriddenBuiltin = function.getOverriddenBuiltinReflectingJvmDescriptor()!! val overriddenBuiltin = function.getOverriddenBuiltinReflectingJvmDescriptor()!!
val reachableDeclarations = findAllReachableDeclarations(function, isBodyOwner) val reachableDeclarations = findAllReachableDeclarations(function, areDeclarationAndDefinitionSame)
// e.g. `getSize()I` // e.g. `getSize()I`
val methodItself = signatureByDescriptor(function) val methodItself = signatureByDescriptor(function)
@@ -78,8 +77,10 @@ object BuiltinSpecialBridgesUtil {
if (fake) { if (fake) {
for (overridden in function.overriddenDescriptors.map { it.original }) { for (overridden in function.overriddenDescriptors.map { it.original }) {
if (!DescriptorBasedFunctionHandle(overridden, isBodyOwner).isAbstract) { if (!DescriptorBasedFunctionHandle(overridden, areDeclarationAndDefinitionSame).isAbstract) {
commonBridges.removeAll(findAllReachableDeclarations(overridden, isBodyOwner).map(signatureByDescriptor)) commonBridges.removeAll(findAllReachableDeclarations(overridden,
areDeclarationAndDefinitionSame
).map(signatureByDescriptor))
} }
} }
} }
@@ -88,7 +89,7 @@ object BuiltinSpecialBridgesUtil {
val superImplementationDescriptor = val superImplementationDescriptor =
if (specialBridge != null && fake && !functionHandle.isAbstract) if (specialBridge != null && fake && !functionHandle.isAbstract)
findSuperImplementationForStubDelegation(function, isBodyOwner, signatureByDescriptor) findSuperImplementationForStubDelegation(function, areDeclarationAndDefinitionSame, signatureByDescriptor)
else else
null null
@@ -137,11 +138,11 @@ object BuiltinSpecialBridgesUtil {
* Also note that there is no special bridges for final declarations, thus no stubs either * Also note that there is no special bridges for final declarations, thus no stubs either
*/ */
private fun <Signature> findSuperImplementationForStubDelegation( private fun <Signature> findSuperImplementationForStubDelegation(
function: FunctionDescriptor, function: FunctionDescriptor,
isBodyOwner: (DeclarationDescriptor) -> Boolean, areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean,
signatureByDescriptor: (FunctionDescriptor) -> Signature signatureByDescriptor: (FunctionDescriptor) -> Signature
): FunctionDescriptor? { ): FunctionDescriptor? {
val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, isBodyOwner)).descriptor val implementation = findConcreteSuperDeclaration(DescriptorBasedFunctionHandle(function, areDeclarationAndDefinitionSame)).descriptor
// Implementation from interface will be generated by common mechanism // Implementation from interface will be generated by common mechanism
if (DescriptorUtils.isInterface(implementation.containingDeclaration)) return null if (DescriptorUtils.isInterface(implementation.containingDeclaration)) return null
@@ -157,10 +158,15 @@ private fun <Signature> findSuperImplementationForStubDelegation(
} }
private fun findAllReachableDeclarations( private fun findAllReachableDeclarations(
functionDescriptor: FunctionDescriptor, functionDescriptor: FunctionDescriptor,
isBodyOwner: (DeclarationDescriptor) -> Boolean areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): MutableSet<FunctionDescriptor> = ): MutableSet<FunctionDescriptor> =
findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor, isBodyOwner)).map { it.descriptor }.toMutableSet() findAllReachableDeclarations(
DescriptorBasedFunctionHandle(
functionDescriptor,
areDeclarationAndDefinitionSame
)
).map { it.descriptor }.toMutableSet()
private fun <Signature> CallableMemberDescriptor.getSpecialBridgeSignatureIfExists( private fun <Signature> CallableMemberDescriptor.getSpecialBridgeSignatureIfExists(
signatureByDescriptor: (FunctionDescriptor) -> Signature signatureByDescriptor: (FunctionDescriptor) -> Signature
@@ -34,11 +34,11 @@ import org.jetbrains.kotlin.codegen.AsmUtil.isAbstractMethod
import org.jetbrains.kotlin.codegen.BuiltinSpecialBridgesUtil import org.jetbrains.kotlin.codegen.BuiltinSpecialBridgesUtil
import org.jetbrains.kotlin.codegen.FunctionCodegen.isMethodOfAny import org.jetbrains.kotlin.codegen.FunctionCodegen.isMethodOfAny
import org.jetbrains.kotlin.codegen.FunctionCodegen.isThereOverriddenInKotlinClass 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.JvmCodegenUtil.getDirectMember
import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.descriptors.FileClassDescriptor import org.jetbrains.kotlin.codegen.descriptors.FileClassDescriptor
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION
import org.jetbrains.kotlin.descriptors.annotations.Annotations 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.name.Name
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor
import org.jetbrains.kotlin.resolve.DescriptorUtils 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.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
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 typeMapper = state.typeMapper
private val IS_PURE_INTERFACE_CHECKER = fun(descriptor: DeclarationDescriptor): Boolean = private val DECLARATION_AND_DEFINITION_CHECKER = fun(descriptor: CallableMemberDescriptor): Boolean =
JvmCodegenUtil.isInterfaceWithoutDefaults(descriptor, state) !isInterface(descriptor.containingDeclaration) || state.target !== JvmTarget.JVM_1_6 && descriptor.hasJvmDefaultAnnotation()
override fun lower(irClass: IrClass) { override fun lower(irClass: IrClass) {
val classDescriptor = irClass.descriptor val classDescriptor = irClass.descriptor
if (IS_PURE_INTERFACE_CHECKER(classDescriptor) || classDescriptor is FileClassDescriptor) return if (classDescriptor is FileClassDescriptor) return
if (classDescriptor is DefaultImplsClassDescriptor) { if (classDescriptor is DefaultImplsClassDescriptor) {
return /*TODO?*/ return /*TODO?*/
@@ -133,7 +135,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
bridgesToGenerate = generateBridgesForFunctionDescriptor( bridgesToGenerate = generateBridgesForFunctionDescriptor(
descriptor, descriptor,
getSignatureMapper(typeMapper), getSignatureMapper(typeMapper),
IS_PURE_INTERFACE_CHECKER DECLARATION_AND_DEFINITION_CHECKER
) )
if (!bridgesToGenerate.isEmpty()) { if (!bridgesToGenerate.isEmpty()) {
val origin = if (descriptor.kind == DECLARATION) getSourceFromDescriptor(descriptor) else null val origin = if (descriptor.kind == DECLARATION) getSourceFromDescriptor(descriptor) else null
@@ -148,7 +150,7 @@ class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass {
val specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial( val specials = BuiltinSpecialBridgesUtil.generateBridgesForBuiltinSpecial(
descriptor, descriptor,
getSignatureMapper(typeMapper), getSignatureMapper(typeMapper),
IS_PURE_INTERFACE_CHECKER DECLARATION_AND_DEFINITION_CHECKER
) )
if (!specials.isEmpty()) { if (!specials.isEmpty()) {
@@ -0,0 +1,63 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FULL_JDK
interface Test<T> {
fun test(p: T): T {
return null!!
}
fun foo(p: T): T {
return null!!
}
}
interface Test2: Test<String> {
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)
}
}
@@ -0,0 +1,56 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FULL_JDK
interface Test<T> {
var T.foo: T
get() = null!!
set(value) {
null!!
}
}
interface Test2 : Test<String> {
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)
}
}
@@ -0,0 +1,66 @@
// !API_VERSION: 1.3
// JVM_TARGET: 1.8
// WITH_RUNTIME
// FULL_JDK
interface Test<T> {
@kotlin.annotations.JvmDefault
fun test(p: T): T {
return null!!
}
fun foo(p: T): T {
return null!!
}
}
interface Test2: Test<String> {
@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)
}
}
@@ -0,0 +1,33 @@
// !API_VERSION: 1.3
// JVM_TARGET: 1.8
// WITH_RUNTIME
interface Test<T> {
@kotlin.annotations.JvmDefault
var test: T
get() = null!!
set(value) {
null!!
}
}
var result = "fail"
interface Test2 : Test<String> {
@kotlin.annotations.JvmDefault
override var test: String
get() = result
set(value) {
result = value
}
}
class TestClass : Test2
fun <T> execute(t: Test<T>, p: T): T {
t.test = p
return t.test
}
fun box(): String {
return execute(TestClass(), "OK")
}
@@ -0,0 +1,80 @@
// !API_VERSION: 1.3
// JVM_TARGET: 1.8
// WITH_RUNTIME
// FULL_JDK
interface Test<T> {
@kotlin.annotations.JvmDefault
var T.test: T
get() = null!!
set(value) {
null!!
}
var T.foo: T
get() = null!!
set(value) {
null!!
}
}
interface Test2 : Test<String> {
@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)
}
}
@@ -0,0 +1,58 @@
// !API_VERSION: 1.3
// JVM_TARGET: 1.8
// WITH_RUNTIME
// FULL_JDK
// FILE: Test.java
public interface Test<T> {
default T test(T p) {
return null;
}
}
// FILE: kotlin.kt
interface Test2: Test<String> {
@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)
}
}
@@ -43,7 +43,7 @@ fun box(): String {
fun checkNoMethod(clazz: Class<*>, name: String) { fun checkNoMethod(clazz: Class<*>, name: String) {
try { try {
clazz.getDeclaredMethod("test") clazz.getDeclaredMethod(name)
} }
catch (e: NoSuchMethodException) { catch (e: NoSuchMethodException) {
return return
@@ -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); 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") @TestMetadata("complexMultiInheritance.kt")
public void testComplexMultiInheritance() throws Exception { public void testComplexMultiInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
@@ -371,6 +371,30 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
doTest(fileName); 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") @TestMetadata("callableReference.kt")
public void testCallableReference() throws Exception { public void testCallableReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/callableReference.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/callableReference.kt");
@@ -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); 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") @TestMetadata("complexMultiInheritance.kt")
public void testComplexMultiInheritance() throws Exception { public void testComplexMultiInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
@@ -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); 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") @TestMetadata("complexMultiInheritance.kt")
public void testComplexMultiInheritance() throws Exception { public void testComplexMultiInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
@@ -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); 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") @TestMetadata("complexMultiInheritance.kt")
public void testComplexMultiInheritance() throws Exception { public void testComplexMultiInheritance() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/bridges/complexMultiInheritance.kt");
@@ -245,7 +245,7 @@ class ClassModelGenerator(val context: TranslationContext) {
if (memberDescriptor is FunctionDescriptor) { 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 //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) { for (bridge in bridgesToGenerate) {