GP-4847: Unify Debugger param dialogs. Prefixed integers allowed.

This commit is contained in:
Dan 2024-09-05 12:35:28 -04:00
parent e0bf7b4c53
commit 16ff4c4d08
36 changed files with 2842 additions and 1509 deletions

View file

@ -0,0 +1,59 @@
/* ###
* 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.debug.api;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public record ValStr<T>(T val, String str) {
public interface Decoder<T> {
default ValStr<T> decodeValStr(String string) {
return new ValStr<>(decode(string), string);
}
T decode(String string);
}
public static ValStr<String> str(String value) {
return new ValStr<>(value, value);
}
public static <T> ValStr<T> from(T value) {
return new ValStr<>(value, value == null ? "" : value.toString());
}
@SuppressWarnings("unchecked")
public static <T> ValStr<T> cast(Class<T> cls, ValStr<?> value) {
if (cls.isInstance(value.val)) {
return (ValStr<T>) value;
}
return new ValStr<>(cls.cast(value.val), value.str);
}
public static Map<String, ValStr<?>> fromPlainMap(Map<String, ?> map) {
return map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getKey, e -> ValStr.from(e.getValue())));
}
public static Map<String, ? super Object> toPlainMap(Map<String, ValStr<?>> map) {
return map.entrySet()
.stream()
.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().val()));
}
}

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.
@ -24,6 +24,7 @@ import ghidra.dbg.DebuggerModelFactory;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.target.TargetLauncher;
import ghidra.dbg.target.TargetObject;
import ghidra.debug.api.ValStr;
import ghidra.util.task.TaskMonitor;
/**
@ -117,8 +118,8 @@ public interface DebuggerProgramLaunchOffer {
* @param relPrompt describes the timing of this callback relative to prompting the user
* @return the adjusted arguments
*/
default Map<String, ?> configureLauncher(TargetLauncher launcher,
Map<String, ?> arguments, RelPrompt relPrompt) {
default Map<String, ValStr<?>> configureLauncher(TargetLauncher launcher,
Map<String, ValStr<?>> arguments, RelPrompt relPrompt) {
return arguments;
}
}

View file

