Support mapping between Kotlin functions and JVM methods/constructors
This commit is contained in:
+7
@@ -0,0 +1,7 @@
|
|||||||
|
public class javaConstructor {
|
||||||
|
public final String result;
|
||||||
|
|
||||||
|
public javaConstructor(String result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.jvm.*
|
||||||
|
import javaConstructor as J
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val reference = ::J
|
||||||
|
val javaConstructor = reference.javaConstructor ?: return "Fail: no Constructor for reference"
|
||||||
|
val j = javaConstructor.newInstance("OK")
|
||||||
|
val kotlinConstructor = javaConstructor.kotlinFunction
|
||||||
|
if (reference != kotlinConstructor) return "Fail: reference != kotlinConstructor"
|
||||||
|
return j.result
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
public class javaMethods {
|
||||||
|
public String f(String s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String g(String s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.jvm.*
|
||||||
|
import javaMethods as J
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val f = J::f
|
||||||
|
val fm = f.javaMethod ?: return "Fail: no Method for f"
|
||||||
|
if (fm.invoke(J(), "abc") != "abc") return "Fail fm"
|
||||||
|
val ff = fm.kotlinFunction ?: return "Fail: no KFunction for fm"
|
||||||
|
if (f != ff) return "Fail f != ff"
|
||||||
|
|
||||||
|
val g = J::g
|
||||||
|
val gm = g.javaMethod ?: return "Fail: no Method for g"
|
||||||
|
if (gm.invoke(null, "ghi") != "ghi") return "Fail gm"
|
||||||
|
val gg = gm.kotlinFunction ?: return "Fail: no KFunction for gm"
|
||||||
|
if (g != gg) return "Fail g != gg"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.jvm.*
|
||||||
|
|
||||||
|
class K {
|
||||||
|
class Nested
|
||||||
|
inner class Inner
|
||||||
|
}
|
||||||
|
|
||||||
|
class Secondary {
|
||||||
|
constructor(x: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun check(f: KFunction<*>) {
|
||||||
|
assert(f.javaMethod == null, "Fail f method")
|
||||||
|
assert(f.javaConstructor != null, "Fail f constructor")
|
||||||
|
val c = f.javaConstructor!!
|
||||||
|
|
||||||
|
assert(c.kotlinFunction != null, "Fail m function")
|
||||||
|
val ff = c.kotlinFunction!!
|
||||||
|
|
||||||
|
assert(f == ff, "Fail f != ff")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
check(::K)
|
||||||
|
|
||||||
|
// Workaround KT-8596
|
||||||
|
val nested = K::Nested
|
||||||
|
check(nested)
|
||||||
|
|
||||||
|
check(K::Inner)
|
||||||
|
check(::Secondary)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import kotlin.reflect.*
|
||||||
|
import kotlin.reflect.jvm.*
|
||||||
|
|
||||||
|
class K {
|
||||||
|
fun foo(s: String): Int = s.length()
|
||||||
|
}
|
||||||
|
fun bar(s: String): Int = s.length()
|
||||||
|
fun String.baz(): Int = this.length()
|
||||||
|
|
||||||
|
fun check(f: KFunction<Int>) {
|
||||||
|
assert(f.javaConstructor == null, "Fail f constructor")
|
||||||
|
assert(f.javaMethod != null, "Fail f method")
|
||||||
|
val m = f.javaMethod!!
|
||||||
|
|
||||||
|
assert(m.kotlinFunction != null, "Fail m function")
|
||||||
|
val ff = m.kotlinFunction!!
|
||||||
|
|
||||||
|
assert(f == ff, "Fail f != ff")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
check(K::foo)
|
||||||
|
check(::bar)
|
||||||
|
check(String::baz)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+12
@@ -510,11 +510,23 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod
|
|||||||
doTestAgainstJava(fileName);
|
doTestAgainstJava(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaConstructor.kt")
|
||||||
|
public void testJavaConstructor() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.kt");
|
||||||
|
doTestAgainstJava(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("javaFields.kt")
|
@TestMetadata("javaFields.kt")
|
||||||
public void testJavaFields() throws Exception {
|
public void testJavaFields() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt");
|
||||||
doTestAgainstJava(fileName);
|
doTestAgainstJava(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaMethods.kt")
|
||||||
|
public void testJavaMethods() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.kt");
|
||||||
|
doTestAgainstJava(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/boxAgainstJava/reflection/properties")
|
@TestMetadata("compiler/testData/codegen/boxAgainstJava/reflection/properties")
|
||||||
|
|||||||
+12
@@ -3114,12 +3114,24 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/mapping"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/mapping"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constructor.kt")
|
||||||
|
public void testConstructor() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/constructor.kt");
|
||||||
|
doTestWithStdlib(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extensionProperty.kt")
|
@TestMetadata("extensionProperty.kt")
|
||||||
public void testExtensionProperty() throws Exception {
|
public void testExtensionProperty() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/extensionProperty.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/extensionProperty.kt");
|
||||||
doTestWithStdlib(fileName);
|
doTestWithStdlib(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functions.kt")
|
||||||
|
public void testFunctions() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/functions.kt");
|
||||||
|
doTestWithStdlib(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("mappedClassIsEqualToClassLiteral.kt")
|
@TestMetadata("mappedClassIsEqualToClassLiteral.kt")
|
||||||
public void testMappedClassIsEqualToClassLiteral() throws Exception {
|
public void testMappedClassIsEqualToClassLiteral() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/mappedClassIsEqualToClassLiteral.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/mappedClassIsEqualToClassLiteral.kt");
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import java.lang.reflect.Type
|
|||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
public abstract class ReflectJavaMember : ReflectJavaElement(), ReflectJavaAnnotationOwner, ReflectJavaModifierListOwner, JavaMember {
|
public abstract class ReflectJavaMember : ReflectJavaElement(), ReflectJavaAnnotationOwner, ReflectJavaModifierListOwner, JavaMember {
|
||||||
protected abstract val member: Member
|
internal abstract val member: Member
|
||||||
|
|
||||||
override val element: AnnotatedElement get() = member as AnnotatedElement
|
override val element: AnnotatedElement get() = member as AnnotatedElement
|
||||||
|
|
||||||
|
|||||||
@@ -75,14 +75,14 @@ abstract class DescriptorBasedProperty<out R> protected constructor(
|
|||||||
open val javaGetter: Method? by ReflectProperties.lazySoft {
|
open val javaGetter: Method? by ReflectProperties.lazySoft {
|
||||||
val proto = protoData
|
val proto = protoData
|
||||||
if (proto == null || !proto.signature.hasGetter()) null
|
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)
|
descriptor.getGetter()?.getVisibility()?.let { Visibilities.isPrivate(it) } ?: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
open val javaSetter: Method? by ReflectProperties.lazySoft {
|
open val javaSetter: Method? by ReflectProperties.lazySoft {
|
||||||
val proto = protoData
|
val proto = protoData
|
||||||
if (proto == null || !proto.signature.hasSetter()) null
|
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)
|
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.deserialization.NameResolver
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
|
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
|
||||||
|
import java.lang.reflect.Constructor
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import kotlin.reflect.KCallable
|
import kotlin.reflect.KCallable
|
||||||
@@ -141,22 +142,55 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check resulting method's return type
|
// 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 name = nameResolver.getString(signature.getName())
|
||||||
val classLoader = jClass.classLoader
|
if (name == "<init>") return null
|
||||||
val parameterTypes = signature.getParameterTypeList().map { jvmType ->
|
|
||||||
loadJvmType(jvmType, nameResolver, classLoader)
|
val parameterTypes = loadParameterTypes(nameResolver, signature)
|
||||||
}.toTypedArray()
|
|
||||||
|
// 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 {
|
return try {
|
||||||
if (declared) jClass.getDeclaredMethod(name, *parameterTypes)
|
if (declared) owner.getDeclaredMethod(name, *parameterTypes)
|
||||||
else jClass.getMethod(name, *parameterTypes)
|
else owner.getMethod(name, *parameterTypes)
|
||||||
}
|
}
|
||||||
catch (e: NoSuchMethodException) {
|
catch (e: NoSuchMethodException) {
|
||||||
null
|
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
|
// TODO: check resulting field's type
|
||||||
fun findFieldBySignature(
|
fun findFieldBySignature(
|
||||||
proto: ProtoBuf.Callable,
|
proto: ProtoBuf.Callable,
|
||||||
@@ -166,18 +200,11 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
|
|||||||
val name = nameResolver.getString(signature.getName())
|
val name = nameResolver.getString(signature.getName())
|
||||||
|
|
||||||
val owner =
|
val owner =
|
||||||
when {
|
implClassForCallable(nameResolver, proto) ?:
|
||||||
proto.hasExtension(JvmProtoBuf.implClassName) -> {
|
if (signature.getIsStaticInOuter()) {
|
||||||
val implClassName = nameResolver.getName(proto.getExtension(JvmProtoBuf.implClassName))
|
jClass.getEnclosingClass() ?: throw KotlinReflectionInternalError("Inconsistent metadata for field $name in $jClass")
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
else jClass
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
owner.getDeclaredField(name)
|
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(
|
private fun loadJvmType(
|
||||||
type: JvmProtoBuf.JvmType,
|
type: JvmProtoBuf.JvmType,
|
||||||
nameResolver: NameResolver,
|
nameResolver: NameResolver,
|
||||||
|
|||||||
@@ -18,6 +18,17 @@
|
|||||||
package kotlin.reflect.jvm.internal
|
package kotlin.reflect.jvm.internal
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
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.jvm.internal.FunctionImpl
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
|
|
||||||
@@ -34,10 +45,57 @@ open class KFunctionImpl protected constructor(
|
|||||||
container, descriptor.getName().asString(), RuntimeTypeMapper.mapSignature(descriptor), descriptor
|
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) {
|
override val descriptor: FunctionDescriptor by ReflectProperties.lazySoft<FunctionDescriptor>(descriptorInitialValue) {
|
||||||
container.findFunctionDescriptor(name, signature)
|
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 val name: String get() = descriptor.getName().asString()
|
||||||
|
|
||||||
override fun getArity(): Int {
|
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.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
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.JavaMethodDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
||||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||||
@@ -56,19 +57,31 @@ object RuntimeTypeMapper {
|
|||||||
|
|
||||||
return StringBuilder {
|
return StringBuilder {
|
||||||
append(method.getName().asString())
|
append(method.getName().asString())
|
||||||
|
appendParameters(method)
|
||||||
append("(")
|
|
||||||
for (parameter in method.getValueParameters()) {
|
|
||||||
appendJavaType(parameter.getType())
|
|
||||||
}
|
|
||||||
append(")")
|
|
||||||
|
|
||||||
appendJavaType(method.getReturnType())
|
appendJavaType(method.getReturnType())
|
||||||
}.toString()
|
}.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})")
|
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
|
// TODO: verify edge cases when it's possible to reference generic functions
|
||||||
private tailRecursive fun StringBuilder.appendJavaType(type: JavaType) {
|
private tailRecursive fun StringBuilder.appendJavaType(type: JavaType) {
|
||||||
when (type) {
|
when (type) {
|
||||||
|
|||||||
@@ -16,13 +16,12 @@
|
|||||||
|
|
||||||
package kotlin.reflect.jvm
|
package kotlin.reflect.jvm
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
import kotlin.reflect.*
|
import kotlin.reflect.*
|
||||||
import kotlin.reflect.jvm.internal.KClassImpl
|
import kotlin.reflect.jvm.internal.*
|
||||||
import kotlin.reflect.jvm.internal.KMutablePropertyImpl
|
|
||||||
import kotlin.reflect.jvm.internal.KPackageImpl
|
|
||||||
import kotlin.reflect.jvm.internal.KPropertyImpl
|
|
||||||
|
|
||||||
// Kotlin reflection -> Java reflection
|
// Kotlin reflection -> Java reflection
|
||||||
|
|
||||||
@@ -64,6 +63,21 @@ public val KMutableProperty<*>.javaSetter: Method?
|
|||||||
get() = (this as? KMutablePropertyImpl<*>)?.javaSetter
|
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
|
// Java reflection -> Kotlin reflection
|
||||||
@@ -82,7 +96,8 @@ public val <T> Class<T>.kotlin: KClass<T>
|
|||||||
* for more information.
|
* for more information.
|
||||||
*/
|
*/
|
||||||
public val Class<*>.kotlinPackage: KPackage?
|
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() {
|
get() {
|
||||||
if (isSynthetic()) return null
|
if (isSynthetic()) return null
|
||||||
|
|
||||||
val clazz = getDeclaringClass().kotlin as KClassImpl
|
// TODO: fields in package parts
|
||||||
|
|
||||||
// TODO: optimize (search by name)
|
// TODO: optimize (search by name)
|
||||||
return clazz.properties.firstOrNull { p: KProperty<*> ->
|
return getDeclaringClass().kotlin.properties.firstOrNull { it.javaField == this }
|
||||||
(p as KPropertyImpl<*>).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