GP-1743 - Added a method to GhiraScript to allow script writers to

disable reusing previously chosen values in the various 'ask' methods.
This commit is contained in:
dragonmacher 2022-02-11 18:47:10 -05:00
parent 7a5f0b4e16
commit ea52da673e
2 changed files with 90 additions and 43 deletions

View file

@ -94,8 +94,8 @@ import ghidra.util.task.TaskMonitor;
* </pre> * </pre>
* <h3>Ghidra Script State</h3> * <h3>Ghidra Script State</h3>
* <blockquote> * <blockquote>
* *
* <p>All scripts, when run, will be handed the current state in the form of class instance * <p>All scripts, when run, will be handed the current state in the form of class instance
* variable. These variables are: * variable. These variables are:
* <ol> * <ol>
* <li><code>currentProgram</code>: the active program</li> * <li><code>currentProgram</code>: the active program</li>
@ -140,6 +140,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
// Stores any parameters in a .properties file sharing the same base name as this GhidraScript // Stores any parameters in a .properties file sharing the same base name as this GhidraScript
protected GhidraScriptProperties propertiesFileParams; protected GhidraScriptProperties propertiesFileParams;
protected List<ResourceFile> potentialPropertiesFileLocs = new ArrayList<>(); protected List<ResourceFile> potentialPropertiesFileLocs = new ArrayList<>();
private boolean reusePreviousChoices = true;
private CodeUnitFormat cuFormat; private CodeUnitFormat cuFormat;
// Stores any script-specific arguments // Stores any script-specific arguments
@ -181,7 +182,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
/** /**
* Set the context for this script. * Set the context for this script.
* *
* @param state state object * @param state state object
* @param monitor the monitor to use during run * @param monitor the monitor to use during run
* @param writer the target of script "print" statements * @param writer the target of script "print" statements
@ -193,9 +194,28 @@ public abstract class GhidraScript extends FlatProgramAPI {
loadVariablesFromState(); loadVariablesFromState();
} }
/**
* Sets whether the user's previously selected values should be used when showing the various
* {@code ask} methods. This is true by default, meaning that previous choices will be shown
* instead of any provided default value.
* @param reuse true to reuse values; false to not reuse previous values
*/
public void setReusePreviousChoices(boolean reuse) {
this.reusePreviousChoices = reuse;
}
/**
* Returns whether scripts will reuse previously selected values when showing the various
* {@code ask} methods.
* @return true to reuse values; false to not reuse previous values
*/
public boolean getReusePreviousChoices() {
return reusePreviousChoices;
}
/** /**
* Execute/run script and {@link #doCleanup} afterwards. * Execute/run script and {@link #doCleanup} afterwards.
* *
* @param runState state object * @param runState state object
* @param runMonitor the monitor to use during run * @param runMonitor the monitor to use during run
* @param runWriter the target of script "print" statements * @param runWriter the target of script "print" statements
@ -490,7 +510,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
/** /**
* Set the script {@link #currentAddress}, {@link #currentLocation}, and update state object. * Set the script {@link #currentAddress}, {@link #currentLocation}, and update state object.
* *
* @param address the new address * @param address the new address
*/ */
public final void setCurrentLocation(Address address) { public final void setCurrentLocation(Address address) {
@ -1707,7 +1727,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* action from a selection in the table. * action from a selection in the table.
* <p> * <p>
* This method is unavailable in headless mode. * This method is unavailable in headless mode.
* *
* @param title the title of the dialog * @param title the title of the dialog
* @param executor the TableChooserExecuter to be used to apply operations on table entries. * @param executor the TableChooserExecuter to be used to apply operations on table entries.
* @return a new TableChooserDialog. * @return a new TableChooserDialog.
@ -1725,7 +1745,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* action from a selection in the table. * action from a selection in the table.
* <p> * <p>
* This method is unavailable in headless mode. * This method is unavailable in headless mode.
* *
* @param title of the dialog * @param title of the dialog
* @param executor the TableChooserExecuter to be used to apply operations on table entries. * @param executor the TableChooserExecuter to be used to apply operations on table entries.
* @param isModal indicates whether the dialog should be modal or not * @param isModal indicates whether the dialog should be modal or not
@ -1954,7 +1974,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
Map<Class<?>, Object> map = getScriptMap(key1, key2); Map<Class<?>, Object> map = getScriptMap(key1, key2);
T mappedValue = null; T mappedValue = null;
if (clazz != null) { if (clazz != null && reusePreviousChoices) {
mappedValue = (T) map.get(clazz); mappedValue = (T) map.get(clazz);
} }
@ -2402,7 +2422,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or * .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li> * missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file) * (in headless mode or when using .properties file)
@ -2475,7 +2495,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or * .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li> * missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file) * (in headless mode or when using .properties file)
@ -2549,7 +2569,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties byte pattern value (if it exists), or throws an Exception if there is * .properties byte pattern value (if it exists), or throws an Exception if there is
* an invalid or missing .properties value.</li> * an invalid or missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable * @param title the title of the dialog (in GUI mode) or the first part of the variable
* name (in headless mode or when using .properties file) * name (in headless mode or when using .properties file)
@ -2608,7 +2628,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an * then that value is returned. Otherwise, an Exception is thrown if there is an
* invalid or missing .properties value.</li> * invalid or missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the pop-up dialog (in GUI mode) or the variable name (in * @param title the title of the pop-up dialog (in GUI mode) or the variable name (in
* headless mode) * headless mode)
@ -2694,7 +2714,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an invalid * then that value is returned. Otherwise, an Exception is thrown if there is an invalid
* or missing .properties value.</li> * or missing .properties value.</li>
* </ol> * </ol>
* *
* @param title the title of the pop-up dialog (in GUI mode) or the variable name (in headless * @param title the title of the pop-up dialog (in GUI mode) or the variable name (in headless
* mode or when using .properties file) * mode or when using .properties file)
* @throws IllegalArgumentException if in headless mode, there was a missing or invalid domain * @throws IllegalArgumentException if in headless mode, there was a missing or invalid domain
@ -2831,7 +2851,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists), or throws an Exception if there is an invalid or * .properties value (if it exists), or throws an Exception if there is an invalid or
* missing .properties value.</li> * missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file) * (in headless mode or when using .properties file)
@ -2874,7 +2894,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* not null or an empty String, it is returned. In all other cases, an exception * not null or an empty String, it is returned. In all other cases, an exception
* is thrown.</li> * is thrown.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file) * (in headless mode or when using .properties file)
@ -2948,7 +2968,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* .properties value (if it exists and is a valid choice), or throws an Exception if * .properties value (if it exists and is a valid choice), or throws an Exception if
* there is an invalid or missing .properties value.</li> * there is an invalid or missing .properties value.</li>
* </ol> * </ol>
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode or when using .properties file) * (in headless mode or when using .properties file)
* @param message the message to display next to the input field (in GUI mode) or the second * @param message the message to display next to the input field (in GUI mode) or the second
@ -3088,7 +3108,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* *
* @throws CancelledException if the user hits the 'cancel' button * @throws CancelledException if the user hits the 'cancel' button
* @throws IllegalArgumentException if in headless mode, there was a missing or invalid set of * @throws IllegalArgumentException if in headless mode, there was a missing or invalid set of
* choices specified in the .properties file * choices specified in the .properties file
*/ */
public <T> List<T> askChoices(String title, String message, List<T> choices) public <T> List<T> askChoices(String title, String message, List<T> choices)
throws CancelledException { throws CancelledException {
@ -3160,8 +3180,8 @@ public abstract class GhidraScript extends FlatProgramAPI {
* @return the user-selected value(s); null if no selection was made * @return the user-selected value(s); null if no selection was made
* *
* @throws CancelledException if the user hits the 'cancel' button * @throws CancelledException if the user hits the 'cancel' button
* @throws IllegalArgumentException if choices is empty; if in headless mode, * @throws IllegalArgumentException if choices is empty; if in headless mode,
* there was a missing or invalid set of choices specified in the .properties file * there was a missing or invalid set of choices specified in the .properties file
*/ */
public <T> List<T> askChoices(String title, String message, List<T> choices, public <T> List<T> askChoices(String title, String message, List<T> choices,
List<String> choiceLabels) throws CancelledException { List<String> choiceLabels) throws CancelledException {
@ -3232,7 +3252,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* then that value is returned. Otherwise, an Exception is thrown if there is an * then that value is returned. Otherwise, an Exception is thrown if there is an
* invalid or missing .properties value.</li> * invalid or missing .properties value.</li>
* </ol> * </ol>
* *
* *
* @param title the title of the dialog (in GUI mode) or the first part of the variable name * @param title the title of the dialog (in GUI mode) or the first part of the variable name
* (in headless mode) * (in headless mode)

View file

@ -123,7 +123,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
} }
/* /*
* Calling askProgram() would stacktrace if the user 1) didn't select a program in the * Calling askProgram() would stacktrace if the user 1) didn't select a program in the
* tree and then 2) pressed the OK button. * tree and then 2) pressed the OK button.
*/ */
@Test @Test
@ -151,17 +151,17 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
runSwing(() -> dtd.close()); runSwing(() -> dtd.close());
} }
/* /*
* For scripts with properties files in a different location (could be the case with subscripts), * For scripts with properties files in a different location (could be the case with subscripts),
* tests that the .properties file is found in the default location and that the default value * tests that the .properties file is found in the default location and that the default value
* for the input field is provided by the .properties file in the alternate location. * for the input field is provided by the .properties file in the alternate location.
* *
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testAlternateLocationPropertiesFile() throws Exception { public void testAlternateLocationPropertiesFile() throws Exception {
// Create a temporary .properties file and set the potentialPropertiesFileLocs to look // Create a temporary .properties file and set the potentialPropertiesFileLocs to look
// in that location // in that location
String tempDirPath = AbstractGTest.getTestDirectoryPath(); String tempDirPath = AbstractGTest.getTestDirectoryPath();
File tempDir = new File(tempDirPath); File tempDir = new File(tempDirPath);
@ -481,7 +481,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
/* /*
* Test that askInt method auto-populates dialog with value in .properties file. * Test that askInt method auto-populates dialog with value in .properties file.
* *
* Also test that subsequent calls to the dialog show the last-used value. * Also test that subsequent calls to the dialog show the last-used value.
*/ */
@Test @Test
@ -622,13 +622,40 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
createScript(); createScript();
final String defaultValue = "a default value"; String defaultValue = "a default value";
String myString = ask_TextInput(() -> { String myString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", defaultValue); return script.askString("Default Test", "Enter a string here:", defaultValue);
}); });
assertEquals(defaultValue, myString); assertEquals(defaultValue, myString);
} }
@Test
public void testAskStringDefaultValue_DoNotReusePreviousValues() throws Exception {
createScript();
String defaultValue = "a default value";
String myString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", defaultValue);
});
assertEquals(defaultValue, myString);
script.setReusePreviousChoices(false);
String secondDefaultValue = "a new default value";
String secondString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", secondDefaultValue);
});
assertEquals(secondDefaultValue, secondString);
script.setReusePreviousChoices(true);
String thirdDefaultValue = "a third default value";
String thirdString = ask_TextInput(() -> {
return script.askString("Default Test", "Enter a string here:", thirdDefaultValue);
});
assertEquals(secondString, thirdString);
}
@Test @Test
public void testAskChoice() throws Exception { public void testAskChoice() throws Exception {
createScript(); createScript();
@ -673,37 +700,37 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
assertEquals(choices.get(choiceIndex), chosen); assertEquals(choices.get(choiceIndex), chosen);
} }
// TODO test for askChoices() // TODO test for askChoices()
/* /*
* No test for either of the two versions of 'askChoices()" because it does not use either the * No test for either of the two versions of 'askChoices()" because it does not use either the
* the last-selected value or a .properties file value to pre-populate the user choice in the * the last-selected value or a .properties file value to pre-populate the user choice in the
* GUI. * GUI.
*/ */
/* /*
* No test for 'askYesNo()" because it does not use either the the last-selected value or * No test for 'askYesNo()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI. * a .properties file value to pre-populate the user choice in the GUI.
*/ */
/* /*
* No test for 'askProjectFolder()" because it does not use either the the last-selected value * No test for 'askProjectFolder()" because it does not use either the the last-selected value
* or a .properties file value to pre-populate the user choice in the GUI. * or a .properties file value to pre-populate the user choice in the GUI.
*/ */
/* /*
* No test for 'askProgram()" because it does not use either the the last-selected value or * No test for 'askProgram()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI. * a .properties file value to pre-populate the user choice in the GUI.
*/ */
/* /*
* No test for 'askDomainFile()" because it does not use either the the last-selected value or * No test for 'askDomainFile()" because it does not use either the the last-selected value or
* a .properties file value to pre-populate the user choice in the GUI. * a .properties file value to pre-populate the user choice in the GUI.
*/ */
//================================================================================================== //==================================================================================================
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
private <T> T ask_ComboInput(Callable<T> c) { private <T> T ask_ComboInput(Callable<T> c) {
return ask_TextInput(null, c); return ask_TextInput(null, c);