GP-5425: Encapsulate object-discovery policy, sort REFRESH, incorporate schema into specificity

This commit is contained in:
Dan 2025-03-04 14:15:07 +00:00
parent 136a944796
commit a94b0493c9
12 changed files with 205 additions and 206 deletions

View file

@ -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<DockingActionIf> 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));
}

View file

@ -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;
}

View file

@ -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<DockingActionIf> 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;

View file

@ -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())

View file

@ -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<String, ActionEntry> 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<Void> 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<Void> future =
TargetActionTask.runAction(plugin.getTool(), ent.display(), ent);
future.handle((__, ex) -> {
node.removeNode(pending);
return null;
});
}
@Override

View file

@ -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<String, ActionEntry> 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())

View file

@ -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<String, ActionEntry> collectAddressActions(
ProgramLocationActionContext context);
protected Map<String, ActionEntry> 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<String, ActionEntry> collectResumeActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectInterruptActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectKillActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectStepIntoActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectStepOverActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectStepOutActions(ActionContext context);
protected abstract Map<String, ActionEntry> collectStepExtActions(ActionContext context);
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) {
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> T doSync(String name, Supplier<CompletableFuture<T>> supplier)
throws InterruptedException, ExecutionException {
if (Swing.isSwingThread()) {

View file

@ -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<String, ActionEntry> collectActions(ActionName name, ActionContext context) {
public Map<String, ActionEntry> collectActions(ActionName name, ActionContext context,
ObjectArgumentPolicy policy) {
return Map.of();
}