/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.ins.method;
import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import com.sun.cri.ci.*;
import com.sun.cri.ri.*;
import com.sun.max.ins.*;
import com.sun.max.ins.debug.*;
import com.sun.max.ins.gui.*;
import com.sun.max.ins.gui.LocationLabel.AsAddressWithPosition;
import com.sun.max.ins.object.*;
import com.sun.max.ins.util.*;
import com.sun.max.ins.value.*;
import com.sun.max.ins.view.InspectionViews.ViewKind;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.method.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.value.*;
/**
* A table-based viewer for an (immutable) section of {@link MaxMachineCodeRoutine} in the VM.
* Supports visual effects for execution state, and permits user selection
* of instructions for various purposes (e.g. set breakpoint).
*/
public class JTableMachineCodeViewer extends MachineCodeViewer {
private static final int TRACE_VALUE = 2;
private final MaxPlatform.ISA isa;
private final Inspection inspection;
private final InspectorView view;
private final MachineCodeTable table;
private final MachineCodeTableModel tableModel;
private final MachineCodeViewPreferences instanceViewPreferences;
private final TableColumn[] columns;
private final OperandsRenderer operandsRenderer;
private final SourceLineRenderer sourceLineRenderer;
private final Color defaultBackgroundColor;
private final Color safepointBackgroundColor;
public JTableMachineCodeViewer(Inspection inspection, MethodView parent, MaxMachineCodeRoutine machineCode) {
super(inspection, parent, machineCode);
this.inspection = inspection;
this.view = parent;
//inspection.vm().bootImage().header.
this.isa = inspection.vm().platform().getISA();
this.operandsRenderer = new OperandsRenderer();
this.sourceLineRenderer = new SourceLineRenderer();
this.tableModel = new MachineCodeTableModel(inspection, machineCode);
this.columns = new TableColumn[MachineCodeColumnKind.values().length];
instanceViewPreferences = new MachineCodeViewPreferences(MachineCodeViewPreferences.globalPreferences(inspection())) {
@Override
public void setIsVisible(MachineCodeColumnKind columnKind, boolean visible) {
super.setIsVisible(columnKind, visible);
table.getInspectorTableColumnModel().setColumnVisible(columnKind.ordinal(), visible);
JTableColumnResizer.adjustColumnPreferredWidths(table);
refresh(true);
}
};
final MachineCodeTableColumnModel tableColumnModel = new MachineCodeTableColumnModel(instanceViewPreferences);
this.table = new MachineCodeTable(inspection, tableModel, tableColumnModel);
defaultBackgroundColor = this.table.getBackground();
safepointBackgroundColor = preference().style().darken2(defaultBackgroundColor);
createView();
}
@Override
protected void createView() {
super.createView();
// Set up toolbar
JButton button = new InspectorButton(inspection, actions().toggleMachineCodeBreakpoint());
button.setToolTipText(button.getText());
button.setText(null);
final InspectorStyle style = preference().style();
button.setIcon(style.debugToggleBreakpointbuttonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugStepOver());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugStepOverButtonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugSingleStep());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugStepInButtonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugReturnFromFrame());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugStepOutButtonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugRunToSelectedInstruction());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugRunToCursorButtonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugResume());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugContinueButtonIcon());
toolBar().add(button);
button = new InspectorButton(inspection, actions().debugPause());
button.setToolTipText(button.getText());
button.setText(null);
button.setIcon(style.debugPauseButtonIcon());
toolBar().add(button);
toolBar().add(Box.createHorizontalGlue());
toolBar().add(new JLabel("Machine Code"));
toolBar().add(Box.createHorizontalGlue());
addActiveRowsButton();
addSearchButton();
final JButton viewOptionsButton = new InspectorButton(inspection(), new AbstractAction("View...") {
public void actionPerformed(ActionEvent actionEvent) {
final MachineCodeViewPreferences globalPreferences = MachineCodeViewPreferences.globalPreferences(inspection());
new TableColumnVisibilityPreferences.ColumnPreferencesDialog<MachineCodeColumnKind>(inspection(), "Machine Code View Options", instanceViewPreferences, globalPreferences);
}
});
viewOptionsButton.setToolTipText("Machine code view options");
viewOptionsButton.setText(null);
viewOptionsButton.setIcon(style.generalPreferencesIcon());
toolBar().add(viewOptionsButton);
toolBar().add(Box.createHorizontalGlue());
addCodeViewCloseButton();
final JScrollPane scrollPane = new InspectorScrollPane(inspection(), table);
add(scrollPane, BorderLayout.CENTER);
refresh(true);
JTableColumnResizer.adjustColumnPreferredWidths(table);
}
@Override
protected int getRowCount() {
return table.getRowCount();
}
@Override
protected int getSelectedRow() {
return table.getSelectedRow();
}
@Override
protected void setFocusAtRow(int row) {
focus().setCodeLocation(machineCodeInfo().instructionLocation(row));
}
@Override
protected RowTextMatcher getRowTextSearcher() {
return new TableRowTextMatcher(inspection, table);
}
/**
* Global code selection has been set; return true iff the view contains selection.
* Update even when the selection is set to the same value, because we want
* that to force a scroll to make the selection visible.
*/
@Override
public boolean updateCodeFocus(MaxCodeLocation codeLocation) {
return table.updateCodeFocus(codeLocation);
}
@Override
public void refresh(boolean force) {
super.refresh(force);
table.refresh(force);
// updateSize();
}
@Override
public void redisplay() {
super.redisplay();
table.redisplay();
// TODO (mlvdv) code view hack for style changes
table.setRowHeight(preference().style().codeTableRowHeight());
invalidate();
repaint();
}
private MaxMachineCodeInfo machineCodeInfo() {
return machineCode().getMachineCodeInfo();
}
/**
* A table specialized for displaying a block of disassembled machine code, one instruction per line.
*/
private final class MachineCodeTable extends InspectorTable {
// TODO (mlvdv) Extract the table class
MachineCodeTable(Inspection inspection, MachineCodeTableModel tableModel, MachineCodeTableColumnModel tableColumnModel) {
super(inspection, tableModel, tableColumnModel);
setFillsViewportHeight(true);
final InspectorStyle style = preference().style();
setShowHorizontalLines(style.codeTableShowHorizontalLines());
setShowVerticalLines(style.codeTableShowVerticalLines());
setIntercellSpacing(style.codeTableIntercellSpacing());
setRowHeight(style.codeTableRowHeight());
setRowSelectionAllowed(true);
setColumnSelectionAllowed(true);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
@Override
protected void mouseButton1Clicked(int row, int col, MouseEvent mouseEvent) {
if (mouseEvent.getClickCount() > 1) {
// Depends on the first click selecting the row, and that changing the current
// code location focus to the location under the mouse event.
actions().toggleMachineCodeBreakpoint().perform();
}
}
@Override
protected InspectorPopupMenu getPopupMenu(int row, int col, MouseEvent mouseEvent) {
if (col == ObjectColumnKind.TAG.ordinal()) {
final InspectorPopupMenu menu = new InspectorPopupMenu();
final MachineCodeTableModel machineCodeTableModel = (MachineCodeTableModel) getModel();
final MaxCodeLocation codeLocation = machineCodeTableModel.rowToLocation(row);
menu.add(actions().debugRunToInstructionWithBreakpoints(codeLocation, "Run to this instruction"));
menu.add(actions().debugRunToInstruction(codeLocation, "Run to this instruction (ignoring breakpoints)"));
menu.add(actions().toggleMachineCodeBreakpoint(codeLocation, "Toggle breakpoint (double-click)"));
menu.add(actions().setMachineCodeBreakpoint(codeLocation, "Set breakpoint"));
menu.add(actions().removeMachineCodeBreakpoint(codeLocation, "Unset breakpoint"));
menu.add(views().activateSingletonViewAction(ViewKind.DEBUG_INFO));
return menu;
}
return null;
}
@Override
public void valueChanged(ListSelectionEvent e) {
// The selection in the table has changed; might have happened via user action (click, arrow) or
// as a side effect of a focus change.
super.valueChanged(e);
if (!e.getValueIsAdjusting()) {
final int selectedRow = getSelectedRow();
final MachineCodeTableModel machineCodeTableModel = (MachineCodeTableModel) getModel();
if (selectedRow >= 0 && selectedRow < machineCodeTableModel.getRowCount()) {
focus().setCodeLocation(machineCodeTableModel.rowToLocation(selectedRow));
}
}
}
/**
* Global code selection has been set; return true iff the view contains selection.
* Update even when the selection is set to the same value, because we want
* that to force a scroll to make the selection visible.
*/
public boolean updateCodeFocus(MaxCodeLocation codeLocation) {
final int oldSelectedRow = getSelectedRow();
if (codeLocation != null && codeLocation.hasAddress()) {
final Address machineCodeInstructionAddress = focus().codeLocation().address();
if (machineCode().contains(machineCodeInstructionAddress)) {
final MachineCodeTableModel model = (MachineCodeTableModel) getModel();
final int row = model.findRow(machineCodeInstructionAddress);
if (row >= 0) {
if (row != oldSelectedRow) {
updateSelection(row);
Trace.line(TRACE_VALUE, tracePrefix() + "changeSelection " + row);
}
scrollToRows(row, row);
Trace.line(TRACE_VALUE, tracePrefix() + " scroll to row " + row);
return true;
}
}
}
// View doesn't contain the focus; clear any old selection
if (oldSelectedRow >= 0) {
clearSelection();
}
return false;
}
public InspectorView getView() {
return view;
}
}
private final class MachineCodeTableColumnModel extends InspectorTableColumnModel<MachineCodeColumnKind> {
private MachineCodeTableColumnModel(MachineCodeViewPreferences viewPreferences) {
super(inspection(), MachineCodeColumnKind.values().length, viewPreferences);
final Address startAddress = tableModel.machineCode.getMachineCodeInfo().length() == 0 ? Address.zero() : tableModel.rowToInstruction(0).address;
addColumnIfSupported(MachineCodeColumnKind.TAG, new TagRenderer(inspection), null);
addColumnIfSupported(MachineCodeColumnKind.NUMBER, new NumberRenderer(), null);
addColumnIfSupported(MachineCodeColumnKind.ADDRESS, new AddressRenderer(startAddress), null);
addColumnIfSupported(MachineCodeColumnKind.POSITION, new PositionRenderer(startAddress), null);
addColumnIfSupported(MachineCodeColumnKind.LABEL, new LabelRenderer(startAddress), null);
addColumnIfSupported(MachineCodeColumnKind.INSTRUCTION, new InstructionRenderer(inspection), null);
addColumnIfSupported(MachineCodeColumnKind.OPERANDS, operandsRenderer, null);
addColumnIfSupported(MachineCodeColumnKind.SOURCE_LINE, sourceLineRenderer, null);
addColumnIfSupported(MachineCodeColumnKind.BYTES, new BytesRenderer(inspection), null);
}
}
/**
* Data model representing a block of disassembled code, one row per instruction.
*/
private final class MachineCodeTableModel extends InspectorTableModel {
final MaxMachineCodeRoutine machineCode;
public MachineCodeTableModel(Inspection inspection, MaxMachineCodeRoutine machineCode) {
super(inspection);
assert machineCode != null;
this.machineCode = machineCode;
}
public int getColumnCount() {
return MachineCodeColumnKind.values().length;
}
public int getRowCount() {
return machineCode.getMachineCodeInfo().length();
}
public Object getValueAt(int row, int col) {
final TargetCodeInstruction machineCodeInstruction = rowToInstruction(row);
switch (MachineCodeColumnKind.values()[col]) {
case TAG:
return null;
case NUMBER:
return row;
case ADDRESS:
return machineCodeInstruction.address;
case POSITION:
return machineCodeInstruction.position;
case LABEL:
final String label = machineCodeInstruction.label;
return label != null ? label + ":" : "";
case INSTRUCTION:
return machineCodeInstruction.mnemonic;
case OPERANDS:
return machineCodeInstruction.operands;
case SOURCE_LINE:
return "";
case BYTES:
return machineCodeInstruction.bytes;
default:
throw new RuntimeException("Column out of range: " + col);
}
}
@Override
public Class< ? > getColumnClass(int col) {
switch (MachineCodeColumnKind.values()[col]) {
case TAG:
return Object.class;
case NUMBER:
return Integer.class;
case ADDRESS:
return Address.class;
case POSITION:
return Integer.class;
case LABEL:
case INSTRUCTION:
case OPERANDS:
case SOURCE_LINE:
return String.class;
case BYTES:
return byte[].class;
default:
throw new RuntimeException("Column out of range: " + col);
}
}
@Override
public String getRowDescription(int row) {
return "Instruction " + row + " (" + rowToInstruction(row).mnemonic + ")";
}
public TargetCodeInstruction rowToInstruction(int row) {
return machineCode.getMachineCodeInfo().instruction(row);
}
public MaxCodeLocation rowToLocation(int row) {
return machineCode.getMachineCodeInfo().instructionLocation(row);
}
/**
* @param address a code address in the VM.
* @return the row in this block of code containing an instruction starting at the address, -1 if none.
*/
public int findRow(Address address) {
final MaxMachineCodeInfo machineCodeInfo = machineCode.getMachineCodeInfo();
for (int row = 0; row < machineCodeInfo.length(); row++) {
final TargetCodeInstruction machineCodeInstruction = machineCodeInfo.instruction(row);
if (machineCodeInstruction.address.equals(address)) {
return row;
}
}
return -1;
}
}
/**
* Return the appropriate color for displaying the row's text depending on whether the instruction pointer is at
* this row.
*
* @param row the row to check
* @param col TODO
* @return the color to be used
*/
public Color cellForegroundColor(int row, int col) {
final InspectorStyle style = preference().style();
return isInstructionPointer(row) ? style.debugIPTextColor() : (isCallReturn(row) ? style.debugCallReturnTextColor() : null);
}
private void setBorderForRow(JComponent component, int row) {
if (machineCodeInfo().isBytecodeBoundary(row)) {
component.setBorder(preference().style().defaultPaneTopBorder());
} else {
component.setBorder(null);
}
}
/**
* Sets the background of a cell rendering component, depending on the row context.
* <br>
* Makes the renderer transparent if there is no special background needed.
*/
private void setBackgroundForRow(JComponent component, int row) {
if (isSearchMatchRow(row)) {
component.setOpaque(true);
component.setBackground(preference().style().searchMatchedBackground());
} else if (machineCodeInfo().isSafepoint(row)) {
component.setOpaque(true);
component.setBackground(safepointBackgroundColor);
} else {
component.setOpaque(false);
//component.setBackground(getBackground());
}
}
private final class TagRenderer extends InspectorLabel implements TableCellRenderer, TextSearchable, Prober {
public TagRenderer(Inspection inspection) {
super(inspection);
}
public Component getTableCellRendererComponent(JTable table, Object ignore, boolean isSelected, boolean hasFocus, int row, int col) {
setToolTipPrefix(tableModel.getRowDescription(row));
final StringBuilder toolTipSB = new StringBuilder(100);
final MaxStackFrame stackFrame = stackFrame(row);
final InspectorStyle style = preference().style();
if (stackFrame != null) {
if (stackFrame.position() == 0) {
toolTipSB.append("<br>IP (stack frame 0) in thread ");
} else {
toolTipSB.append("<br>Call return (frame ");
toolTipSB.append(stackFrame.position());
toolTipSB.append(") in thread ");
}
toolTipSB.append(inspection.nameDisplay().longName(stackFrame.stack().thread()));
toolTipSB.append(" points here");
if (stackFrame.isTop()) {
setIcon(style.debugIPTagIcon());
setForeground(style.debugIPTagColor());
} else {
setIcon(style.debugCallReturnTagIcon());
setForeground(style.debugCallReturnTagColor());
}
} else {
setIcon(null);
setForeground(null);
}
setText(rowToTagText(row));
final MaxBreakpoint machineCodeBreakpoint = getMachineCodeBreakpointAtRow(row);
if (machineCodeBreakpoint != null) {
toolTipSB.append("<br>breakpoint set @ ");
toolTipSB.append(machineCodeBreakpoint.codeLocation().address().to0xHexString());
toolTipSB.append(machineCodeBreakpoint.isEnabled() ? ", enabled" : ", disabled");
if (machineCodeBreakpoint.isEnabled()) {
setBorder(style.debugEnabledMachineCodeBreakpointTagBorder());
} else {
setBorder(style.debugDisabledMachineCodeBreakpointTagBorder());
}
} else if (machineCodeInfo().isBytecodeBoundary(row)) {
setBorder(style.defaultPaneTopBorder());
} else {
setBorder(null);
}
setWrappedToolTipHtmlText(toolTipSB.toString());
setBackgroundForRow(this, row);
return this;
}
@Override
public String getSearchableText() {
return "";
}
public void redisplay() {
}
public void refresh(boolean force) {
}
}
private final class NumberRenderer extends PlainLabel implements TableCellRenderer {
public NumberRenderer() {
super(inspection, "");
}
public Component getTableCellRendererComponent(JTable table, Object ignore, boolean isSelected, boolean hasFocus, int row, int column) {
setValue(row);
setToolTipText(tableModel.getRowDescription(row));
setBackgroundForRow(this, row);
setForeground(cellForegroundColor(row, column));
setBorderForRow(this, row);
return this;
}
}
private final class AddressRenderer extends AsAddressWithPosition implements TableCellRenderer {
private final Address entryAddress;
AddressRenderer(Address entryAddress) {
super(inspection, 0, entryAddress);
this.entryAddress = entryAddress;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return gui().getUnavailableDataTableCellRenderer();
}
final Address address = (Address) value;
setToolTipPrefix(tableModel.getRowDescription(row) + " location<br>address= ");
setValue(address.minus(entryAddress).asSize().toInt());
setBackgroundForRow(this, row);
setForeground(cellForegroundColor(row, column));
setBorderForRow(this, row);
return this;
}
}
private final class PositionRenderer extends LocationLabel.AsPosition implements TableCellRenderer {
private int position;
public PositionRenderer(Address entryAddress) {
super(inspection, 0, entryAddress);
this.position = 0;
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return gui().getUnavailableDataTableCellRenderer();
}
final Integer position = (Integer) value;
if (this.position != position) {
this.position = position;
setValue(position);
}
setToolTipPrefix(tableModel.getRowDescription(row) + " location<br>address= ");
setBackgroundForRow(this, row);
setForeground(cellForegroundColor(row, column));
setBorderForRow(this, row);
return this;
}
}
private final class LabelRenderer extends LocationLabel.AsTextLabel implements TableCellRenderer {
public LabelRenderer(Address entryAddress) {
super(inspection, entryAddress);
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final String label = (String) value;
final Integer position = (Integer) tableModel.getValueAt(row, MachineCodeColumnKind.POSITION.ordinal());
setLocation(label, position);
setWrappedToolTipHtmlText(tableModel.getRowDescription(row));
final InspectorStyle style = preference().style();
setFont(style.defaultFont());
setBackgroundForRow(this, row);
//setForeground(getRowTextColor(row));
if (isInstructionPointer(row)) {
setForeground(style.debugIPTextColor());
} else if (isCallReturn(row)) {
setForeground(style.debugCallReturnTextColor());
} else {
setForeground(null);
}
setBorderForRow(this, row);
return this;
}
}
private final class InstructionRenderer extends MachineCodeLabel implements TableCellRenderer {
InstructionRenderer(Inspection inspection) {
super(inspection, "");
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return gui().getUnavailableDataTableCellRenderer();
}
final String mnemonic = (String) value;
setBackgroundForRow(this, row);
setForeground(cellForegroundColor(row, column));
setText(mnemonic);
setWrappedToolTipHtmlText(tableModel.getRowDescription(row) + "<br>ISA = " + isa.name());
setBorderForRow(this, row);
return this;
}
}
private interface LiteralRenderer {
WordValueLabel render(Inspection inspection, String literalLoadText, Address literalAddress);
}
static final LiteralRenderer AMD64_LITERAL_RENDERER = new LiteralRenderer() {
public WordValueLabel render(Inspection inspection, String literalLoadText, final Address literalAddress) {
final WordValueLabel wordValueLabel = new WordValueLabel(inspection, WordValueLabel.ValueMode.LITERAL_REFERENCE, null, true) {
@Override
public Value fetchValue() {
return vm().memoryIO().readWordValue(literalAddress);
}
};
wordValueLabel.setTextPrefix(literalLoadText.substring(0, literalLoadText.indexOf("[")).trim());
wordValueLabel.setToolTipSuffix(" from RIP " + literalLoadText.substring(literalLoadText.indexOf("["), literalLoadText.length()));
wordValueLabel.setWordDataFont(inspection.preference().style().defaultBoldFont());
wordValueLabel.updateText();
return wordValueLabel;
}
};
static final LiteralRenderer SPARC_LITERAL_RENDERER = new LiteralRenderer() {
public WordValueLabel render(Inspection inspection, String literalLoadText, final Address literalAddress) {
final WordValueLabel wordValueLabel = new WordValueLabel(inspection, WordValueLabel.ValueMode.LITERAL_REFERENCE, null, true) {
@Override
public Value fetchValue() {
return vm().memoryIO().readWordValue(literalAddress);
}
};
wordValueLabel.setTextSuffix(literalLoadText.substring(literalLoadText.indexOf(",")));
wordValueLabel.setToolTipSuffix(" from " + literalLoadText.substring(0, literalLoadText.indexOf(",")));
wordValueLabel.setWordDataFont(inspection.preference().style().defaultBoldFont());
wordValueLabel.updateText();
return wordValueLabel;
}
};
LiteralRenderer getLiteralRenderer(Inspection inspection) {
switch (isa) {
case AMD64:
return AMD64_LITERAL_RENDERER;
case SPARC:
return SPARC_LITERAL_RENDERER;
case ARM:
case PPC:
case IA32:
InspectorError.unimplemented();
return null;
}
InspectorError.unknownCase();
return null;
}
private final class SourceLineRenderer extends PlainLabel implements TableCellRenderer {
private CiCodePos lastCodePos;
SourceLineRenderer() {
super(JTableMachineCodeViewer.this.inspection(), null);
addMouseListener(new InspectorMouseClickAdapter(inspection()) {
@Override
public void procedure(final MouseEvent mouseEvent) {
final CiCodePos frame = lastCodePos;
if (frame != null) {
final InspectorPopupMenu menu = new InspectorPopupMenu();
for (CiCodePos location = frame; location != null; location = location.caller) {
final StackTraceElement stackTraceElement = location.method.toStackTraceElement(location.bci);
final String fileName = stackTraceElement.getFileName();
if (fileName != null) {
final int lineNumber = stackTraceElement.getLineNumber();
if (lineNumber > 0) {
if (vm().findJavaSourceFile(((ClassMethodActor) location.method).codeAttribute().cp.holder()) != null) {
final CiCodePos codePosCopy = location;
menu.add(new AbstractAction("Open " + fileName + " at line " + lineNumber) {
public void actionPerformed(ActionEvent e) {
inspection().viewSourceExternally(codePosCopy);
}
});
}
}
}
}
if (menu.getComponentCount() > 0) {
menu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY());
}
}
}
});
}
private String toolTipText(StackTraceElement stackTraceElement) {
String s = stackTraceElement.toString();
final int openParen = s.indexOf('(');
String methodName = stackTraceElement.getMethodName().replaceAll("<", "<").replaceAll(">", ">");
s = Classes.getSimpleName(stackTraceElement.getClassName()) + "." + methodName + s.substring(openParen);
final String text = s;
return text;
}
public Component getTableCellRendererComponent(JTable table, Object ignore, boolean isSelected, boolean hasFocus, int row, int column) {
CiDebugInfo debugInfo = machineCodeInfo().debugInfoAt(row);
final CiCodePos codePos = debugInfo == null ? null : debugInfo.codePos;
setText("");
setToolTipPrefix(tableModel.getRowDescription(row) + "<br>");
setWrappedToolTipHtmlText("Source location not available");
setBackgroundForRow(this, row);
if (codePos != null) {
final StackTraceElement stackTraceElement = codePos.method.toStackTraceElement(codePos.bci);
final StringBuilder stackTrace = new StringBuilder("<table cellpadding=\"1%\"><tr><td></td><td>").append(toolTipText(stackTraceElement)).append("</td></tr>");
StackTraceElement top = stackTraceElement;
for (CiCodePos caller = codePos.caller; caller != null; caller = caller.caller) {
StackTraceElement parentSTE = caller.method.toStackTraceElement(caller.bci);
stackTrace.append("<tr><td>--> </td><td>").append(toolTipText(parentSTE)).append("</td></tr>");
top = parentSTE;
}
setWrappedToolTipHtmlText("Source location = " + stackTrace.append("</table>").toString());
setText(String.valueOf(top.getLineNumber()));
}
lastCodePos = codePos;
setBorderForRow(this, row);
return this;
}
}
private final class OperandsRenderer implements TableCellRenderer, Prober {
private InspectorLabel[] inspectorLabels = new InspectorLabel[machineCodeInfo().length()];
private MachineCodeLabel machineCodeLabel = new MachineCodeLabel(inspection, "");
private LiteralRenderer literalRenderer = getLiteralRenderer(inspection);
public void refresh(boolean force) {
for (InspectorLabel wordValueLabel : inspectorLabels) {
if (wordValueLabel != null) {
wordValueLabel.refresh(force);
}
}
}
public void redisplay() {
for (InspectorLabel wordValueLabel : inspectorLabels) {
if (wordValueLabel != null) {
wordValueLabel.redisplay();
}
}
machineCodeLabel.redisplay();
}
public Component getTableCellRendererComponent(JTable table, Object ignore, boolean isSelected, boolean hasFocus, int row, int column) {
InspectorLabel renderer = inspectorLabels[row];
if (renderer == null) {
final TargetCodeInstruction machineCodeInstruction = tableModel.rowToInstruction(row);
final String text = machineCodeInstruction.operands;
if (machineCodeInstruction.targetAddress != null && !machineCode().contains(machineCodeInstruction.targetAddress)) {
final WordValueLabel wordValueLabel = new WordValueLabel(inspection, WordValueLabel.ValueMode.CALL_ENTRY_POINT, machineCodeInstruction.targetAddress, table, true);
wordValueLabel.setToolTipPrefix(tableModel.getRowDescription(row) + ": operand = ");
wordValueLabel.setWordDataFont(inspection.preference().style().defaultBoldFont());
renderer = wordValueLabel;
inspectorLabels[row] = renderer;
} else if (machineCodeInstruction.literalSourceAddress != null) {
final Address literalAddress = machineCodeInstruction.literalSourceAddress.asAddress();
renderer = literalRenderer.render(inspection, text, literalAddress);
renderer.setToolTipPrefix(tableModel.getRowDescription(row) + ": operand = ");
inspectorLabels[row] = renderer;
} else {
MaxMachineCodeInfo machineCodeInfo = machineCodeInfo();
final RiMethod callee = machineCodeInfo.calleeAt(row);
if (machineCodeInstruction.mnemonic.contains("call") && callee != null) {
renderer = new TextLabel(inspection, CiUtil.format("%h.%n()", callee));
renderer.setToolTipPrefix(tableModel.getRowDescription(row) + ":");
renderer.setToolTipSuffix("<br>" + CiUtil.format("%r %H.%n(%p)", callee));
renderer.setWrappedToolTipHtmlText("<br>operands = " + text);
renderer.setForeground(cellForegroundColor(row, column));
} else {
if (machineCodeInfo.isNativeCall(row)) {
renderer = new TextLabel(inspection, "<native function>");
renderer.setToolTipPrefix(tableModel.getRowDescription(row) + ":");
renderer.setWrappedToolTipHtmlText("<br>operands = " + text);
renderer.setForeground(cellForegroundColor(row, column));
} else {
renderer = machineCodeLabel;
renderer.setToolTipPrefix(tableModel.getRowDescription(row) + ":");
renderer.setText(text);
renderer.setWrappedToolTipHtmlText("<br>operands = " + text);
renderer.setForeground(cellForegroundColor(row, column));
}
}
}
}
setBackgroundForRow(renderer, row);
setBorderForRow(renderer, row);
return renderer;
}
}
private final class BytesRenderer extends DataLabel.ByteArrayAsHex implements TableCellRenderer {
BytesRenderer(Inspection inspection) {
super(inspection, null);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return gui().getUnavailableDataTableCellRenderer();
}
final byte[] bytes = (byte[]) value;
setBackgroundForRow(this, row);
setForeground(cellForegroundColor(row, column));
setToolTipPrefix(tableModel.getRowDescription(row) + "<br>as bytes = ");
setValue(bytes);
setBorderForRow(this, row);
return this;
}
}
@Override
public void print(String name) {
final MessageFormat header = new MessageFormat(name);
final MessageFormat footer = new MessageFormat(vm().entityName() + ": " + codeViewerKindName() + " Printed: " + new Date() + " -- Page: {0, number, integer}");
try {
table.print(JTable.PrintMode.FIT_WIDTH, header, footer);
} catch (PrinterException printerException) {
gui().errorMessage("Print failed: " + printerException.getMessage());
}
}
}