mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-2968: per review comment (cleanup)
GP-2968: prompting ReviewTool GP-2968: my bad, NPE check was hanging the launch GP-2968: fix for passing Enums; fix for toMap'ing nulls GP-2968: refactoring args logic into loop w/ handling for exception GP-2968: fix for NPE & stored parameter errors
This commit is contained in:
parent
f930701ed6
commit
c43351d742
13 changed files with 159 additions and 92 deletions
|
@ -1382,9 +1382,8 @@ public class DbgManagerImpl implements DbgManager {
|
|||
BitmaskSet<DebugVerifierFlags> vf =
|
||||
new BitmaskSet<DebugVerifierFlags>(DebugVerifierFlags.class,
|
||||
vfVal == null ? 0 : vfVal);
|
||||
execute(new DbgLaunchProcessCommand(this, args,
|
||||
initDir, env, cf, ef, vf));
|
||||
return AsyncUtils.NIL;
|
||||
return execute(new DbgLaunchProcessCommand(this, args,
|
||||
initDir, env, cf, ef, vf)).thenApply(__ -> null);
|
||||
}
|
||||
|
||||
public CompletableFuture<?> openFile(Map<String, ?> args) {
|
||||
|
|
|
@ -82,7 +82,7 @@ public class DbgModelTargetContinuationOptionImpl extends DbgModelTargetObjectIm
|
|||
public void setAttributes() {
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionCont.description, //
|
||||
VALUE_ATTRIBUTE_NAME, optionCont, //
|
||||
VALUE_ATTRIBUTE_NAME, optionCont.val, //
|
||||
ENABLED_ATTRIBUTE_NAME,
|
||||
optionCont.equals(DebugFilterContinuationOption.DEBUG_FILTER_GO_HANDLED)),
|
||||
"Refreshed");
|
||||
|
|
|
@ -83,7 +83,7 @@ public class DbgModelTargetExecutionOptionImpl extends DbgModelTargetObjectImpl
|
|||
public void setAttributes() {
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getName() + " : " + optionExc.description, //
|
||||
VALUE_ATTRIBUTE_NAME, optionExc, //
|
||||
VALUE_ATTRIBUTE_NAME, optionExc.val, //
|
||||
ENABLED_ATTRIBUTE_NAME,
|
||||
optionExc.equals(DebugFilterExecutionOption.DEBUG_FILTER_BREAK)), "Refreshed");
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
|
|||
map.put("cf", cf);
|
||||
map.put("ef", ef);
|
||||
map.put("vf", vf);
|
||||
// Innocuous comment: up-up-down-down-left-right-left-right-B-A
|
||||
return map;
|
||||
}
|
||||
|
||||
|
@ -91,11 +92,4 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
|
|||
return getManager().launch(args);
|
||||
}
|
||||
|
||||
// public CompletableFuture<Void> launch(List<String> args) {
|
||||
// return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
// getManager().launch(args).handle(seq::nextIgnore);
|
||||
// }).finish().exceptionally((exc) -> {
|
||||
// throw new DebuggerUserException("Launch failed for " + args);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -59,7 +59,6 @@ import ghidra.dbg.error.DebuggerMemoryAccessException;
|
|||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetConsole.Channel;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
|
||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetSteppable.TargetStepKind;
|
||||
import ghidra.dbg.util.DebuggerCallbackReorderer;
|
||||
|
@ -1335,38 +1334,32 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
if (currentProgram == null) {
|
||||
return;
|
||||
}
|
||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
||||
// TODO: A generic or pluggable way of deriving the launch arguments
|
||||
return launcher.launch(Map.of(
|
||||
TargetCmdLineLauncher.CMDLINE_ARGS_NAME, currentProgram.getExecutablePath()));
|
||||
}, "Couldn't launch");
|
||||
performLaunchAction(context, false);
|
||||
}
|
||||
|
||||
public void performLaunch(ActionContext context) {
|
||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
||||
performLaunchAction(context, true);
|
||||
}
|
||||
|
||||
Map<String, ?> args = launchOffer.getLauncherArgs(launcher, true);
|
||||
if (args == null) {
|
||||
// Cancelled
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return launcher.launch(args);
|
||||
/*
|
||||
String argsKey = TargetCmdLineLauncher.CMDLINE_ARGS_NAME;
|
||||
String path = (currentProgram != null) ? currentProgram.getExecutablePath() : null;
|
||||
launchDialog.setCurrentContext(path);
|
||||
String cmdlineArgs = launchDialog.getMemorizedArgument(argsKey, String.class);
|
||||
if (cmdlineArgs == null) {
|
||||
cmdlineArgs = path;
|
||||
launchDialog.setMemorizedArgument(argsKey, String.class,
|
||||
cmdlineArgs);
|
||||
}
|
||||
Map<String, ?> args = launchDialog.promptArguments(launcher.getParameters());
|
||||
if (args == null) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return launcher.launch(args);
|
||||
*/
|
||||
private void performLaunchAction(ActionContext context, boolean p) {
|
||||
performAction(context, true, TargetLauncher.class, launcher -> {
|
||||
var locals = new Object() {
|
||||
boolean prompt = p;
|
||||
};
|
||||
return AsyncUtils.loop(TypeSpec.VOID, (loop) -> {
|
||||
Map<String, ?> args = launchOffer.getLauncherArgs(launcher, locals.prompt);
|
||||
if (args == null) {
|
||||
// Cancelled
|
||||
loop.exit();
|
||||
}
|
||||
else {
|
||||
launcher.launch(args).thenAccept(loop::exit).exceptionally(ex -> {
|
||||
loop.repeat();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
locals.prompt = true;
|
||||
});
|
||||
}, "Couldn't launch");
|
||||
}
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ import java.awt.BorderLayout;
|
|||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
@ -32,6 +30,7 @@ import org.apache.commons.lang3.tuple.MutablePair;
|
|||
import org.jdom.Element;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||
import ghidra.dbg.target.TargetMethod;
|
||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
|
@ -92,6 +91,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
private PairLayout layout;
|
||||
|
||||
protected JButton invokeButton;
|
||||
protected JButton resetButton;
|
||||
protected boolean resetRequested;
|
||||
|
||||
private final PluginTool tool;
|
||||
private Map<String, ParameterDescription<?>> parameters;
|
||||
|
@ -105,7 +106,7 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
super(title, true, false, true, false);
|
||||
this.tool = tool;
|
||||
|
||||
populateComponents(buttonText, buttonIcon);
|
||||
populateComponents(buttonText, buttonIcon, "Reset", DebuggerResources.ICON_REFRESH);
|
||||
setRememberSize(false);
|
||||
}
|
||||
|
||||
|
@ -126,7 +127,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
populateOptions();
|
||||
}
|
||||
|
||||
private void populateComponents(String buttonText, Icon buttonIcon) {
|
||||
private void populateComponents(String buttonText, Icon buttonIcon,
|
||||
String resetText, Icon resetIcon) {
|
||||
panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||
|
||||
|
@ -144,19 +146,31 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
|
||||
invokeButton = new JButton(buttonText, buttonIcon);
|
||||
addButton(invokeButton);
|
||||
resetButton = new JButton(resetText, resetIcon);
|
||||
addButton(resetButton);
|
||||
addCancelButton();
|
||||
|
||||
invokeButton.addActionListener(this::invoke);
|
||||
resetButton.addActionListener(this::reset);
|
||||
resetRequested = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
this.arguments = null;
|
||||
this.resetRequested = false;
|
||||
close();
|
||||
}
|
||||
|
||||
private void invoke(ActionEvent evt) {
|
||||
this.arguments = TargetMethod.validateArguments(parameters, collectArguments(), false);
|
||||
this.resetRequested = false;
|
||||
close();
|
||||
}
|
||||
|
||||
private void reset(ActionEvent evt) {
|
||||
this.arguments = new LinkedHashMap<>();
|
||||
this.resetRequested = true;
|
||||
close();
|
||||
}
|
||||
|
||||
|
@ -175,7 +189,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
Msg.warn(this, "No editor for " + type + "? Trying String instead");
|
||||
editor = PropertyEditorManager.findEditor(String.class);
|
||||
}
|
||||
editor.setValue(computeMemorizedValue(param));
|
||||
Object val = computeMemorizedValue(param);
|
||||
editor.setValue(val);
|
||||
editor.addPropertyChangeListener(this);
|
||||
pairPanel.add(MiscellaneousUtils.getEditorComponent(editor));
|
||||
// TODO: How to handle parameter with choices?
|
||||
|
@ -184,10 +199,14 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
}
|
||||
|
||||
protected Map<String, ?> collectArguments() {
|
||||
return paramEditors.keySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(param -> param.name,
|
||||
param -> memorized.get(NameTypePair.fromParameter(param))));
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
for (ParameterDescription<?> param : paramEditors.keySet()) {
|
||||
Object val = memorized.get(NameTypePair.fromParameter(param));
|
||||
if (val != null) {
|
||||
map.put(param.name, val);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String, ?> getArguments() {
|
||||
|
@ -196,6 +215,9 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
|
||||
public <T> void setMemorizedArgument(String name, Class<T> type, T value) {
|
||||
//name = addContext(name, currentContext);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
memorized.put(new NameTypePair(name, type), value);
|
||||
}
|
||||
|
||||
|
@ -239,4 +261,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isResetRequested() {
|
||||
return resetRequested;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -61,8 +61,15 @@ import ghidra.util.classfinder.ClassSearcher;
|
|||
import ghidra.util.datastruct.CollectionChangeListener;
|
||||
import ghidra.util.datastruct.ListenerSet;
|
||||
|
||||
@PluginInfo(shortDescription = "Debugger models manager service", description = "Manage debug sessions, connections, and trace recording", category = PluginCategoryNames.DEBUGGER, packageName = DebuggerPluginPackage.NAME, status = PluginStatus.HIDDEN, servicesRequired = {}, servicesProvided = {
|
||||
DebuggerModelService.class, })
|
||||
@PluginInfo(
|
||||
shortDescription = "Debugger models manager service",
|
||||
description = "Manage debug sessions, connections, and trace recording",
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.HIDDEN,
|
||||
servicesRequired = {},
|
||||
servicesProvided = {
|
||||
DebuggerModelService.class, })
|
||||
public class DebuggerModelServicePlugin extends Plugin
|
||||
implements DebuggerModelServiceInternal, ApplicationLevelOnlyPlugin {
|
||||
|
||||
|
@ -239,7 +246,10 @@ public class DebuggerModelServicePlugin extends Plugin
|
|||
}
|
||||
model.addModelListener(forRemovalAndFocusListener);
|
||||
TargetObject root = model.getModelRoot();
|
||||
if (!root.isValid()) {
|
||||
// root == null, probably means we're between model construction
|
||||
// and root construction, but the model was not closed, so no need
|
||||
// to invalidate
|
||||
if (root != null && !root.isValid()) {
|
||||
forRemovalAndFocusListener.invalidated(root, root,
|
||||
"Invalidated before or during add to service");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources.DebugProgramAction;
|
|||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.DisconnectAllAction;
|
||||
import ghidra.app.plugin.core.debug.mapping.DebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
|
||||
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer.PromptMode;
|
||||
import ghidra.app.plugin.core.debug.utils.BackgroundUtils;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
|
@ -88,7 +89,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
new DebuggerProgramLaunchOffer() {
|
||||
@Override
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor,
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
PromptMode prompt, LaunchConfigurator configurator) {
|
||||
throw new AssertionError("Who clicked me?");
|
||||
}
|
||||
|
||||
|
@ -330,7 +331,8 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
return offers.sorted(Comparator.comparingInt(o -> -mrl.indexOf(o.getConfigName())));
|
||||
}
|
||||
|
||||
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program, boolean prompt) {
|
||||
private void debugProgram(DebuggerProgramLaunchOffer offer, Program program,
|
||||
PromptMode prompt) {
|
||||
BackgroundUtils.asyncModal(tool, offer.getButtonTitle(), true, true, m -> {
|
||||
List<String> recent = new ArrayList<>(readMostRecentLaunches(program));
|
||||
recent.remove(offer.getConfigName());
|
||||
|
@ -360,7 +362,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
if (offer == null || program == null) {
|
||||
return;
|
||||
}
|
||||
debugProgram(offer, program, false);
|
||||
debugProgram(offer, program, PromptMode.ON_ERROR);
|
||||
}
|
||||
|
||||
private void debugProgramStateActivated(ActionState<DebuggerProgramLaunchOffer> offer,
|
||||
|
@ -375,7 +377,7 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
|||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
debugProgram(offer, program, true);
|
||||
debugProgram(offer, program, PromptMode.ALWAYS);
|
||||
}
|
||||
|
||||
private void updateActionDebugProgram() {
|
||||
|
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model.launch;
|
||||
|
||||
import static ghidra.async.AsyncUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -267,7 +270,12 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
if (program == null) {
|
||||
return Map.of();
|
||||
}
|
||||
return Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, program.getExecutablePath());
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
for (Entry<String, ParameterDescription<?>> entry : params.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue().defaultValue);
|
||||
}
|
||||
map.put(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, program.getExecutablePath());
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,20 +290,30 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
DebuggerMethodInvocationDialog dialog =
|
||||
new DebuggerMethodInvocationDialog(tool, getButtonTitle(), "Launch", getIcon());
|
||||
// NB. Do not invoke read/writeConfigState
|
||||
Map<String, ?> args = configurator.configureLauncher(launcher,
|
||||
loadLastLauncherArgs(launcher, true), RelPrompt.BEFORE);
|
||||
for (ParameterDescription<?> param : params.values()) {
|
||||
Object val = args.get(param.name);
|
||||
if (val != null) {
|
||||
dialog.setMemorizedArgument(param.name, param.type.asSubclass(Object.class), val);
|
||||
Map<String, ?> args;
|
||||
boolean reset = false;
|
||||
do {
|
||||
args = configurator.configureLauncher(launcher,
|
||||
loadLastLauncherArgs(launcher, true), RelPrompt.BEFORE);
|
||||
for (ParameterDescription<?> param : params.values()) {
|
||||
Object val = args.get(param.name);
|
||||
if (val != null) {
|
||||
dialog.setMemorizedArgument(param.name, param.type.asSubclass(Object.class),
|
||||
val);
|
||||
}
|
||||
}
|
||||
args = dialog.promptArguments(params);
|
||||
if (args == null) {
|
||||
// Cancelled
|
||||
return null;
|
||||
}
|
||||
reset = dialog.isResetRequested();
|
||||
if (reset) {
|
||||
args = generateDefaultLauncherArgs(params);
|
||||
}
|
||||
saveLauncherArgs(args, params);
|
||||
}
|
||||
args = dialog.promptArguments(params);
|
||||
if (args == null) {
|
||||
// Cancelled
|
||||
return null;
|
||||
}
|
||||
saveLauncherArgs(args, params);
|
||||
while (reset);
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -328,12 +346,15 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
try {
|
||||
Element element = XmlUtilities.fromString(property);
|
||||
SaveState state = new SaveState(element);
|
||||
List<String> names = List.of(state.getNames());
|
||||
Map<String, Object> args = new LinkedHashMap<>();
|
||||
for (ParameterDescription<?> param : params.values()) {
|
||||
Object configState =
|
||||
ConfigStateField.getState(state, param.type, param.name);
|
||||
if (configState != null) {
|
||||
args.put(param.name, configState);
|
||||
if (names.contains(param.name)) {
|
||||
Object configState =
|
||||
ConfigStateField.getState(state, param.type, param.name);
|
||||
if (configState != null) {
|
||||
args.put(param.name, configState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!args.isEmpty()) {
|
||||
|
@ -347,7 +368,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
e);
|
||||
}
|
||||
Msg.error(this,
|
||||
"Saved launcher args are corrup, or launcher parameters changed. Defaulting.",
|
||||
"Saved launcher args are corrupt, or launcher parameters changed. Defaulting.",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
@ -463,12 +484,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
|
||||
// Eww.
|
||||
protected CompletableFuture<Void> launch(TargetLauncher launcher,
|
||||
boolean prompt, LaunchConfigurator configurator) {
|
||||
boolean prompt, LaunchConfigurator configurator, TaskMonitor monitor) {
|
||||
Map<String, ?> args = getLauncherArgs(launcher, prompt, configurator);
|
||||
if (args == null) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
return launcher.launch(args);
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
.timeOut(
|
||||
launcher.launch(args), getTimeoutMillis(), () -> onTimedOutLaunch(monitor));
|
||||
}
|
||||
|
||||
protected void checkCancelled(TaskMonitor monitor) {
|
||||
|
@ -560,7 +583,7 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode mode,
|
||||
LaunchConfigurator configurator) {
|
||||
DebuggerModelService service = tool.getService(DebuggerModelService.class);
|
||||
DebuggerStaticMappingService mappingService =
|
||||
|
@ -573,12 +596,13 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
TargetObject target;
|
||||
TraceRecorder recorder;
|
||||
Throwable exception;
|
||||
boolean prompt = mode == PromptMode.ALWAYS;
|
||||
|
||||
LaunchResult getResult() {
|
||||
return new LaunchResult(model, target, recorder, exception);
|
||||
}
|
||||
};
|
||||
return connect(service, prompt, configurator).thenCompose(m -> {
|
||||
return connect(service, locals.prompt, configurator).thenCompose(m -> {
|
||||
checkCancelled(monitor);
|
||||
locals.model = m;
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -591,12 +615,14 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
monitor.incrementProgress(1);
|
||||
monitor.setMessage("Launching");
|
||||
locals.futureTarget = listenForTarget(l.getModel());
|
||||
if (prompt) {
|
||||
return launch(l, true, configurator);
|
||||
}
|
||||
return AsyncTimer.DEFAULT_TIMER.mark()
|
||||
.timeOut(launch(l, false, configurator), getTimeoutMillis(),
|
||||
() -> onTimedOutLaunch(monitor));
|
||||
return loop(TypeSpec.VOID, (loop) -> {
|
||||
launch(l, locals.prompt, configurator, monitor).thenAccept(loop::exit)
|
||||
.exceptionally(ex -> {
|
||||
loop.repeat();
|
||||
return null;
|
||||
});
|
||||
locals.prompt = mode != PromptMode.NEVER;
|
||||
});
|
||||
}).thenCompose(__ -> {
|
||||
checkCancelled(monitor);
|
||||
monitor.incrementProgress(1);
|
||||
|
@ -636,4 +662,5 @@ public abstract class AbstractDebuggerProgramLaunchOffer implements DebuggerProg
|
|||
return locals.getResult();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -82,6 +82,21 @@ public interface DebuggerProgramLaunchOffer {
|
|||
AFTER;
|
||||
}
|
||||
|
||||
public enum PromptMode {
|
||||
/**
|
||||
* The user is always prompted for parameters.
|
||||
*/
|
||||
ALWAYS,
|
||||
/**
|
||||
* The user is never prompted for parameters.
|
||||
*/
|
||||
NEVER,
|
||||
/**
|
||||
* The user is prompted after an error.
|
||||
*/
|
||||
ON_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks for custom configuration when launching a program
|
||||
*/
|
||||
|
@ -118,7 +133,7 @@ public interface DebuggerProgramLaunchOffer {
|
|||
* @param configurator the configuration callbacks
|
||||
* @return a future which completes when the program is launched
|
||||
*/
|
||||
CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt,
|
||||
LaunchConfigurator configurator);
|
||||
|
||||
/**
|
||||
|
@ -128,7 +143,7 @@ public interface DebuggerProgramLaunchOffer {
|
|||
* @param prompt if the user should be prompted to confirm launch parameters
|
||||
* @return a future which completes when the program is launched
|
||||
*/
|
||||
default CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt) {
|
||||
default CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt) {
|
||||
return launchProgram(monitor, prompt, LaunchConfigurator.NOP);
|
||||
}
|
||||
|
||||
|
|
|
@ -1491,7 +1491,7 @@ public interface FlatDebuggerAPI {
|
|||
default LaunchResult launch(DebuggerProgramLaunchOffer offer, String commandLine,
|
||||
TaskMonitor monitor) {
|
||||
try {
|
||||
return waitOn(offer.launchProgram(monitor, false, new LaunchConfigurator() {
|
||||
return waitOn(offer.launchProgram(monitor, PromptMode.NEVER, new LaunchConfigurator() {
|
||||
@Override
|
||||
public Map<String, ?> configureLauncher(TargetLauncher launcher,
|
||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||
|
@ -1514,7 +1514,7 @@ public interface FlatDebuggerAPI {
|
|||
*/
|
||||
default LaunchResult launch(DebuggerProgramLaunchOffer offer, TaskMonitor monitor) {
|
||||
try {
|
||||
return waitOn(offer.launchProgram(monitor, false));
|
||||
return waitOn(offer.launchProgram(monitor, PromptMode.NEVER));
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
// TODO: This is not ideal, since it's likely partially completed
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -35,7 +35,7 @@ public class TestDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOp
|
|||
|
||||
public static class TestDebuggerProgramLaunchOffer implements DebuggerProgramLaunchOffer {
|
||||
@Override
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, boolean prompt,
|
||||
public CompletableFuture<LaunchResult> launchProgram(TaskMonitor monitor, PromptMode prompt,
|
||||
LaunchConfigurator configurator) {
|
||||
return CompletableFuture
|
||||
.completedFuture(LaunchResult.totalFailure(new AssertionError()));
|
||||
|
|
|
@ -139,4 +139,5 @@ public interface TargetLauncher extends TargetObject {
|
|||
* @return a future which completes when the command is completed
|
||||
*/
|
||||
public CompletableFuture<Void> launch(Map<String, ?> args);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue