diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt index 1c627cf4c82..732849c9393 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallableMethod.kt @@ -30,7 +30,8 @@ class CallableMethod( override val extensionReceiverKotlinType: KotlinType?, override val generateCalleeType: Type?, override val returnKotlinType: KotlinType?, - private val isInterfaceMethod: Boolean = Opcodes.INVOKEINTERFACE == invokeOpcode + private val isInterfaceMethod: Boolean = Opcodes.INVOKEINTERFACE == invokeOpcode, + private val isDefaultMethodInInterface: Boolean = false ) : Callable { fun getValueParameters(): List = signature.valueParameters @@ -67,7 +68,7 @@ class CallableMethod( } else { v.visitMethodInsn( INVOKESTATIC, defaultImplOwner.internalName, - method.name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, false + method.name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, defaultMethodDesc, isDefaultMethodInInterface ) StackValue.coerce(Type.getReturnType(defaultMethodDesc), Type.getReturnType(signature.asmMethod.descriptor), v) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 4f527c3122a..fe8def89b3d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -400,9 +400,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateDelegates(delegationFieldsInfo); - if (!isInterface(descriptor) || kind == OwnerKind.DEFAULT_IMPLS) { - generateSyntheticAccessors(); - } + generateSyntheticAccessors(); generateEnumMethods(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 9ce044718f9..602cce52a45 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -695,9 +695,16 @@ public abstract class MemberCodegen accessor : ((CodegenContext) context).getAccessors()) { - generateSyntheticAccessor(accessor); + boolean hasJvmDefaultAnnotation = CodegenUtilKt.hasJvmDefaultAnnotation(accessor.getCalleeDescriptor()); + OwnerKind kind = context.getContextKind(); + + if (!isInterface(context.getContextDescriptor()) || + (hasJvmDefaultAnnotation && kind == OwnerKind.IMPLEMENTATION) || + (!hasJvmDefaultAnnotation && kind == OwnerKind.DEFAULT_IMPLS)) { + generateSyntheticAccessor(accessor); + } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 0a799d56fac..02d6aa7a2aa 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -423,4 +423,8 @@ fun MethodNode.textifyMethodNode(): String { return "$sw" } -fun CallableMemberDescriptor.hasJvmDefaultAnnotation() = getDirectMember(this).annotations.hasAnnotation(JvmDefaultChecker.JVM_DEFAULT_FQ_NAME) +fun CallableMemberDescriptor.hasJvmDefaultAnnotation() = + getDirectMember(this).annotations.hasAnnotation(JvmDefaultChecker.JVM_DEFAULT_FQ_NAME) + +fun DeclarationDescriptor.isCallableMemberWithJvmDefaultAnnotation() = + (this as? CallableMemberDescriptor)?.hasJvmDefaultAnnotation() ?: false diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java index 133ecdd4cc9..d225406713f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java @@ -569,10 +569,13 @@ public abstract class CodegenContext { CodegenContext properContext = getFirstCrossInlineOrNonInlineContext(); DeclarationDescriptor enclosing = descriptor.getContainingDeclaration(); boolean isInliningContext = properContext.isInlineMethodContext(); + boolean sameJvmDefault = CodegenUtilKt.hasJvmDefaultAnnotation(descriptor) == + CodegenUtilKt.isCallableMemberWithJvmDefaultAnnotation(properContext.contextDescriptor) || + properContext.contextDescriptor instanceof AccessorForCallableDescriptor; if (!isInliningContext && ( !properContext.hasThisDescriptor() || - enclosing == properContext.getThisDescriptor() || - enclosing == properContext.getClassOrPackageParentContext().getContextDescriptor())) { + ((enclosing == properContext.getThisDescriptor()) && sameJvmDefault) || + ((enclosing == properContext.getClassOrPackageParentContext().getContextDescriptor()) && sameJvmDefault))) { return descriptor; } return (D) properContext.accessibleDescriptorIfNeeded(descriptor, superCallTarget, isInliningContext); @@ -623,6 +626,11 @@ public abstract class CodegenContext { if (descriptorContext == null) { return descriptor; } + + if (CodegenUtilKt.hasJvmDefaultAnnotation(descriptor) && descriptorContext instanceof DefaultImplsClassContext) { + descriptorContext = ((DefaultImplsClassContext) descriptorContext).getInterfaceContext(); + } + if (descriptor instanceof PropertyDescriptor) { PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; int propertyAccessFlag = getVisibilityAccessFlag(descriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/DefaultImplsClassContext.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/DefaultImplsClassContext.kt index 69cc02e57ef..2a2ff2476a6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/DefaultImplsClassContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/DefaultImplsClassContext.kt @@ -23,20 +23,21 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor class DefaultImplsClassContext( - typeMapper: KotlinTypeMapper, - contextDescriptor: ClassDescriptor, - contextKind: OwnerKind, - parentContext: CodegenContext<*>?, - localLookup: ((DeclarationDescriptor) -> Boolean)?, - private val interfaceContext: ClassContext + typeMapper: KotlinTypeMapper, + contextDescriptor: ClassDescriptor, + contextKind: OwnerKind, + parentContext: CodegenContext<*>?, + localLookup: ((DeclarationDescriptor) -> Boolean)?, + val interfaceContext: ClassContext ) : ClassContext(typeMapper, contextDescriptor, contextKind, parentContext, localLookup) { override fun getCompanionObjectContext(): CodegenContext<*>? = interfaceContext.companionObjectContext override fun getAccessors(): Collection> { val accessors = super.getAccessors() - val alreadyExistKeys = accessors.map ({ Pair(it.calleeDescriptor, it.superCallTarget) }) - val filtered = interfaceContext.accessors.associateByTo(linkedMapOf()) { Pair(it.calleeDescriptor, it.superCallTarget) }.apply { keys -= alreadyExistKeys } + val alreadyExistKeys = accessors.map({ Pair(it.calleeDescriptor, it.superCallTarget) }) + val filtered = interfaceContext.accessors.associateByTo(linkedMapOf()) { Pair(it.calleeDescriptor, it.superCallTarget) } + .apply { keys -= alreadyExistKeys } return accessors + filtered.values } } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 587173832bb..19a0cff1f98 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -746,7 +746,7 @@ public class KotlinTypeMapper { String defaultImplDesc = mapDefaultMethod(originalDescriptor, OwnerKind.IMPLEMENTATION).getDescriptor(); return new CallableMethod( owner, owner, defaultImplDesc, method, INVOKESPECIAL, - null, null, null, null, null, originalDescriptor.getReturnType(), false + null, null, null, null, null, originalDescriptor.getReturnType(), false, false ); } @@ -770,6 +770,7 @@ public class KotlinTypeMapper { Type thisClass; KotlinType dispatchReceiverKotlinType; boolean isInterfaceMember = false; + boolean isDefaultMethodInInterface = false; if (functionParent instanceof ClassDescriptor) { FunctionDescriptor declarationFunctionDescriptor = findAnyDeclaration(functionDescriptor); @@ -784,6 +785,7 @@ public class KotlinTypeMapper { baseMethodDescriptor = findBaseDeclaration(functionDescriptor).getOriginal(); ClassDescriptor ownerForDefault = (ClassDescriptor) baseMethodDescriptor.getContainingDeclaration(); + isDefaultMethodInInterface = isJvmInterface(ownerForDefault) && CodegenUtilKt.hasJvmDefaultAnnotation(baseMethodDescriptor); ownerForDefaultImpl = isJvmInterface(ownerForDefault) && !CodegenUtilKt.hasJvmDefaultAnnotation(baseMethodDescriptor) ? mapDefaultImpls(ownerForDefault) : mapClass(ownerForDefault); @@ -803,7 +805,14 @@ public class KotlinTypeMapper { FunctionDescriptor originalDescriptor = descriptor.getOriginal(); signature = mapSignatureSkipGeneric(originalDescriptor, OwnerKind.DEFAULT_IMPLS); returnKotlinType = originalDescriptor.getReturnType(); - owner = mapDefaultImpls(currentOwner); + if (descriptor instanceof AccessorForCallableDescriptor && + CodegenUtilKt.hasJvmDefaultAnnotation(((AccessorForCallableDescriptor) descriptor).getCalleeDescriptor())) { + owner = mapClass(currentOwner); + isInterfaceMember = true; + } + else { + owner = mapDefaultImpls(currentOwner); + } } } else { @@ -893,7 +902,7 @@ public class KotlinTypeMapper { return new CallableMethod( owner, ownerForDefaultImpl, defaultImplDesc, signature, invokeOpcode, thisClass, dispatchReceiverKotlinType, receiverParameterType, extensionReceiverKotlinType, calleeType, returnKotlinType, - isJvm8Target ? isInterfaceMember : invokeOpcode == INVOKEINTERFACE + isJvm8Target ? isInterfaceMember : invokeOpcode == INVOKEINTERFACE, isDefaultMethodInInterface ); } diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/accessor.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/accessor.kt new file mode 100644 index 00000000000..da788cb8bb9 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/accessor.kt @@ -0,0 +1,36 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +var storage = "fail" + +interface Test { + + @kotlin.annotations.JvmDefault + private var foo: String + get() = storage + set(value) { + storage = value + } + + @kotlin.annotations.JvmDefault + private fun bar(): String { + return "K" + } + + @kotlin.annotations.JvmDefault + fun call(): String { + return { + foo = "O" + foo + bar() + } () + } +} + +class TestClass : Test { + +} + +fun box(): String { + return TestClass().call() +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/accessorFromCompanion.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/accessorFromCompanion.kt new file mode 100644 index 00000000000..ab09c39aa00 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/accessorFromCompanion.kt @@ -0,0 +1,26 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface Test { + @kotlin.annotations.JvmDefault + private val foo: String + get() = "O" + + @kotlin.annotations.JvmDefault + private fun bar(): String { + return "K" + } + + companion object { + fun call(test: Test): String { + return test.foo + test.bar() + } + } +} + +class TestClass : Test + +fun box(): String { + return Test.call(TestClass()) +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/accessorsFromDefaultImpls.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/accessorsFromDefaultImpls.kt new file mode 100644 index 00000000000..8345e07de15 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/accessorsFromDefaultImpls.kt @@ -0,0 +1,26 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface Test { + @kotlin.annotations.JvmDefault + private val foo: String + get() = "O" + + @kotlin.annotations.JvmDefault + private fun bar(): String { + return "K" + } + + fun call(): String { + return { foo + bar()} () + } +} + +class TestClass : Test { + +} + +fun box(): String { + return TestClass().call() +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/privateDefaultFromDefaultImpl.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/privateDefaultFromDefaultImpl.kt new file mode 100644 index 00000000000..c67c22f6226 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/privateDefaultFromDefaultImpl.kt @@ -0,0 +1,24 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface Test { + fun test(): String { + return privateFun() + privateProp + } + + @kotlin.annotations.JvmDefault + private fun privateFun(): String { + return "O" + } + + @kotlin.annotations.JvmDefault + private val privateProp: String + get() = "K" +} + +class TestImpl: Test + +fun box(): String { + return TestImpl().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/privateInDefaultImpls.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/privateInDefaultImpls.kt new file mode 100644 index 00000000000..aa1eb60a88c --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/privateInDefaultImpls.kt @@ -0,0 +1,23 @@ +// !API_VERSION: 1.3 +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface Test { + @kotlin.annotations.JvmDefault + fun test(): String { + return privateFun() + privateProp + } + + private fun privateFun(): String { + return "O" + } + + private val privateProp: String + get() = "K" +} + +class TestImpl: Test + +fun box(): String { + return TestImpl().test() +} \ No newline at end of file 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 f1d9dea02a2..1802d33295d 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java @@ -337,6 +337,24 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class Defaults extends AbstractBlackBoxCodegenTest { + @TestMetadata("accessor.kt") + public void testAccessor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessor.kt"); + doTest(fileName); + } + + @TestMetadata("accessorFromCompanion.kt") + public void testAccessorFromCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessorFromCompanion.kt"); + doTest(fileName); + } + + @TestMetadata("accessorsFromDefaultImpls.kt") + public void testAccessorsFromDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/accessorsFromDefaultImpls.kt"); + doTest(fileName); + } + public void testAllFilesPresentInDefaults() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8/defaults"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } @@ -413,6 +431,18 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg doTest(fileName); } + @TestMetadata("privateDefaultFromDefaultImpl.kt") + public void testPrivateDefaultFromDefaultImpl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/privateDefaultFromDefaultImpl.kt"); + doTest(fileName); + } + + @TestMetadata("privateInDefaultImpls.kt") + public void testPrivateInDefaultImpls() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/privateInDefaultImpls.kt"); + doTest(fileName); + } + @TestMetadata("simpleCall.kt") public void testSimpleCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/simpleCall.kt");