blob: 73ab03b6f63ec4a51c4573cdb83d604384d8835c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 Wind River Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Wind River Systems - initial API and implementation
* Patrick Chuong (Texas Instruments) - Bug 326670
* Patrick Chuong (Texas Instruments) - Bug 329682
* Patrick Chuong (Texas Instruments) - Bug 330259
* Patrick Chuong (Texas Instruments) - Pin and Clone Supports (331781)
* Patrick Chuong (Texas Instruments) - Bug 364405
* Patrick Chuong (Texas Instruments) - Bug 369998
* Patrick Chuong (Texas Instruments) - Bug 337851
*******************************************************************************/
package org.eclipse.cdt.dsf.debug.internal.ui.disassembly;
import static org.eclipse.cdt.debug.internal.ui.disassembly.dsf.DisassemblyUtils.getAddressText;
import static org.eclipse.cdt.debug.internal.ui.disassembly.dsf.DisassemblyUtils.internalError;
import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.AbstractDisassemblyBackend;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.AddressRangePosition;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.DisassemblyPosition;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.DisassemblyUtils;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.ErrorPosition;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyBackend;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyDocument;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback;
import org.eclipse.cdt.debug.internal.ui.disassembly.dsf.LabelPosition;
import org.eclipse.cdt.debug.internal.ui.preferences.StringSetSerializer;
import org.eclipse.cdt.debug.ui.disassembly.rulers.IColumnSupport;
import org.eclipse.cdt.debug.ui.disassembly.rulers.IContributedRulerColumn;
import org.eclipse.cdt.debug.ui.disassembly.rulers.RulerColumnDescriptor;
import org.eclipse.cdt.debug.ui.disassembly.rulers.RulerColumnPreferenceAdapter;
import org.eclipse.cdt.debug.ui.disassembly.rulers.RulerColumnRegistry;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyAction;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoAddress;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoProgramCounter;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionOpenPreferences;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AddressBarContributionItem;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.JumpToAddressAction;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.TextOperationAction;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.BreakpointsAnnotationModel;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences.DisassemblyPreferenceConstants;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.DisassemblyIPAnnotation;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.provisional.DisassemblyAnnotationModel;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.provisional.DisassemblyRulerColumn;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.provisional.DisassemblyViewer;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.provisional.IDisassemblyPart;
import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util.HSL;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.ui.dnd.TextViewerDragAdapter;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
import org.eclipse.debug.ui.contexts.IDebugContextListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.ResourceLocator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextPresentationListener;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.IVerticalRulerExtension;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.WorkbenchPart;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.FindReplaceAction;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SimpleMarkerAnnotation;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import com.ibm.icu.text.MessageFormat;
/**
* DisassemblyPart
*/
@SuppressWarnings("restriction")
public abstract class DisassemblyPart extends WorkbenchPart
implements IDisassemblyPart, IViewportListener, ITextPresentationListener, IDisassemblyPartCallback {
final static boolean DEBUG = Boolean
.parseBoolean(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/disassembly")); //$NON-NLS-1$
/**
* Annotation model attachment key for breakpoint annotations.
*/
private final static String BREAKPOINT_ANNOTATIONS = "breakpoints"; //$NON-NLS-1$
/**
* Annotation model attachment key for extended PC annotations.
*/
private final static String EXTENDED_PC_ANNOTATIONS = "ExtendedPCAnnotations"; //$NON-NLS-1$
private final static BigInteger PC_UNKNOWN = BigInteger.valueOf(-1);
private final static BigInteger PC_RUNNING = BigInteger.valueOf(-2);
/** Preference key for highlighting current line. */
private final static String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE;
/** Preference key for highlight color of current line. */
private final static String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
/** The width of the vertical ruler. */
protected final static int VERTICAL_RULER_WIDTH = 12;
/** High water mark for cache */
private final static int fgHighWaterMark = 500;
/** Low water mark for cache */
private final static int fgLowWaterMark = 100;
private static final String COMMAND_ID_GOTO_ADDRESS = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress"; //$NON-NLS-1$
private static final String COMMAND_ID_GOTO_PC = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoPC"; //$NON-NLS-1$
private static final String COMMAND_ID_TOGGLE_BREAKPOINT = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.rulerToggleBreakpoint"; //$NON-NLS-1$
public static final String KEY_BINDING_CONTEXT_DISASSEMBLY = "org.eclipse.cdt.dsf.debug.ui.disassembly.context"; //$NON-NLS-1$
/**
* A named preference that controls the visible ruler column contributions.
*/
public static final String PREFERENCE_RULER_CONTRIBUTIONS = "rulerContributions"; //$NON-NLS-1$
protected DisassemblyViewer fViewer;
protected AbstractDisassemblyAction fActionGotoPC;
protected AbstractDisassemblyAction fActionGotoAddress;
protected AbstractDisassemblyAction fActionToggleSource;
protected AbstractDisassemblyAction fActionToggleSymbols;
protected AbstractDisassemblyAction fActionRefreshView;
protected Action fActionOpenPreferences;
private AbstractDisassemblyAction fActionToggleBreakpointEnablement;
protected DisassemblyDocument fDocument;
private IAnnotationAccess fAnnotationAccess;
private AnnotationRulerColumn fAnnotationRulerColumn;
private MarkerAnnotationPreferences fAnnotationPreferences;
private IPreferenceStore fPreferenceStore;
private IOverviewRuler fOverviewRuler;
private final ListenerList<IMenuListener> fRulerContextMenuListeners = new ListenerList<>(ListenerList.IDENTITY);
private SourceViewerDecorationSupport fDecorationSupport;
private Font fFont;
private IVerticalRuler fVerticalRuler;
private IFindReplaceTarget fFindReplaceTarget;
private IPropertyChangeListener fPropertyChangeListener = new PropertyChangeListener();
private Color fInstructionColor;
private Color fErrorColor;
private Color fSourceColor;
private Color fLabelColor;
private Control fRedrawControl;
private RGB fPCAnnotationRGB;
protected Composite fComposite;
private DropTarget fDropTarget;
private DragSource fDragSource;
private TextViewerDragAdapter fDragSourceAdapter;
private DisassemblyDropAdapter fDropTargetAdapter;
private BigInteger fStartAddress;
private BigInteger fEndAddress;
private int fAddressSize = 32;
private volatile boolean fUpdatePending;
private volatile int fUpdateCount;
private BigInteger fPCAddress;
private BigInteger fGotoAddressPending = PC_UNKNOWN;
private BigInteger fFocusAddress = PC_UNKNOWN;
private int fBufferZone;
private String fDebugSessionId;
private int fTargetFrame;
private DisassemblyIPAnnotation fPCAnnotation;
private DisassemblyIPAnnotation fSecondaryPCAnnotation;
private boolean fPCAnnotationUpdatePending;
private ArrayList<BigInteger> fPendingPCUpdates = new ArrayList<>(5);
private Position fScrollPos;
private int fScrollLine;
private Position fFocusPos;
private BigInteger fFrameAddress = PC_UNKNOWN;
protected Map<String, Action> fGlobalActions = new HashMap<>();
private List<Action> fSelectionActions = new ArrayList<>();
private List<AbstractDisassemblyAction> fStateDependentActions = new ArrayList<>();
private boolean fShowSource;
private boolean fShowSymbols;
private Map<String, Object> fFile2Storage = new HashMap<>();
private boolean fShowDisassembly = true;
private LinkedList<AddressRangePosition> fPCHistory = new LinkedList<>();
private int fPCHistorySizeMax = 4;
private boolean fGotoFramePending;
protected Action fTrackExpressionAction;
protected Action fSyncAction;
protected boolean fSynchWithActiveDebugContext = true;
protected boolean fTrackExpression = false;
private String fPCLastLocationTxt = DisassemblyMessages.Disassembly_GotoLocation_initial_text;
private BigInteger fPCLastAddress = PC_UNKNOWN;
private IAdaptable fDebugContext;
private String fPCAnnotationColorKey;
private ArrayList<Runnable> fRunnableQueue = new ArrayList<>();
protected IPartListener2 fPartListener = new IPartListener2() {
@Override
public void partActivated(IWorkbenchPartReference partRef) {
}
@Override
public void partBroughtToTop(IWorkbenchPartReference partRef) {
}
@Override
public void partClosed(IWorkbenchPartReference partRef) {
}
@Override
public void partDeactivated(IWorkbenchPartReference partRef) {
}
@Override
public void partOpened(IWorkbenchPartReference partRef) {
}
@Override
public void partHidden(IWorkbenchPartReference partRef) {
if (partRef.getPart(false) == DisassemblyPart.this) {
setActive(false);
}
}
@Override
public void partVisible(IWorkbenchPartReference partRef) {
if (partRef.getPart(false) == DisassemblyPart.this) {
setActive(true);
}
}
@Override
public void partInputChanged(IWorkbenchPartReference partRef) {
}
};
private boolean fActive = true;
private boolean fDoPendingPosted;
private boolean fUpdateBeforeFocus;
private boolean fRefreshAll;
private IMarker fGotoMarkerPending;
private boolean fUpdateTitlePending;
private boolean fRefreshViewPending;
private boolean fUpdateSourcePending;
private ArrayList<IHandlerActivation> fHandlerActivations;
private IContextActivation fContextActivation;
private IDisassemblyBackend fBackend;
private AddressBarContributionItem fAddressBar = null;
private Action fJumpToAddressAction = new JumpToAddressAction(this);
private IDebugContextListener fDebugContextListener;
private DisassemblyAnnotationModel fExtPCAnnotationModel;
private IColumnSupport fColumnSupport;
private final class SyncActiveDebugContextAction extends Action {
public SyncActiveDebugContextAction() {
setChecked(DisassemblyPart.this.isSyncWithActiveDebugContext());
setText(DisassemblyMessages.Disassembly_action_Sync_label);
setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Sync_enabled));
setDisabledImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Sync_disabled));
}
@Override
public void run() {
DisassemblyPart.this.setSyncWithDebugView(this.isChecked());
}
}
private final class TrackExpressionAction extends Action {
public TrackExpressionAction() {
setChecked(DisassemblyPart.this.isTrackExpression());
setEnabled(!fSynchWithActiveDebugContext);
setText(DisassemblyMessages.Disassembly_action_TrackExpression_label);
}
@Override
public void run() {
DisassemblyPart.this.setTrackExpression(this.isChecked());
}
}
private final class ActionRefreshView extends AbstractDisassemblyAction {
public ActionRefreshView() {
super(DisassemblyPart.this);
setText(DisassemblyMessages.Disassembly_action_RefreshView_label);
setImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_enabled));
setDisabledImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_disabled));
}
@Override
public void run() {
refresh();
}
}
private final class ActionToggleBreakpointEnablement extends AbstractDisassemblyAction {
private IBreakpoint fBreakpoint;
public ActionToggleBreakpointEnablement() {
super(DisassemblyPart.this);
setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label + "\t" + //$NON-NLS-1$
CDebugUIUtils.formatKeyBindingString(SWT.MOD2,
DisassemblyMessages.Disassembly_action_ToggleBreakpoint_accelerator));
}
@Override
public void run() {
try {
fBreakpoint.setEnabled(!fBreakpoint.isEnabled());
} catch (CoreException e) {
internalError(e);
}
}
@Override
public void update() {
super.update();
if (isEnabled()) {
int line = fVerticalRuler.getLineOfLastMouseButtonActivity();
IBreakpoint[] bps = getBreakpointsAtLine(line);
if (bps == null || bps.length == 0) {
setEnabled(false);
} else {
fBreakpoint = bps[0];
try {
if (fBreakpoint.isEnabled()) {
setText(DisassemblyMessages.Disassembly_action_DisableBreakpoint_label + "\t" + //$NON-NLS-1$
CDebugUIUtils.formatKeyBindingString(SWT.MOD2,
DisassemblyMessages.Disassembly_action_ToggleBreakpoint_accelerator));
} else {
setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label + "\t" + //$NON-NLS-1$
CDebugUIUtils.formatKeyBindingString(SWT.MOD2,
DisassemblyMessages.Disassembly_action_ToggleBreakpoint_accelerator));
}
} catch (CoreException e) {
setEnabled(false);
}
}
}
}
}
private final class ActionToggleSource extends AbstractDisassemblyAction {
public ActionToggleSource() {
super(DisassemblyPart.this, IAction.AS_CHECK_BOX);
setText(DisassemblyMessages.Disassembly_action_ShowSource_label);
}
@Override
public void run() {
IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore();
store.setValue(DisassemblyPreferenceConstants.SHOW_SOURCE, !fShowSource);
}
@Override
public void update() {
super.update();
if (isEnabled()) {
setEnabled(fShowDisassembly);
}
setChecked(fShowSource);
}
}
private final class ActionToggleSymbols extends AbstractDisassemblyAction {
public ActionToggleSymbols() {
super(DisassemblyPart.this, IAction.AS_CHECK_BOX);
setText(DisassemblyMessages.Disassembly_action_ShowSymbols_label);
}
@Override
public void run() {
IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore();
store.setValue(DisassemblyPreferenceConstants.SHOW_SYMBOLS, !fShowSymbols);
}
@Override
public void update() {
super.update();
setChecked(fShowSymbols);
}
}
/**
* Internal property change listener for handling changes in the
* preferences.
*/
class PropertyChangeListener implements IPropertyChangeListener {
/*
* @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
handlePreferenceStoreChanged(event);
}
}
/**
* The constructor.
*/
public DisassemblyPart() {
fAnnotationPreferences = new MarkerAnnotationPreferences();
setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] {
DsfUIPlugin.getDefault().getPreferenceStore(), EditorsUI.getPreferenceStore() }));
fPCAddress = fFrameAddress = PC_UNKNOWN;
fTargetFrame = -1;
fBufferZone = 32;
fPCAnnotation = new DisassemblyIPAnnotation(true, 0);
fSecondaryPCAnnotation = new DisassemblyIPAnnotation(false, 0);
IPreferenceStore prefs = getPreferenceStore();
fStartAddress = new BigInteger(prefs.getString(DisassemblyPreferenceConstants.START_ADDRESS));
String endAddressString = prefs.getString(DisassemblyPreferenceConstants.END_ADDRESS);
if (endAddressString.startsWith("0x")) //$NON-NLS-1$
fEndAddress = new BigInteger(endAddressString.substring(2), 16);
else
fEndAddress = new BigInteger(endAddressString, 16);
fShowSource = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SOURCE);
fShowSymbols = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SYMBOLS);
fUpdateBeforeFocus = !prefs.getBoolean(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC);
fPCHistorySizeMax = prefs.getInt(DisassemblyPreferenceConstants.PC_HISTORY_SIZE);
}
public void logWarning(String message, Throwable error) {
DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, message, error));
}
/*
* @see IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> required) {
if (IVerticalRulerInfo.class.equals(required)) {
if (fVerticalRuler != null) {
return (T) fVerticalRuler;
}
} else if (IDisassemblyPart.class.equals(required)) {
return (T) this;
} else if (IFindReplaceTarget.class.equals(required)) {
if (fFindReplaceTarget == null) {
fFindReplaceTarget = (fViewer == null ? null : fViewer.getFindReplaceTarget());
}
return (T) fFindReplaceTarget;
} else if (ITextOperationTarget.class.equals(required)) {
return (fViewer == null ? null : (T) fViewer.getTextOperationTarget());
} else if (Control.class.equals(required)) {
return fViewer != null ? (T) fViewer.getTextWidget() : null;
} else if (IGotoMarker.class.equals(required)) {
return (T) (IGotoMarker) marker -> DisassemblyPart.this.gotoMarker(marker);
} else if (IColumnSupport.class.equals(required)) {
if (fColumnSupport == null)
fColumnSupport = createColumnSupport();
return (T) fColumnSupport;
}
return super.getAdapter(required);
}
/**
* Adds "show" actions for all contributed rulers that support it.
*
* @param menu the ruler context menu
*/
private void addRulerContributionActions(IMenuManager menu) {
// store directly in generic editor preferences
final IColumnSupport support = getAdapter(IColumnSupport.class);
IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore();
final RulerColumnPreferenceAdapter adapter = new RulerColumnPreferenceAdapter(store,
PREFERENCE_RULER_CONTRIBUTIONS);
List<RulerColumnDescriptor> descriptors = RulerColumnRegistry.getDefault().getColumnDescriptors();
for (final RulerColumnDescriptor descriptor : descriptors) {
if (!descriptor.isIncludedInMenu() || !support.isColumnSupported(descriptor))
continue;
final boolean isVisible = support.isColumnVisible(descriptor);
IAction action = new Action(MessageFormat.format(DisassemblyMessages.DisassemblyPart_showRulerColumn_label,
new Object[] { descriptor.getName() }), IAction.AS_CHECK_BOX) {
@Override
public void run() {
if (descriptor.isGlobal())
// column state is modified via preference listener
adapter.setEnabled(descriptor, !isVisible);
else
// directly modify column for this editor instance
support.setColumnVisible(descriptor, !isVisible);
}
};
action.setChecked(isVisible);
action.setImageDescriptor(descriptor.getIcon());
menu.appendToGroup(ITextEditorActionConstants.GROUP_RULERS, action);
}
}
/**
* Adds enabled ruler contributions to the vertical ruler.
*
* @param ruler the composite ruler to add contributions to
*/
protected void updateContributedRulerColumns(CompositeRuler ruler) {
IColumnSupport support = getAdapter(IColumnSupport.class);
if (support == null)
return;
RulerColumnPreferenceAdapter adapter = null;
if (fPreferenceStore != null)
adapter = new RulerColumnPreferenceAdapter(getPreferenceStore(), PREFERENCE_RULER_CONTRIBUTIONS);
RulerColumnRegistry registry = RulerColumnRegistry.getDefault();
List<RulerColumnDescriptor> descriptors = registry.getColumnDescriptors();
for (RulerColumnDescriptor descriptor : descriptors) {
support.setColumnVisible(descriptor, adapter == null || adapter.isEnabled(descriptor));
}
}
protected IColumnSupport createColumnSupport() {
return new DisassemblyColumnSupport(this, RulerColumnRegistry.getDefault());
}
private void setPreferenceStore(IPreferenceStore store) {
if (fPreferenceStore != null) {
fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
}
fPreferenceStore = store;
if (fPreferenceStore != null) {
fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener);
}
}
/**
* Handles a property change event describing a change of the editor's
* preference store and updates the preference related editor properties.
* <p>
* Subclasses may extend.
* </p>
*
* @param event
* the property change event
*/
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
if (fViewer == null)
return;
String property = event.getProperty();
IPreferenceStore store = getPreferenceStore();
if (getFontPropertyPreferenceKey().equals(property)) {
initializeViewerFont(fViewer);
} else if (property.equals(DisassemblyPreferenceConstants.SHOW_SOURCE)) {
boolean showSource = store.getBoolean(property);
if (fShowSource == showSource) {
return;
}
fShowSource = showSource;
fActionToggleSource.update();
refreshView(10);
} else if (property.equals(DisassemblyPreferenceConstants.SHOW_SYMBOLS)) {
boolean showSymbols = store.getBoolean(property);
if (fShowSymbols == showSymbols) {
return;
}
fShowSymbols = showSymbols;
fActionToggleSymbols.update();
refreshView(10);
} else if (property.equals(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC)) {
fUpdateBeforeFocus = !store.getBoolean(property);
updateVisibleArea();
} else if (property.equals(fPCAnnotationColorKey)) {
fPCAnnotationRGB = PreferenceConverter.getColor(store, fPCAnnotationColorKey);
// redraw
for (Iterator<AddressRangePosition> it = fPCHistory.iterator(); it.hasNext();) {
AddressRangePosition pos = it.next();
fViewer.invalidateTextPresentation(pos.offset, pos.length);
}
} else if (property.equals(DisassemblyPreferenceConstants.PC_HISTORY_SIZE)) {
fPCHistorySizeMax = store.getInt(property);
} else if (PREFERENCE_RULER_CONTRIBUTIONS.equals(property)) {
String[] difference = StringSetSerializer.getDifference((String) event.getOldValue(),
(String) event.getNewValue());
IColumnSupport support = getAdapter(IColumnSupport.class);
for (int i = 0; i < difference.length; i++) {
RulerColumnDescriptor desc = RulerColumnRegistry.getDefault().getColumnDescriptor(difference[i]);
if (desc != null && support.isColumnSupported(desc)) {
boolean newState = !support.isColumnVisible(desc);
support.setColumnVisible(desc, newState);
}
}
} else if (property.equals(DisassemblyPreferenceConstants.ERROR_COLOR)) {
fErrorColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.ERROR_COLOR));
refreshView(10);
} else if (property.equals(DisassemblyPreferenceConstants.INSTRUCTION_COLOR)) {
fInstructionColor = EditorsUI.getSharedTextColors().getColor(PreferenceConverter
.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.INSTRUCTION_COLOR));
refreshView(10);
} else if (property.equals(DisassemblyPreferenceConstants.SOURCE_COLOR)) {
fSourceColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.SOURCE_COLOR));
refreshView(10);
} else if (property.equals(DisassemblyPreferenceConstants.LABEL_COLOR)) {
fLabelColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.LABEL_COLOR));
refreshView(10);
}
}
/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
@Override
public void createPartControl(Composite parent) {
fComposite = parent;
FillLayout layout = new FillLayout();
layout.marginHeight = 2;
parent.setLayout(layout);
fVerticalRuler = createVerticalRuler();
int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
fViewer = new DisassemblyViewer(parent, fVerticalRuler, getOverviewRuler(), true, styles, getPreferenceStore());
SourceViewerConfiguration sourceViewerConfig = new DisassemblyViewerConfiguration(this);
fViewer.addTextPresentationListener(this);
fViewer.configure(sourceViewerConfig);
fDecorationSupport = new SourceViewerDecorationSupport(fViewer, getOverviewRuler(), getAnnotationAccess(),
getSharedColors());
configureSourceViewerDecorationSupport(fDecorationSupport);
fDecorationSupport.install(getPreferenceStore());
if (fPCAnnotationColorKey != null) {
fPCAnnotationRGB = PreferenceConverter.getColor(getPreferenceStore(), fPCAnnotationColorKey);
} else {
fPCAnnotationRGB = parent.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB();
}
initializeViewerFont(fViewer);
fDocument = createDocument();
fViewer.setDocument(fDocument, new AnnotationModel());
JFaceResources.getFontRegistry().addListener(fPropertyChangeListener);
createActions();
hookRulerContextMenu();
hookContextMenu();
contributeToActionBars();
fViewer.getTextWidget().addVerifyKeyListener(event -> {
switch (event.keyCode) {
case SWT.PAGE_UP:
case SWT.PAGE_DOWN:
case SWT.ARROW_UP:
case SWT.ARROW_DOWN:
event.doit = !keyScroll(event.keyCode);
}
});
fViewer.addSelectionChangedListener(event -> updateSelectionDependentActions());
fErrorColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.ERROR_COLOR));
fInstructionColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.INSTRUCTION_COLOR));
fSourceColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.SOURCE_COLOR));
fLabelColor = EditorsUI.getSharedTextColors().getColor(
PreferenceConverter.getColor(getPreferenceStore(), DisassemblyPreferenceConstants.LABEL_COLOR));
IVerticalRuler ruler = getVerticalRuler();
if (ruler instanceof CompositeRuler) {
updateContributedRulerColumns((CompositeRuler) ruler);
}
initDragAndDrop();
PlatformUI.getWorkbench().getHelpSystem().setHelp(fViewer.getControl(),
IDisassemblyHelpContextIds.DISASSEMBLY_VIEW);
updateTitle();
updateStateDependentActions();
if (fDebugSessionId != null) {
debugContextChanged();
} else {
updateDebugContext();
}
}
/*
* @see org.eclipse.ui.part.WorkbenchPart#setSite(org.eclipse.ui.IWorkbenchPartSite)
*/
@Override
protected void setSite(IWorkbenchPartSite site) {
super.setSite(site);
site.getPage().addPartListener(fPartListener);
fDebugContextListener = event -> {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) != 0) {
updateDebugContext();
}
};
DebugUITools.addPartDebugContextListener(site, fDebugContextListener);
}
private DisassemblyDocument createDocument() {
DisassemblyDocument doc = new DisassemblyDocument();
return doc;
}
/*
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
@Override
public void dispose() {
fUpdateCount++;
IWorkbenchPartSite site = getSite();
site.setSelectionProvider(null);
site.getPage().removePartListener(fPartListener);
if (fDebugContextListener != null) {
DebugUITools.removePartDebugContextListener(site, fDebugContextListener);
fDebugContextListener = null;
}
if (fHandlerActivations != null) {
IHandlerService handlerService = site.getService(IHandlerService.class);
handlerService.deactivateHandlers(fHandlerActivations);
fHandlerActivations = null;
}
deactivateDisassemblyContext();
fViewer = null;
if (fBackend != null) {
fBackend.clearDebugContext();
fBackend.dispose();
fBackend = null;
}
fAnnotationAccess = null;
fAnnotationPreferences = null;
fAnnotationRulerColumn = null;
fComposite = null;
if (fDecorationSupport != null) {
fDecorationSupport.uninstall();
fDecorationSupport = null;
}
if (fFont != null) {
fFont.dispose();
fFont = null;
}
if (fDropTarget != null) {
fDropTarget.dispose();
fDropTarget = null;
fDragSource.dispose();
fDragSource = null;
}
if (fPropertyChangeListener != null) {
if (fPreferenceStore != null) {
fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener);
fPreferenceStore = null;
}
fPropertyChangeListener = null;
}
if (fColumnSupport != null) {
fColumnSupport.dispose();
fColumnSupport = null;
}
fDocument.dispose();
fDocument = null;
super.dispose();
}
private void initDragAndDrop() {
if (fDropTarget == null) {
Transfer[] dropTypes = new Transfer[] { FileTransfer.getInstance(), TextTransfer.getInstance() };
Transfer[] dragTypes = new Transfer[] { TextTransfer.getInstance() };
Control dropControl = getSourceViewer().getTextWidget();
Control dragControl = dropControl;
int dropOps = DND.DROP_COPY | DND.DROP_DEFAULT;
int dragOps = DND.DROP_COPY | DND.DROP_DEFAULT;
fDropTarget = new DropTarget(dropControl, dropOps);
fDropTarget.setTransfer(dropTypes);
fDropTargetAdapter = new DisassemblyDropAdapter(this);
fDropTarget.addDropListener(fDropTargetAdapter);
fDragSource = new DragSource(dragControl, dragOps);
fDragSource.setTransfer(dragTypes);
fDragSourceAdapter = new TextViewerDragAdapter(getSourceViewer());
fDragSource.addDragListener(fDragSourceAdapter);
}
}
private ISourceViewer getSourceViewer() {
return fViewer;
}
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
Iterator<?> e = fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
AnnotationPreference pref = (AnnotationPreference) e.next();
support.setAnnotationPreference(pref);
if (pref.getAnnotationType().equals(fPCAnnotation.getType())) {
fPCAnnotationColorKey = pref.getColorPreferenceKey();
}
}
support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
support.setSymbolicFontName(getFontPropertyPreferenceKey());
}
/**
* Returns the symbolic font name for this view as defined in XML.
*
* @return a String with the symbolic font name or <code>null</code> if
* none is defined
*/
private String getSymbolicFontName() {
if (getConfigurationElement() != null)
return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$
else
return null;
}
protected final String getFontPropertyPreferenceKey() {
String symbolicFontName = getSymbolicFontName();
if (symbolicFontName != null)
return symbolicFontName;
else
return JFaceResources.TEXT_FONT;
}
/**
* Initializes the given viewer's font.
*
* @param viewer
* the viewer
*/
private void initializeViewerFont(ISourceViewer viewer) {
boolean isSharedFont = true;
Font font = null;
String symbolicFontName = getSymbolicFontName();
if (symbolicFontName != null)
font = JFaceResources.getFont(symbolicFontName);
else if (fPreferenceStore != null) {
// Backward compatibility
if (fPreferenceStore.contains(JFaceResources.TEXT_FONT)
&& !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) {
FontData data = PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT);
if (data != null) {
isSharedFont = false;
font = new Font(viewer.getTextWidget().getDisplay(), data);
}
}
}
if (font == null)
font = JFaceResources.getTextFont();
setFont(viewer, font);
if (fFont != null) {
fFont.dispose();
fFont = null;
}
if (!isSharedFont)
fFont = font;
}
/**
* Sets the font for the given viewer sustaining selection and scroll
* position.
*
* @param sourceViewer
* the source viewer
* @param font
* the font
*/
private void setFont(ISourceViewer sourceViewer, Font font) {
if (sourceViewer.getDocument() != null) {
Point selection = sourceViewer.getSelectedRange();
int topIndex = sourceViewer.getTopIndex();
StyledText styledText = sourceViewer.getTextWidget();
Control parent = styledText;
if (sourceViewer instanceof ITextViewerExtension) {
ITextViewerExtension extension = (ITextViewerExtension) sourceViewer;
parent = extension.getControl();
}
parent.setRedraw(false);
styledText.setFont(font);
if (fVerticalRuler instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler;
e.setFont(font);
}
sourceViewer.setSelectedRange(selection.x, selection.y);
sourceViewer.setTopIndex(topIndex);
if (parent instanceof Composite) {
Composite composite = (Composite) parent;
composite.layout(true);
}
parent.setRedraw(true);
} else {
StyledText styledText = sourceViewer.getTextWidget();
styledText.setFont(font);
if (fVerticalRuler instanceof IVerticalRulerExtension) {
IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler;
e.setFont(font);
}
}
}
protected IVerticalRuler createVerticalRuler() {
CompositeRuler ruler = createCompositeRuler();
IPreferenceStore store = getPreferenceStore();
if (ruler != null && store != null) {
for (Iterator<?> iter = ruler.getDecoratorIterator(); iter.hasNext();) {
IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next();
if (column instanceof AnnotationRulerColumn) {
fAnnotationRulerColumn = (AnnotationRulerColumn) column;
for (Iterator<?> iter2 = fAnnotationPreferences.getAnnotationPreferences().iterator(); iter2
.hasNext();) {
AnnotationPreference preference = (AnnotationPreference) iter2.next();
String key = preference.getVerticalRulerPreferenceKey();
boolean showAnnotation = true;
if (key != null && store.contains(key))
showAnnotation = store.getBoolean(key);
if (showAnnotation)
fAnnotationRulerColumn.addAnnotationType(preference.getAnnotationType());
}
fAnnotationRulerColumn.addAnnotationType(Annotation.TYPE_UNKNOWN);
break;
}
}
}
return ruler;
}
/**
* Returns the vertical ruler.
*
* @return the vertical ruler
*/
protected IVerticalRuler getVerticalRuler() {
return fVerticalRuler;
}
/**
* Returns the overview ruler.
*
* @return the overview ruler
*/
protected IOverviewRuler getOverviewRuler() {
if (fOverviewRuler == null)
fOverviewRuler = createOverviewRuler(getSharedColors());
return fOverviewRuler;
}
protected ISharedTextColors getSharedColors() {
return EditorsUI.getSharedTextColors();
}
protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) {
IOverviewRuler ruler = new OverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH, sharedColors);
Iterator<?> e = fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
AnnotationPreference preference = (AnnotationPreference) e.next();
if (preference.contributesToHeader())
ruler.addHeaderAnnotationType(preference.getAnnotationType());
}
return ruler;
}
/**
* Initializes the given address ruler column from the preference store.
*
* @param rulerColumn the ruler column to be initialized
*/
protected void initializeRulerColumn(DisassemblyRulerColumn rulerColumn, String colorPrefKey) {
ISharedTextColors sharedColors = getSharedColors();
IPreferenceStore store = getPreferenceStore();
if (store != null) {
RGB rgb = null;
// foreground color
if (store.contains(colorPrefKey)) {
if (store.isDefault(colorPrefKey))
rgb = PreferenceConverter.getDefaultColor(store, colorPrefKey);
else
rgb = PreferenceConverter.getColor(store, colorPrefKey);
}
if (rgb == null)
rgb = new RGB(0, 0, 0);
rulerColumn.setForeground(sharedColors.getColor(rgb));
rgb = null;
rulerColumn.redraw();
}
}
/**
* @return the preference store
*/
private IPreferenceStore getPreferenceStore() {
return fPreferenceStore;
}
/**
* Creates a composite ruler to be used as the vertical ruler by this
* editor. Subclasses may re-implement this method.
*
* @return the vertical ruler
*/
protected CompositeRuler createCompositeRuler() {
CompositeRuler ruler = new CompositeRuler();
ruler.addDecorator(0, new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess()));
return ruler;
}
/**
* Returns the annotation access.
*
* @return the annotation access
*/
protected IAnnotationAccess getAnnotationAccess() {
if (fAnnotationAccess == null)
fAnnotationAccess = createAnnotationAccess();
return fAnnotationAccess;
}
/**
* Creates the annotation access for this editor.
*
* @return the created annotation access
*/
protected IAnnotationAccess createAnnotationAccess() {
return new DefaultMarkerAnnotationAccess();
}
private void hookContextMenu() {
String id = "#DisassemblyPartContext"; //$NON-NLS-1$
MenuManager menuMgr = new MenuManager(id, id);
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(manager -> DisassemblyPart.this.fillContextMenu(manager));
Menu menu = menuMgr.createContextMenu(fViewer.getTextWidget());
fViewer.getTextWidget().setMenu(menu);
getSite().registerContextMenu(id, menuMgr, fViewer);
}
private void hookRulerContextMenu() {
String id = "#DisassemblyPartRulerContext"; //$NON-NLS-1$
MenuManager menuMgr = new MenuManager(id, id);
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(manager -> DisassemblyPart.this.fillRulerContextMenu(manager));
Menu menu = menuMgr.createContextMenu(fVerticalRuler.getControl());
fVerticalRuler.getControl().setMenu(menu);
getSite().registerContextMenu(id, menuMgr, fViewer);
}
private void contributeToActionBars() {
IWorkbenchPartSite site = getSite();
site.setSelectionProvider(new DisassemblySelectionProvider(this));
activateDisassemblyContext();
contributeToActionBars(getActionBars());
}
protected abstract IActionBars getActionBars();
protected void contributeToActionBars(IActionBars bars) {
for (Iterator<String> iter = fGlobalActions.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
IAction action = fGlobalActions.get(key);
bars.setGlobalActionHandler(key, action);
}
IMenuManager menu = bars.getMenuManager();
IMenuManager navigateMenu = menu.findMenuUsingPath(IWorkbenchActionConstants.M_NAVIGATE);
if (navigateMenu != null) {
navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoPC);
navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoAddress);
}
bars.updateActionBars();
}
protected void fillContextMenu(IMenuManager manager) {
Point cursorLoc = getSite().getShell().getDisplay().getCursorLocation();
fViewer.getTextWidget().toControl(cursorLoc);
fActionToggleSource.update();
fActionToggleSymbols.update();
manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$
manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$
manager.add(new Separator("group.debug")); //$NON-NLS-1$
manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT,
fGlobalActions.get(ITextEditorActionConstants.COPY));
manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT,
fGlobalActions.get(ITextEditorActionConstants.SELECT_ALL));
manager.add(new Separator(ITextEditorActionConstants.GROUP_SETTINGS));
manager.add(fActionToggleSource);
manager.add(fActionToggleSymbols);
manager.add(fActionOpenPreferences);
manager.add(new Separator("group.bottom")); //$NON-NLS-1$
// Other plug-ins can contribute their actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
protected void fillRulerContextMenu(IMenuManager manager) {
fActionToggleBreakpointEnablement.update();
manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$
manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$
manager.add(fActionToggleBreakpointEnablement);
manager.add(new GroupMarker("debug")); //$NON-NLS-1$
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
manager.add(new GroupMarker(ITextEditorActionConstants.GROUP_RESTORE));
manager.add(new Separator("add")); //$NON-NLS-1$
manager.add(new Separator(ITextEditorActionConstants.GROUP_RULERS));
addRulerContributionActions(manager);
manager.add(new Separator(ITextEditorActionConstants.GROUP_REST));
for (IMenuListener listener : fRulerContextMenuListeners)
listener.menuAboutToShow(manager);
manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT));
manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT,
fGlobalActions.get(ITextEditorActionConstants.COPY));
}
protected void fillLocalToolBar(IToolBarManager manager) {
final int ADDRESS_BAR_WIDTH = 190;
ToolBar toolbar = ((ToolBarManager) manager).getControl();
fAddressBar = new AddressBarContributionItem(fJumpToAddressAction);
fAddressBar.createAddressBox(toolbar, ADDRESS_BAR_WIDTH,
DisassemblyMessages.Disassembly_GotoLocation_initial_text,
DisassemblyMessages.Disassembly_GotoLocation_warning);
manager.add(fAddressBar);
fJumpToAddressAction.setEnabled(fDebugSessionId != null);
manager.add(new Separator());
manager.add(fActionRefreshView);
manager.add(fActionGotoPC);
manager.add(fSyncAction);
manager.add(fActionToggleSource);
// Other plug-ins can contribute their actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
protected void updateSelectionDependentActions() {
Iterator<Action> iterator = fSelectionActions.iterator();
while (iterator.hasNext()) {
IUpdate action = (IUpdate) iterator.next();
action.update();
}
}
protected void updateStateDependentActions() {
Iterator<AbstractDisassemblyAction> iterator = fStateDependentActions.iterator();
while (iterator.hasNext()) {
IUpdate action = iterator.next();
action.update();
}
}
protected void createActions() {
Action action;
action = new TextOperationAction(fViewer, ITextOperationTarget.COPY);
action.setText(DisassemblyMessages.Disassembly_action_Copy_label);
action.setImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_enabled));
action.setDisabledImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_disabled));
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
fGlobalActions.put(ITextEditorActionConstants.COPY, action);
fSelectionActions.add(action);
action = new TextOperationAction(fViewer, ITextOperationTarget.SELECT_ALL);
action.setText(DisassemblyMessages.Disassembly_action_SelectAll_label);
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL);
fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action);
action = new TextOperationAction(fViewer, ITextOperationTarget.PRINT);
action.setActionDefinitionId(IWorkbenchCommandConstants.FILE_PRINT);
fGlobalActions.put(ITextEditorActionConstants.PRINT, action);
action = new FindReplaceAction(DisassemblyMessages.getBundleForConstructedKeys(), "FindReplaceAction.", this); //$NON-NLS-1$
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
fGlobalActions.put(ActionFactory.FIND.getId(), action);
fSelectionActions.add(action);
fActionGotoPC = new ActionGotoProgramCounter(this);
fActionGotoPC.setActionDefinitionId(COMMAND_ID_GOTO_PC);
fActionGotoPC.setImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Home_enabled));
fActionGotoPC.setDisabledImageDescriptor(
DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Home_disabled));
fStateDependentActions.add(fActionGotoPC);
registerWithHandlerService(fActionGotoPC);
fActionGotoAddress = new ActionGotoAddress(this);
fActionGotoAddress.setActionDefinitionId(COMMAND_ID_GOTO_ADDRESS);
fStateDependentActions.add(fActionGotoAddress);
registerWithHandlerService(fActionGotoAddress);
fActionToggleSource = new ActionToggleSource();
fStateDependentActions.add(fActionToggleSource);
fActionToggleSource.setImageDescriptor(
ResourceLocator.imageDescriptorFromBundle(DsfUIPlugin.PLUGIN_ID, "icons/source.gif").get()); //$NON-NLS-1$
fVerticalRuler.getControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(final MouseEvent e) {
// invoke toggle breakpoint
IHandlerService handlerService = getSite().getService(IHandlerService.class);
if (handlerService != null) {
try {
Event event = new Event();
event.display = e.display;
event.widget = e.widget;
event.time = e.time;
event.data = e.data;
event.x = e.x;
event.y = e.y;
event.button = e.button;
event.stateMask = e.stateMask;
event.count = e.count;
handlerService.executeCommand(COMMAND_ID_TOGGLE_BREAKPOINT, event);
} catch (org.eclipse.core.commands.ExecutionException exc) {
DsfUIPlugin.log(exc);
} catch (NotDefinedException exc) {
} catch (NotEnabledException exc) {
} catch (NotHandledException exc) {
}
}
}
});
fActionToggleBreakpointEnablement = new ActionToggleBreakpointEnablement();
fActionToggleSymbols = new ActionToggleSymbols();
fActionRefreshView = new ActionRefreshView();
fSyncAction = new SyncActiveDebugContextAction();
fTrackExpressionAction = new TrackExpressionAction();
fStateDependentActions.add(fActionRefreshView);
fGlobalActions.put(ActionFactory.REFRESH.getId(), fActionRefreshView);
fActionOpenPreferences = new ActionOpenPreferences(getSite().getShell());
}
/**
* Register given action with the handler service for key bindings.
*
* @param action
*/
private void registerWithHandlerService(IAction action) {
if (fHandlerActivations == null) {
fHandlerActivations = new ArrayList<>(5);
}
IHandlerService handlerService = getSite().getService(IHandlerService.class);
fHandlerActivations
.add(handlerService.activateHandler(action.getActionDefinitionId(), new ActionHandler(action)));
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoProgramCounter()
*/
@Override
public final void gotoProgramCounter() {
if (fPCAddress != PC_RUNNING) {
fPCLastAddress = PC_UNKNOWN;
gotoFrame(getActiveStackFrame());
}
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoAddress(java.math.BigInteger)
*/
@Override
public final void gotoAddress(IAddress address) {
assert isGuiThread();
if (address != null) {
final BigInteger addr = address.getValue();
startUpdate(() -> {
fGotoFramePending = false;
fGotoAddressPending = PC_UNKNOWN;
gotoAddress(addr);
});
}
}
public final void gotoLocationByUser(BigInteger address, String locationTxt) {
fPCLastAddress = address;
fPCLastLocationTxt = locationTxt;
gotoAddress(address);
}
public final void gotoActiveFrameByUser() {
gotoFrame(getActiveStackFrame());
}
/*
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#gotoAddress(java.math.BigInteger)
*/
@Override
public final void gotoAddress(BigInteger address) {
if (fDebugSessionId == null) {
return;
}
if (DEBUG)
System.out.println("gotoAddress " + getAddressText(address)); //$NON-NLS-1$
fFocusAddress = address;
BigInteger previousAddress = fGotoAddressPending;
if (fGotoAddressPending == PC_UNKNOWN) {
fGotoAddressPending = address;
}
if (fUpdatePending) {
return;
}
AddressRangePosition pos = getPositionOfAddress(address);
if (pos != null) {
if (pos.fValid) {
if ((pos instanceof ErrorPosition || !pos.fAddressOffset.equals(address))
&& !previousAddress.equals(address)) {
// address is within a disassembled instruction or error - need to invalidate
pos.fValid = false;
fDocument.addInvalidAddressRange(pos);
} else {
if (fGotoAddressPending.equals(address)) {
fGotoAddressPending = PC_UNKNOWN;
}
gotoPosition(pos, !address.equals(fFrameAddress));
return;
}
}
int lines = fBufferZone + 3;
BigInteger endAddress = pos.fAddressOffset.add(pos.fAddressLength)
.min(address.add(BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions())));
retrieveDisassembly(address, endAddress, lines);
}
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoSymbol(java.lang.String)
*/
@Override
public final void gotoSymbol(final String symbol) {
if (!fActive || fBackend == null) {
return;
}
fBackend.gotoSymbol(symbol);
}
private void gotoPosition(Position pos, boolean onTop) {
if (fViewer == null) {
return;
}
setFocusPosition(pos);
fViewer.setSelectedRange(pos.offset, 0);
int revealOffset = pos.offset;
if (pos.offset > 0) {
try {
AddressRangePosition previousPos = fDocument.getModelPosition(pos.offset - 1);
if (previousPos instanceof LabelPosition) {
revealOffset = previousPos.offset;
onTop = true;
} else if (previousPos == null || !previousPos.fValid) {
onTop = true;
}
} catch (BadLocationException e) {
// cannot happen
}
}
fViewer.revealOffset(revealOffset, onTop);
}
private void gotoMarker(final IMarker marker) {
if (marker == null) {
return;
}
if (fDebugSessionId == null || fUpdatePending) {
fGotoMarkerPending = marker;
return;
}
fGotoMarkerPending = null;
//TLETODO [disassembly] goto (breakpoint) marker
}
/*
* @see org.eclipse.jface.text.IViewportListener#viewportChanged(int)
*/
@Override
public void viewportChanged(int verticalOffset) {
if (fDebugSessionId != null && fGotoAddressPending == PC_UNKNOWN && fScrollPos == null && !fUpdatePending
&& !fRefreshViewPending && fFocusAddress != PC_UNKNOWN) {
fUpdatePending = true;
final int updateCount = fUpdateCount;
invokeLater(() -> {
if (updateCount == fUpdateCount) {
assert fUpdatePending;
if (fUpdatePending) {
fUpdatePending = false;
updateVisibleArea();
}
}
});
}
}
/**
* Update lines of currently visible area + one page buffer zone below.
*
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#updateVisibleArea()
*/
@Override
public void updateVisibleArea() {
assert isGuiThread();
if (!fActive || fUpdatePending || fViewer == null || fDebugSessionId == null) {
return;
}
if (fBackend == null || !fBackend.hasDebugContext() || !fBackend.canDisassemble()) {
return;
}
StyledText styledText = fViewer.getTextWidget();
Rectangle clientArea = styledText.getClientArea();
fBufferZone = Math.max(8, clientArea.height / styledText.getLineHeight());
int topIndex = fViewer.getTopIndex();
int bottomIndex = fViewer.getBottomIndex();
int focusIndex = -1;
boolean focusVisible = false;
boolean isScrollingUp = fViewer.isUserTriggeredScrolling()
&& fViewer.getLastTopPixel() >= styledText.getTopPixel();
if (fFocusPos != null) {
try {
int focusOffset = fFocusPos.offset;
focusIndex = fDocument.getLineOfOffset(focusOffset);
focusVisible = focusIndex >= topIndex && focusIndex <= bottomIndex;
// workaround for: Clicking the IP annotation in the right ruler has no effect.
// we deselect the IP location if it is scrolled outside the visible area
if (!focusVisible) {
Point selection = fViewer.getSelectedRange();
if (selection.x == focusOffset && selection.y > 0) {
fViewer.setSelectedRange(selection.x, 0);
}
}
} catch (BadLocationException e) {
setFocusPosition(null);
}
}
if (!focusVisible) {
focusIndex = topIndex + fScrollLine;
}
BigInteger focusAddress = getAddressOfLine(focusIndex);
bottomIndex += 2;
AddressRangePosition bestPosition = null;
int bestLine = -1;
BigInteger bestDistance = null;
if (DEBUG)
System.out.println("DisassemblyPart.updateVisibleArea() called. There are " //$NON-NLS-1$
+ fDocument.getInvalidAddressRanges().length + " invalid ranges to consider updating"); //$NON-NLS-1$
for (AddressRangePosition p : fDocument.getInvalidAddressRanges()) {
try {
int line = fDocument.getLineOfOffset(p.offset);
if (line >= topIndex && line <= bottomIndex) {
if (p instanceof DisassemblyPosition
|| p.fAddressLength.compareTo(BigInteger.valueOf(fBufferZone * 2)) <= 0) {
// small areas and known areas are OK to update
} else if (!isScrollingUp && !fUpdateBeforeFocus && p.fAddressOffset.compareTo(focusAddress) < 0) {
continue;
}
BigInteger distance = p.fAddressOffset.subtract(focusAddress).abs();
if (bestDistance == null || distance.compareTo(bestDistance) < 0) {
bestPosition = p;
bestLine = line;
bestDistance = distance;
if (bestDistance.compareTo(BigInteger.valueOf(fBufferZone * 2)) <= 0) {
break;
}
}
}
} catch (BadLocationException e) {
continue;
}
}
if (bestPosition != null) {
if (DEBUG)
System.out.println("...and the best candidate is: " + bestPosition); //$NON-NLS-1$
int lines = fBufferZone + 3;
BigInteger startAddress = bestPosition.fAddressOffset;
BigInteger endAddress = bestPosition.fAddressOffset.add(bestPosition.fAddressLength);
BigInteger addressRange = BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions());
if (bestLine > focusIndex || bestLine == focusIndex && startAddress.compareTo(focusAddress) >= 0) {
// insert at start of range
if (endAddress.subtract(startAddress).compareTo(addressRange) < 0) {
// try to increase range to reduce number of requests
Iterator<?> iter = fDocument.getModelPositionIterator(endAddress);
while (iter.hasNext()) {
AddressRangePosition p = (AddressRangePosition) iter.next();
if (p.fValid) {
endAddress = endAddress.add(p.fAddressLength);
if (endAddress.subtract(startAddress).compareTo(addressRange) >= 0) {
break;
}
} else {
break;
}
}
}
} else {
// insert at end of range
startAddress = startAddress.max(endAddress.subtract(addressRange));
// make sure we get all disassembly lines until endAddress
lines = endAddress.subtract(startAddress).intValue();
}
retrieveDisassembly(startAddress, endAddress, lines);
} else {
if (DEBUG) {
System.out.println("...but alas we didn't deem any of them worth updating. They are:"); //$NON-NLS-1$
int i = 0;
for (AddressRangePosition p : fDocument.getInvalidAddressRanges()) {
System.out.println("[" + i++ + "] " + p); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
scheduleDoPending();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#asyncExec(java.lang.Runnable)
*/
@Override
public void asyncExec(Runnable runnable) {
if (fViewer != null) {
fViewer.getControl().getDisplay().asyncExec(runnable);
}
}
private void invokeLater(Runnable runnable) {
invokeLater(10, runnable);
}
private void invokeLater(int delay, Runnable runnable) {
if (fViewer != null) {
fViewer.getControl().getDisplay().timerExec(delay, runnable);
}
}
/**
* Insert source lines if available.
*
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#updateInvalidSource()
*/
@Override
public void updateInvalidSource() {
assert isGuiThread();
if (fViewer == null) {
return;
}
boolean unlock = false;
try {
if (fScrollPos == null) {
if (fUpdatePending) {
fUpdateSourcePending = true;
return;
}
fUpdateSourcePending = false;
unlock = true;
fUpdatePending = true;
lockScroller();
}
SourcePosition[] invalidSources = fDocument.getInvalidSourcePositions();
for (SourcePosition p : invalidSources) {
if (!p.fValid) {
insertSource(p);
} else if (DEBUG && fDocument.removeInvalidSourcePosition(p)) {
System.err.println(
"!!! valid source position in invalid source list at " + getAddressText(p.fAddressOffset)); //$NON-NLS-1$
}
}
} finally {
if (unlock) {
fUpdatePending = false;
unlockScroller();
doPending();
}
}
}
/**
* Show disassembly for given (source) file. Retrieves disassembly starting
* at the beginning of the file, for as many lines as are specified. If
* [lines] == -1, the entire file is disassembled.
*
* @param file
* @param lines
*/
void retrieveDisassembly(final String file, final int lines, final boolean mixed) {
if (fDebugSessionId == null) {
return;
}
startUpdate(() -> {
if (DEBUG)
System.out.println("retrieveDisassembly " + file); //$NON-NLS-1$
fBackend.retrieveDisassembly(file, lines, fEndAddress, mixed, fShowSymbols, fShowDisassembly);
});
}
private void retrieveDisassembly(BigInteger startAddress, BigInteger endAddress, int lines) {
if (fDebugSessionId == null) {
return;
}
if (DEBUG)
System.out.println("retrieveDisassembly " + getAddressText(startAddress) + " " + lines + " lines"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
retrieveDisassembly(startAddress, endAddress, lines, fShowSource, true);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#retrieveDisassembly(java.math.BigInteger, java.math.BigInteger, int, boolean, boolean)
*/
@Override
public void retrieveDisassembly(final BigInteger startAddress, BigInteger endAddress, final int linesHint,
boolean mixed, boolean ignoreFile) {
assert isGuiThread();
assert !fUpdatePending;
fUpdatePending = true;
final int lines = linesHint + 2;
final BigInteger addressLength = BigInteger.valueOf(lines * 4);
if (endAddress.subtract(startAddress).compareTo(addressLength) > 0) {
endAddress = startAddress.add(addressLength);
}
boolean insideActiveFrame = startAddress.equals(fFrameAddress);
String file = null;
int lineNumber = -1;
if (!ignoreFile && insideActiveFrame && fBackend != null) {
file = fBackend.getFrameFile();
if (file != null && file.trim().length() == 0) {
file = null;
}
if (file != null) {
lineNumber = fBackend.getFrameLine();
}
}
if (DEBUG)
System.out.println("Asking backend to retrieve disassembly: sa=0x" + startAddress.toString(16) + ",ea=0x" //$NON-NLS-1$//$NON-NLS-2$
+ endAddress.toString(16) + ",file=" + file + ",lineNumber=" + lineNumber + ",lines=" + lines); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
fBackend.retrieveDisassembly(startAddress, endAddress, file, lineNumber, lines, mixed, fShowSymbols,
fShowDisassembly, linesHint);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#insertError(java.math.BigInteger, java.lang.String)
*/
@Override
public void insertError(BigInteger address, String message) {
assert isGuiThread();
AddressRangePosition p = null;
p = getPositionOfAddress(address);
if (p.fValid) {
return;
}
try {
fDocument.insertErrorLine(p, address, BigInteger.ONE, message);
} catch (BadLocationException exc) {
internalError(exc);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getAddressSize()
*/
@Override
public int getAddressSize() {
assert isGuiThread();
return fAddressSize;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#addressSizeChanged(int)
*/
@Override
public void addressSizeChanged(int addressSize) {
assert isGuiThread();
BigInteger oldEndAddress = fEndAddress;
fEndAddress = BigInteger.ONE.shiftLeft(addressSize);
int oldAddressSize = fAddressSize;
fAddressSize = addressSize;
if (addressSize < oldAddressSize) {
fDocument.deleteDisassemblyRange(fEndAddress, oldEndAddress, true, true);
List<AddressRangePosition> toRemove = new ArrayList<>();
for (AddressRangePosition position : fDocument.getInvalidAddressRanges()) {
if (position.fAddressOffset.compareTo(fEndAddress) >= 0) {
try {
fDocument.replace(position, position.length, ""); //$NON-NLS-1$
fDocument.removeModelPosition(position);
toRemove.add(position);
} catch (BadLocationException exc) {
internalError(exc);
}
} else if (position.containsAddress(fEndAddress)) {
position.fAddressLength = fEndAddress.subtract(position.fAddressOffset);
}
}
fDocument.removeInvalidAddressRanges(toRemove);
} else if (addressSize > oldAddressSize) {
fDocument.insertInvalidAddressRange(fDocument.getLength(), 0, oldEndAddress, fEndAddress);
} else {
return;
}
AddressRulerColumn fAddressRulerColumn = (AddressRulerColumn) getRulerColumn(AddressRulerColumn.ID);
if (fAddressRulerColumn != null) {
fAddressRulerColumn.setAddressSize(addressSize);
if (fComposite != null) {
fComposite.layout(true);
}
}
}
private IContributedRulerColumn getRulerColumn(String id) {
CompositeRuler compositeRuler = (CompositeRuler) getVerticalRuler();
for (Iterator<?> iter = compositeRuler.getDecoratorIterator(); iter.hasNext();) {
IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next();
if (column instanceof IContributedRulerColumn) {
IContributedRulerColumn contributedColumn = (IContributedRulerColumn) column;
if (id.equals(contributedColumn.getDescriptor().getId())) {
return contributedColumn;
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getPositionOfAddress(java.math.BigInteger)
*/
@Override
public AddressRangePosition getPositionOfAddress(BigInteger address) {
assert isGuiThread();
if (address == null || address.compareTo(BigInteger.ZERO) < 0) {
return null;
}
AddressRangePosition pos = fDocument.getPositionOfAddress(address);
assert !(pos instanceof SourcePosition);
return pos;
}
private BigInteger getAddressOfLine(int line) {
return fDocument.getAddressOfLine(line);
}
/**
* Passing the focus request to the viewer's control.
*/
@Override
public void setFocus() {
fViewer.getControl().setFocus();
}
protected void setActive(boolean active) {
if (DEBUG)
System.out.println("setActive(" + active + ")"); //$NON-NLS-1$ //$NON-NLS-2$
fActive = active;
if (fActive) {
if (fRefreshAll) {
fRefreshAll = false;
refreshView(0);
} else {
doPendingPCUpdates();
if (fBackend != null && fBackend.hasDebugContext()) {
int frame = getActiveStackFrame();
if (frame < 0 && isSuspended()) {
frame = 0;
}
if (frame != fTargetFrame) {
gotoFrame(frame);
}
}
}
} else {
fGotoAddressPending = fFocusAddress = PC_UNKNOWN;
}
firePropertyChange(PROP_ACTIVE);
}
private int getActiveStackFrame() {
if (fBackend != null) {
return fBackend.getFrameLevel();
}
return -1;
}
protected void updateDebugContext() {
fDebugContext = DebugUITools.getPartDebugContext(getSite());
IDisassemblyBackend prevBackend = fBackend;
IDisassemblyBackend newBackend = null;
fDebugSessionId = null;
boolean needUpdate = false;
if (fDebugContext != null) {
IDisassemblyBackend contextBackend = fDebugContext.getAdapter(IDisassemblyBackend.class);
// Need to compare the backend classes to prevent reusing the same backend object.
// sub class can overwrite the standard disassembly backend to provide its own customization.
if ((prevBackend != null) && (contextBackend != null)
&& prevBackend.getClass().equals(contextBackend.getClass())
&& prevBackend.supportsDebugContext(fDebugContext)) {
newBackend = prevBackend;
} else {
needUpdate = true;
newBackend = fDebugContext.getAdapter(IDisassemblyBackend.class);
if (newBackend != null) {
if (newBackend.supportsDebugContext(fDebugContext)) {
newBackend.init(this);
} else {
newBackend = null;
}
}
}
}
fBackend = newBackend;
if (newBackend != null) {
IDisassemblyBackend.SetDebugContextResult result = newBackend.setDebugContext(fDebugContext);
if (result != null) {
fDebugSessionId = result.sessionId;
if (result.contextChanged) {
needUpdate = true;
}
}
}
if (prevBackend != null && newBackend != prevBackend) {
needUpdate = true;
prevBackend.clearDebugContext();
prevBackend.dispose();
}
if (needUpdate && fViewer != null) {
startUpdate(() -> debugContextChanged());
}
}
private void startUpdate(final Runnable update) {
if (fViewer == null)
return;
final int updateCount = fUpdateCount;
final SafeRunnable safeUpdate = new SafeRunnable() {
@Override
public void run() {
update.run();
}
@Override
public void handleException(Throwable e) {
internalError(e);
}
};
if (fUpdatePending) {
invokeLater(new Runnable() {
@Override
public void run() {
if (updateCount == fUpdateCount && fViewer != null) {
if (fUpdatePending)
invokeLater(this);
else
SafeRunner.run(safeUpdate);
}
}
});
} else {
SafeRunner.run(safeUpdate);
}
}
private void debugContextChanged() {
if (DEBUG)
System.out.println("DisassemblyPart.debugContextChanged()"); //$NON-NLS-1$
fUpdateCount++;
fRunnableQueue.clear();
fUpdatePending = false;
resetViewer();
if (fDebugSessionId != null) {
fJumpToAddressAction.setEnabled(true);
if (fAddressBar != null)
fAddressBar.enableAddressBox(true);
int activeFrame = getActiveStackFrame();
if (activeFrame > 0) {
gotoFrame(activeFrame);
} else {
updatePC(PC_UNKNOWN);
}
if (fGotoAddressPending != PC_UNKNOWN) {
gotoAddress(fGotoAddressPending);
}
if (fGotoMarkerPending != null) {
gotoMarker(fGotoMarkerPending);
}
fViewer.addViewportListener(this);
} else {
fJumpToAddressAction.setEnabled(false);
if (fAddressBar != null)
fAddressBar.enableAddressBox(false);
fViewer.removeViewportListener(this);
fGotoMarkerPending = null;
}
updateTitle();
updateStateDependentActions();
firePropertyChange(PROP_CONNECTED);
firePropertyChange(PROP_SUSPENDED);
}
private void attachExtendedPCAnnotationModel() {
IAnnotationModel annotationModel = fViewer.getAnnotationModel();
if (annotationModel instanceof IAnnotationModelExtension) {
IAnnotationModelExtension ame = (IAnnotationModelExtension) annotationModel;
fExtPCAnnotationModel = new DisassemblyAnnotationModel();
ame.addAnnotationModel(EXTENDED_PC_ANNOTATIONS, fExtPCAnnotationModel);
}
}
private void attachBreakpointsAnnotationModel() {
IAnnotationModel annotationModel = fViewer.getAnnotationModel();
if (annotationModel instanceof IAnnotationModelExtension) {
IAnnotationModelExtension ame = (IAnnotationModelExtension) annotationModel;
ame.addAnnotationModel(BREAKPOINT_ANNOTATIONS, new BreakpointsAnnotationModel(fDebugContext));
}
}
private void refreshView(int delay) {
if (fViewer == null || fRefreshViewPending || fRefreshAll) {
return;
}
fRunnableQueue.clear();
fRefreshViewPending = true;
final long refreshViewScheduled = System.currentTimeMillis() + delay;
final Runnable refresh = () -> {
fRefreshViewPending = false;
long now = System.currentTimeMillis();
if (now >= refreshViewScheduled) {
if (DEBUG)
System.err.println("*** refreshing view ***"); //$NON-NLS-1$
// save viewport position and frame info
BigInteger topAddress = getTopAddress();
int targetFrame = fTargetFrame;
BigInteger frameAddress = fFrameAddress;
BigInteger pcAddress = fPCAddress;
// clear viewer
resetViewer();
if (fScrollPos != null) {
fScrollPos.isDeleted = true;
}
// restore frame info and viewport
fPCAnnotationUpdatePending = true;
fTargetFrame = targetFrame;
fFrameAddress = frameAddress;
fPCAddress = pcAddress;
gotoAddress(topAddress);
} else {
refreshView((int) (refreshViewScheduled - now));
}
};
if (delay > 0) {
invokeLater(delay, () -> doScrollLocked(refresh));
} else {
doScrollLocked(refresh);
}
}
private BigInteger getTopAddress() {
if (fViewer != null) {
BigInteger topAddress = getAddressOfLine(fViewer.getTopIndex());
if (topAddress.equals(fStartAddress)) {
// in rare cases, the top line can be '...'
// don't use it as reference, take the next line
topAddress = getAddressOfLine(fViewer.getTopIndex() + 1);
}
return topAddress;
} else {
return PC_UNKNOWN;
}
}
private void resetViewer() {
// clear all state and cache
fExtPCAnnotationModel = null;
fPCAnnotationUpdatePending = false;
fGotoFramePending = false;
fPCAddress = fFrameAddress = PC_RUNNING;
fTargetFrame = -1;
fGotoAddressPending = PC_UNKNOWN;
fFocusAddress = PC_UNKNOWN;
setFocusPosition(null);
fPCHistory.clear();
fPendingPCUpdates.clear();
fFile2Storage.clear();
fDocument.clear();
fViewer.setDocument(fDocument, new AnnotationModel());
if (fDebugSessionId != null) {
attachBreakpointsAnnotationModel();
attachExtendedPCAnnotationModel();
fDocument.insertInvalidAddressRange(0, 0, fStartAddress, fEndAddress);
}
}
private AddressRangePosition getPCPosition(BigInteger address) {
if (address.compareTo(BigInteger.ZERO) < 0) {
// invalid address
return null;
}
AddressRangePosition pos = getPositionOfAddress(address);
if (pos == null || !pos.fValid) {
// invalid disassembly line
return null;
}
if (pos.length > 0) {
// valid disassembly line
return pos;
}
// hidden disassembly
if (!(pos instanceof DisassemblyPosition)) {
return pos;
}
String srcFile = ((DisassemblyPosition) pos).getFile();
if (srcFile == null) {
return pos;
}
SourceFileInfo fi = fDocument.getSourceInfo(srcFile);
if (fi == null) {
return pos;
}
if (fi.fSource == null) {
if (fi.fError != null) {
// could not read source
return pos;
}
return null;
}
int stmtLine = ((DisassemblyPosition) pos).getLine();
if (stmtLine < 0) {
return pos;
}
Position srcPos = fDocument.getSourcePosition(fi, stmtLine);
if (srcPos == null) {
return pos;
}
int offset = srcPos.offset;
int length = srcPos.length;
return new AddressRangePosition(offset, length, address, BigInteger.ZERO);
}
/**
* Update the annotation indicating the given address.
* @return a position which denotes the documents position
*/
private AddressRangePosition updateAddressAnnotation(Annotation annotation, BigInteger address) {
if (fViewer == null) {
return null; // can happen during session shutdown
}
IAnnotationModel annotationModel = fViewer.getAnnotationModel();
annotationModel.removeAnnotation(annotation);
AddressRangePosition pos = getPCPosition(address);
if (pos != null) {
annotationModel.addAnnotation(annotation, new Position(pos.offset, Math.max(0, pos.length - 1)));
}
return pos;
}
public IBreakpoint[] getBreakpointsAtLine(int line) {
BreakpointsAnnotationModel bpModel = null;
IAnnotationModel am = fViewer.getAnnotationModel();
if (am instanceof IAnnotationModelExtension) {
IAnnotationModelExtension ame = (IAnnotationModelExtension) am;
bpModel = (BreakpointsAnnotationModel) ame.getAnnotationModel(BREAKPOINT_ANNOTATIONS);
if (bpModel != null) {
IRegion lineRegion;
try {
lineRegion = fDocument.getLineInformation(line);
} catch (BadLocationException exc) {
return null;
}
int offset = lineRegion.getOffset();
int length = lineRegion.getLength();
Iterator<Annotation> it = bpModel.getAnnotationIterator(offset, length, true, true);
List<IBreakpoint> bpList = new ArrayList<>(5);
final IBreakpointManager bpMgr = DebugPlugin.getDefault().getBreakpointManager();
while (it.hasNext()) {
final SimpleMarkerAnnotation annotation = (SimpleMarkerAnnotation) it.next();
IBreakpoint bp = bpMgr.getBreakpoint(annotation.getMarker());
if (bp != null) {
bpList.add(bp);
}
}
if (!bpList.isEmpty()) {
return bpList.toArray(new IBreakpoint[bpList.size()]);
}
}
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#gotoFrame(int)
*/
@Override
public void gotoFrame(int frame) {
assert isGuiThread();
fGotoAddressPending = PC_UNKNOWN;
gotoFrame(frame, PC_UNKNOWN);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#gotoFrameIfActive(int)
*/
@Override
public void gotoFrameIfActive(int frame) {
assert isGuiThread();
if (fActive) {
gotoFrame(frame);
} else {
// this will trigger an update in #setActive()
fTargetFrame = -1;
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#gotoFrame(int, java.math.BigInteger)
*/
@Override
public void gotoFrame(int frame, BigInteger address) {
assert isGuiThread();
if (DEBUG)
System.out.println("gotoFrame " + frame + " " + getAddressText(address)); //$NON-NLS-1$ //$NON-NLS-2$
// cache the last PC address
if (!isSyncWithActiveDebugContext()) {
if (isTrackExpression()) {
if (!DisassemblyMessages.Disassembly_GotoLocation_initial_text.equals(fPCLastLocationTxt))
fPCLastAddress = eval(fPCLastLocationTxt, true);
}
if (fPCLastAddress != PC_UNKNOWN) {
address = fPCLastAddress;
} else if (address != PC_UNKNOWN) {
fPCLastAddress = address;
}
// need to get the frame address when the view first started.
if (fPCLastAddress != PC_UNKNOWN)
frame = -2; // clear the annotation
} else {
fPCLastAddress = address;
}
if (fGotoAddressPending == fFrameAddress) {
// cancel goto address from previous goto frame
fGotoAddressPending = PC_UNKNOWN;
}
fTargetFrame = frame;
fFrameAddress = address;
if (fTargetFrame == -1) {
fTargetFrame = getActiveStackFrame();
if (fTargetFrame < 0 && fBackend != null && fBackend.canDisassemble()) {
fTargetFrame = 0;
}
if (fTargetFrame == -1) {
fGotoFramePending = false;
return;
}
}
fGotoFramePending = true;
if (frame == 0) {
fPCAddress = fFrameAddress;
}
if (fFrameAddress.compareTo(PC_UNKNOWN) == 0) {
if (!fUpdatePending) {
fGotoFramePending = false;
if (fBackend != null && fBackend.hasDebugContext() && fBackend.canDisassemble()) {
if (DEBUG)
System.out.println("retrieveFrameAddress " + frame); //$NON-NLS-1$
fUpdatePending = true;
fBackend.retrieveFrameAddress(fTargetFrame);
}
}
return;
}
AddressRangePosition pcPos = updatePCAnnotation();
if (pcPos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0) {
pcPos = getPCPosition(fFrameAddress);
if (pcPos == null) {
gotoAddress(fFrameAddress);
return;
}
}
if (pcPos != null) {
if (frame == 0) {
addToPCHistory(pcPos);
}
fGotoFramePending = false;
gotoPosition(pcPos, false);
updateVisibleArea();
} else {
// give up
fGotoFramePending = false;
fGotoAddressPending = PC_UNKNOWN;
}
doPendingPCUpdates();
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isActive()
*/
@Override
public final boolean isActive() {
return fActive;
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isConnected()
*/
@Override
public final boolean isConnected() {
if (fDebugSessionId == null) {
return false;
}
return (fBackend != null) ? fBackend.hasDebugContext() : false;
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isSuspended()
*/
@Override
public final boolean isSuspended() {
return isConnected() && fBackend.isSuspended();
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#getTextViewer()
*/
@Override
public final ISourceViewer getTextViewer() {
return fViewer;
}
@Override
public final boolean hasViewer() {
return fViewer != null;
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#addRulerContextMenuListener(org.eclipse.jface.action.IMenuListener)
*/
@Override
public final void addRulerContextMenuListener(IMenuListener listener) {
fRulerContextMenuListeners.add(listener);
}
/*
* @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#removeRulerContextMenuListener(org.eclipse.jface.action.IMenuListener)
*/
@Override
public final void removeRulerContextMenuListener(IMenuListener listener) {
fRulerContextMenuListeners.remove(listener);
}
/**
* Schedule the retrieval of a module time stamp for the given address.
* Should return a <code>Long</code> object in case the value was computed,
* another object to be waited on if the retrieval is in progress, <code>null</code>
* if no time stamp could be retrieved.
*
* @param address
* @return Long, Object or <code>null</code>
*/
synchronized Object retrieveModuleTimestamp(BigInteger address) {
// TLETODO [disassembly] retrieve and cache module time stamp
return null;
}
private void setFocusPosition(Position pcPos) {
if (fFocusPos != null) {
fDocument.removePosition(fFocusPos);
fFocusPos = null;
}
if (pcPos != null) {
fFocusPos = new Position(pcPos.offset, pcPos.length);
try {
fDocument.addPosition(fFocusPos);
} catch (BadLocationException e) {
internalError(e);
}
} else {
fFocusAddress = PC_UNKNOWN;
}
}
/**
* Act on the first PC in the pending list that is not a special value
* (UNKNOWN, RUNNING), discarding all special value entries leading up to
* it. If the list only has special values, act on the last one and clear
* the list.
*/
private void doPendingPCUpdates() {
if (fPendingPCUpdates.isEmpty()) {
return;
}
BigInteger pc;
do {
pc = fPendingPCUpdates.remove(0);
if (pc.compareTo(BigInteger.ZERO) >= 0) {
break;
}
} while (!fPendingPCUpdates.isEmpty());
gotoFrame(0, pc);
}
private void addToPCHistory(AddressRangePosition pcPos) {
if (DEBUG)
System.out.println("addToPCHistory " + getAddressText(pcPos.fAddressOffset)); //$NON-NLS-1$
if (fPCHistorySizeMax <= 1) {
return;
}
AddressRangePosition first = null;
if (!fPCHistory.isEmpty()) {
first = fPCHistory.getFirst();
if (first.fAddressOffset == pcPos.fAddressOffset) {
if (first.offset != pcPos.offset || first.length != pcPos.length) {
fPCHistory.removeFirst();
fViewer.invalidateTextPresentation(first.offset, first.length);
} else {
return;
}
}
}
// clone and add
pcPos = new AddressRangePosition(pcPos.offset, pcPos.length, pcPos.fAddressOffset, BigInteger.ZERO);
fPCHistory.addFirst(pcPos);
try {
fDocument.addPosition(pcPos);
} catch (BadLocationException e) {
internalError(e);
}
// limit to max size
if (fPCHistory.size() > fPCHistorySizeMax) {
AddressRangePosition last = fPCHistory.removeLast();
fDocument.removePosition(last);
fViewer.invalidateTextPresentation(last.offset, last.length);
}
// redraw
for (Iterator<AddressRangePosition> it = fPCHistory.iterator(); it.hasNext();) {
AddressRangePosition pos = it.next();
fViewer.invalidateTextPresentation(pos.offset, pos.length);
}
}
/**
* Update current pc. If a pc update is currently under way, adds this
* address to a list of pending pc updates.
*
* @param pc Current pc address. -1 means retrieve pc from top frame, -2
* means target resumed
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#updatePC(java.math.BigInteger)
*/
@Override
public void updatePC(BigInteger pc) {
assert isGuiThread();
if (!fPendingPCUpdates.isEmpty()) {
BigInteger last = fPendingPCUpdates.get(fPendingPCUpdates.size() - 1);
if (last.compareTo(BigInteger.ZERO) < 0) {
fPendingPCUpdates.remove(fPendingPCUpdates.size() - 1);
}
}
fPendingPCUpdates.add(pc);
if (fPendingPCUpdates.size() > fPCHistorySizeMax) {
if (!fActive) {
// if not active, we can savely remove
// the pc updates before the history range
fPendingPCUpdates.remove(0);
}
// we ignore the current goto frame request
// and continue with the pending updates
fGotoFramePending = false;
}
if (fActive) {
if (fGotoFramePending) {
if (!fUpdatePending) {
gotoFrame(0, fFrameAddress);
}
} else {
doPendingPCUpdates();
}
}
}
private AddressRangePosition updatePCAnnotation() {
if (fUpdatePending) {
fPCAnnotationUpdatePending = true;
return null;
}
AddressRangePosition pos = null;
if (fTargetFrame == 0) {
// clear secondary
updateAddressAnnotation(fSecondaryPCAnnotation, PC_UNKNOWN);
// set primary
pos = updateAddressAnnotation(fPCAnnotation, fPCAddress);
} else if (fTargetFrame < 0) {
// clear both
updateAddressAnnotation(fPCAnnotation, PC_UNKNOWN);
updateAddressAnnotation(fSecondaryPCAnnotation, PC_UNKNOWN);
} else {
// clear primary
updateAddressAnnotation(fPCAnnotation, PC_UNKNOWN);
// set secondary
pos = updateAddressAnnotation(fSecondaryPCAnnotation, fFrameAddress);
}
fPCAnnotationUpdatePending = pos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0;
if (fExtPCAnnotationModel != null) {
fBackend.updateExtendedPCAnnotation(fExtPCAnnotationModel);
}
return pos;
}
private void scheduleDoPending() {
if (!fUpdatePending && !fDoPendingPosted) {
fDoPendingPosted = true;
invokeLater(() -> {
doPending();
fDoPendingPosted = false;
});
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#doPending()
*/
@Override
public void doPending() {
assert isGuiThread();
if (fViewer == null || fDocument == null) {
return;
}
if (fUpdateSourcePending) {
updateInvalidSource();
}
boolean sourceValid = !fDocument.hasInvalidSourcePositions();
if (sourceValid || fShowDisassembly) {
if (fGotoFramePending) {
gotoFrame(fTargetFrame, fFrameAddress);
}
}
if (sourceValid) {
if (fGotoAddressPending != PC_UNKNOWN) {
gotoAddress(fGotoAddressPending);
} else if (fGotoMarkerPending != null) {
gotoMarker(fGotoMarkerPending);
}
if (fPCAnnotationUpdatePending && !fGotoFramePending) {
updatePCAnnotation();
}
if (fUpdateTitlePending) {
updateTitle();
}
}
}
/**
* Safely run given runnable in a state when no update is pending.
* Delays execution by 10 ms if update is currently pending.
* @param doit
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#doScrollLocked(java.lang.Runnable)
*/
@Override
public void doScrollLocked(final Runnable doit) {
assert isGuiThread();
if (fViewer == null || fDebugSessionId == null) {
// disposed
return;
}
if (!fActive) {
// refresh all when becoming active again
fRefreshViewPending = false;
fRefreshAll = true;
return;
}
if (doit != null) {
fRunnableQueue.add(doit);
}
final int updateCount = fUpdateCount;
if (fUpdatePending) {
if (fRunnableQueue.size() == 1) {
Runnable doitlater = () -> {
if (updateCount == fUpdateCount) {
doScrollLocked(null);
}
};
invokeLater(doitlater);
}
} else {
fUpdatePending = true;
lockScroller();
try {
ArrayList<Runnable> copy = new ArrayList<>(fRunnableQueue);
fRunnableQueue.clear();
for (Iterator<Runnable> iter = copy.iterator(); iter.hasNext();) {
if (updateCount != fUpdateCount) {
return;
}
Runnable doitnow = iter.next();
try {
doitnow.run();
} catch (Exception e) {
internalError(e);
}
}
} finally {
fUpdatePending = false;
unlockScroller();
doPending();
updateVisibleArea();
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#lockScroller()
*/
@Override
public void lockScroller() {
assert isGuiThread();
assert fScrollPos == null;
fRedrawControl = fViewer.getControl();
fRedrawControl.setRedraw(false);
try {
int topOffset = fViewer.getTopIndexStartOffset();
int topIndex = fViewer.getTopIndex();
int bottomIndex = fViewer.getBottomIndex();
int bottomOffset = fViewer.getBottomIndexEndOffset();
int focusLine;
int focusOffset;
if (fFocusPos != null && fFocusPos.isDeleted) {
fFocusPos = null;
}
if (fFocusPos != null && fFocusPos.offset >= topOffset && fFocusPos.offset <= bottomOffset) {
focusOffset = fFocusPos.offset;
focusLine = fDocument.getLineOfOffset(focusOffset);
} else {
focusLine = Math.max(0, (topIndex + bottomIndex) / 2);
focusOffset = fDocument.getLineOffset(focusLine);
AddressRangePosition pos = fDocument.getDisassemblyPosition(focusOffset);
if (pos != null && !pos.fValid) {
// don't lock position of invalid range
focusOffset = pos.offset + pos.length;
focusLine = fDocument.getLineOfOffset(focusOffset);
}
}
fScrollPos = new Position(focusOffset);
fScrollLine = focusLine - topIndex;
fDocument.addPosition(fScrollPos);
} catch (BadLocationException e) {
// should not happen
internalError(e);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#unlockScroller()
*/
@Override
public void unlockScroller() {
assert isGuiThread();
try {
if (fScrollPos == null) {
return;
}
if (fScrollPos.isDeleted) {
fScrollPos.isDeleted = false;
if (fScrollPos.offset >= fDocument.getLength()) {
fScrollPos.offset = 0;
fScrollLine = 0;
}
}
if (fFocusPos != null && (fFocusPos.isDeleted || fFocusPos.length == 0)) {
if (fFocusAddress.compareTo(BigInteger.ZERO) >= 0) {
fGotoAddressPending = fFocusAddress;
setFocusPosition(getPositionOfAddress(fFocusAddress));
}
}
int topLine = fDocument.getLineOfOffset(fScrollPos.offset) - fScrollLine;
// limit text size
int lineCount = fDocument.getNumberOfLines();
if (lineCount > fgHighWaterMark * fBufferZone) {
int startLine = Math.max(0, topLine - fgLowWaterMark / 2 * fBufferZone);
int endLine = Math.min(lineCount - 1, topLine + fgLowWaterMark / 2 * fBufferZone);
fDocument.deleteLineRange(endLine, lineCount - 1);
fDocument.deleteLineRange(0, startLine);
}
int lineHeight = fViewer.getTextWidget().getLineHeight();
int topPixel = topLine * lineHeight;
if (Math.abs(fViewer.getTextWidget().getTopPixel() - topPixel) >= lineHeight) {
fViewer.setTopIndex(topLine);
}
} catch (BadLocationException e) {
// should not happen
internalError(e);
} finally {
if (fScrollPos != null && fDocument != null) {
fDocument.removePosition(fScrollPos);
fScrollPos = null;
}
if (fViewer != null) {
fRedrawControl.setRedraw(true);
getVerticalRuler().update();
getOverviewRuler().update();
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#insertSource(org.eclipse.cdt.debug.internal.ui.disassembly.dsf.AddressRangePosition)
*/
@Override
public void insertSource(AddressRangePosition _pos) {
assert isGuiThread();
// IDisassemblyPartCallback does not have visibility to the
// SourcePosition type, which is DSF-specific, so it uses the base type
if (!(_pos instanceof SourcePosition)) {
assert false : "Caller should have passed in a SourcePosition"; //$NON-NLS-1$
return;
}
SourcePosition pos = (SourcePosition) _pos;
if (!fShowSource) {
fDocument.insertSource(pos, "", pos.fLine, true); //$NON-NLS-1$
return;
}
SourceFileInfo fi = pos.fFileInfo;
if (fi.fSource != null || fi.fError != null) {
int lineNr = pos.fLine;
if (fi.fSource != null && lineNr >= 0 && lineNr < fi.fSource.getNumberOfLines()) {
fi.fStartAddress = fi.fStartAddress.min(pos.fAddressOffset);
fi.fEndAddress = fi.fEndAddress.max(pos.fAddressOffset.add(pos.fAddressLength));
int last = pos.fLast > lineNr ? pos.fLast : lineNr;
final BigInteger lineAddr = fi.fLine2Addr[lineNr];
if (lineAddr == null) {
fi.fLine2Addr[lineNr] = pos.fAddressOffset;
String source = fi.getLines(lineNr, last);
fDocument.insertSource(pos, source, lineNr, true);
} else {
final int comparison = lineAddr.compareTo(pos.fAddressOffset);
if (comparison > 0) {
// new source position is before old position
SourcePosition oldPos = fDocument.getSourcePosition(lineAddr);
if (oldPos != null) {
// test if source positions are consecutive
try {
int index = fDocument.computeIndexInCategory(DisassemblyDocument.CATEGORY_SOURCE,
pos.fAddressOffset);
if (index >= 0) {
SourcePosition nextPos = (SourcePosition) fDocument
.getPositionOfIndex(DisassemblyDocument.CATEGORY_SOURCE, index + 1);
if (nextPos.fFileInfo == fi && nextPos.fLine == lineNr) {
fDocument.replace(oldPos, oldPos.length, ""); //$NON-NLS-1$
fDocument.removeSourcePosition(oldPos);
}
}
} catch (BadLocationException e) {
internalError(e);
} catch (BadPositionCategoryException e) {
internalError(e);
}
}
fi.fLine2Addr[lineNr] = pos.fAddressOffset;
String source = fi.getLines(lineNr, last);
fDocument.insertSource(pos, source, lineNr, true);
} else if (comparison == 0) {
String source = fi.getLines(lineNr, last);
fDocument.insertSource(pos, source, lineNr, true);
} else {
// new source position is after old position
try {
// test if source positions are consecutive
int index = fDocument.computeIndexInCategory(DisassemblyDocument.CATEGORY_SOURCE,
pos.fAddressOffset);
if (index > 0) {
SourcePosition prevPos = (SourcePosition) fDocument
.getPositionOfIndex(DisassemblyDocument.CATEGORY_SOURCE, index - 1);
if (prevPos.fFileInfo == fi && prevPos.fLine == lineNr) {
fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$
fDocument.removeSourcePosition(pos);
} else {
String source = fi.getLines(lineNr, last);
fDocument.insertSource(pos, source, lineNr, true);
}
} else {
String source = fi.getLines(lineNr, last);
fDocument.insertSource(pos, source, lineNr, true);
}
} catch (BadPositionCategoryException e) {
internalError(e);
}
}
}
} else {
// no source at all
fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$
fDocument.removeSourcePosition(pos);
}
}
}
private void updateTitle() {
if (fDebugSessionId == null) {
String descr = DisassemblyMessages.Disassembly_message_notConnected;
String title = getConfigurationElement().getAttribute("name"); //$NON-NLS-1$
setPartName(title);
setContentDescription(descr);
setTitleToolTip(title);
} else {
// TLETODO Proper content description
setContentDescription(""); //$NON-NLS-1$
}
}
/**
* Close this part
*/
protected abstract void closePart();
@Override
public void applyTextPresentation(TextPresentation textPresentation) {
IRegion coverage = textPresentation.getExtent();
if (coverage == null) {
coverage = new Region(0, fDocument.getLength());
}
int startOffset = coverage.getOffset();
int length = coverage.getLength();
int endOffset = startOffset + length;
Iterator<Position> it;
try {
// make sure we start with first overlapping position
AddressRangePosition pos = fDocument.getModelPosition(startOffset);
if (pos == null) {
assert false;
return;
}
it = fDocument.getPositionIterator(DisassemblyDocument.CATEGORY_MODEL, pos.offset);
} catch (BadPositionCategoryException e) {
return;
} catch (BadLocationException e) {
return;
}
ArrayList<StyleRange> styleRanges = new ArrayList<>();
while (it.hasNext()) {
AddressRangePosition pos = (AddressRangePosition) it.next();
if (pos.offset >= endOffset) {
break;
}
if (pos.offset + pos.length <= startOffset) {
continue;
}
if (pos.fValid && pos.length > 0) {
if (pos instanceof DisassemblyPosition) {
DisassemblyPosition disPos = (DisassemblyPosition) pos;
styleRanges.add(new StyleRange(pos.offset, disPos.length, fInstructionColor, null, SWT.NULL));
} else if (pos instanceof ErrorPosition) {
styleRanges.add(new StyleRange(pos.offset, pos.length, fErrorColor, null, SWT.NULL));
} else if (pos instanceof LabelPosition) {
styleRanges.add(new StyleRange(pos.offset, pos.length, fLabelColor, null, SWT.BOLD));
} else if (pos instanceof SourcePosition) {
SourcePosition srcPos = (SourcePosition) pos;
TextPresentation presentation = null;
if (srcPos.fFileInfo.fSource != null) {
presentation = srcPos.fFileInfo
.getPresentation(srcPos.fFileInfo.getRegion(srcPos.fLine, pos.length));
}
if (presentation != null) {
// clip result window to coverage
int start = Math.max(startOffset, srcPos.offset);
int end = Math.min(endOffset, srcPos.offset + srcPos.length);
int srcOffset = srcPos.fFileInfo.getLineOffset(srcPos.fLine);
int clipOffset = start - srcPos.offset;
presentation.setResultWindow(new Region(srcOffset + clipOffset, end - start));
for (Iterator<StyleRange> iter = presentation.getNonDefaultStyleRangeIterator(); iter
.hasNext();) {
StyleRange styleRange = iter.next();
styleRange.start += srcPos.offset + clipOffset;
styleRanges.add(styleRange);
}
} else {
styleRanges.add(new StyleRange(pos.offset, pos.length, fSourceColor, null, SWT.NULL));
}
}
}
}
if (!styleRanges.isEmpty()) {
for (Iterator<StyleRange> iter = styleRanges.iterator(); iter.hasNext();) {
textPresentation.addStyleRange(iter.next());
}
}
// update pc history trail
if (fPCHistory.size() > 1) {
final double bgLuminance = new HSL(fViewer.getTextWidget().getBackground().getRGB()).luminance;
HSL hsv = new HSL(fPCAnnotationRGB);
double luminanceStep = (bgLuminance - hsv.luminance) / (fPCHistorySizeMax + 1);
hsv.luminance = bgLuminance - luminanceStep * (fPCHistorySizeMax - fPCHistory.size());
for (ListIterator<AddressRangePosition> listIt = fPCHistory.listIterator(fPCHistory.size()); listIt
.hasPrevious();) {
AddressRangePosition pcPos = listIt.previous();
hsv.luminance -= luminanceStep;
if (pcPos.isDeleted) {
listIt.remove();
continue;
}
if (!pcPos.fValid) {
continue;
}
if (pcPos.overlapsWith(startOffset, length)) {
RGB rgb = hsv.toRGB();
Color pcColor = getSharedColors().getColor(rgb);
Color textColor = null;
// experimental: if color is dark, use white (background) as text color
// Color textColor = hsv.luminance < 0.7 ? fViewer.getTextWidget().getBackground() : null;
textPresentation.mergeStyleRange(new StyleRange(pcPos.offset, pcPos.length, textColor, pcColor));
}
}
}
}
@Override
public AddressRangePosition insertSource(AddressRangePosition pos, BigInteger address, final String file,
int lineNumber) {
return insertSource(pos, address, file, lineNumber, lineNumber);
}
@Override
public AddressRangePosition insertSource(AddressRangePosition pos, BigInteger address, final String file,
int firstLine, int lastLine) {
assert isGuiThread();
Object sourceElement = null;
if (fFile2Storage.containsKey(file)) {
sourceElement = fFile2Storage.get(file);
} else {
sourceElement = fBackend.insertSource(pos, address, file, firstLine);
}
if (sourceElement instanceof File) {
sourceElement = new LocalFileStorage((File) sourceElement);
} else if (sourceElement instanceof ITranslationUnit) {
IPath location = ((ITranslationUnit) sourceElement).getLocation();
if (location != null) {
sourceElement = new LocalFileStorage(location.toFile());
}
}
if (sourceElement instanceof IStorage) {
if (!(sourceElement instanceof IFile)) {
// try to resolve as resource
final IPath location = ((IStorage) sourceElement).getFullPath();
if (location != null) {
IFile iFile = ResourceLookup.selectFileForLocation(location, null);
if (iFile != null && iFile.isAccessible()) {
sourceElement = iFile;
}
}
}
fFile2Storage.put(file, sourceElement);
} else if (sourceElement == null) {
if (!fFile2Storage.containsKey(file)) {
logWarning(DisassemblyMessages.Disassembly_log_error_locateFile + file, null);
fFile2Storage.put(file, null);
}
} else {
fFile2Storage.put(file, null);
assert false : "missing support for source element of type " + sourceElement.getClass().toString(); //$NON-NLS-1$
}
if (sourceElement instanceof IStorage) {
SourceFileInfo fi = fDocument.getSourceInfo((IStorage) sourceElement);
if (fi == null) {
IStorage storage = (IStorage) sourceElement;
Display display = getSite().getShell().getDisplay();
Runnable done = new SourceColorerJob(display, storage, this);
fi = fDocument.createSourceInfo(file, storage, done);
EditionFinderJob editionJob = null;
if (storage instanceof IFile) {
editionJob = new EditionFinderJob(fi, address, this);
editionJob.schedule();
}
fi.fReadingJob.schedule();
}
pos = fDocument.insertInvalidSource(pos, address, fi, firstLine, lastLine);
}
return pos;
}
public AddressBarContributionItem getAddressBar() {
return fAddressBar;
}
public void generateErrorDialog(String message) {
MessageDialog messageDialog = new MessageDialog(PlatformUI.getWorkbench().getDisplay().getActiveShell(),
DisassemblyMessages.Disassembly_Error_Dialog_title, null, message, MessageDialog.ERROR,
new String[] { DisassemblyMessages.Disassembly_Error_Dialog_ok_button }, 0);
messageDialog.open();
}
public void activateDisassemblyContext() {
IContextService ctxService = getSite().getService(IContextService.class);
if (ctxService != null)
fContextActivation = ctxService.activateContext(KEY_BINDING_CONTEXT_DISASSEMBLY);
}
public void deactivateDisassemblyContext() {
if (fContextActivation != null) {
IContextService ctxService = getSite().getService(IContextService.class);
ctxService.deactivateContext(fContextActivation);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#handleTargetSuspended()
*/
@Override
public void handleTargetSuspended() {
asyncExec(() -> {
updatePC(PC_UNKNOWN);
firePropertyChange(PROP_SUSPENDED);
});
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#handleTargetResumed()
*/
@Override
public void handleTargetResumed() {
asyncExec(() -> {
updatePC(PC_RUNNING);
firePropertyChange(PROP_SUSPENDED);
});
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#handleTargetEnded()
*/
@Override
public void handleTargetEnded() {
asyncExec(() -> {
fDebugSessionId = null;
startUpdate(() -> debugContextChanged());
});
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#setUpdatePending(boolean)
*/
@Override
public void setUpdatePending(boolean pending) {
fUpdatePending = pending;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getUpdatePending()
*/
@Override
public boolean getUpdatePending() {
assert isGuiThread();
return fUpdatePending;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#setGotoAddressPending(java.math.BigInteger)
*/
@Override
public void setGotoAddressPending(BigInteger address) {
assert isGuiThread();
fGotoAddressPending = address;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getGotoAddressPending()
*/
@Override
public BigInteger getGotoAddressPending() {
assert isGuiThread();
return fGotoAddressPending;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getDocument()
*/
@Override
public IDisassemblyDocument getDocument() {
assert isGuiThread();
return fDocument;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.internal.ui.disassembly.dsf.IDisassemblyPartCallback#getStorageForFile(java.lang.String)
*/
@Override
public Object getStorageForFile(String file) {
assert isGuiThread();
return fFile2Storage.get(file);
}
/**
* A pass through to process the text to populate into a text hover.
*/
public String getHoverInfoData(AddressRangePosition pos, String ident) {
if (fBackend != null) {
return fBackend.getHoverInfoData(pos, ident);
}
return ""; //$NON-NLS-1$
}
/**
* A passthru from the text hover code to the backend.
*/
public String evaluateRegister(String register) {
if (fBackend != null) {
return fBackend.evaluateRegister(register);
}
return ""; //$NON-NLS-1$
}
/**
* A passthru from the text hover code to the backend.
*/
public String evaluateExpression(String expr) {
if (fBackend != null) {
return fBackend.evaluateExpression(expr);
}
return ""; //$NON-NLS-1$
}
public BigInteger eval(String expr, boolean suppressError) {
if (fBackend != null) {
BigInteger address = null;
if (fBackend instanceof AbstractDisassemblyBackend) {
address = ((AbstractDisassemblyBackend) fBackend).evaluateAddressExpression(expr, suppressError);
} else {
String value = fBackend.evaluateExpression(expr);
if (value != null) {
try {
address = DisassemblyUtils.decodeAddress(value);
} catch (NumberFormatException e) {
if (!suppressError) {
generateErrorDialog(DisassemblyMessages.Disassembly_log_error_expression_eval);
}
}
}
}
if (address != null)
return address;
}
return PC_UNKNOWN;
}
protected boolean isTrackExpression() {
return fTrackExpression;
}
private void setTrackExpression(boolean track) {
fTrackExpression = track;
}
protected boolean isSyncWithActiveDebugContext() {
return fSynchWithActiveDebugContext;
}
private void setSyncWithDebugView(boolean sync) {
fSynchWithActiveDebugContext = sync;
fTrackExpressionAction.setEnabled(!sync);
if (sync) {
gotoActiveFrameByUser();
} else {
// redraw
while (!fPCHistory.isEmpty()) {
AddressRangePosition pos = fPCHistory.removeFirst();
fViewer.invalidateTextPresentation(pos.offset, pos.length);
}
fTargetFrame = -2; // clear the annotation
updatePCAnnotation();
}
}
/**
* Most methods in IDisassemblyPartCallback require execution on the GUI thread.
*/
private static boolean isGuiThread() {
return Display.getCurrent() != null;
}
boolean keyScroll(int keyCode) {
BigInteger topAddress = getTopAddress();
BigInteger bottomAddress = getBottomAddress();
BigInteger addressRange = bottomAddress.subtract(topAddress);
StyledText styledText = fViewer.getTextWidget();
Rectangle clientArea = styledText.getClientArea();
int lineRange;
if (SWT.PAGE_UP == keyCode || SWT.PAGE_DOWN == keyCode) {
lineRange = clientArea.height / styledText.getLineHeight();
} else {
lineRange = 1;
}
addressRange = addressRange.min(BigInteger.valueOf(lineRange * fDocument.getMeanSizeOfInstructions()));
BigInteger scrollToAddress;
switch (keyCode) {
case SWT.PAGE_UP:
case SWT.ARROW_UP:
scrollToAddress = topAddress.subtract(addressRange).max(fStartAddress);
break;
case SWT.PAGE_DOWN:
scrollToAddress = topAddress.add(addressRange).min(bottomAddress);
break;
case SWT.ARROW_DOWN:
scrollToAddress = bottomAddress.add(addressRange).min(bottomAddress);
break;
default:
assert false; // invalid keycode passed
scrollToAddress = fFocusAddress;
}
AddressRangePosition pos = getPositionOfAddress(scrollToAddress);
if (pos != null && pos.fValid) {
if (SWT.ARROW_DOWN == keyCode && pos.fAddressOffset.compareTo(bottomAddress) <= 0
|| SWT.ARROW_UP == keyCode && pos.fAddressOffset.compareTo(topAddress) >= 0) {
return false;
}
gotoPosition(pos, SWT.ARROW_DOWN != keyCode);
} else {
gotoAddress(scrollToAddress);
}
return true;
}
private BigInteger getBottomAddress() {
BigInteger bottomAddress = getAddressOfLine(fViewer.getBottomIndex());
if (bottomAddress == null || bottomAddress.equals(PC_UNKNOWN)) {
bottomAddress = fEndAddress.subtract(BigInteger.ONE);
}
return bottomAddress;
}
@Override
public void refresh() {
fBackend.clearCaches();
asyncExec(() -> {
updatePC(PC_UNKNOWN);
});
}
}