Check return type when locating method in reflection
#KT-11824 Fixed
This commit is contained in:
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
data class Foo(val id: String) {
|
||||||
|
fun getId() = -42 // Fail
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return Foo::id.call(Foo("OK"))
|
||||||
|
}
|
||||||
@@ -11190,6 +11190,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyGetterAndGetFunctionDifferentReturnType.kt")
|
||||||
|
public void testPropertyGetterAndGetFunctionDifferentReturnType() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/call/propertyGetterAndGetFunctionDifferentReturnType.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("returnUnit.kt")
|
@TestMetadata("returnUnit.kt")
|
||||||
public void testReturnUnit() throws Exception {
|
public void testReturnUnit() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/call/returnUnit.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/call/returnUnit.kt");
|
||||||
|
|||||||
@@ -156,10 +156,24 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
|||||||
return functions.single()
|
return functions.single()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Class<*>.tryGetMethod(name: String, parameterTypes: List<Class<*>>, declared: Boolean) =
|
private fun Class<*>.tryGetMethod(name: String, parameterTypes: List<Class<*>>, returnType: Class<*>, declared: Boolean): Method? =
|
||||||
try {
|
try {
|
||||||
if (declared) getDeclaredMethod(name, *parameterTypes.toTypedArray())
|
val parametersArray = parameterTypes.toTypedArray()
|
||||||
else getMethod(name, *parameterTypes.toTypedArray())
|
val result = if (declared) getDeclaredMethod(name, *parametersArray) else getMethod(name, *parametersArray)
|
||||||
|
|
||||||
|
if (result.returnType == returnType) result
|
||||||
|
else {
|
||||||
|
// If we've found a method with an unexpected return type, it's likely that there are several methods in this class
|
||||||
|
// with the given parameter types and Java reflection API has returned not the one we're looking for.
|
||||||
|
// Falling back to enumerating all methods in the class in this (rather rare) case.
|
||||||
|
// Example: class A(val x: Int) { fun getX(): String = ... }
|
||||||
|
val allMethods = if (declared) declaredMethods else methods
|
||||||
|
allMethods.firstOrNull { method ->
|
||||||
|
method.name == name &&
|
||||||
|
method.returnType == returnType &&
|
||||||
|
Arrays.equals(method.parameterTypes, parametersArray)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e: NoSuchMethodException) {
|
catch (e: NoSuchMethodException) {
|
||||||
null
|
null
|
||||||
@@ -174,11 +188,10 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check resulting method's return type
|
|
||||||
fun findMethodBySignature(name: String, desc: String, declared: Boolean): Method? {
|
fun findMethodBySignature(name: String, desc: String, declared: Boolean): Method? {
|
||||||
if (name == "<init>") return null
|
if (name == "<init>") return null
|
||||||
|
|
||||||
return methodOwner.tryGetMethod(name, loadParameterTypes(desc), declared)
|
return methodOwner.tryGetMethod(name, loadParameterTypes(desc), loadReturnType(desc), declared)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findDefaultMethod(name: String, desc: String, isMember: Boolean, declared: Boolean): Method? {
|
fun findDefaultMethod(name: String, desc: String, isMember: Boolean, declared: Boolean): Method? {
|
||||||
@@ -190,7 +203,7 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
|||||||
}
|
}
|
||||||
addParametersAndMasks(parameterTypes, desc, false)
|
addParametersAndMasks(parameterTypes, desc, false)
|
||||||
|
|
||||||
return methodOwner.tryGetMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, declared)
|
return methodOwner.tryGetMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, loadReturnType(desc), declared)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findConstructorBySignature(desc: String, declared: Boolean): Constructor<*>? {
|
fun findConstructorBySignature(desc: String, declared: Boolean): Constructor<*>? {
|
||||||
@@ -214,46 +227,44 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadParameterTypes(desc: String): List<Class<*>> {
|
private fun loadParameterTypes(desc: String): List<Class<*>> {
|
||||||
val classLoader = jClass.safeClassLoader
|
|
||||||
val result = arrayListOf<Class<*>>()
|
val result = arrayListOf<Class<*>>()
|
||||||
|
|
||||||
var i = 1
|
var begin = 1
|
||||||
while (desc[i] != ')') {
|
while (desc[begin] != ')') {
|
||||||
var arrayDimension = 0
|
var end = begin
|
||||||
while (desc[i] == '[') {
|
while (desc[end] == '[') end++
|
||||||
arrayDimension++
|
when (desc[end]) {
|
||||||
i++
|
in "VZCBSIFJD" -> end++
|
||||||
}
|
'L' -> end = desc.indexOf(';', begin) + 1
|
||||||
|
|
||||||
var type = when (desc[i++]) {
|
|
||||||
'L' -> {
|
|
||||||
val semicolon = desc.indexOf(';', i)
|
|
||||||
val internalName = desc.substring(i, semicolon)
|
|
||||||
i = semicolon + 1
|
|
||||||
classLoader.loadClass(internalName.replace('/', '.'))
|
|
||||||
}
|
|
||||||
'V' -> Void.TYPE
|
|
||||||
'Z' -> java.lang.Boolean.TYPE
|
|
||||||
'C' -> java.lang.Character.TYPE
|
|
||||||
'B' -> java.lang.Byte.TYPE
|
|
||||||
'S' -> java.lang.Short.TYPE
|
|
||||||
'I' -> java.lang.Integer.TYPE
|
|
||||||
'F' -> java.lang.Float.TYPE
|
|
||||||
'J' -> java.lang.Long.TYPE
|
|
||||||
'D' -> java.lang.Double.TYPE
|
|
||||||
else -> throw KotlinReflectionInternalError("Unknown type prefix in the method signature: $desc")
|
else -> throw KotlinReflectionInternalError("Unknown type prefix in the method signature: $desc")
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat(arrayDimension) {
|
result.add(parseType(desc, begin, end))
|
||||||
type = type.createArrayType()
|
begin = end
|
||||||
}
|
|
||||||
|
|
||||||
result.add(type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseType(desc: String, begin: Int, end: Int): Class<*> =
|
||||||
|
when (desc[begin]) {
|
||||||
|
'L' -> jClass.safeClassLoader.loadClass(desc.substring(begin + 1, end - 1).replace('/', '.'))
|
||||||
|
'[' -> parseType(desc, begin + 1, end).createArrayType()
|
||||||
|
'V' -> Void.TYPE
|
||||||
|
'Z' -> Boolean::class.java
|
||||||
|
'C' -> Char::class.java
|
||||||
|
'B' -> Byte::class.java
|
||||||
|
'S' -> Short::class.java
|
||||||
|
'I' -> Int::class.java
|
||||||
|
'F' -> Float::class.java
|
||||||
|
'J' -> Long::class.java
|
||||||
|
'D' -> Double::class.java
|
||||||
|
else -> throw KotlinReflectionInternalError("Unknown type prefix in the method signature: $desc")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadReturnType(desc: String): Class<*> =
|
||||||
|
parseType(desc, desc.indexOf(')') + 1, desc.length)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val DEFAULT_CONSTRUCTOR_MARKER = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker")
|
private val DEFAULT_CONSTRUCTOR_MARKER = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user