Java to Kotlin annotation retention mapping + new test + JvmLoader test adaptation

This commit is contained in:
Mikhail Glukhikh
2015-07-20 16:09:43 +03:00
parent c3c02e49c9
commit c2480d1183
13 changed files with 316 additions and 39 deletions
@@ -21,10 +21,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationArgumentVisitor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget;
import org.jetbrains.kotlin.descriptors.annotations.*;
import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.resolve.AnnotationTargetChecker;
@@ -371,16 +368,13 @@ public abstract class AnnotationCodegen {
value.accept(argumentVisitor, null);
}
private enum KotlinRetention {
SOURCE(RetentionPolicy.SOURCE),
BINARY(RetentionPolicy.CLASS),
RUNTIME(RetentionPolicy.RUNTIME);
private static final Map<AnnotationRetention, RetentionPolicy> annotationRetentionMap =
new EnumMap<AnnotationRetention, RetentionPolicy>(AnnotationRetention.class);
final RetentionPolicy mapped;
KotlinRetention(RetentionPolicy mapped) {
this.mapped = mapped;
}
static {
annotationRetentionMap.put(AnnotationRetention.SOURCE, RetentionPolicy.SOURCE);
annotationRetentionMap.put(AnnotationRetention.BINARY, RetentionPolicy.CLASS);
annotationRetentionMap.put(AnnotationRetention.RUNTIME, RetentionPolicy.RUNTIME);
}
@Nullable
@@ -421,10 +415,8 @@ public abstract class AnnotationCodegen {
JetType classObjectType = getClassObjectType(enumEntry);
if (classObjectType != null) {
if ("kotlin/annotation/AnnotationRetention".equals(typeMapper.mapType(classObjectType).getInternalName())) {
String entryName = enumEntry.getName().asString();
for (KotlinRetention retention : KotlinRetention.values()) {
if (retention.name().equals(entryName)) return retention.mapped;
}
AnnotationRetention retention = AnnotationRetention.valueOf(enumEntry.getName().asString());
return annotationRetentionMap.get(retention);
}
}
}
@@ -1,8 +1,8 @@
import java.lang.annotation.*
@java.lang.annotation.Retention(RetentionPolicy.CLASS)
<!DEPRECATED_JAVA_ANNOTATION!>@java.lang.annotation.Retention(RetentionPolicy.CLASS)<!>
annotation class my
Retention(RetentionPolicy.RUNTIME)
<!DEPRECATED_JAVA_ANNOTATION!>Retention(RetentionPolicy.RUNTIME)<!>
<!DEPRECATED_JAVA_ANNOTATION!>Target(ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR)<!>
annotation class my1
@@ -0,0 +1,35 @@
// FILE: AnnotationRetentions.java
import java.lang.annotation.*;
public class AnnotationRetentions {
public @interface BaseAnnotation {
}
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {
}
@Retention(RetentionPolicy.CLASS)
public @interface BinaryAnnotation {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {
}
}
// FILE: AnnotationRetentions.kt
AnnotationRetentions.BaseAnnotation class BaseClass
AnnotationRetentions.SourceAnnotation class SourceClass
AnnotationRetentions.BinaryAnnotation class BinaryClass
AnnotationRetentions.RuntimeAnnotation class RuntimeClass
@@ -0,0 +1,64 @@
package
public open class AnnotationRetentions {
public constructor AnnotationRetentions()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final class BaseAnnotation : kotlin.Annotation {
public constructor BaseAnnotation()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
kotlin.annotation.annotation(retention = AnnotationRetention.BINARY) public final class BinaryAnnotation : kotlin.Annotation {
public constructor BinaryAnnotation()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
kotlin.annotation.annotation(retention = AnnotationRetention.RUNTIME) public final class RuntimeAnnotation : kotlin.Annotation {
public constructor RuntimeAnnotation()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
kotlin.annotation.annotation(retention = AnnotationRetention.SOURCE) public final class SourceAnnotation : kotlin.Annotation {
public constructor SourceAnnotation()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
AnnotationRetentions.BaseAnnotation() internal final class BaseClass {
public constructor BaseClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
AnnotationRetentions.BinaryAnnotation() internal final class BinaryClass {
public constructor BinaryClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
AnnotationRetentions.RuntimeAnnotation() internal final class RuntimeClass {
public constructor RuntimeClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
AnnotationRetentions.SourceAnnotation() internal final class SourceClass {
public constructor SourceClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,43 @@
// SKIP_IN_RUNTIME_TEST
package test;
import java.lang.annotation.*;
public class AnnotationRetentions {
public @interface BaseAnnotation {
}
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation {
}
@Retention(RetentionPolicy.CLASS)
public @interface BinaryAnnotation {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation {
}
@BaseAnnotation class BaseClass {
}
@SourceAnnotation class SourceClass {
}
@BinaryAnnotation class BinaryClass {
}
@RuntimeAnnotation class RuntimeClass {
}
}
@@ -0,0 +1,37 @@
package test
public open class AnnotationRetentions {
public constructor AnnotationRetentions()
public final class BaseAnnotation : kotlin.Annotation {
public constructor BaseAnnotation()
}
test.AnnotationRetentions.BaseAnnotation() public/*package*/ open inner class BaseClass {
public/*package*/ constructor BaseClass()
}
kotlin.annotation.annotation(retention = AnnotationRetention.BINARY) public final class BinaryAnnotation : kotlin.Annotation {
public constructor BinaryAnnotation()
}
test.AnnotationRetentions.BinaryAnnotation() public/*package*/ open inner class BinaryClass {
public/*package*/ constructor BinaryClass()
}
kotlin.annotation.annotation(retention = AnnotationRetention.RUNTIME) public final class RuntimeAnnotation : kotlin.Annotation {
public constructor RuntimeAnnotation()
}
test.AnnotationRetentions.RuntimeAnnotation() public/*package*/ open inner class RuntimeClass {
public/*package*/ constructor RuntimeClass()
}
kotlin.annotation.annotation(retention = AnnotationRetention.SOURCE) public final class SourceAnnotation : kotlin.Annotation {
public constructor SourceAnnotation()
}
public/*package*/ open inner class SourceClass {
public/*package*/ constructor SourceClass()
}
}
@@ -1016,6 +1016,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("javaretention.kt")
public void testJavaretention() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/options/javaretention.kt");
doTest(fileName);
}
@TestMetadata("repeatable.kt")
public void testRepeatable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/options/repeatable.kt");
@@ -327,6 +327,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
doTestCompiledJava(fileName);
}
@TestMetadata("AnnotationRetentions.java")
public void testAnnotationRetentions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationRetentions.java");
doTestCompiledJava(fileName);
}
@TestMetadata("AnnotationTargets.java")
public void testAnnotationTargets() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java");
@@ -3378,6 +3378,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD
doTest(fileName);
}
@TestMetadata("AnnotationRetentions.java")
public void testAnnotationRetentions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationRetentions.java");
doTest(fileName);
}
@TestMetadata("AnnotationTargets.java")
public void testAnnotationTargets() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/annotations/AnnotationTargets.java");
@@ -39,6 +39,7 @@ public final class JvmAnnotationNames {
public static final String DATA_FIELD_NAME = "data";
public static final Name DEFAULT_ANNOTATION_MEMBER_NAME = Name.identifier("value");
public static final Name TARGET_ANNOTATION_MEMBER_NAME = Name.identifier("allowedTargets");
public static final Name RETENTION_ANNOTATION_PARAMETER_NAME = Name.identifier("retention");
public static final FqName JETBRAINS_NOT_NULL_ANNOTATION = new FqName("org.jetbrains.annotations.NotNull");
public static final FqName JETBRAINS_NULLABLE_ANNOTATION = new FqName("org.jetbrains.annotations.Nullable");
@@ -37,47 +37,79 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.descriptors.annotations.AnnotationTarget
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.descriptors.annotations.AnnotationRetention
import org.jetbrains.kotlin.storage.StorageManager
import java.lang.annotation.Retention
import java.lang.annotation.Target
import java.util.*
import kotlin.annotation
public object JavaAnnotationMapper {
private val targetClassId = ClassId.topLevel(FqName(javaClass<Target>().getCanonicalName()))
private val javaTargetFqName = FqName(javaClass<Target>().canonicalName)
private val javaRetentionFqName = FqName(javaClass<Retention>().canonicalName)
public fun mapJavaAnnotation(annotation: JavaAnnotation, c: LazyJavaResolverContext): AnnotationDescriptor? =
when (annotation.classId) {
targetClassId -> JavaTargetAnnotationDescriptor(annotation, c, c.module.builtIns)
ClassId.topLevel(javaTargetFqName) -> JavaTargetAnnotationDescriptor(annotation, c)
ClassId.topLevel(javaRetentionFqName) -> JavaRetentionAnnotationDescriptor(annotation, c)
else -> null
}
public val kotlinToJavaNameMap: Map<FqName, FqName> =
mapOf(KotlinBuiltIns.FQ_NAMES.target to FqName(javaClass<Target>().getCanonicalName()))
mapOf(KotlinBuiltIns.FQ_NAMES.target to javaTargetFqName,
KotlinBuiltIns.FQ_NAMES.annotation to javaRetentionFqName)
public val javaToKotlinNameMap: Map<FqName, FqName> = kotlinToJavaNameMap.map { it.value to it.key }.toMap()
public val javaToKotlinNameMap: Map<FqName, FqName> =
mapOf(javaTargetFqName to KotlinBuiltIns.FQ_NAMES.target,
javaRetentionFqName to KotlinBuiltIns.FQ_NAMES.annotation)
}
class JavaTargetAnnotationDescriptor(
abstract class AbstractJavaAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext,
val builtIns: KotlinBuiltIns
private val kotlinAnnotationClassDescriptor: ClassDescriptor
): AnnotationDescriptor {
override fun getType() = kotlinAnnotationClassDescriptor.defaultType
protected val valueParameters: List<ValueParameterDescriptor>
get() = kotlinAnnotationClassDescriptor.constructors.single().valueParameters
protected val firstArgument: JavaAnnotationArgument? = annotation.arguments.firstOrNull()
}
class JavaRetentionAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext
): AbstractJavaAnnotationDescriptor(annotation, c.module.builtIns.annotationAnnotation) {
private val valueArguments = c.storageManager.createLazyValue {
val firstArgument = annotation.arguments.firstOrNull()
val targetArgument = when (firstArgument) {
is JavaArrayAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(firstArgument.getElements(), builtIns)
is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(listOf(firstArgument), builtIns)
is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaRetentionArgument(firstArgument, c.module.builtIns)
else -> return@createLazyValue emptyMap<ValueParameterDescriptor, ConstantValue<*>>()
}
val parameterDescriptor = builtIns.targetAnnotation.constructors.firstOrNull()?.valueParameters?.firstOrNull()
val parameterDescriptor = valueParameters.firstOrNull {
it.name == JvmAnnotationNames.RETENTION_ANNOTATION_PARAMETER_NAME
}
parameterDescriptor?.let { mapOf(it to targetArgument) } ?: emptyMap()
}
override fun getAllValueArguments() = valueArguments()
}
override fun getType() = builtIns.targetAnnotation.defaultType
class JavaTargetAnnotationDescriptor(
annotation: JavaAnnotation,
c: LazyJavaResolverContext
): AbstractJavaAnnotationDescriptor(annotation, c.module.builtIns.targetAnnotation) {
private val valueArguments = c.storageManager.createLazyValue {
val targetArgument = when (firstArgument) {
is JavaArrayAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(firstArgument.getElements(), c.module.builtIns)
is JavaEnumValueAnnotationArgument -> JavaAnnotationTargetMapper.mapJavaTargetArguments(listOf(firstArgument), c.module.builtIns)
else -> return@createLazyValue emptyMap<ValueParameterDescriptor, ConstantValue<*>>()
}
mapOf(valueParameters.single() to targetArgument)
}
override fun getAllValueArguments() = valueArguments()
}
public object JavaAnnotationTargetMapper {
@@ -101,11 +133,25 @@ public object JavaAnnotationTargetMapper {
// Map arguments: java.lang.annotation.Target -> kotlin.annotation.target
val kotlinTargets = arguments.filterIsInstance<JavaEnumValueAnnotationArgument>()
.flatMap { mapJavaTargetArgumentByName(it.resolve()?.name?.asString()) }
.map { builtIns.getAnnotationTargetEnumEntry(Name.identifier(it.name())) }
.map { builtIns.getAnnotationTargetEnumEntry(it) }
.filterNotNull()
.map { EnumValue(it) }
val parameterDescriptor = DescriptorResolverUtils.getAnnotationParameterByName(JvmAnnotationNames.TARGET_ANNOTATION_MEMBER_NAME,
builtIns.targetAnnotation)
return ArrayValue(kotlinTargets, parameterDescriptor?.type ?: ErrorUtils.createErrorType("Error: AnnotationTarget[]"), builtIns)
}
private val retentionNameList = mapOf("RUNTIME" to AnnotationRetention.RUNTIME,
"CLASS" to AnnotationRetention.BINARY,
"SOURCE" to AnnotationRetention.SOURCE
)
public fun mapJavaRetentionArgument(element: JavaAnnotationArgument, builtIns: KotlinBuiltIns): ConstantValue<*>? {
// Map argument: java.lang.annotation.Retention -> kotlin.annotation.annotation
return (element as? JavaEnumValueAnnotationArgument)?.let {
retentionNameList[it.resolve()?.name?.asString()]?.let {
(builtIns.getAnnotationRetentionEnumEntry(it) as? ClassDescriptor)?.let { EnumValue(it) }
}
}
}
}
@@ -22,10 +22,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.functions.BuiltInFictitiousFunctionClassFactory;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
import org.jetbrains.kotlin.descriptors.annotations.*;
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.name.FqName;
@@ -182,6 +179,7 @@ public class KotlinBuiltIns {
public final FqName target = annotationName("target");
public final FqName annotation = annotationName("annotation");
public final FqName annotationTarget = annotationName("AnnotationTarget");
public final FqName annotationRetention = annotationName("AnnotationRetention");
public final FqNameUnsafe kClass = new FqName("kotlin.reflect.KClass").toUnsafe();
@@ -400,11 +398,31 @@ public class KotlinBuiltIns {
}
@Nullable
public ClassDescriptor getAnnotationTargetEnumEntry(@NotNull Name name) {
ClassifierDescriptor result = getAnnotationTargetEnum().getUnsubstitutedInnerClassesScope().getClassifier(name, UsageLocation.NO_LOCATION);
public ClassDescriptor getAnnotationTargetEnumEntry(@NotNull AnnotationTarget target) {
ClassifierDescriptor result = getAnnotationTargetEnum().getUnsubstitutedInnerClassesScope().getClassifier(
Name.identifier(target.name()), UsageLocation.NO_LOCATION
);
return result instanceof ClassDescriptor ? (ClassDescriptor) result : null;
}
@NotNull
public ClassDescriptor getAnnotationRetentionEnum() {
return getAnnotationClassByName(FQ_NAMES.annotationRetention.shortName());
}
@Nullable
public ClassDescriptor getAnnotationRetentionEnumEntry(@NotNull AnnotationRetention retention) {
ClassifierDescriptor result = getAnnotationRetentionEnum().getUnsubstitutedInnerClassesScope().getClassifier(
Name.identifier(retention.name()), UsageLocation.NO_LOCATION
);
return result instanceof ClassDescriptor ? (ClassDescriptor) result : null;
}
@NotNull
public ClassDescriptor getAnnotationAnnotation() {
return getAnnotationClassByName(FQ_NAMES.annotation.shortName());
}
@NotNull
public ClassDescriptor getString() {
return getBuiltInClassByName("String");
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.descriptors.annotations
public enum class AnnotationRetention {
RUNTIME,
BINARY,
SOURCE
}