Skip to content

Commit 85e010c

Browse files
committed
Fix breakpoints in inner classes
Fixes #2946
1 parent 6f6d703 commit 85e010c

File tree

2 files changed

+67
-56
lines changed

2 files changed

+67
-56
lines changed

java/src/processing/mode/java/Debugger.java

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.HashMap;
3030
import java.util.HashSet;
3131
import java.util.Iterator;
32+
import java.util.LinkedHashSet;
3233
import java.util.List;
3334
import java.util.Map;
3435
import java.util.Map.Entry;
@@ -68,7 +69,7 @@ public class Debugger {
6869
protected ReferenceType mainClass;
6970

7071
/// holds all loaded classes in the debuggee VM
71-
protected Set<ReferenceType> classes = new HashSet<>();
72+
protected Set<ReferenceType> classes = new LinkedHashSet<>();
7273

7374
/// listeners for class load events
7475
protected List<ClassLoadListener> classLoadListeners = new ArrayList<>();
@@ -124,24 +125,12 @@ public ReferenceType getMainClass() {
124125

125126

126127
/**
127-
* Get the {@link ReferenceType} for a class name.
128-
* @param name the class name
129-
* @return the {@link ReferenceType} or null if not found
130-
* (e.g. not yet loaded)
128+
* Get the main and nested {@link ReferenceType}s for the sketch.
129+
* @return a list of main and nested {@link ReferenceType}s,
130+
* empty list if nothing found (e.g. not yet loaded)
131131
*/
132-
public ReferenceType getClass(String name) {
133-
if (name == null) {
134-
return null;
135-
}
136-
if (name.equals(mainClassName)) {
137-
return mainClass;
138-
}
139-
for (ReferenceType rt : classes) {
140-
if (rt.name().equals(name)) {
141-
return rt;
142-
}
143-
}
144-
return null;
132+
public Set<ReferenceType> getClasses() {
133+
return classes;
145134
}
146135

147136

@@ -239,6 +228,11 @@ public synchronized void stopDebug() {
239228
editor.variableInspector().lock();
240229
if (runtime != null) {
241230
Messages.log("closing runtime");
231+
232+
for (LineBreakpoint bp : breakpoints) {
233+
bp.detach();
234+
}
235+
242236
runtime.close();
243237
runtime = null;
244238
//build = null;
@@ -563,21 +557,25 @@ public synchronized void vmEvent(EventSet es) {
563557
}
564558
}
565559

560+
private void createClassPrepareRequest(String name) {
561+
ClassPrepareRequest classPrepareRequest = runtime.vm().eventRequestManager().createClassPrepareRequest();
562+
classPrepareRequest.addClassFilter(name);
563+
classPrepareRequest.enable();
564+
}
565+
566566

567567
private void vmStartEvent() {
568568
// break on main class load
569569
log("requesting event on main class load: " + mainClassName);
570-
ClassPrepareRequest mainClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest();
571-
mainClassPrepare.addClassFilter(mainClassName);
572-
mainClassPrepare.enable();
573-
570+
createClassPrepareRequest(mainClassName);
571+
createClassPrepareRequest(mainClassName + "$*");
574572
// break on loading custom classes
575573
for (SketchCode tab : editor.getSketch().getCode()) {
576574
if (tab.isExtension("java")) {
577575
log("requesting event on class load: " + tab.getPrettyName());
578-
ClassPrepareRequest customClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest();
579-
customClassPrepare.addClassFilter(tab.getPrettyName());
580-
customClassPrepare.enable();
576+
String name = tab.getPrettyName();
577+
createClassPrepareRequest(name);
578+
createClassPrepareRequest(name + "$*");
581579
}
582580
}
583581
runtime.vm().resume();
@@ -592,6 +590,7 @@ private void vmClassPrepareEvent(ClassPrepareEvent ce) {
592590
if (rt.name().equals(mainClassName)) {
593591
//printType(rt);
594592
mainClass = rt;
593+
classes.add(rt);
595594
log("main class load: " + rt.name());
596595
started = true; // now that main class is loaded, we're started
597596
} else {

java/src/processing/mode/java/debug/LineBreakpoint.java

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.sun.jdi.AbsentInformationException;
3030
import com.sun.jdi.Location;
3131
import com.sun.jdi.ReferenceType;
32+
import com.sun.jdi.VMDisconnectedException;
3233
import com.sun.jdi.request.BreakpointRequest;
3334

3435

@@ -40,7 +41,7 @@ public class LineBreakpoint implements ClassLoadListener {
4041
protected Debugger dbg; // the debugger
4142
protected LineID line; // the line this breakpoint is set on
4243
protected BreakpointRequest bpr; // the request on the VM's event request manager
43-
protected ReferenceType theClass; // the class containing this breakpoint, null when not yet loaded
44+
protected String className;
4445

4546

4647
/**
@@ -56,9 +57,9 @@ public LineBreakpoint(LineID line, Debugger dbg) {
5657
this.line = line;
5758
line.startTracking(dbg.getEditor().getTab(line.fileName()).getDocument());
5859
this.dbg = dbg;
59-
theClass = dbg.getClass(className()); // try to get the class immediately, may return null if not yet loaded
60+
this.className = className();
6061
set(); // activate the breakpoint (show highlight, attach if debugger is running)
61-
Messages.log("LBP Created " + toString() + " class: " + className());
62+
Messages.log("LBP Created " + toString() + " class: " + this.className);
6263
}
6364

6465

@@ -94,48 +95,61 @@ public boolean isOnLine(LineID testLine) {
9495
/**
9596
* Attach this breakpoint to the VM. Creates and enables a
9697
* {@link BreakpointRequest}. VM needs to be paused.
98+
*
99+
* @param theClass class to attach to
100+
* @return true on success
97101
*/
98-
protected void attach() {
99-
if (!dbg.isPaused()) {
100-
log("can't attach breakpoint, debugger not paused");
101-
return;
102+
protected boolean attach(ReferenceType theClass) {
103+
104+
if (theClass == null || className == null ||
105+
!className.equals(parseTopLevelClassName(theClass.name()))) {
106+
return false;
102107
}
103108

104-
if (theClass == null) {
105-
log("can't attach breakpoint, class not loaded: " + className());
106-
return;
109+
log("trying to attach: " + line.fileName + ":" + line.lineIdx + " to " + theClass.name());
110+
111+
if (!dbg.isPaused()) {
112+
log("can't attach breakpoint, debugger not paused");
113+
return false;
107114
}
108115

109116
// find line in java space
110117
LineID javaLine = dbg.sketchToJavaLine(line);
111118
if (javaLine == null) {
112119
log("couldn't find line " + line + " in the java code");
113-
return;
120+
return false;
114121
}
115122
try {
116123
log("BPs of class: " + theClass + ", line " + (javaLine.lineIdx() + 1));
117124
List<Location> locations = theClass.locationsOfLine(javaLine.lineIdx() + 1);
118125
if (locations.isEmpty()) {
119126
log("no location found for line " + line + " -> " + javaLine);
120-
return;
127+
return false;
121128
}
122129
// use first found location
123130
bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0));
124131
bpr.enable();
125132
log("attached breakpoint to " + line + " -> " + javaLine);
133+
return true;
126134
} catch (AbsentInformationException ex) {
127135
Messages.loge(null, ex);
128136
}
137+
return false;
129138
}
130139

140+
protected boolean isAttached() {
141+
return bpr != null;
142+
}
131143

132144
/**
133145
* Detach this breakpoint from the VM. Deletes the
134146
* {@link BreakpointRequest}.
135147
*/
136-
protected void detach() {
148+
public void detach() {
137149
if (bpr != null) {
138-
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
150+
try {
151+
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
152+
} catch (VMDisconnectedException ignore) { }
139153
bpr = null;
140154
}
141155
}
@@ -148,9 +162,11 @@ protected void detach() {
148162
protected void set() {
149163
dbg.addClassLoadListener(this); // class may not yet be loaded
150164
dbg.getEditor().addBreakpointedLine(line);
151-
if (theClass != null && dbg.isPaused()) { // class is loaded
152-
// immediately activate the breakpoint
153-
attach();
165+
if (className != null && dbg.isPaused()) { // debugging right now, try to attach
166+
for (ReferenceType rt : dbg.getClasses()) {
167+
// try to attach to all top level or nested classes
168+
if (attach(rt)) break;
169+
}
154170
}
155171
if (dbg.getEditor().isInCurrentTab(line)) {
156172
dbg.getEditor().getSketch().setModified(true);
@@ -191,12 +207,7 @@ public String toString() {
191207
protected String className() {
192208
if (line.fileName().endsWith(".pde")) {
193209
// standard tab
194-
ReferenceType mainClass = dbg.getMainClass();
195-
//System.out.println(dbg.getMainClass().name());
196-
if (mainClass == null) {
197-
return null;
198-
}
199-
return dbg.getMainClass().name();
210+
return dbg.getEditor().getSketch().getName();
200211
}
201212

202213
if (line.fileName().endsWith(".java")) {
@@ -215,17 +226,18 @@ protected String className() {
215226
*/
216227
@Override
217228
public void classLoaded(ReferenceType theClass) {
218-
// check if our class is being loaded
219-
Messages.log("Class Loaded: " + theClass.name());
220-
if (theClass.name().equals(className())) {
221-
this.theClass = theClass;
222-
attach();
223-
}
224-
for (ReferenceType ct : theClass.nestedTypes()) {
225-
Messages.log("Nested " + ct.name());
229+
if (!isAttached()) {
230+
// try to attach
231+
attach(theClass);
226232
}
227233
}
228234

235+
public String parseTopLevelClassName(String name) {
236+
// Get rid of nested class name
237+
int dollar = name.indexOf('$');
238+
return (dollar == -1) ? name : name.substring(0, dollar);
239+
}
240+
229241

230242
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
231243

0 commit comments

Comments
 (0)