diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java index c42d876112..3862a774f5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java @@ -31,30 +31,28 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.util.ProgramSelection; import ghidra.util.HelpLocation; +import ghidra.util.Msg; import ghidra.util.layout.PairLayout; /** - * Class to set up dialog box that will enable the user - * to set the available options for block selection + * Dialog for making program selections */ class SelectBlockDialog extends ReusableDialogComponentProvider { - private static final String OVERFLOW_SELECTION_WARNING = - "Selection is larger than available " + "bytes, using the end of the address space"; + + private PluginTool tool; + private Navigatable navigatable; private JTextField toAddressField; - private IntegerTextField numberInputField; // AddressInput allows decimal and hex input + private IntegerTextField lengthField; private JRadioButton forwardButton; private JRadioButton backwardButton; private JRadioButton allButton; private JRadioButton toButton; - private Navigatable navigatable; - private PluginTool tool; SelectBlockDialog(PluginTool tool, Navigatable navigatable) { super("Select Bytes", false, true, true, false); this.tool = tool; this.navigatable = navigatable; -// navigatable.addNavigatableListener(this); addWorkPanel(buildPanel()); addOKButton(); @@ -62,7 +60,6 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { addDismissButton(); setHelpLocation(new HelpLocation("SelectBlockPlugin", "Select_Block_Help")); - // make sure the state of the widgets is correct setItemsEnabled(false); forwardButton.doClick(); } @@ -94,11 +91,10 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { main.add(toAddressField); main.add(new GLabel("Length: ")); - numberInputField = new IntegerTextField(10); - numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input"); - numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE)); - numberInputField.setMinValue(BigInteger.ZERO); - main.add(numberInputField.getComponent()); + lengthField = new IntegerTextField(10); + lengthField.getComponent().getAccessibleContext().setAccessibleName("Number Input"); + lengthField.setMinValue(BigInteger.ZERO); + main.add(lengthField.getComponent()); main.getAccessibleContext().setAccessibleName("Block"); return main; } @@ -169,6 +165,11 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { navigatable = null; } + void setNavigatable(Navigatable navigatable) { + this.navigatable = navigatable; + setOkEnabled(navigatable != null); + } + void show(ComponentProvider provider) { tool.showDialog(this, provider); repack(); @@ -187,188 +188,147 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { private void setLengthInputEnabled(boolean enabled) { if (!enabled) { - numberInputField.setValue(null); + lengthField.setValue(null); } - numberInputField.setEnabled(enabled); + lengthField.setEnabled(enabled); } @Override protected void okCallback() { if (toButton.isSelected()) { - handleToAddressSelection(); + selectToAddress(); } else if (allButton.isSelected()) { - handleAllSelection(); + selectAll(); } else if (forwardButton.isSelected()) { - handleForwardSelection(); + createSelection(true); } else if (backwardButton.isSelected()) { - handleBackwardSelection(); + createSelection(false); } else { setStatusText("You must choose the type of selection to make"); } } - private void handleAllSelection() { + private void selectAll() { AddressSetView addressSet = navigatable.getProgram().getMemory(); ProgramSelection selection = new ProgramSelection(addressSet); NavigationUtils.setSelection(tool, navigatable, selection); clearStatusText(); } - private void handleToAddressSelection() { - Address toAddress = null; + private void selectToAddress() { + String addressValue = toAddressField.getText(); clearStatusText(); // make sure the order of the addresses is correct Address currentAddress = navigatable.getLocation().getAddress(); + Address to = null; try { - toAddress = currentAddress.getAddress(addressValue); + to = currentAddress.getAddress(addressValue); } catch (AddressFormatException e) { // use the fact that toAddress remains null } - if (toAddress == null) { + + if (to == null) { setStatusText("Invalid address value, enter another address"); return; } - if (toAddress.compareTo(currentAddress) < 0) { - Address tmp = toAddress; - toAddress = currentAddress; + + if (to.compareTo(currentAddress) < 0) { + Address tmp = to; + to = currentAddress; currentAddress = tmp; } - AddressSet addressSet = new AddressSet(currentAddress, toAddress); + AddressSet addressSet = new AddressSet(currentAddress, to); ProgramSelection selection = new ProgramSelection(addressSet); NavigationUtils.setSelection(tool, navigatable, selection); } - private void handleForwardSelection() { - // value is a length - int length = numberInputField.getIntValue(); // throws NFE - if (length == 0) { + private void createSelection(boolean forward) { + BigInteger length = lengthField.getValue(); + if (length == null || length == BigInteger.ZERO) { setStatusText("length must be > 0"); return; } clearStatusText(); - Address currentAddress = navigatable.getLocation().getAddress(); - - AddressSet addressSet = new AddressSet(navigatable.getSelection()); - - if (addressSet.isEmpty()) { - addressSet.addRange(currentAddress, currentAddress); + AddressSet startSet; + ProgramSelection currentSelection = navigatable.getSelection(); + if (!currentSelection.isEmpty()) { + startSet = new AddressSet(currentSelection); + } + else { + Address currentAddress = navigatable.getLocation().getAddress(); + startSet = new AddressSet(currentAddress); } - AddressRangeIterator aiter = addressSet.getAddressRanges(); + AddressRangeIterator it = startSet.getAddressRanges(); AddressSet newSet = new AddressSet(); - while (aiter.hasNext()) { - AddressRange range = aiter.next(); - Address toAddress = createForwardToAddress(range.getMinAddress(), length - 1); - if (toAddress != null) { - newSet.addRange(range.getMinAddress(), toAddress); + while (it.hasNext()) { + AddressRange range = it.next(); + + if (forward) { + Address from = range.getMinAddress(); + createForwardRange(newSet, from, length); + } + else { + Address to = range.getMaxAddress(); + createBackwardRange(newSet, to, length); } } - ProgramSelection selection = new ProgramSelection(newSet); - NavigationUtils.setSelection(tool, navigatable, selection); + + ProgramSelection newSelection = new ProgramSelection(newSet); + NavigationUtils.setSelection(tool, navigatable, newSelection); } - private void handleBackwardSelection() { - // value is a length - int length = numberInputField.getIntValue(); - if (length == 0) { - setStatusText("length must be > 0"); - return; - } - clearStatusText(); - - Address currentAddress = navigatable.getLocation().getAddress(); - AddressSet addressSet = new AddressSet(navigatable.getSelection()); - if (addressSet.isEmpty()) { - addressSet.addRange(currentAddress, currentAddress); - } - - AddressRangeIterator aiter = addressSet.getAddressRanges(); - AddressSet newSet = new AddressSet(); - while (aiter.hasNext()) { - AddressRange range = aiter.next(); - - Address fromAddress = createBackwardToAddress(range.getMaxAddress(), length - 1); - if (fromAddress != null) { - newSet.addRange(fromAddress, range.getMaxAddress()); - } - } - ProgramSelection selection = new ProgramSelection(newSet); - NavigationUtils.setSelection(tool, navigatable, selection); + private void createForwardRange(AddressSet set, Address from, BigInteger length) { + Address to = getToAddress(from, length); + set.addRange(from, to); } - private Address createBackwardToAddress(Address toAddress, long length) { - AddressSpace addressSpace = toAddress.getAddressSpace(); - if (addressSpace.isOverlaySpace()) { - OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - AddressRange range = oas.getOverlayAddressSet().getRangeContaining(toAddress); - if (range == null) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return toAddress; - } - long avail = toAddress.subtract(range.getMinAddress()); - if (avail < (length - 1)) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return range.getMinAddress(); - } - } + private void createBackwardRange(AddressSet set, Address to, BigInteger length) { + Address from = getFromAddress(to, length); + set.addRange(from, to); + } - Address addr = null; + private Address getFromAddress(Address to, BigInteger length) { + + // subtract one to be inclusive; address ranges are inclusive + BigInteger inclusiveLength = length.subtract(BigInteger.ONE); try { - addr = toAddress.subtractNoWrap(length); + return to.subtractNoWrap(inclusiveLength); } - catch (AddressOverflowException aoobe) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - addr = addressSpace.getMinAddress(); + catch (AddressOverflowException e) { + showWarningDialog(); + AddressSpace space = to.getAddressSpace(); + return space.getMinAddress(); } - - return addr; } - private Address createForwardToAddress(Address fromAddress, long length) { + private Address getToAddress(Address from, BigInteger length) { - AddressSpace addressSpace = fromAddress.getAddressSpace(); - if (addressSpace.isOverlaySpace()) { - OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - AddressRange range = oas.getOverlayAddressSet().getRangeContaining(fromAddress); - if (range == null) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return fromAddress; - } - long avail = range.getMaxAddress().subtract(fromAddress); - if (avail < (length - 1)) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return range.getMaxAddress(); - } - } - - Address addr = null; + // subtract one to be inclusive; address ranges are inclusive + BigInteger inclusiveLength = length.subtract(BigInteger.ONE); try { - addr = fromAddress.addNoWrap(length); + return from.addNoWrap(inclusiveLength); } - catch (AddressOverflowException aoobe) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - addr = addressSpace.getMaxAddress(); + catch (AddressOverflowException e) { + showWarningDialog(); + AddressSpace space = from.getAddressSpace(); + return space.getMaxAddress(); } - - return addr; } - private void showWarningDialog(final String text) { - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(getComponent(), text)); + private void showWarningDialog() { + Msg.showWarn(this, getComponent(), "Selection Overflow", + "Selection is larger than available bytes. Using the boundary of the address space."); } - public void setNavigatable(Navigatable navigatable) { - this.navigatable = navigatable; - setOkEnabled(navigatable != null); - } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java index 42c940d0ea..662d075616 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -35,23 +35,24 @@ import ghidra.app.plugin.core.navigation.NextPrevAddressPlugin; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; +import ghidra.program.database.mem.MemoryMapDB; import ghidra.program.model.address.*; -import ghidra.program.util.ProgramLocation; +import ghidra.program.model.mem.MemoryBlock; import ghidra.program.util.ProgramSelection; import ghidra.test.*; +import ghidra.util.task.TaskMonitor; /** - * Test class to test SelectBlock + * Test class to test the Select Bytes dialog. */ - public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { + private static final String SELECT_BYTES_BUTTON_NAME = "Select Bytes"; private TestEnv env; private PluginTool tool; private CodeBrowserPlugin browser; - private SelectBlockPlugin plugin; - private DockingActionIf action; + private DockingActionIf showDialogAction; private ProgramDB program; @Before @@ -63,9 +64,9 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { configureTool(tool); browser = env.getPlugin(CodeBrowserPlugin.class); - plugin = env.getPlugin(SelectBlockPlugin.class); + SelectBlockPlugin plugin = env.getPlugin(SelectBlockPlugin.class); - action = (DockingActionIf) getInstanceField("toolBarAction", plugin); + showDialogAction = (DockingActionIf) getInstanceField("toolBarAction", plugin); } @After @@ -82,7 +83,7 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - public void open8051Program() throws Exception { + private void open8051Program() throws Exception { ProgramBuilder builder = new ProgramBuilder("program2", ProgramBuilder._8051); builder.createMemory("CODE", "CODE:0000", 0x6fff); program = builder.getProgram(); @@ -91,18 +92,6 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - private void closeProgram() throws Exception { - if (program != null) { - env.close(program); - program = null; - } - } - - private Address addr(long addr) { - AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); - return space.getAddress(addr); - } - private void configureTool(PluginTool toolToConfigure) throws Exception { toolToConfigure.addPlugin(BlockModelServicePlugin.class.getName()); toolToConfigure.addPlugin(NextPrevAddressPlugin.class.getName()); @@ -111,6 +100,311 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { toolToConfigure.addPlugin(SelectBlockPlugin.class.getName()); } + @Test + public void testActionEnablement() throws Exception { + + assertFalse(showDialogAction.isEnabledForContext(getContext())); + openProgram(); + assertTrue(showDialogAction.isEnabledForContext(getContext())); + + SelectBlockDialog dialog = showDialog(); + + closeProgram(); + assertTrue(!showDialogAction.isEnabledForContext(getContext())); + close(dialog); + } + + @Test + public void testSelectAll() throws Exception { + openProgram(); + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(program.getMemory()), new AddressSet(selection)); + close(dialog); + } + + @Test + public void testSelectForward() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + assertAddressFieldDisabled(dialog); + assertLengthFieldDisabled(dialog); + + pressSelectForward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006420), addr(0x100651f)), selection); + close(dialog); + } + + @Test + public void testBadInput() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setAddress(dialog, "foo"); + + pressSelectBytes(dialog); + assertEquals("length must be > 0", dialog.getStatusText()); + close(dialog); + } + + @Test + public void testSelectBackward() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006321), addr(0x1006420)), selection); + close(dialog); + + } + + @Test + public void testSelectToAddr() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectToAddress(dialog); + + setAddress(dialog, "0x1007000"); + + pressSelectBytes(dialog); + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006420), addr(0x1006fff)), selection); + close(dialog); + } + + @Test + public void testSelectForward_OverlayBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayStart); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectForward_OverlayBlock_LengthLargerThanBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayStart); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setLength(dialog, 0x101); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectBackward_OverlayBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayEnd); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectBackward_OverlayBlock_LengthLargerThanBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayEnd); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x101); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSegmentedProgram() throws Exception { + + open8051Program(); + Address startAddress = addr(0x6420); + goTo(startAddress); + + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldDisabled(dialog); + + pressSelectBytes(dialog); + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(program.getMemory()), new AddressSet(selection)); + + //--------------------------- + + clearSelection(); + + pressSelectToAddress(dialog); + + assertAddressFieldEnabled(dialog); + assertLengthFieldDisabled(dialog); + + setAddress(dialog, "0x6520"); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(startAddress, addr(0x6520)), selection); + + //--------------------------- + + clearSelection(); + + pressSelectForward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 100); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(startAddress, addr(0x6483)), selection); + + //--------------------------- + + clearSelection(); + + pressSelectBackward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 100); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x63bd), startAddress), selection); + close(dialog); + } + + @Test + public void testCloseProgram() throws Exception { + openProgram(); + closeProgram(); + + assertFalse(showDialogAction.isEnabledForContext(getContext())); + } + +//================================================================================================= +// Private Methods +//================================================================================================= + + private void pressSelectToAddress(SelectBlockDialog dialog) { + JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); + pressButton(toButton, true); + } + + private SelectBlockDialog showDialog() { + performAction(showDialogAction, getContext(), true); + SelectBlockDialog dialog = waitForDialogComponent(SelectBlockDialog.class); + return dialog; + } + + private MemoryBlock createOverlayBlock(Address start, int length) { + return tx(program, () -> { + MemoryMapDB memory = program.getMemory(); + return memory.createInitializedBlock("Name", start, length, (byte) 0, TaskMonitor.DUMMY, + true); + }); + } + + private Address addr(long addr) { + AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); + return space.getAddress(addr); + } + private ActionContext getContext() { ActionContext context = browser.getProvider().getActionContext(null); if (context == null) { @@ -119,205 +413,64 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { return context; } - @Test - public void testActionEnablement() throws Exception { - assertTrue(!action.isEnabledForContext(getContext())); - openProgram(); - assertTrue(action.isEnabledForContext(getContext())); - performAction(action, getContext(), true); - assertTrue(action.isEnabledForContext(getContext())); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - assertNotNull(dialog); - assertTrue(dialog.isVisible()); - closeProgram(); - assertTrue(!action.isEnabledForContext(getContext())); - runSwing(() -> dialog.close()); + private void closeProgram() throws Exception { + if (program != null) { + env.close(program); + program = null; + } } - @Test - public void testSelectAll() throws Exception { - openProgram(); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(program.getMemory()), new AddressSet(currSelection)); - runSwing(() -> dialog.close()); - } - - @Test - public void testSelectForward() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - - JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); - assertTrue(!addressInputField.isEnabled()); - - final IntegerTextField inputField = - (IntegerTextField) getInstanceField("numberInputField", dialog); - assertTrue(!inputField.getComponent().isEnabled()); - - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - runSwing(() -> inputField.setValue(0x100)); - - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006420), addr(0x100651f)), currSelection); - runSwing(() -> dialog.close()); - } - - @Test - public void testBadInput() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - final JTextField addressInputField = - (JTextField) getInstanceField("toAddressField", dialog); - - runSwing(() -> addressInputField.setText("foo")); - pressSelectBytes(dialog); - assertEquals("length must be > 0", dialog.getStatusText()); - runSwing(() -> dialog.close()); - } - - @Test - public void testSelectBackward() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); + private void pressSelectBackward(SelectBlockDialog dialog) { JRadioButton backwardButton = (JRadioButton) findComponentByName(dialog, "backwardButton"); pressButton(backwardButton, true); - - final IntegerTextField inputField = - (IntegerTextField) getInstanceField("numberInputField", dialog); - runSwing(() -> inputField.setValue(256)); - - pressButtonByText(dialog, SELECT_BYTES_BUTTON_NAME, true); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006321), addr(0x1006420)), currSelection); - runSwing(() -> dialog.close()); - } - @Test - public void testSelectToAddr() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); - pressButton(toButton, true); - + private void setAddress(SelectBlockDialog dialog, String text) { final JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); - runSwing(() -> addressInputField.setText("0x1007000")); - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006420), addr(0x1006fff)), currSelection); - runSwing(() -> dialog.close()); - + runSwing(() -> addressInputField.setText(text)); } - @Test - public void testSegmentedProgram() throws Exception { - // select all - open8051Program(); - Address startAddress = addr(0x6420); - browser.goTo(new ProgramLocation(program, startAddress)); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - - // all input fields disabled - final JTextField addressInputField = - (JTextField) getInstanceField("toAddressField", dialog); - assertTrue(!addressInputField.isEnabled()); - + private void setLength(SelectBlockDialog dialog, int length) { IntegerTextField inputField = (IntegerTextField) getInstanceField("numberInputField", dialog); - assertTrue(!inputField.getComponent().isEnabled()); - - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(program.getMemory()), new AddressSet(currSelection)); - - clearSelection(); - - // to address - JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); - pressButton(toButton, true); - - // only address input field is enabled - assertTrue(addressInputField.isEnabled()); - assertTrue(!inputField.getComponent().isEnabled()); - - runSwing(() -> addressInputField.setText("0x6520")); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(startAddress, addr(0x6520)), currSelection); - - clearSelection(); - - // select forward - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - // only address input field is enabled - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - inputField.setValue(100); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(startAddress, addr(0x6483)), currSelection); - - clearSelection(); - - // select backward - JRadioButton backwardButton = (JRadioButton) findComponentByName(dialog, "backwardButton"); - pressButton(backwardButton, true); - - // only address input field is enabled - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - inputField.setValue(100); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x63bd), startAddress), currSelection); - runSwing(() -> dialog.close()); + runSwing(() -> inputField.setValue(length)); } - @Test - public void testCloseProgram() throws Exception { - openProgram(); - closeProgram(); + private void pressSelectForward(SelectBlockDialog dialog) { + JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); + pressButton(forwardButton, true); + } - assertTrue(!action.isEnabledForContext(getContext())); + private void assertLengthFieldDisabled(SelectBlockDialog dialog) { + IntegerTextField inputField = + (IntegerTextField) getInstanceField("numberInputField", dialog); + assertFalse(inputField.getComponent().isEnabled()); + } + + private void assertLengthFieldEnabled(SelectBlockDialog dialog) { + IntegerTextField inputField = + (IntegerTextField) getInstanceField("numberInputField", dialog); + assertTrue(inputField.getComponent().isEnabled()); + } + + private void assertAddressFieldDisabled(SelectBlockDialog dialog) { + JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); + assertFalse(addressInputField.isEnabled()); + } + + private void assertAddressFieldEnabled(SelectBlockDialog dialog) { + JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); + assertTrue(addressInputField.isEnabled()); + } + + private void pressSelectAll(SelectBlockDialog dialog) { + JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); + pressButton(allButton, true); + } + + private void goTo(Address a) { + goTo(tool, program, a); } private void clearSelection() { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java index 5fe08eca94..ae1188b31f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java @@ -68,6 +68,7 @@ public interface Address extends Comparable
{ /** * Returns a new address in this address's space with the given offset. + * *NOTE: for those spaces with an addressable unit size other than 1, the address returned * may not correspond to an addressable unit/word boundary if a byte-offset is specified. * @@ -81,14 +82,16 @@ public interface Address extends Comparable
{ * @throws AddressOutOfBoundsException if the offset is less than 0 or greater than the max * offset allowed for this space. */ - Address getNewAddress(long offset, boolean isAddressableWordOffset) + public Address getNewAddress(long offset, boolean isAddressableWordOffset) throws AddressOutOfBoundsException; /** * Returns a new address in this address's space with the given offset. The specified offset * will be truncated within the space and will not throw an exception. + * *NOTE: for those spaces with an addressable unit size other than 1, the address returned * may not correspond to a word boundary (addressable unit) if a byte-offset is specified. + * * @param offset the offset for the new address. * @param isAddressableWordOffset if true the specified offset is an addressable unit/word * offset, otherwise offset is a byte offset. See @@ -97,7 +100,7 @@ public interface Address extends Comparable
{ * (i.e., wordOffset = byteOffset * addressableUnitSize). * @return address with given byte offset truncated to the physical space size */ - Address getNewTruncatedAddress(long offset, boolean isAddressableWordOffset); + public Address getNewTruncatedAddress(long offset, boolean isAddressableWordOffset); /** * Returns the number of bytes needed to form a pointer to this address. The result will be @@ -193,7 +196,7 @@ public interface Address extends Comparable { * Creates a new address by subtracting the displacement from the current address. If the * offset is greater than the max offset of the address space, the high order bits are masked * off, making the address wrap. For non-segmented addresses this will be the same as - * subtractWrap(). For segmented addresses, the address will wrap when the 20 bit (oxfffff) + * subtractWrap(). For segmented addresses, the address will wrap when the 20 bit (0xfffff) * offset is exceeded, as opposed to when the segment offset is exceeded. * @param displacement the displacement to add. * @return The new Address formed by subtracting the displacement from this address's offset. @@ -212,6 +215,18 @@ public interface Address extends Comparable { */ public Address subtractNoWrap(long displacement) throws AddressOverflowException; + /** + * Creates a new Address by subtracting displacement from the Address. The Address will not + * wrap within the space and in fact will throw an exception if the result is less than the min + * address in this space or greater than the max address in this space. + * + * @param displacement the displacement to subtract. + * @return The new Address + * @throws AddressOverflowException if the offset in this Address would overflow due to this + * operation. + */ + public Address subtractNoWrap(BigInteger displacement) throws AddressOverflowException; + /** * Creates a new address (possibly in a new space) by subtracting the displacement to this * address. @@ -235,7 +250,7 @@ public interface Address extends Comparable { * Creates a new address by adding the displacement to the current address. If the offset is * greater than the max offset of the address space, the high order bits are masked off, making * the address wrap. For non-segmented addresses this will be the same as addWrap(). For - * segmented addresses, the address will wrap when the 20 bit (oxfffff) offset is exceeded, as + * segmented addresses, the address will wrap when the 20 bit (0xfffff) offset is exceeded, as * opposed to when the segment offset is exceeded. * @param displacement the displacement to add. * @return The new Address formed by adding the displacement to this address's offset. @@ -253,8 +268,16 @@ public interface Address extends Comparable { */ public Address addNoWrap(long displacement) throws AddressOverflowException; + /** + * Creates a new Address with a displacement relative to this Address. The Address will not + * wrap around! An exception will be throw if the result is not within this address space. + * + * @param displacement the displacement to add. + * @return the new address. + * @throws AddressOverflowException if the offset in this Address would overflow (wrap around) + * due to this operation. + */ public Address addNoWrap(BigInteger displacement) throws AddressOverflowException; - public Address subtractNoWrap(BigInteger displacement) throws AddressOverflowException; /** * Creates a new address (possibly in a new space) by adding the displacement to this address. @@ -394,6 +417,7 @@ public interface Address extends Comparable { /** * Returns true if this address represents a location in the register space. + * *NOTE: It is important to note that a {@link Register} could reside within a memory space
* and not the register space in which case this method would return false for its address.
* @return true if a register address
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java
index bef7522f99..d34aef083b 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java
@@ -105,20 +105,20 @@ public interface AddressSpace extends Comparable
* NOTE: This method is the same as invoking getAddress(long byteOffset, false).
+ *
* @param byteOffset the byte offset for the new address.
* @return address with given byte offset
* @throws AddressOutOfBoundsException if the offset is less than 0 or greater
* than the max offset allowed for this space.
*/
- Address getAddress(long byteOffset) throws AddressOutOfBoundsException;
+ public Address getAddress(long byteOffset) throws AddressOutOfBoundsException;
/**
- * Returns a new address in this space with the given offset.
+ * Returns a new address in this space with the given offset.
+ *
* NOTE: for those spaces with an addressable unit size other than 1, the address
* returned may not correspond to an addressable unit/word boundary if a byte-offset
* is specified.
+ *
* @param offset the offset for the new address.
- * @param isAddressableWordOffset if true the specified offset is an addressable unit/word offset,
- * otherwise offset is a byte offset. See {@link #getAddressableUnitSize()}
+ * @param isAddressableWordOffset if true the specified offset is an addressable unit/word
+ * offset, otherwise offset is a byte offset. See {@link #getAddressableUnitSize()}
* to understand the distinction (i.e., wordOffset = byteOffset * addressableUnitSize).
* @return address with given offset
* @throws AddressOutOfBoundsException if the offset is less than 0 or greater
* than the max offset allowed for this space.
*/
- Address getAddress(long offset, boolean isAddressableWordOffset)
+ public Address getAddress(long offset, boolean isAddressableWordOffset)
throws AddressOutOfBoundsException;
/**
* Returns a new address in this space with the given offset. The specified
* offset will be truncated within the space and will not throw an exception.
+ *
* NOTE: for those spaces with an addressable unit size other than 1, the address
* returned may not correspond to a word boundary (addressable unit) if a byte-offset
* is specified.
+ *
* @param offset the offset for the new address.
* @param isAddressableWordOffset if true the specified offset is an addressable unit/word offset,
* otherwise offset is a byte offset. See {@link #getAddressableUnitSize()}
* to understand the distinction (i.e., wordOffset = byteOffset * addressableUnitSize).
* @return address with given byte offset truncated to the physical space size
*/
- Address getTruncatedAddress(long offset, boolean isAddressableWordOffset);
+ public Address getTruncatedAddress(long offset, boolean isAddressableWordOffset);
/**
* Get a byte address from this address space. Don't allow overlay spaces
@@ -231,18 +238,17 @@ public interface AddressSpace extends Comparable
* NOTE: Use of this method to identify the region associated with an overlay memory block
* within its overlay address space is no longer supported. Defined regions of an overlay space
* may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}.
@@ -414,7 +420,7 @@ public interface AddressSpace extends Comparable
* NOTE: Use of this method to identify the region associated with an overlay memory block
* within its overlay address space is no longer supported. Defined regions of an overlay space
* may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}.
@@ -506,22 +512,22 @@ public interface AddressSpace extends Comparable