mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 06:09:17 +01:00
253 lines
7.3 KiB
Java
253 lines
7.3 KiB
Java
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
/*
|
|
Part of the Processing project - http://processing.org
|
|
|
|
Copyright (c) 2012-16 The Processing Foundation
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
version 2, as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
package processing.mode.java.debug;
|
|
|
|
import java.util.List;
|
|
|
|
import processing.app.Messages;
|
|
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;
|
|
|
|
|
|
/**
|
|
* Model/Controller of a line breakpoint. Can be set before or while debugging.
|
|
* Adds a highlight using the debuggers view ({@link DebugEditor}).
|
|
*/
|
|
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 String className;
|
|
|
|
|
|
/**
|
|
* Create a {@link LineBreakpoint}. If in a debug session, will try to
|
|
* immediately set the breakpoint. If not in a debug session or the
|
|
* corresponding class is not yet loaded the breakpoint will activate on
|
|
* class load.
|
|
*
|
|
* @param line the line id to create the breakpoint on
|
|
* @param dbg the {@link Debugger}
|
|
*/
|
|
public LineBreakpoint(LineID line, Debugger dbg) {
|
|
this.line = line;
|
|
line.startTracking(dbg.getEditor().getTab(line.fileName()).getDocument());
|
|
this.dbg = dbg;
|
|
this.className = className();
|
|
set(); // activate the breakpoint (show highlight, attach if debugger is running)
|
|
Messages.log("LBP Created " + toString() + " class: " + this.className);
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a {@link LineBreakpoint} on a line in the current tab.
|
|
* @param lineIdx the line index of the current tab to create the breakpoint
|
|
*/
|
|
// TODO: remove and replace by {@link #LineBreakpoint(LineID line, Debugger dbg)}
|
|
public LineBreakpoint(int lineIdx, Debugger dbg) {
|
|
this(dbg.getEditor().getLineIDInCurrentTab(lineIdx), dbg);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the line id this breakpoint is on.
|
|
*/
|
|
public LineID lineID() {
|
|
return line;
|
|
}
|
|
|
|
|
|
/**
|
|
* Test if this breakpoint is on a certain line.
|
|
*
|
|
* @param testLine the line id to test
|
|
* @return true if this breakpoint is on the given line
|
|
*/
|
|
public boolean isOnLine(LineID testLine) {
|
|
return line.equals(testLine);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 boolean attach(ReferenceType theClass) {
|
|
|
|
if (theClass == null || className == null ||
|
|
!className.equals(parseTopLevelClassName(theClass.name()))) {
|
|
return false;
|
|
}
|
|
|
|
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 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 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}.
|
|
*/
|
|
public void detach() {
|
|
if (bpr != null) {
|
|
try {
|
|
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
|
|
} catch (VMDisconnectedException ignore) { }
|
|
bpr = null;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Set this breakpoint. Adds the line highlight. If Debugger is paused
|
|
* also attaches the breakpoint by calling {@link #attach()}.
|
|
*/
|
|
protected void set() {
|
|
dbg.addClassLoadListener(this); // class may not yet be loaded
|
|
dbg.getEditor().addBreakpointedLine(line);
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove this breakpoint. Clears the highlight and detaches
|
|
* the breakpoint if the debugger is paused.
|
|
*/
|
|
public void remove() {
|
|
dbg.removeClassLoadListener(this);
|
|
//System.out.println("removing " + line.lineIdx());
|
|
dbg.getEditor().removeBreakpointedLine(line.lineIdx());
|
|
if (dbg.isPaused()) {
|
|
// immediately remove the breakpoint
|
|
detach();
|
|
}
|
|
line.stopTracking();
|
|
if (dbg.getEditor().isInCurrentTab(line)) {
|
|
dbg.getEditor().getSketch().setModified(true);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public String toString() {
|
|
return line.toString();
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the name of the class this breakpoint belongs to. Needed for
|
|
* fetching the right location to create a breakpoint request.
|
|
* @return the class name
|
|
*/
|
|
protected String className() {
|
|
if (line.fileName().endsWith(".pde")) {
|
|
// standard tab
|
|
return dbg.getEditor().getSketch().getName();
|
|
}
|
|
|
|
if (line.fileName().endsWith(".java")) {
|
|
// pure java tab
|
|
return line.fileName().substring(0, line.fileName().lastIndexOf(".java"));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Event handler called when a class is loaded in the debugger. Causes the
|
|
* breakpoint to be attached, if its class was loaded.
|
|
*
|
|
* @param theClass the class that was just loaded.
|
|
*/
|
|
@Override
|
|
public void classLoaded(ReferenceType theClass) {
|
|
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);
|
|
}
|
|
|
|
|
|
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
|
|
|
|
|
private void log(String msg, Object... args) {
|
|
if (args != null && args.length != 0) {
|
|
Messages.logf(getClass().getName() + " " + msg, args);
|
|
} else {
|
|
Messages.log(getClass().getName() + " " + msg);
|
|
}
|
|
}
|
|
}
|