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
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 <Signature> generateBridgesForFunctionDescriptor(
descriptor: FunctionDescriptor,
signature: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean
descriptor: FunctionDescriptor,
signature: (FunctionDescriptor) -> Signature,
areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): 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.
*/
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)
@@ -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<DeclarationDescriptor, Boolean> IS_PURE_INTERFACE_CHECKER = new Function1<DeclarationDescriptor, Boolean>() {
private final Function1<CallableMemberDescriptor, Boolean> DECLARATION_AND_DEFINITION_CHECKER = new Function1<CallableMemberDescriptor, Boolean>() {
@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<Bridge<Method>> 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<BridgeForBuiltinSpecial<Method>> 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) {
@@ -1404,7 +1404,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
private void generateTraitMethods() {
if (isInterfaceWithoutDefaults(descriptor, state)) return;
if (isInterface(descriptor)) return;
for (Map.Entry<FunctionDescriptor, FunctionDescriptor> entry : CodegenUtil.getNonPrivateTraitMethods(descriptor).entrySet()) {
FunctionDescriptor interfaceFun = entry.getKey();
@@ -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;
@@ -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<out Signature : Any>(
object BuiltinSpecialBridgesUtil {
@JvmStatic fun <Signature : Any> generateBridgesForBuiltinSpecial(
function: FunctionDescriptor,
signatureByDescriptor: (FunctionDescriptor) -> Signature,
isBodyOwner: (DeclarationDescriptor) -> Boolean
function: FunctionDescriptor,
signatureByDescriptor: (FunctionDescriptor) -> Signature,
areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): Set<BridgeForBuiltinSpecial<Signature>> {
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 <Signature> 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 <Signature> findSuperImplementationForStubDelegation(
}
private fun findAllReachableDeclarations(
functionDescriptor: FunctionDescriptor,
isBodyOwner: (DeclarationDescriptor) -> Boolean
functionDescriptor: FunctionDescriptor,
areDeclarationAndDefinitionSame: (CallableMemberDescriptor) -> Boolean
): MutableSet<FunctionDescriptor> =
findAllReachableDeclarations(DescriptorBasedFunctionHandle(functionDescriptor, isBodyOwner)).map { it.descriptor }.toMutableSet()
findAllReachableDeclarations(
DescriptorBasedFunctionHandle(
functionDescriptor,
areDeclarationAndDefinitionSame
)
).map { it.descriptor }.toMutableSet()
private fun <Signature> CallableMemberDescriptor.getSpecialBridgeSignatureIfExists(
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.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()) {
@@ -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) {
try {
clazz.getDeclaredMethod("test")
clazz.getDeclaredMethod(name)
}
catch (e: NoSuchMethodException) {
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);
}
@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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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) {