move more of debugger logic out of JavaEditor and into Debugger class

This commit is contained in:
Ben Fry
2020-01-28 19:08:07 -05:00
parent b13ae99b34
commit 0b4f80c656
3 changed files with 200 additions and 266 deletions

View File

@@ -25,6 +25,10 @@ import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,22 +40,31 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JTree; // needed for javadocs
import javax.swing.tree.DefaultMutableTreeNode;
import processing.app.Language;
import processing.app.Messages;
import processing.app.RunnerListenerEdtAdapter;
import processing.app.Sketch;
import processing.app.SketchCode;
import processing.app.ui.Toolkit;
import processing.mode.java.debug.*;
import processing.mode.java.runner.Runner;
public class Debugger {
/// editor window, acting as main view
protected JavaEditor editor;
protected JMenu debugMenu;
protected JMenuItem debugItem;
protected boolean enabled;
protected VariableInspector inspector;
/// the runtime, contains debuggee VM
protected Runner runtime;
@@ -97,9 +110,139 @@ public class Debugger {
public Debugger(JavaEditor editor) {
this.editor = editor;
inspector = new VariableInspector(editor);
}
/**
* Creates the debug menu. Includes ActionListeners for the menu items.
* Intended for adding to the menu bar.
*
* @return The debug menu
*/
protected JMenu buildMenu() {
debugMenu = new JMenu(Language.text("menu.debug"));
JMenuItem item;
debugItem = Toolkit.newJMenuItem(Language.text("menu.debug.enable"), 'D');
debugItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
toggleEnabled();
}
});
debugMenu.add(debugItem);
debugMenu.addSeparator();
item = Toolkit.newJMenuItem(Language.text("menu.debug.continue"), KeyEvent.VK_U);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//editor.handleContinue();
continueDebug();
}
});
debugMenu.add(item);
item.setEnabled(false);
item = Toolkit.newJMenuItemExt("menu.debug.step");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//editor.handleStep(0);
stepOver();
}
});
debugMenu.add(item);
item.setEnabled(false);
item = Toolkit.newJMenuItemExt("menu.debug.step_into");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//handleStep(ActionEvent.SHIFT_MASK);
stepInto();
}
});
debugMenu.add(item);
item.setEnabled(false);
item = Toolkit.newJMenuItemExt("menu.debug.step_out");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//handleStep(ActionEvent.ALT_MASK);
stepOut();
}
});
debugMenu.add(item);
item.setEnabled(false);
debugMenu.addSeparator();
item =
Toolkit.newJMenuItem(Language.text("menu.debug.toggle_breakpoint"), 'B');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Messages.log("Invoked 'Toggle Breakpoint' menu item");
// TODO wouldn't getCaretLine() do the same thing with less effort?
toggleBreakpoint(editor.getCurrentLineID().lineIdx());
}
});
debugMenu.add(item);
item.setEnabled(false);
return debugMenu;
}
void enableMenuItem(boolean enabled) {
debugItem.setEnabled(enabled);
}
boolean isEnabled() {
return enabled;
}
void toggleEnabled() {
enabled = !enabled;
if (enabled) {
debugItem.setText(Language.text("menu.debug.disable"));
} else {
debugItem.setText(Language.text("menu.debug.enable"));
}
inspector.setVisible(enabled);
for (Component item : debugMenu.getMenuComponents()) {
if (item instanceof JMenuItem && item != debugItem) {
item.setEnabled(enabled);
}
}
}
/*
public void handleStep(int modifiers) {
if (modifiers == 0) {
Messages.log("Invoked 'Step Over' menu item");
stepOver();
} else if ((modifiers & ActionEvent.SHIFT_MASK) != 0) {
Messages.log("Invoked 'Step Into' menu item");
stepInto();
} else if ((modifiers & ActionEvent.ALT_MASK) != 0) {
Messages.log("Invoked 'Step Out' menu item");
stepOut();
}
}
public void handleContinue() {
Messages.log("Invoked 'Continue' menu item");
continueDebug();
}
*/
public VirtualMachine vm() {
if (runtime != null) {
return runtime.vm();
@@ -156,6 +299,11 @@ public class Debugger {
}
public void dispose() {
inspector.dispose();
}
/**
* Start a debugging session. Builds the sketch and launches a VM to run it.
* VM starts suspended. Should produce a VMStartEvent.
@@ -173,7 +321,7 @@ public class Debugger {
editor.clearConsole();
// clear variable inspector (also resets expanded states)
editor.variableInspector().reset();
inspector.reset();
// load edits into sketch obj, etc...
editor.prepareRun();
@@ -227,7 +375,7 @@ public class Debugger {
* VMDisconnectEvent.
*/
public synchronized void stopDebug() {
editor.variableInspector().lock();
inspector.lock();
if (runtime != null) {
Messages.log("closing runtime");
@@ -256,7 +404,7 @@ public class Debugger {
/** Resume paused debugging session. Resumes VM. */
public synchronized void continueDebug() {
editor.activateContinue();
editor.variableInspector().lock();
inspector.lock();
//editor.clearSelection();
//clearHighlight();
editor.clearCurrentLine();
@@ -279,7 +427,7 @@ public class Debugger {
if (!isStarted()) {
startDebug();
} else if (isPaused()) {
editor.variableInspector().lock();
inspector.lock();
editor.activateStep();
// use global to mark that there is a step request pending
@@ -884,7 +1032,6 @@ public class Debugger {
// TODO: needs to be handled in a better way:
log("call stack empty");
} else {
final VariableInspector vi = editor.variableInspector();
// first get data
final List<DefaultMutableTreeNode> stackTrace = getStackTrace(t);
final List<VariableNode> locals = getLocals(t, 0);
@@ -893,16 +1040,16 @@ public class Debugger {
final List<VariableNode> declaredThisFields = getThisFields(t, 0, false);
final String thisName = thisName(t);
// now update asynchronously
javax.swing.SwingUtilities.invokeLater(new Runnable() {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
//System.out.println("updating vi. from EDT: " + javax.swing.SwingUtilities.isEventDispatchThread());
vi.updateCallStack(stackTrace, "Call Stack");
vi.updateLocals(locals, "Locals at " + currentLocation);
vi.updateThisFields(thisFields, "Class " + thisName);
vi.updateDeclaredThisFields(declaredThisFields, "Class " + thisName);
vi.unlock(); // need to do this before rebuilding, otherwise we get these ... dots in the labels
vi.rebuild();
inspector.updateCallStack(stackTrace, "Call Stack");
inspector.updateLocals(locals, "Locals at " + currentLocation);
inspector.updateThisFields(thisFields, "Class " + thisName);
inspector.updateDeclaredThisFields(declaredThisFields, "Class " + thisName);
inspector.unlock(); // need to do this before rebuilding, otherwise we get these ... dots in the labels
inspector.rebuild();
}
});
}

View File

@@ -74,12 +74,6 @@ public class JavaEditor extends Editor {
protected LineHighlight currentLine; // where the debugger is suspended
protected final String breakpointMarkerComment = " //<>//";
protected JMenu debugMenu;
protected JMenuItem debugItem;
protected Debugger debugger;
protected boolean debugEnabled;
protected VariableInspector inspector;
protected JMenuItem inspectorItem;
static final int ERROR_TAB_INDEX = 0;
@@ -87,7 +81,9 @@ public class JavaEditor extends Editor {
private boolean hasJavaTabs;
private boolean javaTabWarned;
protected PreprocService preprocessingService;
protected PreprocService preprocService;
protected Debugger debugger;
private InspectMode inspect;
private ShowUsage usage;
@@ -108,7 +104,6 @@ public class JavaEditor extends Editor {
jmode = (JavaMode) mode;
debugger = new Debugger(this);
inspector = new VariableInspector(this);
// set breakpoints from marker comments
for (LineID lineID : stripBreakpointComments()) {
@@ -136,19 +131,19 @@ public class JavaEditor extends Editor {
box.add(textAndError);
*/
preprocessingService = new PreprocService(this);
preprocService = new PreprocService(this);
pdexEnabled = !hasJavaTabs();
usage = new ShowUsage(this, preprocessingService);
inspect = new InspectMode(this, preprocessingService, usage);
rename = new Rename(this, preprocessingService, usage);
usage = new ShowUsage(this, preprocService);
inspect = new InspectMode(this, preprocService, usage);
rename = new Rename(this, preprocService, usage);
if (SHOW_AST_VIEWER) {
astViewer = new ASTViewer(this, preprocessingService);
astViewer = new ASTViewer(this, preprocService);
}
errorChecker = new ErrorChecker(this, preprocessingService);
errorChecker = new ErrorChecker(this, preprocService);
for (SketchCode code : getSketch().getCode()) {
Document document = code.getDocument();
@@ -198,9 +193,9 @@ public class JavaEditor extends Editor {
boolean hasJavaTabsChanged = hasJavaTabs != newHasJavaTabs;
hasJavaTabs = newHasJavaTabs;
if (preprocessingService != null) {
if (preprocService != null) {
if (hasJavaTabsChanged) {
preprocessingService.handleHasJavaTabsChange(hasJavaTabs);
preprocService.handleHasJavaTabsChange(hasJavaTabs);
pdexEnabled = !hasJavaTabs;
if (!pdexEnabled) {
usage.hide();
@@ -1194,28 +1189,6 @@ public class JavaEditor extends Editor {
}
public void handleStep(int modifiers) {
if (modifiers == 0) {
Messages.log("Invoked 'Step Over' menu item");
debugger.stepOver();
} else if ((modifiers & ActionEvent.SHIFT_MASK) != 0) {
Messages.log("Invoked 'Step Into' menu item");
debugger.stepInto();
} else if ((modifiers & ActionEvent.ALT_MASK) != 0) {
Messages.log("Invoked 'Step Out' menu item");
debugger.stepOut();
}
}
public void handleContinue() {
Messages.log("Invoked 'Continue' menu item");
debugger.continueDebug();
}
public void onRunnerExiting(Runner runner) {
synchronized (runtimeLock) {
if (this.runtime == runner) {
@@ -1315,20 +1288,20 @@ public class JavaEditor extends Editor {
@Override
public void librariesChanged() {
preprocessingService.notifyLibrariesChanged();
preprocService.notifyLibrariesChanged();
}
@Override
public void codeFolderChanged() {
preprocessingService.notifyCodeFolderChanged();
preprocService.notifyCodeFolderChanged();
}
@Override
public void sketchChanged() {
errorChecker.notifySketchChanged();
preprocessingService.notifySketchChanged();
preprocService.notifySketchChanged();
}
@@ -1385,13 +1358,11 @@ public class JavaEditor extends Editor {
public void dispose() {
//System.out.println("window dispose");
// quit running debug session
if (debugEnabled) {
if (debugger.isEnabled()) {
debugger.stopDebug();
}
if (inspector != null) {
inspector.dispose();
}
preprocessingService.dispose();
debugger.dispose();
preprocService.dispose();
inspect.dispose();
usage.dispose();
@@ -1405,201 +1376,16 @@ public class JavaEditor extends Editor {
}
/**
* Creates the debug menu. Includes ActionListeners for the menu items.
* Intended for adding to the menu bar.
*
* @return The debug menu
*/
protected JMenu buildDebugMenu() {
debugMenu = new JMenu(Language.text("menu.debug"));
JMenuItem item;
// "use the debugger" sounds too colloquial, and "enable" sounds too technical
// enableDebug =
// Toolkit.newJCheckBoxMenuItem(Language.text("menu.debug.enable"),
// KeyEvent.VK_D);
// enableDebug =
// Toolkit.newJCheckBoxMenuItem(Language.text("menu.debug.enable"),
// KeyEvent.VK_D);
debugItem = Toolkit.newJMenuItem(Language.text("menu.debug.enable"), 'D');
// enableDebug.setSelected(false);
debugItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// updateDebugToggle();
toggleDebug();
}
});
// toggleDebugger.addChangeListener(new ChangeListener() {
// public void stateChanged(ChangeEvent e) {
// }
// });
debugMenu.add(debugItem);
debugMenu.addSeparator();
// item = Toolkit.newJMenuItemAlt(Language.text("menu.debug.debug"), KeyEvent.VK_R);
// item.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Debug' menu item");
// debugger.startDebug();
// }
// });
// debugMenu.add(item);
item = Toolkit.newJMenuItem(Language.text("menu.debug.continue"), KeyEvent.VK_U);
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleContinue();
}
});
debugMenu.add(item);
item.setEnabled(false);
// item = new JMenuItem(Language.text("menu.debug.stop"));
// item.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Stop' menu item");
// debugger.stopDebug();
// }
// });
// debugMenu.add(item);
item = Toolkit.newJMenuItemExt("menu.debug.step");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleStep(0);
}
});
debugMenu.add(item);
item.setEnabled(false);
item = Toolkit.newJMenuItemExt("menu.debug.step_into");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleStep(ActionEvent.SHIFT_MASK);
}
});
debugMenu.add(item);
item.setEnabled(false);
item = Toolkit.newJMenuItemExt("menu.debug.step_out");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleStep(ActionEvent.ALT_MASK);
}
});
debugMenu.add(item);
item.setEnabled(false);
debugMenu.addSeparator();
item =
Toolkit.newJMenuItem(Language.text("menu.debug.toggle_breakpoint"), 'B');
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Messages.log("Invoked 'Toggle Breakpoint' menu item");
// TODO wouldn't getCaretLine() do the same thing with less effort?
toggleBreakpoint(getCurrentLineID().lineIdx());
}
});
debugMenu.add(item);
item.setEnabled(false);
/*
inspectorItem = Toolkit.newJMenuItem(Language.text("menu.debug.show_variables"), 'Y');
inspectorItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
toggleVariableInspector();
}
});
debugMenu.add(inspectorItem);
inspectorItem.setEnabled(false);
*/
/*
item = new JMenuItem(Language.text("menu.debug.list_breakpoints"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'List Breakpoints' menu item");
debugger.listBreakpoints();
}
});
debugMenu.add(item);
debugMenu.addSeparator();
*/
/*
item = new JMenuItem(Language.text("menu.debug.print_stack_trace"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Print Stack Trace' menu item");
debugger.printStackTrace();
}
});
debugMenu.add(item);
item = new JMenuItem(Language.text("menu.debug.print_locals"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Print Locals' menu item");
debugger.printLocals();
}
});
debugMenu.add(item);
item = new JMenuItem(Language.text("menu.debug.print_fields"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Print This' menu item");
debugger.printThis();
}
});
debugMenu.add(item);
item = new JMenuItem(Language.text("menu.debug.print_source_location"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Print Source' menu item");
debugger.printSource();
}
});
debugMenu.add(item);
item = new JMenuItem(Language.text("menu.debug.print_threads"));
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Print Threads' menu item");
debugger.printThreads();
}
});
debugMenu.add(item);
debugMenu.addSeparator();
*/
// item = Toolkit.newJMenuItem(Language.text("menu.debug.toggle_variable_inspector"), KeyEvent.VK_I);
// item.addActionListener(new ActionListener() {
// public void actionPerformed(ActionEvent e) {
// Logger.getLogger(JavaEditor.class.getName()).log(Level.INFO, "Invoked 'Toggle Variable Inspector' menu item");
// toggleVariableInspector();
// }
// });
// debugMenu.add(item);
return debugMenu;
}
@Override
public boolean isDebuggerEnabled() {
return debugEnabled;
return debugger.isEnabled();
}
@Override
public JMenu buildModeMenu() {
return buildDebugMenu();
//return buildDebugMenu();
return debugger.buildMenu();
}
@@ -1836,7 +1622,7 @@ public class JavaEditor extends Editor {
public PreprocService getPreprocessingService() {
return preprocessingService;
return preprocService;
}
@@ -1849,7 +1635,7 @@ public class JavaEditor extends Editor {
autoSave();
super.prepareRun();
downloadImports();
preprocessingService.cancel();
preprocService.cancel();
}
@@ -2020,18 +1806,8 @@ public class JavaEditor extends Editor {
}
/**
* Access variable inspector window.
* @return the variable inspector object
*/
public VariableInspector variableInspector() {
return inspector;
}
protected void activateRun() {
debugItem.setEnabled(false);
// toolbar.activate(JavaToolbar.RUN);
debugger.enableMenuItem(false);
toolbar.activateRun();
}
@@ -2044,12 +1820,11 @@ public class JavaEditor extends Editor {
*/
public void deactivateRun() {
toolbar.deactivateRun();
debugItem.setEnabled(true);
debugger.enableMenuItem(true);
}
protected void activateDebug() {
//debugToolbar.activate(DebugToolbar.DEBUG);
activateRun();
}
@@ -2080,11 +1855,12 @@ public class JavaEditor extends Editor {
public void toggleDebug() {
debugEnabled = !debugEnabled;
// debugEnabled = !debugEnabled;
rebuildToolbar();
repaint(); // show/hide breakpoints in the gutter
/*
if (debugEnabled) {
debugItem.setText(Language.text("menu.debug.disable"));
} else {
@@ -2097,6 +1873,7 @@ public class JavaEditor extends Editor {
item.setEnabled(debugEnabled);
}
}
*/
}

View File

@@ -77,8 +77,17 @@ public class JavaToolbar extends EditorToolbar {
Language.text("menu.debug.step_out")) {
@Override
public void actionPerformed(ActionEvent e) {
final int mask = ActionEvent.SHIFT_MASK | ActionEvent.ALT_MASK;
jeditor.handleStep(e.getModifiers() & mask);
Debugger d = jeditor.getDebugger();
final int modifiers = e.getModifiers() &
(ActionEvent.SHIFT_MASK | ActionEvent.ALT_MASK);
if (modifiers == 0) {
d.stepOver();
} else if ((modifiers & ActionEvent.SHIFT_MASK) != 0) {
d.stepInto();
} else if ((modifiers & ActionEvent.ALT_MASK) != 0) {
d.stepOut();
}
}
};
outgoing.add(stepButton);
@@ -88,7 +97,8 @@ public class JavaToolbar extends EditorToolbar {
Language.text("menu.debug.continue")) {
@Override
public void actionPerformed(ActionEvent e) {
jeditor.handleContinue();
//jeditor.handleContinue();
jeditor.getDebugger().continueDebug();
}
};
outgoing.add(continueButton);