diff --git a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt index 93a473ac7fe..fd42ef70498 100644 --- a/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt +++ b/eval4j/src/org/jetbrains/eval4j/jdi/jdiEval.kt @@ -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>()) +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 { + 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 mayThrow(f: () -> T): T { diff --git a/eval4j/test/org/jetbrains/eval4j/test/TestData.java b/eval4j/test/org/jetbrains/eval4j/test/TestData.java index 62cfa338d02..729704d80cc 100644 --- a/eval4j/test/org/jetbrains/eval4j/test/TestData.java +++ b/eval4j/test/org/jetbrains/eval4j/test/TestData.java @@ -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; } + } + }