diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 5fb8db2d433..b3b66e3128d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; +import org.jetbrains.kotlin.config.JvmDefaultMode; import org.jetbrains.kotlin.config.JvmTarget; import org.jetbrains.kotlin.config.LanguageFeature; import org.jetbrains.kotlin.descriptors.*; @@ -179,7 +180,10 @@ public class FunctionCodegen { ) { OwnerKind contextKind = methodContext.getContextKind(); DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); - if (isInterface(containingDeclaration) && !processInterfaceMethod(functionDescriptor, contextKind, false)) return; + if (isInterface(containingDeclaration) && + !processInterfaceMethod(functionDescriptor, contextKind, false, state.getJvmDefaultMode())) { + return; + } boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor); JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge); @@ -392,7 +396,7 @@ public class FunctionCodegen { } if (!functionDescriptor.isExternal()) { - generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen); + generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode()); } else if (staticInCompanionObject) { // native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class @@ -402,7 +406,7 @@ public class FunctionCodegen { Method accessorMethod = typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null)); Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration()); - generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName()); + generateDelegateToStaticMethodBody(false, mv, accessorMethod, owningType.getInternalName(), false); } endVisit(mv, null, origin.getElement()); @@ -574,7 +578,8 @@ public class FunctionCodegen { @NotNull MethodContext context, @NotNull JvmMethodSignature signature, @NotNull FunctionGenerationStrategy strategy, - @NotNull MemberCodegen parentCodegen + @NotNull MemberCodegen parentCodegen, + @NotNull JvmDefaultMode jvmDefaultMode ) { mv.visitCode(); @@ -596,6 +601,20 @@ public class FunctionCodegen { generateFacadeDelegateMethodBody(mv, signature.getAsmMethod(), (MultifileClassFacadeContext) context.getParentContext()); methodEnd = new Label(); } + else if (isCompatibilityStubInDefaultImpls(functionDescriptor, context, jvmDefaultMode)) { + FunctionDescriptor compatibility = ((DefaultImplsClassContext) context.getParentContext()).getInterfaceContext() + .getAccessorForJvmDefaultCompatibility(functionDescriptor); + 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; + CallableMethod method = typeMapper.mapToCallableMethod(compatibility, false); + + generateDelegateToStaticMethodBody( + true, mv, + method.getAsmMethod(), + method.getOwner().getInternalName(), + true); + methodEnd = new Label(); + } else { FrameMap frameMap = createFrameMap( parentCodegen.state, signature, functionDescriptor.getExtensionReceiverParameter(), @@ -690,6 +709,16 @@ public class FunctionCodegen { } } + private static boolean isCompatibilityStubInDefaultImpls( + @NotNull FunctionDescriptor functionDescriptor, + @NotNull MethodContext context, + @NotNull JvmDefaultMode jvmDefaultMode + ) { + return OwnerKind.DEFAULT_IMPLS == context.getContextKind() && + hasJvmDefaultAnnotation(functionDescriptor) && + jvmDefaultMode.isCompatibility(); + } + private static void generateLocalVariableTable( @NotNull MethodVisitor mv, @NotNull JvmMethodSignature jvmMethodSignature, @@ -869,7 +898,7 @@ public class FunctionCodegen { @NotNull Method asmMethod, @NotNull MultifileClassFacadeContext context ) { - generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName()); + generateDelegateToStaticMethodBody(true, mv, asmMethod, context.getFilePartType().getInternalName(), false); } private static void generateDelegateToMethodBody( @@ -937,9 +966,10 @@ public class FunctionCodegen { boolean isStatic, @NotNull MethodVisitor mv, @NotNull Method asmMethod, - @NotNull String classToDelegateTo + @NotNull String classToDelegateTo, + boolean isInterfaceMethodCall ) { - generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, false); + generateDelegateToMethodBody(isStatic ? 0 : 1, mv, asmMethod, classToDelegateTo, Opcodes.INVOKESTATIC, isInterfaceMethodCall); } private static boolean needIndexForVar(JvmMethodParameterKind kind) { @@ -1119,7 +1149,7 @@ public class FunctionCodegen { DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration(); if (isInterface(contextClass) && - !processInterfaceMethod(functionDescriptor, kind, true)) { + !processInterfaceMethod(functionDescriptor, kind, true, state.getJvmDefaultMode())) { return; } @@ -1164,6 +1194,17 @@ public class FunctionCodegen { generateFacadeDelegateMethodBody(mv, defaultMethod, (MultifileClassFacadeContext) this.owner); endVisit(mv, "default method delegation", getSourceFromDescriptor(functionDescriptor)); } + else if (isCompatibilityStubInDefaultImpls(functionDescriptor, owner, state.getJvmDefaultMode())) { + mv.visitCode(); + Method interfaceDefaultMethod = typeMapper.mapDefaultMethod(functionDescriptor, OwnerKind.IMPLEMENTATION); + generateDelegateToStaticMethodBody( + true, mv, + interfaceDefaultMethod, + typeMapper.mapOwner(functionDescriptor).getInternalName(), + true + ); + endVisit(mv, "default method delegation to interface one", getSourceFromDescriptor(functionDescriptor)); + } else { mv.visitCode(); generateDefaultImplBody(owner, functionDescriptor, mv, loadStrategy, function, memberCodegen, defaultMethod); @@ -1538,14 +1579,15 @@ public class FunctionCodegen { public static boolean processInterfaceMethod( @NotNull CallableMemberDescriptor memberDescriptor, @NotNull OwnerKind kind, - boolean isDefaultOrSynthetic + boolean isDefaultOrSynthetic, + JvmDefaultMode mode ) { DeclarationDescriptor containingDeclaration = memberDescriptor.getContainingDeclaration(); assert isInterface(containingDeclaration) : "'processInterfaceMethod' method should be called only for interfaces, but: " + containingDeclaration; if (hasJvmDefaultAnnotation(memberDescriptor)) { - return kind != OwnerKind.DEFAULT_IMPLS; + return kind != OwnerKind.DEFAULT_IMPLS || mode.isCompatibility(); } else { switch (kind) { case DEFAULT_IMPLS: return true; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 93264ff17b7..a441599de4d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -726,8 +726,19 @@ public abstract class MemberCodegen { return getAccessor(propertyDescriptor, FieldAccessorKind.NORMAL, null, superCallTarget, getterAccessorRequired, setterAccessorRequired); } + + public D getAccessorForJvmDefaultCompatibility(@NotNull D descriptor) { + return getAccessor(descriptor, FieldAccessorKind.JVM_DEFAULT_COMPATIBILITY, null, + (ClassDescriptor) descriptor.getContainingDeclaration()); + } + @NotNull private D getAccessor(@NotNull D descriptor, @Nullable ClassDescriptor superCallTarget) { return getAccessor(descriptor, FieldAccessorKind.NORMAL, null, superCallTarget); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt index 1d4b2c4f07c..fce04c68b1c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SourceCompilerForInline.kt @@ -192,7 +192,7 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody) } - FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen) + FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode) if (isLambda) { codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 0ada0f3dcbf..e1eea952547 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -227,6 +227,8 @@ class GenerationState private constructor( JVMConstructorCallNormalizationMode.DEFAULT ) + val jvmDefaultMode = languageVersionSettings.getFlag(AnalysisFlag.jvmDefaultMode) + init { val disableOptimization = configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt index c8bde4da6b1..ef5cc40a1d6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt @@ -24,6 +24,7 @@ enum class FieldAccessorKind(val suffix: String) { IN_CLASS_COMPANION("cp"), FIELD_FROM_LOCAL("lp"), LATEINIT_INTRINSIC("li"), + JVM_DEFAULT_COMPATIBILITY("jd") } private fun CallableMemberDescriptor.getJvmName() = @@ -42,6 +43,6 @@ fun getAccessorNameSuffix( else -> throw UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor) } - + if (accessorKind == FieldAccessorKind.JVM_DEFAULT_COMPATIBILITY) return suffix + "$" + FieldAccessorKind.JVM_DEFAULT_COMPATIBILITY.suffix return if (superCallDescriptor == null) suffix else "$suffix\$s${superCallDescriptor.name.asString().hashCode()}" } diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge.kt new file mode 100644 index 00000000000..514128d93d2 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge.kt @@ -0,0 +1,38 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// FILE: Simple.java + +public interface Simple extends KInterface2 { + default String test() { + return KInterface2.DefaultImpls.test2(this, "OK"); + } +} + +// FILE: Foo.java +public class Foo implements Simple { + +} + +// FILE: main.kt +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface KInterface { + @JvmDefault + fun test2(p: T): T { + return p + } +} + +interface KInterface2 : KInterface { + +} + + +fun box(): String { + + val result = Foo().test() + if (result != "OK") return "fail 1: ${result}" + + return Foo().test2("OK") +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge2.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge2.kt new file mode 100644 index 00000000000..50233a84f9a --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge2.kt @@ -0,0 +1,36 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// FILE: Simple.java + +public interface Simple extends KInterface2 { + default String test() { + return KInterface2.DefaultImpls.test2(this, "OK"); + } +} + +// FILE: Foo.java +public class Foo implements Simple { + public String test2(String p) { + return "fail"; + } +} + +// FILE: main.kt +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface KInterface { + @JvmDefault + fun test2(p: T): T { + return p + } +} + +interface KInterface2 : KInterface { + +} + + +fun box(): String { + return Foo().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge3.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge3.kt new file mode 100644 index 00000000000..df52c0ae264 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge3.kt @@ -0,0 +1,40 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// FILE: Simple.java + +public interface Simple extends KInterface3 { + default String test() { + return KInterface3.DefaultImpls.test2(this, "OK"); + } +} + +// FILE: Foo.java +public class Foo implements Simple { + public String test2(String p) { + return "fail"; + } +} + +// FILE: main.kt +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface KInterface { + @JvmDefault + fun test2(p: T): T { + return p + } +} + +interface KInterface2 : KInterface { + +} + +interface KInterface3 : KInterface2 { + +} + + +fun box(): String { + return Foo().test() +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/defaultArgs.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/defaultArgs.kt new file mode 100644 index 00000000000..c11e820ba63 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/defaultArgs.kt @@ -0,0 +1,22 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK +interface Test { + @JvmDefault + fun test(s: String ="OK"): String { + return s + } +} + +class TestClass : Test { + +} + +fun box(): String { + val defaultImpls = java.lang.Class.forName(Test::class.java.canonicalName + "\$DefaultImpls") + + val declaredMethod = defaultImpls.getDeclaredMethod("test\$default", Test::class.java, String::class.java, Int::class.java, Any::class.java) + return declaredMethod.invoke(null, TestClass(), null, 1, null) as String +} diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/inheritedJvmDefault.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/inheritedJvmDefault.kt new file mode 100644 index 00000000000..9286c69c5c8 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/inheritedJvmDefault.kt @@ -0,0 +1,37 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// FILE: Simple.java + +public interface Simple extends KInterface2 { + default String test() { + return KInterface2.DefaultImpls.test2(this); + } +} + +// FILE: Foo.java +public class Foo implements Simple { + +} + +// FILE: main.kt +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface KInterface { + @JvmDefault + fun test2(): String { + return "OK" + } +} + +interface KInterface2 : KInterface { + +} + + +fun box(): String { + val result = Foo().test() + if (result != "OK") return "fail 1: ${result}" + + return Foo().test2() +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/simpleFunction.kt b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/simpleFunction.kt new file mode 100644 index 00000000000..d41cd2259f5 --- /dev/null +++ b/compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/simpleFunction.kt @@ -0,0 +1,33 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// FILE: Simple.java + +public interface Simple extends KInterface { + default String test() { + return KInterface.DefaultImpls.test2(this); + } +} + +// FILE: Foo.java +public class Foo implements Simple { + +} + +// FILE: main.kt +// JVM_TARGET: 1.8 +// WITH_RUNTIME + +interface KInterface { + @JvmDefault + fun test2(): String { + return "OK" + } +} + + +fun box(): String { + val result = Foo().test() + if (result != "OK") return "fail 1: ${result}" + + return Foo().test2() +} \ No newline at end of file diff --git a/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/defaultArgs.kt b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/defaultArgs.kt new file mode 100644 index 00000000000..9414e2e0eb2 --- /dev/null +++ b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/defaultArgs.kt @@ -0,0 +1,20 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// JVM_TARGET: 1.8 +// WITH_RUNTIME +// FULL_JDK +interface KInterface { + @JvmDefault + fun test(s: String ="OK"): String { + return s + } +} + +// 1 INVOKESTATIC KInterface.access\$test\$jd +// 1 INVOKESTATIC KInterface.test\$default + +// from $default +// 1 INVOKEINTERFACE KInterface.test + +//from $jd +// 1 INVOKESPECIAL KInterface.test \ No newline at end of file diff --git a/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunction.kt b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunction.kt new file mode 100644 index 00000000000..4570f89d04d --- /dev/null +++ b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunction.kt @@ -0,0 +1,20 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// JVM_TARGET: 1.8 + +interface KInterface { + @JvmDefault + fun test2(): String { + return "OK" + } +} + +interface KInterface2 : KInterface { + +} + +// 1 INVOKESTATIC KInterface2.access\$test2\$jd +// 1 INVOKESTATIC KInterface.access\$test2\$jd + +// 1 INVOKESPECIAL KInterface2.test2 +// 1 INVOKESPECIAL KInterface.test2 \ No newline at end of file diff --git a/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunctionWithAbstractOverride.kt b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunctionWithAbstractOverride.kt new file mode 100644 index 00000000000..356cb336248 --- /dev/null +++ b/compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunctionWithAbstractOverride.kt @@ -0,0 +1,24 @@ +// !API_VERSION: 1.3 +// !JVM_DEFAULT_MODE: compatibility +// JVM_TARGET: 1.8 + +interface KInterface { + @JvmDefault + fun test2(): String { + return "OK" + } +} + +interface KInterface2 : KInterface { + @JvmDefault + abstract override fun test2(): String +} + +// 1 INVOKESTATIC KInterface.access\$test2\$jd +// + +// 0 INVOKESTATIC KInterface2.access\$test2\$jd +// = +// 1 INVOKESTATIC + +// 1 INVOKESPECIAL KInterface.test2 +// 0 INVOKESPECIAL KInterface2.test2 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 0b2c22f54b8..dbfb3514cde 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJava8CodegenTestGenerated.java @@ -481,6 +481,49 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg runTest("compiler/testData/codegen/java8/box/jvm8/defaults/superCall.kt"); } + @TestMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Compatibility extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInCompatibility() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("bridge.kt") + public void testBridge() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge.kt"); + } + + @TestMetadata("bridge2.kt") + public void testBridge2() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge2.kt"); + } + + @TestMetadata("bridge3.kt") + public void testBridge3() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/bridge3.kt"); + } + + @TestMetadata("defaultArgs.kt") + public void testDefaultArgs() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/defaultArgs.kt"); + } + + @TestMetadata("inheritedJvmDefault.kt") + public void testInheritedJvmDefault() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/inheritedJvmDefault.kt"); + } + + @TestMetadata("simpleFunction.kt") + public void testSimpleFunction() throws Exception { + runTest("compiler/testData/codegen/java8/box/jvm8/defaults/compatibility/simpleFunction.kt"); + } + } + @TestMetadata("compiler/testData/codegen/java8/box/jvm8/defaults/delegationBy") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BytecodeTextJava8TestGenerated.java b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BytecodeTextJava8TestGenerated.java index cc995345446..4482b5643ad 100644 --- a/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BytecodeTextJava8TestGenerated.java +++ b/compiler/tests-java8/tests/org/jetbrains/kotlin/codegen/BytecodeTextJava8TestGenerated.java @@ -51,4 +51,45 @@ public class BytecodeTextJava8TestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/java8/bytecodeText/hashCode/hashCode.kt"); } } + + @TestMetadata("compiler/testData/codegen/java8/bytecodeText/jvmDefault") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmDefault extends AbstractBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInJvmDefault() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/bytecodeText/jvmDefault"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Compatibility extends AbstractBytecodeTextTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInCompatibility() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("defaultArgs.kt") + public void testDefaultArgs() throws Exception { + runTest("compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/defaultArgs.kt"); + } + + @TestMetadata("simpleFunction.kt") + public void testSimpleFunction() throws Exception { + runTest("compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunction.kt"); + } + + @TestMetadata("simpleFunctionWithAbstractOverride.kt") + public void testSimpleFunctionWithAbstractOverride() throws Exception { + runTest("compiler/testData/codegen/java8/bytecodeText/jvmDefault/compatibility/simpleFunctionWithAbstractOverride.kt"); + } + } + } } diff --git a/compiler/util/src/org/jetbrains/kotlin/config/JvmDefaultMode.kt b/compiler/util/src/org/jetbrains/kotlin/config/JvmDefaultMode.kt index 18d4eada942..364da1ae351 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/JvmDefaultMode.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/JvmDefaultMode.kt @@ -13,6 +13,9 @@ enum class JvmDefaultMode(val description: String) { val isEnabled get() = this != DISABLE + val isCompatibility + get() = this == ENABLE_WITH_DEFAULT_IMPLS + companion object { @JvmField val DEFAULT = DISABLE