Support mapping between Kotlin functions and JVM methods/constructors
This commit is contained in:
@@ -75,14 +75,14 @@ abstract class DescriptorBasedProperty<out R> protected constructor(
|
||||
open val javaGetter: Method? by ReflectProperties.lazySoft {
|
||||
val proto = protoData
|
||||
if (proto == null || !proto.signature.hasGetter()) null
|
||||
else container.findMethodBySignature(proto.signature.getGetter(), proto.nameResolver,
|
||||
else container.findMethodBySignature(proto.proto, proto.signature.getGetter(), proto.nameResolver,
|
||||
descriptor.getGetter()?.getVisibility()?.let { Visibilities.isPrivate(it) } ?: false)
|
||||
}
|
||||
|
||||
open val javaSetter: Method? by ReflectProperties.lazySoft {
|
||||
val proto = protoData
|
||||
if (proto == null || !proto.signature.hasSetter()) null
|
||||
else container.findMethodBySignature(proto.signature.getSetter(), proto.nameResolver,
|
||||
else container.findMethodBySignature(proto.proto, proto.signature.getSetter(), proto.nameResolver,
|
||||
descriptor.getSetter()?.getVisibility()?.let { Visibilities.isPrivate(it) } ?: false)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.reflect.KCallable
|
||||
@@ -141,22 +142,55 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
|
||||
}
|
||||
|
||||
// TODO: check resulting method's return type
|
||||
fun findMethodBySignature(signature: JvmProtoBuf.JvmMethodSignature, nameResolver: NameResolver, declared: Boolean): Method? {
|
||||
fun findMethodBySignature(
|
||||
@suppress("UNUSED_PARAMETER") proto: ProtoBuf.Callable,
|
||||
signature: JvmProtoBuf.JvmMethodSignature,
|
||||
nameResolver: NameResolver,
|
||||
declared: Boolean
|
||||
): Method? {
|
||||
val name = nameResolver.getString(signature.getName())
|
||||
val classLoader = jClass.classLoader
|
||||
val parameterTypes = signature.getParameterTypeList().map { jvmType ->
|
||||
loadJvmType(jvmType, nameResolver, classLoader)
|
||||
}.toTypedArray()
|
||||
if (name == "<init>") return null
|
||||
|
||||
val parameterTypes = loadParameterTypes(nameResolver, signature)
|
||||
|
||||
// Method for a top level function should be the one from the package facade.
|
||||
// This is likely to change after the package part reform.
|
||||
val owner = jClass
|
||||
|
||||
return try {
|
||||
if (declared) jClass.getDeclaredMethod(name, *parameterTypes)
|
||||
else jClass.getMethod(name, *parameterTypes)
|
||||
if (declared) owner.getDeclaredMethod(name, *parameterTypes)
|
||||
else owner.getMethod(name, *parameterTypes)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun findConstructorBySignature(
|
||||
signature: JvmProtoBuf.JvmMethodSignature,
|
||||
nameResolver: NameResolver,
|
||||
declared: Boolean
|
||||
): Constructor<*>? {
|
||||
if (nameResolver.getString(signature.getName()) != "<init>") return null
|
||||
|
||||
val parameterTypes = loadParameterTypes(nameResolver, signature)
|
||||
|
||||
return try {
|
||||
if (declared) jClass.getDeclaredConstructor(*parameterTypes)
|
||||
else jClass.getConstructor(*parameterTypes)
|
||||
}
|
||||
catch (e: NoSuchMethodException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadParameterTypes(nameResolver: NameResolver, signature: JvmProtoBuf.JvmMethodSignature): Array<Class<*>> {
|
||||
val classLoader = jClass.classLoader
|
||||
return signature.getParameterTypeList().map { jvmType ->
|
||||
loadJvmType(jvmType, nameResolver, classLoader)
|
||||
}.toTypedArray()
|
||||
}
|
||||
|
||||
// TODO: check resulting field's type
|
||||
fun findFieldBySignature(
|
||||
proto: ProtoBuf.Callable,
|
||||
@@ -166,18 +200,11 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
|
||||
val name = nameResolver.getString(signature.getName())
|
||||
|
||||
val owner =
|
||||
when {
|
||||
proto.hasExtension(JvmProtoBuf.implClassName) -> {
|
||||
val implClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName))
|
||||
// TODO: store fq name of impl class name in jvm_descriptors.proto
|
||||
val classId = ClassId(jClass.classId.getPackageFqName(), implClassName)
|
||||
jClass.classLoader.loadClass(classId.asSingleFqName().asString())
|
||||
}
|
||||
signature.getIsStaticInOuter() -> {
|
||||
jClass.getEnclosingClass() ?: throw KotlinReflectionInternalError("Inconsistent metadata for field $name in $jClass")
|
||||
}
|
||||
else -> jClass
|
||||
implClassForCallable(nameResolver, proto) ?:
|
||||
if (signature.getIsStaticInOuter()) {
|
||||
jClass.getEnclosingClass() ?: throw KotlinReflectionInternalError("Inconsistent metadata for field $name in $jClass")
|
||||
}
|
||||
else jClass
|
||||
|
||||
return try {
|
||||
owner.getDeclaredField(name)
|
||||
@@ -187,6 +214,17 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the JVM class which contains this callable. This class may be different from the one represented by descriptors
|
||||
// in case of top level functions (their bodies are in package parts), methods with implementations in interfaces, etc.
|
||||
private fun implClassForCallable(nameResolver: NameResolver, proto: ProtoBuf.Callable): Class<*>? {
|
||||
if (!proto.hasExtension(JvmProtoBuf.implClassName)) return null
|
||||
|
||||
val implClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName))
|
||||
// TODO: store fq name of impl class name in jvm_descriptors.proto
|
||||
val classId = ClassId(jClass.classId.getPackageFqName(), implClassName)
|
||||
return jClass.classLoader.loadClass(classId.asSingleFqName().asString())
|
||||
}
|
||||
|
||||
private fun loadJvmType(
|
||||
type: JvmProtoBuf.JvmType,
|
||||
nameResolver: NameResolver,
|
||||
|
||||
@@ -18,6 +18,17 @@
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaConstructor
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaMethod
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.jvm.internal.FunctionImpl
|
||||
import kotlin.reflect.*
|
||||
|
||||
@@ -34,10 +45,57 @@ open class KFunctionImpl protected constructor(
|
||||
container, descriptor.getName().asString(), RuntimeTypeMapper.mapSignature(descriptor), descriptor
|
||||
)
|
||||
|
||||
private data class FunctionProtoData(
|
||||
val proto: ProtoBuf.Callable,
|
||||
val nameResolver: NameResolver,
|
||||
val signature: JvmProtoBuf.JvmMethodSignature
|
||||
)
|
||||
|
||||
override val descriptor: FunctionDescriptor by ReflectProperties.lazySoft<FunctionDescriptor>(descriptorInitialValue) {
|
||||
container.findFunctionDescriptor(name, signature)
|
||||
}
|
||||
|
||||
// null if this is a function declared in a foreign (Java) class
|
||||
private val protoData: FunctionProtoData? by ReflectProperties.lazyWeak {
|
||||
val function = DescriptorUtils.unwrapFakeOverride(descriptor) as? DeserializedCallableMemberDescriptor
|
||||
if (function != null) {
|
||||
val proto = function.proto
|
||||
if (proto.hasExtension(JvmProtoBuf.methodSignature)) {
|
||||
return@lazyWeak FunctionProtoData(proto, function.nameResolver, proto.getExtension(JvmProtoBuf.methodSignature))
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
internal val javaMethod: Method? by ReflectProperties.lazySoft {
|
||||
if (name != "<init>") {
|
||||
val proto = protoData
|
||||
if (proto != null) {
|
||||
container.findMethodBySignature(proto.proto, proto.signature, proto.nameResolver,
|
||||
Visibilities.isPrivate(descriptor.getVisibility()))
|
||||
}
|
||||
else {
|
||||
((descriptor.getOriginal().getSource() as? JavaSourceElement)?.javaElement as? ReflectJavaMethod)?.member
|
||||
}
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
internal val javaConstructor: Constructor<*>? by ReflectProperties.lazySoft {
|
||||
if (name == "<init>") {
|
||||
val proto = protoData
|
||||
if (proto != null) {
|
||||
return@lazySoft container.findConstructorBySignature(
|
||||
proto.signature, proto.nameResolver, Visibilities.isPrivate(descriptor.getVisibility())
|
||||
)
|
||||
}
|
||||
else {
|
||||
((descriptor.getOriginal().getSource() as? JavaSourceElement)?.javaElement as? ReflectJavaConstructor)?.member
|
||||
}
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
override val name: String get() = descriptor.getName().asString()
|
||||
|
||||
override fun getArity(): Int {
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaConstructorDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
@@ -56,19 +57,31 @@ object RuntimeTypeMapper {
|
||||
|
||||
return StringBuilder {
|
||||
append(method.getName().asString())
|
||||
|
||||
append("(")
|
||||
for (parameter in method.getValueParameters()) {
|
||||
appendJavaType(parameter.getType())
|
||||
}
|
||||
append(")")
|
||||
|
||||
appendParameters(method)
|
||||
appendJavaType(method.getReturnType())
|
||||
}.toString()
|
||||
}
|
||||
else if (function is JavaConstructorDescriptor) {
|
||||
val constructor = (function.getSource() as? JavaSourceElement)?.javaElement as? JavaConstructor ?:
|
||||
throw KotlinReflectionInternalError("Incorrect resolution sequence for Java constructor $function")
|
||||
|
||||
return StringBuilder {
|
||||
append("<init>")
|
||||
appendParameters(constructor)
|
||||
append("V")
|
||||
}.toString()
|
||||
}
|
||||
else throw KotlinReflectionInternalError("Unknown origin of $function (${function.javaClass})")
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendParameters(callable: JavaCallable) {
|
||||
append("(")
|
||||
for (parameter in callable.getValueParameters()) {
|
||||
appendJavaType(parameter.getType())
|
||||
}
|
||||
append(")")
|
||||
}
|
||||
|
||||
// TODO: verify edge cases when it's possible to reference generic functions
|
||||
private tailRecursive fun StringBuilder.appendJavaType(type: JavaType) {
|
||||
when (type) {
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package kotlin.reflect.jvm
|
||||
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.jvm.internal.KClassImpl
|
||||
import kotlin.reflect.jvm.internal.KMutablePropertyImpl
|
||||
import kotlin.reflect.jvm.internal.KPackageImpl
|
||||
import kotlin.reflect.jvm.internal.KPropertyImpl
|
||||
import kotlin.reflect.jvm.internal.*
|
||||
|
||||
// Kotlin reflection -> Java reflection
|
||||
|
||||
@@ -64,6 +63,21 @@ public val KMutableProperty<*>.javaSetter: Method?
|
||||
get() = (this as? KMutablePropertyImpl<*>)?.javaSetter
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Java [Method] instance corresponding to the given Kotlin function,
|
||||
* or `null` if this function is a constructor or cannot be represented by a Java [Method].
|
||||
*/
|
||||
public val KFunction<*>.javaMethod: Method?
|
||||
get() = (this as? KFunctionImpl)?.javaMethod
|
||||
|
||||
/**
|
||||
* Returns a Java [Constructor] instance corresponding to the given Kotlin function,
|
||||
* or `null` if this function is not a constructor or cannot be represented by a Java [Constructor].
|
||||
*/
|
||||
@suppress("UNCHECKED_CAST")
|
||||
public val <T> KFunction<T>.javaConstructor: Constructor<T>?
|
||||
get() = (this as? KFunctionImpl)?.javaConstructor as Constructor<T>?
|
||||
|
||||
|
||||
|
||||
// Java reflection -> Kotlin reflection
|
||||
@@ -82,7 +96,8 @@ public val <T> Class<T>.kotlin: KClass<T>
|
||||
* for more information.
|
||||
*/
|
||||
public val Class<*>.kotlinPackage: KPackage?
|
||||
get() = if (getAnnotation(javaClass<kotlin.jvm.internal.KotlinPackage>()) != null) KPackageImpl(this) else null
|
||||
get() = if (getSimpleName().endsWith("Package") &&
|
||||
getAnnotation(javaClass<kotlin.jvm.internal.KotlinPackage>()) != null) KPackageImpl(this) else null
|
||||
|
||||
|
||||
/**
|
||||
@@ -94,10 +109,38 @@ public val Field.kotlin: KProperty<*>?
|
||||
get() {
|
||||
if (isSynthetic()) return null
|
||||
|
||||
val clazz = getDeclaringClass().kotlin as KClassImpl
|
||||
|
||||
// TODO: fields in package parts
|
||||
// TODO: optimize (search by name)
|
||||
return clazz.properties.firstOrNull { p: KProperty<*> ->
|
||||
(p as KPropertyImpl<*>).javaField == this
|
||||
}
|
||||
return getDeclaringClass().kotlin.properties.firstOrNull { it.javaField == this }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a [KFunction] instance corresponding to the given Java [Method] instance,
|
||||
* or `null` if this method cannot be represented by a Kotlin function
|
||||
* (for example, if it is a synthetic method).
|
||||
*/
|
||||
public val Method.kotlinFunction: KFunction<*>?
|
||||
get() {
|
||||
if (isSynthetic()) return null
|
||||
|
||||
if (Modifier.isStatic(getModifiers())) {
|
||||
getDeclaringClass().kotlinPackage?.let { pkg ->
|
||||
return pkg.functions.firstOrNull { it.javaMethod == this }
|
||||
}
|
||||
}
|
||||
|
||||
return getDeclaringClass().kotlin.functions.firstOrNull { it.javaMethod == this }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [KFunction] instance corresponding to the given Java [Constructor] instance,
|
||||
* or `null` if this constructor cannot be represented by a Kotlin function
|
||||
* (for example, if it is a synthetic constructor).
|
||||
*/
|
||||
public val <T> Constructor<T>.kotlinFunction: KFunction<T>?
|
||||
get() {
|
||||
if (isSynthetic()) return null
|
||||
|
||||
return getDeclaringClass().kotlin.constructors.firstOrNull { it.javaConstructor == this }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user