Repository navigation
Expand file tree
/
Copy pathLineBreakpoint.java
More file actions
232 lines (207 loc) · 8.1 KB
/
Copy pathLineBreakpoint.java
File metadata and controls
232 lines (207 loc) · 8.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 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.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package processing.mode.java.debug;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
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.request.BreakpointRequest;
/**
* Model/Controller of a line breakpoint. Can be set before or while debugging.
* Adds a highlight using the debuggers view ({@link DebugEditor}).
*
* @author Martin Leopold <m@martinleopold.com>
*/
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
/**
* 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;
theClass = dbg.getClass(className()); // try to get the class immediately, may return null if not yet loaded
set(); // activate the breakpoint (show highlight, attach if debugger is running)
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.INFO, "LBP Created " +toString() + " class: " + className(), new Object[]{});
}
/**
* Create a {@link LineBreakpoint} on a line in the current tab.
*
* @param lineIdx the line index of the current tab to create the breakpoint
* on
* @param dbg the {@link Debugger}
*/
// 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.
*
* @return the line id
*/
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.
*/
protected void attach() {
if (!dbg.isPaused()) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, debugger not paused");
return;
}
if (theClass == null) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "can't attach breakpoint, class not loaded: {0}", className());
return;
}
// find line in java space
LineID javaLine = dbg.sketchToJavaLine(line);
if (javaLine == null) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "couldn't find line {0} in the java code", line);
return;
}
try {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "BPs of class: {0} , line " + (javaLine.lineIdx() + 1), new Object[]{theClass});
List<Location> locations = theClass.locationsOfLine(javaLine.lineIdx() + 1);
if (locations.isEmpty()) {
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.WARNING, "no location found for line {0} -> {1}", new Object[]{line, javaLine});
return;
}
// use first found location
bpr = dbg.vm().eventRequestManager().createBreakpointRequest(locations.get(0));
bpr.enable();
Logger.getLogger(LineBreakpoint.class.getName()).log(Level.INFO, "attached breakpoint to {0} -> {1}", new Object[]{line, javaLine});
} catch (AbsentInformationException ex) {
Logger.getLogger(Debugger.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Detach this breakpoint from the VM. Deletes the
* {@link BreakpointRequest}.
*/
protected void detach() {
if (bpr != null) {
dbg.vm().eventRequestManager().deleteEventRequest(bpr);
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 (theClass != null && dbg.isPaused()) { // class is loaded
// immediately activate the breakpoint
attach();
}
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);
}
}
// public void enable() {
// }
//
// public void disable() {
// }
@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
ReferenceType mainClass = dbg.getMainClass();
//System.out.println(dbg.getMainClass().name());
if (mainClass == null) {
return null;
}
return dbg.getMainClass().name();
}
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) {
// 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());
}
}
}