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.*
|
||||||
import org.jetbrains.eval4j.Value
|
import org.jetbrains.eval4j.Value
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import java.lang.reflect.AccessibleObject
|
||||||
import com.sun.jdi.Type as jdi_Type
|
import com.sun.jdi.Type as jdi_Type
|
||||||
import com.sun.jdi.Value as jdi_Value
|
import com.sun.jdi.Value as jdi_Value
|
||||||
|
|
||||||
val CLASS = Type.getType(javaClass<Class<*>>())
|
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;")
|
val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
||||||
|
|
||||||
public class JDIEval(
|
public class JDIEval(
|
||||||
@@ -224,6 +226,11 @@ public class JDIEval(
|
|||||||
|
|
||||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||||
args.disableCollection()
|
args.disableCollection()
|
||||||
|
|
||||||
|
if (shouldInvokeMethodWithReflection(method, args)) {
|
||||||
|
return invokeMethodWithReflection(_class.asType(), NULL_VALUE, args, methodDesc)
|
||||||
|
}
|
||||||
|
|
||||||
val result = mayThrow { _class.invokeMethod(thread, method, args, invokePolicy) }
|
val result = mayThrow { _class.invokeMethod(thread, method, args, invokePolicy) }
|
||||||
args.enableCollection()
|
args.enableCollection()
|
||||||
return result.asValue()
|
return result.asValue()
|
||||||
@@ -290,6 +297,11 @@ public class JDIEval(
|
|||||||
fun doInvokeMethod(obj: ObjectReference, method: Method, policy: Int): Value {
|
fun doInvokeMethod(obj: ObjectReference, method: Method, policy: Int): Value {
|
||||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||||
args.disableCollection()
|
args.disableCollection()
|
||||||
|
|
||||||
|
if (shouldInvokeMethodWithReflection(method, args)) {
|
||||||
|
return invokeMethodWithReflection(instance.asmType, instance, args, methodDesc)
|
||||||
|
}
|
||||||
|
|
||||||
val result = mayThrow { obj.invokeMethod(thread, method, args, policy) }
|
val result = mayThrow { obj.invokeMethod(thread, method, args, policy) }
|
||||||
args.enableCollection()
|
args.enableCollection()
|
||||||
return result.asValue()
|
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() {
|
private fun List<jdi_Value?>.disableCollection() {
|
||||||
forEach { (it as? ObjectReference)?.disableCollection() }
|
forEach { (it as? ObjectReference)?.disableCollection() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -784,6 +784,41 @@ class TestData extends BaseTestData {
|
|||||||
return b[1];
|
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() {
|
public TestData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user