Skip to content

Commit 49cdd0f

Browse files
authored
Merge pull request #50559 from angelozerr/qute_debug_in_jar
Use Qute Template#getSource() in Qute debugger
2 parents 43e231e + 4206df2 commit 49cdd0f

File tree

14 files changed

+874
-232
lines changed

14 files changed

+874
-232
lines changed

independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/Debugger.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.eclipse.lsp4j.debug.Scope;
1010
import org.eclipse.lsp4j.debug.Source;
1111
import org.eclipse.lsp4j.debug.SourceBreakpoint;
12+
import org.eclipse.lsp4j.debug.SourceResponse;
1213
import org.eclipse.lsp4j.debug.StackFrame;
1314
import org.eclipse.lsp4j.debug.StackTraceResponse;
1415
import org.eclipse.lsp4j.debug.Thread;
@@ -142,6 +143,15 @@ public interface Debugger {
142143
*/
143144
CompletableFuture<CompletionsResponse> completions(CompletionsArguments args);
144145

146+
/**
147+
* Returns the source code template for a given source reference.
148+
*
149+
* @param sourceReference the source reference
150+
*
151+
* @return the source code template for a given source reference
152+
*/
153+
SourceResponse getSourceReference(int sourceReference);
154+
145155
/**
146156
* Registers a debugger listener to receive debug events.
147157
*
@@ -169,4 +179,5 @@ public interface Debugger {
169179
* @return {@code true} if enabled, {@code false} otherwise
170180
*/
171181
boolean isEnabled();
182+
172183
}

independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/adapter/DebugServerAdapter.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import org.eclipse.lsp4j.debug.SetExceptionBreakpointsArguments;
2626
import org.eclipse.lsp4j.debug.SetExceptionBreakpointsResponse;
2727
import org.eclipse.lsp4j.debug.Source;
28+
import org.eclipse.lsp4j.debug.SourceArguments;
2829
import org.eclipse.lsp4j.debug.SourceBreakpoint;
30+
import org.eclipse.lsp4j.debug.SourceResponse;
2931
import org.eclipse.lsp4j.debug.StackTraceArguments;
3032
import org.eclipse.lsp4j.debug.StackTraceResponse;
3133
import org.eclipse.lsp4j.debug.StepInArguments;
@@ -325,6 +327,15 @@ public CompletableFuture<CompletionsResponse> completions(CompletionsArguments a
325327
return agent.completions(args);
326328
}
327329

330+
@Override
331+
public CompletableFuture<SourceResponse> source(SourceArguments args) {
332+
return CompletableFuture.supplyAsync(() -> {
333+
var source = args.getSource();
334+
return agent.getSourceReference(source != null && source.getSourceReference() != null ? source.getSourceReference()
335+
: args.getSourceReference());
336+
});
337+
}
338+
328339
/**
329340
* Handles a stopped event and notifies the client.
330341
*

independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/agent/DebuggeeAgent.java

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import static io.quarkus.qute.debug.agent.RemoteStackFrame.EMPTY_STACK_FRAMES;
44
import static io.quarkus.qute.debug.agent.scopes.RemoteScope.EMPTY_SCOPES;
55

6+
import java.net.URI;
67
import java.util.ArrayList;
78
import java.util.Collection;
8-
import java.util.HashMap;
99
import java.util.HashSet;
1010
import java.util.Map;
1111
import java.util.Set;
@@ -21,6 +21,7 @@
2121
import org.eclipse.lsp4j.debug.Scope;
2222
import org.eclipse.lsp4j.debug.Source;
2323
import org.eclipse.lsp4j.debug.SourceBreakpoint;
24+
import org.eclipse.lsp4j.debug.SourceResponse;
2425
import org.eclipse.lsp4j.debug.StackTraceResponse;
2526
import org.eclipse.lsp4j.debug.Thread;
2627
import org.eclipse.lsp4j.debug.Variable;
@@ -32,8 +33,12 @@
3233
import io.quarkus.qute.debug.StoppedEvent;
3334
import io.quarkus.qute.debug.ThreadEvent;
3435
import io.quarkus.qute.debug.ThreadEvent.ThreadStatus;
36+
import io.quarkus.qute.debug.agent.breakpoints.BreakpointsRegistry;
37+
import io.quarkus.qute.debug.agent.breakpoints.RemoteBreakpoint;
3538
import io.quarkus.qute.debug.agent.completions.CompletionSupport;
3639
import io.quarkus.qute.debug.agent.evaluations.EvaluationSupport;
40+
import io.quarkus.qute.debug.agent.source.SourceReferenceRegistry;
41+
import io.quarkus.qute.debug.agent.source.SourceTemplateRegistry;
3742
import io.quarkus.qute.debug.agent.variables.VariablesRegistry;
3843
import io.quarkus.qute.trace.ResolveEvent;
3944
import io.quarkus.qute.trace.TemplateEvent;
@@ -59,8 +64,8 @@ public class DebuggeeAgent implements Debugger {
5964
*/
6065
private final DebuggerTraceListener debugListener;
6166

