Generate 'DefaultImpls' for jvm 8 target only within compiler option
This commit is contained in:
@@ -79,6 +79,7 @@ import java.util.Set;
|
|||||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
|
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isAnnotationOrJvm6Interface;
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isAnnotationOrJvm6Interface;
|
||||||
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8Interface;
|
||||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceMember;
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceMember;
|
||||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
||||||
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
||||||
@@ -163,9 +164,7 @@ public class FunctionCodegen {
|
|||||||
OwnerKind contextKind = methodContext.getContextKind();
|
OwnerKind contextKind = methodContext.getContextKind();
|
||||||
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
|
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
|
||||||
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
|
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
|
||||||
(isJvm8InterfaceMember(functionDescriptor, state)
|
!processInterfaceMember(functionDescriptor, contextKind, state)) {
|
||||||
? contextKind == OwnerKind.DEFAULT_IMPLS
|
|
||||||
: contextKind != OwnerKind.DEFAULT_IMPLS)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +198,7 @@ public class FunctionCodegen {
|
|||||||
|
|
||||||
generateBridges(functionDescriptor);
|
generateBridges(functionDescriptor);
|
||||||
|
|
||||||
if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS) {
|
if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()) {
|
||||||
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
|
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +770,7 @@ public class FunctionCodegen {
|
|||||||
) {
|
) {
|
||||||
DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
|
DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
|
||||||
|
|
||||||
if (kind != OwnerKind.DEFAULT_IMPLS && isInterface(contextClass)) {
|
if (isInterface(contextClass) && !processInterface(contextClass, kind, state)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,4 +1156,21 @@ public class FunctionCodegen {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean processInterfaceMember(
|
||||||
|
@NotNull CallableMemberDescriptor function,
|
||||||
|
@NotNull OwnerKind kind,
|
||||||
|
@NotNull GenerationState state
|
||||||
|
) {
|
||||||
|
return processInterface(function.getContainingDeclaration(), kind, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean processInterface(
|
||||||
|
@NotNull DeclarationDescriptor contextClass,
|
||||||
|
@NotNull OwnerKind kind,
|
||||||
|
@NotNull GenerationState state
|
||||||
|
) {
|
||||||
|
assert isInterface(contextClass) : "'processInterface' method should be called only for interfaces, but: " + contextClass;
|
||||||
|
return isJvm8Interface(contextClass, state) ? kind != OwnerKind.DEFAULT_IMPLS : kind == OwnerKind.DEFAULT_IMPLS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateDefaultImplsIfNeeded() {
|
protected void generateDefaultImplsIfNeeded() {
|
||||||
if (isInterface(descriptor) && !isLocal) {
|
if (isInterface(descriptor) && !isLocal && (!isJvm8Interface(descriptor, state) || state.getGenerateDefaultImplsForJvm8())) {
|
||||||
Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
|
Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
|
||||||
ClassBuilder defaultImplsBuilder =
|
ClassBuilder defaultImplsBuilder =
|
||||||
state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(myClass, descriptor), defaultImplsType, myClass.getContainingFile());
|
state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(myClass, descriptor), defaultImplsType, myClass.getContainingFile());
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class JvmCodegenUtil {
|
|||||||
return !state.isJvm8Target();
|
return !state.isJvm8Target();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isJvm8Interface(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
|
public static boolean isJvm8Interface(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state) {
|
||||||
return DescriptorUtils.isInterface(descriptor) && !isAnnotationOrJvm6Interface(descriptor, state);
|
return DescriptorUtils.isInterface(descriptor) && !isAnnotationOrJvm6Interface(descriptor, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -275,7 +275,8 @@ public class PropertyCodegen {
|
|||||||
|
|
||||||
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
|
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
|
||||||
if (!isInterface(contextDescriptor) ||
|
if (!isInterface(contextDescriptor) ||
|
||||||
(isJvm8Interface(contextDescriptor, state) ? kind != OwnerKind.DEFAULT_IMPLS : kind == OwnerKind.DEFAULT_IMPLS)) {
|
(FunctionCodegen.processInterface(contextDescriptor, kind, state) ||
|
||||||
|
(kind == OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()))) {
|
||||||
int flags = ACC_DEPRECATED | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
|
int flags = ACC_DEPRECATED | ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC;
|
||||||
Method syntheticMethod = getSyntheticMethodSignature(descriptor);
|
Method syntheticMethod = getSyntheticMethodSignature(descriptor);
|
||||||
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, syntheticMethod.getName(),
|
MethodVisitor mv = v.newMethod(JvmDeclarationOriginKt.OtherOrigin(descriptor), flags, syntheticMethod.getName(),
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
val useTypeTableInSerializer: Boolean = configuration.getBoolean(JVMConfigurationKeys.USE_TYPE_TABLE)
|
val useTypeTableInSerializer: Boolean = configuration.getBoolean(JVMConfigurationKeys.USE_TYPE_TABLE)
|
||||||
val inheritMultifileParts: Boolean = configuration.getBoolean(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS)
|
val inheritMultifileParts: Boolean = configuration.getBoolean(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS)
|
||||||
val isJvm8Target: Boolean = configuration.get(JVMConfigurationKeys.JVM_TARGET) == JvmTarget.JVM_1_8
|
val isJvm8Target: Boolean = configuration.get(JVMConfigurationKeys.JVM_TARGET) == JvmTarget.JVM_1_8
|
||||||
|
val generateDefaultImplsForJvm8: Boolean = configuration.getBoolean(JVMConfigurationKeys.INTERFACE_COMPATIBILITY)
|
||||||
|
|
||||||
val rootContext: CodegenContext<*> = RootContext(this)
|
val rootContext: CodegenContext<*> = RootContext(this)
|
||||||
|
|
||||||
|
|||||||
+3
@@ -98,6 +98,9 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
|
|||||||
@Argument(value = "Xsingle-module", description = "Combine modules for source files and binary dependencies into a single module")
|
@Argument(value = "Xsingle-module", description = "Combine modules for source files and binary dependencies into a single module")
|
||||||
public boolean singleModule;
|
public boolean singleModule;
|
||||||
|
|
||||||
|
@Argument(value = "Xinterface-compatibility", description = "Generate DefaultImpls classes for interfaces in JVM target bytecode version 1.8 for binary compatibility with 1.6")
|
||||||
|
public boolean interfaceCompatibility;
|
||||||
|
|
||||||
// Paths to output directories for friend modules.
|
// Paths to output directories for friend modules.
|
||||||
public String[] friendPaths;
|
public String[] friendPaths;
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,17 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arguments.interfaceCompatibility) {
|
||||||
|
val target = configuration.get(JVMConfigurationKeys.JVM_TARGET)
|
||||||
|
if (target != JvmTarget.JVM_1_8) {
|
||||||
|
val errorMessage = "The -Xinterface-compatibility option has effect only for JVM target bytecode version 1.8."
|
||||||
|
messageCollector.report(CompilerMessageSeverity.WARNING, errorMessage, CompilerMessageLocation.NO_LOCATION)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configuration.put(JVMConfigurationKeys.INTERFACE_COMPATIBILITY, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
putAdvancedOptions(configuration, arguments)
|
putAdvancedOptions(configuration, arguments)
|
||||||
|
|
||||||
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment", CompilerMessageLocation.NO_LOCATION)
|
messageCollector.report(CompilerMessageSeverity.LOGGING, "Configuring the compilation environment", CompilerMessageLocation.NO_LOCATION)
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ public class JVMConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<JvmTarget> JVM_TARGET =
|
public static final CompilerConfigurationKey<JvmTarget> JVM_TARGET =
|
||||||
CompilerConfigurationKey.create("JVM bytecode target version");
|
CompilerConfigurationKey.create("JVM bytecode target version");
|
||||||
|
|
||||||
|
public static final CompilerConfigurationKey<Boolean> INTERFACE_COMPATIBILITY =
|
||||||
|
CompilerConfigurationKey.create("Generate additional 'DefaultImpls' class files for jvm 8 target for compatibility with 6 target interfaces");
|
||||||
|
|
||||||
public static final CompilerConfigurationKey<IncrementalCompilationComponents> INCREMENTAL_COMPILATION_COMPONENTS =
|
public static final CompilerConfigurationKey<IncrementalCompilationComponents> INCREMENTAL_COMPILATION_COMPONENTS =
|
||||||
CompilerConfigurationKey.create("incremental cache provider");
|
CompilerConfigurationKey.create("incremental cache provider");
|
||||||
|
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ where advanced options include:
|
|||||||
-Xdump-declarations-to <path> Path to JSON file to dump Java to Kotlin declaration mappings
|
-Xdump-declarations-to <path> Path to JSON file to dump Java to Kotlin declaration mappings
|
||||||
-Xload-script-configs Load script configuration files from project directory tree
|
-Xload-script-configs Load script configuration files from project directory tree
|
||||||
-Xsingle-module Combine modules for source files and binary dependencies into a single module
|
-Xsingle-module Combine modules for source files and binary dependencies into a single module
|
||||||
|
-Xinterface-compatibility Generate DefaultImpls classes for interfaces in JVM target bytecode version 1.8 for binary compatibility with 1.6
|
||||||
-Xno-inline Disable method inlining
|
-Xno-inline Disable method inlining
|
||||||
-Xrepeat <count> Repeat compilation (for performance analysis)
|
-Xrepeat <count> Repeat compilation (for performance analysis)
|
||||||
-Xplugin <path> Load plugins from the given classpath
|
-Xplugin <path> Load plugins from the given classpath
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// WITH_REFLECT
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INTERFACE_COMPATIBILITY
|
||||||
|
|
||||||
|
annotation class Property(val value: String)
|
||||||
|
annotation class Accessor(val value: String)
|
||||||
|
|
||||||
|
interface Z {
|
||||||
|
@Property("OK")
|
||||||
|
val z: String;
|
||||||
|
@Accessor("OK")
|
||||||
|
get() = "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Test : Z
|
||||||
|
|
||||||
|
fun box() : String {
|
||||||
|
val value = (Z::z.annotations.single() as Property).value
|
||||||
|
if (value != "OK") return value
|
||||||
|
val forName = Class.forName("Z\$DefaultImpls")
|
||||||
|
val annotation = forName.getDeclaredMethod("z\$annotations").getAnnotation(Property::class.java)
|
||||||
|
return annotation.value
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INTERFACE_COMPATIBILITY
|
||||||
|
|
||||||
|
|
||||||
|
interface Test {
|
||||||
|
fun test(): String {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test, test
|
||||||
|
// FLAGS: ACC_PUBLIC
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test, test$defaultImpl
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC, ACC_SYNTHETIC
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: class
|
||||||
|
// TESTED_OBJECTS: Test$DefaultImpls
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test$DefaultImpls, test
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// JVM_TARGET: 1.8
|
||||||
|
// KOTLIN_CONFIGURATION_FLAGS: +JVM.INTERFACE_COMPATIBILITY
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test, getZ$defaultImpl
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC, ACC_SYNTHETIC
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test, setZ$defaultImpl
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC, ACC_SYNTHETIC
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: class
|
||||||
|
// TESTED_OBJECTS: Test$DefaultImpls
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test$DefaultImpls, getZ
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC
|
||||||
|
|
||||||
|
// TESTED_OBJECT_KIND: function
|
||||||
|
// TESTED_OBJECTS: Test$DefaultImpls, setZ
|
||||||
|
// FLAGS: ACC_PUBLIC, ACC_STATIC
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// JVM_TARGET: 1.8
|
|
||||||
|
|
||||||
interface Test {
|
|
||||||
fun a() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TESTED_OBJECT_KIND: class
|
|
||||||
// TESTED_OBJECTS: Test$DefaultImpls
|
|
||||||
// FLAGS: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
|
|
||||||
|
|
||||||
// TESTED_OBJECT_KIND: class
|
|
||||||
// TESTED_OBJECTS: Test
|
|
||||||
// FLAGS: ACC_PUBLIC, ACC_ABSTRACT, ACC_INTERFACE
|
|
||||||
+6
@@ -209,6 +209,12 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/reflection/propertyAnnotations.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/reflection/propertyAnnotations.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyAnnotationsCompatibility.kt")
|
||||||
|
public void testPropertyAnnotationsCompatibility() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/jvm8/reflection/propertyAnnotationsCompatibility.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-3
@@ -41,15 +41,21 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("defaultMethodCompatibility.kt")
|
||||||
|
public void testDefaultMethodCompatibility() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultMethodCompatibility.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultProperty.kt")
|
@TestMetadata("defaultProperty.kt")
|
||||||
public void testDefaultProperty() throws Exception {
|
public void testDefaultProperty() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultProperty.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultProperty.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("interfaceAndDefaultImpls.kt")
|
@TestMetadata("defaultPropertyCompatibility.kt")
|
||||||
public void testInterfaceAndDefaultImpls() throws Exception {
|
public void testDefaultPropertyCompatibility() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/interfaceAndDefaultImpls.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultPropertyCompatibility.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user