mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 12:11:55 +00:00
Merge remote-tracking branch 'origin/GP-4293_Dan_doubleClickModelActions--SQUASHED'
This commit is contained in:
commit
611aae64ae
@ -29,6 +29,7 @@
|
||||
<attribute schema="ANY" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Access" />
|
||||
<interface name="Attacher" />
|
||||
<interface name="Interpreter" />
|
||||
@ -163,6 +164,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Process" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
@ -290,6 +292,7 @@
|
||||
<attribute schema="VOID" fixed="yes" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Thread" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Steppable" />
|
||||
@ -416,6 +419,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="StackFrame" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
@ -120,6 +120,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Process" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
@ -245,6 +246,7 @@
|
||||
<attribute schema="VOID" fixed="yes" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Thread" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Steppable" />
|
||||
@ -355,6 +357,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="StackFrame" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
@ -117,6 +117,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Inferior" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Process" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
@ -241,6 +242,7 @@
|
||||
<attribute schema="VOID" fixed="yes" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Thread" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Steppable" />
|
||||
@ -349,6 +351,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="StackFrame" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
@ -153,6 +153,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Process" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
@ -280,6 +281,7 @@
|
||||
<attribute schema="VOID" fixed="yes" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Thread" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Steppable" />
|
||||
@ -389,6 +391,7 @@
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="StackFrame" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
|
@ -458,7 +458,7 @@ public interface DebuggerTraceManagerService {
|
||||
DebuggerCoordinates resolvePath(TraceObjectKeyPath path);
|
||||
|
||||
/**
|
||||
* Activate the given object path
|
||||
* Activate the given canonical object path
|
||||
*
|
||||
* @param path the desired path
|
||||
*/
|
||||
|
@ -169,6 +169,11 @@ public interface Target {
|
||||
*/
|
||||
Map<String, ActionEntry> collectActions(ActionName name, ActionContext context);
|
||||
|
||||
/**
|
||||
* @see #execute(String, boolean)
|
||||
*/
|
||||
CompletableFuture<String> executeAsync(String command, boolean toString);
|
||||
|
||||
/**
|
||||
* Execute a command as if in the CLI
|
||||
*
|
||||
@ -176,11 +181,6 @@ public interface Target {
|
||||
* @param toString true to capture the output and return it, false to print to the terminal
|
||||
* @return the captured output, or null if {@code toString} is false
|
||||
*/
|
||||
CompletableFuture<String> executeAsync(String command, boolean toString);
|
||||
|
||||
/**
|
||||
* @see #executeAsync(String, boolean)
|
||||
*/
|
||||
String execute(String command, boolean toString);
|
||||
|
||||
/**
|
||||
|
@ -499,11 +499,9 @@ public class DebuggerCoordinates {
|
||||
else if (trace == null) {
|
||||
throw new IllegalArgumentException("No trace");
|
||||
}
|
||||
else {
|
||||
if (newPath == null) {
|
||||
return new DebuggerCoordinates(trace, platform, target, thread, view, time, frame,
|
||||
newPath);
|
||||
}
|
||||
else if (newPath == null) {
|
||||
return new DebuggerCoordinates(trace, platform, target, thread, view, time, frame,
|
||||
newPath);
|
||||
}
|
||||
TraceThread newThread = target != null
|
||||
? resolveThread(target, newPath)
|
||||
@ -516,6 +514,31 @@ public class DebuggerCoordinates {
|
||||
newFrame, newPath);
|
||||
}
|
||||
|
||||
public DebuggerCoordinates pathNonCanonical(TraceObjectKeyPath newPath) {
|
||||
if (trace == null && newPath == null) {
|
||||
return NOWHERE;
|
||||
}
|
||||
else if (trace == null) {
|
||||
throw new IllegalArgumentException("No trace");
|
||||
}
|
||||
else if (newPath == null) {
|
||||
return new DebuggerCoordinates(trace, platform, target, thread, view, time, frame,
|
||||
newPath);
|
||||
}
|
||||
TraceObject object = trace.getObjectManager().getObjectByCanonicalPath(newPath);
|
||||
if (object != null) {
|
||||
return path(newPath);
|
||||
}
|
||||
object = trace.getObjectManager()
|
||||
.getObjectsByPath(Lifespan.at(getSnap()), newPath)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
if (object != null) {
|
||||
return path(object.getCanonicalPath());
|
||||
}
|
||||
throw new IllegalArgumentException("No such object at path" + path);
|
||||
}
|
||||
|
||||
protected static TraceThread resolveThread(Target target, TraceObjectKeyPath objectPath) {
|
||||
return target.getThreadForSuccessor(objectPath);
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import ghidra.dbg.util.PathMatcher;
|
||||
import ghidra.dbg.util.PathPredicates;
|
||||
import ghidra.dbg.util.PathPredicates.Align;
|
||||
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
||||
import ghidra.debug.api.model.DebuggerSingleObjectPathActionContext;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.debug.api.tracermi.*;
|
||||
@ -144,6 +145,20 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (context instanceof DebuggerSingleObjectPathActionContext ctx) {
|
||||
TraceObject object =
|
||||
trace.getObjectManager().getObjectByCanonicalPath(ctx.getPath());
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
object = trace.getObjectManager()
|
||||
.getObjectsByPath(Lifespan.at(getSnap()), ctx.getPath())
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allowCoordsObject) {
|
||||
DebuggerTraceManagerService traceManager =
|
||||
@ -156,13 +171,45 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object findArgumentForSchema(ActionContext context, TargetObjectSchema schema,
|
||||
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
||||
/**
|
||||
* "Find" a boolean value for the given context.
|
||||
*
|
||||
* <p>
|
||||
* At the moment, this is only used for toggle actions, where the "found" parameter is the
|
||||
* opposite of the context object's current state. That object is presumed the object argument
|
||||
* of the "toggle" method.
|
||||
*
|
||||
* @param action the action name, so this is only applied to {@link ActionName#TOGGLE}
|
||||
* @param context the context in which to find the object whose current state is to be
|
||||
* considered
|
||||
* @param allowContextObject true to allow the object to come from context
|
||||
* @param allowCoordsObject true to allow the object to come from the current coordinates
|
||||
* @return a value if found, null if not
|
||||
*/
|
||||
protected Boolean findBool(ActionName action, ActionContext context, boolean allowContextObject,
|
||||
boolean allowCoordsObject) {
|
||||
if (!Objects.equals(action, ActionName.TOGGLE)) {
|
||||
return null;
|
||||
}
|
||||
TraceObject object = findObject(context, allowContextObject, allowCoordsObject);
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
TraceObjectValue attrEnabled =
|
||||
object.getAttribute(getSnap(), TargetTogglable.ENABLED_ATTRIBUTE_NAME);
|
||||
boolean enabled = attrEnabled != null && attrEnabled.getValue() instanceof Boolean b && b;
|
||||
return !enabled;
|
||||
}
|
||||
|
||||
protected Object findArgumentForSchema(ActionName action, ActionContext context,
|
||||
TargetObjectSchema schema, boolean allowContextObject, boolean allowCoordsObject,
|
||||
boolean allowSuitableObject) {
|
||||
if (schema instanceof EnumerableTargetObjectSchema prim) {
|
||||
return switch (prim) {
|
||||
case OBJECT -> findObject(context, allowContextObject, allowCoordsObject);
|
||||
case ADDRESS -> findAddress(context);
|
||||
case RANGE -> findRange(context);
|
||||
case BOOL -> findBool(action, context, allowContextObject, allowCoordsObject);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
@ -183,8 +230,9 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
MISSING; // The argument requires a prompt
|
||||
}
|
||||
|
||||
protected Object findArgument(RemoteParameter parameter, ActionContext context,
|
||||
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
||||
protected Object findArgument(ActionName action, RemoteParameter parameter,
|
||||
ActionContext context, boolean allowContextObject, boolean allowCoordsObject,
|
||||
boolean allowSuitableObject) {
|
||||
SchemaName type = parameter.type();
|
||||
SchemaContext ctx = getSchemaContext();
|
||||
if (ctx == null) {
|
||||
@ -196,8 +244,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
Msg.error(this, "Schema " + type + " not in trace! " + trace);
|
||||
return null;
|
||||
}
|
||||
Object arg = findArgumentForSchema(context, schema, allowContextObject, allowCoordsObject,
|
||||
allowSuitableObject);
|
||||
Object arg = findArgumentForSchema(action, context, schema, allowContextObject,
|
||||
allowCoordsObject, allowSuitableObject);
|
||||
if (arg != null) {
|
||||
return arg;
|
||||
}
|
||||
@ -211,8 +259,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
for (RemoteParameter param : method.parameters().values()) {
|
||||
Object found = findArgument(param, context, allowContextObject, allowCoordsObject,
|
||||
allowSuitableObject);
|
||||
Object found = findArgument(method.action(), param, context, allowContextObject,
|
||||
allowCoordsObject, allowSuitableObject);
|
||||
if (found != null) {
|
||||
args.put(param.name(), found);
|
||||
}
|
||||
@ -433,6 +481,12 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
true, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, ActionEntry> collectToggleActions(ActionContext context) {
|
||||
return collectFromMethods(connection.getMethods().getByAction(ActionName.TOGGLE), context,
|
||||
true, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupportsFocus() {
|
||||
TargetObjectSchema schema = trace.getObjectManager().getRootSchema();
|
||||
@ -1219,9 +1273,8 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||
String condition, String commands) {
|
||||
RemoteParameter paramProc = brk.params.get("process");
|
||||
if (paramProc != null) {
|
||||
Object proc =
|
||||
findArgumentForSchema(null, getSchemaContext().getSchema(paramProc.type()), true,
|
||||
true, true);
|
||||
Object proc = findArgumentForSchema(null, null,
|
||||
getSchemaContext().getSchema(paramProc.type()), true, true, true);
|
||||
if (proc == null) {
|
||||
Msg.error(this, "Cannot find required process argument for " + brk.method);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
|
||||
import ghidra.pcode.exec.SleighUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
||||
@ -82,6 +83,10 @@ public class BreakpointLocationRow {
|
||||
return loc.getMinAddress();
|
||||
}
|
||||
|
||||
public ProgramLocation getProgramLocation() {
|
||||
return new ProgramLocation(loc.getTrace().getProgramView(), getAddress());
|
||||
}
|
||||
|
||||
public String getTraceName() {
|
||||
return loc.getTrace().getName();
|
||||
}
|
||||
|
@ -1153,7 +1153,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||
}
|
||||
traceManager.activateTrace(trace);
|
||||
}
|
||||
listingService.goTo(row.getAddress(), true);
|
||||
listingService.goTo(row.getProgramLocation(), true);
|
||||
}
|
||||
|
||||
protected void createActions() {
|
||||
|
@ -267,8 +267,8 @@ public class DebuggerLegacyRegionsPanel extends JPanel {
|
||||
int selectedRow = regionTable.getSelectedRow();
|
||||
int selectedColumn = regionTable.getSelectedColumn();
|
||||
Object value = regionTable.getValueAt(selectedRow, selectedColumn);
|
||||
if (value instanceof Address) {
|
||||
listingService.goTo((Address) value, true);
|
||||
if (value instanceof Address address) {
|
||||
listingService.goTo(address, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ public class DebuggerRegionsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new RegionTableModel(plugin);
|
||||
}
|
||||
|
||||
|
@ -26,27 +26,25 @@ import javax.swing.event.ListSelectionListener;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActivationListener;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueProperty;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.plugintool.AutoService;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectInterface;
|
||||
import ghidra.trace.model.target.*;
|
||||
|
||||
public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterface>
|
||||
extends ObjectsTablePanel implements ListSelectionListener, CellActivationListener {
|
||||
extends ObjectsTablePanel
|
||||
implements ListSelectionListener, CellActivationListener, ObjectDefaultActionsMixin {
|
||||
|
||||
private final ComponentProvider provider;
|
||||
private final Class<U> objType;
|
||||
|
||||
@AutoServiceConsumed
|
||||
protected DebuggerTraceManagerService traceManager;
|
||||
@AutoServiceConsumed
|
||||
protected DebuggerListingService listingService;
|
||||
@SuppressWarnings("unused")
|
||||
@ -121,23 +119,32 @@ public abstract class AbstractObjectsTableBasedPanel<U extends TraceObjectInterf
|
||||
|
||||
@Override
|
||||
public void cellActivated(JTable table) {
|
||||
if (listingService == null) {
|
||||
if (performElementCellDefaultAction(table)) {
|
||||
return;
|
||||
}
|
||||
int row = table.getSelectedRow();
|
||||
int col = table.getSelectedColumn();
|
||||
Object value = table.getValueAt(row, col);
|
||||
if (!(value instanceof ValueProperty<?> property)) {
|
||||
performValueRowDefaultAction(getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerCoordinates getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return plugin.getTool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activatePath(TraceObjectKeyPath path) {
|
||||
if (current.getTrace() == null) {
|
||||
return;
|
||||
}
|
||||
Object propVal = property.getValue();
|
||||
if (propVal instanceof Address address) {
|
||||
listingService.goTo(address, true);
|
||||
try {
|
||||
traceManager.activate(current.pathNonCanonical(path));
|
||||
}
|
||||
else if (propVal instanceof AddressRange range) {
|
||||
listingService.setCurrentSelection(
|
||||
new ProgramSelection(range.getMinAddress(), range.getMaxAddress()));
|
||||
listingService.goTo(range.getMinAddress(), true);
|
||||
catch (IllegalArgumentException e) {
|
||||
plugin.getTool().setStatusInfo(e.getMessage(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
void cellActivated(JTable table);
|
||||
}
|
||||
|
||||
protected final Plugin plugin;
|
||||
protected final M tableModel;
|
||||
protected final GhidraTable table;
|
||||
protected final GhidraTableFilterPanel<T> filterPanel;
|
||||
@ -54,7 +55,9 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
|
||||
public AbstractQueryTablePanel(Plugin plugin) {
|
||||
super(new BorderLayout());
|
||||
tableModel = createModel(plugin);
|
||||
this.plugin = plugin;
|
||||
|
||||
tableModel = createModel();
|
||||
table = new GhidraTable(tableModel);
|
||||
filterPanel = new GhidraTableFilterPanel<>(table, tableModel);
|
||||
|
||||
@ -80,7 +83,7 @@ public abstract class AbstractQueryTablePanel<T, M extends AbstractQueryTableMod
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract M createModel(Plugin plugin);
|
||||
protected abstract M createModel();
|
||||
|
||||
protected void coordinatesChanged() {
|
||||
// Extension point
|
||||
|
@ -45,11 +45,11 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.CloneWindowAction;
|
||||
import ghidra.app.plugin.core.debug.gui.MultiProviderSaveBehavior.SaveableProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
|
||||
import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActivationListener;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ObjectRow;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTreeModel.*;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTreeModel.RootNode;
|
||||
import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
@ -233,6 +233,8 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
|
||||
@AutoServiceConsumed
|
||||
protected DebuggerTraceManagerService traceManager;
|
||||
@AutoServiceConsumed
|
||||
protected DebuggerListingService listingService;
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
@ -431,8 +433,49 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
mainPanel.revalidate();
|
||||
}
|
||||
|
||||
protected class ObjectsTreeListener implements Adapters.FocusListener,
|
||||
protected void activatePath(TraceObjectKeyPath path) {
|
||||
if (current.getTrace() == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
traceManager.activate(current.pathNonCanonical(path));
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
plugin.getTool().setStatusInfo(e.getMessage(), true);
|
||||
}
|
||||
}
|
||||
|
||||
protected class MyMixin implements ObjectDefaultActionsMixin {
|
||||
@Override
|
||||
public DebuggerCoordinates getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTool getTool() {
|
||||
return plugin.getTool();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activatePath(TraceObjectKeyPath path) {
|
||||
DebuggerModelProvider.this.activatePath(path);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ObjectsTreeListener extends MyMixin implements Adapters.FocusListener,
|
||||
Adapters.TreeExpansionListener, Adapters.MouseListener, Adapters.KeyListener {
|
||||
|
||||
private void activateObjectSelectedInTree() {
|
||||
List<AbstractNode> sel = objectsTreePanel.getSelectedItems();
|
||||
if (sel.size() != 1) {
|
||||
// TODO: Multiple paths? PathMatcher can do it, just have to parse
|
||||
// Just leave whatever was there.
|
||||
return;
|
||||
}
|
||||
TraceObjectValue value = sel.get(0).getValue();
|
||||
performDefaultAction(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
@ -501,18 +544,14 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
}
|
||||
}
|
||||
|
||||
protected class ElementsTableListener
|
||||
protected class ElementsTableListener extends MyMixin
|
||||
implements Adapters.FocusListener, CellActivationListener {
|
||||
@Override
|
||||
public void cellActivated(JTable table) {
|
||||
ValueRow row = elementsTablePanel.getSelectedItem();
|
||||
if (row == null) {
|
||||
if (performElementCellDefaultAction(table)) {
|
||||
return;
|
||||
}
|
||||
if (!(row instanceof ObjectRow objectRow)) {
|
||||
return;
|
||||
}
|
||||
activatePath(objectRow.getTraceObject().getCanonicalPath());
|
||||
performValueRowDefaultAction(elementsTablePanel.getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -567,19 +606,11 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
}
|
||||
}
|
||||
|
||||
protected class AttributesTableListener
|
||||
protected class AttributesTableListener extends MyMixin
|
||||
implements Adapters.FocusListener, CellActivationListener {
|
||||
@Override
|
||||
public void cellActivated(JTable table) {
|
||||
PathRow row = attributesTablePanel.getSelectedItem();
|
||||
if (row == null) {
|
||||
return;
|
||||
}
|
||||
Object value = row.getValue();
|
||||
if (!(value instanceof TraceObject object)) {
|
||||
return;
|
||||
}
|
||||
activatePath(object.getCanonicalPath());
|
||||
performPathRowDefaultAction(attributesTablePanel.getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -746,19 +777,6 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
}
|
||||
}
|
||||
|
||||
private void activateObjectSelectedInTree() {
|
||||
List<AbstractNode> sel = objectsTreePanel.getSelectedItems();
|
||||
if (sel.size() != 1) {
|
||||
// TODO: Multiple paths? PathMatcher can do it, just have to parse
|
||||
// Just leave whatever was there.
|
||||
return;
|
||||
}
|
||||
TraceObjectValue value = sel.get(0).getValue();
|
||||
if (value != null && value.getValue() instanceof TraceObject child) {
|
||||
activatePath(child.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
if (event != null) {
|
||||
@ -970,26 +988,6 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
|
||||
}
|
||||
}
|
||||
|
||||
protected void activatePath(TraceObjectKeyPath path) {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace != null) {
|
||||
TraceObject object = trace.getObjectManager().getObjectByCanonicalPath(path);
|
||||
if (object != null) {
|
||||
traceManager.activateObject(object);
|
||||
return;
|
||||
}
|
||||
object = trace.getObjectManager()
|
||||
.getObjectsByPath(Lifespan.at(current.getSnap()), path)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (object != null) {
|
||||
traceManager.activateObject(object);
|
||||
return;
|
||||
}
|
||||
plugin.getTool().setStatusInfo("No such object at path " + path, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setPath(TraceObjectKeyPath path, JComponent source, EventOrigin origin) {
|
||||
if (Objects.equals(this.path, path) && getTreeSelection() != null) {
|
||||
return;
|
||||
|
@ -0,0 +1,169 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.model;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JTable;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueProperty;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||
import ghidra.app.plugin.core.debug.gui.model.PathTableModel.PathRow;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.debug.api.model.DebuggerSingleObjectPathActionContext;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.debug.api.target.Target;
|
||||
import ghidra.debug.api.target.Target.ActionEntry;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public interface ObjectDefaultActionsMixin {
|
||||
|
||||
PluginTool getTool();
|
||||
|
||||
DebuggerCoordinates getCurrent();
|
||||
|
||||
void activatePath(TraceObjectKeyPath path);
|
||||
|
||||
default void toggleObject(TraceObject object) {
|
||||
if (!getCurrent().isAliveAndPresent()) {
|
||||
return;
|
||||
}
|
||||
Target target = getCurrent().getTarget();
|
||||
Map<String, ActionEntry> actions = target.collectActions(ActionName.TOGGLE,
|
||||
new DebuggerSingleObjectPathActionContext(object.getCanonicalPath()));
|
||||
ActionEntry action = actions.values()
|
||||
.stream()
|
||||
.filter(e -> !e.requiresPrompt())
|
||||
.sorted(Comparator.comparing(e -> -e.specificity()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (action == null) {
|
||||
Msg.error(this, "No suitable toggle action for " + object);
|
||||
return;
|
||||
}
|
||||
TargetActionTask.runAction(getTool(), "Toggle", action);
|
||||
}
|
||||
|
||||
default void goToAddress(DebuggerListingService listingService, Address address) {
|
||||
ProgramLocation loc = new ProgramLocation(getCurrent().getView(), address);
|
||||
listingService.goTo(loc, true);
|
||||
}
|
||||
|
||||
default void goToAddress(Address address) {
|
||||
DebuggerListingService listingService = getTool().getService(DebuggerListingService.class);
|
||||
if (listingService == null) {
|
||||
return;
|
||||
}
|
||||
goToAddress(listingService, address);
|
||||
}
|
||||
|
||||
default void goToRange(AddressRange range) {
|
||||
DebuggerListingService listingService = getTool().getService(DebuggerListingService.class);
|
||||
if (listingService == null) {
|
||||
return;
|
||||
}
|
||||
listingService.setCurrentSelection(
|
||||
new ProgramSelection(range.getMinAddress(), range.getMaxAddress()));
|
||||
goToAddress(listingService, range.getMinAddress());
|
||||
}
|
||||
|
||||
default boolean performElementCellDefaultAction(JTable table) {
|
||||
int row = table.getSelectedRow();
|
||||
int col = table.getSelectedColumn();
|
||||
Object cellValue = table.getValueAt(row, col);
|
||||
if (cellValue instanceof ValueProperty<?> property) {
|
||||
Object propValue = property.getValue();
|
||||
if (performDefaultAction(propValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean performValueRowDefaultAction(ValueRow row) {
|
||||
if (row == null) {
|
||||
return false;
|
||||
}
|
||||
return performDefaultAction(row.getValue());
|
||||
}
|
||||
|
||||
default boolean performPathRowDefaultAction(PathRow row) {
|
||||
if (row == null) {
|
||||
return false;
|
||||
}
|
||||
return performDefaultAction(row.getValue());
|
||||
}
|
||||
|
||||
default boolean performDefaultAction(TraceObjectValue value) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return performDefaultAction(value.getValue());
|
||||
}
|
||||
|
||||
default boolean performDefaultAction(TraceObject object) {
|
||||
Set<Class<? extends TargetObject>> interfaces = object.getTargetSchema().getInterfaces();
|
||||
if (interfaces.contains(TargetActivatable.class)) {
|
||||
activatePath(object.getCanonicalPath());
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Should I check aliveAndPresent() here? If I do, behavior changes when target is dead,
|
||||
* which might be unexpected.
|
||||
*/
|
||||
if (interfaces.contains(TargetTogglable.class)) {
|
||||
toggleObject(object);
|
||||
return true;
|
||||
}
|
||||
long snap = getCurrent().getSnap();
|
||||
TraceObjectValue valAddress = object.getAttribute(snap, "_address");
|
||||
if (valAddress != null && valAddress.getValue() instanceof Address address) {
|
||||
goToAddress(address);
|
||||
return true;
|
||||
}
|
||||
TraceObjectValue valRange = object.getAttribute(snap, "_range");
|
||||
if (valRange != null && valRange.getValue() instanceof AddressRange range) {
|
||||
goToRange(range);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean performDefaultAction(Object value) {
|
||||
if (value instanceof Address address) {
|
||||
goToAddress(address);
|
||||
return true;
|
||||
}
|
||||
if (value instanceof AddressRange range) {
|
||||
goToRange(range);
|
||||
return true;
|
||||
}
|
||||
if (value instanceof TraceObject object) {
|
||||
return performDefaultAction(object);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -69,7 +69,7 @@ public class ObjectsTablePanel extends AbstractQueryTablePanel<ValueRow, ObjectT
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new ObjectTableModel(plugin);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ public class PathsTablePanel extends AbstractQueryTablePanel<PathRow, PathTableM
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathTableModel createModel(Plugin plugin) {
|
||||
protected PathTableModel createModel() {
|
||||
return new PathTableModel(plugin);
|
||||
}
|
||||
}
|
||||
|
@ -229,8 +229,8 @@ public class DebuggerLegacyModulesPanel extends JPanel {
|
||||
int selectedRow = moduleTable.getSelectedRow();
|
||||
int selectedColumn = moduleTable.getSelectedColumn();
|
||||
Object value = moduleTable.getValueAt(selectedRow, selectedColumn);
|
||||
if (value instanceof Address) {
|
||||
provider.listingService.goTo((Address) value, true);
|
||||
if (value instanceof Address address) {
|
||||
provider.listingService.goTo(address, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,8 +273,8 @@ public class DebuggerLegacySectionsPanel extends JPanel {
|
||||
int selectedRow = sectionTable.getSelectedRow();
|
||||
int selectedColumn = sectionTable.getSelectedColumn();
|
||||
Object value = sectionTable.getValueAt(selectedRow, selectedColumn);
|
||||
if (value instanceof Address) {
|
||||
provider.listingService.goTo((Address) value, true);
|
||||
if (value instanceof Address address) {
|
||||
provider.listingService.goTo(address, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ public class DebuggerModulesPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new ModuleTableModel(plugin);
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,7 @@ public class DebuggerSectionsPanel extends AbstractObjectsTableBasedPanel<TraceO
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new SectionTableModel(plugin);
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.listing.*;
|
||||
@ -639,7 +640,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||
if (listingService == null) {
|
||||
return;
|
||||
}
|
||||
listingService.goTo(address, true);
|
||||
ProgramLocation loc = new ProgramLocation(current.getView(), address);
|
||||
listingService.goTo(loc, true);
|
||||
}).build());
|
||||
}
|
||||
return result;
|
||||
@ -658,7 +660,8 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
listingService.goTo(address, true);
|
||||
ProgramLocation loc = new ProgramLocation(current.getView(), address);
|
||||
listingService.goTo(loc, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,7 +23,6 @@ import javax.swing.event.ListSelectionListener;
|
||||
import docking.widgets.table.AbstractDynamicTableColumn;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
import ghidra.app.plugin.core.debug.gui.model.*;
|
||||
import ghidra.app.plugin.core.debug.gui.model.AbstractQueryTablePanel.CellActivationListener;
|
||||
import ghidra.app.plugin.core.debug.gui.model.ObjectTableModel.ValueRow;
|
||||
import ghidra.app.plugin.core.debug.gui.model.columns.TraceValueKeyColumn;
|
||||
import ghidra.app.plugin.core.debug.gui.model.columns.TraceValueObjectAttributeColumn;
|
||||
@ -46,7 +45,7 @@ import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
|
||||
public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObjectStackFrame>
|
||||
implements ListSelectionListener, CellActivationListener {
|
||||
implements ListSelectionListener {
|
||||
|
||||
private static class FrameLevelColumn extends TraceValueKeyColumn {
|
||||
@Override
|
||||
@ -137,7 +136,7 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new StackTableModel(plugin);
|
||||
}
|
||||
|
||||
@ -165,7 +164,11 @@ public class DebuggerStackPanel extends AbstractObjectsTableBasedPanel<TraceObje
|
||||
|
||||
@Override
|
||||
public void cellActivated(JTable table) {
|
||||
// No super
|
||||
/**
|
||||
* Override, because PC columns is fairly wide and representative of the stack frame.
|
||||
* Likely, when the user double-clicks, they mean to activate the frame, even if it happens
|
||||
* to be in that column. Simply going to the address will confuse and/or disappoint.
|
||||
*/
|
||||
ValueRow item = getSelectedItem();
|
||||
if (item != null) {
|
||||
traceManager.activateObject(item.getValue().getChild());
|
||||
|
@ -17,7 +17,6 @@ package ghidra.app.plugin.core.debug.gui.thread;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
|
||||
import docking.widgets.table.AbstractDynamicTableColumn;
|
||||
@ -258,7 +257,7 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectTableModel createModel(Plugin plugin) {
|
||||
protected ObjectTableModel createModel() {
|
||||
return new ThreadTableModel(plugin);
|
||||
}
|
||||
|
||||
@ -293,15 +292,6 @@ public class DebuggerThreadsPanel extends AbstractObjectsTableBasedPanel<TraceOb
|
||||
trySelectCurrentThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cellActivated(JTable table) {
|
||||
// No super
|
||||
ValueRow item = getSelectedItem();
|
||||
if (item != null) {
|
||||
traceManager.activateObject(item.getValue().getChild());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
super.valueChanged(e);
|
||||
|
@ -483,7 +483,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter
|
||||
return;
|
||||
}
|
||||
if (address.isMemoryAddress()) {
|
||||
listingService.goTo(address, true);
|
||||
ProgramLocation loc = new ProgramLocation(current.getView(), address);
|
||||
listingService.goTo(loc, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -370,6 +370,14 @@ public class TraceRecorderTarget extends AbstractTarget {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, ActionEntry> collectToggleActions(ActionContext context) {
|
||||
return collectIfaceActions(context, TargetTogglable.class, "Toggle",
|
||||
ActionName.TOGGLE, "Toggle the object",
|
||||
togglable -> true,
|
||||
togglable -> togglable.toggle(!togglable.isEnabled()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return recorder.getTrace();
|
||||
|
@ -198,7 +198,8 @@ public abstract class AbstractTarget implements Target {
|
||||
collectStepOverActions(context),
|
||||
collectStepOutActions(context),
|
||||
collectStepExtActions(context),
|
||||
collectRefreshActions(context))
|
||||
collectRefreshActions(context),
|
||||
collectToggleActions(context))
|
||||
.flatMap(m -> m.entrySet().stream())
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
@ -219,6 +220,8 @@ public abstract class AbstractTarget implements Target {
|
||||
|
||||
protected abstract Map<String, ActionEntry> collectRefreshActions(ActionContext context);
|
||||
|
||||
protected abstract Map<String, ActionEntry> collectToggleActions(ActionContext context);
|
||||
|
||||
@Override
|
||||
public Map<String, ActionEntry> collectActions(ActionName name, ActionContext context) {
|
||||
if (name == null) {
|
||||
@ -251,6 +254,9 @@ public abstract class AbstractTarget implements Target {
|
||||
else if (ActionName.REFRESH.equals(name)) {
|
||||
return collectRefreshActions(context);
|
||||
}
|
||||
else if (ActionName.TOGGLE.equals(name)) {
|
||||
return collectToggleActions(context);
|
||||
}
|
||||
Msg.warn(this, "Unrecognized action name: " + name);
|
||||
return Map.of();
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.dbg.target;
|
||||
|
||||
import ghidra.dbg.DebuggerTargetObjectIface;
|
||||
|
||||
/**
|
||||
* An object which can be activated
|
||||
*
|
||||
* <p>
|
||||
* Activation generally means to become the active, selected or focused object. Subsequent commands
|
||||
* to the debugger implicitly apply to this object. For example, if a user activates a thread, then
|
||||
* subsequent register read/write commands ought to affect the active thread's context.
|
||||
*
|
||||
* <p>
|
||||
* This interface is only used by RMI targets. The back end must register a suitable method so that
|
||||
* the front end can notify it when the user has activated this object. Generally, a user activates
|
||||
* the object by double-clicking it in the appropriate table or tree. If it is <em>not</em> marked
|
||||
* with this interface, the UI will ignore the action. If it is, the UI will mark it the active
|
||||
* object and invoke the appropriate target method. If this interface is present, but a suitable
|
||||
* method is not, an error is logged upon attempted activation.
|
||||
*
|
||||
* <p>
|
||||
* We cannot just use the presence or absence of a suitable activation method as a proxy for this
|
||||
* interface, because the registry is only available when the back end is alive.
|
||||
*/
|
||||
@DebuggerTargetObjectIface("Activatable")
|
||||
public interface TargetActivatable extends TargetObject {
|
||||
// No methods
|
||||
}
|
@ -105,8 +105,7 @@ import ghidra.lifecycle.Internal;
|
||||
* <ul>
|
||||
* <li>"Threads" : {@link TargetObject}</li>
|
||||
* <ul>
|
||||
* <li>"Thread 1" : {@link TargetExecutionStateful}, {@link TargetSingleSteppable},
|
||||
* {@link TargetMultiSteppable}</li>
|
||||
* <li>"Thread 1" : {@link TargetExecutionStateful}, {@link TargetSteppable}</li>
|
||||
* <ul>
|
||||
* <li>"Registers" : {@link TargetRegisterBank}</li>
|
||||
* <ul>
|
||||
@ -167,15 +166,16 @@ import ghidra.lifecycle.Internal;
|
||||
public interface TargetObject extends Comparable<TargetObject> {
|
||||
|
||||
Set<Class<? extends TargetObject>> ALL_INTERFACES =
|
||||
Set.of(TargetAccessConditioned.class, TargetActiveScope.class, TargetAggregate.class,
|
||||
TargetAttachable.class, TargetAttacher.class, TargetBreakpointLocation.class,
|
||||
TargetBreakpointLocationContainer.class, TargetBreakpointSpec.class,
|
||||
TargetBreakpointSpecContainer.class, TargetConfigurable.class, TargetConsole.class,
|
||||
TargetDataTypeMember.class, TargetDataTypeNamespace.class, TargetDeletable.class,
|
||||
TargetDetachable.class, TargetEnvironment.class, TargetEventScope.class,
|
||||
TargetExecutionStateful.class, TargetFocusScope.class, TargetInterpreter.class,
|
||||
TargetInterruptible.class, TargetKillable.class, TargetLauncher.class,
|
||||
TargetMemory.class, TargetMemoryRegion.class, TargetMethod.class, TargetModule.class,
|
||||
Set.of(TargetAccessConditioned.class, TargetActivatable.class, TargetActiveScope.class,
|
||||
TargetAggregate.class, TargetAttachable.class, TargetAttacher.class,
|
||||
TargetBreakpointLocation.class, TargetBreakpointLocationContainer.class,
|
||||
TargetBreakpointSpec.class, TargetBreakpointSpecContainer.class,
|
||||
TargetConfigurable.class, TargetConsole.class, TargetDataTypeMember.class,
|
||||
TargetDataTypeNamespace.class, TargetDeletable.class, TargetDetachable.class,
|
||||
TargetEnvironment.class, TargetEventScope.class, TargetExecutionStateful.class,
|
||||
TargetFocusScope.class, TargetInterpreter.class, TargetInterruptible.class,
|
||||
TargetKillable.class, TargetLauncher.class, TargetMemory.class,
|
||||
TargetMemoryRegion.class, TargetMethod.class, TargetModule.class,
|
||||
TargetModuleContainer.class, TargetNamedDataType.class, TargetProcess.class,
|
||||
TargetRegister.class, TargetRegisterBank.class, TargetRegisterContainer.class,
|
||||
TargetResumable.class, TargetSection.class, TargetSectionContainer.class,
|
||||
@ -472,7 +472,7 @@ public interface TargetObject extends Comparable<TargetObject> {
|
||||
* <p>
|
||||
* This is an informal notion of type and may only be used for visual styling, logging, or other
|
||||
* informational purposes. Scripts should not rely on this to predict behavior, but instead on
|
||||
* {@link #getAs(Class)}, {@link #getInterfaces()}, or {@link #getSchema()}.
|
||||
* {@link #as(Class)}, {@link #getInterfaces()}, or {@link #getSchema()}.
|
||||
*
|
||||
* @return an informal name of this object's type
|
||||
*/
|
||||
@ -516,10 +516,11 @@ public interface TargetObject extends Comparable<TargetObject> {
|
||||
*
|
||||
* <p>
|
||||
* In general, an invalid object should be disposed by the user immediately on discovering it is
|
||||
* invalid. See {@link DebuggerModelListener#invalidated(TargetObject)} for a means of reacting
|
||||
* to object invalidation. Nevertheless, it is acceptable to access stale attributes and element
|
||||
* keys, for informational purposes only. Implementors must reject all commands, including
|
||||
* fetches, on an invalid object by throwing an {@link IllegalStateException}.
|
||||
* invalid. See {@link DebuggerModelListener#invalidated(TargetObject, TargetObject, String)}
|
||||
* for a means of reacting to object invalidation. Nevertheless, it is acceptable to access
|
||||
* stale attributes and element keys, for informational purposes only. Implementors must reject
|
||||
* all commands, including fetches, on an invalid object by throwing an
|
||||
* {@link IllegalStateException}.
|
||||
*
|
||||
* @return true if valid, false if invalid
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user