Workaround for bug in jdi: ClassCastException in invoke method with Object[] param using Interface[] argument
This commit is contained in:
@@ -20,10 +20,12 @@ import com.sun.jdi.*
|
||||
import org.jetbrains.eval4j.*
|
||||
import org.jetbrains.eval4j.Value
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import java.lang.reflect.AccessibleObject
|
||||
import com.sun.jdi.Type as jdi_Type
|
||||
import com.sun.jdi.Value as jdi_Value
|
||||
|
||||
val CLASS = Type.getType(javaClass<Class<*>>())
|
||||
val OBJECT = Type.getType(Any::class.java)
|
||||
val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
||||
|
||||
public class JDIEval(
|
||||
@@ -224,6 +226,11 @@ public class JDIEval(
|
||||
|
||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||
args.disableCollection()
|
||||
|
||||
if (shouldInvokeMethodWithReflection(method, args)) {
|
||||
return invokeMethodWithReflection(_class.asType(), NULL_VALUE, args, methodDesc)
|
||||
}
|
||||
|
||||
val result = mayThrow { _class.invokeMethod(thread, method, args, invokePolicy) }
|
||||
args.enableCollection()
|
||||
return result.asValue()
|
||||
@@ -290,6 +297,11 @@ public class JDIEval(
|
||||
fun doInvokeMethod(obj: ObjectReference, method: Method, policy: Int): Value {
|
||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||
args.disableCollection()
|
||||
|
||||
if (shouldInvokeMethodWithReflection(method, args)) {
|
||||
return invokeMethodWithReflection(instance.asmType, instance, args, methodDesc)
|
||||
}
|
||||
|
||||
val result = mayThrow { obj.invokeMethod(thread, method, args, policy) }
|
||||
args.enableCollection()
|
||||
return result.asValue()
|
||||
@@ -306,6 +318,54 @@ public class JDIEval(
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldInvokeMethodWithReflection(method: Method, args: List<com.sun.jdi.Value?>): Boolean {
|
||||
return !method.isVarArgs && args.zip(method.argumentTypes()).any { isArrayOfInterfaces(it.first?.type(), it.second) }
|
||||
}
|
||||
|
||||
private fun isArrayOfInterfaces(valueType: jdi_Type?, expectedType: jdi_Type?): Boolean {
|
||||
return (valueType as? ArrayType)?.componentType() is InterfaceType && (expectedType as? ArrayType)?.componentType() == OBJECT.asReferenceType()
|
||||
}
|
||||
|
||||
private fun invokeMethodWithReflection(ownerType: Type, instance: Value, args: List<jdi_Value?>, methodDesc: MethodDescription): Value {
|
||||
val methodToInvoke = invokeMethod(
|
||||
loadClass(ownerType),
|
||||
MethodDescription(
|
||||
CLASS.internalName,
|
||||
"getDeclaredMethod",
|
||||
"(Ljava/lang/String;[L${CLASS.internalName};)Ljava/lang/reflect/Method;",
|
||||
true
|
||||
),
|
||||
listOf(vm.mirrorOf(methodDesc.name).asValue(), *methodDesc.parameterTypes.map { loadClass(it) }.toTypedArray())
|
||||
)
|
||||
|
||||
invokeMethod(
|
||||
methodToInvoke,
|
||||
MethodDescription(
|
||||
Type.getType(AccessibleObject::class.java).internalName,
|
||||
"setAccessible",
|
||||
"(Z)V",
|
||||
true
|
||||
),
|
||||
listOf(vm.mirrorOf(true).asValue())
|
||||
)
|
||||
|
||||
val invocationResult = invokeMethod(
|
||||
methodToInvoke,
|
||||
MethodDescription(
|
||||
methodToInvoke.asmType.internalName,
|
||||
"invoke",
|
||||
"(L${OBJECT.internalName};[L${OBJECT.internalName};)L${OBJECT.internalName};",
|
||||
true
|
||||
),
|
||||
listOf(instance, *args.map { it.asValue() }.toTypedArray())
|
||||
)
|
||||
|
||||
if (methodDesc.returnType.sort != Type.OBJECT && methodDesc.returnType.sort != Type.ARRAY && methodDesc.returnType.sort != Type.VOID) {
|
||||
return unboxType(invocationResult, methodDesc.returnType)
|
||||
}
|
||||
return invocationResult
|
||||
}
|
||||
|
||||
private fun List<jdi_Value?>.disableCollection() {
|
||||
forEach { (it as? ObjectReference)?.disableCollection() }
|
||||
}
|
||||
|
||||
@@ -784,6 +784,41 @@ class TestData extends BaseTestData {
|
||||
return b[1];
|
||||
}
|
||||
|
||||
@IgnoreInReflectionTests
|
||||
public static void invokeMethodWithArrayOfInterfaces() {
|
||||
BaseToArray[] c = new BaseToArray[1];
|
||||
|
||||
TestInvokeWithObjectArray.invokeStaticFun(c);
|
||||
TestInvokeWithObjectArray.invokeStaticPrivateFun(c);
|
||||
|
||||
TestInvokeWithObjectArray myObj = new TestInvokeWithObjectArray();
|
||||
myObj.invokeMemberFun(c);
|
||||
myObj.invokeMemberPrivateFun(c);
|
||||
|
||||
int i = TestInvokeWithObjectArray.primitiveReturnValue(c) + 1;
|
||||
}
|
||||
|
||||
private static class TestInvokeWithObjectArray {
|
||||
public static void invokeStaticFun(Object[] o) {
|
||||
}
|
||||
|
||||
public static void invokeStaticPrivateFun(Object[] o) {
|
||||
}
|
||||
|
||||
public void invokeMemberFun(Object[] o) {
|
||||
}
|
||||
|
||||
public void invokeMemberPrivateFun(Object[] o) {
|
||||
}
|
||||
|
||||
public static int primitiveReturnValue(Object[] o) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private interface BaseToArray {
|
||||
}
|
||||
|
||||
public TestData() {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user