Support setters for Java-method-based properties in reflection
Also heuristically support the corner case of multiple properties with the same name and signature in a Java class, see the comment in KDeclarationContainerImpl #KT-11258 Fixed
This commit is contained in:
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Method
|
||||
import java.util.*
|
||||
import kotlin.jvm.internal.ClassBasedDeclarationContainer
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
|
||||
@@ -106,6 +107,25 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
}
|
||||
|
||||
if (properties.size != 1) {
|
||||
// Try working around the case of a Java class with a field 'foo' and a method 'getFoo' which overrides Kotlin property 'foo'.
|
||||
// Such class has two property descriptors with the name 'foo' in its scope and they may be indistinguishable from each other.
|
||||
// However, it's not possible to write 'A::foo' if they're indistinguishable; overload resolution would not be able to choose
|
||||
// between the two. So we assume that one of the properties must have a greater visibility than the other, and try loading
|
||||
// that one first.
|
||||
// Note that this heuristic may result in _incorrect behavior_ if a KProperty object for a less visible property is obtained
|
||||
// by other means (through reflection API) and then the soft-referenced descriptor instance for that property is invalidated
|
||||
// because there's no more memory left. In that case the KProperty object will now point to another (more visible) property.
|
||||
// TODO: consider writing additional info (besides signature) to property reference objects to distinguish them in this case
|
||||
|
||||
val mostVisibleProperties = properties
|
||||
.groupBy { it.visibility }
|
||||
.toSortedMap(Comparator { first, second ->
|
||||
Visibilities.compare(first, second) ?: 0
|
||||
}).values.last()
|
||||
if (mostVisibleProperties.size == 1) {
|
||||
return mostVisibleProperties.first()
|
||||
}
|
||||
|
||||
val debugText = "'$name' (JVM signature: $signature)"
|
||||
throw KotlinReflectionInternalError(
|
||||
if (properties.isEmpty()) "Property $debugText not resolved in $this"
|
||||
|
||||
@@ -152,8 +152,12 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean
|
||||
computeFieldCaller(jvmSignature.field)
|
||||
}
|
||||
is JvmPropertySignature.JavaMethodProperty -> {
|
||||
if (!isGetter) throw KotlinReflectionInternalError("Setter requested for special built-in $this")
|
||||
FunctionCaller.InstanceMethod(jvmSignature.method)
|
||||
val method =
|
||||
if (isGetter) jvmSignature.getterMethod
|
||||
else jvmSignature.setterMethod ?: throw KotlinReflectionInternalError(
|
||||
"No source found for setter of Java method property: ${jvmSignature.getterMethod}"
|
||||
)
|
||||
FunctionCaller.InstanceMethod(method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +129,8 @@ internal sealed class JvmPropertySignature {
|
||||
override fun asString(): String = string
|
||||
}
|
||||
|
||||
class JavaMethodProperty(val method: Method) : JvmPropertySignature() {
|
||||
override fun asString(): String = method.signature
|
||||
class JavaMethodProperty(val getterMethod: Method, val setterMethod: Method?) : JvmPropertySignature() {
|
||||
override fun asString(): String = getterMethod.signature
|
||||
}
|
||||
|
||||
class JavaField(val field: Field) : JvmPropertySignature() {
|
||||
@@ -206,7 +206,10 @@ internal object RuntimeTypeMapper {
|
||||
val element = (property.source as? JavaSourceElement)?.javaElement
|
||||
when (element) {
|
||||
is ReflectJavaField -> return JvmPropertySignature.JavaField(element.member)
|
||||
is ReflectJavaMethod -> return JvmPropertySignature.JavaMethodProperty(element.member)
|
||||
is ReflectJavaMethod -> return JvmPropertySignature.JavaMethodProperty(
|
||||
element.member,
|
||||
((property.setter?.source as? JavaSourceElement)?.javaElement as? ReflectJavaMethod)?.member
|
||||
)
|
||||
else -> throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property (source = $element)")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user