Generate 'DefaultImpls' for jvm 8 target only within compiler option

This commit is contained in:
Michael Bogdanov
2016-09-12 12:05:45 +03:00
parent ca41f01468
commit dd4d5e3aa1
15 changed files with 145 additions and 26 deletions
@@ -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)
@@ -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
View File
@@ -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
@@ -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
@@ -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);
}
}
}
@@ -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);
}
}