From 980b91d0829169f273638ef8873c4d7ffeb619e6 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 28 Jul 2020 13:33:59 +0300 Subject: [PATCH] JVM: generate 'Deprecated' on method as runtime-visible annotation We generate @java.lang.Deprecated annotation on methods of $DefaultImpls classes in compatibility mode. This annotation has RUNTIME retention and should be visible. Also, get rid of representing annotations as Class'es (yes we know that these annotations are in compiler CLASSPATH, but we should not rely on such classes and associated information). --- .../kotlin/codegen/AnnotationCodegen.java | 34 ++++++++++--------- .../kotlin/codegen/FunctionCodegen.java | 8 +++-- .../ir/FirBlackBoxCodegenTestGenerated.java | 5 +++ .../compatibility/deprecatedAnnotation.kt | 20 +++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++ .../LightAnalysisModeTestGenerated.java | 5 +++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 +++ 7 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java index b740a133e7c..980d9ba1c47 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java @@ -50,6 +50,9 @@ import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getA public abstract class AnnotationCodegen { + private static final String ORG_JETBRAINS_ANNOTATIONS_NOTNULL = Type.getType(NotNull.class).getDescriptor(); + private static final String ORG_JETBRAINS_ANNOTATIONS_NULLABLE = Type.getType(Nullable.class).getDescriptor(); + public static final class JvmFlagAnnotation { private final FqName fqName; private final int jvmFlag; @@ -120,7 +123,7 @@ public abstract class AnnotationCodegen { @Nullable Type returnType, @Nullable KotlinType typeForTypeAnnotations, @Nullable DeclarationDescriptorWithVisibility parameterContainer, - @NotNull List> additionalAnnotations + @NotNull List additionalVisibleAnnotations ) { if (annotated == null) return; @@ -155,9 +158,9 @@ public abstract class AnnotationCodegen { } } - for (Class annotation : additionalAnnotations) { - String descriptor = generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotation); - annotationDescriptorsAlreadyPresent.add(descriptor); + for (String annotation : additionalVisibleAnnotations) { + generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotation, true); + annotationDescriptorsAlreadyPresent.add(annotation); } generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent, parameterContainer); @@ -248,17 +251,15 @@ public abstract class AnnotationCodegen { if (!TypeUtils.isNullableType(flexibleType.getLowerBound()) && TypeUtils.isNullableType(flexibleType.getUpperBound())) { AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION); if (notNull != null) { - generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, NotNull.class); + generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, ORG_JETBRAINS_ANNOTATIONS_NOTNULL, false); } return; } } - boolean isNullableType = TypeUtils.isNullableType(type); - - Class annotationClass = isNullableType ? Nullable.class : NotNull.class; - - generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass); + String annotationDescriptor = + TypeUtils.isNullableType(type) ? ORG_JETBRAINS_ANNOTATIONS_NULLABLE : ORG_JETBRAINS_ANNOTATIONS_NOTNULL; + generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationDescriptor, false); } private static final Map> annotationTargetMaps = new EnumMap<>(JvmTarget.class); @@ -338,13 +339,14 @@ public abstract class AnnotationCodegen { visitor.visitEnd(); } - @NotNull - private String generateAnnotationIfNotPresent(Set annotationDescriptorsAlreadyPresent, Class annotationClass) { - String descriptor = Type.getType(annotationClass).getDescriptor(); - if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) { - visitAnnotation(descriptor, false).visitEnd(); + private void generateAnnotationIfNotPresent( + Set annotationDescriptorsAlreadyPresent, + String annotationDescriptor, + boolean visible + ) { + if (!annotationDescriptorsAlreadyPresent.contains(annotationDescriptor)) { + visitAnnotation(annotationDescriptor, visible).visitEnd(); } - return descriptor; } private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull KotlinType type) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 399c10ab7af..4409f5fe9e6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -83,6 +83,8 @@ import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; public class FunctionCodegen { + private static final String JAVA_LANG_DEPRECATED = Type.getType(Deprecated.class).getDescriptor(); + public final GenerationState state; private final KotlinTypeMapper typeMapper; private final BindingContext bindingContext; @@ -221,7 +223,7 @@ public class FunctionCodegen { InlineClassDescriptorResolver.isSpecializedEqualsMethod(functionDescriptor); generateMethodAnnotationsIfRequired( functionDescriptor, asmMethod, jvmSignature, mv, - isCompatibilityStubInDefaultImpls ? Collections.singletonList(Deprecated.class) : Collections.emptyList(), + isCompatibilityStubInDefaultImpls ? Collections.singletonList(JAVA_LANG_DEPRECATED) : Collections.emptyList(), skipNullabilityAnnotations ); GenerateJava8ParameterNamesKt.generateParameterNames(functionDescriptor, mv, jvmSignature, state, (flags & ACC_SYNTHETIC) != 0); @@ -285,7 +287,7 @@ public class FunctionCodegen { @NotNull Method asmMethod, @NotNull JvmMethodGenericSignature jvmSignature, @NotNull MethodVisitor mv, - @NotNull List> additionalNoArgAnnotations, + @NotNull List additionalVisibleAnnotations, boolean skipNullabilityAnnotations ) { FunctionDescriptor annotationsOwner; @@ -302,7 +304,7 @@ public class FunctionCodegen { } AnnotationCodegen.forMethod(mv, memberCodegen, state, skipNullabilityAnnotations) - .genAnnotations(annotationsOwner, asmMethod.getReturnType(), functionDescriptor.getReturnType(), null, additionalNoArgAnnotations); + .genAnnotations(annotationsOwner, asmMethod.getReturnType(), functionDescriptor.getReturnType(), null, additionalVisibleAnnotations); generateParameterAnnotations( annotationsOwner, mv, jvmSignature, diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 52ad4b9e01a..e118a1d2068 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -15640,6 +15640,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt"); } + @TestMetadata("deprecatedAnnotation.kt") + public void testDeprecatedAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt"); + } + @TestMetadata("inheritedFunctionWithDefaultParameters.kt") public void testInheritedFunctionWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt"); diff --git a/compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt b/compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt new file mode 100644 index 00000000000..59051a6bafc --- /dev/null +++ b/compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt @@ -0,0 +1,20 @@ +// !JVM_DEFAULT_MODE: compatibility +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// FULL_JDK +// WITH_RUNTIME + +interface IFoo { + @JvmDefault + fun foo() {} +} + +fun box(): String { + val iFoo = IFoo::class.java + val iFooDefaultImpls = Class.forName("${iFoo.name}\$DefaultImpls") + val fooMethod = iFooDefaultImpls.declaredMethods.find { it.name == "foo" } + ?: throw AssertionError("No method 'foo' in class ${iFooDefaultImpls.name}") + fooMethod.getAnnotation(java.lang.Deprecated::class.java) + ?: throw AssertionError("No java.lang.Deprecated annotation on method 'foo'") + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 4b677f600ab..8df5d378b01 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16865,6 +16865,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt"); } + @TestMetadata("deprecatedAnnotation.kt") + public void testDeprecatedAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt"); + } + @TestMetadata("inheritedFunctionWithDefaultParameters.kt") public void testInheritedFunctionWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 319f5485ca3..e7dc61889c7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -16865,6 +16865,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt"); } + @TestMetadata("deprecatedAnnotation.kt") + public void testDeprecatedAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt"); + } + @TestMetadata("inheritedFunctionWithDefaultParameters.kt") public void testInheritedFunctionWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b0b0c3e05bc..30146ace1d6 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -15640,6 +15640,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt"); } + @TestMetadata("deprecatedAnnotation.kt") + public void testDeprecatedAnnotation() throws Exception { + runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/deprecatedAnnotation.kt"); + } + @TestMetadata("inheritedFunctionWithDefaultParameters.kt") public void testInheritedFunctionWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");