[debugger] Introduce a way to provide evaluationContext without converting it to pausedContext

GitOrigin-RevId: 0b640745949259f4054467cbf047201c97838bc5
This commit is contained in:
Nikolay Egorov
2024-03-21 20:23:26 +02:00
committed by intellij-monorepo-bot
parent b4a3c40497
commit 3edbfaa5d0
3 changed files with 63 additions and 5 deletions

View File

@@ -2,8 +2,10 @@
package com.intellij.debugger.engine;
import com.intellij.debugger.*;
import com.intellij.debugger.engine.evaluation.DebuggerImplicitEvaluationContextUtil;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
import com.intellij.debugger.engine.requests.CustomProcessingLocatableEventRequestor;
import com.intellij.debugger.engine.requests.LocatableEventRequestor;
import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
@@ -283,7 +285,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
//((SuspendManagerImpl)getSuspendManager()).popContext(context);
}
else if (!DebuggerSession.enableBreakpointsDuringEvaluation()) {
notifySkippedBreakpointInEvaluation(locatableEvent);
notifySkippedBreakpointInEvaluation(locatableEvent, context);
DebuggerUtilsAsync.resume(eventSet);
return true;
}
@@ -642,7 +644,7 @@ public class DebugProcessEvents extends DebugProcessImpl {
if ((isEvaluationOnCurrentThread || mySuspendAllInvocation.get() > 0) &&
!(requestor instanceof InstrumentationTracker.InstrumentationMethodBreakpoint) &&
!DebuggerSession.enableBreakpointsDuringEvaluation()) {
notifySkippedBreakpointInEvaluation(event);
notifySkippedBreakpointInEvaluation(event, suspendContext);
// is inside evaluation, so ignore any breakpoints
suspendManager.voteResume(suspendContext);
return;
@@ -658,7 +660,10 @@ public class DebugProcessEvents extends DebugProcessImpl {
LightOrRealThreadInfo filter = getRequestsManager().getFilterThread();
if (filter != null) {
if (myPreparingToSuspendAll || !filter.checkSameThread(thread, suspendContext)) {
notifySkippedBreakpoints(event, SkippedBreakpointReason.STEPPING);
// notify only if the current session is not one with evaluations hidden from the user
if (!checkContextIsFromImplicitThread(suspendContext)) {
notifySkippedBreakpoints(event, SkippedBreakpointReason.STEPPING);
}
suspendManager.voteResume(suspendContext);
return;
}
@@ -836,7 +841,12 @@ public class DebugProcessEvents extends DebugProcessImpl {
STEPPING, // Suspend-all stepping ignores breakpoints in other threads for the sake of ease-of-debug.
}
private void notifySkippedBreakpointInEvaluation(@Nullable LocatableEvent event) {
private void notifySkippedBreakpointInEvaluation(@Nullable LocatableEvent event, @NotNull SuspendContextImpl suspendContext) {
// notify only if the current session is not one with evaluations hidden from the user
if (checkContextIsFromImplicitThread(suspendContext)) {
return;
}
SkippedBreakpointReason reason = SkippedBreakpointReason.EVALUATION_IN_ANOTHER_THREAD;
if (event != null) {
ThreadReferenceProxyImpl proxy = getVirtualMachineProxy().getThreadReferenceProxy(event.thread());
@@ -847,6 +857,25 @@ public class DebugProcessEvents extends DebugProcessImpl {
notifySkippedBreakpoints(event, reason);
}
private boolean checkContextIsFromImplicitThread(@NotNull SuspendContextImpl eventContext) {
DebugProcess debugProcess = eventContext.getDebugProcess();
LightOrRealThreadInfo implicitThread = DebuggerImplicitEvaluationContextUtil.getImplicitEvaluationThread(debugProcess);
LightOrRealThreadInfo filterThread = getRequestsManager().getFilterThread();
ThreadReferenceProxy eventThread = eventContext.getThread();
if (implicitThread == null || eventThread == null) {
return false;
}
// Case 1: We have filter and implicit threads provided, they are different, hence, this skipped breakpoint is no use for the user
if (filterThread != null && !implicitThread.checkSameThread(eventThread.getThreadReference(), eventContext)) {
return true;
}
// Case 2: Implicit thread is provided, no filter thread, check correct hit
return implicitThread.checkSameThread(eventThread.getThreadReference(), eventContext);
}
private void notifySkippedBreakpoints(@Nullable LocatableEvent event, SkippedBreakpointReason reason) {
if (event == null) return;

View File

@@ -2604,7 +2604,8 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
}
public boolean isEvaluationPossible() {
return getSuspendManager().getPausedContext() != null;
return getSuspendManager().getPausedContext() != null
|| DebuggerImplicitEvaluationContextUtil.getImplicitEvaluationThread(this) != null;
}
public boolean isEvaluationPossible(SuspendContextImpl suspendContext) {

View File

@@ -0,0 +1,28 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.debugger.engine.evaluation;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.LightOrRealThreadInfo;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.ApiStatus;
/**
* Utility class for providing an implicit evaluation-ready {@link LightOrRealThreadInfo} for a particular {@link DebugProcess}.
* This might be useful to support evaluations in the current {@link com.intellij.debugger.impl.DebuggerSession},
* but keeping any UI elements hidden.
* <p>
* {@link DebugProcessImpl#isEvaluationPossible()}
*/
@ApiStatus.Experimental
public final class DebuggerImplicitEvaluationContextUtil {
private static final Key<LightOrRealThreadInfo> IMPLICIT_EVALUATION_READY_THREAD_KEY = new Key<>("ImplicitEvaluationThread");
public static LightOrRealThreadInfo getImplicitEvaluationThread(DebugProcess process) {
return process.getUserData(IMPLICIT_EVALUATION_READY_THREAD_KEY);
}
public static void provideImplicitEvaluationThread(DebugProcess process, LightOrRealThreadInfo thread) {
process.putUserData(IMPLICIT_EVALUATION_READY_THREAD_KEY, thread);
}
}