Add KFunction.name, support reflection for function references

#KT-7694 Fixed
This commit is contained in:
Alexander Udalov
2015-06-17 20:37:57 +03:00
parent ab297a4da0
commit 3549e1e035
16 changed files with 302 additions and 54 deletions
@@ -0,0 +1,8 @@
import kotlin.test.assertEquals
class A
fun box(): String {
assertEquals("<init>", ::A.name)
return "OK"
}
@@ -0,0 +1,5 @@
fun box(): String {
fun OK() {}
return ::OK.name
}
@@ -0,0 +1,6 @@
import kotlin.platform.platformName
@platformName("Fail")
fun OK() {}
fun box() = ::OK.name
@@ -0,0 +1,16 @@
import kotlin.test.assertEquals
fun foo() {}
class A {
fun bar() = ""
}
fun Int.baz() = this
fun box(): String {
assertEquals("foo", ::foo.name)
assertEquals("bar", A::bar.name)
assertEquals("baz", Int::baz.name)
return "OK"
}
@@ -2821,6 +2821,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/constructors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Constructors extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInConstructors() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/constructors"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("constructorName.kt")
public void testConstructorName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/constructors/constructorName.kt");
doTestWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/enclosing")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2974,6 +2989,33 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Functions extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInFunctions() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/functions"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("localFunctionName.kt")
public void testLocalFunctionName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions/localFunctionName.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("platformName.kt")
public void testPlatformName() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions/platformName.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("simpleNames.kt")
public void testSimpleNames() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/functions/simpleNames.kt");
doTestWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -19,4 +19,4 @@ package kotlin.reflect
/**
* Represents a function with introspection capabilities.
*/
public interface KFunction<out R> : Function<R>
public interface KFunction<out R> : KCallable<R>, Function<R>
@@ -29,7 +29,7 @@ import java.util.Arrays
public class ReflectJavaClass(
private val klass: Class<*>
) : ReflectJavaElement(), ReflectJavaAnnotationOwner, ReflectJavaModifierListOwner, JavaClass {
override val element: AnnotatedElement get() = klass
override val element: Class<*> get() = klass
override val modifiers: Int get() = klass.getModifiers()
@@ -26,7 +26,7 @@ import java.lang.reflect.Method
import java.lang.reflect.TypeVariable
public class ReflectJavaTypeParameter(
private val typeVariable: TypeVariable<*>
public val typeVariable: TypeVariable<*>
) : ReflectJavaElement(), JavaTypeParameter {
override fun getUpperBounds(): List<ReflectJavaClassifierType> {
val bounds = typeVariable.getBounds().map { bound -> ReflectJavaClassifierType(bound) }
@@ -16,9 +16,7 @@
package org.jetbrains.kotlin.builtins.functions
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -32,7 +30,7 @@ class FunctionClassScope(
private val storageManager: StorageManager,
private val functionClass: FunctionClassDescriptor
) : JetScopeImpl() {
private val allFunctions = storageManager.createLazyValue {
private val allDescriptors = storageManager.createLazyValue {
if (functionClass.functionKind == FunctionClassDescriptor.Kind.Function) {
val invoke = FunctionInvokeDescriptor.create(functionClass)
(listOf(invoke) + createFakeOverrides(invoke)).toReadOnlyList()
@@ -45,35 +43,42 @@ class FunctionClassScope(
override fun getContainingDeclaration() = functionClass
override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> {
if (!kindFilter.acceptsKinds(DescriptorKindFilter.FUNCTIONS_MASK)) return listOf()
return allFunctions()
if (!kindFilter.acceptsKinds(DescriptorKindFilter.CALLABLES.kindMask)) return listOf()
return allDescriptors()
}
override fun getFunctions(name: Name): Collection<FunctionDescriptor> {
return allFunctions().filter { it.getName() == name }
return allDescriptors().filterIsInstance<FunctionDescriptor>().filter { it.getName() == name }
}
private fun createFakeOverrides(invoke: FunctionDescriptor?): List<FunctionDescriptor> {
val result = ArrayList<FunctionDescriptor>(3)
val allSuperDescriptors = functionClass.getTypeConstructor().getSupertypes().flatMap { it.getMemberScope().getAllDescriptors() }
for ((name, descriptors) in allSuperDescriptors.groupBy { it.getName() }) {
@suppress("UNCHECKED_CAST")
OverridingUtil.generateOverridesInFunctionGroup(
name,
/* membersFromSupertypes = */ descriptors as Collection<FunctionDescriptor>,
/* membersFromCurrent = */ if (name == invoke?.getName()) listOf(invoke) else listOf(),
functionClass,
object : OverridingUtil.DescriptorSink {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, null)
result.add(fakeOverride as FunctionDescriptor)
}
override fun getProperties(name: Name): Collection<VariableDescriptor> {
return allDescriptors().filterIsInstance<PropertyDescriptor>().filter { it.getName() == name }
}
override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("Conflict in scope of ${getContainingDeclaration()}: $fromSuper vs $fromCurrent")
private fun createFakeOverrides(invoke: FunctionDescriptor?): List<DeclarationDescriptor> {
val result = ArrayList<DeclarationDescriptor>(3)
val allSuperDescriptors = functionClass.getTypeConstructor().getSupertypes()
.flatMap { it.getMemberScope().getAllDescriptors() }
.filterIsInstance<CallableMemberDescriptor>()
for ((name, group) in allSuperDescriptors.groupBy { it.getName() }) {
for ((isFunction, descriptors) in group.groupBy { it is FunctionDescriptor }) {
OverridingUtil.generateOverridesInFunctionGroup(
name,
/* membersFromSupertypes = */ descriptors,
/* membersFromCurrent = */ if (isFunction && name == invoke?.getName()) listOf(invoke) else listOf(),
functionClass,
object : OverridingUtil.DescriptorSink {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, null)
result.add(fakeOverride)
}
override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("Conflict in scope of ${getContainingDeclaration()}: $fromSuper vs $fromCurrent")
}
}
}
)
)
}
}
return result
@@ -16,6 +16,7 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.java.structure.reflect.classLoader
@@ -29,8 +30,8 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.JvmType.PrimitiveType.*
import java.lang.reflect.Field
import java.lang.reflect.Method
import kotlin.reflect.KotlinReflectionInternalError
import kotlin.reflect.KDeclarationContainer
import kotlin.reflect.KotlinReflectionInternalError
abstract class KCallableContainerImpl : KDeclarationContainer {
// Note: this is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
@@ -44,7 +45,7 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
fun findPropertyDescriptor(name: String, receiverDesc: String? = null): PropertyDescriptor {
val properties = scope
.getProperties(Name.identifier(name))
.getProperties(Name.guess(name))
.filter { descriptor ->
descriptor is PropertyDescriptor &&
descriptor.getName().asString() == name &&
@@ -55,16 +56,34 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
}
if (properties.size() != 1) {
val debugText = if (receiverDesc == null) name else "$receiverDesc.$name"
val debugText = if (receiverDesc == null) name else "'$receiverDesc.$name'"
throw KotlinReflectionInternalError(
if (properties.isEmpty()) "Property '$debugText' not resolved in $this"
else "${properties.size()} properties '$debugText' resolved in $this"
if (properties.isEmpty()) "Property $debugText not resolved in $this"
else "${properties.size()} properties $debugText resolved in $this: $properties"
)
}
return properties.single() as PropertyDescriptor
}
fun findFunctionDescriptor(name: String, signature: String): FunctionDescriptor {
val functions = scope
.getFunctions(Name.guess(name))
.filter { descriptor ->
RuntimeTypeMapper.mapSignature(descriptor) == signature
}
if (functions.size() != 1) {
val debugText = "'$name' (JVM signature: $signature)"
throw KotlinReflectionInternalError(
if (functions.isEmpty()) "Function $debugText not resolved in $this"
else "${functions.size()} functions $debugText resolved in $this: $functions"
)
}
return functions.single()
}
// TODO: check resulting method's return type
fun findMethodBySignature(signature: JvmProtoBuf.JvmMethodSignature, nameResolver: NameResolver, declared: Boolean): Method? {
val name = nameResolver.getString(signature.getName())
@@ -19,6 +19,7 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.scopes.ChainedScope
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
import kotlin.reflect.*
@@ -39,7 +40,9 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass)
override val scope: JetScope get() = descriptor.getDefaultType().getMemberScope()
override val scope: JetScope get() = ChainedScope(
descriptor, "KClassImpl scope", descriptor.getDefaultType().getMemberScope(), descriptor.getStaticScope()
)
override val simpleName: String? get() {
if (jClass.isAnonymousClass()) return null
@@ -16,6 +16,41 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import kotlin.jvm.internal.FunctionImpl
import kotlin.reflect.KFunction
abstract class KFunctionImpl<out R> : FunctionImpl()
abstract class KFunctionImpl private constructor(
container: KCallableContainerImpl,
name: String,
signature: String,
descriptorInitialValue: FunctionDescriptor?
) : KFunction<Any?>, FunctionImpl() {
constructor(container: KCallableContainerImpl, name: String, signature: String): this(container, name, signature, null)
constructor(container: KCallableContainerImpl, descriptor: FunctionDescriptor): this(
container, descriptor.getName().asString(), RuntimeTypeMapper.mapSignature(descriptor), descriptor
)
protected val descriptor: FunctionDescriptor by ReflectProperties.lazySoft<FunctionDescriptor>(descriptorInitialValue) {
container.findFunctionDescriptor(name, signature)
}
override val name: String get() = descriptor.getName().asString()
override fun getArity(): Int {
// TODO: test?
return descriptor.getValueParameters().size() +
(if (descriptor.getDispatchReceiverParameter() != null) 1 else 0) +
(if (descriptor.getExtensionReceiverParameter() != null) 1 else 0)
}
override fun equals(other: Any?): Boolean =
other is KFunctionImpl && descriptor == other.descriptor
override fun hashCode(): Int =
descriptor.hashCode()
override fun toString(): String =
ReflectionObjectRenderer.renderFunction(descriptor)
}
@@ -44,7 +44,7 @@ public class ReflectionFactoryImpl extends ReflectionFactory {
@Override
public KFunction function(FunctionReference f) {
return f;
return new KTopLevelFreeFunctionImpl((KPackageImpl) f.getOwner(), f.getName(), f.getSignature());
}
// Properties
@@ -16,6 +16,8 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
@@ -23,29 +25,47 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
object ReflectionObjectRenderer {
private val renderer = DescriptorRenderer.FQ_NAMES_IN_TYPES
private fun StringBuilder.appendReceiverType(receiver: ReceiverParameterDescriptor?) {
if (receiver != null) {
append(renderer.renderType(receiver.getType()))
append(".")
}
}
private fun StringBuilder.appendReceiversAndName(callable: CallableDescriptor) {
val dispatchReceiver = callable.getDispatchReceiverParameter()
val extensionReceiver = callable.getExtensionReceiverParameter()
appendReceiverType(dispatchReceiver)
val addParentheses = dispatchReceiver != null && extensionReceiver != null
if (addParentheses) append("(")
appendReceiverType(extensionReceiver)
if (addParentheses) append(")")
append(renderer.renderName(callable.getName()))
}
// TODO: include visibility, return type
fun renderProperty(descriptor: PropertyDescriptor): String {
fun StringBuilder.appendReceiverType(receiver: ReceiverParameterDescriptor?) {
if (receiver != null) {
append(renderer.renderType(receiver.getType()))
append(".")
}
}
return StringBuilder {
append(if (descriptor.isVar()) "var " else "val ")
appendReceiversAndName(descriptor)
}.toString()
}
val dispatchReceiver = descriptor.getDispatchReceiverParameter()
val extensionReceiver = descriptor.getExtensionReceiverParameter()
fun renderFunction(descriptor: FunctionDescriptor): String {
// TODO: add tests
return StringBuilder {
append("fun ")
appendReceiversAndName(descriptor)
appendReceiverType(dispatchReceiver)
descriptor.getValueParameters().joinTo(this, separator = ", ", prefix = "(", postfix = ")") {
renderer.renderType(it.getType()) // TODO: vararg
}
val addParentheses = dispatchReceiver != null && extensionReceiver != null
if (addParentheses) append("(")
appendReceiverType(extensionReceiver)
if (addParentheses) append(")")
append(renderer.renderName(descriptor.getName()))
append(": ")
append(renderer.renderType(descriptor.getReturnType()!!))
}.toString()
}
}
@@ -19,16 +19,24 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.load.java.structure.reflect.*
import org.jetbrains.kotlin.load.kotlin.SignatureDeserializer
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import kotlin.reflect.KotlinReflectionInternalError
object RuntimeTypeMapper {
// TODO: this logic must be shared with JetTypeMapper
@@ -62,6 +70,61 @@ object RuntimeTypeMapper {
return classDescriptor.classId.desc
}
fun mapSignature(function: FunctionDescriptor): String {
if (function is DeserializedSimpleFunctionDescriptor) {
val proto = function.getProto()
if (!proto.hasExtension(JvmProtoBuf.methodSignature)) {
throw KotlinReflectionInternalError("No metadata found for $function")
}
val signature = proto.getExtension(JvmProtoBuf.methodSignature)
return SignatureDeserializer(function.getNameResolver()).methodSignatureString(signature)
}
else if (function is JavaMethodDescriptor) {
val method = (function.getSource() as? JavaSourceElement)?.javaElement as? JavaMethod ?:
throw KotlinReflectionInternalError("Incorrect resolution sequence for Java method $function")
return StringBuilder {
append(method.getName().asString())
append("(")
for (parameter in method.getValueParameters()) {
appendJavaType(parameter.getType())
}
append(")")
appendJavaType(method.getReturnType())
}.toString()
}
else throw KotlinReflectionInternalError("Unknown origin of $function (${function.javaClass})")
}
// TODO: verify edge cases when it's possible to reference generic functions
private tailRecursive fun StringBuilder.appendJavaType(type: JavaType) {
when (type) {
is JavaPrimitiveType -> {
append(type.getType()?.let { JvmPrimitiveType.get(it).getDesc() } ?: "V")
}
is JavaArrayType -> {
append("[")
appendJavaType(type.getComponentType())
}
is JavaWildcardType -> {
val bound = type.getBound()
if (bound != null && type.isExtends()) appendJavaType(bound)
else append("Ljava/lang/Object;")
}
is JavaClassifierType -> {
val classifier = type.getClassifier()
when (classifier) {
is ReflectJavaClass ->
append(classifier.element.desc)
is ReflectJavaTypeParameter ->
appendJavaType(ReflectJavaType.create(classifier.typeVariable.getBounds().first()))
}
}
}
}
fun mapJvmClassToKotlinClassId(klass: Class<*>): ClassId {
if (klass.isArray()) {
klass.getComponentType().primitiveType?.let {
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import kotlin.reflect.KTopLevelFunction
class KTopLevelFreeFunctionImpl : KFunctionImpl, KTopLevelFunction<Any?> {
constructor(container: KPackageImpl, name: String, signature: String): super(container, name, signature)
constructor(container: KPackageImpl, descriptor: FunctionDescriptor): super(container, descriptor)
}