62-
/** Breakpoints mapped by template ID and line number. */
63-
private final Map<String, Map<Integer, RemoteBreakpoint>> breakpoints;
67+
/** Breakpoints registry */
68+
private final BreakpointsRegistry breakpointsRegistry;
6469

6570
/** Currently active debuggee threads mapped by thread ID. */
6671
private final Map<Integer, RemoteThread> debuggees;
@@ -78,7 +83,10 @@ public class DebuggeeAgent implements Debugger {
7883
private final VariablesRegistry variablesRegistry;
7984

8085
/** Registry mapping Qute templates to DAP {@link Source} objects. */
81-
private final SourceTemplateRegistry sourceTemplateRegistry;
86+
private final Map<Engine, SourceTemplateRegistry> sourceTemplateRegistry;
87+
88+
/** Source reference registry (used to retrieve template content from JAR) */
89+
private final SourceReferenceRegistry sourceReferenceRegistry;
8290

8391
/** The set of Qute engines being tracked for debugging. */
8492
private final Set<Engine> trackedEngine;
@@ -99,11 +107,12 @@ public class DebuggeeAgent implements Debugger {
99107
*/
100108
public DebuggeeAgent() {
101109
this.debugListener = new DebuggerTraceListener(this);
102-
this.breakpoints = new ConcurrentHashMap<>();
103110
this.debuggees = new ConcurrentHashMap<>();
104111
this.listeners = new ArrayList<>();
105112
this.variablesRegistry = new VariablesRegistry();
106-
this.sourceTemplateRegistry = new SourceTemplateRegistry();
113+
this.breakpointsRegistry = new BreakpointsRegistry();
114+
this.sourceTemplateRegistry = new ConcurrentHashMap<>();
115+
this.sourceReferenceRegistry = new SourceReferenceRegistry();
107116
this.trackedEngine = new HashSet<>();
108117
this.enginesWithDebugListener = new HashSet<>();
109118
this.evaluationSupport = new EvaluationSupport(this);
@@ -221,30 +230,7 @@ private RemoteThread getRemoteThread(int threadId) {
221230

222231
@Override
223232
public Breakpoint[] setBreakpoints(SourceBreakpoint[] sourceBreakpoints, Source source) {
224-
sourceTemplateRegistry.registerSource(source);
225-
String templateId = sourceTemplateRegistry.getTemplateId(source);
226-
Map<Integer, RemoteBreakpoint> templateBreakpoints = templateId != null
227-
? this.breakpoints.computeIfAbsent(templateId, k -> new HashMap<>())
228-
: null;
229-
if (templateBreakpoints != null) {
230-
templateBreakpoints.clear();
231-
}
232-
233-
Breakpoint[] result = new Breakpoint[sourceBreakpoints.length];
234-
for (int i = 0; i < sourceBreakpoints.length; i++) {
235-
SourceBreakpoint sourceBreakpoint = sourceBreakpoints[i];
236-
int line = sourceBreakpoint.getLine();
237-
String condition = sourceBreakpoint.getCondition();
238-
RemoteBreakpoint breakpoint = new RemoteBreakpoint(source, line, condition);
239-
if (templateBreakpoints != null) {
240-
templateBreakpoints.put(line, breakpoint);
241-
breakpoint.setVerified(true);
242-
} else {
243-
breakpoint.setVerified(false);
244-
}
245-
result[i] = breakpoint;
246-
}
247-
return result;
233+
return breakpointsRegistry.setBreakpoints(sourceBreakpoints, source);
248234
}
249235

250236
@Override
@@ -260,21 +246,15 @@ public Thread[] getThreads() {
260246
/**
261247
* Retrieves a breakpoint for the given template and line number.
262248
*
249+
* @param sourceUri
250+
*
263251
* @param templateId the template identifier.
264252
* @param line the line number.
253+
* @param engine
265254
* @return the matching breakpoint, or {@code null} if none exists.
266255
*/
267-
RemoteBreakpoint getBreakpoint(String templateId, int line) {
268-
Map<Integer, RemoteBreakpoint> templateBreakpoints = this.breakpoints.get(templateId);
269-
if (templateBreakpoints == null) {
270-
for (var fileExtension : sourceTemplateRegistry.getFileExtensions()) {
271-
templateBreakpoints = this.breakpoints.get(templateId + fileExtension);
272-
if (templateBreakpoints != null) {
273-
break;
274-
}
275-
}
276-
}
277-
return templateBreakpoints != null ? templateBreakpoints.get(line) : null;
256+
RemoteBreakpoint getBreakpoint(URI sourceUri, String templateId, int line, Engine engine) {
257+
return breakpointsRegistry.resolveBreakpoint(sourceUri, templateId, line, getSourceTemplateRegistry(engine));
278258
}
279259

280260
@Override
@@ -368,7 +348,9 @@ public void unlockAllDebuggeeThreads() {
368348
public void terminate() {
369349
try {
370350
unlockAllDebuggeeThreads();
371-
this.breakpoints.clear();
351+
this.sourceTemplateRegistry.clear();
352+
this.sourceReferenceRegistry.reset();
353+
this.breakpointsRegistry.reset();
372354
} finally {
373355
fireTerminateEvent();
374356
}
@@ -411,6 +393,11 @@ public CompletableFuture<CompletionsResponse> completions(CompletionsArguments a
411393
return completionSupport.completions(args);
412394
}
413395

396+
@Override
397+
public SourceResponse getSourceReference(int sourceReference) {
398+
return sourceReferenceRegistry.getSourceReference(sourceReference);
399+
}
400+
414401
@Override
415402
public StackTraceResponse getStackFrames(int threadId, Integer startFrame, Integer levels) {
416403
StackTraceResponse response = new StackTraceResponse();
@@ -474,8 +461,9 @@ public VariablesRegistry getVariablesRegistry() {
474461
return variablesRegistry;
475462
}
476463

477-
public SourceTemplateRegistry getSourceTemplateRegistry() {
478-
return sourceTemplateRegistry;
464+
public SourceTemplateRegistry getSourceTemplateRegistry(Engine engine) {
465+
return this.sourceTemplateRegistry.computeIfAbsent(engine,
466+
k -> new SourceTemplateRegistry(breakpointsRegistry, sourceReferenceRegistry, engine));
479467
}
480468

481469
@Override
@@ -516,5 +504,7 @@ public boolean isEnabled() {
516504
*/
517505
public void reset() {
518506
unlockAllDebuggeeThreads();
507+
this.sourceTemplateRegistry.clear();
508+
this.sourceReferenceRegistry.reset();
519509
}
520510
}

independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/agent/RemoteStackFrame.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.qute.debug.agent;
22

3+
import java.net.URI;
34
import java.util.ArrayList;
45
import java.util.Collection;
56
import java.util.concurrent.CompletableFuture;
@@ -17,15 +18,18 @@
1718
import io.quarkus.qute.debug.agent.scopes.LocalsScope;
1819
import io.quarkus.qute.debug.agent.scopes.NamespaceResolversScope;
1920
import io.quarkus.qute.debug.agent.scopes.RemoteScope;
21+
import io.quarkus.qute.debug.agent.source.RemoteSource;
22+
import io.quarkus.qute.debug.agent.source.SourceTemplateRegistry;
2023
import io.quarkus.qute.debug.agent.variables.VariablesRegistry;
2124
import io.quarkus.qute.trace.ResolveEvent;
2225

2326
/**
2427
* Represents a single stack frame in the Qute debugging process.
2528
* <p>
26-
* A {@link RemoteStackFrame} corresponds to the evaluation of a {@link TemplateNode}
27-
* at runtime. It stores contextual information such as the variables in scope,
28-
* the template being executed, and the current execution state.
29+
* A {@link RemoteStackFrame} corresponds to the evaluation of a
30+
* {@link TemplateNode} at runtime. It stores contextual information such as the
31+
* variables in scope, the template being executed, and the current execution
32+
* state.
2933
* </p>
3034
*
3135
* <p>
@@ -46,7 +50,8 @@ public class RemoteStackFrame extends StackFrame {
4650
private static final AtomicInteger frameIdCounter = new AtomicInteger();
4751

4852
/**
49-
* The previous frame in the call stack, or {@code null} if this is the first frame.
53+
* The previous frame in the call stack, or {@code null} if this is the first
54+
* frame.
5055
*/
5156
private final transient RemoteStackFrame previousFrame;
5257

@@ -73,7 +78,8 @@ public class RemoteStackFrame extends StackFrame {
7378
/**
7479
* Creates a new {@link RemoteStackFrame}.
7580
*
76-
* @param event the resolve event describing the current execution
81+
* @param event the resolve event describing the current
82+
* execution
7783
* @param previousFrame the previous stack frame, may be {@code null}
7884
* @param sourceTemplateRegistry registry for mapping templates to debug sources
7985
* @param variablesRegistry the registry for managing variables
@@ -90,7 +96,8 @@ public RemoteStackFrame(ResolveEvent event, RemoteStackFrame previousFrame,
9096
super.setLine(line);
9197
this.templateId = event.getTemplateNode().getOrigin().getTemplateId();
9298
super.setSource(
93-
sourceTemplateRegistry.getSource(templateId, previousFrame != null ? previousFrame.getSource() : null));
99+
sourceTemplateRegistry.getSource(templateId,
100+
previousFrame != null ? previousFrame.getSource() : null));
94101
}
95102

96103
/**
@@ -102,6 +109,21 @@ public String getTemplateId() {
102109
return templateId;
103110
}
104111

112+
/**
113+
* Returns the template Uri associated with this frame and null otherwise.
114+
*
115+
* @return the template Uri associated with this frame and null otherwise.
116+
*/
117+
public URI getTemplateUri() {
118+
var source = getSource();
119+
return source != null ? source.getUri() : null;
120+
}
121+
122+
@Override
123+
public RemoteSource getSource() {
124+
return (RemoteSource) super.getSource();
125+
}
126+
105127
/**
106128
* Returns the previous stack frame, or {@code null} if none exists.
107129
*
@@ -151,7 +173,8 @@ private Collection<RemoteScope> createScopes() {
151173
* Evaluates an expression in the current frame context.
152174
* <p>
153175
* If the expression contains conditional operators, it is parsed and evaluated
154-
* as a conditional expression. Otherwise, it is treated as a simple Qute expression.
176+
* as a conditional expression. Otherwise, it is treated as a simple Qute
177+
* expression.
155178
* </p>
156179
*
157180
* @param expression the expression to evaluate
@@ -173,10 +196,12 @@ public CompletableFuture<Object> evaluate(String expression) {
173196
}
174197

175198
/**
176-
* Determines if a given expression should be treated as a conditional expression.
199+
* Determines if a given expression should be treated as a conditional
200+
* expression.
177201
*
178202
* @param expression the expression to test
179-
* @return {@code true} if the expression contains conditional operators, {@code false} otherwise
203+
* @return {@code true} if the expression contains conditional operators,
204+
* {@code false} otherwise
180205
*/
181206
private static boolean isConditionExpression(String expression) {
182207
return expression.contains("!") || expression.contains(">") || expression.contains("gt")
@@ -191,7 +216,8 @@ private static boolean isConditionExpression(String expression) {
191216
* Evaluates a parsed conditional expression.
192217
*
193218
* @param ifNode the parsed {@link TemplateNode} representing the condition
194-
* @param ignoreError whether to ignore evaluation errors and return {@code false}
219+
* @param ignoreError whether to ignore evaluation errors and return
220+
* {@code false}
195221
* @return a {@link CompletableFuture} containing {@code true} or {@code false}
196222
*/
197223
public CompletableFuture<Object> evaluateCondition(TemplateNode ifNode, boolean ignoreError) {

independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/agent/RemoteThread.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.quarkus.qute.debug.agent;
22

3+
import java.net.URI;
34
import java.util.LinkedList;
45
import java.util.List;
56
import java.util.function.Predicate;
@@ -13,6 +14,7 @@
1314
import io.quarkus.qute.debug.StoppedEvent.StoppedReason;
1415
import io.quarkus.qute.debug.ThreadEvent;
1516
import io.quarkus.qute.debug.ThreadEvent.ThreadStatus;
17+
import io.quarkus.qute.debug.agent.breakpoints.RemoteBreakpoint;
1618
import io.quarkus.qute.trace.ResolveEvent;
1719

1820
/**
@@ -163,18 +165,20 @@ public void onTemplateNode(ResolveEvent event) {
163165
return;
164166
}
165167

166-
RemoteStackFrame frame = new RemoteStackFrame(event, getCurrentFrame(), agent.getSourceTemplateRegistry(),
168+
var engine = event.getEngine();
169+
RemoteStackFrame frame = new RemoteStackFrame(event, getCurrentFrame(), agent.getSourceTemplateRegistry(engine),
167170
agent.getVariablesRegistry());
168171
this.frames.addFirst(frame);
169172
String templateId = frame.getTemplateId();
173+
URI sourceUri = frame.getTemplateUri();
170174
RemoteStackFrame previous = frame.getPrevious();
171175

172176
if (this.stopCondition != null && this.stopCondition.test(event.getTemplateNode())) {
173177
// suspend and wait because of step reason.
174178
this.suspendAndWait(StoppedReason.STEP);
175179
} else {
176180
int lineNumber = frame.getLine();
177-
RemoteBreakpoint breakpoint = agent.getBreakpoint(templateId, lineNumber);
181+
RemoteBreakpoint breakpoint = agent.getBreakpoint(sourceUri, templateId, lineNumber, engine);
178182
if (breakpoint != null // a breakpoint matches the frame line number
179183
&& (previous == null || !previous.getTemplateId().equals(templateId) || previous.getLine() != lineNumber
180184
|| event.getTemplateNode().isExpression())

0 commit comments

Comments
 (0)