Fix KotlinReflectionInternalError on invoking callBy with defaults in supertypes
There are two parts in this change: 1) Previously, we looked up $default methods with the incorrect signature in supertypes. For example in defaultInSuperClass.kt, we'd try to find a method foo$default with the signature `(B, String, String, int, Object)` in the class A. Now we're modifying the array of parameter types on each step if we're looking for a static $default method, by assigning its first element to be the containing class. This fixes cases when defaults come from a superclass. 2) For interfaces, $default methods are actually located in the corresponding DefaultImpls class. Now we look up that class and search for the $default method there. Note that this is needed because of KT-33430. This fixes cases when defaults come from a superinterface. #KT-13936 Fixed
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// IGNORE_BACKEND: JS_IR, JS, NATIVE
|
||||
// WITH_REFLECT
|
||||
|
||||
open class A {
|
||||
open fun foo(a: String, b: String = "b") = b + a
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
override fun foo(a: String, b: String) = a + b
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val f = B::foo
|
||||
|
||||
assert("ab" == f.callBy(mapOf(
|
||||
f.parameters.first() to B(),
|
||||
f.parameters.single { it.name == "a" } to "a"
|
||||
)))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// IGNORE_BACKEND: JS_IR, JS, NATIVE
|
||||
// WITH_REFLECT
|
||||
|
||||
interface A1 {
|
||||
fun test(o: String, k: String): String
|
||||
}
|
||||
interface A2 : A1
|
||||
|
||||
interface B1 {
|
||||
fun test(o: String = "O", k: String = "K"): String
|
||||
}
|
||||
interface B2 : B1
|
||||
|
||||
interface C1
|
||||
interface C2 : C1
|
||||
|
||||
interface D
|
||||
interface E : A2, B2, C2 {
|
||||
override fun test(o: String, k: String): String {
|
||||
return o + k
|
||||
}
|
||||
}
|
||||
|
||||
class Z : D, E
|
||||
|
||||
fun box(): String {
|
||||
val f = Z::test
|
||||
return f.callBy(mapOf(f.parameters.first() to Z()))
|
||||
}
|
||||
+10
@@ -20614,6 +20614,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperClass.kt")
|
||||
public void testDefaultInSuperClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperInterface.kt")
|
||||
public void testDefaultInSuperInterface() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
|
||||
|
||||
+10
@@ -20614,6 +20614,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperClass.kt")
|
||||
public void testDefaultInSuperClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperInterface.kt")
|
||||
public void testDefaultInSuperInterface() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
|
||||
|
||||
+10
@@ -19499,6 +19499,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperClass.kt")
|
||||
public void testDefaultInSuperClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperInterface.kt")
|
||||
public void testDefaultInSuperInterface() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Method
|
||||
import kotlin.jvm.internal.ClassBasedDeclarationContainer
|
||||
import kotlin.reflect.jvm.internal.components.RuntimeModuleData
|
||||
import kotlin.reflect.jvm.internal.components.tryLoadClass
|
||||
import kotlin.reflect.jvm.internal.structure.createArrayType
|
||||
import kotlin.reflect.jvm.internal.structure.safeClassLoader
|
||||
import kotlin.reflect.jvm.internal.structure.wrapperByPrimitive
|
||||
@@ -165,26 +166,30 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
return functions.single()
|
||||
}
|
||||
|
||||
private fun Class<*>.lookupMethod(name: String, parameterTypes: List<Class<*>>, returnType: Class<*>): Method? {
|
||||
lookupMethod(name, parameterTypes.toTypedArray(), returnType)?.let { return it }
|
||||
|
||||
// Methods from java.lang.Object (equals, hashCode, toString) cannot be found in the interface via
|
||||
// Class.getMethod/getDeclaredMethod, so for interfaces, we also look in java.lang.Object.
|
||||
if (isInterface) {
|
||||
Any::class.java.lookupMethod(name, parameterTypes.toTypedArray(), returnType)?.let { return it }
|
||||
private fun Class<*>.lookupMethod(
|
||||
name: String, parameterTypes: Array<Class<*>>, returnType: Class<*>, isStaticDefault: Boolean
|
||||
): Method? {
|
||||
// Static "$default" method in any class takes an instance of that class as the first parameter.
|
||||
if (isStaticDefault) {
|
||||
parameterTypes[0] = this
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun Class<*>.lookupMethod(name: String, parameterTypes: Array<Class<*>>, returnType: Class<*>): Method? {
|
||||
tryGetMethod(name, parameterTypes, returnType)?.let { return it }
|
||||
|
||||
superclass?.lookupMethod(name, parameterTypes, returnType)?.let { return it }
|
||||
superclass?.lookupMethod(name, parameterTypes, returnType, isStaticDefault)?.let { return it }
|
||||
|
||||
// TODO: avoid exponential complexity here
|
||||
for (superInterface in interfaces) {
|
||||
superInterface.lookupMethod(name, parameterTypes, returnType)?.let { return it }
|
||||
superInterface.lookupMethod(name, parameterTypes, returnType, isStaticDefault)?.let { return it }
|
||||
|
||||
// Static "$default" methods should be looked up in each DefaultImpls class, see KT-33430
|
||||
if (isStaticDefault) {
|
||||
val defaultImpls = superInterface.classLoader.tryLoadClass(superInterface.name + JvmAbi.DEFAULT_IMPLS_SUFFIX)
|
||||
if (defaultImpls != null) {
|
||||
parameterTypes[0] = superInterface
|
||||
defaultImpls.tryGetMethod(name, parameterTypes, returnType)?.let { return it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -220,7 +225,17 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
fun findMethodBySignature(name: String, desc: String): Method? {
|
||||
if (name == "<init>") return null
|
||||
|
||||
return methodOwner.lookupMethod(name, loadParameterTypes(desc), loadReturnType(desc))
|
||||
val parameterTypes = loadParameterTypes(desc).toTypedArray()
|
||||
val returnType = loadReturnType(desc)
|
||||
methodOwner.lookupMethod(name, parameterTypes, returnType, false)?.let { return it }
|
||||
|
||||
// Methods from java.lang.Object (equals, hashCode, toString) cannot be found in the interface via
|
||||
// Class.getMethod/getDeclaredMethod, so for interfaces, we also look in java.lang.Object.
|
||||
if (methodOwner.isInterface) {
|
||||
Any::class.java.lookupMethod(name, parameterTypes, returnType, false)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun findDefaultMethod(name: String, desc: String, isMember: Boolean): Method? {
|
||||
@@ -228,11 +243,14 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
|
||||
val parameterTypes = arrayListOf<Class<*>>()
|
||||
if (isMember) {
|
||||
// Note that this value is replaced inside the lookupMethod call below, for each class/interface in the hierarchy.
|
||||
parameterTypes.add(jClass)
|
||||
}
|
||||
addParametersAndMasks(parameterTypes, desc, false)
|
||||
|
||||
return methodOwner.lookupMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, loadReturnType(desc))
|
||||
return methodOwner.lookupMethod(
|
||||
name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes.toTypedArray(), loadReturnType(desc), isStaticDefault = isMember
|
||||
)
|
||||
}
|
||||
|
||||
fun findConstructorBySignature(desc: String): Constructor<*>? =
|
||||
|
||||
Generated
+10
@@ -16304,6 +16304,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperClass.kt")
|
||||
public void testDefaultInSuperClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperInterface.kt")
|
||||
public void testDefaultInSuperInterface() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
|
||||
|
||||
+10
@@ -17459,6 +17459,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultAndNonDefaultIntertwined.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperClass.kt")
|
||||
public void testDefaultInSuperClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("defaultInSuperInterface.kt")
|
||||
public void testDefaultInSuperInterface() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/defaultInSuperInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/reflection/callBy/extensionFunction.kt");
|
||||
|
||||
Reference in New Issue
Block a user