@ -0,0 +1,106 @@
/* ###
* 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.debug.api.tracermi;
import java.util.*;
import ghidra.debug.api.ValStr;
public record LaunchParameter<T>(Class<T> type, String name, String display, String description,
boolean required, List<T> choices, ValStr<T> defaultValue, ValStr.Decoder<T> decoder) {
public static <T> LaunchParameter<T> create(Class<T> type, String name, String display,
String description, boolean required, ValStr<T> defaultValue,
ValStr.Decoder<T> decoder) {
return new LaunchParameter<>(type, name, display, description, required, List.of(),
defaultValue, decoder);
}
public static <T> LaunchParameter<T> choices(Class<T> type, String name, String display,
String description, Collection<T> choices, ValStr<T> defaultValue) {
return new LaunchParameter<>(type, name, display, description, false,
List.copyOf(new LinkedHashSet<>(choices)), defaultValue, str -> {
for (T t : choices) {
if (t.toString().equals(str)) {
return t;
}
}
return null;
});
}
public static Map<String, LaunchParameter<?>> mapOf(Collection<LaunchParameter<?>> parameters) {
Map<String, LaunchParameter<?>> result = new LinkedHashMap<>();
for (LaunchParameter<?> param : parameters) {
LaunchParameter<?> exists = result.put(param.name(), param);
if (exists != null) {
throw new IllegalArgumentException(
"Duplicate names in parameter map: first=%s, second=%s".formatted(exists,
param));
}
}
return Collections.unmodifiableMap(result);
}
public static Map<String, ValStr<?>> validateArguments(
Map<String, LaunchParameter<?>> parameters, Map<String, ValStr<?>> arguments) {
if (!parameters.keySet().containsAll(arguments.keySet())) {
Set<String> extraneous = new TreeSet<>(arguments.keySet());
extraneous.removeAll(parameters.keySet());
throw new IllegalArgumentException("Extraneous parameters: " + extraneous);
}
Map<String, String> typeErrors = null;
for (Map.Entry<String, ValStr<?>> ent : arguments.entrySet()) {
String name = ent.getKey();
ValStr<?> val = ent.getValue();
LaunchParameter<?> param = parameters.get(name);
if (val.val() != null && !param.type.isAssignableFrom(val.val().getClass())) {
if (typeErrors == null) {
typeErrors = new LinkedHashMap<>();
}
typeErrors.put(name, "val '%s' is not a %s".formatted(val.val(), param.type()));
}
}
if (typeErrors != null) {
throw new IllegalArgumentException("Type errors: " + typeErrors);
}
return arguments;
}
public static Map<String, LaunchParameter<?>> mapOf(LaunchParameter<?>... parameters) {
return mapOf(Arrays.asList(parameters));
}
public ValStr<T> decode(String string) {
return decoder.decodeValStr(string);
}
public ValStr<T> get(Map<String, ValStr<?>> arguments) {
if (arguments.containsKey(name)) {
return ValStr.cast(type, arguments.get(name));
}
if (required) {
throw new IllegalArgumentException(
"Missing required parameter '%s' (%s)".formatted(display, name));
}
return defaultValue;
}
public void set(Map<String, ValStr<?>> arguments, ValStr<T> value) {
arguments.put(name, value);
}
}

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.
@ -20,7 +20,7 @@ import java.util.Map;
import javax.swing.Icon;
import ghidra.dbg.target.TargetMethod.ParameterDescription;
import ghidra.debug.api.ValStr;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
import ghidra.util.HelpLocation;
@ -150,8 +150,8 @@ public interface TraceRmiLaunchOffer {
* @param relPrompt describes the timing of this callback relative to prompting the user
* @return the adjusted arguments
*/
default Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
Map<String, ?> arguments, RelPrompt relPrompt) {
default Map<String, ValStr<?>> configureLauncher(TraceRmiLaunchOffer offer,
Map<String, ValStr<?>> arguments, RelPrompt relPrompt) {
return arguments;
}
}
@ -293,7 +293,7 @@ public interface TraceRmiLaunchOffer {
*
* @return the parameters
*/
Map<String, ParameterDescription<?>> getParameters();
Map<String, LaunchParameter<?>> getParameters();
/**
* Check if this offer requires an open program

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.
@ -28,6 +28,7 @@ import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
import ghidra.dbg.target.TargetSteppable.TargetStepKind;
import ghidra.dbg.util.PathUtils;
import ghidra.debug.api.ValStr;
import ghidra.debug.api.model.DebuggerProgramLaunchOffer;
import ghidra.debug.api.model.DebuggerProgramLaunchOffer.*;
import ghidra.debug.api.model.TraceRecorder;
@ -578,10 +579,10 @@ public interface FlatDebuggerRecorderAPI extends FlatDebuggerAPI {
try {
return waitOn(offer.launchProgram(monitor, PromptMode.NEVER, new LaunchConfigurator() {
@Override
public Map<String, ?> configureLauncher(TargetLauncher launcher,
Map<String, ?> arguments, RelPrompt relPrompt) {
Map<String, Object> adjusted = new HashMap<>(arguments);
adjusted.put(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, commandLine);
public Map<String, ValStr<?>> configureLauncher(TargetLauncher launcher,
Map<String, ValStr<?>> arguments, RelPrompt relPrompt) {
Map<String, ValStr<?>> adjusted = new HashMap<>(arguments);
adjusted.put(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, ValStr.str(commandLine));
return adjusted;
}
}));

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.
@ -16,8 +16,10 @@
package ghidra.debug.flatapi;
import java.util.*;
import java.util.Map.Entry;
import ghidra.app.services.TraceRmiLauncherService;
import ghidra.debug.api.ValStr;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
import ghidra.program.model.listing.Program;
@ -116,13 +118,15 @@ public interface FlatDebuggerRmiAPI extends FlatDebuggerAPI {
TaskMonitor monitor) {
return offer.launchProgram(monitor, new LaunchConfigurator() {
@Override
public Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
Map<String, ?> arguments, RelPrompt relPrompt) {
public Map<String, ValStr<?>> configureLauncher(TraceRmiLaunchOffer offer,
Map<String, ValStr<?>> arguments, RelPrompt relPrompt) {
if (arguments.isEmpty()) {
return arguments;
}
Map<String, Object> args = new HashMap<>(arguments);
args.putAll(overrideArgs);
Map<String, ValStr<?>> args = new HashMap<>(arguments);
for (Entry<String, ?> ent : overrideArgs.entrySet()) {
args.put(ent.getKey(), ValStr.from(ent.getValue()));
}
return args;
}
});