Debugger: Do not disable breakpoints on evaluation (KT-12242)

This commit is contained in:
Yan Zhulanow
2019-07-11 22:18:11 +09:00
parent d46510897b
commit 9fb622e148
3 changed files with 49 additions and 34 deletions
@@ -31,10 +31,10 @@ private val CLASS = Type.getType(Class::class.java)
private val OBJECT = Type.getType(Any::class.java) private val OBJECT = Type.getType(Any::class.java)
private val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;") private val BOOTSTRAP_CLASS_DESCRIPTORS = setOf("Ljava/lang/String;", "Ljava/lang/ClassLoader;", "Ljava/lang/Class;")
class JDIEval( open class JDIEval(
private val vm: VirtualMachine, private val vm: VirtualMachine,
private val defaultClassLoader: ClassLoaderReference?, private val defaultClassLoader: ClassLoaderReference?,
private val thread: ThreadReference, protected val thread: ThreadReference,
private val invokePolicy: Int private val invokePolicy: Int
) : Eval { ) : Eval {
@@ -268,6 +268,14 @@ class JDIEval(
return null return null
} }
open fun jdiInvokeStaticMethod(type: ClassType, method: Method, args: List<jdi_Value?>, invokePolicy: Int): jdi_Value? {
return type.invokeMethod(thread, method, args, invokePolicy)
}
open fun jdiInvokeStaticMethod(type: InterfaceType, method: Method, args: List<jdi_Value?>, invokePolicy: Int): jdi_Value? {
return type.invokeMethod(thread, method, args, invokePolicy)
}
override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value { override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value {
val method = findMethod(methodDesc) val method = findMethod(methodDesc)
if (!method.isStatic) { if (!method.isStatic) {
@@ -284,14 +292,14 @@ class JDIEval(
val result = mayThrow { val result = mayThrow {
when (declaringType) { when (declaringType) {
is ClassType -> declaringType.invokeMethod(thread, method, args, invokePolicy) is ClassType -> jdiInvokeStaticMethod(declaringType, method, args, invokePolicy)
is InterfaceType -> { is InterfaceType -> {
if (!isJava8OrLater) { if (!isJava8OrLater) {
val message = "Calling interface static methods is not supported in JVM ${vm.version()} ($method)" val message = "Calling interface static methods is not supported in JVM ${vm.version()} ($method)"
throwBrokenCodeException(NoSuchMethodError(message)) throwBrokenCodeException(NoSuchMethodError(message))
} }
declaringType.invokeMethod(thread, method, args, invokePolicy) jdiInvokeStaticMethod(declaringType, method, args, invokePolicy)
} }
else -> { else -> {
val message = "Calling static methods is only supported for classes and interfaces ($method)" val message = "Calling static methods is only supported for classes and interfaces ($method)"
@@ -344,6 +352,10 @@ class JDIEval(
return invokeMethod(boxedValue, method, listOf(), true) return invokeMethod(boxedValue, method, listOf(), true)
} }
open fun jdiInvokeMethod(obj: ObjectReference, method: Method, args: List<jdi_Value?>, policy: Int): jdi_Value? {
return obj.invokeMethod(thread, method, args, policy)
}
override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value { override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value {
if (invokespecial && methodDesc.name == "<init>") { if (invokespecial && methodDesc.name == "<init>") {
// Constructor call // Constructor call
@@ -365,7 +377,7 @@ class JDIEval(
} }
args.disableCollection() args.disableCollection()
val result = mayThrow { obj.invokeMethod(thread, method, args, policy) }.ifFail(method, obj) val result = mayThrow { jdiInvokeMethod(obj, method, args, policy) }.ifFail(method, obj)
args.enableCollection() args.enableCollection()
return result.asValue() return result.asValue()
} }
@@ -330,7 +330,19 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, private val sourcePositi
val thread = context.suspendContext.thread?.threadReference?.takeIf { it.isSuspended } val thread = context.suspendContext.thread?.threadReference?.takeIf { it.isSuspended }
?: error("Can not find a thread to run evaluation on") ?: error("Can not find a thread to run evaluation on")
val eval = JDIEval(vm, classLoader, thread, context.invokePolicy) val eval = object : JDIEval(vm, classLoader, thread, context.invokePolicy) {
override fun jdiInvokeStaticMethod(type: ClassType, method: Method, args: List<Value?>, invokePolicy: Int): Value? {
return context.invokeMethod(type, method, args)
}
override fun jdiInvokeStaticMethod(type: InterfaceType, method: Method, args: List<Value?>, invokePolicy: Int): Value? {
return context.invokeMethod(type, method, args)
}
override fun jdiInvokeMethod(obj: ObjectReference, method: Method, args: List<Value?>, policy: Int): Value? {
return context.invokeMethod(obj, method, args, ObjectReference.INVOKE_NONVIRTUAL)
}
}
interpreterLoop(mainMethod, makeInitialFrame(mainMethod, args.map { it.asValue() }), eval) interpreterLoop(mainMethod, makeInitialFrame(mainMethod, args.map { it.asValue() }), eval)
} }
} }
@@ -347,22 +359,20 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, private val sourcePositi
.filter { !it.isMainClass } .filter { !it.isMainClass }
.forEach { context.findClass(it.className, classLoader) } .forEach { context.findClass(it.className, classLoader) }
return context.vm.virtualMachine.executeWithBreakpointsDisabled { for (parameterType in compiledData.mainMethodSignature.parameterTypes) {
for (parameterType in compiledData.mainMethodSignature.parameterTypes) { context.findClass(parameterType, classLoader)
context.findClass(parameterType, classLoader)
}
val variableFinder = VariableFinder(context)
val args = calculateMainMethodCallArguments(variableFinder, compiledData, status)
val result = block(args)
for (wrapper in variableFinder.refWrappers) {
updateLocalVariableValue(variableFinder.evaluatorValueConverter, wrapper)
}
return@executeWithBreakpointsDisabled result
} }
val variableFinder = VariableFinder(context)
val args = calculateMainMethodCallArguments(variableFinder, compiledData, status)
val result = block(args)
for (wrapper in variableFinder.refWrappers) {
updateLocalVariableValue(variableFinder.evaluatorValueConverter, wrapper)
}
return result
} }
private fun updateLocalVariableValue(converter: EvaluatorValueConverter, ref: VariableFinder.RefWrapper) { private fun updateLocalVariableValue(converter: EvaluatorValueConverter, ref: VariableFinder.RefWrapper) {
@@ -465,17 +475,6 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, private val sourcePositi
} }
} }
private fun <T> VirtualMachine.executeWithBreakpointsDisabled(block: () -> T): T {
val allRequests = eventRequestManager().breakpointRequests() + eventRequestManager().classPrepareRequests()
try {
allRequests.forEach { it.disable() }
return block()
} finally {
allRequests.forEach { it.enable() }
}
}
private fun isSpecialException(th: Throwable): Boolean { private fun isSpecialException(th: Throwable): Boolean {
return when (th) { return when (th) {
is ClassNotPreparedException, is ClassNotPreparedException,
@@ -40,14 +40,18 @@ class ExecutionContext(val evaluationContext: EvaluationContextImpl, val framePr
} }
@Throws(EvaluateException::class) @Throws(EvaluateException::class)
fun invokeMethod(obj: ObjectReference, method: Method, args: List<Value?>): Value? { fun invokeMethod(obj: ObjectReference, method: Method, args: List<Value?>, invocationOptions: Int = 0): Value? {
return debugProcess.invokeInstanceMethod(evaluationContext, obj, method, args, invokePolicy) return debugProcess.invokeInstanceMethod(evaluationContext, obj, method, args, invocationOptions)
} }
fun invokeMethod(type: ClassType, method: Method, args: List<Value?>): Value? { fun invokeMethod(type: ClassType, method: Method, args: List<Value?>): Value? {
return debugProcess.invokeMethod(evaluationContext, type, method, args) return debugProcess.invokeMethod(evaluationContext, type, method, args)
} }
fun invokeMethod(type: InterfaceType, method: Method, args: List<Value?>): Value? {
return debugProcess.invokeMethod(evaluationContext, type, method, args)
}
@Throws(EvaluateException::class) @Throws(EvaluateException::class)
fun newInstance(type: ClassType, constructor: Method, args: List<Value?>): ObjectReference { fun newInstance(type: ClassType, constructor: Method, args: List<Value?>): ObjectReference {
return debugProcess.newInstance(evaluationContext, type, constructor, args) return debugProcess.newInstance(evaluationContext, type, constructor, args)