diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java index 66417daea8..2a04eb4260 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java @@ -15,7 +15,7 @@ */ package ghidra.app.plugin.core.debug.gui.listing; -import static ghidra.app.plugin.core.debug.gui.DebuggerResources.GROUP_TRANSIENT_VIEWS; +import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*; import java.util.List; import java.util.function.Consumer; @@ -81,7 +81,8 @@ import ghidra.trace.model.program.TraceProgramView; }, servicesProvided = { DebuggerListingService.class, - }) + } +) public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin implements DebuggerListingService { private static final String KEY_CONNECTED_PROVIDER = "connectedProvider"; @@ -180,13 +181,13 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin ex protected FormatManager formatMgr; protected ViewManagerService viewManager; private MarkerService markerService; - protected AddressSetView currentView; + protected AddressSetView currentView = ImmutableAddressSet.EMPTY_SET; protected Program currentProgram; private boolean selectionChanging; private MarkerSet currentSelectionMarkers; @@ -118,20 +118,26 @@ public abstract class AbstractCodeBrowserPlugin

ex protected abstract P createProvider(FormatManager formatManager, boolean isConnected); - protected void viewChanged(AddressSetView addrSet) { - ProgramLocation currLoc = getCurrentLocation(); - currentView = addrSet; - if (addrSet != null && !addrSet.isEmpty()) { - connectedProvider.setView(addrSet); - if (currLoc != null && addrSet.contains(currLoc.getAddress())) { - goTo(currLoc, true); - } - } - else { - connectedProvider.setView(new AddressSet()); - } - updateBackgroundColorModel(); + protected void setView(AddressSetView newView) { + if (currentView.hasSameAddresses(newView)) { + return; + } + + ProgramLocation location = getCurrentLocation(); + currentView = ImmutableAddressSet.asImmutable(newView); + + connectedProvider.setView(currentView); + + if (location != null && currentView.contains(location.getAddress())) { + goTo(location, true); + } + + viewUpdated(); + } + + private void viewUpdated() { + updateBackgroundColorModel(); setHighlight(connectedProvider.getHighlight()); setSelection(connectedProvider.getSelection()); } @@ -168,7 +174,7 @@ public abstract class AbstractCodeBrowserPlugin

ex listingPanel.setBackgroundColorModel(null); } - // TODO: update all providers, not just the connected provider + // Note: this should update all providers, not just the connected provider } @Override @@ -220,14 +226,14 @@ public abstract class AbstractCodeBrowserPlugin

ex public void serviceAdded(Class interfaceClass, Object service) { if (interfaceClass == ViewManagerService.class && viewManager == null) { viewManager = (ViewManagerService) service; - viewChanged(viewManager.getCurrentView()); + setView(viewManager.getCurrentView()); } if (interfaceClass == MarkerService.class && markerService == null) { markerService = tool.getService(MarkerService.class); markerService.addChangeListener(markerChangeListener); updateBackgroundColorModel(); if (viewManager != null) { - viewChanged(viewManager.getCurrentView()); + viewUpdated(); } } if (interfaceClass == ListingHoverService.class) { @@ -247,7 +253,7 @@ public abstract class AbstractCodeBrowserPlugin

ex public void serviceRemoved(Class interfaceClass, Object service) { if ((service == viewManager) && (currentProgram != null)) { viewManager = null; - viewChanged(currentProgram.getMemory()); + setView(currentProgram.getMemory()); } if (service == markerService) { markerService.removeChangeListener(markerChangeListener); @@ -338,7 +344,7 @@ public abstract class AbstractCodeBrowserPlugin

ex @Override public void setListingPanel(ListingPanel lp) { connectedProvider.setOtherPanel(lp); - viewChanged(currentView); + viewUpdated(); } @Override @@ -363,7 +369,7 @@ public abstract class AbstractCodeBrowserPlugin

ex } if (connectedProvider.getOtherPanel() == lp) { connectedProvider.clearPanel(); - viewChanged(currentView); + viewUpdated(); } } @@ -857,11 +863,11 @@ public abstract class AbstractCodeBrowserPlugin

ex connectedProvider.updateTitle(); } - if (viewManager != null) { - return; - } if (ev.contains(DomainObjectEvent.RESTORED)) { - viewChanged(currentProgram.getMemory()); + if (viewManager == null) { + setView(currentProgram.getMemory()); + viewUpdated(); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java index 6ba71da28e..542e5ae16d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java @@ -113,9 +113,6 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin codeBrowser.viewChanged(addrSet), true); - + runSwing(() -> codeBrowser.setView(addrSet), true); } private void adjustFieldPanelSize(int numRows) { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserTest.java index b26e73de00..fee6af539b 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/codebrowser/CodeBrowserTest.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. @@ -664,7 +664,7 @@ public class CodeBrowserTest extends AbstractGhidraHeadedIntegrationTest { } private void setView(AddressSet addrSet) { - runSwing(() -> cb.viewChanged(addrSet), true); + runSwing(() -> cb.setView(addrSet), true); } private void setSelection(final AddressSet addrSet) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ImmutableAddressSet.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ImmutableAddressSet.java new file mode 100644 index 0000000000..821e6b69a3 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/ImmutableAddressSet.java @@ -0,0 +1,226 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.program.model.address; + +import java.util.Iterator; + +/** + * Immutable implementation of the {@link AddressSetView} interface; + */ +public class ImmutableAddressSet implements AddressSetView { + + public static final ImmutableAddressSet EMPTY_SET = new ImmutableAddressSet(new AddressSet()); + + public static ImmutableAddressSet asImmutable(AddressSetView view) { + if (view == null) { + return EMPTY_SET; + } + if (view instanceof ImmutableAddressSet ias) { + return ias; + } + return new ImmutableAddressSet(view); + } + + private AddressSet set; + + public ImmutableAddressSet(AddressSetView addresses) { + set = new AddressSet(addresses); + } + + @Override + public boolean contains(Address addr) { + return set.contains(addr); + } + + @Override + public boolean contains(Address start, Address end) { + return set.contains(start, end); + } + + @Override + public boolean contains(AddressSetView rangeSet) { + return set.contains(rangeSet); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public Address getMinAddress() { + return set.getMinAddress(); + } + + @Override + public Address getMaxAddress() { + return set.getMaxAddress(); + } + + @Override + public int getNumAddressRanges() { + return set.getNumAddressRanges(); + } + + @Override + public AddressRangeIterator getAddressRanges() { + return set.getAddressRanges(); + } + + @Override + public AddressRangeIterator getAddressRanges(boolean forward) { + return set.getAddressRanges(forward); + } + + @Override + public AddressRangeIterator getAddressRanges(Address start, boolean forward) { + return set.getAddressRanges(start, forward); + } + + @Override + public Iterator iterator() { + return set.iterator(); + } + + @Override + public Iterator iterator(boolean forward) { + return set.iterator(forward); + } + + @Override + public Iterator iterator(Address start, boolean forward) { + return set.iterator(start, forward); + } + + @Override + public long getNumAddresses() { + return set.getNumAddresses(); + } + + @Override + public AddressIterator getAddresses(boolean forward) { + return set.getAddresses(forward); + } + + @Override + public AddressIterator getAddresses(Address start, boolean forward) { + return set.getAddresses(start, forward); + } + + @Override + public boolean intersects(AddressSetView addrSet) { + return set.intersects(addrSet); + } + + @Override + public boolean intersects(Address start, Address end) { + return set.intersects(start, end); + } + + @Override + public AddressSet intersect(AddressSetView view) { + return set.intersect(view); + } + + @Override + public AddressSet intersectRange(Address start, Address end) { + return set.intersectRange(start, end); + } + + @Override + public AddressSet union(AddressSetView addrSet) { + return set.union(addrSet); + } + + @Override + public AddressSet subtract(AddressSetView addrSet) { + return set.subtract(addrSet); + } + + @Override + public AddressSet xor(AddressSetView addrSet) { + return set.xor(addrSet); + } + + @Override + public boolean hasSameAddresses(AddressSetView view) { + return set.hasSameAddresses(view); + } + + @Override + public AddressRange getFirstRange() { + return set.getFirstRange(); + } + + @Override + public AddressRange getLastRange() { + return set.getLastRange(); + } + + @Override + public AddressRange getRangeContaining(Address address) { + return set.getRangeContaining(address); + } + + @Override + public Address findFirstAddressInCommon(AddressSetView addresses) { + return set.findFirstAddressInCommon(addresses); + } + + @Override + public final boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj == this) { + return true; + } + + if (!(obj instanceof ImmutableAddressSet)) { + return false; + } + + ImmutableAddressSet otherSet = (ImmutableAddressSet) obj; + if (this.getNumAddresses() != otherSet.getNumAddresses()) { + return false; + } + + // if don't have same number of ranges, not equal + if (this.getNumAddressRanges() != otherSet.getNumAddressRanges()) { + return false; + } + + AddressRangeIterator otherRanges = otherSet.getAddressRanges(); + AddressRangeIterator myRanges = getAddressRanges(); + while (myRanges.hasNext()) { + AddressRange myRange = myRanges.next(); + AddressRange otherRange = otherRanges.next(); + if (!myRange.equals(otherRange)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + if (isEmpty()) { + return 0; + } + return getMinAddress().hashCode() + getMaxAddress().hashCode(); + } +}