Support Kotlin annotation constructors in reflection

#KT-13106 In Progress
This commit is contained in:
Alexander Udalov
2016-09-14 19:52:57 +03:00
parent fb18e4bf4c
commit aeba124bdb
12 changed files with 344 additions and 14 deletions
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.load.java.structure.reflect.wrapperByPrimitive
import java.lang.reflect.Proxy
import kotlin.reflect.KClass
import kotlin.reflect.KotlinReflectionInternalError
import java.lang.reflect.Method as ReflectMethod
internal class AnnotationConstructorCaller(
private val jClass: Class<*>,
private val parameterNames: List<String>,
private val callMode: CallMode,
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 }
// 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 }
override fun call(args: Array<*>): Any? {
checkArguments(args)
val values = args.mapIndexed { index, arg ->
val value =
if (arg == null && callMode == CallMode.CALL_BY_NAME) defaultValues[index]
else arg.transformKotlinToJvm(erasedParameterTypes[index])
value ?: throwIllegalArgumentType(index, parameterNames[index], erasedParameterTypes[index])
}
return createAnnotationInstance(jClass, parameterNames.zip(values).toMap())
}
}
/**
* Transforms a Kotlin value to the one required by the JVM, e.g. KClass<*> -> Class<*> or Array<KClass<*>> -> Array<Class<*>>.
* Returns `null` in case when no transformation is possible (an argument of an incorrect type was passed).
*/
private fun Any?.transformKotlinToJvm(expectedType: Class<*>): Any? {
@Suppress("UNCHECKED_CAST")
val result = when (this) {
is Class<*> -> return null
is KClass<*> -> this.java
is Array<*> -> when {
this.isArrayOf<Class<*>>() -> return null
this.isArrayOf<KClass<*>>() -> (this as Array<KClass<*>>).map(KClass<*>::java).toTypedArray()
else -> this
}
else -> this
}
return if (expectedType.isInstance(result)) result else null
}
private fun throwIllegalArgumentType(index: Int, name: String, expectedJvmType: Class<*>): Nothing {
val kotlinClass = when {
expectedJvmType == Class::class.java -> KClass::class
expectedJvmType.isArray && expectedJvmType.componentType == Class::class.java ->
@Suppress("CLASS_LITERAL_LHS_NOT_A_CLASS") Array<KClass<*>>::class // Workaround KT-13924
else -> expectedJvmType.kotlin
}
// For arrays, also render the type argument in the message, e.g. "... not of the required type kotlin.Array<kotlin.reflect.KClass>"
val typeString = when {
kotlinClass.qualifiedName == Array<Any>::class.qualifiedName ->
"${kotlinClass.qualifiedName}<${kotlinClass.java.componentType.kotlin.qualifiedName}>"
else -> kotlinClass.qualifiedName
}
throw IllegalArgumentException("Argument #$index $name is not of the required type $typeString")
}
private fun createAnnotationInstance(annotationClass: Class<*>, values: Map<String, Any>): Any {
return Proxy.newProxyInstance(annotationClass.classLoader, arrayOf(annotationClass)) { proxy, method, args ->
// TODO: support equals, hashCode, toString, annotationType
values[method.name] ?: throw KotlinReflectionInternalError("Method is not supported: $method (args: ${args.orEmpty().toList()})")
}
}
@@ -23,7 +23,7 @@ import java.lang.reflect.Constructor as ReflectConstructor
import java.lang.reflect.Field as ReflectField
import java.lang.reflect.Method as ReflectMethod
internal abstract class FunctionCaller<out M : Member>(
internal abstract class FunctionCaller<out M : Member?>(
internal val member: M,
internal val returnType: Type,
internal val instanceClass: Class<*>?,
@@ -45,7 +45,7 @@ internal abstract class FunctionCaller<out M : Member>(
}
protected fun checkObjectInstance(obj: Any?) {
if (obj == null || !member.declaringClass.isInstance(obj)) {
if (obj == null || !member!!.declaringClass.isInstance(obj)) {
throw IllegalArgumentException("An object member requires the object instance passed as the first argument.")
}
}
@@ -27,8 +27,10 @@ import kotlin.reflect.jvm.javaType
internal abstract class KCallableImpl<out R> : KCallable<R> {
abstract val descriptor: CallableMemberDescriptor
// The instance which is used to perform a positional call, i.e. `call`
abstract val caller: FunctionCaller<*>
// The instance which is used to perform a call "by name", i.e. `callBy`
abstract val defaultCaller: FunctionCaller<*>?
abstract val container: KDeclarationContainerImpl
@@ -94,7 +96,7 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
override val isAbstract: Boolean
get() = descriptor.modality == Modality.ABSTRACT
private val isAnnotationConstructor: Boolean
protected val isAnnotationConstructor: Boolean
get() = name == "<init>" && container.jClass.isAnnotation
@Suppress("UNCHECKED_CAST")
@@ -102,8 +104,12 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
return caller.call(args) as R
}
// See ArgumentGenerator#generate
override fun callBy(args: Map<KParameter, Any?>): R {
return if (isAnnotationConstructor) callAnnotationConstructor(args) else callDefaultMethod(args)
}
// See ArgumentGenerator#generate
private fun callDefaultMethod(args: Map<KParameter, Any?>): R {
val parameters = parameters
val arguments = ArrayList<Any?>(parameters.size)
var mask = 0
@@ -153,6 +159,25 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
}
}
private fun callAnnotationConstructor(args: Map<KParameter, Any?>): R {
val arguments = parameters.map { parameter ->
when {
args.containsKey(parameter) -> {
args[parameter] ?: throw IllegalArgumentException("Annotation argument value cannot be null ($parameter)")
}
parameter.isOptional -> null
else -> throw IllegalArgumentException("No argument provided for a required parameter: $parameter")
}
}
val caller = defaultCaller ?: throw KotlinReflectionInternalError("This callable does not support a default call: $descriptor")
@Suppress("UNCHECKED_CAST")
return reflectionCall {
caller.call(arguments.toTypedArray()) as R
}
}
private fun defaultPrimitiveValue(type: Type): Any? =
if (type is Class<*> && type.isPrimitive) {
when (type) {
@@ -170,10 +170,10 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
override val constructorDescriptors: Collection<ConstructorDescriptor>
get() {
val descriptor = descriptor
if (descriptor.kind == ClassKind.CLASS || descriptor.kind == ClassKind.ENUM_CLASS) {
return descriptor.constructors
if (descriptor.kind == ClassKind.INTERFACE || descriptor.kind == ClassKind.OBJECT) {
return emptyList()
}
return emptyList()
return descriptor.constructors
}
override fun getProperties(name: Name): Collection<PropertyDescriptor> =
@@ -26,6 +26,8 @@ import java.lang.reflect.Modifier
import kotlin.jvm.internal.FunctionImpl
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.JvmFunctionSignature.*
internal class KFunctionImpl private constructor(
@@ -48,10 +50,14 @@ internal class KFunctionImpl private constructor(
private fun isDeclared(): Boolean = Visibilities.isPrivate(descriptor.visibility)
override val caller: FunctionCaller<*> by ReflectProperties.lazySoft {
override val caller: FunctionCaller<*> by ReflectProperties.lazySoft caller@ {
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
val member: Member? = when (jvmSignature) {
is KotlinConstructor -> container.findConstructorBySignature(jvmSignature.constructorDesc, isDeclared())
is KotlinConstructor -> {
if (isAnnotationConstructor)
return@caller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, POSITIONAL_CALL)
container.findConstructorBySignature(jvmSignature.constructorDesc, isDeclared())
}
is KotlinFunction -> container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc, isDeclared())
is JavaMethod -> jvmSignature.method
is JavaConstructor -> jvmSignature.constructor
@@ -71,14 +77,16 @@ internal class KFunctionImpl private constructor(
}
}
override val defaultCaller: FunctionCaller<*>? by ReflectProperties.lazySoft {
override val defaultCaller: FunctionCaller<*>? by ReflectProperties.lazySoft defaultCaller@ {
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
val member: Member? = when (jvmSignature) {
is KotlinFunction -> {
container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc,
!Modifier.isStatic(caller.member.modifiers), isDeclared())
!Modifier.isStatic(caller.member!!.modifiers), isDeclared())
}
is KotlinConstructor -> {
if (isAnnotationConstructor)
return@defaultCaller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, CALL_BY_NAME)
container.findDefaultConstructor(jvmSignature.constructorDesc, isDeclared())
}
else -> {