Merge remote-tracking branch 'origin/Ghidra_11.0'

This commit is contained in:
Ryan Kurtz
2023-12-19 16:07:15 -05:00
7 changed files with 35 additions and 108 deletions
@@ -18,16 +18,11 @@ package ghidra.app.events;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import ghidra.framework.plugintool.PluginEvent; import ghidra.framework.plugintool.PluginEvent;
import ghidra.framework.plugintool.ToolEventName;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* Event for telling a tool to open a program * Event for telling a tool to close a program
* <p>
* 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 CloseProgramPluginEvent extends PluginEvent { public class CloseProgramPluginEvent extends PluginEvent {
static final String NAME = "Close Program"; static final String NAME = "Close Program";
@@ -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;
}
}
@@ -18,20 +18,14 @@ package ghidra.app.events;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import ghidra.framework.plugintool.PluginEvent; import ghidra.framework.plugintool.PluginEvent;
import ghidra.framework.plugintool.ToolEventName;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
/** /**
* Event for telling a tool to open a program * Event for telling a tool to open a program
* <p>
* 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 { public class OpenProgramPluginEvent extends PluginEvent {
static final String NAME = "Open Program"; static final String NAME = "Open Program";
static final String TOOL_EVENT_NAME = "Open/Close Program";
private WeakReference<Program> programRef; private WeakReference<Program> programRef;
@@ -20,7 +20,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import ghidra.framework.data.DomainObjectFileListener; import ghidra.framework.data.DomainObjectFileListener;
import ghidra.framework.model.DomainObject; import ghidra.framework.model.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.timer.GTimerCache; import ghidra.util.timer.GTimerCache;
@@ -66,6 +66,7 @@ class ProgramCache extends GTimerCache<ProgramLocator, Program> {
program.addConsumer(this); program.addConsumer(this);
ProgramFileListener listener = new ProgramFileListener(key); ProgramFileListener listener = new ProgramFileListener(key);
program.addDomainFileListener(listener); program.addDomainFileListener(listener);
program.addListener(listener);
listenerMap.put(program, listener); listenerMap.put(program, listener);
} }
@@ -73,9 +74,10 @@ class ProgramCache extends GTimerCache<ProgramLocator, Program> {
protected void valueRemoved(ProgramLocator locator, Program program) { protected void valueRemoved(ProgramLocator locator, Program program) {
// whenever programs are removed from the cache, we need to remove the cache as a consumer // whenever programs are removed from the cache, we need to remove the cache as a consumer
// and remove the file changed listener // and remove the file changed listener
program.release(this);
ProgramFileListener listener = listenerMap.remove(program); ProgramFileListener listener = listenerMap.remove(program);
program.removeDomainFileListener(listener); program.removeDomainFileListener(listener);
program.removeListener(listener);
program.release(this);
} }
@Override @Override
@@ -92,9 +94,11 @@ class ProgramCache extends GTimerCache<ProgramLocator, Program> {
* DomainObjectFileListener for programs in the cache. If a program instance has its DomainFile * 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 * 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 * 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; private ProgramLocator key;
ProgramFileListener(ProgramLocator key) { ProgramFileListener(ProgramLocator key) {
@@ -105,6 +109,10 @@ class ProgramCache extends GTimerCache<ProgramLocator, Program> {
public void domainFileChanged(DomainObject object) { public void domainFileChanged(DomainObject object) {
remove(key); remove(key);
} }
}
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
remove(key);
}
}
} }
@@ -304,7 +304,13 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti
program = programMgr.getOpenProgram(locator); program = programMgr.getOpenProgram(locator);
if (program != null) { if (program != null) {
program.addConsumer(consumer); 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; return program;
} }
@@ -341,6 +347,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager, Opti
@Override @Override
public void dispose() { public void dispose() {
programCache.clear();
programMgr.dispose(); programMgr.dispose();
tool.clearLastEvents(); tool.clearLastEvents();
} }
@@ -66,8 +66,7 @@ class ProgramSaveManager {
* the user * the user
*/ */
boolean canClose(Program program) { boolean canClose(Program program) {
if (program == null || if (!isOnlyToolConsumer(program)) {
(program.getDomainFile().getConsumers().size() > 1 && !tool.hasToolListeners())) {
return true; return true;
} }
if (acquireSaveLock(program, "Close")) { if (acquireSaveLock(program, "Close")) {
@@ -105,9 +104,7 @@ class ProgramSaveManager {
return saveChangedPrograms(saveList); return saveChangedPrograms(saveList);
} }
finally { finally {
Iterator<Program> it = lockList.iterator(); for (Program p : lockList) {
while (it.hasNext()) {
Program p = it.next();
p.unlock(); p.unlock();
} }
} }
@@ -378,10 +375,9 @@ class ProgramSaveManager {
"The Program is currently being modified by the following actions/tasks:\n "); "The Program is currently being modified by the following actions/tasks:\n ");
TransactionInfo t = program.getCurrentTransactionInfo(); TransactionInfo t = program.getCurrentTransactionInfo();
List<String> list = t.getOpenSubTransactions(); List<String> list = t.getOpenSubTransactions();
Iterator<String> it = list.iterator(); for (String element : list) {
while (it.hasNext()) {
buf.append("\n "); buf.append("\n ");
buf.append(it.next()); buf.append(element);
} }
buf.append("\n \n"); buf.append("\n \n");
buf.append("WARNING! The above task(s) should be cancelled before attempting a " + 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 "); "The Program is currently being modified by the following actions/tasks:\n ");
TransactionInfo t = program.getCurrentTransactionInfo(); TransactionInfo t = program.getCurrentTransactionInfo();
List<String> list = t.getOpenSubTransactions(); List<String> list = t.getOpenSubTransactions();
Iterator<String> it = list.iterator(); for (String element : list) {
while (it.hasNext()) {
buf.append("\n "); buf.append("\n ");
buf.append(it.next()); buf.append(element);
} }
buf.append("\n \n"); buf.append("\n \n");
buf.append( buf.append(
@@ -130,10 +130,14 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
public void testAskProgram_SCR8486() throws Exception { public void testAskProgram_SCR8486() throws Exception {
createScript(); createScript();
Program[] container = new Program[1]; AtomicReference<Program> container = new AtomicReference<>();
runSwing(() -> { runSwing(() -> {
try { 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) { catch (Exception ioe) {
failWithException("Caught unexepected during askProgram()", ioe); failWithException("Caught unexepected during askProgram()", ioe);
@@ -146,7 +150,7 @@ public class GhidraScriptAskMethodsTest extends AbstractGhidraHeadedIntegrationT
runSwing(() -> okButton.doClick()); runSwing(() -> okButton.doClick());
// this test will fail if we encountered an exception // this test will fail if we encountered an exception
assertNull(container[0]); assertNull(container.get());
runSwing(() -> dtd.close()); runSwing(() -> dtd.close());
} }