From 936bede8b188b0b613fd0eb363ee5729e0c08741 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 15 Jul 2015 23:33:37 +0300 Subject: [PATCH] Support mapping between Kotlin functions and JVM methods/constructors --- .../reflection/mapping/javaConstructor.java | 7 ++ .../reflection/mapping/javaConstructor.kt | 12 +++ .../reflection/mapping/javaMethods.java | 9 +++ .../reflection/mapping/javaMethods.kt | 19 +++++ .../reflection/mapping/constructor.kt | 35 +++++++++ .../reflection/mapping/functions.kt | 27 +++++++ ...ackBoxAgainstJavaCodegenTestGenerated.java | 12 +++ ...lackBoxWithStdlibCodegenTestGenerated.java | 12 +++ .../structure/reflect/ReflectJavaMember.kt | 2 +- .../jvm/internal/DescriptorBasedProperty.kt | 4 +- .../jvm/internal/KCallableContainerImpl.kt | 74 ++++++++++++++----- .../reflect/jvm/internal/KFunctionImpl.kt | 58 +++++++++++++++ .../reflect/jvm/internal/RuntimeTypeMapper.kt | 27 +++++-- .../src/kotlin/reflect/jvm/mapping.kt | 63 +++++++++++++--- 14 files changed, 323 insertions(+), 38 deletions(-) create mode 100644 compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.java create mode 100644 compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.java create mode 100644 compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/mapping/constructor.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/mapping/functions.kt diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.java b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.java new file mode 100644 index 00000000000..72e8632823e --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.java @@ -0,0 +1,7 @@ +public class javaConstructor { + public final String result; + + public javaConstructor(String result) { + this.result = result; + } +} diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.kt b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.kt new file mode 100644 index 00000000000..17edc8625ca --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaConstructor.kt @@ -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 +} diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.java b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.java new file mode 100644 index 00000000000..a1a6dd5380d --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.java @@ -0,0 +1,9 @@ +public class javaMethods { + public String f(String s) { + return s; + } + + public static String g(String s) { + return s; + } +} diff --git a/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.kt b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.kt new file mode 100644 index 00000000000..c03578eb4ca --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaMethods.kt @@ -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" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/mapping/constructor.kt b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/constructor.kt new file mode 100644 index 00000000000..59385dd50a2 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/constructor.kt @@ -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" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/mapping/functions.kt b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/functions.kt new file mode 100644 index 00000000000..2adf3f89de1 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/mapping/functions.kt @@ -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) { + 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" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index 5afb47422e4..466389a6df4 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -510,11 +510,23 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod 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") public void testJavaFields() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/reflection/mapping/javaFields.kt"); 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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 2316b7a9ec9..816529baacd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -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); } + @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") public void testExtensionProperty() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/extensionProperty.kt"); 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") public void testMappedClassIsEqualToClassLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/mapping/mappedClassIsEqualToClassLiteral.kt"); diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaMember.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaMember.kt index 034a502f1b9..c92a454c8e7 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaMember.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/load/java/structure/reflect/ReflectJavaMember.kt @@ -26,7 +26,7 @@ import java.lang.reflect.Type import java.util.ArrayList 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 diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt index 9e4bf5b8b1e..a3872938acd 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt @@ -75,14 +75,14 @@ abstract class DescriptorBasedProperty 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) } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt index 0e5b9e5aec1..72fc2c4e479 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableContainerImpl.kt @@ -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 == "") 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()) != "") 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> { + 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, diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt index 18532e385cd..6bd32b6ab21 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -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(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 != "") { + 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 == "") { + 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 { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt index 1bb2c0e760e..d284136bce1 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -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("") + 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) { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt index da985344e3e..931b75109c6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/mapping.kt @@ -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 KFunction.javaConstructor: Constructor? + get() = (this as? KFunctionImpl)?.javaConstructor as Constructor? + // Java reflection -> Kotlin reflection @@ -82,7 +96,8 @@ public val Class.kotlin: KClass * for more information. */ public val Class<*>.kotlinPackage: KPackage? - get() = if (getAnnotation(javaClass()) != null) KPackageImpl(this) else null + get() = if (getSimpleName().endsWith("Package") && + getAnnotation(javaClass()) != 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 Constructor.kotlinFunction: KFunction? + get() { + if (isSynthetic()) return null + + return getDeclaringClass().kotlin.constructors.firstOrNull { it.javaConstructor == this } }