Support Kotlin annotation constructors in reflection
#KT-13106 In Progress
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
annotation class A1
|
||||||
|
|
||||||
|
annotation class A2(val k: KClass<*>, val s: A1)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(1, A1::class.constructors.size)
|
||||||
|
assertEquals(A1::class.primaryConstructor, A1::class.constructors.single())
|
||||||
|
|
||||||
|
val cs = A2::class.constructors
|
||||||
|
assertEquals(1, cs.size)
|
||||||
|
assertEquals(A2::class.primaryConstructor, cs.single())
|
||||||
|
val params = cs.single().parameters
|
||||||
|
assertEquals(listOf("k", "s"), params.map { it.name })
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
-3
@@ -1,10 +1,8 @@
|
|||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
|
|
||||||
import kotlin.reflect.*
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
interface Interface
|
interface Interface
|
||||||
annotation class Anno(val x: Int)
|
|
||||||
object Obj
|
object Obj
|
||||||
|
|
||||||
class C {
|
class C {
|
||||||
@@ -13,7 +11,6 @@ class C {
|
|||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
assertTrue(Interface::class.constructors.isEmpty())
|
assertTrue(Interface::class.constructors.isEmpty())
|
||||||
assertTrue(Anno::class.constructors.isEmpty())
|
|
||||||
assertTrue(Obj::class.constructors.isEmpty())
|
assertTrue(Obj::class.constructors.isEmpty())
|
||||||
assertTrue(C.Companion::class.constructors.isEmpty())
|
assertTrue(C.Companion::class.constructors.isEmpty())
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
annotation class Anno(val klasses: Array<KClass<*>> = arrayOf(String::class, Int::class))
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val anno = Anno::class.constructors.single().callBy(emptyMap())
|
||||||
|
assertEquals(listOf(String::class, Int::class), (anno.klasses as Array<KClass<*>>).toList() /* TODO: KT-9453 */)
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.primaryConstructor
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
|
||||||
|
annotation class NoParams
|
||||||
|
annotation class OneDefault(val s: String = "OK")
|
||||||
|
annotation class OneNonDefault(val s: String)
|
||||||
|
annotation class TwoParamsOneDefault(val s: String, val x: Int = 42)
|
||||||
|
annotation class TwoParamsOneDefaultKClass(val string: String, val klass: KClass<*> = Number::class)
|
||||||
|
annotation class TwoNonDefaults(val string: String, val klass: KClass<*>)
|
||||||
|
|
||||||
|
|
||||||
|
inline fun <reified T : Annotation> create(args: Map<String, Any?>): T {
|
||||||
|
val ctor = T::class.constructors.single()
|
||||||
|
return ctor.callBy(args.mapKeys { entry -> ctor.parameters.single { it.name == entry.key } })
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Annotation> create(): T = create(emptyMap())
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
create<NoParams>()
|
||||||
|
|
||||||
|
val t1 = create<OneDefault>()
|
||||||
|
assertEquals("OK", t1.s)
|
||||||
|
assertFails { create<OneDefault>(mapOf("s" to 42)) }
|
||||||
|
|
||||||
|
val t2 = create<OneNonDefault>(mapOf("s" to "OK"))
|
||||||
|
assertEquals("OK", t2.s)
|
||||||
|
assertFails { create<OneNonDefault>() }
|
||||||
|
|
||||||
|
val t3 = create<TwoParamsOneDefault>(mapOf("s" to "OK"))
|
||||||
|
assertEquals("OK", t3.s)
|
||||||
|
assertEquals(42, t3.x)
|
||||||
|
val t4 = create<TwoParamsOneDefault>(mapOf("s" to "OK", "x" to 239))
|
||||||
|
assertEquals(239, t4.x)
|
||||||
|
assertFails { create<TwoParamsOneDefault>(mapOf("s" to "Fail", "x" to "Fail")) }
|
||||||
|
|
||||||
|
val t5 = create<TwoParamsOneDefaultKClass>(mapOf("string" to "OK"))
|
||||||
|
assertEquals(Number::class, t5.klass as KClass<*> /* TODO: KT-9453 */)
|
||||||
|
|
||||||
|
assertFails("KClass (not Class) instances should be passed as arguments") {
|
||||||
|
create<TwoNonDefaults>(mapOf("klass" to String::class.java, "string" to "Fail"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val t6 = create<TwoNonDefaults>(mapOf("klass" to String::class, "string" to "OK"))
|
||||||
|
return t6.string
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.primaryConstructor
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
|
||||||
|
annotation class NoParams
|
||||||
|
annotation class OneDefault(val s: String = "Fail")
|
||||||
|
annotation class TwoNonDefaults(val string: String, val klass: KClass<*>)
|
||||||
|
|
||||||
|
inline fun <reified T : Annotation> create(vararg args: Any?): T =
|
||||||
|
T::class.constructors.single().call(*args)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
create<NoParams>()
|
||||||
|
assertFails { create<NoParams>("Fail") }
|
||||||
|
|
||||||
|
assertFails { create<OneDefault>() }
|
||||||
|
assertFails { create<OneDefault>(42) }
|
||||||
|
val o = create<OneDefault>("OK")
|
||||||
|
assertEquals("OK", o.s)
|
||||||
|
|
||||||
|
assertFails("call() should fail because arguments were passed in an incorrect order") {
|
||||||
|
create<TwoNonDefaults>(Any::class, "Fail")
|
||||||
|
}
|
||||||
|
assertFails("call() should fail because KClass (not Class) instances should be passed as arguments") {
|
||||||
|
create<TwoNonDefaults>("Fail", Any::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
val k = create<TwoNonDefaults>("OK", Int::class)
|
||||||
|
assertEquals(Int::class, k.klass as KClass<*> /* TODO: KT-9453 */)
|
||||||
|
|
||||||
|
return k.string
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
// FULL_JDK
|
||||||
|
|
||||||
|
import java.lang.reflect.GenericArrayType
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.jvm.javaType
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
annotation class Z
|
||||||
|
enum class E
|
||||||
|
|
||||||
|
annotation class Anno(
|
||||||
|
val b: Byte,
|
||||||
|
val s: String,
|
||||||
|
val ss: Array<String>,
|
||||||
|
val z: Z,
|
||||||
|
val zs: Array<Z>,
|
||||||
|
val e: E,
|
||||||
|
val es: Array<E>,
|
||||||
|
val k: KClass<*>,
|
||||||
|
val ka: Array<KClass<*>>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun tmp(): Array<Class<*>> = null!!
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val t = Anno::class.constructors.single().parameters.map { it.type.javaType }
|
||||||
|
|
||||||
|
assertEquals(Byte::class.java, t[0])
|
||||||
|
assertEquals(String::class.java, t[1])
|
||||||
|
assertEquals(Array<String>::class.java, t[2])
|
||||||
|
assertEquals(Z::class.java, t[3])
|
||||||
|
assertEquals(Array<Z>::class.java, t[4])
|
||||||
|
assertEquals(E::class.java, t[5])
|
||||||
|
assertEquals(Array<E>::class.java, t[6])
|
||||||
|
|
||||||
|
assertTrue(t[7] is ParameterizedType)
|
||||||
|
assertEquals(Class::class.java, (t[7] as ParameterizedType).rawType)
|
||||||
|
|
||||||
|
assertTrue(t[8] is GenericArrayType)
|
||||||
|
val e = (t[8] as GenericArrayType).genericComponentType
|
||||||
|
assertTrue(e is ParameterizedType)
|
||||||
|
assertEquals(Class::class.java, (e as ParameterizedType).rawType)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -12111,6 +12111,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/constructors"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/constructors"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationClass.kt")
|
||||||
|
public void testAnnotationClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/constructors/annotationClass.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("classesWithoutConstructors.kt")
|
@TestMetadata("classesWithoutConstructors.kt")
|
||||||
public void testClassesWithoutConstructors() throws Exception {
|
public void testClassesWithoutConstructors() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/constructors/classesWithoutConstructors.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/constructors/classesWithoutConstructors.kt");
|
||||||
@@ -12136,6 +12142,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/reflection/createAnnotation")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class CreateAnnotation extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInCreateAnnotation() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/createAnnotation"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("arrayOfKClasses.kt")
|
||||||
|
public void testArrayOfKClasses() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/createAnnotation/arrayOfKClasses.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callByKotlin.kt")
|
||||||
|
public void testCallByKotlin() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/createAnnotation/callByKotlin.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callKotlin.kt")
|
||||||
|
public void testCallKotlin() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/createAnnotation/callKotlin.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/reflection/enclosing")
|
@TestMetadata("compiler/testData/codegen/box/reflection/enclosing")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -12614,6 +12647,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("annotationConstructorParameters.kt")
|
||||||
|
public void testAnnotationConstructorParameters() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/annotationConstructorParameters.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("array.kt")
|
@TestMetadata("array.kt")
|
||||||
public void testArray() throws Exception {
|
public void testArray() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/array.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/mapping/types/array.kt");
|
||||||
|
|||||||
@@ -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.Field as ReflectField
|
||||||
import java.lang.reflect.Method as ReflectMethod
|
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 member: M,
|
||||||
internal val returnType: Type,
|
internal val returnType: Type,
|
||||||
internal val instanceClass: Class<*>?,
|
internal val instanceClass: Class<*>?,
|
||||||
@@ -45,7 +45,7 @@ internal abstract class FunctionCaller<out M : Member>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun checkObjectInstance(obj: Any?) {
|
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.")
|
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> {
|
internal abstract class KCallableImpl<out R> : KCallable<R> {
|
||||||
abstract val descriptor: CallableMemberDescriptor
|
abstract val descriptor: CallableMemberDescriptor
|
||||||
|
|
||||||
|
// The instance which is used to perform a positional call, i.e. `call`
|
||||||
abstract val caller: FunctionCaller<*>
|
abstract val caller: FunctionCaller<*>
|
||||||
|
|
||||||
|
// The instance which is used to perform a call "by name", i.e. `callBy`
|
||||||
abstract val defaultCaller: FunctionCaller<*>?
|
abstract val defaultCaller: FunctionCaller<*>?
|
||||||
|
|
||||||
abstract val container: KDeclarationContainerImpl
|
abstract val container: KDeclarationContainerImpl
|
||||||
@@ -94,7 +96,7 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
|
|||||||
override val isAbstract: Boolean
|
override val isAbstract: Boolean
|
||||||
get() = descriptor.modality == Modality.ABSTRACT
|
get() = descriptor.modality == Modality.ABSTRACT
|
||||||
|
|
||||||
private val isAnnotationConstructor: Boolean
|
protected val isAnnotationConstructor: Boolean
|
||||||
get() = name == "<init>" && container.jClass.isAnnotation
|
get() = name == "<init>" && container.jClass.isAnnotation
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -102,8 +104,12 @@ internal abstract class KCallableImpl<out R> : KCallable<R> {
|
|||||||
return caller.call(args) as R
|
return caller.call(args) as R
|
||||||
}
|
}
|
||||||
|
|
||||||
// See ArgumentGenerator#generate
|
|
||||||
override fun callBy(args: Map<KParameter, Any?>): R {
|
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 parameters = parameters
|
||||||
val arguments = ArrayList<Any?>(parameters.size)
|
val arguments = ArrayList<Any?>(parameters.size)
|
||||||
var mask = 0
|
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? =
|
private fun defaultPrimitiveValue(type: Type): Any? =
|
||||||
if (type is Class<*> && type.isPrimitive) {
|
if (type is Class<*> && type.isPrimitive) {
|
||||||
when (type) {
|
when (type) {
|
||||||
|
|||||||
@@ -170,10 +170,10 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
|
|||||||
override val constructorDescriptors: Collection<ConstructorDescriptor>
|
override val constructorDescriptors: Collection<ConstructorDescriptor>
|
||||||
get() {
|
get() {
|
||||||
val descriptor = descriptor
|
val descriptor = descriptor
|
||||||
if (descriptor.kind == ClassKind.CLASS || descriptor.kind == ClassKind.ENUM_CLASS) {
|
if (descriptor.kind == ClassKind.INTERFACE || descriptor.kind == ClassKind.OBJECT) {
|
||||||
return descriptor.constructors
|
return emptyList()
|
||||||
}
|
}
|
||||||
return emptyList()
|
return descriptor.constructors
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProperties(name: Name): Collection<PropertyDescriptor> =
|
override fun getProperties(name: Name): Collection<PropertyDescriptor> =
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import java.lang.reflect.Modifier
|
|||||||
import kotlin.jvm.internal.FunctionImpl
|
import kotlin.jvm.internal.FunctionImpl
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
import kotlin.reflect.KotlinReflectionInternalError
|
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.*
|
import kotlin.reflect.jvm.internal.JvmFunctionSignature.*
|
||||||
|
|
||||||
internal class KFunctionImpl private constructor(
|
internal class KFunctionImpl private constructor(
|
||||||
@@ -48,10 +50,14 @@ internal class KFunctionImpl private constructor(
|
|||||||
|
|
||||||
private fun isDeclared(): Boolean = Visibilities.isPrivate(descriptor.visibility)
|
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 jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
|
||||||
val member: Member? = when (jvmSignature) {
|
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 KotlinFunction -> container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc, isDeclared())
|
||||||
is JavaMethod -> jvmSignature.method
|
is JavaMethod -> jvmSignature.method
|
||||||
is JavaConstructor -> jvmSignature.constructor
|
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 jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
|
||||||
val member: Member? = when (jvmSignature) {
|
val member: Member? = when (jvmSignature) {
|
||||||
is KotlinFunction -> {
|
is KotlinFunction -> {
|
||||||
container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc,
|
container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc,
|
||||||
!Modifier.isStatic(caller.member.modifiers), isDeclared())
|
!Modifier.isStatic(caller.member!!.modifiers), isDeclared())
|
||||||
}
|
}
|
||||||
is KotlinConstructor -> {
|
is KotlinConstructor -> {
|
||||||
|
if (isAnnotationConstructor)
|
||||||
|
return@defaultCaller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, CALL_BY_NAME)
|
||||||
container.findDefaultConstructor(jvmSignature.constructorDesc, isDeclared())
|
container.findDefaultConstructor(jvmSignature.constructorDesc, isDeclared())
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user