Fix breakpoints in inner classes

Fixes #2946
This commit is contained in:
Jakub Valtar
2017-04-16 17:22:08 +02:00
parent a05a375104
commit f433a1e5ea
2 changed files with 67 additions and 56 deletions

View File

@@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -68,7 +69,7 @@ public class Debugger {
protected ReferenceType mainClass;
/// holds all loaded classes in the debuggee VM
protected Set<ReferenceType> classes = new HashSet<>();
protected Set<ReferenceType> classes = new LinkedHashSet<>();
/// listeners for class load events
protected List<ClassLoadListener> classLoadListeners = new ArrayList<>();
@@ -124,24 +125,12 @@ public class Debugger {
/**
* Get the {@link ReferenceType} for a class name.
* @param name the class name
* @return the {@link ReferenceType} or null if not found
* (e.g. not yet loaded)
* Get the main and nested {@link ReferenceType}s for the sketch.
* @return a list of main and nested {@link ReferenceType}s,
* empty list if nothing found (e.g. not yet loaded)
*/
public ReferenceType getClass(String name) {
if (name == null) {
return null;
}
if (name.equals(mainClassName)) {
return mainClass;
}
for (ReferenceType rt : classes) {
if (rt.name().equals(name)) {
return rt;
}
}
return null;
public Set<ReferenceType> getClasses() {
return classes;
}
@@ -239,6 +228,11 @@ public class Debugger {
editor.variableInspector().lock();
if (runtime != null) {
Messages.log("closing runtime");
for (LineBreakpoint bp : breakpoints) {
bp.detach();
}
runtime.close();
runtime = null;
//build = null;
@@ -563,21 +557,25 @@ public class Debugger {
}
}
private void createClassPrepareRequest(String name) {
ClassPrepareRequest classPrepareRequest = runtime.vm().eventRequestManager().createClassPrepareRequest();
classPrepareRequest.addClassFilter(name);
classPrepareRequest.enable();
}
private void vmStartEvent() {
// break on main class load
log("requesting event on main class load: " + mainClassName);
ClassPrepareRequest mainClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest();
mainClassPrepare.addClassFilter(mainClassName);
mainClassPrepare.enable();
createClassPrepareRequest(mainClassName);
createClassPrepareRequest(mainClassName + "$*");
// break on loading custom classes
for (SketchCode tab : editor.getSketch().getCode()) {
if (tab.isExtension("java")) {
log("requesting event on class load: " + tab.getPrettyName());
ClassPrepareRequest customClassPrepare = runtime.vm().eventRequestManager().createClassPrepareRequest();
customClassPrepare.addClassFilter(tab.getPrettyName());
customClassPrepare.enable();
String name = tab.getPrettyName();
createClassPrepareRequest(name);
createClassPrepareRequest(name + "$*");
}
}
runtime.vm().resume();
@@ -592,6 +590,7 @@ public class Debugger {
if (rt.name().equals(mainClassName)) {
//printType(rt);
mainClass = rt;
classes.add(rt);
log("main class load: " + rt.name());
started = true; // now that main class is loaded, we're started
} else {

View File

@@ -29,6 +29,7 @@ import processing.mode.java.Debugger;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Location;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.request.BreakpointRequest;
@@ -40,7 +41,7 @@ public class LineBreakpoint implements ClassLoadListener {
protected Debugger dbg; // the debugger
protected LineID line; // the line this breakpoint is set on
protected BreakpointRequest bpr; // the request on the VM's event request manager
protected ReferenceType theClass; // the class containing this breakpoint, null when not yet loaded
protected String className;
/**
@@ -56,9 +57,9 @@ public class LineBreakpoint implements ClassLoadListener {
this.line = line;
line.startTracking(dbg.getEditor().getTab(line.fileName()).getDocument());
this.dbg = dbg;
theClass = dbg.getClass(className()); // try to get the class immediately, may return null if not yet loaded
this.className = className();
set(); // activate the breakpoint (show highlight, attach if debugger is running)
Messages.log("LBP Created " + toString() + " class: " + className());
Messages.log("LBP Created " + toString() + " class: " + this.className);
}
@@ -94,48 +95,61 @@ public class LineBreakpoint implements ClassLoadListener {
/**
* Attach this breakpoint to the VM. Creates and enables a
* {@link BreakpointRequest}. VM needs to be paused.
*
* @param theClass class to attach to
* @return true on success
*/
protected void attach() {
if (!dbg.isPaused()) {
log("can't attach breakpoint, debugger not paused");
return;
protected boolean attach(ReferenceType theClass) {
if (theClass == null || className == null ||
!className.equals(parseTopLevelClassName(theClass.name()))) {
return false;
}
if (theClass == null) {
log("can't attach breakpoint, class not loaded: " + className());
return;
log("trying to attach: " + line.fileName + ":" + line.lineIdx + " to " + theClass.name());
if (!dbg.isPaused()) {
log("can't attach breakpoint, debugger not paused");
return false;
}
// find line in java space
LineID javaLine = dbg.sketchToJavaLine(line);
if (javaLine == null) {
log("couldn't find line " + line + " in the java code");
return;
return false;
}
try {
log("BPs of class: " + theClass + ", line " + (javaLine.lineIdx() + 1));
List<Location> locations = theClass.locationsOfLine(javaLine.lineIdx() + 1);
if (locations.isEmpty()) {
log("no location found for line " + line + " -> " + javaLine);
return;
return false;
}
// use first found location
bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0));
bpr.enable();
log("attached breakpoint to " + line + " -> " + javaLine);
return true;
} catch (AbsentInformationException ex) {
Messages.loge(null, ex);
}
return false;
}
protected boolean isAttached() {
return bpr != null;
}
/**
* Detach this breakpoint from the VM. Deletes the
* {@link BreakpointRequest}.
*/
protected void detach() {
public void detach() {
if (bpr != null) {
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
try {
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
} catch (VMDisconnectedException ignore) { }
bpr = null;
}
}
@@ -148,9 +162,11 @@ public class LineBreakpoint implements ClassLoadListener {
protected void set() {
dbg.addClassLoadListener(this); // class may not yet be loaded
dbg.getEditor().addBreakpointedLine(line);
if (theClass != null && dbg.isPaused()) { // class is loaded
// immediately activate the breakpoint
attach();
if (className != null && dbg.isPaused()) { // debugging right now, try to attach
for (ReferenceType rt : dbg.getClasses()) {
// try to attach to all top level or nested classes
if (attach(rt)) break;
}
}
if (dbg.getEditor().isInCurrentTab(line)) {
dbg.getEditor().getSketch().setModified(true);
@@ -191,12 +207,7 @@ public class LineBreakpoint implements ClassLoadListener {
protected String className() {
if (line.fileName().endsWith(".pde")) {
// standard tab
ReferenceType mainClass = dbg.getMainClass();
//System.out.println(dbg.getMainClass().name());
if (mainClass == null) {
return null;
}
return dbg.getMainClass().name();
return dbg.getEditor().getSketch().getName();
}
if (line.fileName().endsWith(".java")) {
@@ -215,17 +226,18 @@ public class LineBreakpoint implements ClassLoadListener {
*/
@Override
public void classLoaded(ReferenceType theClass) {
// check if our class is being loaded
Messages.log("Class Loaded: " + theClass.name());
if (theClass.name().equals(className())) {
this.theClass = theClass;
attach();
}
for (ReferenceType ct : theClass.nestedTypes()) {
Messages.log("Nested " + ct.name());
if (!isAttached()) {
// try to attach
attach(theClass);
}
}
public String parseTopLevelClassName(String name) {
// Get rid of nested class name
int dollar = name.indexOf('$');
return (dollar == -1) ? name : name.substring(0, dollar);
}
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .