Support Java annotation constructors in reflection

#KT-13106 In Progress
This commit is contained in:
Alexander Udalov
2016-09-15 16:12:28 +03:00
parent aeba124bdb
commit 525937252d
7 changed files with 244 additions and 9 deletions
@@ -26,17 +26,31 @@ internal class AnnotationConstructorCaller(
private val jClass: Class<*>,
private val parameterNames: List<String>,
private val callMode: CallMode,
origin: Origin,
methods: List<ReflectMethod> = parameterNames.map { name -> jClass.getDeclaredMethod(name) }
) : FunctionCaller<Nothing?>(
null, jClass, null, methods.map { it.genericReturnType }.toTypedArray()
) {
enum class CallMode { CALL_BY_NAME, POSITIONAL_CALL }
enum class Origin { JAVA, KOTLIN }
// Transform primitive int to java.lang.Integer because actual arguments passed here will be boxed and Class#isInstance should succeed
private val erasedParameterTypes: List<Class<*>> = methods.map { method -> method.returnType.let { it.wrapperByPrimitive ?: it } }
private val defaultValues: List<Any?> = methods.map { method -> method.defaultValue }
init {
// TODO: consider lifting this restriction once KT-8957 is implemented
if (callMode == CallMode.POSITIONAL_CALL && origin == Origin.JAVA && (parameterNames - "value").isNotEmpty()) {
throw UnsupportedOperationException(
"Positional call of a Java annotation constructor is allowed only if there are no parameters " +
"or one parameter named \"value\". This restriction exists because Java annotations (in contrast to Kotlin)" +
"do not impose any order on their arguments. Use KCallable#callBy instead."
)
}
}
override fun call(args: Array<*>): Any? {
checkArguments(args)
@@ -28,6 +28,8 @@ import kotlin.reflect.KFunction
import kotlin.reflect.KotlinReflectionInternalError
import kotlin.reflect.jvm.internal.AnnotationConstructorCaller.CallMode.CALL_BY_NAME
import kotlin.reflect.jvm.internal.AnnotationConstructorCaller.CallMode.POSITIONAL_CALL
import kotlin.reflect.jvm.internal.AnnotationConstructorCaller.Origin.JAVA
import kotlin.reflect.jvm.internal.AnnotationConstructorCaller.Origin.KOTLIN
import kotlin.reflect.jvm.internal.JvmFunctionSignature.*
internal class KFunctionImpl private constructor(
@@ -55,12 +57,16 @@ internal class KFunctionImpl private constructor(
val member: Member? = when (jvmSignature) {
is KotlinConstructor -> {
if (isAnnotationConstructor)
return@caller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, POSITIONAL_CALL)
return@caller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, POSITIONAL_CALL, KOTLIN)
container.findConstructorBySignature(jvmSignature.constructorDesc, isDeclared())
}
is KotlinFunction -> container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc, isDeclared())
is JavaMethod -> jvmSignature.method
is JavaConstructor -> jvmSignature.constructor
is FakeJavaAnnotationConstructor -> {
val methods = jvmSignature.methods
return@caller AnnotationConstructorCaller(container.jClass, methods.map { it.name }, POSITIONAL_CALL, JAVA, methods)
}
is BuiltInFunction -> jvmSignature.getMember(container)
}
@@ -86,9 +92,13 @@ internal class KFunctionImpl private constructor(
}
is KotlinConstructor -> {
if (isAnnotationConstructor)
return@defaultCaller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, CALL_BY_NAME)
return@defaultCaller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, CALL_BY_NAME, KOTLIN)
container.findDefaultConstructor(jvmSignature.constructorDesc, isDeclared())
}
is FakeJavaAnnotationConstructor -> {
val methods = jvmSignature.methods
return@defaultCaller AnnotationConstructorCaller(container.jClass, methods.map { it.name }, CALL_BY_NAME, JAVA, methods)
}
else -> {
// Java methods, Java constructors and built-ins don't have $default methods
null
@@ -71,9 +71,15 @@ internal sealed class JvmFunctionSignature {
class JavaConstructor(val constructor: Constructor<*>) : JvmFunctionSignature() {
override fun asString(): String =
"<init>" +
constructor.parameterTypes.joinToString(separator = "", prefix = "(", postfix = ")") { it.desc } +
"V"
constructor.parameterTypes.joinToString(separator = "", prefix = "<init>(", postfix = ")V") { it.desc }
}
class FakeJavaAnnotationConstructor(val jClass: Class<*>) : JvmFunctionSignature() {
// Java annotations do not impose any order of methods inside them, so we consider them lexicographic here for stability
val methods = jClass.declaredMethods.sortedBy { it.name }
override fun asString(): String =
methods.joinToString(separator = "", prefix = "<init>(", postfix = ")V") { it.returnType.desc }
}
open class BuiltInFunction(private val signature: String) : JvmFunctionSignature() {
@@ -189,10 +195,14 @@ internal object RuntimeTypeMapper {
return JvmFunctionSignature.JavaMethod(method)
}
is JavaClassConstructorDescriptor -> {
val constructor = ((function.source as? JavaSourceElement)?.javaElement as? ReflectJavaConstructor)?.member ?:
throw KotlinReflectionInternalError("Incorrect resolution sequence for Java constructor $function")
return JvmFunctionSignature.JavaConstructor(constructor)
val element = (function.source as? JavaSourceElement)?.javaElement
when {
element is ReflectJavaConstructor ->
return JvmFunctionSignature.JavaConstructor(element.member)
element is ReflectJavaClass && element.isAnnotationType ->
return JvmFunctionSignature.FakeJavaAnnotationConstructor(element.element)
else -> throw KotlinReflectionInternalError("Incorrect resolution sequence for Java constructor $function ($element)")
}
}
else -> throw KotlinReflectionInternalError("Unknown origin of $function (${function.javaClass})")
}