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.codegen.AsmUtil.*;
|
||||
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.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
||||
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
||||
@@ -163,9 +164,7 @@ public class FunctionCodegen {
|
||||
OwnerKind contextKind = methodContext.getContextKind();
|
||||
if (isInterface(functionDescriptor.getContainingDeclaration()) &&
|
||||
functionDescriptor.getVisibility() == Visibilities.PRIVATE &&
|
||||
(isJvm8InterfaceMember(functionDescriptor, state)
|
||||
? contextKind == OwnerKind.DEFAULT_IMPLS
|
||||
: contextKind != OwnerKind.DEFAULT_IMPLS)) {
|
||||
!processInterfaceMember(functionDescriptor, contextKind, state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,7 +198,7 @@ public class FunctionCodegen {
|
||||
|
||||
generateBridges(functionDescriptor);
|
||||
|
||||
if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS) {
|
||||
if (isJvm8InterfaceMember(functionDescriptor, state) && contextKind != OwnerKind.DEFAULT_IMPLS && state.getGenerateDefaultImplsForJvm8()) {
|
||||
generateDelegateForDefaultImpl(functionDescriptor, origin.getElement());
|
||||
}
|
||||
|
||||
@@ -771,7 +770,7 @@ public class FunctionCodegen {
|
||||
) {
|
||||
DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
|
||||
|
||||
if (kind != OwnerKind.DEFAULT_IMPLS && isInterface(contextClass)) {
|
||||
if (isInterface(contextClass) && !processInterface(contextClass, kind, state)) {
|
||||
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
|
||||
protected void generateDefaultImplsIfNeeded() {
|
||||
if (isInterface(descriptor) && !isLocal) {
|
||||
if (isInterface(descriptor) && !isLocal && (!isJvm8Interface(descriptor, state) || state.getGenerateDefaultImplsForJvm8())) {
|
||||
Type defaultImplsType = state.getTypeMapper().mapDefaultImpls(descriptor);
|
||||
ClassBuilder defaultImplsBuilder =
|
||||
state.getFactory().newVisitor(JvmDeclarationOriginKt.DefaultImpls(myClass, descriptor), defaultImplsType, myClass.getContainingFile());
|
||||
|
||||
@@ -79,7 +79,7 @@ public class JvmCodegenUtil {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +275,8 @@ public class PropertyCodegen {
|
||||
|
||||
DeclarationDescriptor contextDescriptor = context.getContextDescriptor();
|
||||
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;
|
||||
Method syntheticMethod = getSyntheticMethodSignature(descriptor);
|
||||
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 inheritMultifileParts: Boolean = configuration.getBoolean(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS)
|
||||
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)
|
||||
|
||||
|
||||
+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")
|
||||
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.
|
||||
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)
|
||||
|
||||
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 =
|
||||
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 =
|
||||
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
|
||||
-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
|
||||
-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
|
||||
-Xrepeat <count> Repeat compilation (for performance analysis)
|
||||
-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");
|
||||
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);
|
||||
}
|
||||
|
||||
@TestMetadata("defaultMethodCompatibility.kt")
|
||||
public void testDefaultMethodCompatibility() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultMethodCompatibility.kt");
|
||||
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");
|
||||
@TestMetadata("defaultPropertyCompatibility.kt")
|
||||
public void testDefaultPropertyCompatibility() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/writeFlags/defaultPropertyCompatibility.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user