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
This commit is contained in:
Alexander Udalov
2016-03-02 13:47:47 +03:00
parent 6429dd4b04
commit 14b1a3a048
5 changed files with 65 additions and 14 deletions
@@ -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"
}
@@ -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") @TestMetadata("compiler/testData/codegen/boxWithStdlib/regressions")
@@ -21,8 +21,7 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
import java.lang.reflect.Field import java.lang.reflect.Field
import kotlin.reflect.jvm.internal.JvmPropertySignature.JavaField import kotlin.reflect.jvm.internal.JvmPropertySignature.*
import kotlin.reflect.jvm.internal.JvmPropertySignature.KotlinProperty
internal abstract class DescriptorBasedProperty<out R> protected constructor( internal abstract class DescriptorBasedProperty<out R> protected constructor(
override val container: KDeclarationContainerImpl, override val container: KDeclarationContainerImpl,
@@ -70,6 +69,7 @@ internal abstract class DescriptorBasedProperty<out R> protected constructor(
} }
} }
is JavaField -> jvmSignature.field is JavaField -> jvmSignature.field
is JavaMethodProperty -> null
} }
} }
@@ -25,6 +25,7 @@ import java.lang.reflect.Field
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import kotlin.reflect.KMutableProperty import kotlin.reflect.KMutableProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KotlinReflectionInternalError
internal interface KPropertyImpl<out R> : KProperty<R>, KCallableImpl<R> { internal interface KPropertyImpl<out R> : KProperty<R>, KCallableImpl<R> {
val javaField: Field? val javaField: Field?
@@ -98,10 +99,10 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean
} }
return false return false
} }
fun isJvmStaticProperty() = fun isJvmStaticProperty() =
property.descriptor.annotations.findAnnotation(JVM_STATIC) != null property.descriptor.annotations.findAnnotation(JVM_STATIC) != null
fun isNotNullProperty() = fun isNotNullProperty() =
!TypeUtils.isNullableType(property.descriptor.type) !TypeUtils.isNullableType(property.descriptor.type)
@@ -150,5 +151,9 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean
is JvmPropertySignature.JavaField -> { is JvmPropertySignature.JavaField -> {
computeFieldCaller(jvmSignature.field) computeFieldCaller(jvmSignature.field)
} }
is JvmPropertySignature.JavaMethodProperty -> {
if (!isGetter) throw KotlinReflectionInternalError("Setter requested for special built-in $this")
FunctionCaller.InstanceMethod(jvmSignature.method)
}
} }
} }
@@ -63,10 +63,7 @@ internal sealed class JvmFunctionSignature {
} }
class JavaMethod(val method: Method) : JvmFunctionSignature() { class JavaMethod(val method: Method) : JvmFunctionSignature() {
override fun asString(): String = override fun asString(): String = method.signature
method.name +
method.parameterTypes.joinToString(separator = "", prefix = "(", postfix = ")") { it.desc } +
method.returnType.desc
} }
class JavaConstructor(val constructor: Constructor<*>) : JvmFunctionSignature() { class JavaConstructor(val constructor: Constructor<*>) : JvmFunctionSignature() {
@@ -132,6 +129,10 @@ internal sealed class JvmPropertySignature {
override fun asString(): String = string override fun asString(): String = string
} }
class JavaMethodProperty(val method: Method) : JvmPropertySignature() {
override fun asString(): String = method.signature
}
class JavaField(val field: Field) : JvmPropertySignature() { class JavaField(val field: Field) : JvmPropertySignature() {
override fun asString(): String = override fun asString(): String =
JvmAbi.getterName(field.name) + 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 { internal object RuntimeTypeMapper {
fun mapSignature(possiblySubstitutedFunction: FunctionDescriptor): JvmFunctionSignature { fun mapSignature(possiblySubstitutedFunction: FunctionDescriptor): JvmFunctionSignature {
// Fake overrides don't have a source element, so we need to take a declaration. // 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 // 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. " + 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 -> { is JavaMethodDescriptor -> {
val method = ((function.source as? JavaSourceElement)?.javaElement as? ReflectJavaMethod)?.member ?: val method = ((function.source as? JavaSourceElement)?.javaElement as? ReflectJavaMethod)?.member ?:
@@ -184,21 +190,25 @@ internal object RuntimeTypeMapper {
} }
fun mapPropertySignature(possiblyOverriddenProperty: PropertyDescriptor): JvmPropertySignature { fun mapPropertySignature(possiblyOverriddenProperty: PropertyDescriptor): JvmPropertySignature {
val property = DescriptorUtils.unwrapFakeOverride(possiblyOverriddenProperty) val property = DescriptorUtils.unwrapFakeOverride(possiblyOverriddenProperty).original
if (property is DeserializedPropertyDescriptor) { if (property is DeserializedPropertyDescriptor) {
val proto = property.proto val proto = property.proto
if (!proto.hasExtension(JvmProtoBuf.propertySignature)) { 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( return JvmPropertySignature.KotlinProperty(
property, proto, proto.getExtension(JvmProtoBuf.propertySignature), property.nameResolver, property.typeTable property, proto, proto.getExtension(JvmProtoBuf.propertySignature), property.nameResolver, property.typeTable
) )
} }
else if (property is JavaPropertyDescriptor) { else if (property is JavaPropertyDescriptor) {
val field = ((property.source as? JavaSourceElement)?.javaElement as? ReflectJavaField)?.member ?: val element = (property.source as? JavaSourceElement)?.javaElement
throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property") when (element) {
is ReflectJavaField -> return JvmPropertySignature.JavaField(element.member)
return JvmPropertySignature.JavaField(field) 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})") else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})")
} }