JVM: Support using KClass as annotation parameter type
This commit is contained in:
@@ -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
|
||||
}
|
||||
+28
@@ -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)
|
||||
|
||||
+33
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user