diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java index 002ba5a921..3ec21864cd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/BitFieldEditorPanel.java @@ -204,7 +204,7 @@ public class BitFieldEditorPanel extends JPanel { private JComponent createDataTypeChoiceEditor() { dtChoiceEditor = - new DataTypeSelectionEditor(dtmService, -1, AllowedDataTypes.BITFIELD_BASE_TYPE); + new DataTypeSelectionEditor(dtmService, AllowedDataTypes.BITFIELD_BASE_TYPE); dtChoiceEditor.setConsumeEnterKeyPress(false); dtChoiceEditor.setTabCommitsEdit(true); //dtChoiceEditor.setPreferredDataTypeManager(composite.getDataTypeManager()); @@ -539,7 +539,7 @@ public class BitFieldEditorPanel extends JPanel { dtChoiceEditor.getDropDownTextField().setText(""); fieldNameTextField.setText(null); fieldCommentTextField.setText(null); - ; + bitOffsetModel.setValue(0L); bitSizeModel.setValue(1L); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java index 77899c61d3..db361ac227 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorPanel.java @@ -1256,7 +1256,7 @@ public abstract class CompositeEditorPanel extends JPanel Plugin plugin = provider.getPlugin(); final PluginTool tool = plugin.getTool(); - editor = new DataTypeSelectionEditor(tool, maxLength, + editor = new DataTypeSelectionEditor(tool, bitfieldAllowed ? AllowedDataTypes.SIZABLE_DYNAMIC_AND_BITFIELD : AllowedDataTypes.SIZABLE_DYNAMIC); editor.setTabCommitsEdit(true); @@ -1288,12 +1288,7 @@ public abstract class CompositeEditorPanel extends JPanel } }; - dataTypeChooserButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Swing.runLater(() -> stopEdit(tool)); - } - }); + dataTypeChooserButton.addActionListener(e -> Swing.runLater(() -> stopEdit(tool))); textField.addFocusListener(new FocusAdapter() { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java index df32e16246..4c8e71d91b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/ChooseDataTypeAction.java @@ -24,12 +24,8 @@ import docking.action.*; import ghidra.app.context.ListingActionContext; import ghidra.app.util.datatype.DataTypeSelectionDialog; import ghidra.framework.plugintool.PluginTool; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressRange; -import ghidra.program.model.data.*; -import ghidra.program.model.listing.*; -import ghidra.program.util.*; -import ghidra.util.SystemUtilities; +import ghidra.program.model.data.DataType; +import ghidra.program.model.listing.Data; import ghidra.util.data.DataTypeParser.AllowedDataTypes; /** @@ -58,121 +54,21 @@ public class ChooseDataTypeAction extends DockingAction { @Override public void actionPerformed(ActionContext context) { - ListingActionContext programActionContext = - (ListingActionContext) context.getContextObject(); - int maxSize = Integer.MAX_VALUE; - Program program = programActionContext.getProgram(); - ProgramLocation loc = programActionContext.getLocation(); - ProgramSelection sel = programActionContext.getSelection(); - if (sel != null && !sel.isEmpty()) { - InteriorSelection interiorSel = sel.getInteriorSelection(); - if (interiorSel != null) { - maxSize = getSizeInsideStructure(program, interiorSel); - } - else { - maxSize = getSizeForSelection(program, sel); - } - } - else { - int[] compPath = loc.getComponentPath(); - if (compPath != null && compPath.length > 0) { - maxSize = getSizeInsideStructure(program, loc); - } - else { - maxSize = getSizeForAddress(program, loc); - } - } - - // unable to create data types at the current location - if (maxSize < 0) { - return; - } - - Pointer pointer = program.getDataTypeManager().getPointer(null); - DataType dataType = getDataType(programActionContext, maxSize, pointer.getLength()); + ListingActionContext listingContext = (ListingActionContext) context.getContextObject(); + DataType dataType = getDataType(listingContext); if (dataType != null) { - plugin.doCreateData(program, loc, sel, dataType, false); + plugin.createData(dataType, listingContext, true); } } - private int getSizeInsideStructure(Program program, InteriorSelection selection) { - ProgramLocation location = selection.getFrom(); - Data dataComponent = getParentDataType(program, location); - if (dataComponent == null) { - return -1; - } - return selection.getByteLength(); - } - - private int getSizeInsideStructure(Program program, ProgramLocation location) { - Data dataComponent = getParentDataType(program, location); - if (dataComponent == null) { - return -1; - } - return getMaxSizeInStructure((Structure) dataComponent.getParent().getBaseDataType(), - dataComponent.getComponentIndex()); - } - - private int getSizeForAddress(Program program, ProgramLocation location) { - - Address address = location.getAddress(); - Data data = program.getListing().getDataAt(address); - if (data == null) { - plugin.getTool().setStatusInfo("Create Data Failed! No data at " + address); - return -1; - } - - return getMaxSize(program, address); - } - - private Data getParentDataType(Program program, ProgramLocation location) { - - int[] path = location.getComponentPath(); - Address address = location.getAddress(); - Data data = program.getListing().getDataContaining(address); - Data dataComponent = null; - if (data != null) { - dataComponent = data.getComponent(path); - } - - if (dataComponent == null) { - plugin.getTool().setStatusInfo("Create data type failed! No data at " + address); - return null; - } - - DataType parentDataType = dataComponent.getParent().getBaseDataType(); - - if (!(parentDataType instanceof Structure)) { - plugin.getTool().setStatusInfo("Cannot set data type here."); - return null; - } - - return dataComponent; - } - - private int getSizeForSelection(Program program, ProgramSelection selection) { - - PluginTool tool = plugin.getTool(); - - AddressRange range = selection.getFirstRange(); - Address address = selection.getMinAddress(); - Data data = program.getListing().getDataAt(address); - if (data == null) { - tool.setStatusInfo("Cannot set data type! No data at " + address); - return -1; - } - - return (int) range.getLength(); - } - - private DataType getDataType(ListingActionContext context, int maxElements, - int defaultPointerSize) { + private DataType getDataType(ListingActionContext context) { PluginTool tool = plugin.getTool(); Data data = plugin.getDataUnit(context); + int noSizeRestriction = -1; DataTypeSelectionDialog selectionDialog = new DataTypeSelectionDialog(tool, - data.getProgram().getDataTypeManager(), maxElements, AllowedDataTypes.ALL); - DataType currentDataType = data.getBaseDataType(); - selectionDialog.setInitialDataType(currentDataType); + data.getProgram().getDataTypeManager(), noSizeRestriction, AllowedDataTypes.ALL); + DataType initialType = data.getBaseDataType(); + selectionDialog.setInitialDataType(initialType); tool.showDialog(selectionDialog); return selectionDialog.getUserChosenDataType(); } @@ -185,47 +81,4 @@ public class ChooseDataTypeAction extends DockingAction { } return false; } - - private int getMaxSizeInStructure(Structure struct, int index) { - int n = struct.getNumComponents(); - DataTypeComponent dtc = struct.getComponent(index++); - int length = dtc.getLength(); - while (index < n) { - dtc = struct.getComponent(index++); - DataType dataType = dtc.getDataType(); - if (dataType != DataType.DEFAULT) { - break; - } - length += dtc.getLength(); - } - return length; - } - - private int getMaxSize(Program program, Address addr) { - - // can't go past the end of a block to start with - Address maxAddr = program.getMemory().getBlock(addr).getEnd(); - - // get the next non undefined element in memory - Instruction instr = program.getListing().getInstructionAfter(addr); - if (instr != null) { - Address instrAddr = instr.getMinAddress(); - if (instrAddr.compareTo(maxAddr) < 0) { - maxAddr = instrAddr.subtract(1); - } - } - - Data data = DataUtilities.getNextNonUndefinedDataAfter(program, addr, maxAddr); - if (data != null) { - Address dataAddr = data.getMinAddress(); - if (dataAddr.compareTo(maxAddr) < 0) { - maxAddr = dataAddr.subtract(1); - } - } - - long length = maxAddr.subtract(addr) + 1; - SystemUtilities.assertTrue(length > 0, - "Subtraction an address from the max address in its block should never be negative"); - return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java index afd26f4cd7..c83008091a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataPlugin.java @@ -238,31 +238,13 @@ public class DataPlugin extends Plugin implements DataService { dtmService.addDataTypeManagerChangeListener(adapter); } - boolean isEditDataTypeAllowed(ListingActionContext context) { - Data data = getDataUnit(context); - if (data == null || dtmService == null) { - return false; - } - if (dtmService.isEditable(data.getBaseDataType())) { - return true; - } - Data pdata = data.getParent(); - if (pdata != null) { - if (dtmService.isEditable(pdata.getBaseDataType())) { - return true; - } - } - return false; - } - DataType getEditableDataTypeFromContext(ListingActionContext context) { - ProgramSelection currentSelection = context.getSelection(); - Program currentProgram = context.getProgram(); - DataType editableDataType = null; + ProgramSelection selection = context.getSelection(); + Program program = context.getProgram(); Data data = null; - if (currentSelection != null && !currentSelection.isEmpty()) { - Listing listing = currentProgram.getListing(); - boolean isDataOnly = !listing.getInstructions(currentSelection, true).hasNext(); + if (selection != null && !selection.isEmpty()) { + Listing listing = program.getListing(); + boolean isDataOnly = !listing.getInstructions(selection, true).hasNext(); if (isDataOnly) { data = getDataUnit(context); } @@ -270,30 +252,30 @@ public class DataPlugin extends Plugin implements DataService { else { data = getDataUnit(context); } - if (data != null) { - if (dtmService != null) { - DataType baseDt = data.getBaseDataType(); - if (dtmService.isEditable(baseDt)) { - editableDataType = baseDt; - } - else { - Data pdata = data.getParent(); - if (pdata != null) { - baseDt = pdata.getBaseDataType(); - if (dtmService.isEditable(baseDt)) { - editableDataType = baseDt; - } - } - } - } - } - return editableDataType; + + return getEditableDataType(data); + } + + private DataType getEditableDataType(Data data) { + if (data == null || dtmService == null) { + return null; + } + + DataType baseDt = data.getBaseDataType(); + if (dtmService.isEditable(baseDt)) { + return baseDt; + } + + Data pdata = data.getParent(); + if (pdata != null) { + baseDt = pdata.getBaseDataType(); + if (dtmService.isEditable(baseDt)) { + return baseDt; + } + } + return null; } - /** - * @see ghidra.app.services.DataService#createData(ghidra.program.model.data.DataType, - * ghidra.app.context.ListingActionContext, boolean) - */ @Override public boolean createData(DataType dt, ListingActionContext context, boolean enableConflictHandling) { @@ -307,8 +289,7 @@ public class DataPlugin extends Plugin implements DataService { } /* - * This version uses the ProgramActionContext and does not depend on any - * plugin's currentProgram + * This version uses the ListingActionContext and does not depend on any plugin's currentProgram */ boolean doCreateData(ListingActionContext context, DataType dt) { ProgramSelection selection = context.getSelection(); @@ -316,7 +297,7 @@ public class DataPlugin extends Plugin implements DataService { Program program = context.getProgram(); dt = dt.clone(program.getDataTypeManager()); - boolean didCreateData = true; + boolean didCreateData = false; if (selection != null && !selection.isEmpty()) { didCreateData = createDataForSelection(program, dt, selection); } @@ -375,7 +356,7 @@ public class DataPlugin extends Plugin implements DataService { Listing listing = program.getListing(); Data data = listing.getDataAt(start); if (data == null) { - tool.setStatusInfo("Invalid data location"); + tool.setStatusInfo("Invalid data location. Cannot create data at " + start + '.'); return false; } @@ -393,10 +374,13 @@ public class DataPlugin extends Plugin implements DataService { end = start.addNoWrap(newSize - 1); } catch (AddressOverflowException e) { + tool.setStatusInfo("Invalid data location. Not enough space at " + start + " for " + + newSize + " bytes."); return false; } if (intstrutionExists(listing, dataType, start, end)) { + tool.setStatusInfo("Invalid data location. Instruction exists at " + start + '.'); return false; } @@ -415,7 +399,7 @@ public class DataPlugin extends Plugin implements DataService { Data definedData = DataUtilities.getNextNonUndefinedDataAfter(program, start, blockMaxAddress); if (dataExists(program, dataType, definedData, start, end)) { - return false; + return false; // status updated in 'dataExists()' call } return true; @@ -516,58 +500,6 @@ public class DataPlugin extends Plugin implements DataService { return dataTypeInstance.getLength(); } - boolean doCreateData(Program program, ProgramLocation loc, ProgramSelection sel, DataType dt) { - return doCreateData(program, loc, sel, dt, true); - } - - boolean doCreateData(Program program, ProgramLocation loc, ProgramSelection sel, DataType dt, - boolean convertPointers) { - - // Handle selection case - boolean rc = true; - if (sel != null && !sel.isEmpty()) { - BackgroundCommand cmd; - Address start = sel.getMinAddress(); - InteriorSelection interSel = sel.getInteriorSelection(); - if (interSel != null) { - int[] startPath = interSel.getFrom().getComponentPath(); - int length = (int) sel.getNumAddresses(); // interior selections can't be that big - cmd = new CreateDataInStructureBackgroundCmd(start, startPath, length, dt, - convertPointers); - } - else { - cmd = new CreateDataBackgroundCmd(sel, dt, convertPointers); - } - if (sel.getNumAddresses() < DataPlugin.BACKGROUND_SELECTION_THRESHOLD) { - rc = tool.execute(cmd, program); - } - else { - getPluginTool().executeBackgroundCommand(cmd, program); - } - } - - // Handle single location case - else if (loc != null) { - - Address start = loc.getAddress(); - int[] startPath = loc.getComponentPath(); - Command cmd; - if (startPath != null && startPath.length != 0) { - cmd = new CreateDataInStructureCmd(start, startPath, dt, convertPointers); - } - else { - if (!checkEnoughSpace(program, start, dt, convertPointers)) { - return false; - } - cmd = new CreateDataCmd(start, dt, false, convertPointers); - } - rc = getPluginTool().execute(cmd, program); - } - - updateRecentlyUsed(dt); - return rc; - } - PluginTool getPluginTool() { return tool; } @@ -741,9 +673,6 @@ public class DataPlugin extends Plugin implements DataService { } } - /** - * @see ghidra.framework.plugintool.Plugin#dispose() - */ @Override public void dispose() { favoritesUpdateManager.dispose(); @@ -756,10 +685,6 @@ public class DataPlugin extends Plugin implements DataService { createStructureAction.dispose(); } - /** - * @see ghidra.app.plugin.ProgramPlugin#locationChanged(ghidra.program.util.ProgramLocation) - * @see ghidra.app.plugin.ProgramPlugin#selectionChanged(ghidra.program.util.ProgramSelection) - */ Data getDataUnit(ListingActionContext context) { ProgramLocation location = context.getLocation(); ProgramSelection selection = context.getSelection(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefDialog.java index 50074cbf34..a021ff11c4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CreateTypeDefDialog.java @@ -69,7 +69,7 @@ public class CreateTypeDefDialog extends DialogComponentProvider { // data type info dataTypeEditor = - new DataTypeSelectionEditor(plugin.getTool(), Integer.MAX_VALUE, AllowedDataTypes.ALL); + new DataTypeSelectionEditor(plugin.getTool(), AllowedDataTypes.ALL); panel.add(new GLabel("Data type:")); panel.add(dataTypeEditor.getEditorComponent()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/FunctionPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/FunctionPlugin.java index 96a0e0b4f3..40445439ed 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/FunctionPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/FunctionPlugin.java @@ -373,7 +373,8 @@ public class FunctionPlugin extends Plugin implements DataService { /** * Get an iterator over all functions overlapping the current selection. * If there is no selection any functions overlapping the current location. - * + * + * @param context the context * @return Iterator over functions */ public Iterator getFunctions(ListingActionContext context) { @@ -470,11 +471,10 @@ public class FunctionPlugin extends Plugin implements DataService { * Lay down the specified dataType on a function return, parameter or local variable * based upon the programActionContext. Pointer conversion will be handled * by merging the existing dataType with the specified dataType. - * @param dataType The DataType to create. + * @param dt The DataType to create. * @param programActionContext action context - * @param promptForConflictRemoval if true and specified dataType results in a storage conflict, + * @param enableConflictHandling if true and specified dataType results in a storage conflict, * user may be prompted for removal of conflicting variables (not applicable for return type) - * @return True if the DataType could be created at the given location. */ @Override public boolean createData(DataType dt, ListingActionContext programActionContext, @@ -483,11 +483,12 @@ public class FunctionPlugin extends Plugin implements DataService { } /** - * This method is the same as {@link #createData(DataType, ProgramLocation)}, except that this - * method will use the given value of convertPointers to determine if the new - * DataType should be made into a pointer if the existing DataType is a pointer. - * @param dataType The DataType to create. - * @param location The location at which to create the DataType. + * This method is the same as {@link #createData(DataType, ListingActionContext, boolean)}, + * except that this method will use the given value of convertPointers to determine + * if the new DataType should be made into a pointer if the existing DataType is a pointer. + * + * @param dataType the DataType to create + * @param context the context containing the location at which to create the DataType * @param convertPointers True signals to convert the given DataType to a pointer if there is * an existing pointer at the specified location. * @param promptForConflictRemoval if true and specified dataType results in a storage conflict, @@ -609,23 +610,12 @@ public class FunctionPlugin extends Plugin implements DataService { return null; } -// private boolean checkStackVarToFit(Function fun, StackVariable var, DataType dt) { -// if (var.getDataType() instanceof Pointer) { -// return true; -// } -// int startOffset = var.getLength(); -// if (startOffset < 0) startOffset = 1; -// int size = getMaxStackVariableSize(fun, var); -// if (size < 0) return true; -// return size >= dt.getLength(); -// } - /** - * Return the maximum data type length permitted - * for the specified local variable. A -1 returned - * value indicates no limit imposed. - * @param fun - * @param var + * Return the maximum data type length permitted for the specified local variable. A -1 + * returned value indicates no limit imposed. + * + * @param fun the function + * @param var the variable * @return maximum data type length permitted for var */ int getMaxStackVariableSize(Function fun, Variable var) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/ParameterDataTypeCellEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/ParameterDataTypeCellEditor.java index e82d6c94d0..0be0367f99 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/ParameterDataTypeCellEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/editor/ParameterDataTypeCellEditor.java @@ -61,7 +61,7 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor implements TableCel } private void init() { - editor = new DataTypeSelectionEditor(service, -1, DataTypeParser.AllowedDataTypes.ALL); + editor = new DataTypeSelectionEditor(service, DataTypeParser.AllowedDataTypes.ALL); editor.setTabCommitsEdit(true); editor.setConsumeEnterKeyPress(false); // we want the table to handle Enter key presses @@ -88,25 +88,16 @@ class ParameterDataTypeCellEditor extends AbstractCellEditor implements TableCel } }; - dataTypeChooserButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - DataType dataType = service.getDataType((String) null); - if (dataType != null) { - editor.setCellEditorValue(dataType); - editor.stopCellEditing(); - } - else { - editor.cancelCellEditing(); - } - } - }); + dataTypeChooserButton.addActionListener(e -> SwingUtilities.invokeLater(() -> { + DataType dataType = service.getDataType((String) null); + if (dataType != null) { + editor.setCellEditorValue(dataType); + editor.stopCellEditing(); } - }); + else { + editor.cancelCellEditing(); + } + })); FocusAdapter focusListener = new FocusAdapter() { @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataService.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataService.java index 7acbcf7bf2..37e012ae63 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataService.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/DataService.java @@ -20,13 +20,9 @@ import ghidra.framework.plugintool.ServiceInfo; import ghidra.program.model.data.DataType; /** - * Data Creation service. - * - * NOTE: This version is dependant on the currentProgram of the implementing - * plugin class. If you want a version that is not dependant on it, please use - * DataCreationService + * Service for creating data */ -@ServiceInfo(description = "Data creation service.") +@ServiceInfo(description = "Data creation service") public interface DataService { /** @@ -42,13 +38,12 @@ public interface DataService { /** * Apply the given data type at a location. * - * @param dt - * dataType to create at the location - * @param context - * the context containing program, location, and selection information - * @param enableConflictHandling - * if true, the service may prompt the user to resolve data conflicts + * @param dt data type to create at the location + * @param context the context containing program, location, and selection information + * @param enableConflictHandling if true, the service may prompt the user to resolve data + * conflicts * @return true if the data could be created at the current location */ - public boolean createData(DataType dt, ListingActionContext context, boolean enableConflictHandling); + public boolean createData(DataType dt, ListingActionContext context, + boolean enableConflictHandling); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionDialog.java index add87eb097..833f045eeb 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionDialog.java @@ -63,7 +63,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider { private void buildEditor() { removeWorkPanel(); - editor = new DataTypeSelectionEditor(pluginTool, maxSize, allowedTypes); + editor = new DataTypeSelectionEditor(pluginTool, allowedTypes); editor.setPreferredDataTypeManager(dtm); editor.setConsumeEnterKeyPress(false); // we want to handle Enter key presses editor.addCellEditorListener(new CellEditorListener() { @@ -171,6 +171,7 @@ public class DataTypeSelectionDialog extends DialogComponentProvider { * This method is useful for widgets that have embedded editors that launch this dialog. For * these editors, like tables, it is nice to be able to tab through various editors. This * method allows these editors to keep this functionality, even though a new dialog was shown. + * @param doesCommit true commits edits on Tab press */ public void setTabCommitsEdit(boolean doesCommit) { editor.setTabCommitsEdit(doesCommit); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java index 36bfd231f8..f7f7ba92fa 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/DataTypeSelectionEditor.java @@ -59,7 +59,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { private DropDownSelectionTextField selectionField; private JButton browseButton; private DataTypeManagerService dataTypeManagerService; - private int maxSize = -1; private DataTypeManager dataTypeManager; private DataTypeParser.AllowedDataTypes allowedDataTypes; @@ -69,12 +68,12 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { // optional path to initially select in the data type chooser tree private TreePath initiallySelectedTreePath; - public DataTypeSelectionEditor(ServiceProvider serviceProvider, int maxSize, + public DataTypeSelectionEditor(ServiceProvider serviceProvider, DataTypeParser.AllowedDataTypes allowedDataTypes) { - this(serviceProvider.getService(DataTypeManagerService.class), maxSize, allowedDataTypes); + this(serviceProvider.getService(DataTypeManagerService.class), allowedDataTypes); } - public DataTypeSelectionEditor(DataTypeManagerService service, int maxSize, + public DataTypeSelectionEditor(DataTypeManagerService service, DataTypeParser.AllowedDataTypes allowedDataTypes) { if (service == null) { @@ -82,7 +81,6 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { } this.dataTypeManagerService = service; - this.maxSize = maxSize; this.allowedDataTypes = allowedDataTypes; init(); @@ -102,7 +100,10 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { } /** + * Sets whether this editor should consumer Enter key presses * @see DropDownSelectionTextField#setConsumeEnterKeyPress(boolean) + * + * @param consume true to consume */ public void setConsumeEnterKeyPress(boolean consume) { selectionField.setConsumeEnterKeyPress(consume); @@ -276,6 +277,7 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { /** * Returns the direction of the user triggered navigation; null if the user did not trigger * navigation out of this component. + * @return the direction */ public NavigationDirection getNavigationDirection() { return navigationDirection; @@ -362,10 +364,8 @@ public class DataTypeSelectionEditor extends AbstractCellEditor { catch (CancelledException e) { return false; } + if (newDataType != null) { - if (maxSize >= 0 && newDataType.getLength() > newDataType.getLength()) { - throw new InvalidDataTypeException("data-type larger than " + maxSize + " bytes"); - } selectionField.setSelectedValue(newDataType); return true; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java index 3505971ed9..ce224a8c62 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/data/DataTypeParser.java @@ -95,8 +95,8 @@ public class DataTypeParser { * the source data type manager, this means that all data type managers will be used when * resolving data types. * - * @param dataTypeManagerService - * @param allowedTypes + * @param dataTypeManagerService data-type manager tool service, or null + * @param allowedTypes constrains which data-types may be parsed */ public DataTypeParser(DataTypeQueryService dataTypeManagerService, AllowedDataTypes allowedTypes) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java index 1f6f476384..4d6ffa774f 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/data/ApplyDataTypeToBrowserTest.java @@ -15,6 +15,7 @@ */ package ghidra.app.plugin.core.data; +import static org.hamcrest.core.StringContains.*; import static org.junit.Assert.*; import javax.swing.*; @@ -22,9 +23,9 @@ import javax.swing.*; import org.apache.commons.lang3.StringUtils; import org.junit.*; -import docking.DialogComponentProvider; -import docking.DockingDialog; +import docking.*; import docking.action.DockingActionIf; +import generic.test.TestUtils; import ghidra.app.cmd.disassemble.DisassembleCommand; import ghidra.app.plugin.core.codebrowser.*; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; @@ -50,7 +51,6 @@ import ghidra.test.TestEnv; public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationTest { private static final String PROGRAM_FILENAME = "WallaceSrc"; - private static final int TASK_TIMEOUT = 2000; private static final String CYCLE_BYTE_WORD_DWORD_QWORD = "Cycle: byte,word,dword,qword"; private TestEnv env; @@ -63,6 +63,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT private DataTypeArchiveGTree tree; private ArchiveRootNode archiveRootNode; private ArchiveNode programNode; + private CodeViewerProvider codeViewerProvider; @Before public void setUp() throws Exception { @@ -94,6 +95,9 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT assertNotNull("Did not successfully wait for the program node to load", programNode); tool.showComponentProvider(provider, true); + + CodeBrowserPlugin codeBrowserPlugin = env.getPlugin(CodeBrowserPlugin.class); + codeViewerProvider = codeBrowserPlugin.getProvider(); } private ProgramDB buildWallaceSrcProgram() throws Exception { @@ -121,24 +125,19 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT executeOnSwingWithoutBlocking(() -> { ProgramManager pm = tool.getService(ProgramManager.class); pm.closeProgram(); - }); // this handles the save changes dialog and potential analysis dialogs - closeAllWindowsAndFrames(); + closeAllWindows(); - env.release(program); env.dispose(); } @Test public void testChooseDataTypeOnDefaultDts() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); + showDataTypeChooser(); chooseInDialog("_person"); @@ -152,12 +151,9 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testChooseDataTypeOnUndefinedDts() throws Exception { createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); + showDataTypeChooser(); chooseInDialog("_person"); @@ -170,80 +166,67 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testChooseDataTypeOnDefinedDts() throws Exception { + + // + // Test that apply data on an existing type will offer to clear that type + // + createData("004027d1", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); + showDataTypeChooser(); - DialogComponentProvider dialog = - chooseInDialog("_person", "_person doesn't fit within 1 bytes, need 41 bytes"); + chooseInDialog("_person"); + + pressConflictingDataDialog("Yes"); Data data = program.getListing().getDataAt(addr("004027d0")); - assertEquals(DataType.DEFAULT, data.getDataType()); - assertEquals(addr("004027d0"), data.getMaxAddress()); - - pressButtonByText(dialog, "Cancel"); - waitForSwing(); + ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); + assertEquals(dataType, data.getDataType()); + assertEquals(addr("004027f8"), data.getMaxAddress()); } @Test public void testChooseDataTypeOnDefinedAndUndefinedDts() throws Exception { createData("004027d1", new ByteDataType()); createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); - - DialogComponentProvider dialog = - chooseInDialog("_person", "_person doesn't fit within 1 bytes, need 41 bytes"); + showDataTypeChooser(); + chooseInDialog("_person"); + pressConflictingDataDialog("No"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(DataType.DEFAULT, data.getDataType()); assertEquals(addr("004027d0"), data.getMaxAddress()); - - pressButtonByText(dialog, "Cancel"); - waitForSwing(); } @Test public void testChooseDataTypeWhereDoesNotFit() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027e0"); - waitForSwing(); + goTo("004027e0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); + showDataTypeChooser(); - DialogComponentProvider dialog = - chooseInDialog("_person", "_person doesn't fit within 32 bytes, need 41 bytes"); + showDataTypeChooser(); + chooseInDialog("_person"); + assertToolStatus("Not enough room in memory block containing address"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(DataType.DEFAULT, data.getDataType()); assertEquals(addr("004027d0"), data.getMaxAddress()); - - pressButtonByText(dialog, "Cancel"); - waitForSwing(); } @Test public void testCreateArrayOnDefaultDts() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); - assertNotNull(chooseDataTypeAction); performAction(chooseDataTypeAction, codeViewerProvider, false); waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); JTextField tf = findComponent(dialog, JTextField.class); triggerText(tf, "48"); waitForSwing(); @@ -261,8 +244,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCreateArrayOnUndefinedDts() throws Exception { createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); assertNotNull(chooseDataTypeAction); @@ -270,8 +252,8 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); + JTextField tf = findComponent(dialog, JTextField.class); triggerText(tf, "48"); @@ -288,8 +270,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCreateArrayFailureOnDefinedDts() throws Exception { createData("004027d4", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); assertNotNull(chooseDataTypeAction); @@ -297,8 +278,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); checkStatus((DockingDialog) dialog, "Entering more than 4 will overwrite existing data"); @@ -326,8 +306,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT createCode("004027d4", 1); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); assertNotNull(chooseDataTypeAction); @@ -335,8 +314,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); checkStatus((DockingDialog) dialog, " "); @@ -364,8 +342,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT createCode("004027d4", 1); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); assertNotNull(chooseDataTypeAction); @@ -373,8 +350,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); checkStatus((DockingDialog) dialog, " "); @@ -396,8 +372,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCreateArrayOverwriteOnDefinedDts() throws Exception { createData("004027d4", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Define Array"); assertNotNull(chooseDataTypeAction); @@ -405,8 +380,8 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT waitForSwing(); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Create undefined[]", 2000); - assertNotNull(dialog); + JDialog dialog = waitForJDialog("Create undefined[]"); + JTextField tf = findComponent(dialog, JTextField.class); triggerText(tf, "48"); waitForSwing(); @@ -417,8 +392,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT pressButtonByText(dialog, "OK"); waitForSwing(); - dialog = waitForJDialog(tool.getToolFrame(), "Overwrite Existing Data?", 2000); - assertNotNull(dialog); + dialog = waitForJDialog("Overwrite Existing Data?"); pressButtonByText(dialog, "Yes"); waitForSwing(); @@ -434,8 +408,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); plugin.setRecentlyUsed(dataType); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Recently Used"); assertNotNull(chooseDataTypeAction); @@ -455,8 +428,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT plugin.setRecentlyUsed(dataType); createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Recently Used"); assertNotNull(chooseDataTypeAction); @@ -476,20 +448,12 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT plugin.setRecentlyUsed(dataType); createData("004027d1", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Recently Used"); - assertNotNull(chooseDataTypeAction); performAction(chooseDataTypeAction, codeViewerProvider, false); - waitForSwing(); - - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "Yes"); - waitForSwing(); + pressConflictingDataDialog("Yes"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(dataType, data.getDataType()); @@ -503,20 +467,13 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT plugin.setRecentlyUsed(dataType); createData("004027d1", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Recently Used"); assertNotNull(chooseDataTypeAction); performAction(chooseDataTypeAction, codeViewerProvider, false); - waitForSwing(); - - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "No"); - waitForSwing(); + pressConflictingDataDialog("No"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(DataType.DEFAULT, data.getDataType()); @@ -525,21 +482,18 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testFavoriteOnDefaultDts() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); + goTo("004027d0"); + + ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); + + // Set _person as a favorite + runSwing(() -> dataTypeManager.setFavorite(dataType, true), false); waitForSwing(); - final ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); - final DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - - // Set _person as a favorite. - executeOnSwingWithoutBlocking(() -> dataTypeManager.setFavorite(dataType, true)); - waitForSwing(); - - // Choose favorite. + // Choose favorite DockingActionIf favoriteAction = getAction(dataPlugin, "Define _person"); - assertNotNull(favoriteAction); performAction(favoriteAction, codeViewerProvider, true); - waitForSwing(); Data data = program.getListing().getDataAt(addr("004027d0")); @@ -550,21 +504,18 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testFavoriteOnUndefinedDts() throws Exception { createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - final ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); - final DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); + ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - // Set _person as a favorite. - executeOnSwingWithoutBlocking(() -> dataTypeManager.setFavorite(dataType, true)); + // Set _person as a favorite + runSwing(() -> dataTypeManager.setFavorite(dataType, true), false); waitForSwing(); // Choose favorite. DockingActionIf favoriteAction = getAction(dataPlugin, "Define _person"); - assertNotNull(favoriteAction); performAction(favoriteAction, codeViewerProvider, true); - waitForSwing(); Data data = program.getListing().getDataAt(addr("004027d0")); @@ -575,28 +526,20 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testFavoriteOnDefinedDtsAnswerYes() throws Exception { createData("004027d1", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); + goTo("004027d0"); + + ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); + + // Set _person as a favorite + runSwing(() -> dataTypeManager.setFavorite(dataType, true), false); waitForSwing(); - final ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); - final DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - - // Set _person as a favorite. - executeOnSwingWithoutBlocking(() -> dataTypeManager.setFavorite(dataType, true)); - waitForSwing(); - - // Choose favorite. + // Choose favorite DockingActionIf favoriteAction = getAction(dataPlugin, "Define _person"); - assertNotNull(favoriteAction); performAction(favoriteAction, codeViewerProvider, false); - waitForSwing(); - - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "Yes"); - waitForSwing(); + pressConflictingDataDialog("Yes"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(dataType, data.getDataType()); @@ -606,28 +549,20 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testFavoriteOnDefinedDtsAnswerNo() throws Exception { createData("004027d1", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - final ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); - final DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); + ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); + DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - // Set _person as a favorite. - executeOnSwingWithoutBlocking(() -> dataTypeManager.setFavorite(dataType, true)); + // Set _person as a favorite + runSwing(() -> dataTypeManager.setFavorite(dataType, true), false); waitForSwing(); // Choose favorite. DockingActionIf favoriteAction = getAction(dataPlugin, "Define _person"); - assertNotNull(favoriteAction); performAction(favoriteAction, codeViewerProvider, false); - waitForSwing(); - - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "No"); - waitForSwing(); + pressConflictingDataDialog("No"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(DataType.DEFAULT, data.getDataType()); @@ -636,12 +571,9 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCycleOnDefaultDts() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); - DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); - assertNotNull(chooseDataTypeAction); - performAction(chooseDataTypeAction, codeViewerProvider, false); + showDataTypeChooser(); chooseInDialog("_person"); @@ -655,8 +587,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCycleOnUndefinedDts() throws Exception { createData("004027d2", new Undefined4DataType()); - positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); Data data = program.getListing().getDataAt(addr("004027d0")); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); @@ -689,8 +620,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testCycleOnDefinedDts() throws Exception { createData("004027d3", new ByteDataType()); - positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); Data data = program.getListing().getDataAt(addr("004027d0")); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); @@ -722,13 +652,12 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testDragNDropOnDefaultDts() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - dragNDropDataTypeToCurrentBrowserLocation(codeViewerProvider, dataType); + dragNDropDataTypeToCurrentBrowserLocation(dataType); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(dataType, data.getDataType()); @@ -738,13 +667,12 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testDragNDropOnUndefinedDts() throws Exception { createData("004027d2", new Undefined4DataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - dragNDropDataTypeToCurrentBrowserLocation(codeViewerProvider, dataType); + dragNDropDataTypeToCurrentBrowserLocation(dataType); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(dataType, data.getDataType()); @@ -754,19 +682,14 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testDragNDropYesOnDefinedDts() throws Exception { createData("004027d3", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - dragNDropDataTypeToCurrentBrowserLocation(codeViewerProvider, dataType); + dragNDropDataTypeToCurrentBrowserLocation(dataType); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "Yes"); - waitForSwing(); + pressConflictingDataDialog("Yes"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(dataType, data.getDataType()); @@ -776,19 +699,14 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testDragNDropNoOnDefinedDts() throws Exception { createData("004027d3", new ByteDataType()); - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027d0"); - waitForSwing(); + goTo("004027d0"); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - dragNDropDataTypeToCurrentBrowserLocation(codeViewerProvider, dataType); + dragNDropDataTypeToCurrentBrowserLocation(dataType); - JDialog dialog = waitForJDialog(tool.getToolFrame(), "Data Conflict", 2000); - assertNotNull(dialog); - - pressButtonByText(dialog, "No"); - waitForSwing(); + pressConflictingDataDialog("No"); Data data = program.getListing().getDataAt(addr("004027d0")); assertEquals(DataType.DEFAULT, data.getDataType()); @@ -797,13 +715,12 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT @Test public void testDragNDropWhereDoesNotFit() throws Exception { - CodeViewerProvider codeViewerProvider = positionListingCursorAtAddress("004027e0"); - waitForSwing(); + goTo("004027e0"); ProgramDataTypeManager dataTypeManager = program.getDataTypeManager(); DataType dataType = dataTypeManager.getDataType(new CategoryPath("/"), "_person"); - dragNDropDataTypeToCurrentBrowserLocation(codeViewerProvider, dataType); + dragNDropDataTypeToCurrentBrowserLocation(dataType); Data data = program.getListing().getDataAt(addr("004027e0")); assertEquals(DataType.DEFAULT, data.getDataType()); @@ -814,24 +731,36 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT // Private Helper Methods //================================================================================================== - private DialogComponentProvider chooseInDialog(String typeName) { + private void assertToolStatus(String expectedMessage) { + waitForSwing(); + PluginTool pluginTool = plugin.getTool(); + DockingWindowManager windowManager = pluginTool.getWindowManager(); + Object rootNode = TestUtils.invokeInstanceMethod("getRootNode", windowManager); + StatusBar statusBar = (StatusBar) TestUtils.getInstanceField("statusBar", rootNode); + String actualMessage = runSwing(() -> statusBar.getStatusText()); + assertThat("The tool's status text was not set", actualMessage, + containsString(expectedMessage)); + } + + private void showDataTypeChooser() { + DockingActionIf chooseDataTypeAction = getAction(dataPlugin, "Choose Data Type"); + performAction(chooseDataTypeAction, codeViewerProvider, false); + } + + private void pressConflictingDataDialog(String button) { + DialogComponentProvider dialog = waitForDialogComponent("Data Conflict"); + pressButtonByText(dialog, button); + waitForTasks(); + } + + private DialogComponentProvider chooseInDialog(String typeName) { return chooseInDialog(typeName, null); } - /** - * Waits for the Data Type Chooser dialog to appear. Then, enters the given text to - * select that type. Finally, OK is pressed. The dialog may or may not go away, - * depending upon the state of the dialog. - * - * @param typeName the name of the dt - * @param errorStatus the expected status after pressing OK - */ private DialogComponentProvider chooseInDialog(String typeName, String errorStatus) { DataTypeSelectionDialog dialog = waitForDialogComponent(DataTypeSelectionDialog.class); - - assertNotNull(dialog); JTextField tf = findComponent(dialog, JTextField.class); triggerText(tf, "_person"); waitForSwing(); @@ -862,7 +791,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT } private void dragNDropDataTypeToCurrentBrowserLocation( - final CodeViewerProvider codeViewerProvider, final DataType dataType) { + final DataType dataType) { executeOnSwingWithoutBlocking(() -> { // Simulate the drag-n-drop of the data type onto the location. ProgramLocation programLocation = codeViewerProvider.getLocation(); @@ -880,12 +809,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT } private void doAction(Plugin pluginForAction, String name, boolean waitForCompletion) { - CodeBrowserPlugin codeBrowserPlugin = env.getPlugin(CodeBrowserPlugin.class); - assertNotNull(codeBrowserPlugin); - CodeViewerProvider connectedProvider = - (CodeViewerProvider) getInstanceField("connectedProvider", codeBrowserPlugin); - assertNotNull(connectedProvider); - CodeViewerActionContext codeViewerContext = new CodeViewerActionContext(connectedProvider); + CodeViewerActionContext codeViewerContext = new CodeViewerActionContext(codeViewerProvider); DockingActionIf action = getAction(pluginForAction, name); assertNotNull("Action was not found: " + name, action); @@ -894,28 +818,26 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT } try { - performAction(action, connectedProvider, waitForCompletion); + performAction(action, codeViewerProvider, waitForCompletion); } catch (Throwable t) { - t.printStackTrace(); - Assert.fail("Action '" + name + "' failed: " + t.toString()); + failWithException("Action '" + name + "' failed: ", t); } } - private CodeViewerProvider positionListingCursorAtAddress(String addressString) { - CodeBrowserPlugin codeBrowserPlugin = env.getPlugin(CodeBrowserPlugin.class); - assertNotNull(codeBrowserPlugin); + private void goTo(String addressString) { + CodeBrowserPlugin codeBrowser = env.getPlugin(CodeBrowserPlugin.class); Address address = program.getAddressFactory().getAddress(addressString); - codeBrowserPlugin.goToField(address, "Address", 0, 0); - assertEquals(addressString, codeBrowserPlugin.getCurrentAddress().toString()); + codeBrowser.goToField(address, "Address", 0, 0); + assertEquals(addressString, codeBrowser.getCurrentAddress().toString()); CodeViewerProvider connectedProvider = - (CodeViewerProvider) getInstanceField("connectedProvider", codeBrowserPlugin); + (CodeViewerProvider) getInstanceField("connectedProvider", codeBrowser); assertNotNull(connectedProvider); ListingPanel listingPanel = (ListingPanel) getInstanceField("listingPanel", connectedProvider); assertNotNull(listingPanel); - return connectedProvider; + waitForSwing(); } private void createData(String addressString, DataType dataType) throws Exception { @@ -928,7 +850,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT finally { program.endTransaction(transactionID, success); } - waitForProgram(); + waitForProgram(program); } private void createCode(String addressString, int len) throws Exception { @@ -945,7 +867,7 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT finally { program.endTransaction(transactionID, success); } - waitForProgram(); + waitForProgram(program); } private Address addr(String addressString) { @@ -955,10 +877,4 @@ public class ApplyDataTypeToBrowserTest extends AbstractGhidraHeadedIntegrationT private void waitForTree() { waitForTree(tree); } - - private void waitForProgram() throws Exception { - program.flushEvents(); - waitForTasks(); - waitForSwing(); - } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java index 46fb25bf6f..19efc9886b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/datatype/DataTypeSelectionDialogTest.java @@ -46,7 +46,8 @@ import generic.test.AbstractGTest; import generic.util.WindowUtilities; import generic.util.image.ImageUtils; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.archive.Archive; +import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog; @@ -1175,7 +1176,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration JPanel editorPanel = new JPanel(new BorderLayout()); DataTypeSelectionEditor editor = - new DataTypeSelectionEditor(tool, -1, AllowedDataTypes.ALL); + new DataTypeSelectionEditor(tool, AllowedDataTypes.ALL); editor.setPreferredDataTypeManager(program.getDataTypeManager()); editorPanel.add(panelUpdateField, BorderLayout.SOUTH); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/data/CreateDataCmdTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/data/CreateDataCmdTest.java index f3a7e61497..ef1c2adca8 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/data/CreateDataCmdTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/data/CreateDataCmdTest.java @@ -45,14 +45,6 @@ public class CreateDataCmdTest extends AbstractGenericTest { private Listing listing; private ProgramBuilder builder; - /** - * Constructor for CreateDataCmdTest. - * @param arg0 - */ - public CreateDataCmdTest() { - super(); - } - @Before public void setUp() throws Exception { program = buildProgram(); @@ -637,8 +629,9 @@ public class CreateDataCmdTest extends AbstractGenericTest { cmd.applyTo(program); // Add external reference from pointer - program.getReferenceManager().addExternalReference(addr, "OtherFile", "ExtLabel", null, - SourceType.USER_DEFINED, 0, RefType.DATA); + program.getReferenceManager() + .addExternalReference(addr, "OtherFile", "ExtLabel", null, + SourceType.USER_DEFINED, 0, RefType.DATA); // Undefined* becomes Byte* cmd = new CreateDataCmd(addr, false, true, new ByteDataType()); @@ -724,7 +717,8 @@ public class CreateDataCmdTest extends AbstractGenericTest { assertEquals(10, dt.getLength()); // Byte[] becomes Byte - CreateDataCmd cmd = new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA); + CreateDataCmd cmd = + new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA); cmd.applyTo(program); d = listing.getDataAt(addr); @@ -792,7 +786,8 @@ public class CreateDataCmdTest extends AbstractGenericTest { assertEquals(10, dt.getLength()); // struct becomes Byte - CreateDataCmd cmd = new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA); + CreateDataCmd cmd = + new CreateDataCmd(addr, new ByteDataType(), false, ClearDataMode.CLEAR_SINGLE_DATA); cmd.applyTo(program); d = listing.getDataAt(addr); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java b/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java index 24288d522f..98dcc4a78d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java @@ -139,7 +139,7 @@ class DetachedWindowNode extends WindowNode { } private void setFrameIcon(Frame frame, Image image) { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(image); setFrameIcon(frame, list); } @@ -222,7 +222,7 @@ class DetachedWindowNode extends WindowNode { } private String getTitleOfChildren() { - List placeholders = new ArrayList(); + List placeholders = new ArrayList<>(); child.populateActiveComponents(placeholders); @@ -253,12 +253,12 @@ class DetachedWindowNode extends WindowNode { // the same provider. // Map> providerNameToPlacholdersMap = - new HashMap>(); + new HashMap<>(); for (ComponentPlaceholder placeholder : placeholders) { String providerName = placeholder.getProvider().getName(); List list = providerNameToPlacholdersMap.get(providerName); if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); providerNameToPlacholdersMap.put(providerName, list); } list.add(placeholder); @@ -267,13 +267,13 @@ class DetachedWindowNode extends WindowNode { // // Turn the created mapping into a mapping of providers names to sub-titles // - Map> providerNameToTitlesMap = new HashMap>(); + Map> providerNameToTitlesMap = new HashMap<>(); Set>> entrySet = providerNameToPlacholdersMap.entrySet(); for (Entry> entry : entrySet) { String providerName = entry.getKey(); List placeholdersList = entry.getValue(); - List titles = new ArrayList(); + List titles = new ArrayList<>(); if (placeholdersList.size() == 1) { titles.add(placeholdersList.get(0).getTitle()); } @@ -290,7 +290,7 @@ class DetachedWindowNode extends WindowNode { // Use the created mapping to create an individual title based on a single provider // or a group of providers. // - List finalTitles = new ArrayList(); + List finalTitles = new ArrayList<>(); Set>> providersEntrySet = providerNameToTitlesMap.entrySet(); for (Entry> entry : providersEntrySet) { String providerName = entry.getKey(); @@ -539,12 +539,11 @@ class DetachedWindowNode extends WindowNode { /** * Set the status text - * @param text + * @param text the text */ public void setStatusText(String text) { if (statusBar != null) { - boolean isActive = window == null ? false : window.isActive(); - statusBar.setStatusText(text, isActive); + statusBar.setStatusText(text); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java index 94456dd833..9b80e8cead 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java @@ -133,6 +133,7 @@ class RootNode extends WindowNode { /** * Return whether the component for this RootNode is visible. */ + @Override boolean isVisible() { return windowWrapper.isVisible(); } @@ -574,7 +575,7 @@ class RootNode extends WindowNode { return; } - statusBar.setStatusText(text, getMainWindow().isActive()); + statusBar.setStatusText(text); Iterator iter = detachedWindows.iterator(); while (iter.hasNext()) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/StatusBar.java b/Ghidra/Framework/Docking/src/main/java/docking/StatusBar.java index d743a87ff3..02774917ca 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/StatusBar.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/StatusBar.java @@ -24,6 +24,7 @@ import javax.swing.*; import javax.swing.Timer; import javax.swing.border.Border; +import org.apache.commons.lang3.StringUtils; import org.jdesktop.animation.timing.Animator; import docking.util.AnimationUtils; @@ -153,14 +154,40 @@ public class StatusBar extends JPanel { statusAreaPanel.remove(c.getParent()); } - public void setStatusText(String text, boolean isActiveWindow) { - SystemUtilities.runIfSwingOrPostSwingLater(() -> doSetStatusText(text, isActiveWindow)); + /** + * Returns the current text in this status bar + * @return the text + */ + public String getStatusText() { + return statusLabel.getText(); } - private void doSetStatusText(String text, boolean isActiveWindow) { + /** + * Deprecated. Call {@link #setStatusText(String)} instead. + * + * @param text the text + * @param isActiveWindow this parameter is ignored + * @deprecated Call {@link #setStatusText(String)} instead. Remove after 9.3 + */ + @Deprecated + public void setStatusText(String text, boolean isActiveWindow) { + setStatusText(text); + } + + /** + * Sets the status text + * @param text the text + */ + public void setStatusText(String text) { + // Run this later in case we are in the midst of a Java focus transition, such as when a + // dialog is closing. If we don't let the focus transition finish, then we will not + // correctly locate the active window. + Swing.runLater(() -> doSetStatusText(text)); + } + + private void doSetStatusText(String text) { if (text == null) { - // not sure what do do here, do nothing for now so that the previous message - // stays around + // do nothing for now so that the previous message stays around return; } @@ -171,11 +198,12 @@ public class StatusBar extends JPanel { statusLabel.setToolTipText(getToolTipText()); statusLabel.setForeground(Color.BLACK); - if (!isActiveWindow) { + if (StringUtils.isBlank(updatedText)) { return; } - if (updatedText.trim().isEmpty()) { + Window window = WindowUtilities.windowForComponent(statusLabel); + if (!window.isActive()) { return; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java index 80cf115ca2..db1a2d9f79 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/util/AnimationUtils.java @@ -496,6 +496,13 @@ public class AnimationUtils { } Rectangle startBounds = component.getBounds(); + Container parent = component.getParent(); + if (parent == null) { + // the given component is a Window; make it be the root + startBounds.x = 0; + startBounds.y = 0; + } + Point relativeStartCenter = new Point((int) startBounds.getCenterX(), (int) startBounds.getCenterY()); return SwingUtilities.convertPoint(component.getParent(), relativeStartCenter, @@ -556,11 +563,20 @@ public class AnimationUtils { int scaledWidth = (int) (defaultBounds.width * percentComplete); int scaledHeight = (int) (defaultBounds.height * percentComplete); + // gains opacity as it gets closer to the end; capped at the given percentage + float opacity = (float) Math.min(.65, percentComplete); + Composite originalComposite = g2d.getComposite(); + AlphaComposite alphaComposite = AlphaComposite.getInstance( + AlphaComposite.SrcOver.getRule(), opacity); + g2d.setComposite(alphaComposite); + // // Calculate the position of the image. At 100% we want to be in the center of // the display; at 0% we want to be at our default location // g2d.drawImage(image, (int) currentX, (int) currentY, scaledWidth, scaledHeight, null); + + g2d.setComposite(originalComposite); } } diff --git a/Ghidra/Framework/Docking/src/test.slow/java/docking/StatusBarTest.java b/Ghidra/Framework/Docking/src/test.slow/java/docking/StatusBarTest.java index 81975fc4ff..be729971d2 100644 --- a/Ghidra/Framework/Docking/src/test.slow/java/docking/StatusBarTest.java +++ b/Ghidra/Framework/Docking/src/test.slow/java/docking/StatusBarTest.java @@ -15,7 +15,7 @@ */ package docking; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import javax.swing.JFrame; import javax.swing.JLabel; @@ -38,15 +38,6 @@ public class StatusBarTest extends AbstractDockingTest { private StatusBar statusBar; private JFrame testFrame; - /** - * Constructor to run the test passed as a parameter. - * - * @param testName The name of the test to run - */ - public StatusBarTest() { - super(); - } - @Before public void setUp() throws Exception { @@ -113,78 +104,50 @@ public class StatusBarTest extends AbstractDockingTest { final JLabel label2 = new GDLabel("Test Label 2"); // normal add/remove operations - runSwing(new Runnable() { - @Override - public void run() { - statusBar.addStatusItem(label1, true, true); - statusBar.addStatusItem(label2, true, true); - } + runSwing(() -> { + statusBar.addStatusItem(label1, true, true); + statusBar.addStatusItem(label2, true, true); }); - runSwing(new Runnable() { - @Override - public void run() { - statusBar.removeStatusItem(label1); - statusBar.removeStatusItem(label2); - } + runSwing(() -> { + statusBar.removeStatusItem(label1); + statusBar.removeStatusItem(label2); }); // method call variations - runSwing(new Runnable() { - @Override - public void run() { - statusBar.addStatusItem(label1, false, true); - statusBar.addStatusItem(label2, true, false); - } + runSwing(() -> { + statusBar.addStatusItem(label1, false, true); + statusBar.addStatusItem(label2, true, false); }); - runSwing(new Runnable() { - @Override - public void run() { - statusBar.removeStatusItem(label1); - statusBar.removeStatusItem(label2); - } + runSwing(() -> { + statusBar.removeStatusItem(label1); + statusBar.removeStatusItem(label2); }); // repeat adding - runSwing(new Runnable() { - @Override - public void run() { - statusBar.addStatusItem(label1, true, true); - statusBar.addStatusItem(label1, true, true); - } + runSwing(() -> { + statusBar.addStatusItem(label1, true, true); + statusBar.addStatusItem(label1, true, true); }); // removing non-existent elements - runSwing(new Runnable() { - @Override - public void run() { - statusBar.removeStatusItem(label2); + runSwing(() -> statusBar.removeStatusItem(label2)); + + runSwing(() -> { + try { + statusBar.removeStatusItem(new GLabel("Test Label 3")); + + Assert.fail("Did not receive an expected NullPointerException."); } - }); - - runSwing(new Runnable() { - @Override - public void run() { - try { - statusBar.removeStatusItem(new GLabel("Test Label 3")); - - Assert.fail("Did not receive an expected NullPointerException."); - } - catch (NullPointerException npe) { - // expected, caused by a null parent - } + catch (NullPointerException npe) { + // expected, caused by a null parent } }); } private void setStatusText(final String text) { - runSwing(new Runnable() { - @Override - public void run() { - statusBar.setStatusText(text, true); - } - }); - waitForPostedSwingRunnables(); + runSwing(() -> statusBar.setStatusText(text)); + waitForSwing(); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java index e7de5c10ef..8d62b17349 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataUtilities.java @@ -26,9 +26,8 @@ import ghidra.util.exception.InvalidInputException; public final class DataUtilities { - //private final static Set VALID_DATA_TYPE_NAME_SET = SystemUtilities.makeSet(FileSystem.DASH_CHAR, '_',' ', '.'); - private DataUtilities() { + // utilities class } /** @@ -88,95 +87,58 @@ public final class DataUtilities { /** * Create data where existing data may already exist. - * @param program + * @param program the program * @param addr data address (offcut data address only allowed if clearMode == ClearDataMode.CLEAR_ALL_CONFLICT_DATA) - * @param newDataType new data-type being applied + * @param newType new data-type being applied * @param length data length (used only for Dynamic newDataType which has canSpecifyLength()==true) * @param stackPointers see {@link #reconcileAppliedDataType(DataType, DataType, boolean)} * @param clearMode see CreateDataMode * @return new data created * @throws CodeUnitInsertionException if data creation failed */ - public static Data createData(Program program, Address addr, DataType newDataType, int length, + public static Data createData(Program program, Address addr, DataType newType, int length, boolean stackPointers, ClearDataMode clearMode) throws CodeUnitInsertionException { Listing listing = program.getListing(); ReferenceManager refMgr = program.getReferenceManager(); - Data data = listing.getDataAt(addr); - DataType existingDT = null; + Data data = getData(addr, clearMode, listing); + int existingLength = addr.getAddressSpace().getAddressableUnitSize(); + DataType existingType = data.getDataType(); Reference extRef = null; - int existingDataLen = addr.getAddressSpace().getAddressableUnitSize(); - if (data == null) { - if (clearMode == ClearDataMode.CLEAR_ALL_CONFLICT_DATA || - clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA) { - // allow offcut addr if CLEAR_ALL_CONFLICT_DATA - data = listing.getDataContaining(addr); - if (data != null && clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && - !Undefined.isUndefined(data.getDataType())) { - data = null; // force error - } - } - if (data == null) { - throw new CodeUnitInsertionException("Could not create Data at address " + addr); - } - } - else { - existingDataLen = data.getLength(); - existingDT = data.getDataType(); + if (!isParentData(data, addr)) { - if (data.isDefined() && newDataType.isEquivalent(existingDT)) { + existingLength = data.getLength(); + if (data.isDefined() && newType.isEquivalent(existingType)) { return data; } if (!stackPointers && clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && - !Undefined.isUndefined(existingDT)) { + !Undefined.isUndefined(existingType)) { throw new CodeUnitInsertionException("Could not create Data at address " + addr); } + // TODO: This can probably be eliminated // Check for external reference on pointer - if ((stackPointers || newDataType instanceof Pointer) && - existingDT instanceof Pointer) { - // TODO: This can probably be eliminated - Reference[] refs = refMgr.getReferencesFrom(addr); - for (Reference ref : refs) { - if (ref.getOperandIndex() == 0 && ref.isExternalReference()) { - extRef = ref; - break; - } - } - } + extRef = + getExternalPointerReference(addr, newType, stackPointers, refMgr, existingType); } - newDataType = newDataType.clone(program.getDataTypeManager()); - newDataType = reconcileAppliedDataType(existingDT, newDataType, stackPointers); + newType = newType.clone(program.getDataTypeManager()); + newType = reconcileAppliedDataType(existingType, newType, stackPointers); - DataType realType = newDataType; - if (newDataType instanceof TypeDef) { - realType = ((TypeDef) newDataType).getBaseDataType(); + DataType realType = newType; + if (newType instanceof TypeDef) { + realType = ((TypeDef) newType).getBaseDataType(); } // is the datatype already there? - if (!(realType instanceof Dynamic) && !(realType instanceof FactoryDataType) && - newDataType.equals(existingDT)) { + if (isExistingNonDynamicType(realType, newType, existingType)) { return data; } - MemBuffer memBuf = new DumbMemBufferImpl(program.getMemory(), addr); - DataTypeInstance dti; - if (length > 0 && (realType instanceof Dynamic) && - ((Dynamic) realType).canSpecifyLength()) { - dti = DataTypeInstance.getDataTypeInstance(newDataType, memBuf, length); - } - else { - dti = DataTypeInstance.getDataTypeInstance(newDataType, memBuf); - } - if (dti == null) { - throw new CodeUnitInsertionException( - "Could not create DataType " + newDataType.getDisplayName()); - } - - if (stackPointers && existingDT instanceof Pointer && newDataType instanceof Pointer) { + DataTypeInstance dti = getDtInstance(program, addr, newType, length, realType); + if (stackPointers && existingType instanceof Pointer && newType instanceof Pointer) { listing.clearCodeUnits(addr, addr, false); } @@ -190,71 +152,200 @@ public final class DataUtilities { listing.clearCodeUnits(addr, addr, false); } else { - checkEnoughSpace(program, addr, existingDataLen, dti, clearMode); + checkEnoughSpace(program, addr, existingLength, dti, clearMode); } newData = listing.createData(addr, dti.getDataType(), dti.getLength()); } - // if this was a pointer and had an external reference, put it back! - if ((newDataType instanceof Pointer) && extRef != null) { - ExternalLocation extLoc = ((ExternalReference) extRef).getExternalLocation(); - try { - refMgr.addExternalReference(extRef.getFromAddress(), 0, extLoc, extRef.getSource(), - extRef.getReferenceType()); - } - catch (InvalidInputException e) { - throw new AssertException(e); + restoreReference(newType, refMgr, extRef); + + return newData; + } + + private static boolean isParentData(Data data, Address addr) { + return !data.getAddress().equals(addr); + } + + private static Data getData(Address addr, ClearDataMode clearMode, Listing listing) + throws CodeUnitInsertionException { + + Data data = listing.getDataAt(addr); + if (data != null) { + return data; // existing data; it us possible to create data + } + + // null data; see if we are in a composite + if (clearMode == ClearDataMode.CLEAR_ALL_CONFLICT_DATA || + clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA) { + + // allow offcut addr if CLEAR_ALL_CONFLICT_DATA + data = listing.getDataContaining(addr); + if (data != null && clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && + !Undefined.isUndefined(data.getDataType())) { + data = null; // force error } } - return newData; + + // null data implies that we cannot create data at this address + if (data == null) { + throw new CodeUnitInsertionException("Could not create Data at address " + addr); + } + + return data; + } + + private static DataTypeInstance getDtInstance(Program program, Address addr, DataType newType, + int length, DataType realType) throws CodeUnitInsertionException { + + MemBuffer memBuf = new DumbMemBufferImpl(program.getMemory(), addr); + DataTypeInstance dti; + if (length > 0 && (realType instanceof Dynamic) && + ((Dynamic) realType).canSpecifyLength()) { + dti = DataTypeInstance.getDataTypeInstance(newType, memBuf, length); + } + else { + dti = DataTypeInstance.getDataTypeInstance(newType, memBuf); + } + + if (dti == null) { + throw new CodeUnitInsertionException( + "Could not create DataType " + newType.getDisplayName()); + } + + return dti; + } + + private static boolean isExistingNonDynamicType(DataType realType, DataType newType, + DataType existingType) { + + if (realType instanceof Dynamic || realType instanceof FactoryDataType) { + return false; + } + + // not dynamic or factory--does it exist? + return newType.equals(existingType); + } + + private static void restoreReference(DataType newType, ReferenceManager refMgr, + Reference ref) { + + if (ref == null) { + return; + } + + if (!(newType instanceof Pointer)) { + return; + } + + // if this was a pointer and had an external reference, put it back! + ExternalLocation extLoc = ((ExternalReference) ref).getExternalLocation(); + Address fromAddress = ref.getFromAddress(); + SourceType source = ref.getSource(); + RefType type = ref.getReferenceType(); + try { + refMgr.addExternalReference(fromAddress, 0, extLoc, source, type); + } + catch (InvalidInputException e) { + throw new AssertException(e); + } + } + + private static Reference getExternalPointerReference(Address addr, DataType newType, + boolean stackPointers, + ReferenceManager refMgr, DataType existingType) { + Reference extRef = null; + if ((stackPointers || newType instanceof Pointer) && + existingType instanceof Pointer) { + Reference[] refs = refMgr.getReferencesFrom(addr); + for (Reference ref : refs) { + if (ref.getOperandIndex() == 0 && ref.isExternalReference()) { + extRef = ref; + break; + } + } + } + return extRef; + } + + private static void validateCanCreateData(Address addr, ClearDataMode clearMode, + Listing listing, Data data) throws CodeUnitInsertionException { + + if (data != null) { + return; // existing data; it us possible to create data + } + + if (clearMode == ClearDataMode.CLEAR_ALL_CONFLICT_DATA || + clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA) { + + // allow offcut addr if CLEAR_ALL_CONFLICT_DATA + data = listing.getDataContaining(addr); + if (data != null && clearMode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && + !Undefined.isUndefined(data.getDataType())) { + data = null; // force error + } + } + + // null data implies that we cannot create data at this address + if (data == null) { + throw new CodeUnitInsertionException("Could not create Data at address " + addr); + } } private static void checkEnoughSpace(Program program, Address addr, int existingDataLen, DataTypeInstance dti, ClearDataMode mode) throws CodeUnitInsertionException { // NOTE: method not invoked when clearMode == ClearDataMode.CLEAR_SINGLE_DATA Listing listing = program.getListing(); + Address end = null; + Address newEnd = null; try { - Address end = addr.addNoWrap(existingDataLen - 1); - Address newEnd = addr.addNoWrap(dti.getLength() - 1); - Instruction instr = listing.getInstructionAfter(end); - if (instr != null && instr.getMinAddress().compareTo(newEnd) <= 0) { - throw new CodeUnitInsertionException( - "Not enough space to create DataType " + dti.getDataType().getDisplayName()); - } - Data definedData = listing.getDefinedDataAfter(end); - if (definedData != null && definedData.getMinAddress().compareTo(newEnd) <= 0) { - if (mode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && - Undefined.isUndefined(definedData.getDataType())) { - // ignore all defined data which is considered Undefined and may be cleared - end = definedData.getMaxAddress(); - while (end.compareTo(newEnd) <= 0) { - definedData = listing.getDefinedDataAfter(end); - if (definedData == null || - definedData.getMinAddress().compareTo(newEnd) > 0) { - break; - } - if (!Undefined.isUndefined(definedData.getDataType())) { - throw new CodeUnitInsertionException( - "Not enough space to create DataType " + - dti.getDataType().getDisplayName()); - } - end = definedData.getMaxAddress(); - } - } - else if (mode != ClearDataMode.CLEAR_ALL_CONFLICT_DATA) { - throw new CodeUnitInsertionException("Not enough space to create DataType " + - dti.getDataType().getDisplayName()); - } - listing.clearCodeUnits(addr, newEnd, false); - } - else { - listing.clearCodeUnits(addr, addr, false); - } + end = addr.addNoWrap(existingDataLen - 1); + newEnd = addr.addNoWrap(dti.getLength() - 1); } catch (AddressOverflowException e) { throw new CodeUnitInsertionException( "Not enough space to create DataType " + dti.getDataType().getDisplayName()); } + + Instruction instr = listing.getInstructionAfter(end); + if (instr != null && instr.getMinAddress().compareTo(newEnd) <= 0) { + throw new CodeUnitInsertionException( + "Not enough space to create DataType " + dti.getDataType().getDisplayName()); + } + + Data definedData = listing.getDefinedDataAfter(end); + if (definedData == null || definedData.getMinAddress().compareTo(newEnd) > 0) { + listing.clearCodeUnits(addr, addr, false); + return; + } + + if (mode == ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA && + Undefined.isUndefined(definedData.getDataType())) { + checkForDefinedData(dti, listing, newEnd, definedData.getMaxAddress()); + } + else if (mode != ClearDataMode.CLEAR_ALL_CONFLICT_DATA) { + throw new CodeUnitInsertionException("Not enough space to create DataType " + + dti.getDataType().getDisplayName()); + } + listing.clearCodeUnits(addr, newEnd, false); + } + + private static void checkForDefinedData(DataTypeInstance dti, Listing listing, Address address, + Address end) throws CodeUnitInsertionException { + + // ignore all defined data which is considered Undefined and may be cleared + while (end.compareTo(address) <= 0) { + Data definedData = listing.getDefinedDataAfter(end); + if (definedData == null || + definedData.getMinAddress().compareTo(address) > 0) { + return; + } + + if (!Undefined.isUndefined(definedData.getDataType())) { + throw new CodeUnitInsertionException("Not enough space to create DataType " + + dti.getDataType().getDisplayName()); + } + end = definedData.getMaxAddress(); + } } private static DataType stackPointers(Pointer pointer, DataType dataType) { @@ -355,7 +446,10 @@ public final class DataUtilities { * Get the data for the given address. *

* This will return a Data if and only if there is data that starts at the given address. - * @return the Data that starts at the given address or null if the address is code or offcut. + * + * @param program the program + * @param address the data address + * @return the Data that starts at the given address or null if the address is code or offcut */ public static Data getDataAtAddress(Program program, Address address) { if (address == null) { @@ -417,9 +511,9 @@ public final class DataUtilities { * Determine if the specified addr corresponds to an undefined data location * where both undefined code units and defined data which has an Undefined * data type is considered to be undefined. - * @param program - * @param addr - * @return + * @param program the program + * @param addr the data address + * @return true if the data is undefined */ public static boolean isUndefinedData(Program program, Address addr) { Data data = program.getListing().getDataAt(addr);