JVM: Support using KClass as annotation parameter type

This commit is contained in:
Denis Zharkov
2015-04-07 17:27:15 +03:00
parent 82674e56b9
commit cc17f7d82d
19 changed files with 248 additions and 10 deletions
@@ -852,6 +852,15 @@ public class AsmUtil {
}
}
public static void wrapJavaClassIntoKClass(@NotNull InstructionAdapter v) {
v.invokestatic(REFLECTION, "foreignKotlinClass", Type.getMethodDescriptor(K_CLASS_TYPE, getType(Class.class)), false);
}
public static void wrapJavaClassesIntoKClasses(@NotNull InstructionAdapter v) {
v.invokestatic(REFLECTION, "foreignKotlinClasses", Type.getMethodDescriptor(K_CLASS_ARRAY_TYPE, getType(Class[].class)), false);
}
public static int getReceiverIndex(@NotNull CodegenContext context, @NotNull CallableMemberDescriptor descriptor) {
OwnerKind kind = context.getContextKind();
//Trait always should have this descriptor
@@ -2782,7 +2782,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
if (descriptor instanceof JavaClassDescriptor || module == module.getBuiltIns().getBuiltInsModule()) {
putJavaLangClassInstance(v, classAsmType);
v.invokestatic(REFLECTION, "foreignKotlinClass", Type.getMethodDescriptor(K_CLASS_TYPE, getType(Class.class)), false);
wrapJavaClassIntoKClass(v);
}
else {
v.getstatic(classAsmType.getInternalName(), JvmAbi.KOTLIN_CLASS_FIELD_NAME, K_CLASS_TYPE.getDescriptor());
@@ -166,7 +166,7 @@ public class PropertyCodegen {
}
public void generateConstructorPropertyAsMethodForAnnotationClass(JetParameter p, PropertyDescriptor descriptor) {
Type type = typeMapper.mapType(descriptor);
Type type = typeMapper.mapAnnotationParameterType(descriptor);
String name = p.getName();
assert name != null : "Annotation parameter has no name: " + p.getText();
MethodVisitor mv = v.newMethod(OtherOrigin(p, descriptor), ACC_PUBLIC | ACC_ABSTRACT, name, "()" + type.getDescriptor(), null, null);
@@ -347,12 +347,23 @@ public abstract class StackValue {
}
}
else if (toType.getSort() == Type.ARRAY) {
v.checkcast(toType);
if (fromType.getSort() == Type.ARRAY &&
fromType.getElementType().equals(AsmTypes.JAVA_CLASS_TYPE) && toType.equals(K_CLASS_ARRAY_TYPE)) {
wrapJavaClassesIntoKClasses(v);
}
else {
v.checkcast(toType);
}
}
else if (toType.getSort() == Type.OBJECT) {
if (fromType.getSort() == Type.OBJECT || fromType.getSort() == Type.ARRAY) {
if (!toType.equals(OBJECT_TYPE)) {
v.checkcast(toType);
if (fromType.equals(AsmTypes.JAVA_CLASS_TYPE) && toType.equals(AsmTypes.K_CLASS_TYPE)) {
wrapJavaClassIntoKClass(v);
}
else {
v.checkcast(toType);
}
}
}
else {
@@ -107,7 +107,21 @@ public class JetTypeMapper {
* kotlin.Int is mapped to Ljava/lang/Integer;
* No projections allowed in immediate arguments
*/
SUPER_TYPE
SUPER_TYPE,
/**
* kotlin.reflect.KClass mapped to java.lang.Class
* Other types mapped as VALUE
*/
VALUE_FOR_ANNOTATION,
/**
* kotlin.reflect.KClass mapped to java.lang.Class
* Other types mapped as TYPE_PARAMETER
*/
TYPE_PARAMETER_FOR_ANNOTATION;
boolean isForAnnotation() {
return this == VALUE_FOR_ANNOTATION || this == TYPE_PARAMETER_FOR_ANNOTATION;
}
}
@NotNull
@@ -186,6 +200,10 @@ public class JetTypeMapper {
//noinspection ConstantConditions
return mapType(descriptor.getReturnType(), sw, JetTypeMapperMode.TYPE_PARAMETER);
}
else if (DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration())) {
//noinspection ConstantConditions
return mapType(descriptor.getReturnType(), sw, JetTypeMapperMode.VALUE_FOR_ANNOTATION);
}
else {
return mapType(returnType, sw, JetTypeMapperMode.VALUE, Variance.OUT_VARIANCE);
}
@@ -222,6 +240,11 @@ public class JetTypeMapper {
return mapType(descriptor.getReturnType());
}
@NotNull
public Type mapAnnotationParameterType(@NotNull PropertyDescriptor descriptor) {
return mapType(descriptor.getType(), null, JetTypeMapperMode.VALUE_FOR_ANNOTATION);
}
@NotNull
public Type mapType(@NotNull ClassifierDescriptor descriptor) {
return mapType(descriptor.getDefaultType());
@@ -251,10 +274,11 @@ public class JetTypeMapper {
boolean projectionsAllowed = kind != JetTypeMapperMode.SUPER_TYPE;
if (known != null) {
if (kind == JetTypeMapperMode.VALUE) {
if (kind == JetTypeMapperMode.VALUE || kind == JetTypeMapperMode.VALUE_FOR_ANNOTATION) {
return mapKnownAsmType(jetType, known, signatureVisitor, howThisTypeIsUsed);
}
else if (kind == JetTypeMapperMode.TYPE_PARAMETER || kind == JetTypeMapperMode.SUPER_TYPE) {
else if (kind == JetTypeMapperMode.TYPE_PARAMETER || kind == JetTypeMapperMode.SUPER_TYPE ||
kind == JetTypeMapperMode.TYPE_PARAMETER_FOR_ANNOTATION) {
return mapKnownAsmType(jetType, boxType(known), signatureVisitor, howThisTypeIsUsed, projectionsAllowed);
}
else if (kind == JetTypeMapperMode.IMPL) {
@@ -307,7 +331,10 @@ public class JetTypeMapper {
arrayElementType = boxType(mapType(memberType, kind));
if (signatureVisitor != null) {
signatureVisitor.writeArrayType();
mapType(memberType, signatureVisitor, JetTypeMapperMode.TYPE_PARAMETER, memberProjection.getProjectionKind());
JetTypeMapperMode newMode = kind.isForAnnotation() ?
JetTypeMapperMode.TYPE_PARAMETER_FOR_ANNOTATION :
JetTypeMapperMode.TYPE_PARAMETER;
mapType(memberType, signatureVisitor, newMode, memberProjection.getProjectionKind());
signatureVisitor.writeArrayEnd();
}
}
@@ -325,9 +352,14 @@ public class JetTypeMapper {
return asmType;
}
}
if (descriptor instanceof ClassDescriptor) {
if (kind.isForAnnotation() && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor)) {
if (signatureVisitor != null) {
signatureVisitor.writeAsmType(AsmTypes.JAVA_CLASS_TYPE);
}
return AsmTypes.JAVA_CLASS_TYPE;
}
Type asmType = computeAsmType((ClassDescriptor) descriptor.getOriginal());
writeGenericType(signatureVisitor, asmType, jetType, howThisTypeIsUsed, projectionsAllowed);
return asmType;
@@ -28,12 +28,14 @@ public class AsmTypes {
public static final Type OBJECT_TYPE = getType(Object.class);
public static final Type JAVA_STRING_TYPE = getType(String.class);
public static final Type JAVA_THROWABLE_TYPE = getType(Throwable.class);
public static final Type JAVA_CLASS_TYPE = getType(Class.class);
public static final Type UNIT_TYPE = Type.getObjectType("kotlin/Unit");
public static final Type PROPERTY_METADATA_TYPE = Type.getObjectType("kotlin/PropertyMetadata");
public static final Type PROPERTY_METADATA_IMPL_TYPE = Type.getObjectType("kotlin/PropertyMetadataImpl");
public static final Type K_CLASS_TYPE = reflect("KClass");
public static final Type K_CLASS_ARRAY_TYPE = Type.getObjectType("[" + K_CLASS_TYPE.getDescriptor());
public static final Type K_PACKAGE_TYPE = reflect("KPackage");
public static final Type K_MEMBER_PROPERTY_TYPE = reflect("KMemberProperty");
@@ -0,0 +1,6 @@
class O {}
class K {}
@Ann(args={O.class, K.class})
class Test {
}
@@ -0,0 +1,13 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(val args: Array<KClass<*>>)
fun box(): String {
val args = javaClass<Test>().getAnnotation(javaClass<Ann>()).args
val argName1 = args[0].simpleName ?: "fail 1"
val argName2 = args[1].simpleName ?: "fail 2"
return argName1 + argName2
}
@@ -0,0 +1,5 @@
class OK {}
@Ann(arg=OK.class)
class Test {
}
@@ -0,0 +1,11 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(val arg: KClass<*>)
fun box(): String {
val argName = javaClass<Test>().getAnnotation(javaClass<Ann>()).arg.simpleName ?: "fail 1"
return argName
}
@@ -0,0 +1,6 @@
class O {}
class K {}
@Ann(args={O.class, K.class})
class Test {
}
@@ -0,0 +1,13 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(vararg val args: KClass<*>)
fun box(): String {
val args = javaClass<Test>().getAnnotation(javaClass<Ann>()).args
val argName1 = args[0].simpleName ?: "fail 1"
val argName2 = args[1].simpleName ?: "fail 2"
return argName1 + argName2
}
@@ -0,0 +1,18 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(val args: Array<KClass<*>>)
class O
class K
Ann(array(O::class, K::class)) class MyClass
fun box(): String {
val args = javaClass<MyClass>().getAnnotation(javaClass<Ann>()).args
val argName1 = args[0].simpleName ?: "fail 1"
val argName2 = args[1].simpleName ?: "fail 2"
return argName1 + argName2
}
@@ -0,0 +1,15 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(val arg: KClass<*>)
class OK
Ann(OK::class) class MyClass
fun box(): String {
val argName = javaClass<MyClass>().getAnnotation(javaClass<Ann>()).arg.simpleName ?: "fail 1"
return argName
}
@@ -0,0 +1,10 @@
import kotlin.reflect.KClass
fun box(): String {
try {
javaClass<String>() as KClass<String>
} catch (e: Exception) {
return "OK"
}
return "fail"
}
@@ -0,0 +1,18 @@
import kotlin.reflect.KClass
import java.lang.annotation.*
Retention(RetentionPolicy.RUNTIME)
annotation
class Ann(vararg val args: KClass<*>)
class O
class K
Ann(O::class, K::class) class MyClass
fun box(): String {
val args = javaClass<MyClass>().getAnnotation(javaClass<Ann>()).args
val argName1 = args[0].simpleName ?: "fail 1"
val argName2 = args[1].simpleName ?: "fail 2"
return argName1 + argName2
}
@@ -59,6 +59,34 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
doTestWithJava(fileName);
}
@TestMetadata("compiler/testData/codegen/boxWithJava/annotationsWithKClass")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AnnotationsWithKClass extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInAnnotationsWithKClass() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/annotationsWithKClass"), Pattern.compile("^([^\\.]+)$"), true);
}
@TestMetadata("array")
public void testArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/");
doTestWithJava(fileName);
}
@TestMetadata("basic")
public void testBasic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/");
doTestWithJava(fileName);
}
@TestMetadata("vararg")
public void testVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/");
doTestWithJava(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithJava/builtinStubMethods")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2878,6 +2878,39 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class KClassInAnnotation extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInKClassInAnnotation() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/array.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("basic.kt")
public void testBasic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/basic.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("checkcast.kt")
public void testCheckcast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/checkcast.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("vararg.kt")
public void testVararg() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/vararg.kt");
doTestWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -43,6 +43,14 @@ public class Reflection {
return factory.createKotlinClass(javaClass);
}
public static KClass[] foreignKotlinClasses(Class[] javaClasses) {
KClass[] kClasses = new KClass[javaClasses.length];
for (int i = 0; i < javaClasses.length; i++) {
kClasses[i] = foreignKotlinClass(javaClasses[i]);
}
return kClasses;
}
public static KPackage createKotlinPackage(Class javaClass) {
return factory.createKotlinPackage(javaClass);
}