Support KCallable.callBy with map of parameters to arguments
callBy is able to handle optional parameters. #KT-8827 Fixed
This commit is contained in:
@@ -16,10 +16,12 @@
|
||||
|
||||
package kotlin.reflect.jvm
|
||||
|
||||
import java.lang.reflect.AccessibleObject
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KMutableProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.internal.KCallableImpl
|
||||
|
||||
/**
|
||||
* Provides a way to suppress JVM access checks for a callable.
|
||||
@@ -50,6 +52,7 @@ public var KCallable<*>.isAccessible: Boolean
|
||||
javaMethod?.isAccessible ?: true
|
||||
is KFunction ->
|
||||
javaMethod?.isAccessible ?: true &&
|
||||
((this as KCallableImpl<*>).defaultCaller?.member as? AccessibleObject)?.isAccessible ?: true &&
|
||||
this.javaConstructor?.isAccessible ?: true
|
||||
else -> throw UnsupportedOperationException("Unknown callable: $this ($javaClass)")
|
||||
}
|
||||
@@ -75,6 +78,7 @@ public var KCallable<*>.isAccessible: Boolean
|
||||
}
|
||||
is KFunction -> {
|
||||
javaMethod?.isAccessible = value
|
||||
((this as KCallableImpl<*>).defaultCaller?.member as? AccessibleObject)?.isAccessible = true
|
||||
this.javaConstructor?.isAccessible = value
|
||||
}
|
||||
else -> throw UnsupportedOperationException("Unknown callable: $this ($javaClass)")
|
||||
|
||||
@@ -18,20 +18,26 @@ package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import java.util.ArrayList
|
||||
import java.lang.reflect.Type
|
||||
import java.util.*
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
import kotlin.reflect.jvm.javaType
|
||||
|
||||
interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
|
||||
val descriptor: CallableMemberDescriptor
|
||||
|
||||
val caller: FunctionCaller<*>
|
||||
|
||||
val defaultCaller: FunctionCaller<*>?
|
||||
|
||||
override val annotated: Annotated get() = descriptor
|
||||
|
||||
override val parameters: List<KParameter>
|
||||
get() {
|
||||
val descriptor = descriptor
|
||||
val result = ArrayList<KParameter>()
|
||||
var index = 0
|
||||
|
||||
@@ -58,4 +64,74 @@ interface KCallableImpl<out R> : KCallable<R>, KAnnotatedElementImpl {
|
||||
override fun call(vararg args: Any?): R = reflectionCall {
|
||||
return caller.call(args) as R
|
||||
}
|
||||
|
||||
// See ArgumentGenerator#generate
|
||||
override fun callBy(args: Map<KParameter, Any?>): R {
|
||||
val parameters = parameters
|
||||
val arguments = ArrayList<Any?>(parameters.size())
|
||||
var mask = 0
|
||||
val masks = ArrayList<Int>(1)
|
||||
var index = 0
|
||||
|
||||
for (parameter in parameters) {
|
||||
if (index != 0 && index % Integer.SIZE == 0) {
|
||||
masks.add(mask)
|
||||
mask = 0
|
||||
}
|
||||
|
||||
when {
|
||||
args.containsKey(parameter) -> {
|
||||
arguments.add(args[parameter])
|
||||
}
|
||||
parameter.isOptional -> {
|
||||
arguments.add(defaultPrimitiveValue(parameter.type.javaType))
|
||||
mask = mask or (1 shl (index % Integer.SIZE))
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("No argument provided for a required parameter: $parameter")
|
||||
}
|
||||
}
|
||||
|
||||
if (parameter.kind == KParameter.Kind.VALUE) {
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
if (mask == 0 && masks.isEmpty()) {
|
||||
return call(*arguments.toTypedArray())
|
||||
}
|
||||
|
||||
masks.add(mask)
|
||||
|
||||
val caller = defaultCaller ?: throw KotlinReflectionInternalError("This callable does not support a default call: $descriptor")
|
||||
|
||||
arguments.addAll(masks)
|
||||
|
||||
if (caller is FunctionCaller.Constructor) {
|
||||
// DefaultConstructorMarker
|
||||
arguments.add(null)
|
||||
}
|
||||
|
||||
@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) {
|
||||
java.lang.Boolean.TYPE -> false
|
||||
java.lang.Character.TYPE -> 0.toChar()
|
||||
java.lang.Byte.TYPE -> 0.toByte()
|
||||
java.lang.Short.TYPE -> 0.toShort()
|
||||
java.lang.Integer.TYPE -> 0
|
||||
java.lang.Float.TYPE -> 0f
|
||||
java.lang.Long.TYPE -> 0L
|
||||
java.lang.Double.TYPE -> 0.0
|
||||
java.lang.Void.TYPE -> throw IllegalStateException("Parameter with void type is illegal")
|
||||
else -> throw UnsupportedOperationException("Unknown primitive: $type")
|
||||
}
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.safeClassLoader
|
||||
@@ -137,6 +138,24 @@ abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
return functions.single()
|
||||
}
|
||||
|
||||
private fun Class<*>.tryGetMethod(name: String, parameterTypes: List<Class<*>>, declared: Boolean) =
|
||||
try {
|
||||
if (declared) getDeclaredMethod(name, *parameterTypes.toTypedArray())
|
||||
else getMethod(name, *parameterTypes.toTypedArray())
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun Class<*>.tryGetConstructor(parameterTypes: List<Class<*>>, declared: Boolean) =
|
||||
try {
|
||||
if (declared) getDeclaredConstructor(*parameterTypes.toTypedArray())
|
||||
else getConstructor(*parameterTypes.toTypedArray())
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
}
|
||||
|
||||
// TODO: check resulting method's return type
|
||||
fun findMethodBySignature(
|
||||
@suppress("UNUSED_PARAMETER") proto: ProtoBuf.Callable,
|
||||
@@ -144,7 +163,7 @@ abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
nameResolver: NameResolver,
|
||||
declared: Boolean
|
||||
): Method? {
|
||||
val name = nameResolver.getString(signature.getName())
|
||||
val name = nameResolver.getString(signature.name)
|
||||
if (name == "<init>") return null
|
||||
|
||||
val parameterTypes = loadParameterTypes(nameResolver, signature)
|
||||
@@ -153,13 +172,25 @@ abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
// This is likely to change after the package part reform.
|
||||
val owner = jClass
|
||||
|
||||
return try {
|
||||
if (declared) owner.getDeclaredMethod(name, *parameterTypes)
|
||||
else owner.getMethod(name, *parameterTypes)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
return owner.tryGetMethod(name, parameterTypes, declared)
|
||||
}
|
||||
|
||||
fun findDefaultMethod(
|
||||
signature: JvmProtoBuf.JvmMethodSignature,
|
||||
nameResolver: NameResolver,
|
||||
isMember: Boolean,
|
||||
declared: Boolean
|
||||
): Method? {
|
||||
val name = nameResolver.getString(signature.name)
|
||||
if (name == "<init>") return null
|
||||
|
||||
val parameterTypes = arrayListOf<Class<*>>()
|
||||
if (isMember) {
|
||||
parameterTypes.add(jClass)
|
||||
}
|
||||
addParametersAndMasks(parameterTypes, nameResolver, signature)
|
||||
|
||||
return jClass.tryGetMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, declared)
|
||||
}
|
||||
|
||||
fun findConstructorBySignature(
|
||||
@@ -167,24 +198,40 @@ abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
nameResolver: NameResolver,
|
||||
declared: Boolean
|
||||
): Constructor<*>? {
|
||||
if (nameResolver.getString(signature.getName()) != "<init>") return null
|
||||
if (nameResolver.getString(signature.name) != "<init>") return null
|
||||
|
||||
val parameterTypes = loadParameterTypes(nameResolver, signature)
|
||||
return jClass.tryGetConstructor(loadParameterTypes(nameResolver, signature), declared)
|
||||
}
|
||||
|
||||
return try {
|
||||
if (declared) jClass.getDeclaredConstructor(*parameterTypes)
|
||||
else jClass.getConstructor(*parameterTypes)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
fun findDefaultConstructor(
|
||||
signature: JvmProtoBuf.JvmMethodSignature,
|
||||
nameResolver: NameResolver,
|
||||
declared: Boolean
|
||||
): Constructor<*>? {
|
||||
if (nameResolver.getString(signature.name) != "<init>") return null
|
||||
|
||||
val parameterTypes = arrayListOf<Class<*>>()
|
||||
addParametersAndMasks(parameterTypes, nameResolver, signature)
|
||||
parameterTypes.add(DEFAULT_CONSTRUCTOR_MARKER)
|
||||
|
||||
return jClass.tryGetConstructor(parameterTypes, declared)
|
||||
}
|
||||
|
||||
private fun addParametersAndMasks(
|
||||
result: MutableList<Class<*>>, nameResolver: NameResolver, signature: JvmProtoBuf.JvmMethodSignature
|
||||
) {
|
||||
val valueParameters = loadParameterTypes(nameResolver, signature)
|
||||
result.addAll(valueParameters)
|
||||
repeat((valueParameters.size() + Integer.SIZE - 1) / Integer.SIZE) {
|
||||
result.add(Integer.TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadParameterTypes(nameResolver: NameResolver, signature: JvmProtoBuf.JvmMethodSignature): Array<Class<*>> {
|
||||
private fun loadParameterTypes(nameResolver: NameResolver, signature: JvmProtoBuf.JvmMethodSignature): List<Class<*>> {
|
||||
val classLoader = jClass.safeClassLoader
|
||||
return signature.getParameterTypeList().map { jvmType ->
|
||||
return signature.parameterTypeList.map { jvmType ->
|
||||
loadJvmType(jvmType, nameResolver, classLoader)
|
||||
}.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check resulting field's type
|
||||
@@ -256,5 +303,7 @@ abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
LONG to java.lang.Long.TYPE,
|
||||
DOUBLE to java.lang.Double.TYPE
|
||||
)
|
||||
|
||||
private val DEFAULT_CONSTRUCTOR_MARKER = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,14 +49,14 @@ open class KFunctionImpl protected constructor(
|
||||
|
||||
override val name: String get() = descriptor.name.asString()
|
||||
|
||||
private fun isDeclared(): Boolean = Visibilities.isPrivate(descriptor.visibility)
|
||||
|
||||
override val caller: FunctionCaller<*> by ReflectProperties.lazySoft {
|
||||
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
|
||||
val member: Member? = when (jvmSignature) {
|
||||
is KotlinFunction ->
|
||||
if (name == "<init>") container.findConstructorBySignature(jvmSignature.signature, jvmSignature.nameResolver,
|
||||
Visibilities.isPrivate(descriptor.visibility))
|
||||
else container.findMethodBySignature(jvmSignature.proto, jvmSignature.signature, jvmSignature.nameResolver,
|
||||
Visibilities.isPrivate(descriptor.visibility))
|
||||
if (name == "<init>") container.findConstructorBySignature(jvmSignature.signature, jvmSignature.nameResolver, isDeclared())
|
||||
else container.findMethodBySignature(jvmSignature.proto, jvmSignature.signature, jvmSignature.nameResolver, isDeclared())
|
||||
is JavaMethod -> jvmSignature.method
|
||||
is JavaConstructor -> jvmSignature.constructor
|
||||
is BuiltInFunction -> jvmSignature.getMember(container)
|
||||
@@ -68,12 +68,43 @@ open class KFunctionImpl protected constructor(
|
||||
!Modifier.isStatic(member.modifiers) -> FunctionCaller.InstanceMethod(member)
|
||||
|
||||
descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null,
|
||||
descriptor.annotations.findAnnotation(JVM_STATIC) != null->
|
||||
descriptor.annotations.findAnnotation(JVM_STATIC) != null ->
|
||||
FunctionCaller.PlatformStaticInObject(member)
|
||||
|
||||
else -> FunctionCaller.StaticMethod(member)
|
||||
}
|
||||
else -> throw KotlinReflectionInternalError("Call is not yet supported for this function: $descriptor")
|
||||
else -> throw KotlinReflectionInternalError("Call is not yet supported for this function: $descriptor (member = $member)")
|
||||
}
|
||||
}
|
||||
|
||||
override val defaultCaller: FunctionCaller<*>? by ReflectProperties.lazySoft {
|
||||
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
|
||||
val member: Member? = when (jvmSignature) {
|
||||
is KotlinFunction -> {
|
||||
if (name == "<init>") {
|
||||
container.findDefaultConstructor(jvmSignature.signature, jvmSignature.nameResolver, isDeclared())
|
||||
}
|
||||
else {
|
||||
val isMember = !Modifier.isStatic(caller.member.modifiers)
|
||||
container.findDefaultMethod(jvmSignature.signature, jvmSignature.nameResolver, isMember, isDeclared())
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Java methods, Java constructors and built-ins don't have $default methods
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
when (member) {
|
||||
is Constructor<*> -> FunctionCaller.Constructor(member)
|
||||
is Method -> when {
|
||||
descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null,
|
||||
descriptor.annotations.findAnnotation(JVM_STATIC) != null ->
|
||||
FunctionCaller.PlatformStaticInObject(member)
|
||||
|
||||
else -> FunctionCaller.StaticMethod(member)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ interface KPropertyImpl<out R> : KProperty<R>, KCallableImpl<R> {
|
||||
|
||||
override val caller: FunctionCaller<*> get() = getter.caller
|
||||
|
||||
override val defaultCaller: FunctionCaller<*>? get() = getter.defaultCaller
|
||||
|
||||
abstract class Accessor<out R> : KProperty.Accessor<R> {
|
||||
abstract override val property: KPropertyImpl<R>
|
||||
|
||||
@@ -54,6 +56,8 @@ interface KPropertyImpl<out R> : KProperty<R>, KCallableImpl<R> {
|
||||
override val caller: FunctionCaller<*> by ReflectProperties.lazySoft {
|
||||
computeCallerForAccessor(isGetter = true)
|
||||
}
|
||||
|
||||
override val defaultCaller: FunctionCaller<*>? get() = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +78,8 @@ interface KMutablePropertyImpl<R> : KMutableProperty<R>, KPropertyImpl<R> {
|
||||
override val caller: FunctionCaller<*> by ReflectProperties.lazySoft {
|
||||
computeCallerForAccessor(isGetter = false)
|
||||
}
|
||||
|
||||
override val defaultCaller: FunctionCaller<*>? get() = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user