Implement support for class literals as annotation arguments...
on deserialization and reflection
This commit is contained in:
+31
-6
@@ -20,16 +20,15 @@ import com.intellij.openapi.util.Ref;
|
||||
import kotlin.jvm.functions.Function4;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap;
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.FieldVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -173,7 +172,13 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return new org.jetbrains.org.objectweb.asm.AnnotationVisitor(ASM5) {
|
||||
@Override
|
||||
public void visit(String name, @NotNull Object value) {
|
||||
v.visit(name == null ? null : Name.identifier(name), value);
|
||||
if (value instanceof Type) {
|
||||
ClassLiteralId classLiteralId = resolveKotlinNameByType((Type)value, innerClasses);
|
||||
v.visitClassLiteral(Name.identifier(name), classLiteralId);
|
||||
}
|
||||
else {
|
||||
v.visit(name == null ? null : Name.identifier(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,7 +187,13 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return arv == null ? null : new org.jetbrains.org.objectweb.asm.AnnotationVisitor(ASM5) {
|
||||
@Override
|
||||
public void visit(String name, @NotNull Object value) {
|
||||
arv.visit(value);
|
||||
if (value instanceof Type) {
|
||||
ClassLiteralId classLiteralId = resolveKotlinNameByType((Type)value, innerClasses);
|
||||
arv.visitClassLiteral(classLiteralId);
|
||||
}
|
||||
else {
|
||||
arv.visit(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -270,6 +281,20 @@ public abstract class FileBasedKotlinClass implements KotlinJvmBinaryClass {
|
||||
return resolveNameByInternalName(name, innerClasses);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassLiteralId resolveKotlinNameByType(@NotNull Type type, @NotNull InnerClassesInfo innerClasses) {
|
||||
String typeDesc = type.getDescriptor();
|
||||
int nestedness = typeDesc.charAt(0) == '[' ? type.getDimensions() : 0;
|
||||
String elementDesc = nestedness == 0 ? typeDesc : type.getElementType().getDescriptor();
|
||||
JvmPrimitiveType primType = JvmPrimitiveType.getByDesc(elementDesc);
|
||||
if (primType != null) {
|
||||
return new ClassLiteralId(ClassId.topLevel(primType.getPrimitiveType().getTypeFqName()), nestedness);
|
||||
}
|
||||
ClassId javaClassId = resolveNameByDesc(elementDesc, innerClasses);
|
||||
ClassId kotlinClassId = JavaToKotlinClassMap.INSTANCE.mapJavaToKotlin(javaClassId.asSingleFqName());
|
||||
return new ClassLiteralId(kotlinClassId != null ? kotlinClassId : javaClassId, nestedness);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassId resolveNameByInternalName(@NotNull String name, @NotNull InnerClassesInfo innerClasses) {
|
||||
if (!name.contains("$")) {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package test
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class Anno(
|
||||
val klass: KClass<*>,
|
||||
val klasses: Array<KClass<*>>,
|
||||
val sarKlass: KClass<Array<String>>,
|
||||
val d2arKlass: KClass<Array<DoubleArray>>
|
||||
)
|
||||
|
||||
@Anno(
|
||||
String::class,
|
||||
arrayOf(Int::class, String::class, Float::class),
|
||||
Array<String>::class,
|
||||
Array<DoubleArray>::class
|
||||
)
|
||||
class Klass
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package test
|
||||
|
||||
public final annotation class Anno : kotlin.Annotation {
|
||||
/*primary*/ public constructor Anno(/*0*/ klass: kotlin.reflect.KClass<*>, /*1*/ klasses: kotlin.Array<kotlin.reflect.KClass<*>>, /*2*/ sarKlass: kotlin.reflect.KClass<kotlin.Array<kotlin.String>>, /*3*/ d2arKlass: kotlin.reflect.KClass<kotlin.Array<kotlin.DoubleArray>>)
|
||||
public final val d2arKlass: kotlin.reflect.KClass<kotlin.Array<kotlin.DoubleArray>>
|
||||
public final fun <get-d2arKlass>(): kotlin.reflect.KClass<kotlin.Array<kotlin.DoubleArray>>
|
||||
public final val klass: kotlin.reflect.KClass<*>
|
||||
public final fun <get-klass>(): kotlin.reflect.KClass<*>
|
||||
public final val klasses: kotlin.Array<kotlin.reflect.KClass<*>>
|
||||
public final fun <get-klasses>(): kotlin.Array<kotlin.reflect.KClass<*>>
|
||||
public final val sarKlass: kotlin.reflect.KClass<kotlin.Array<kotlin.String>>
|
||||
public final fun <get-sarKlass>(): kotlin.reflect.KClass<kotlin.Array<kotlin.String>>
|
||||
}
|
||||
|
||||
@test.Anno(d2arKlass = kotlin.Array<kotlin.DoubleArray>::class, klass = kotlin.String::class, klasses = {kotlin.Int::class, kotlin.String::class, kotlin.Float::class}, sarKlass = kotlin.Array<kotlin.String>::class) public final class Klass {
|
||||
/*primary*/ public constructor Klass()
|
||||
}
|
||||
@@ -1712,6 +1712,11 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
Generated
+5
@@ -56,6 +56,11 @@ public class LoadKotlinWithTypeTableTestGenerated extends AbstractLoadKotlinWith
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
Generated
+5
@@ -1712,6 +1712,11 @@ public class LoadJavaUsingJavacTestGenerated extends AbstractLoadJavaUsingJavacT
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
+32
-4
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
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.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.*
|
||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass.AnnotationArrayArgumentVisitor
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
@@ -30,6 +28,9 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.AnnotationDeserializer
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import java.util.*
|
||||
|
||||
@@ -103,6 +104,11 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClassLiteral(name: Name, classLiteralId: KotlinJvmBinaryClass.ClassLiteralId) {
|
||||
arguments[name] = classLiteralId.toClassValue() ?:
|
||||
ErrorValue.create("Error value of annotation argument: $name: class ${classLiteralId.classId.asSingleFqName()} not found")
|
||||
}
|
||||
|
||||
override fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name) {
|
||||
arguments[name] = EnumValue(enumClassId, enumEntryName)
|
||||
}
|
||||
@@ -119,6 +125,13 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
elements.add(EnumValue(enumClassId, enumEntryName))
|
||||
}
|
||||
|
||||
override fun visitClassLiteral(classLiteralId: KotlinJvmBinaryClass.ClassLiteralId) {
|
||||
elements.add(
|
||||
classLiteralId.toClassValue()
|
||||
?: ErrorValue.create("Error array element value of annotation argument: $name: class ${classLiteralId.classId.asSingleFqName()} not found")
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
val parameter = DescriptorResolverUtils.getAnnotationParameterByName(name, annotationClass)
|
||||
if (parameter != null) {
|
||||
@@ -150,6 +163,21 @@ class BinaryClassAnnotationAndConstantLoaderImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinJvmBinaryClass.ClassLiteralId.toClassValue(): KClassValue? =
|
||||
module.findClassAcrossModuleDependencies(this.classId)?.let { classDescriptor ->
|
||||
var currentType = classDescriptor.defaultType
|
||||
for (i in 0 until this.arrayNestedness) {
|
||||
val nextWrappedType =
|
||||
(if (i == 0) module.builtIns.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(currentType) else null)
|
||||
?: module.builtIns.getArrayType(Variance.INVARIANT, currentType)
|
||||
currentType = nextWrappedType
|
||||
}
|
||||
val kClass = resolveClass(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.kClass.toSafe()))
|
||||
val arguments = listOf(TypeProjectionImpl(currentType))
|
||||
val type = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, kClass, arguments)
|
||||
KClassValue(type)
|
||||
}
|
||||
|
||||
private fun resolveClass(classId: ClassId): ClassDescriptor {
|
||||
return module.findNonGenericClassAcrossDependencies(classId, notFoundClasses)
|
||||
}
|
||||
|
||||
@@ -53,10 +53,13 @@ interface KotlinJvmBinaryClass {
|
||||
fun visitParameterAnnotation(index: Int, classId: ClassId, source: SourceElement): AnnotationArgumentVisitor?
|
||||
}
|
||||
|
||||
class ClassLiteralId(val classId: ClassId, val arrayNestedness: Int)
|
||||
|
||||
interface AnnotationArgumentVisitor {
|
||||
// TODO: class literals
|
||||
fun visit(name: Name?, value: Any?)
|
||||
|
||||
fun visitClassLiteral(name: Name, classLiteralId: ClassLiteralId)
|
||||
|
||||
fun visitEnum(name: Name, enumClassId: ClassId, enumEntryName: Name)
|
||||
|
||||
fun visitAnnotation(name: Name, classId: ClassId): AnnotationArgumentVisitor?
|
||||
@@ -71,6 +74,8 @@ interface KotlinJvmBinaryClass {
|
||||
|
||||
fun visitEnum(enumClassId: ClassId, enumEntryName: Name)
|
||||
|
||||
fun visitClassLiteral(classLiteralId: ClassLiteralId)
|
||||
|
||||
fun visitEnd()
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -162,6 +162,10 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassLiteral(@NotNull Name name, @NotNull ClassLiteralId classLiteralId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
|
||||
@@ -233,6 +237,10 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassLiteral(@NotNull Name name, @NotNull ClassLiteralId classLiteralId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public AnnotationArrayArgumentVisitor visitArray(@NotNull Name name) {
|
||||
@@ -301,6 +309,10 @@ public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor
|
||||
public void visitEnum(@NotNull ClassId enumClassId, @NotNull Name enumEntryName) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassLiteral(@NotNull ClassLiteralId classLiteralId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
//noinspection SSBasedInspection
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
|
||||
@@ -36,16 +37,19 @@ public enum JvmPrimitiveType {
|
||||
private static final Set<FqName> WRAPPERS_CLASS_NAMES;
|
||||
private static final Map<String, JvmPrimitiveType> TYPE_BY_NAME;
|
||||
private static final Map<PrimitiveType, JvmPrimitiveType> TYPE_BY_PRIMITIVE_TYPE;
|
||||
private static final Map<String, JvmPrimitiveType> TYPE_BY_DESC;
|
||||
|
||||
static {
|
||||
WRAPPERS_CLASS_NAMES = new HashSet<FqName>();
|
||||
TYPE_BY_NAME = new HashMap<String, JvmPrimitiveType>();
|
||||
TYPE_BY_PRIMITIVE_TYPE = new EnumMap<PrimitiveType, JvmPrimitiveType>(PrimitiveType.class);
|
||||
TYPE_BY_DESC = new HashMap<String, JvmPrimitiveType>();
|
||||
|
||||
for (JvmPrimitiveType type : values()) {
|
||||
WRAPPERS_CLASS_NAMES.add(type.getWrapperFqName());
|
||||
TYPE_BY_NAME.put(type.getJavaKeywordName(), type);
|
||||
TYPE_BY_PRIMITIVE_TYPE.put(type.getPrimitiveType(), type);
|
||||
TYPE_BY_DESC.put(type.getDesc(), type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +71,11 @@ public enum JvmPrimitiveType {
|
||||
return TYPE_BY_PRIMITIVE_TYPE.get(type);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JvmPrimitiveType getByDesc(@NotNull String desc) {
|
||||
return TYPE_BY_DESC.get(desc);
|
||||
}
|
||||
|
||||
private final PrimitiveType primitiveType;
|
||||
private final String name;
|
||||
private final String desc;
|
||||
|
||||
+22
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.load.kotlin.header.ReadKotlinClassHeaderAnnotationVisitor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
@@ -183,9 +184,25 @@ private object ReflectClassStructure {
|
||||
visitor.visitEnd()
|
||||
}
|
||||
|
||||
private fun Class<*>.classLiteralId(): KotlinJvmBinaryClass.ClassLiteralId {
|
||||
var currentClass = this
|
||||
var nestedness = 0
|
||||
while (currentClass.isArray) {
|
||||
nestedness++
|
||||
currentClass = currentClass.componentType
|
||||
}
|
||||
val classId =
|
||||
if (!currentClass.isPrimitive) currentClass.classId
|
||||
else ClassId.topLevel(JvmPrimitiveType.get(currentClass.name).primitiveType.typeFqName)
|
||||
return KotlinJvmBinaryClass.ClassLiteralId(classId, nestedness)
|
||||
}
|
||||
|
||||
private fun processAnnotationArgumentValue(visitor: KotlinJvmBinaryClass.AnnotationArgumentVisitor, name: Name, value: Any) {
|
||||
val clazz = value::class.java
|
||||
when {
|
||||
clazz == Class::class.java -> {
|
||||
visitor.visitClassLiteral(name, (value as Class<*>).classLiteralId())
|
||||
}
|
||||
clazz in TYPES_ELIGIBLE_FOR_SIMPLE_VISIT -> {
|
||||
visitor.visit(name, value)
|
||||
}
|
||||
@@ -208,6 +225,11 @@ private object ReflectClassStructure {
|
||||
v.visitEnum(enumClassId, Name.identifier((element as Enum<*>).name))
|
||||
}
|
||||
}
|
||||
else if (componentType == Class::class.java) {
|
||||
for (element in value as Array<*>) {
|
||||
v.visitClassLiteral((element as Class<*>).classLiteralId())
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (element in value as Array<*>) {
|
||||
v.visit(element)
|
||||
|
||||
+5
@@ -58,6 +58,11 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
Generated
+5
@@ -56,6 +56,11 @@ public class LoadJavaClsStubTestGenerated extends AbstractLoadJavaClsStubTest {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
@@ -56,6 +56,11 @@ public class ResolveByStubTestGenerated extends AbstractResolveByStubTest {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/AnnotationInAnnotationArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassLiteralArguments.kt")
|
||||
public void testClassLiteralArguments() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/ClassLiteralArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("EnumArgumentWithCustomToString.kt")
|
||||
public void testEnumArgumentWithCustomToString() throws Exception {
|
||||
runTest("compiler/testData/loadJava/compiledKotlin/annotations/EnumArgumentWithCustomToString.kt");
|
||||
|
||||
Reference in New Issue
Block a user