diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 3ab39633f1c..1c202339f0c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -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 diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index e41a79987a7..237252adcf2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2782,7 +2782,7 @@ public class ExpressionCodegen extends JetVisitor 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()); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index dd895d32f49..7fcbc239c00 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -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); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 42692cd6b03..3e22b1d2abb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -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 { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index ddb274dd1f1..f3913f22962 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -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; diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java index ca83173a1c9..99bdaacfe65 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java @@ -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"); diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/Test.java b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/Test.java new file mode 100644 index 00000000000..fc6ae917a57 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/Test.java @@ -0,0 +1,6 @@ +class O {} +class K {} + +@Ann(args={O.class, K.class}) +class Test { +} diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/array.kt b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/array.kt new file mode 100644 index 00000000000..0f8718e1bc0 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/array/array.kt @@ -0,0 +1,13 @@ +import kotlin.reflect.KClass +import java.lang.annotation.* + +Retention(RetentionPolicy.RUNTIME) +annotation +class Ann(val args: Array>) + +fun box(): String { + val args = javaClass().getAnnotation(javaClass()).args + val argName1 = args[0].simpleName ?: "fail 1" + val argName2 = args[1].simpleName ?: "fail 2" + return argName1 + argName2 +} diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/Test.java b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/Test.java new file mode 100644 index 00000000000..3c55129f786 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/Test.java @@ -0,0 +1,5 @@ +class OK {} + +@Ann(arg=OK.class) +class Test { +} diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/basic.kt b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/basic.kt new file mode 100644 index 00000000000..b0c937dff79 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/basic/basic.kt @@ -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().getAnnotation(javaClass()).arg.simpleName ?: "fail 1" + return argName +} diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/Test.java b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/Test.java new file mode 100644 index 00000000000..fc6ae917a57 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/Test.java @@ -0,0 +1,6 @@ +class O {} +class K {} + +@Ann(args={O.class, K.class}) +class Test { +} diff --git a/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/vararg.kt b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/vararg.kt new file mode 100644 index 00000000000..d22a733d399 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/annotationsWithKClass/vararg/vararg.kt @@ -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().getAnnotation(javaClass()).args + val argName1 = args[0].simpleName ?: "fail 1" + val argName2 = args[1].simpleName ?: "fail 2" + return argName1 + argName2 +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/array.kt b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/array.kt new file mode 100644 index 00000000000..baf6024ecf4 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/array.kt @@ -0,0 +1,18 @@ +import kotlin.reflect.KClass +import java.lang.annotation.* + +Retention(RetentionPolicy.RUNTIME) +annotation +class Ann(val args: Array>) + +class O +class K + +Ann(array(O::class, K::class)) class MyClass + +fun box(): String { + val args = javaClass().getAnnotation(javaClass()).args + val argName1 = args[0].simpleName ?: "fail 1" + val argName2 = args[1].simpleName ?: "fail 2" + return argName1 + argName2 +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/basic.kt b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/basic.kt new file mode 100644 index 00000000000..eb42b6d11b8 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/basic.kt @@ -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().getAnnotation(javaClass()).arg.simpleName ?: "fail 1" + return argName +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/checkcast.kt b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/checkcast.kt new file mode 100644 index 00000000000..2af5caa9947 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/checkcast.kt @@ -0,0 +1,10 @@ +import kotlin.reflect.KClass + +fun box(): String { + try { + javaClass() as KClass + } catch (e: Exception) { + return "OK" + } + return "fail" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/vararg.kt b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/vararg.kt new file mode 100644 index 00000000000..5033ea07085 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation/vararg.kt @@ -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().getAnnotation(javaClass()).args + val argName1 = args[0].simpleName ?: "fail 1" + val argName2 = args[1].simpleName ?: "fail 2" + return argName1 + argName2 +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index acc882fde24..361e89009b9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index dd93ba61562..a253f7c39f1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -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) diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java index f6da8f40534..2531d5de3f3 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java @@ -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); }