|
15 | 15 | import java.util.Arrays; |
16 | 16 | import java.util.List; |
17 | 17 | import java.util.concurrent.CompletableFuture; |
| 18 | +import java.util.concurrent.ExecutionException; |
18 | 19 | import java.util.logging.Level; |
19 | 20 | import java.util.logging.Logger; |
20 | 21 |
|
|
24 | 25 | import com.microsoft.java.debug.core.Configuration; |
25 | 26 | import com.microsoft.java.debug.core.DebugException; |
26 | 27 | import com.microsoft.java.debug.core.IBreakpoint; |
| 28 | +import com.microsoft.java.debug.core.IDebugSession; |
27 | 29 | import com.microsoft.java.debug.core.adapter.AdapterUtils; |
28 | 30 | import com.microsoft.java.debug.core.adapter.BreakpointManager; |
29 | 31 | import com.microsoft.java.debug.core.adapter.ErrorCode; |
30 | 32 | import com.microsoft.java.debug.core.adapter.HotCodeReplaceEvent.EventType; |
31 | 33 | import com.microsoft.java.debug.core.adapter.IDebugAdapterContext; |
32 | 34 | import com.microsoft.java.debug.core.adapter.IDebugRequestHandler; |
| 35 | +import com.microsoft.java.debug.core.adapter.IEvaluationProvider; |
33 | 36 | import com.microsoft.java.debug.core.adapter.IHotCodeReplaceProvider; |
34 | 37 | import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider; |
35 | 38 | import com.microsoft.java.debug.core.protocol.Events; |
|
39 | 42 | import com.microsoft.java.debug.core.protocol.Requests.SetBreakpointArguments; |
40 | 43 | import com.microsoft.java.debug.core.protocol.Responses; |
41 | 44 | import com.microsoft.java.debug.core.protocol.Types; |
| 45 | +import com.sun.jdi.PrimitiveValue; |
| 46 | +import com.sun.jdi.ThreadReference; |
| 47 | +import com.sun.jdi.Value; |
| 48 | +import com.sun.jdi.event.BreakpointEvent; |
| 49 | +import com.sun.jdi.event.Event; |
| 50 | +import com.sun.jdi.event.StepEvent; |
42 | 51 |
|
43 | 52 | public class SetBreakpointsRequestHandler implements IDebugRequestHandler { |
44 | 53 |
|
45 | 54 | private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME); |
46 | 55 |
|
47 | 56 | private BreakpointManager manager = new BreakpointManager(); |
48 | 57 |
|
| 58 | + private boolean registered = false; |
| 59 | + |
49 | 60 | @Override |
50 | 61 | public List<Command> getTargetCommands() { |
51 | 62 | return Arrays.asList(Command.SETBREAKPOINTS); |
@@ -73,6 +84,11 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments, |
73 | 84 | return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.EMPTY_DEBUG_SESSION, "Empty debug session."); |
74 | 85 | } |
75 | 86 |
|
| 87 | + if (!registered) { |
| 88 | + registered = true; |
| 89 | + registerBreakpointHandler(context); |
| 90 | + } |
| 91 | + |
76 | 92 | SetBreakpointArguments bpArguments = (SetBreakpointArguments) arguments; |
77 | 93 | String clientPath = bpArguments.source.path; |
78 | 94 | if (AdapterUtils.isWindows()) { |
@@ -130,6 +146,55 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments, |
130 | 146 | } |
131 | 147 | } |
132 | 148 |
|
| 149 | + private void registerBreakpointHandler(IDebugAdapterContext context) { |
| 150 | + IDebugSession debugSession = context.getDebugSession(); |
| 151 | + if (debugSession != null) { |
| 152 | + debugSession.getEventHub().events().subscribe(debugEvent -> { |
| 153 | + if (!(debugEvent.event instanceof BreakpointEvent)) { |
| 154 | + return; |
| 155 | + } |
| 156 | + Event event = debugEvent.event; |
| 157 | + if (debugEvent.eventSet.size() > 1 && debugEvent.eventSet.stream().anyMatch(t -> t instanceof StepEvent)) { |
| 158 | + // The StepEvent and BreakpointEvent are grouped in the same event set only if they occurs at the same location and in the same thread. |
| 159 | + // In order to avoid two duplicated StoppedEvents, the debugger will skip the BreakpointEvent. |
| 160 | + } else { |
| 161 | + ThreadReference bpThread = ((BreakpointEvent) event).thread(); |
| 162 | + IEvaluationProvider engine = context.getProvider(IEvaluationProvider.class); |
| 163 | + if (engine.isInEvaluation(bpThread)) { |
| 164 | + return; |
| 165 | + } |
| 166 | + |
| 167 | + // find the breakpoint related to this breakpoint event |
| 168 | + IBreakpoint conditionalBP = Arrays.asList(manager.getBreakpoints()).stream().filter(bp -> StringUtils.isNotBlank(bp.getCondition()) |
| 169 | + && bp.requests().contains(((BreakpointEvent) event).request()) |
| 170 | + ).findFirst().get(); |
| 171 | + if (conditionalBP != null) { |
| 172 | + CompletableFuture.runAsync(() -> { |
| 173 | + Value value; |
| 174 | + try { |
| 175 | + value = engine.evaluate(conditionalBP.getCondition(), bpThread, 0).get(); |
| 176 | + if (value instanceof PrimitiveValue) { |
| 177 | + boolean evaluationResultAsBool = ((PrimitiveValue) value).booleanValue(); |
| 178 | + if (!evaluationResultAsBool) { |
| 179 | + debugEvent.eventSet.resume(); |
| 180 | + return; |
| 181 | + } |
| 182 | + } |
| 183 | + } catch (InterruptedException | ExecutionException e) { |
| 184 | + // TODO: notify user about evaluation failure |
| 185 | + } |
| 186 | + context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); |
| 187 | + |
| 188 | + }); |
| 189 | + } else { |
| 190 | + context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID())); |
| 191 | + } |
| 192 | + debugEvent.shouldResume = false; |
| 193 | + } |
| 194 | + }); |
| 195 | + } |
| 196 | + } |
| 197 | + |
133 | 198 | private Types.Breakpoint convertDebuggerBreakpointToClient(IBreakpoint breakpoint, IDebugAdapterContext context) { |
134 | 199 | int id = (int) breakpoint.getProperty("id"); |
135 | 200 | boolean verified = breakpoint.getProperty("verified") != null && (boolean) breakpoint.getProperty("verified"); |
|
0 commit comments