diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/events/CloseProgramPluginEvent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/events/CloseProgramPluginEvent.java index 36aff6b3b6..d039488aa6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/events/CloseProgramPluginEvent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/events/CloseProgramPluginEvent.java @@ -18,16 +18,11 @@ package ghidra.app.events; import java.lang.ref.WeakReference; import ghidra.framework.plugintool.PluginEvent; -import ghidra.framework.plugintool.ToolEventName; import ghidra.program.model.listing.Program; /** - * Event for telling a tool to open a program - *

- * This event shares a common tool-event name with the {@link OpenProgramPluginEvent} - * so that they have a single shared tool connection. + * Event for telling a tool to close a program */ -@ToolEventName(OpenProgramPluginEvent.TOOL_EVENT_NAME) // this allows the event to be considered for tool connection public class CloseProgramPluginEvent extends PluginEvent { static final String NAME = "Close Program"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/events/DualProgramLocationPluginEvent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/events/DualProgramLocationPluginEvent.java deleted file mode 100644 index 9b45a30013..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/events/DualProgramLocationPluginEvent.java +++ /dev/null @@ -1,76 +0,0 @@ -/* ### - * 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.app.events; - -import ghidra.framework.plugintool.PluginEvent; -import ghidra.framework.plugintool.ToolEventName; -import ghidra.program.model.listing.Program; -import ghidra.program.util.ProgramLocation; - -/** - * This plugin event class provides program location information for - * plugins that send information to two or more tools containing associated addresses. - */ -@ToolEventName(DualProgramLocationPluginEvent.NAME) // this allows the event to be considered for tool connection -public final class DualProgramLocationPluginEvent extends PluginEvent { - - /** - * Name of this plugin event. - */ - public static final String NAME = "DualProgramLocation"; - - private ProgramLocation loc; - private String programName; - - /** - * Construct a new DualProgramLocationPluginEvent. - * @param src the name of the plugin that generated this event. - * @param loc the ProgramLocation object that contains the new location. - * @param programName the name of the program for which the loc object refers. - */ - public DualProgramLocationPluginEvent(String src, ProgramLocation loc, String programName) { - super(src, NAME); - this.loc = loc; - this.programName = programName; - } - - /** - * Construct a new DualProgramLocationPluginEvent. - * @param src the name of the plugin that generated this event. - * @param loc the ProgramLocation object that contains the new location. - * @param program the program for which the loc object refers. - */ - public DualProgramLocationPluginEvent(String src, ProgramLocation loc, Program program) { - super(src, NAME); - this.loc = loc; - this.programName = program.getName(); - } - - /** - * Returns the ProgramLocation stored in this event. - */ - public ProgramLocation getLocation() { - return loc; - } - - /** - * Returns the Program object that the location refers to. - */ - public String getProgramName() { - return programName; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/events/OpenProgramPluginEvent.java b/Ghidra/Features/Base/src/main/java/ghidra/app/events/OpenProgramPluginEvent.java index ad67b55586..9cbf4c14dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/events/OpenProgramPluginEvent.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/events/OpenProgramPluginEvent.java @@ -18,20 +18,14 @@ package ghidra.app.events; import java.lang.ref.WeakReference; import ghidra.framework.plugintool.PluginEvent; -import ghidra.framework.plugintool.ToolEventName; import ghidra.program.model.listing.Program; /** * Event for telling a tool to open a program - *

