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:
@@ -50,6 +50,9 @@ import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getA
|
|||||||
|
|
||||||
public abstract class AnnotationCodegen {
|
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 {
|
public static final class JvmFlagAnnotation {
|
||||||
private final FqName fqName;
|
private final FqName fqName;
|
||||||
private final int jvmFlag;
|
private final int jvmFlag;
|
||||||
@@ -120,7 +123,7 @@ public abstract class AnnotationCodegen {
|
|||||||
@Nullable Type returnType,
|
@Nullable Type returnType,
|
||||||
@Nullable KotlinType typeForTypeAnnotations,
|
@Nullable KotlinType typeForTypeAnnotations,
|
||||||
@Nullable DeclarationDescriptorWithVisibility parameterContainer,
|
@Nullable DeclarationDescriptorWithVisibility parameterContainer,
|
||||||
@NotNull List<Class<?>> additionalAnnotations
|
@NotNull List<String> additionalVisibleAnnotations
|
||||||
) {
|
) {
|
||||||
if (annotated == null) return;
|
if (annotated == null) return;
|
||||||
|
|
||||||
@@ -155,9 +158,9 @@ public abstract class AnnotationCodegen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Class<?> annotation : additionalAnnotations) {
|
for (String annotation : additionalVisibleAnnotations) {
|
||||||
String descriptor = generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotation);
|
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotation, true);
|
||||||
annotationDescriptorsAlreadyPresent.add(descriptor);
|
annotationDescriptorsAlreadyPresent.add(annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent, parameterContainer);
|
generateAdditionalAnnotations(annotated, returnType, annotationDescriptorsAlreadyPresent, parameterContainer);
|
||||||
@@ -248,17 +251,15 @@ public abstract class AnnotationCodegen {
|
|||||||
if (!TypeUtils.isNullableType(flexibleType.getLowerBound()) && TypeUtils.isNullableType(flexibleType.getUpperBound())) {
|
if (!TypeUtils.isNullableType(flexibleType.getLowerBound()) && TypeUtils.isNullableType(flexibleType.getUpperBound())) {
|
||||||
AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION);
|
AnnotationDescriptor notNull = type.getAnnotations().findAnnotation(JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION);
|
||||||
if (notNull != null) {
|
if (notNull != null) {
|
||||||
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, NotNull.class);
|
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, ORG_JETBRAINS_ANNOTATIONS_NOTNULL, false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isNullableType = TypeUtils.isNullableType(type);
|
String annotationDescriptor =
|
||||||
|
TypeUtils.isNullableType(type) ? ORG_JETBRAINS_ANNOTATIONS_NULLABLE : ORG_JETBRAINS_ANNOTATIONS_NOTNULL;
|
||||||
Class<?> annotationClass = isNullableType ? Nullable.class : NotNull.class;
|
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationDescriptor, false);
|
||||||
|
|
||||||
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<JvmTarget, Map<KotlinTarget, ElementType>> annotationTargetMaps = new EnumMap<>(JvmTarget.class);
|
private static final Map<JvmTarget, Map<KotlinTarget, ElementType>> annotationTargetMaps = new EnumMap<>(JvmTarget.class);
|
||||||
@@ -338,13 +339,14 @@ public abstract class AnnotationCodegen {
|
|||||||
visitor.visitEnd();
|
visitor.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private void generateAnnotationIfNotPresent(
|
||||||
private String generateAnnotationIfNotPresent(Set<String> annotationDescriptorsAlreadyPresent, Class<?> annotationClass) {
|
Set<String> annotationDescriptorsAlreadyPresent,
|
||||||
String descriptor = Type.getType(annotationClass).getDescriptor();
|
String annotationDescriptor,
|
||||||
if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
|
boolean visible
|
||||||
visitAnnotation(descriptor, false).visitEnd();
|
) {
|
||||||
|
if (!annotationDescriptorsAlreadyPresent.contains(annotationDescriptor)) {
|
||||||
|
visitAnnotation(annotationDescriptor, visible).visitEnd();
|
||||||
}
|
}
|
||||||
return descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBareTypeParameterWithNullableUpperBound(@NotNull KotlinType type) {
|
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.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
public class FunctionCodegen {
|
public class FunctionCodegen {
|
||||||
|
private static final String JAVA_LANG_DEPRECATED = Type.getType(Deprecated.class).getDescriptor();
|
||||||
|
|
||||||
public final GenerationState state;
|
public final GenerationState state;
|
||||||
private final KotlinTypeMapper typeMapper;
|
private final KotlinTypeMapper typeMapper;
|
||||||
private final BindingContext bindingContext;
|
private final BindingContext bindingContext;
|
||||||
@@ -221,7 +223,7 @@ public class FunctionCodegen {
|
|||||||
InlineClassDescriptorResolver.isSpecializedEqualsMethod(functionDescriptor);
|
InlineClassDescriptorResolver.isSpecializedEqualsMethod(functionDescriptor);
|
||||||
generateMethodAnnotationsIfRequired(
|
generateMethodAnnotationsIfRequired(
|
||||||
functionDescriptor, asmMethod, jvmSignature, mv,
|
functionDescriptor, asmMethod, jvmSignature, mv,
|
||||||
isCompatibilityStubInDefaultImpls ? Collections.singletonList(Deprecated.class) : Collections.emptyList(),
|
isCompatibilityStubInDefaultImpls ? Collections.singletonList(JAVA_LANG_DEPRECATED) : Collections.emptyList(),
|
||||||
skipNullabilityAnnotations
|
skipNullabilityAnnotations
|
||||||
);
|
);
|
||||||
GenerateJava8ParameterNamesKt.generateParameterNames(functionDescriptor, mv, jvmSignature, state, (flags & ACC_SYNTHETIC) != 0);
|
GenerateJava8ParameterNamesKt.generateParameterNames(functionDescriptor, mv, jvmSignature, state, (flags & ACC_SYNTHETIC) != 0);
|
||||||
@@ -285,7 +287,7 @@ public class FunctionCodegen {
|
|||||||
@NotNull Method asmMethod,
|
@NotNull Method asmMethod,
|
||||||
@NotNull JvmMethodGenericSignature jvmSignature,
|
@NotNull JvmMethodGenericSignature jvmSignature,
|
||||||
@NotNull MethodVisitor mv,
|
@NotNull MethodVisitor mv,
|
||||||
@NotNull List<Class<?>> additionalNoArgAnnotations,
|
@NotNull List<String> additionalVisibleAnnotations,
|
||||||
boolean skipNullabilityAnnotations
|
boolean skipNullabilityAnnotations
|
||||||
) {
|
) {
|
||||||
FunctionDescriptor annotationsOwner;
|
FunctionDescriptor annotationsOwner;
|
||||||
@@ -302,7 +304,7 @@ public class FunctionCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnnotationCodegen.forMethod(mv, memberCodegen, state, skipNullabilityAnnotations)
|
AnnotationCodegen.forMethod(mv, memberCodegen, state, skipNullabilityAnnotations)
|
||||||
.genAnnotations(annotationsOwner, asmMethod.getReturnType(), functionDescriptor.getReturnType(), null, additionalNoArgAnnotations);
|
.genAnnotations(annotationsOwner, asmMethod.getReturnType(), functionDescriptor.getReturnType(), null, additionalVisibleAnnotations);
|
||||||
|
|
||||||
generateParameterAnnotations(
|
generateParameterAnnotations(
|
||||||
annotationsOwner, mv, jvmSignature,
|
annotationsOwner, mv, jvmSignature,
|
||||||
|
|||||||
Generated
+5
@@ -15640,6 +15640,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt");
|
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")
|
@TestMetadata("inheritedFunctionWithDefaultParameters.kt")
|
||||||
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
||||||
|
|||||||
+20
@@ -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"
|
||||||
|
}
|
||||||
+5
@@ -16865,6 +16865,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt");
|
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")
|
@TestMetadata("inheritedFunctionWithDefaultParameters.kt")
|
||||||
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
||||||
|
|||||||
+5
@@ -16865,6 +16865,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt");
|
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")
|
@TestMetadata("inheritedFunctionWithDefaultParameters.kt")
|
||||||
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
||||||
|
|||||||
+5
@@ -15640,6 +15640,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/defaultArgsViaAnonymousObject.kt");
|
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")
|
@TestMetadata("inheritedFunctionWithDefaultParameters.kt")
|
||||||
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
public void testInheritedFunctionWithDefaultParameters() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
runTest("compiler/testData/codegen/box/jvm8/defaults/compatibility/inheritedFunctionWithDefaultParameters.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user