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.eval4j.*
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import com.sun.jdi
|
import com.sun.jdi
|
||||||
|
import com.sun.jdi.ClassNotLoadedException
|
||||||
|
import com.sun.tools.jdi.ReferenceTypeImpl
|
||||||
|
|
||||||
val CLASS = Type.getType(javaClass<Class<*>>())
|
val CLASS = Type.getType(javaClass<Class<*>>())
|
||||||
|
val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
|
||||||
|
|
||||||
class JDIEval(
|
class JDIEval(
|
||||||
private val vm: jdi.VirtualMachine,
|
private val vm: jdi.VirtualMachine,
|
||||||
private val classLoader: jdi.ClassLoaderReference,
|
private val classLoader: jdi.ClassLoaderReference,
|
||||||
private val thread: jdi.ThreadReference
|
private val thread: jdi.ThreadReference
|
||||||
) : Eval {
|
) : 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 {
|
override fun loadClass(classType: Type): Value {
|
||||||
val loadedClasses = vm.classesByName(classType.getInternalName())
|
val loadedClasses = vm.classesByName(classType.getInternalName())
|
||||||
if (!loadedClasses.isEmpty()) {
|
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(
|
return invokeStaticMethod(
|
||||||
MethodDescription(
|
MethodDescription(
|
||||||
@@ -158,7 +176,7 @@ class JDIEval(
|
|||||||
val _class = method.declaringType()
|
val _class = method.declaringType()
|
||||||
if (_class !is jdi.ClassType) throwEvalException(NoSuchMethodError("Static method is a non-class type: $method"))
|
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) }
|
val result = mayThrow { _class.invokeMethod(thread, method, args, 0) }
|
||||||
return result.asValue()
|
return result.asValue()
|
||||||
}
|
}
|
||||||
@@ -184,7 +202,7 @@ class JDIEval(
|
|||||||
// Constructor call
|
// Constructor call
|
||||||
val ctor = findMethod(methodDesc)
|
val ctor = findMethod(methodDesc)
|
||||||
val _class = (instance as NewObjectValue).asmType.asReferenceType() as jdi.ClassType
|
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) }
|
val result = mayThrow { _class.newInstance(thread, ctor, args, 0) }
|
||||||
instance.value = result
|
instance.value = result
|
||||||
return result.asValue()
|
return result.asValue()
|
||||||
@@ -197,7 +215,7 @@ class JDIEval(
|
|||||||
val method = findMethod(methodDesc)
|
val method = findMethod(methodDesc)
|
||||||
|
|
||||||
val obj = instance.jdiObj.checkNull()
|
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) }
|
val result = mayThrow { obj.invokeMethod(thread, method, args, 0) }
|
||||||
return result.asValue()
|
return result.asValue()
|
||||||
}
|
}
|
||||||
@@ -208,6 +226,31 @@ class JDIEval(
|
|||||||
arg.asJdiValue(vm, expectedType.asType())
|
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 {
|
fun <T> mayThrow(f: () -> T): T {
|
||||||
|
|||||||
@@ -564,6 +564,98 @@ class TestData {
|
|||||||
static void castToArray() {
|
static void castToArray() {
|
||||||
int[] i = (int[]) null;
|
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