From 14b1a3a048a1dd0ce9768eb25b5ddcebff37c8ab Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 2 Mar 2016 13:47:47 +0300 Subject: [PATCH] Don't fail on requesting members of Java collection classes RuntimeTypeMapper assumed that JavaPropertyDescriptor was necessarily backed by JavaField, which has changed when we started to load Java method (or a pair of methods) overriding a Kotlin property as a property, not as a method. Another example of this situation is special built-in properties which are mapped to Java methods, e.g. java.util.Collection#size() <-> kotlin.Collection#size. This change adds support for the case of a property backed by a JavaMethod. Note that callable references or usage of any reflection API related to built-in members is not supported anyway and will currently fail with errors #KT-11258 Fixed --- .../getMembersOfStandardJavaClasses.kt | 21 ++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 15 +++++++++ .../jvm/internal/DescriptorBasedProperty.kt | 4 +-- .../reflect/jvm/internal/KPropertyImpl.kt | 7 +++- .../reflect/jvm/internal/RuntimeTypeMapper.kt | 32 ++++++++++++------- 5 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns/getMembersOfStandardJavaClasses.kt diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns/getMembersOfStandardJavaClasses.kt b/compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns/getMembersOfStandardJavaClasses.kt new file mode 100644 index 00000000000..d5ff8a0dadd --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns/getMembersOfStandardJavaClasses.kt @@ -0,0 +1,21 @@ +// FULL_JDK +// See KT-11258 Incorrect resolution sequence for Java field + +import java.util.* + +fun box(): String { + listOf( + ArrayList::class, + LinkedList::class, + AbstractList::class, + HashSet::class, + TreeSet::class, + HashMap::class, + TreeMap::class, + AbstractMap::class, + AbstractMap.SimpleEntry::class + ).map { + it.members.map(Any::toString) + } + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java index cfff5ce6775..7c1fd1b1281 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java @@ -4428,6 +4428,21 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } } } + + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SpecialBuiltIns extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSpecialBuiltIns() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("getMembersOfStandardJavaClasses.kt") + public void testGetMembersOfStandardJavaClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/specialBuiltIns/getMembersOfStandardJavaClasses.kt"); + doTestWithStdlib(fileName); + } + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/regressions") 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 8f019d187b7..d09a1802391 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/DescriptorBasedProperty.kt @@ -21,8 +21,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil import java.lang.reflect.Field -import kotlin.reflect.jvm.internal.JvmPropertySignature.JavaField -import kotlin.reflect.jvm.internal.JvmPropertySignature.KotlinProperty +import kotlin.reflect.jvm.internal.JvmPropertySignature.* internal abstract class DescriptorBasedProperty protected constructor( override val container: KDeclarationContainerImpl, @@ -70,6 +69,7 @@ internal abstract class DescriptorBasedProperty protected constructor( } } is JavaField -> jvmSignature.field + is JavaMethodProperty -> null } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt index c7fb4894252..370b1cdd2c4 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -25,6 +25,7 @@ import java.lang.reflect.Field import java.lang.reflect.Modifier import kotlin.reflect.KMutableProperty import kotlin.reflect.KProperty +import kotlin.reflect.KotlinReflectionInternalError internal interface KPropertyImpl : KProperty, KCallableImpl { val javaField: Field? @@ -98,10 +99,10 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean } return false } + fun isJvmStaticProperty() = property.descriptor.annotations.findAnnotation(JVM_STATIC) != null - fun isNotNullProperty() = !TypeUtils.isNullableType(property.descriptor.type) @@ -150,5 +151,9 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean is JvmPropertySignature.JavaField -> { computeFieldCaller(jvmSignature.field) } + is JvmPropertySignature.JavaMethodProperty -> { + if (!isGetter) throw KotlinReflectionInternalError("Setter requested for special built-in $this") + FunctionCaller.InstanceMethod(jvmSignature.method) + } } } 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 a35914bc592..3fa3e279e59 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -63,10 +63,7 @@ internal sealed class JvmFunctionSignature { } class JavaMethod(val method: Method) : JvmFunctionSignature() { - override fun asString(): String = - method.name + - method.parameterTypes.joinToString(separator = "", prefix = "(", postfix = ")") { it.desc } + - method.returnType.desc + override fun asString(): String = method.signature } class JavaConstructor(val constructor: Constructor<*>) : JvmFunctionSignature() { @@ -132,6 +129,10 @@ internal sealed class JvmPropertySignature { override fun asString(): String = string } + class JavaMethodProperty(val method: Method) : JvmPropertySignature() { + override fun asString(): String = method.signature + } + class JavaField(val field: Field) : JvmPropertySignature() { override fun asString(): String = JvmAbi.getterName(field.name) + @@ -140,6 +141,11 @@ internal sealed class JvmPropertySignature { } } +private val Method.signature: String + get() = name + + parameterTypes.joinToString(separator = "", prefix = "(", postfix = ")") { it.desc } + + returnType.desc + internal object RuntimeTypeMapper { fun mapSignature(possiblySubstitutedFunction: FunctionDescriptor): JvmFunctionSignature { // Fake overrides don't have a source element, so we need to take a declaration. @@ -165,7 +171,7 @@ internal object RuntimeTypeMapper { } // If it's a deserialized function but has no JVM signature, it must be from built-ins throw KotlinReflectionInternalError("Reflection on built-in Kotlin types is not yet fully supported. " + - "No metadata found for $function") + "No metadata found for $function") } is JavaMethodDescriptor -> { val method = ((function.source as? JavaSourceElement)?.javaElement as? ReflectJavaMethod)?.member ?: @@ -184,21 +190,25 @@ internal object RuntimeTypeMapper { } fun mapPropertySignature(possiblyOverriddenProperty: PropertyDescriptor): JvmPropertySignature { - val property = DescriptorUtils.unwrapFakeOverride(possiblyOverriddenProperty) + val property = DescriptorUtils.unwrapFakeOverride(possiblyOverriddenProperty).original if (property is DeserializedPropertyDescriptor) { val proto = property.proto if (!proto.hasExtension(JvmProtoBuf.propertySignature)) { - throw KotlinReflectionInternalError("No metadata found for $property") + // If this property has no JVM signature, it must be from built-ins + throw KotlinReflectionInternalError("Reflection on built-in Kotlin types is not yet fully supported. " + + "No metadata found for $property") } return JvmPropertySignature.KotlinProperty( property, proto, proto.getExtension(JvmProtoBuf.propertySignature), property.nameResolver, property.typeTable ) } else if (property is JavaPropertyDescriptor) { - val field = ((property.source as? JavaSourceElement)?.javaElement as? ReflectJavaField)?.member ?: - throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property") - - return JvmPropertySignature.JavaField(field) + val element = (property.source as? JavaSourceElement)?.javaElement + when (element) { + is ReflectJavaField -> return JvmPropertySignature.JavaField(element.member) + is ReflectJavaMethod -> return JvmPropertySignature.JavaMethodProperty(element.member) + else -> throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property (source = $element)") + } } else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})") }