Debugger: Do not disable breakpoints on evaluation (KT-12242)
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-27
@@ -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,
|
||||||
|
|||||||
+6
-2
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user