Implement support for class literals as annotation arguments...

on deserialization and reflection
This commit is contained in:
Ilya Chernikov
2018-08-27 11:49:05 +03:00
parent 68da28edb9
commit fd0eb6ee34
14 changed files with 177 additions and 11 deletions
@@ -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("$")) {
@@ -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
@@ -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");
@@ -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");
@@ -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");