diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java index 7b55969a939c..ebc866563b87 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -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; diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 282a30a1cdc7..50893af951b9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -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) { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerImplicitEvaluationContextUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerImplicitEvaluationContextUtil.java new file mode 100644 index 000000000000..0872501b1c07 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerImplicitEvaluationContextUtil.java @@ -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. + *
+ * {@link DebugProcessImpl#isEvaluationPossible()}
+ */
+@ApiStatus.Experimental
+public final class DebuggerImplicitEvaluationContextUtil {
+ private static final Key