diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
index 214c4631e1..c7df85f76e 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/ActionName.java
@@ -183,6 +183,7 @@ public record ActionName(String name, Show show, Enabler enabler, String display
public static final ActionName REFRESH =
create("refresh", Show.EXTENDED, Enabler.ALWAYS, "Refresh", ICON_REFRESH, "Refresh");
+
/**
* Activate a given object and optionally a time
*
@@ -191,6 +192,7 @@ public record ActionName(String name, Show show, Enabler enabler, String display
*/
public static final ActionName ACTIVATE =
create("activate", Show.BUILTIN, Enabler.ALWAYS, "Activate", null, "Activate");
+
/**
* A weaker form of activate.
*
@@ -262,6 +264,7 @@ public record ActionName(String name, Show show, Enabler enabler, String display
create("step_over", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Over", ICON_STEP_OVER, "Step");
public static final ActionName STEP_OUT =
create("step_out", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Out", ICON_STEP_OUT, "Step");
+
/**
* Skip is not typically available, except in emulators. If the back-end debugger does not have
* a command for this action out-of-the-box, we do not recommend trying to implement it
@@ -270,12 +273,14 @@ public record ActionName(String name, Show show, Enabler enabler, String display
*/
public static final ActionName STEP_SKIP =
create("step_skip", Show.BUILTIN, Enabler.NOT_RUNNING, "Skip Over", ICON_SKIP_OVER, "Skip");
+
/**
* Step back is not typically available, except in emulators and timeless (or time-travel)
* debuggers.
*/
public static final ActionName STEP_BACK =
create("step_back", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Back", ICON_STEP_BACK, "Back");
+
/**
* The action for steps that don't fit one of the common stepping actions.
*/
@@ -315,6 +320,7 @@ public record ActionName(String name, Show show, Enabler enabler, String display
*/
public static final ActionName READ_MEM =
create("read_mem", Show.BUILTIN, Enabler.ALWAYS, "Read Memory", null, "Read");
+
/**
* Forms: (addr:ADDRESS,data:BYTES)
*/
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
index 4f6eb5e6cd..cca206f874 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/target/Target.java
@@ -181,6 +181,78 @@ public interface Target {
}
}
+ /**
+ * Specifies how object arguments are derived
+ */
+ public enum ObjectArgumentPolicy {
+ /**
+ * The object should be taken exactly from the action context, if applicable, present, and
+ * matching in schema.
+ */
+ CONTEXT_ONLY {
+ @Override
+ public boolean allowContextObject() {
+ return true;
+ }
+
+ @Override
+ public boolean allowCoordsObject() {
+ return false;
+ }
+
+ @Override
+ public boolean allowSuitableRelative() {
+ return false;
+ }
+ },
+ /**
+ * The object should be taken from the current (active) object in the tool, or a suitable
+ * relative having the correct schema.
+ */
+ CURRENT_AND_RELATED {
+ @Override
+ public boolean allowContextObject() {
+ return false;
+ }
+
+ @Override
+ public boolean allowCoordsObject() {
+ return true;
+ }
+
+ @Override
+ public boolean allowSuitableRelative() {
+ return true;
+ }
+ },
+ /**
+ * The object can be taken from the given context, or the current (active) object in the
+ * tool, or a suitable relative having the correct schema.
+ */
+ EITHER_AND_RELATED {
+ @Override
+ public boolean allowContextObject() {
+ return true;
+ }
+
+ @Override
+ public boolean allowCoordsObject() {
+ return true;
+ }
+
+ @Override
+ public boolean allowSuitableRelative() {
+ return true;
+ }
+ };
+
+ public abstract boolean allowContextObject();
+
+ public abstract boolean allowCoordsObject();
+
+ public abstract boolean allowSuitableRelative();
+ }
+
/**
* Describe the target for display in the UI
*
@@ -216,11 +288,17 @@ public interface Target {
/**
* Collect all actions that implement the given common debugger command
*
+ *
+ * Note that if the context provides a program location (i.e., address), the object policy is
+ * ignored. It will use current and related objects.
+ *
* @param name the action name
* @param context applicable context from the UI
+ * @param policy determines how objects may be found
* @return the collected actions
*/
- Map collectActions(ActionName name, ActionContext context);
+ Map collectActions(ActionName name, ActionContext context,
+ ObjectArgumentPolicy policy);
/**
* @see #execute(String, boolean)
diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
index 2f8a88eff0..06e7c2821f 100644
--- a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java
@@ -34,6 +34,7 @@ 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.target.Target.ObjectArgumentPolicy;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener;
@@ -1473,7 +1474,7 @@ public interface FlatDebuggerAPI {
}
default ActionEntry findAction(Target target, ActionName action, ActionContext context) {
- return target.collectActions(action, context)
+ return target.collectActions(action, context, ObjectArgumentPolicy.EITHER_AND_RELATED)
.values()
.stream()
.filter(e -> !e.requiresPrompt())
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java
index 0254bf5b13..60179b73b6 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java
@@ -56,6 +56,7 @@ import ghidra.trace.model.target.info.TraceObjectInterfaceUtils;
import ghidra.trace.model.target.path.*;
import ghidra.trace.model.target.path.PathFilter.Align;
import ghidra.trace.model.target.schema.*;
+import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema.MinimalSchemaContext;
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
import ghidra.trace.model.thread.*;
import ghidra.util.Msg;
@@ -75,7 +76,7 @@ public class TraceRmiTarget extends AbstractTarget {
this.args = args;
this.requiresPrompt = args.values().contains(Missing.MISSING);
- this.specificity = computeSpecificity(args);
+ this.specificity = computeSpecificity(method, args);
this.first = getFirstObjectArgument(method, args);
}
@@ -197,9 +198,8 @@ public class TraceRmiTarget extends AbstractTarget {
.orElse(null);
}
- protected TraceObject findObject(ActionContext context, boolean allowContextObject,
- boolean allowCoordsObject) {
- if (allowContextObject) {
+ protected TraceObject findObject(ActionContext context, ObjectArgumentPolicy policy) {
+ if (policy.allowContextObject()) {
if (context instanceof DebuggerObjectActionContext ctx) {
List values = ctx.getObjectValues();
if (values.size() == 1) {
@@ -224,7 +224,7 @@ public class TraceRmiTarget extends AbstractTarget {
}
}
}
- if (allowCoordsObject) {
+ if (policy.allowCoordsObject()) {
DebuggerTraceManagerService traceManager =
tool.getService(DebuggerTraceManagerService.class);
if (traceManager == null) {
@@ -246,16 +246,15 @@ public class TraceRmiTarget extends AbstractTarget {
* @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
+ * @param policy how object arguments can be found
* @return a value if found, null if not
*/
- protected Boolean findBool(ActionName action, ActionContext context, boolean allowContextObject,
- boolean allowCoordsObject) {
+ protected Boolean findBool(ActionName action, ActionContext context,
+ ObjectArgumentPolicy policy) {
if (!Objects.equals(action, ActionName.TOGGLE)) {
return null;
}
- TraceObject object = findObject(context, allowContextObject, allowCoordsObject);
+ TraceObject object = findObject(context, policy);
if (object == null) {
return null;
}
@@ -266,22 +265,21 @@ public class TraceRmiTarget extends AbstractTarget {
}
protected Object findArgumentForSchema(ActionName action, ActionContext context,
- TraceObjectSchema schema, boolean allowContextObject, boolean allowCoordsObject,
- boolean allowSuitableObject) {
+ TraceObjectSchema schema, ObjectArgumentPolicy policy) {
if (schema instanceof PrimitiveTraceObjectSchema prim) {
return switch (prim) {
- case OBJECT -> findObject(context, allowContextObject, allowCoordsObject);
+ case OBJECT -> findObject(context, policy);
case ADDRESS -> findAddress(context);
case RANGE -> findRange(context);
- case BOOL -> findBool(action, context, allowContextObject, allowCoordsObject);
+ case BOOL -> findBool(action, context, policy);
default -> null;
};
}
- TraceObject object = findObject(context, allowContextObject, allowCoordsObject);
+ TraceObject object = findObject(context, policy);
if (object == null) {
return null;
}
- if (allowSuitableObject) {
+ if (policy.allowSuitableRelative()) {
return object.findSuitableSchema(schema);
}
if (object.getSchema() == schema) {
@@ -299,8 +297,7 @@ public class TraceRmiTarget extends AbstractTarget {
}
protected Object findArgument(ActionName action, RemoteParameter parameter,
- ActionContext context, boolean allowContextObject, boolean allowCoordsObject,
- boolean allowSuitableObject) {
+ ActionContext context, ObjectArgumentPolicy policy) {
SchemaName type = parameter.type();
SchemaContext ctx = getSchemaContext();
if (ctx == null) {
@@ -312,8 +309,7 @@ public class TraceRmiTarget extends AbstractTarget {
Msg.error(this, "Schema " + type + " not in trace! " + trace);
return null;
}
- Object arg = findArgumentForSchema(action, context, schema, allowContextObject,
- allowCoordsObject, allowSuitableObject);
+ Object arg = findArgumentForSchema(action, context, schema, policy);
if (arg != null) {
return arg;
}
@@ -324,11 +320,10 @@ public class TraceRmiTarget extends AbstractTarget {
}
protected Map collectArguments(RemoteMethod method, ActionContext context,
- boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
+ ObjectArgumentPolicy policy) {
Map args = new HashMap<>();
for (RemoteParameter param : method.parameters().values()) {
- Object found = findArgument(method.action(), param, context, allowContextObject,
- allowCoordsObject, allowSuitableObject);
+ Object found = findArgument(method.action(), param, context, policy);
if (found != null) {
args.put(param.name(), found);
}
@@ -336,8 +331,36 @@ public class TraceRmiTarget extends AbstractTarget {
return args;
}
- protected static long computeSpecificity(Map args) {
+ /**
+ * Compute the specificity of the entry.
+ *
+ * More specific is generally preferred. There are two sorts of specificity here. 1) The
+ * specificity of the methods formal parameters. A parameter having a non-primitive schema is
+ * more specific than one having an ANY or OBJECT schema. 2) The specificity of the objects
+ * selected as arguments. This is crudely computed as the length of the canonical path.
+ *
+ * @param method the method
+ * @param args the arguments
+ * @return the specificity
+ */
+ protected static long computeSpecificity(RemoteMethod method, Map args) {
long score = 0;
+ for (RemoteParameter param : method.parameters().values()) {
+ score += switch (MinimalSchemaContext.INSTANCE.getSchemaOrNull(param.type())) {
+ case PrimitiveTraceObjectSchema prim -> switch (prim) {
+ case ANY -> 0; // Absolutely not specific
+ case OBJECT -> 1; // well, it is better than ANY
+ default -> 2; // real primitives
+ };
+ /**
+ * Because we're using the "minimal" schema, not the actual one, anything
+ * user-defined will be null.
+ */
+ case null -> 100;
+ default -> 100;
+ };
+ }
+ score *= 1000;
for (Object o : args.values()) {
if (o instanceof TraceObject obj) {
score += obj.getCanonicalPath().size();
@@ -408,19 +431,16 @@ public class TraceRmiTarget extends AbstractTarget {
}
protected ActionEntry createEntry(RemoteMethod method, ActionContext context,
- boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
- Map args = collectArguments(method, context, allowContextObject,
- allowCoordsObject, allowSuitableObject);
+ ObjectArgumentPolicy policy) {
+ Map args = collectArguments(method, context, policy);
return new TraceRmiActionEntry(method, args);
}
protected Map collectFromMethods(Collection methods,
- ActionContext context, boolean allowContextObject, boolean allowCoordsObject,
- boolean allowSuitableObject) {
+ ActionContext context, ObjectArgumentPolicy policy) {
Map result = new HashMap<>();
for (RemoteMethod m : methods) {
- ActionEntry entry = createEntry(m, context, allowContextObject, allowCoordsObject,
- allowSuitableObject);
+ ActionEntry entry = createEntry(m, context, policy);
result.put(m.name(), entry);
}
return result;
@@ -442,7 +462,6 @@ public class TraceRmiTarget extends AbstractTarget {
.count() == 1;
}
- @Override
protected Map collectAddressActions(ProgramLocationActionContext context) {
SchemaContext ctx = getSchemaContext();
Map result = new HashMap<>();
@@ -450,78 +469,31 @@ public class TraceRmiTarget extends AbstractTarget {
if (!isAddressMethod(m, ctx)) {
continue;
}
- result.put(m.name(), createEntry(m, context, true, true, true));
+ result.put(m.name(), createEntry(m, context, ObjectArgumentPolicy.CURRENT_AND_RELATED));
}
return result;
}
- @Override
- protected Map collectAllActions(ActionContext context) {
- return collectFromMethods(connection.getMethods().all().values(), context, true, false,
- false);
+ protected Map collectAllActions(ActionContext context,
+ ObjectArgumentPolicy policy) {
+ return collectFromMethods(connection.getMethods().all().values(), context, policy);
}
- protected Map collectByName(ActionName name, ActionContext context) {
- return collectFromMethods(connection.getMethods().getByAction(name), context, false, true,
- true);
+ protected Map collectByName(ActionName name, ActionContext context,
+ ObjectArgumentPolicy policy) {
+ return collectFromMethods(connection.getMethods().getByAction(name), context, policy);
}
@Override
- public Map collectActions(ActionName name, ActionContext context) {
+ public Map collectActions(ActionName name, ActionContext context,
+ ObjectArgumentPolicy policy) {
if (name == null) {
if (context instanceof ProgramLocationActionContext ctx) {
return collectAddressActions(ctx);
}
- return collectAllActions(context);
+ return collectAllActions(context, policy);
}
- return collectByName(name, context);
- }
-
- @Override
- protected Map collectResumeActions(ActionContext context) {
- return collectByName(ActionName.RESUME, context);
- }
-
- @Override
- protected Map collectInterruptActions(ActionContext context) {
- return collectByName(ActionName.INTERRUPT, context);
- }
-
- @Override
- protected Map collectKillActions(ActionContext context) {
- return collectByName(ActionName.KILL, context);
- }
-
- @Override
- protected Map collectStepIntoActions(ActionContext context) {
- return collectByName(ActionName.STEP_INTO, context);
- }
-
- @Override
- protected Map collectStepOverActions(ActionContext context) {
- return collectByName(ActionName.STEP_OVER, context);
- }
-
- @Override
- protected Map collectStepOutActions(ActionContext context) {
- return collectByName(ActionName.STEP_OUT, context);
- }
-
- @Override
- protected Map collectStepExtActions(ActionContext context) {
- return collectByName(ActionName.STEP_EXT, context);
- }
-
- @Override
- protected Map collectRefreshActions(ActionContext context) {
- return collectFromMethods(connection.getMethods().getByAction(ActionName.REFRESH), context,
- true, false, false);
- }
-
- @Override
- protected Map collectToggleActions(ActionContext context) {
- return collectFromMethods(connection.getMethods().getByAction(ActionName.TOGGLE), context,
- true, false, false);
+ return collectByName(name, context, policy);
}
@Override
@@ -1420,7 +1392,8 @@ public class TraceRmiTarget extends AbstractTarget {
RemoteParameter paramProc = brk.params.get("process");
if (paramProc != null) {
Object proc = findArgumentForSchema(null, null,
- getSchemaContext().getSchema(paramProc.type()), true, true, true);
+ getSchemaContext().getSchema(paramProc.type()),
+ ObjectArgumentPolicy.CURRENT_AND_RELATED);
if (proc == null) {
Msg.error(this, "Cannot find required process argument for " + brk.method);
}
@@ -1675,7 +1648,8 @@ public class TraceRmiTarget extends AbstractTarget {
@Override
public CompletableFuture forceTerminateAsync() {
- Map kills = collectKillActions(null);
+ Map kills =
+ collectByName(ActionName.KILL, null, ObjectArgumentPolicy.CURRENT_AND_RELATED);
for (ActionEntry kill : kills.values()) {
if (kill.requiresPrompt()) {
continue;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
index c478f6606e..7a14b7309f 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/breakpoint/DebuggerBreakpointsProvider.java
@@ -46,6 +46,7 @@ import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
+import ghidra.debug.api.target.Target.ObjectArgumentPolicy;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.model.DomainObjectEvent;
import ghidra.framework.plugintool.*;
@@ -327,7 +328,9 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
return stub;
}
List result = new ArrayList<>();
- for (ActionEntry entry : target.collectActions(ActionName.BREAK_EXT, context)
+ for (ActionEntry entry : target
+ .collectActions(ActionName.BREAK_EXT, context,
+ ObjectArgumentPolicy.CURRENT_AND_RELATED)
.values()) {
result.add(new GenericSetBreakpointAction(entry));
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
index 68db4d689e..6be2c952c7 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerControlPlugin.java
@@ -42,6 +42,7 @@ import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
+import ghidra.debug.api.target.Target.ObjectArgumentPolicy;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
@@ -282,7 +283,10 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
}
protected void addTargetStepExtActions(Target target) {
- for (ActionEntry entry : target.collectActions(ActionName.STEP_EXT, context).values()) {
+ for (ActionEntry entry : target
+ .collectActions(ActionName.STEP_EXT, context,
+ ObjectArgumentPolicy.CURRENT_AND_RELATED)
+ .values()) {
if (entry.requiresPrompt()) {
continue;
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
index a3074a91c3..5384bfbfa1 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/DebuggerMethodActionsPlugin.java
@@ -33,6 +33,7 @@ import ghidra.debug.api.control.ControlMode;
import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
+import ghidra.debug.api.target.Target.ObjectArgumentPolicy;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
@@ -119,7 +120,9 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
}
List result = new ArrayList<>();
- for (ActionEntry entry : target.collectActions(null, context).values()) {
+ for (ActionEntry entry : target
+ .collectActions(null, context, ObjectArgumentPolicy.CONTEXT_ONLY)
+ .values()) {
//if (entry.requiresPrompt() || entry.builtIn()) {
if (!entry.isEnabled() || !entry.getShow().isShowing(context)) {
continue;
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetDockingAction.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetDockingAction.java
index 72efd87cf7..5e0e45eebb 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetDockingAction.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/control/TargetDockingAction.java
@@ -24,6 +24,7 @@ import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
+import ghidra.debug.api.target.Target.ObjectArgumentPolicy;
import ghidra.framework.plugintool.PluginTool;
class TargetDockingAction extends DockingAction {
@@ -51,7 +52,7 @@ class TargetDockingAction extends DockingAction {
if (target == null) {
return null;
}
- return target.collectActions(action, context)
+ return target.collectActions(action, context, ObjectArgumentPolicy.CURRENT_AND_RELATED)
.values()
.stream()
.filter(e -> !e.requiresPrompt())
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
index 1882b11d66..755f69934b 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/DebuggerModelProvider.java
@@ -55,6 +55,7 @@ import ghidra.debug.api.model.DebuggerObjectActionContext;
import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.Target.ActionEntry;
+import ghidra.debug.api.target.Target.ObjectArgumentPolicy;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
@@ -767,30 +768,34 @@ public class DebuggerModelProvider extends ComponentProvider implements Saveable
if (target == null) {
return;
}
- Map actions = target.collectActions(ActionName.REFRESH,
+ ActionEntry ent = target.collectActions(ActionName.REFRESH,
new DebuggerObjectActionContext(List.of(value), this, objectsTreePanel,
- current.getSnap()));
- for (ActionEntry ent : actions.values()) {
- if (ent.requiresPrompt()) {
- continue;
- }
- if (path.getLastPathComponent() instanceof AbstractNode node) {
- /**
- * This pending node does not duplicate what the lazy node already does. For all
- * it's concerned, once it has loaded the entries from the database, it is done.
- * This task asks the target to update that database, so it needs its own indicator.
- */
- PendingNode pending = new PendingNode();
- node.addNode(0, pending);
- CompletableFuture future =
- TargetActionTask.runAction(plugin.getTool(), ent.display(), ent);
- future.handle((__, ex) -> {
- node.removeNode(pending);
- return null;
- });
- return;
- }
+ current.getSnap()),
+ ObjectArgumentPolicy.CONTEXT_ONLY)
+ .values()
+ .stream()
+ .filter(e -> !e.requiresPrompt())
+ .sorted(Comparator.comparing(e -> -e.specificity()))
+ .findFirst()
+ .orElse(null);
+ if (ent == null) {
+ // Fail silently. It's common for nodes to not have a refresh action.
+ return;
}
+ AbstractNode node = (AbstractNode) path.getLastPathComponent();
+ /**
+ * This pending node does not duplicate what the lazy node already does. For all it's
+ * concerned, once it has loaded the entries from the database, it is done. This task asks
+ * the target to update that database, so it needs its own indicator.
+ */
+ PendingNode pending = new PendingNode();
+ node.addNode(0, pending);
+ CompletableFuture future =
+ TargetActionTask.runAction(plugin.getTool(), ent.display(), ent);
+ future.handle((__, ex) -> {
+ node.removeNode(pending);
+ return null;
+ });
}
@Override
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectDefaultActionsMixin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectDefaultActionsMixin.java
index d30c495253..41e5b5ff49 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectDefaultActionsMixin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/model/ObjectDefaultActionsMixin.java
@@ -28,13 +28,15 @@ 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.target.Target.ObjectArgumentPolicy;
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.trace.model.target.TraceObject;
+import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.model.target.iface.*;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.util.Msg;
@@ -53,7 +55,8 @@ public interface ObjectDefaultActionsMixin {
}
Target target = getCurrent().getTarget();
Map actions = target.collectActions(ActionName.TOGGLE,
- new DebuggerSingleObjectPathActionContext(object.getCanonicalPath()));
+ new DebuggerSingleObjectPathActionContext(object.getCanonicalPath()),
+ ObjectArgumentPolicy.CONTEXT_ONLY);
ActionEntry action = actions.values()
.stream()
.filter(e -> !e.requiresPrompt())
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
index e44852ec96..60b9c2e449 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/target/AbstractTarget.java
@@ -4,9 +4,9 @@
* 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.
@@ -19,17 +19,13 @@ import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import docking.ActionContext;
import ghidra.app.context.NavigatableActionContext;
-import ghidra.app.context.ProgramLocationActionContext;
import ghidra.app.nav.Navigatable;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.async.AsyncUtils;
-import ghidra.debug.api.target.ActionName;
import ghidra.debug.api.target.Target;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.framework.plugintool.PluginTool;
@@ -46,7 +42,6 @@ import ghidra.trace.model.guest.TracePlatform;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.TraceRegisterUtils;
-import ghidra.util.Msg;
import ghidra.util.Swing;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -186,81 +181,6 @@ public abstract class AbstractTarget implements Target {
return null;
}
- protected abstract Map collectAddressActions(
- ProgramLocationActionContext context);
-
- protected Map collectAllActions(ActionContext context) {
- return Stream.of(
- collectResumeActions(context),
- collectInterruptActions(context),
- collectKillActions(context),
- collectStepIntoActions(context),
- collectStepOverActions(context),
- collectStepOutActions(context),
- collectStepExtActions(context),
- collectRefreshActions(context),
- collectToggleActions(context))
- .flatMap(m -> m.entrySet().stream())
- .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
- }
-
- protected abstract Map collectResumeActions(ActionContext context);
-
- protected abstract Map collectInterruptActions(ActionContext context);
-
- protected abstract Map collectKillActions(ActionContext context);
-
- protected abstract Map collectStepIntoActions(ActionContext context);
-
- protected abstract Map collectStepOverActions(ActionContext context);
-
- protected abstract Map collectStepOutActions(ActionContext context);
-
- protected abstract Map collectStepExtActions(ActionContext context);
-
- protected abstract Map collectRefreshActions(ActionContext context);
-
- protected abstract Map collectToggleActions(ActionContext context);
-
- @Override
- public Map collectActions(ActionName name, ActionContext context) {
- if (name == null) {
- if (context instanceof ProgramLocationActionContext ctx) {
- return collectAddressActions(ctx);
- }
- return collectAllActions(context);
- }
- else if (ActionName.RESUME.equals(name)) {
- return collectResumeActions(context);
- }
- else if (ActionName.INTERRUPT.equals(name)) {
- return collectInterruptActions(context);
- }
- else if (ActionName.KILL.equals(name)) {
- return collectKillActions(context);
- }
- else if (ActionName.STEP_INTO.equals(name)) {
- return collectStepIntoActions(context);
- }
- else if (ActionName.STEP_OVER.equals(name)) {
- return collectStepOverActions(context);
- }
- else if (ActionName.STEP_OUT.equals(name)) {
- return collectStepOutActions(context);
- }
- else if (ActionName.STEP_EXT.equals(name)) {
- return collectStepExtActions(context);
- }
- 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();
- }
-
protected static T doSync(String name, Supplier> supplier)
throws InterruptedException, ExecutionException {
if (Swing.isSwingThread()) {
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/MockTarget.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/MockTarget.java
index ffc8d50c0a..a37e6479b2 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/MockTarget.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/MockTarget.java
@@ -27,8 +27,8 @@ import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
-import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.Trace;
+import ghidra.trace.model.TraceExecutionState;
import ghidra.trace.model.breakpoint.TraceBreakpoint;
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.guest.TracePlatform;
@@ -71,7 +71,8 @@ public class MockTarget implements Target {
}
@Override
- public Map collectActions(ActionName name, ActionContext context) {
+ public Map collectActions(ActionName name, ActionContext context,
+ ObjectArgumentPolicy policy) {
return Map.of();
}