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).
This commit is contained in:
Dmitry Petrov
2020-07-28 13:33:59 +03:00
parent 4fdccb3b35
commit 980b91d082
7 changed files with 63 additions and 19 deletions
@@ -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<Class<?>> additionalAnnotations
@NotNull List<String> 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<JvmTarget, Map<KotlinTarget, ElementType>> annotationTargetMaps = new EnumMap<>(JvmTarget.class);
@@ -338,13 +339,14 @@ public abstract class AnnotationCodegen {
visitor.visitEnd();
}
@NotNull
private String generateAnnotationIfNotPresent(Set<String> annotationDescriptorsAlreadyPresent, Class<?> annotationClass) {
String descriptor = Type.getType(annotationClass).getDescriptor();
if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
visitAnnotation(descriptor, false).visitEnd();
private void generateAnnotationIfNotPresent(
Set<String> annotationDescriptorsAlreadyPresent,
String annotationDescriptor,
boolean visible
) {
if (!annotationDescriptorsAlreadyPresent.contains(annotationDescriptor)) {
visitAnnotation(annotationDescriptor, visible).visitEnd();
}
return descriptor;
}
private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull KotlinType type) {
@@ -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<Class<?>> additionalNoArgAnnotations,
@NotNull List<String> 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,
@@ -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");
@@ -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"
}
@@ -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");
@@ -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");
@@ -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");