diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 8ba0bd83dc1..4439691cfcf 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 com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import kotlin.collections.CollectionsKt; +import kotlin.Unit; import kotlin.jvm.functions.Function1; import kotlin.text.StringsKt; import org.jetbrains.annotations.NotNull; @@ -52,6 +53,7 @@ import org.jetbrains.kotlin.resolve.constants.ArrayValue; import org.jetbrains.kotlin.resolve.constants.ConstantValue; import org.jetbrains.kotlin.resolve.constants.KClassValue; import org.jetbrains.kotlin.resolve.inline.InlineUtil; +import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind; @@ -161,7 +163,9 @@ public class FunctionCodegen { OwnerKind contextKind = methodContext.getContextKind(); if (isInterface(functionDescriptor.getContainingDeclaration()) && functionDescriptor.getVisibility() == Visibilities.PRIVATE && - contextKind != OwnerKind.DEFAULT_IMPLS) { + (isJvm8InterfaceMember(functionDescriptor, state) + ? contextKind == OwnerKind.DEFAULT_IMPLS + : contextKind != OwnerKind.DEFAULT_IMPLS)) { return; } @@ -195,6 +199,10 @@ public class FunctionCodegen { generateBridges(functionDescriptor); + if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS) { + generateDelegateForDefaultImpl(functionDescriptor, origin.getElement()); + } + boolean staticInCompanionObject = AnnotationUtilKt.isPlatformStaticInCompanionObject(functionDescriptor); if (staticInCompanionObject) { ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen(); @@ -227,12 +235,40 @@ public class FunctionCodegen { Method accessorMethod = typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null)); Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration()); - generateDelegateToMethodBody(false, mv, accessorMethod, owningType.getInternalName()); + generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName()); } endVisit(mv, null, origin.getElement()); } + private void generateDelegateForDefaultImpl( + @NotNull final FunctionDescriptor functionDescriptor, + @Nullable PsiElement element + ) { + Method defaultImplMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS); + + CodegenUtilKt.generateMethod( + v, "Default Impl delegate in interface", Opcodes.ACC_SYNTHETIC | Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, + new Method(defaultImplMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, defaultImplMethod.getDescriptor()), + element, JvmDeclarationOrigin.NO_ORIGIN, + state, new Function1() { + @Override + public Unit invoke(InstructionAdapter adapter) { + Method interfaceMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.IMPLEMENTATION); + Type type = typeMapper.mapOwner(functionDescriptor); + generateDelegateToMethodBody( + -1, adapter, + interfaceMethod, + type.getInternalName(), + Opcodes.INVOKESPECIAL, + true + ); + return null; + } + } + ); + } + private void generateMethodAnnotations( @NotNull FunctionDescriptor functionDescriptor, Method asmMethod, @@ -360,13 +396,16 @@ public class FunctionCodegen { generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext()); methodEnd = new Label(); } - else if (OwnerKind.IMPLEMENTATION == context.getContextKind() && - JvmCodegenUtil.isJvmInterface(functionDescriptor.getContainingDeclaration())) { - int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.IMPLEMENTATION, context.getState()); + else if (OwnerKind.DEFAULT_IMPLS == context.getContextKind() && isJvm8InterfaceMember(functionDescriptor, parentCodegen.state)) { + int flags = AsmUtil.getMethodAsmFlags(functionDescriptor, OwnerKind.DEFAULT_IMPLS, context.getState()); assert (flags & Opcodes.ACC_ABSTRACT) == 0 : "Interface method with body should be non-abstract" + functionDescriptor; - Type type = typeMapper.mapDefaultImpls((ClassDescriptor) functionDescriptor.getContainingDeclaration()); + Type type = typeMapper.mapOwner(functionDescriptor); Method asmMethod = typeMapper.mapAsmMethod(functionDescriptor, OwnerKind.DEFAULT_IMPLS); - generateDelegateToMethodBody(true, mv, asmMethod, type.getInternalName()); + generateDelegateToStaticMethodBody( + true, mv, + new Method(asmMethod.getName() + JvmAbi.DEFAULT_IMPLS_DELEGATE_SUFFIX, asmMethod.getDescriptor()), + type.getInternalName() + ); methodEnd = new Label(); } else { @@ -525,14 +564,17 @@ public class FunctionCodegen { @NotNull Method asmMethod, @NotNull MultifileClassFacadeContext context ) { - generateDelegateToMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName()); + generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName()); } private static void generateDelegateToMethodBody( - boolean isStatic, + // -1 means to add additional this parameter on stack + int firstParamIndex, @NotNull MethodVisitor mv, @NotNull Method asmMethod, - @NotNull String classToDelegateTo + @NotNull String classToDelegateTo, + int opcode, + boolean isInterface ) { InstructionAdapter iv = new InstructionAdapter(mv); Type[] argTypes = asmMethod.getArgumentTypes(); @@ -543,15 +585,29 @@ public class FunctionCodegen { iv.visitLabel(label); iv.visitLineNumber(1, label); - int k = isStatic ? 0 : 1; - for (Type argType : argTypes) { - iv.load(k, argType); - k += argType.getSize(); + int paramIndex = firstParamIndex; + if (paramIndex == -1) { + iv.load(0, AsmTypes.OBJECT_TYPE); + paramIndex = 1; } - iv.invokestatic(classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), false); + + for (Type argType : argTypes) { + iv.load(paramIndex, argType); + paramIndex += argType.getSize(); + } + iv.visitMethodInsn(opcode, classToDelegateTo, asmMethod.getName(), asmMethod.getDescriptor(), isInterface); iv.areturn(asmMethod.getReturnType()); } + private static void generateDelegateToStaticMethodBody( + boolean isStatic, + @NotNull MethodVisitor mv, + @NotNull Method asmMethod, + @NotNull String classToDelegateTo + ) { + generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, false); + } + private static boolean needIndexForVar(JvmMethodParameterKind kind) { return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE || kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL || diff --git a/compiler/testData/codegen/java8/box/jvm8/simpleProperty.kt b/compiler/testData/codegen/java8/box/jvm8/simpleProperty.kt new file mode 100644 index 00000000000..e98f05ed57a --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/simpleProperty.kt @@ -0,0 +1,13 @@ +// JVM_TARGET: 1.8 + +interface Z { + val z: String; + get() = "OK" +} + + +class Test : Z + +fun box() : String { + return Test().z +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/writeFlags/defaultProperty.kt b/compiler/testData/codegen/java8/writeFlags/defaultProperty.kt new file mode 100644 index 00000000000..b35aad76cf0 --- /dev/null +++ b/compiler/testData/codegen/java8/writeFlags/defaultProperty.kt @@ -0,0 +1,15 @@ +// JVM_TARGET: 1.8 + +interface Test { + var z: String + get() = "OK" + set(value) {} +} + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Test, getZ +// FLAGS: ACC_PUBLIC + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Test, setZ +// FLAGS: ACC_PUBLIC diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java index 3ecef9d7b5a..86e8164a403 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java @@ -41,6 +41,12 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest { doTest(fileName); } + @TestMetadata("defaultProperty.kt") + public void testDefaultProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultProperty.kt"); + doTest(fileName); + } + @TestMetadata("interfaceAndDefaultImpls.kt") public void testInterfaceAndDefaultImpls() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/interfaceAndDefaultImpls.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java index 7a8702e7a8e..c298488b5af 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAbi.java @@ -34,6 +34,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject; public final class JvmAbi { public static final String DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"; public static final String DEFAULT_IMPLS_SUFFIX = "$" + DEFAULT_IMPLS_CLASS_NAME; + public static final String DEFAULT_IMPLS_DELEGATE_SUFFIX = "$defaultImpl"; public static final String DEFAULT_PARAMS_IMPL_SUFFIX = "$default";