Support KClass.functions and declaredFunctions

This commit is contained in:
Alexander Udalov
2015-07-08 03:59:35 +03:00
parent 50dbda1e1a
commit da5cffdb60
7 changed files with 105 additions and 9 deletions
@@ -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"
}
@@ -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<A>().kotlin.extensionProperties.single()
return if ("$p" == "var A.(kotlin.String.)id") "OK" else "Fail $p"
val q = javaClass<A>().kotlin.declaredFunctions.single()
if ("$q" != "fun A.(kotlin.Int.)foo(): kotlin.Double") return "Fail q $q"
return "OK"
}
@@ -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"
@@ -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");
@@ -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 <T> KClass<T>.functions: Collection<KFunction<*>>
get() = (this as KClassImpl<T>)
.getMembers(declaredOnly = false, nonExtensions = true, extensions = true)
.filterIsInstance<KFunction<*>>()
.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<KFunction<*>>
get() = (this as KClassImpl)
.getMembers(declaredOnly = true, nonExtensions = true, extensions = true)
.filterIsInstance<KFunction<*>>()
.toList()
/**
* Returns non-extension properties declared in this class and all of its superclasses.
*/
@@ -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<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
}
.map { descriptor ->
descriptor.accept(object : DeclarationDescriptorVisitorEmptyBodies<KCallable<*>?, 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<T, Any?>(this@KClassImpl, descriptor)
else KProperty1Impl<T, Any?>(this@KClassImpl, descriptor)
}
@@ -102,6 +106,12 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
else KProperty2Impl<T, Any?, Any?>(this@KClassImpl, descriptor)
}
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?): KCallable<*>? {
if (skipCallable(descriptor)) return null
return KFunctionImpl(this@KClassImpl, descriptor)
}
}, null)
}
.filterNotNull()
@@ -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 {