Load classes mentioned in method arguments before invokeMethod
This commit is contained in:
@@ -3,18 +3,36 @@ package org.jetbrains.eval4j.jdi
|
||||
import org.jetbrains.eval4j.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import com.sun.jdi
|
||||
import com.sun.jdi.ClassNotLoadedException
|
||||
import com.sun.tools.jdi.ReferenceTypeImpl
|
||||
|
||||
val CLASS = Type.getType(javaClass<Class<*>>())
|
||||
val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
||||
|
||||
class JDIEval(
|
||||
private val vm: jdi.VirtualMachine,
|
||||
private val classLoader: jdi.ClassLoaderReference,
|
||||
private val thread: jdi.ThreadReference
|
||||
) : Eval {
|
||||
|
||||
private val primitiveTypes = mapOf(
|
||||
Type.BOOLEAN_TYPE.getClassName() to vm.mirrorOf(true).`type`(),
|
||||
Type.BYTE_TYPE.getClassName() to vm.mirrorOf(1.toByte()).`type`(),
|
||||
Type.SHORT_TYPE.getClassName() to vm.mirrorOf(1.toShort()).`type`(),
|
||||
Type.INT_TYPE.getClassName() to vm.mirrorOf(1.toInt()).`type`(),
|
||||
Type.CHAR_TYPE.getClassName() to vm.mirrorOf('1').`type`(),
|
||||
Type.LONG_TYPE.getClassName() to vm.mirrorOf(1L).`type`(),
|
||||
Type.FLOAT_TYPE.getClassName() to vm.mirrorOf(1.0f).`type`(),
|
||||
Type.DOUBLE_TYPE.getClassName() to vm.mirrorOf(1.0).`type`()
|
||||
)
|
||||
|
||||
override fun loadClass(classType: Type): Value {
|
||||
val loadedClasses = vm.classesByName(classType.getInternalName())
|
||||
if (!loadedClasses.isEmpty()) {
|
||||
return loadedClasses[0].classObject().asValue()
|
||||
val loadedClass = loadedClasses[0]
|
||||
if (classType.getDescriptor() in BOOTSTRAP_CLASS_DESCRIPTORS || loadedClass.classLoader() == classLoader) {
|
||||
return loadedClass.classObject().asValue()
|
||||
}
|
||||
}
|
||||
return invokeStaticMethod(
|
||||
MethodDescription(
|
||||
@@ -158,7 +176,7 @@ class JDIEval(
|
||||
val _class = method.declaringType()
|
||||
if (_class !is jdi.ClassType) throwEvalException(NoSuchMethodError("Static method is a non-class type: $method"))
|
||||
|
||||
val args = mapArguments(arguments, method.argumentTypes())
|
||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||
val result = mayThrow { _class.invokeMethod(thread, method, args, 0) }
|
||||
return result.asValue()
|
||||
}
|
||||
@@ -184,7 +202,7 @@ class JDIEval(
|
||||
// Constructor call
|
||||
val ctor = findMethod(methodDesc)
|
||||
val _class = (instance as NewObjectValue).asmType.asReferenceType() as jdi.ClassType
|
||||
val args = mapArguments(arguments, ctor.argumentTypes())
|
||||
val args = mapArguments(arguments, ctor.safeArgumentTypes())
|
||||
val result = mayThrow { _class.newInstance(thread, ctor, args, 0) }
|
||||
instance.value = result
|
||||
return result.asValue()
|
||||
@@ -197,7 +215,7 @@ class JDIEval(
|
||||
val method = findMethod(methodDesc)
|
||||
|
||||
val obj = instance.jdiObj.checkNull()
|
||||
val args = mapArguments(arguments, method.argumentTypes())
|
||||
val args = mapArguments(arguments, method.safeArgumentTypes())
|
||||
val result = mayThrow { obj.invokeMethod(thread, method, args, 0) }
|
||||
return result.asValue()
|
||||
}
|
||||
@@ -208,6 +226,31 @@ class JDIEval(
|
||||
arg.asJdiValue(vm, expectedType.asType())
|
||||
}
|
||||
}
|
||||
|
||||
private fun jdi.Method.safeArgumentTypes(): List<jdi.Type> {
|
||||
try {
|
||||
return argumentTypes()
|
||||
}
|
||||
catch (e: ClassNotLoadedException) {
|
||||
return argumentTypeNames()!!.map {
|
||||
name ->
|
||||
val dimensions = name.count { it == '[' }
|
||||
val baseTypeName = if (dimensions > 0) name.substring(0, name.indexOf('[')) else name
|
||||
|
||||
val primitiveType = primitiveTypes[baseTypeName]
|
||||
val baseType = if (primitiveType != null)
|
||||
primitiveType
|
||||
else {
|
||||
Type.getType("L$baseTypeName;").asReferenceType()
|
||||
}
|
||||
|
||||
if (dimensions == 0)
|
||||
baseType
|
||||
else
|
||||
Type.getType("[".repeat(dimensions) + baseType.asType().getDescriptor()).asReferenceType()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> mayThrow(f: () -> T): T {
|
||||
|
||||
@@ -564,6 +564,98 @@ class TestData {
|
||||
static void castToArray() {
|
||||
int[] i = (int[]) null;
|
||||
}
|
||||
|
||||
static void classNotLoadedException() {
|
||||
ClassNotLoadedExceptionTest klass = new ClassNotLoadedExceptionTest();
|
||||
|
||||
klass.f1(null);
|
||||
klass.f2(null);
|
||||
klass.f3(null);
|
||||
klass.f4(null, 1);
|
||||
klass.f5(null, null);
|
||||
klass.f6(null, null);
|
||||
|
||||
ClassNotLoadedExceptionTest.s1(null);
|
||||
ClassNotLoadedExceptionTest.s2(null);
|
||||
ClassNotLoadedExceptionTest.s3(null);
|
||||
ClassNotLoadedExceptionTest.s4(null, 1);
|
||||
ClassNotLoadedExceptionTest.s5(null, null);
|
||||
ClassNotLoadedExceptionTest.s6(null, null);
|
||||
|
||||
new ClassNotLoadedExceptionTest(null, null, null);
|
||||
new ClassNotLoadedExceptionTest(null, 1, null, null);
|
||||
}
|
||||
|
||||
static class ClassNotLoadedExceptionTest {
|
||||
ClassNotLoadedExceptionTest() {}
|
||||
// instance methods
|
||||
static class F1 {}
|
||||
void f1(F1 p) {}
|
||||
|
||||
static class F2 {}
|
||||
void f2(F2[] p) {}
|
||||
|
||||
static class F3 {}
|
||||
void f3(F3[][] p) {}
|
||||
|
||||
static class F4 {}
|
||||
void f4(F4[] p, int p2) {}
|
||||
|
||||
static class F5 {}
|
||||
void f5(F5[] p, int[] p2) {}
|
||||
|
||||
static class F6 {}
|
||||
void f6(F6[] p, int[][] p2) {}
|
||||
|
||||
// static methods
|
||||
static class S1 {}
|
||||
static void s1(S1 p) {}
|
||||
|
||||
static class S2 {}
|
||||
static void s2(S2[] p) {}
|
||||
|
||||
static class S3 {}
|
||||
static void s3(S3[][] p) {}
|
||||
|
||||
static class S4 {}
|
||||
static void s4(S4[] p, int p2) {}
|
||||
|
||||
static class S5 {}
|
||||
static void s5(S5[] p, int[] p2) {}
|
||||
|
||||
static class S6 {}
|
||||
static void s6(S6[] p, int[][] p2) {}
|
||||
|
||||
// constructor
|
||||
static class C2 {}
|
||||
static class C1 {}
|
||||
static class C3 {}
|
||||
ClassNotLoadedExceptionTest(C1 p, C2[] p2, C3[][] p3) {}
|
||||
|
||||
static class C4 {}
|
||||
ClassNotLoadedExceptionTest(C4 p, int p2, int[] p3, int[][] p4) {}
|
||||
}
|
||||
|
||||
static void loadLibraryClasses() {
|
||||
LoadLibraryClasses klass = new LoadLibraryClasses();
|
||||
|
||||
klass.f1(1);
|
||||
klass.f2(LoadLibraryClasses.str());
|
||||
klass.f3(LoadLibraryClasses.c());
|
||||
klass.f4(LoadLibraryClasses.cl());
|
||||
}
|
||||
|
||||
static class LoadLibraryClasses {
|
||||
void f1(Integer i) {}
|
||||
void f2(String s) {}
|
||||
void f3(Class c) {}
|
||||
void f4(ClassLoader cl) {}
|
||||
|
||||
static ClassLoader cl() { return null; }
|
||||
static String str() { return null; }
|
||||
static Class c() { return null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user