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); 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") @TestMetadata("functionReferenceErasedToKFunction")
public void testFunctionReferenceErasedToKFunction() throws Exception { public void testFunctionReferenceErasedToKFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/functionReferenceErasedToKFunction/"); 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"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/topLevelProperty.kt");
doTestWithStdlib(fileName); 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") @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny")
@@ -17,20 +17,23 @@
package kotlin.reflect.jvm.internal package kotlin.reflect.jvm.internal
import java.lang.reflect.Member 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.Constructor as ReflectConstructor
import java.lang.reflect.Field as ReflectField import java.lang.reflect.Field as ReflectField
import java.lang.reflect.Method as ReflectMethod import java.lang.reflect.Method as ReflectMethod
internal sealed class FunctionCaller( internal abstract class FunctionCaller {
private val expectedArgumentNumber: Int
) {
abstract val member: Member abstract val member: Member
abstract val returnType: Type
abstract val parameterTypes: List<Type>
abstract fun call(args: Array<*>): Any? abstract fun call(args: Array<*>): Any?
protected open fun checkArguments(args: Array<*>) { protected open fun checkArguments(args: Array<*>) {
if (expectedArgumentNumber != args.size()) { if (parameterTypes.size() != args.size()) {
throw IllegalArgumentException("Callable expects $expectedArgumentNumber arguments, but ${args.size()} were provided.") 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 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? { override fun call(args: Array<*>): Any? {
checkArguments(args) checkArguments(args)
return constructor.newInstance(*args) return constructor.newInstance(*args)
@@ -51,10 +61,17 @@ internal sealed class FunctionCaller(
abstract class Method( abstract class Method(
val method: ReflectMethod, val method: ReflectMethod,
requiresInstance: Boolean private val requiresInstance: Boolean
) : FunctionCaller(method.parameterTypes.size() + (if (requiresInstance) 1 else 0)) { ) : FunctionCaller() {
override val member: Member get() = method 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? { protected fun callMethod(instance: Any?, args: Array<*>): Any? {
val result = method.invoke(instance, *args) 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 override val member: Member get() = field
} }
abstract class FieldGetter( abstract class FieldGetter(
field: ReflectField, field: ReflectField,
private val requiresInstance: Boolean 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? { override fun call(args: Array<*>): Any? {
checkArguments(args) checkArguments(args)
return field.get(if (requiresInstance) args.first() else null) return field.get(if (requiresInstance) args.first() else null)
@@ -104,7 +126,13 @@ internal sealed class FunctionCaller(
field: ReflectField, field: ReflectField,
private val notNull: Boolean, private val notNull: Boolean,
private val requiresInstance: 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<*>) { override fun checkArguments(args: Array<*>) {
super.checkArguments(args) super.checkArguments(args)
if (notNull && args.last() == null) { if (notNull && args.last() == null) {
@@ -36,15 +36,15 @@ interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
var index = 0 var index = 0
if (descriptor.dispatchReceiverParameter != null) { if (descriptor.dispatchReceiverParameter != null) {
result.add(KParameterImpl(index++) { descriptor.dispatchReceiverParameter!! }) result.add(KParameterImpl(this, index++) { descriptor.dispatchReceiverParameter!! })
} }
if (descriptor.extensionReceiverParameter != null) { if (descriptor.extensionReceiverParameter != null) {
result.add(KParameterImpl(index++) { descriptor.extensionReceiverParameter!! }) result.add(KParameterImpl(this, index++) { descriptor.extensionReceiverParameter!! })
} }
for (i in descriptor.valueParameters.indices) { for (i in descriptor.valueParameters.indices) {
result.add(KParameterImpl(index++) { descriptor.valueParameters[i] }) result.add(KParameterImpl(this, index++) { descriptor.valueParameters[i] })
} }
result.trimToSize() result.trimToSize()
@@ -52,7 +52,7 @@ interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
} }
override val returnType: KType override val returnType: KType
get() = KTypeImpl(descriptor.returnType!!) get() = KTypeImpl(descriptor.returnType!!) { caller.returnType }
@suppress("UNCHECKED_CAST") @suppress("UNCHECKED_CAST")
override fun call(vararg args: Any?): R = reflectionCall { override fun call(vararg args: Any?): R = reflectionCall {
@@ -23,8 +23,9 @@ import kotlin.reflect.KParameter
import kotlin.reflect.KType import kotlin.reflect.KType
class KParameterImpl( class KParameterImpl(
val callable: KCallableImpl<*>,
override val index: Int, override val index: Int,
private val computeDescriptor: () -> ParameterDescriptor computeDescriptor: () -> ParameterDescriptor
) : KParameter, KAnnotatedElementImpl { ) : KParameter, KAnnotatedElementImpl {
private val descriptor: ParameterDescriptor by ReflectProperties.lazySoft(computeDescriptor) private val descriptor: ParameterDescriptor by ReflectProperties.lazySoft(computeDescriptor)
@@ -38,5 +39,5 @@ class KParameterImpl(
} }
override val type: KType 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 package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
import java.lang.reflect.Type
import kotlin.reflect.KType 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 override val isMarkedNullable: Boolean
get() = type.isMarkedNullable get() = type.isMarkedNullable
} }
@@ -16,10 +16,7 @@
package kotlin.reflect.jvm package kotlin.reflect.jvm
import java.lang.reflect.Constructor import java.lang.reflect.*
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import kotlin.jvm.internal.Intrinsic import kotlin.jvm.internal.Intrinsic
import kotlin.reflect.* import kotlin.reflect.*
import kotlin.reflect.jvm.internal.* 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 // Java reflection -> Kotlin reflection