Load classes mentioned in method arguments before invokeMethod

This commit is contained in:
Natalia Ukhorskaya
2014-04-08 15:42:19 +04:00
parent 964b81ce6a
commit 4f84d4d2db
2 changed files with 139 additions and 4 deletions
+47 -4
View File
@@ -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; }
}
}