Support mapping of KType to j.l.reflect.Type

This commit is contained in:
Alexander Udalov
2015-08-04 22:29:54 +03:00
parent 49b689f883
commit d3e39812cb
20 changed files with 397 additions and 23 deletions
@@ -0,0 +1,11 @@
public class J {
private final int param;
public J(int param) {
this.param = param;
}
public String foo(int[] arr, Object[] arr2, Integer y) {
return "" + param + arr[0] + arr2[0] + y;
}
}
@@ -0,0 +1,15 @@
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
fun box(): String {
val f = J::foo
assertEquals(
listOf(javaClass<J>(), javaClass<IntArray>(), javaClass<Array<Any>>(), javaClass<Integer>()),
f.parameters.map { it.type.javaType }
)
assertEquals(javaClass<String>(), f.returnType.javaType)
assertEquals("01A2", f.call(J(0), intArrayOf(1), arrayOf("A"), 2))
return "OK"
}
@@ -0,0 +1,5 @@
public class J {
public static String foo(int x, int[] arr, Object[] arr2) {
return "" + x + arr[0] + arr2[0];
}
}
@@ -0,0 +1,12 @@
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
fun box(): String {
val f = J::foo
assertEquals(listOf(Integer.TYPE, javaClass<IntArray>(), javaClass<Array<Any>>()), f.parameters.map { it.type.javaType })
assertEquals(javaClass<String>(), f.returnType.javaType)
assertEquals("01A", f.call(0, intArrayOf(1), arrayOf("A")))
return "OK"
}
@@ -0,0 +1,30 @@
// FULL_JDK
import java.lang.reflect.GenericArrayType
import java.lang.reflect.TypeVariable
import java.lang.reflect.ParameterizedType
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
fun foo(strings: Array<String>, integers: Array<Int>, objectArrays: Array<Array<Any>>) {}
fun bar(): Array<List<String>> = null!!
class A<T> {
fun baz(): Array<T> = null!!
}
fun box(): String {
assertEquals(javaClass<Array<String>>(), ::foo.parameters[0].type.javaType)
assertEquals(javaClass<Array<Int>>(), ::foo.parameters[1].type.javaType)
assertEquals(javaClass<Array<Array<Any>>>(), ::foo.parameters[2].type.javaType)
val g = ::bar.returnType.javaType
if (g !is GenericArrayType || g.genericComponentType !is ParameterizedType)
return "Fail: should be array of parameterized type, but was $g (${g.javaClass})"
val h = A<String>::baz.returnType.javaType
if (h !is GenericArrayType || h.genericComponentType !is TypeVariable<*>)
return "Fail: should be array of type variable, but was $h (${h.javaClass})"
return "OK"
}
@@ -0,0 +1,20 @@
import kotlin.reflect.*
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
class A(d: Double, s: String, parent: A?) {
class Nested(a: A)
inner class Inner(nested: Nested)
}
fun box(): String {
assertEquals(listOf(java.lang.Double.TYPE, javaClass<String>(), javaClass<A>()), ::A.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<A>()), A::Nested.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<A>(), javaClass<A.Nested>()), A::Inner.parameters.map { it.type.javaType })
assertEquals(javaClass<A>(), ::A.returnType.javaType)
assertEquals(javaClass<A.Nested>(), A::Nested.returnType.javaType)
assertEquals(javaClass<A.Inner>(), A::Inner.returnType.javaType)
return "OK"
}
@@ -0,0 +1,21 @@
import kotlin.platform.platformStatic as static
import kotlin.reflect.jvm.javaType
import kotlin.test.assertEquals
class A {
fun foo(t: Long?): Long = t!!
}
object O {
static fun bar(a: A): String = ""
}
fun box(): String {
assertEquals(listOf(javaClass<A>(), javaClass<java.lang.Long>()), A::foo.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<O>(), javaClass<A>()), O::bar.parameters.map { it.type.javaType })
assertEquals(java.lang.Long.TYPE, A::foo.returnType.javaType)
assertEquals(javaClass<String>(), O::bar.returnType.javaType)
return "OK"
}
@@ -0,0 +1,23 @@
import kotlin.reflect.jvm.*
import kotlin.test.*
interface I {
fun foo(): Any
}
class A : I {
override fun foo(): Int = 0
fun bar(x: Long): Int = x.toInt()
}
fun box(): String {
assertEquals(javaClass<Integer>(), A::foo.returnType.javaType)
assertNotEquals(Integer.TYPE, A::foo.returnType.javaType)
assertNotEquals(javaClass<Integer>(), A::bar.returnType.javaType)
assertEquals(Integer.TYPE, A::bar.returnType.javaType)
assertEquals(java.lang.Long.TYPE, A::bar.parameters.last().type.javaType)
return "OK"
}
@@ -0,0 +1,41 @@
// FULL_JDK
import java.lang.reflect.ParameterizedType
import kotlin.platform.platformStatic as static
import kotlin.reflect.*
import kotlin.reflect.jvm.javaType
import kotlin.test.assertEquals
class A(private var foo: List<String>)
object O {
private static var bar: List<String> = listOf()
}
fun topLevel(): List<String> = listOf()
fun Any.extension(): List<String> = listOf()
fun assertGenericType(type: KType) {
val javaType = type.javaType
if (javaType !is ParameterizedType) {
throw AssertionError("Type should be a parameterized type, but was $javaType (${javaType.javaClass})")
}
}
fun box(): String {
val foo = A::class.members.single { it.name == "foo" } as KMutableProperty<*>
assertGenericType(foo.returnType)
assertGenericType(foo.getter.returnType)
assertGenericType(foo.setter.parameters.last().type)
val bar = O::class.members.single { it.name == "bar" } as KMutableProperty<*>
assertGenericType(bar.returnType)
assertGenericType(bar.getter.returnType)
assertGenericType(bar.setter.parameters.last().type)
assertGenericType(::topLevel.returnType)
assertGenericType(Any::extension.returnType)
assertGenericType(::A.parameters.single().type)
return "OK"
}
@@ -0,0 +1,24 @@
import kotlin.platform.platformStatic as static
import kotlin.reflect.KMutableProperty
import kotlin.reflect.jvm.javaType
import kotlin.test.assertEquals
class A(private var foo: String)
object O {
private static var bar: String = ""
}
fun box(): String {
val foo = A::class.members.single { it.name == "foo" } as KMutableProperty<*>
assertEquals(listOf(javaClass<A>()), foo.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<A>()), foo.getter.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<A>(), javaClass<String>()), foo.setter.parameters.map { it.type.javaType })
val bar = O::class.members.single { it.name == "bar" } as KMutableProperty<*>
assertEquals(listOf(javaClass<O>()), bar.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<O>()), bar.getter.parameters.map { it.type.javaType })
assertEquals(listOf(javaClass<O>(), javaClass<String>()), bar.setter.parameters.map { it.type.javaType })
return "OK"
}
@@ -0,0 +1,15 @@
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
fun free(s: String): Int = s.length()
fun Any.extension() {}
fun box(): String {
assertEquals(java.lang.Integer.TYPE, ::free.returnType.javaType)
assertEquals(javaClass<String>(), ::free.parameters.single().type.javaType)
assertEquals(javaClass<Any>(), Any::extension.parameters.single().type.javaType)
return "OK"
}
@@ -0,0 +1,20 @@
// FULL_JDK
import java.lang.reflect.TypeVariable
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
class A<T> {
fun foo(t: T) {}
}
fun box(): String {
val f = A<String>::foo
val t = f.parameters.last().type.javaType
if (t !is TypeVariable<*>) return "Fail, t should be a type variable: $t"
assertEquals("T", t.name)
assertEquals("A", (t.genericDeclaration as Class<*>).name)
return "OK"
}
@@ -0,0 +1,21 @@
import kotlin.reflect.jvm.*
import kotlin.test.assertEquals
fun foo(unitParam: Unit, nullableUnitParam: Unit?): Unit {}
var bar: Unit = Unit
fun box(): String {
assert(javaClass<Unit>() != java.lang.Void.TYPE)
assertEquals(javaClass<Unit>(), ::foo.parameters[0].type.javaType)
assertEquals(javaClass<Unit>(), ::foo.parameters[1].type.javaType)
assertEquals(java.lang.Void.TYPE, ::foo.returnType.javaType)
assertEquals(javaClass<Unit>(), ::bar.returnType.javaType)
assertEquals(javaClass<Unit>(), ::bar.getter.returnType.javaType)
assertEquals(javaClass<Unit>(), ::bar.setter.parameters.single().type.javaType)
assertEquals(java.lang.Void.TYPE, ::bar.setter.returnType.javaType)
return "OK"
}
@@ -267,6 +267,18 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithJava/reflection"), Pattern.compile("^([^\\.]+)$"), true);
}
@TestMetadata("callInstanceJavaMethod")
public void testCallInstanceJavaMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/callInstanceJavaMethod/");
doTestWithJava(fileName);
}
@TestMetadata("callStaticJavaMethod")
public void testCallStaticJavaMethod() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/callStaticJavaMethod/");
doTestWithJava(fileName);
}
@TestMetadata("functionReferenceErasedToKFunction")
public void testFunctionReferenceErasedToKFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/functionReferenceErasedToKFunction/");
@@ -3359,6 +3359,69 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/topLevelProperty.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Types extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInTypes() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/array.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("constructors.kt")
public void testConstructors() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/constructors.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("memberFunctions.kt")
public void testMemberFunctions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/memberFunctions.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("overrideAnyWithPrimitive.kt")
public void testOverrideAnyWithPrimitive() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/overrideAnyWithPrimitive.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("parameterizedTypes.kt")
public void testParameterizedTypes() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/parameterizedTypes.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("propertyAccessors.kt")
public void testPropertyAccessors() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/propertyAccessors.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("topLevelFunctions.kt")
public void testTopLevelFunctions() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/topLevelFunctions.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("typeParameters.kt")
public void testTypeParameters() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/typeParameters.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("unit.kt")
public void testUnit() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/types/unit.kt");
doTestWithStdlib(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny")
@@ -17,20 +17,23 @@
package kotlin.reflect.jvm.internal
import java.lang.reflect.Member
import java.lang.reflect.Modifier
import java.lang.reflect.Type
import java.lang.reflect.Constructor as ReflectConstructor
import java.lang.reflect.Field as ReflectField
import java.lang.reflect.Method as ReflectMethod
internal sealed class FunctionCaller(
private val expectedArgumentNumber: Int
) {
internal abstract class FunctionCaller {
abstract val member: Member
abstract val returnType: Type
abstract val parameterTypes: List<Type>
abstract fun call(args: Array<*>): Any?
protected open fun checkArguments(args: Array<*>) {
if (expectedArgumentNumber != args.size()) {
throw IllegalArgumentException("Callable expects $expectedArgumentNumber arguments, but ${args.size()} were provided.")
if (parameterTypes.size() != args.size()) {
throw IllegalArgumentException("Callable expects ${parameterTypes.size()} arguments, but ${args.size()} were provided.")
}
}
@@ -40,9 +43,16 @@ internal sealed class FunctionCaller(
}
}
class Constructor(val constructor: ReflectConstructor<*>) : FunctionCaller(constructor.parameterTypes.size()) {
class Constructor(val constructor: ReflectConstructor<*>) : FunctionCaller() {
override val member: Member get() = constructor
override val returnType = constructor.declaringClass
override val parameterTypes =
if (returnType.declaringClass == null || Modifier.isStatic(returnType.modifiers))
constructor.genericParameterTypes.toList()
else listOf(returnType.declaringClass, *constructor.genericParameterTypes)
override fun call(args: Array<*>): Any? {
checkArguments(args)
return constructor.newInstance(*args)
@@ -51,10 +61,17 @@ internal sealed class FunctionCaller(
abstract class Method(
val method: ReflectMethod,
requiresInstance: Boolean
) : FunctionCaller(method.parameterTypes.size() + (if (requiresInstance) 1 else 0)) {
private val requiresInstance: Boolean
) : FunctionCaller() {
override val member: Member get() = method
private val isVoidMethod = method.returnType == Void.TYPE
override val returnType = method.genericReturnType
override val parameterTypes =
if (requiresInstance) listOf(method.declaringClass, *method.genericParameterTypes)
else method.genericParameterTypes.toList()
private val isVoidMethod = returnType == Void.TYPE
protected fun callMethod(instance: Any?, args: Array<*>): Any? {
val result = method.invoke(instance, *args)
@@ -86,14 +103,19 @@ internal sealed class FunctionCaller(
}
}
abstract class FieldAccessor(val field: ReflectField, expectedArgumentNumber: Int) : FunctionCaller(expectedArgumentNumber) {
abstract class FieldAccessor(val field: ReflectField) : FunctionCaller() {
override val member: Member get() = field
}
abstract class FieldGetter(
field: ReflectField,
private val requiresInstance: Boolean
) : FieldAccessor(field, if (requiresInstance) 1 else 0) {
) : FieldAccessor(field) {
override val returnType = field.genericType
override val parameterTypes =
if (requiresInstance) listOf(field.declaringClass) else listOf()
override fun call(args: Array<*>): Any? {
checkArguments(args)
return field.get(if (requiresInstance) args.first() else null)
@@ -104,7 +126,13 @@ internal sealed class FunctionCaller(
field: ReflectField,
private val notNull: Boolean,
private val requiresInstance: Boolean
) : FieldAccessor(field, if (requiresInstance) 2 else 1) {
) : FieldAccessor(field) {
override val returnType: Type get() = Void.TYPE
override val parameterTypes =
if (requiresInstance) listOf(field.declaringClass, field.genericType)
else listOf(field.genericType)
override fun checkArguments(args: Array<*>) {
super.checkArguments(args)
if (notNull && args.last() == null) {
@@ -36,15 +36,15 @@ interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
var index = 0
if (descriptor.dispatchReceiverParameter != null) {
result.add(KParameterImpl(index++) { descriptor.dispatchReceiverParameter!! })
result.add(KParameterImpl(this, index++) { descriptor.dispatchReceiverParameter!! })
}
if (descriptor.extensionReceiverParameter != null) {
result.add(KParameterImpl(index++) { descriptor.extensionReceiverParameter!! })
result.add(KParameterImpl(this, index++) { descriptor.extensionReceiverParameter!! })
}
for (i in descriptor.valueParameters.indices) {
result.add(KParameterImpl(index++) { descriptor.valueParameters[i] })
result.add(KParameterImpl(this, index++) { descriptor.valueParameters[i] })
}
result.trimToSize()
@@ -52,7 +52,7 @@ interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
}
override val returnType: KType
get() = KTypeImpl(descriptor.returnType!!)
get() = KTypeImpl(descriptor.returnType!!) { caller.returnType }
@suppress("UNCHECKED_CAST")
override fun call(vararg args: Any?): R = reflectionCall {
@@ -23,8 +23,9 @@ import kotlin.reflect.KParameter
import kotlin.reflect.KType
class KParameterImpl(
val callable: KCallableImpl<*>,
override val index: Int,
private val computeDescriptor: () -> ParameterDescriptor
computeDescriptor: () -> ParameterDescriptor
) : KParameter, KAnnotatedElementImpl {
private val descriptor: ParameterDescriptor by ReflectProperties.lazySoft(computeDescriptor)
@@ -38,5 +39,5 @@ class KParameterImpl(
}
override val type: KType
get() = KTypeImpl(descriptor.getType())
get() = KTypeImpl(descriptor.type) { callable.caller.parameterTypes[index] }
}
@@ -17,9 +17,15 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.types.JetType
import java.lang.reflect.Type
import kotlin.reflect.KType
class KTypeImpl(val type: JetType) : KType {
class KTypeImpl(
val type: JetType,
computeJavaType: () -> Type
) : KType {
internal val javaType: Type by ReflectProperties.lazySoft(computeJavaType)
override val isMarkedNullable: Boolean
get() = type.isMarkedNullable
}
@@ -16,10 +16,7 @@
package kotlin.reflect.jvm
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.lang.reflect.*
import kotlin.jvm.internal.Intrinsic
import kotlin.reflect.*
import kotlin.reflect.jvm.internal.*
@@ -87,6 +84,15 @@ public val <T> KFunction<T>.javaConstructor: Constructor<T>?
}
/**
* Returns a Java [Type] instance corresponding to the given Kotlin type.
* Note that one Kotlin type may correspond to different JVM types depending on where it appears. For example, [Unit] corresponds to
* the JVM class [Unit] when it's the type of a parameter, or to `void` when it's the return type of a function.
*/
public val KType.javaType: Type
get() = (this as KTypeImpl).javaType
// Java reflection -> Kotlin reflection