diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleGetFunctions.kt b/compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleGetFunctions.kt new file mode 100644 index 00000000000..80658d4b2ab --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleGetFunctions.kt @@ -0,0 +1,21 @@ +import kotlin.reflect.* + +open class A { + fun mem() {} + fun Int.memExt() {} +} + +class B : A() + +fun box(): String { + val all = A::class.functions.map { it.name }.toSortedList() + assert(all == listOf("equals", "hashCode", "mem", "memExt", "toString")) { "Fail A functions: ${A::class.functions}" } + + val declared = A::class.declaredFunctions.map { it.name }.toSortedList() + assert(declared == listOf("mem", "memExt")) { "Fail A declaredFunctions: ${A::class.declaredFunctions}" } + + val declaredSubclass = B::class.declaredFunctions.map { it.name }.toSortedList() + assert(declaredSubclass.isEmpty()) { "Fail B declaredFunctions: ${B::class.declaredFunctions}" } + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/memberExtensionToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/memberExtensionToString.kt index a5df279344b..762c03732cc 100644 --- a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/memberExtensionToString.kt +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/memberExtensionToString.kt @@ -5,9 +5,16 @@ class A { var String.id: String get() = this set(value) {} + + fun Int.foo(): Double = toDouble() } fun box(): String { val p = javaClass().kotlin.extensionProperties.single() return if ("$p" == "var A.(kotlin.String.)id") "OK" else "Fail $p" + + val q = javaClass().kotlin.declaredFunctions.single() + if ("$q" != "fun A.(kotlin.Int.)foo(): kotlin.Double") return "Fail q $q" + + return "OK" } diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/parameters/functionParameterNameAndIndex.kt b/compiler/testData/codegen/boxWithStdlib/reflection/parameters/functionParameterNameAndIndex.kt index 3d101c0a46d..5edeac991da 100644 --- a/compiler/testData/codegen/boxWithStdlib/reflection/parameters/functionParameterNameAndIndex.kt +++ b/compiler/testData/codegen/boxWithStdlib/reflection/parameters/functionParameterNameAndIndex.kt @@ -5,6 +5,8 @@ fun foo(bar: String): Int = bar.length() class A(val c: String) { fun foz(baz: Int) {} + + fun Double.mext(mez: Long) {} } fun Int.qux(zux: String) {} @@ -21,6 +23,8 @@ fun box(): String { checkParameters(A::foz, listOf(null, "baz")) checkParameters(Int::qux, listOf(null, "zux")) + checkParameters(A::class.functions.single { it.name == "mext" }, listOf(null, null, "mez")) + checkParameters(::A, listOf("c")) return "OK" diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 74ec03218ec..d1b59679596 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3027,6 +3027,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("simpleGetFunctions.kt") + public void testSimpleGetFunctions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleGetFunctions.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("simpleNames.kt") public void testSimpleNames() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleNames.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/KClassExtensions.kt b/core/reflection.jvm/src/kotlin/reflect/KClassExtensions.kt index 6e8b6c00cde..e37794e2ff4 100644 --- a/core/reflection.jvm/src/kotlin/reflect/KClassExtensions.kt +++ b/core/reflection.jvm/src/kotlin/reflect/KClassExtensions.kt @@ -18,6 +18,27 @@ package kotlin.reflect import kotlin.reflect.jvm.internal.KClassImpl +/** + * Returns all functions declared in this class and all of its superclasses. + * If this is a Java class, it includes all non-static methods declared in the class and the superclasses, + * as well as static methods declared in the class. + */ +public val KClass.functions: Collection> + get() = (this as KClassImpl) + .getMembers(declaredOnly = false, nonExtensions = true, extensions = true) + .filterIsInstance>() + .toList() + +/** + * Returns all functions declared in this class. + * If this is a Java class, it includes both non-static and static methods. + */ +public val KClass<*>.declaredFunctions: Collection> + get() = (this as KClassImpl) + .getMembers(declaredOnly = true, nonExtensions = true, extensions = true) + .filterIsInstance>() + .toList() + /** * Returns non-extension properties declared in this class and all of its superclasses. */ diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 74b8b6fc8ba..b7a58e346f5 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -16,9 +16,7 @@ package kotlin.reflect.jvm.internal -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.scopes.ChainedScope @@ -86,14 +84,20 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K } .map { descriptor -> descriptor.accept(object : DeclarationDescriptorVisitorEmptyBodies?, Nothing>() { - override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?): KCallable<*>? { - if (declaredOnly && !descriptor.getKind().isReal()) return null + private fun skipCallable(descriptor: CallableMemberDescriptor): Boolean { + if (declaredOnly && !descriptor.getKind().isReal()) return true val isExtension = descriptor.getExtensionReceiverParameter() != null - if (isExtension && !extensions) return null - if (!isExtension && !nonExtensions) return null + if (isExtension && !extensions) return true + if (!isExtension && !nonExtensions) return true - return if (!isExtension) { + return false + } + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?): KCallable<*>? { + if (skipCallable(descriptor)) return null + + return if (descriptor.getExtensionReceiverParameter() == null) { if (descriptor.isVar()) KMutableProperty1Impl(this@KClassImpl, descriptor) else KProperty1Impl(this@KClassImpl, descriptor) } @@ -102,6 +106,12 @@ class KClassImpl(override val jClass: Class) : KCallableContainerImpl(), K else KProperty2Impl(this@KClassImpl, descriptor) } } + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?): KCallable<*>? { + if (skipCallable(descriptor)) return null + + return KFunctionImpl(this@KClassImpl, descriptor) + } }, null) } .filterNotNull() 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 4a960067e1e..74fef9465d1 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -41,7 +41,9 @@ object RuntimeTypeMapper { if (function is DeserializedSimpleFunctionDescriptor) { val proto = function.getProto() if (!proto.hasExtension(JvmProtoBuf.methodSignature)) { - throw KotlinReflectionInternalError("No metadata found for $function") + // If it's a deserialized function but has no JVM signature, it must be from built-ins + return mapIntrinsicFunctionSignature(function) ?: + throw KotlinReflectionInternalError("No metadata found for $function") } val signature = proto.getExtension(JvmProtoBuf.methodSignature) return SignatureDeserializer(function.getNameResolver()).methodSignatureString(signature) @@ -128,6 +130,31 @@ object RuntimeTypeMapper { else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})") } + private fun mapIntrinsicFunctionSignature(function: DeserializedSimpleFunctionDescriptor): String? { + val parameters = function.getValueParameters() + + when (function.getName().asString()) { + "equals" -> { + if (parameters.size() == 1 && KotlinBuiltIns.isNullableAny(parameters.single().getType())) { + return "equals(Ljava/lang/Object;)Z" + } + } + "hashCode" -> { + if (parameters.isEmpty()) { + return "hashCode()I" + } + } + "toString" -> { + if (parameters.isEmpty()) { + return "toString()Ljava/lang/String;" + } + } + // TODO: generalize and support other functions from built-ins + } + + return null + } + fun mapJvmClassToKotlinClassId(klass: Class<*>): ClassId { if (klass.isArray()) { klass.getComponentType().primitiveType?.let {