- * This event shares a common tool-event name with the {@link OpenProgramPluginEvent} - * so that they have a single shared tool connection. */ -@ToolEventName(OpenProgramPluginEvent.TOOL_EVENT_NAME) // this allows the event to be considered for tool connection public class OpenProgramPluginEvent extends PluginEvent { static final String NAME = "Open Program"; - static final String TOOL_EVENT_NAME = "Open/Close Program"; private WeakReference programRef; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramCache.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramCache.java index 5cee62e4f8..a10028d25e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramCache.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramCache.java @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.Map; import ghidra.framework.data.DomainObjectFileListener; -import ghidra.framework.model.DomainObject; +import ghidra.framework.model.*; import ghidra.program.model.listing.Program; import ghidra.util.timer.GTimerCache; @@ -66,6 +66,7 @@ class ProgramCache extends GTimerCache { program.addConsumer(this); ProgramFileListener listener = new ProgramFileListener(key); program.addDomainFileListener(listener); + program.addListener(listener); listenerMap.put(program, listener); } @@ -73,9 +74,10 @@ class ProgramCache extends GTimerCache { protected void valueRemoved(ProgramLocator locator, Program program) { // whenever programs are removed from the cache, we need to remove the cache as a consumer // and remove the file changed listener - program.release(this); ProgramFileListener listener = listenerMap.remove(program); program.removeDomainFileListener(listener); + program.removeListener(listener); + program.release(this); } @Override @@ -92,9 +94,11 @@ class ProgramCache extends GTimerCache { * DomainObjectFileListener for programs in the cache. If a program instance has its DomainFile * changed (e.g., 'Save As' action), then the cache mapping is incorrect as it sill has the * program instance associated with its old DomainFile. So we need to add a listener to - * recognize when this occurs. If it does, we simply remove the entry from the cache. + * recognize when this occurs. If it does, we simply remove the entry from the cache. Also, + * we need to remove any programs from the cache if changes are made to avoid questions about + * who is responsible for saving changed programs that only live in the cache. */ - class ProgramFileListener implements DomainObjectFileListener { + class ProgramFileListener implements DomainObjectFileListener, DomainObjectListener { private ProgramLocator key; ProgramFileListener(ProgramLocator key) { @@ -105,6 +109,10 @@ class ProgramCache extends GTimerCache { public void domainFileChanged(DomainObject object) { remove(key); } - } + @Override + public void domainObjectChanged(DomainObjectChangedEvent ev) { + remove(key); + } + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java index 58f3a0857c..368a795e3c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramManagerPlugin.java @@ -304,7 +304,13 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti program = programMgr.getOpenProgram(locator); if (program != null) { program.addConsumer(consumer); - programCache.put(locator, program); + if (!program.isChanged()) { + // Don't put modified programs into the cache. + // NOTE: This will prevent upgraded programs from being added to the cache + // which are already open in the tool. This could be improved if we could + // distinguish between upgrade and non-upgrade changes. + programCache.put(locator, program); + } return program; } @@ -341,6 +347,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti @Override public void dispose() { + programCache.clear(); programMgr.dispose(); tool.clearLastEvents(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramSaveManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramSaveManager.java index f9a5dc6523..393e932cec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramSaveManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/progmgr/ProgramSaveManager.java @@ -66,8 +66,7 @@ class ProgramSaveManager { * the user */ boolean canClose(Program program) { - if (program == null || - (program.getDomainFile().getConsumers().size() > 1 && !tool.hasToolListeners())) { + if (!isOnlyToolConsumer(program)) { return true; } if (acquireSaveLock(program, "Close")) { @@ -105,9 +104,7 @@ class ProgramSaveManager { return saveChangedPrograms(saveList); } finally { - Iterator it = lockList.iterator(); - while (it.hasNext()) { - Program p = it.next(); + for (Program p : lockList) { p.unlock(); } } @@ -378,10 +375,9 @@ class ProgramSaveManager { "The Program is currently being modified by the following actions/tasks:\n "); TransactionInfo t = program.getCurrentTransactionInfo(); List list = t.getOpenSubTransactions(); - Iterator it = list.iterator(); - while (it.hasNext()) { + for (String element : list) { buf.append("\n "); - buf.append(it.next()); + buf.append(element); } buf.append("\n \n"); buf.append("WARNING! The above task(s) should be cancelled before attempting a " + @@ -412,10 +408,9 @@ class ProgramSaveManager { "The Program is currently being modified by the following actions/tasks:\n "); TransactionInfo t = program.getCurrentTransactionInfo(); List list = t.getOpenSubTransactions(); - Iterator it = list.iterator(); - while (it.hasNext()) { + for (String element : list) { buf.append("\n "); - buf.append(it.next()); + buf.append(element); } buf.append("\n \n"); buf.append( diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptAskMethodsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptAskMethodsTest.java index eada70135d..157d54eb22 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptAskMethodsTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptAskMethodsTest.java @@ -130,10 +130,14 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT public void testAskProgram_SCR8486() throws Exception { createScript(); - Program[] container = new Program[1]; + AtomicReference container = new AtomicReference<>(); runSwing(() -> { try { - container[0] = script.askProgram("Test - Pick Program"); + Program p = script.askProgram("Test - Pick Program"); + container.set(p); + if (p != null) { + p.release(this); + } } catch (Exception ioe) { failWithException("Caught unexepected during askProgram()", ioe); @@ -146,7 +150,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT runSwing(() -> okButton.doClick()); // this test will fail if we encountered an exception - assertNull(container[0]); + assertNull(container.get()); runSwing(() -> dtd.close()); }