mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Merge remote-tracking branch
'origin/GP-788-dragonmacher-symbol-table-ui-lockup--SQUASHED' Conflicts: Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java
This commit is contained in:
commit
ceb0b5ad27
10 changed files with 411 additions and 123 deletions
|
@ -299,6 +299,10 @@ public class BookmarkPlugin extends ProgramPlugin
|
|||
@Override
|
||||
public synchronized void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
|
||||
if (!provider.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_MOVED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) {
|
||||
|
|
|
@ -106,7 +106,7 @@ public class AboutProgramPlugin extends Plugin implements FrontEndable {
|
|||
ProgramActionContext pac = (ProgramActionContext) context;
|
||||
Program program = pac.getProgram();
|
||||
if (program != null) {
|
||||
String menuName = "About " + program.getDomainFile().getName() + "...";
|
||||
String menuName = "About " + program.getDomainFile().getName();
|
||||
getMenuBarData().setMenuItemName(menuName);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,10 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
|||
setVisible(true);
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return referenceKeyModel.isBusy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
referenceKeyModel.setProgram(null);
|
||||
|
|
|
@ -151,8 +151,8 @@ class SymbolPanel extends JPanel {
|
|||
filterDialog.adjustFilter(symProvider, tableModel);
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
return filterDialog.getFilter();
|
||||
SymbolFilter getFilter() {
|
||||
return tableModel.getFilter();
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
|
|
|
@ -149,10 +149,14 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
return symbolPanel.getTable();
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
SymbolFilter getFilter() {
|
||||
return symbolPanel.getFilter();
|
||||
}
|
||||
|
||||
boolean isShowingDynamicSymbols() {
|
||||
return getFilter().acceptsDefaultLabelSymbols();
|
||||
}
|
||||
|
||||
private String generateSubTitle() {
|
||||
SymbolFilter filter = symbolKeyModel.getFilter();
|
||||
int rowCount = symbolKeyModel.getRowCount();
|
||||
|
@ -174,11 +178,15 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return symbolKeyModel.isBusy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
symbolKeyModel.reload(null);
|
||||
if (plugin != null) {
|
||||
plugin.closeReferenceProvider();
|
||||
plugin.symbolProviderClosed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,5 +207,4 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
void writeConfigState(SaveState saveState) {
|
||||
symbolPanel.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
|
||||
@Override
|
||||
protected TableColumnDescriptor<Reference> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Reference> descriptor = new TableColumnDescriptor<Reference>();
|
||||
TableColumnDescriptor<Reference> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new ReferenceFromAddressTableColumn()),
|
||||
|
@ -139,7 +139,6 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
|
||||
void symbolChanged(Symbol symbol) {
|
||||
if (currentSymbol != null && currentSymbol.equals(symbol)) {
|
||||
setCurrentSymbol(symbol);
|
||||
return;
|
||||
}
|
||||
checkRefs(symbol);
|
||||
|
|
|
@ -264,12 +264,13 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
|||
updateObject(Symbol);
|
||||
}
|
||||
else {
|
||||
// the symbol may be in the table, as it could have passed the filter before the change
|
||||
removeObject(Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void delete(List<Symbol> rowObjects) {
|
||||
if (rowObjects == null || rowObjects.size() == 0) {
|
||||
if (rowObjects == null || rowObjects.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -530,7 +531,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
|||
private class SourceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, SourceType> {
|
||||
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<SourceType>() {
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected String getText(Object value) {
|
||||
if (value == null) {
|
||||
|
|
|
@ -44,6 +44,9 @@ import ghidra.util.table.GhidraTable;
|
|||
import ghidra.util.table.SelectionNavigationAction;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.worker.Job;
|
||||
import ghidra.util.worker.Worker;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
|
@ -88,6 +91,13 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
private BlockModelService blockModelService;
|
||||
private SwingUpdateManager swingMgr;
|
||||
|
||||
/**
|
||||
* A worker that will process domain object change event work off of the Swing thread. This
|
||||
* solves the issue of db lock contention that can happen during analysis while this class
|
||||
* attempts to get symbols from the db while processing a flurry of domain events.
|
||||
*/
|
||||
private Worker domainObjectWorker = Worker.createGuiWorker();
|
||||
|
||||
public SymbolTablePlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
|
@ -124,6 +134,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
deleteAction.dispose();
|
||||
makeSelectionAction.dispose();
|
||||
|
||||
domainObjectWorker.dispose();
|
||||
if (symProvider != null) {
|
||||
symProvider.dispose();
|
||||
symProvider = null;
|
||||
|
@ -165,17 +176,15 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
if (oldProg != null) {
|
||||
inspector.setProgram(null);
|
||||
oldProg.removeListener(this);
|
||||
domainObjectWorker.clearAllJobs();
|
||||
symProvider.setProgram(null, inspector);
|
||||
refProvider.setProgram(null, inspector);
|
||||
tool.contextChanged(symProvider);
|
||||
}
|
||||
currentProgram = newProg;
|
||||
if (newProg != null) {
|
||||
|
||||
currentProgram.addListener(this);
|
||||
|
||||
inspector.setProgram(currentProgram);
|
||||
|
||||
symProvider.setProgram(currentProgram, inspector);
|
||||
refProvider.setProgram(currentProgram, inspector);
|
||||
}
|
||||
|
@ -184,17 +193,27 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
}
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return domainObjectWorker.isBusy() || symProvider.isBusy() || refProvider.isBusy();
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
domainObjectWorker.clearAllJobs();
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (!symProvider.isVisible()) {
|
||||
if (!symProvider.isVisible() && !refProvider.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) {
|
||||
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -208,103 +227,74 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
}
|
||||
|
||||
ProgramChangeRecord rec = (ProgramChangeRecord) doRecord;
|
||||
Symbol symbol = null;
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
switch (eventType) {
|
||||
case ChangeManager.DOCR_CODE_ADDED:
|
||||
case ChangeManager.DOCR_CODE_REMOVED:
|
||||
if (rec.getNewValue() instanceof Data) {
|
||||
symbol = symbolTable.getPrimarySymbol(rec.getStart());
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
domainObjectWorker.schedule(
|
||||
new CodeAddedRemoveJob(currentProgram, rec.getStart()));
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_ADDED:
|
||||
|
||||
Address addAddr = rec.getStart();
|
||||
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(addAddr);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolAdded(symbol);
|
||||
refProvider.symbolAdded(symbol);
|
||||
Symbol symbol = (Symbol) rec.getNewValue();
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolAddedJob(currentProgram, symbol, addAddr));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_REMOVED:
|
||||
|
||||
Address removeAddr = rec.getStart();
|
||||
Long symbolID = (Long) rec.getNewValue();
|
||||
Symbol removedSymbol =
|
||||
symbolTable.createSymbolPlaceholder(removeAddr, symbolID);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(removeAddr);
|
||||
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
|
||||
symProvider.symbolAdded(primaryAtRemove);
|
||||
}
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolRemovedJob(currentProgram, removeAddr, symbolID));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_RENAMED:
|
||||
case ChangeManager.DOCR_SYMBOL_SCOPE_CHANGED:
|
||||
case ChangeManager.DOCR_SYMBOL_DATA_CHANGED:
|
||||
|
||||
symbol = (Symbol) rec.getObject();
|
||||
if (!symbol.isDeleted()) { // symbol may have been removed (e.g., parameter)
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
domainObjectWorker.schedule(new SymbolChangedJob(currentProgram, symbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_SOURCE_CHANGED:
|
||||
|
||||
symbol = (Symbol) rec.getObject();
|
||||
symProvider.symbolChanged(symbol);
|
||||
domainObjectWorker.schedule(new SymbolSourceChangedJob(currentProgram, symbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_SET_AS_PRIMARY:
|
||||
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolChanged(symbol);
|
||||
Symbol oldSymbol = (Symbol) rec.getOldValue();
|
||||
if (oldSymbol != null) {
|
||||
symProvider.symbolChanged(oldSymbol);
|
||||
}
|
||||
Symbol oldPrimarySymbol = (Symbol) rec.getOldValue();
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolSetAsPrimaryJob(currentProgram, symbol, oldPrimarySymbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_ADDED:
|
||||
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_REMOVED:
|
||||
break;
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_ADDED:
|
||||
Reference ref = (Reference) rec.getObject();
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol != null) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
break;
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_REMOVED:
|
||||
ref = (Reference) rec.getObject();
|
||||
Address toAddr = ref.getToAddress();
|
||||
if (toAddr.isMemoryAddress()) {
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol == null) {
|
||||
|
||||
long id = symbolTable.getDynamicSymbolID(ref.getToAddress());
|
||||
removedSymbol = symbolTable.createSymbolPlaceholder(toAddr, id);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
}
|
||||
else {
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
Reference ref = (Reference) rec.getObject();
|
||||
domainObjectWorker.schedule(new ReferenceAddedJob(currentProgram, ref));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_REMOVED:
|
||||
|
||||
ref = (Reference) rec.getObject();
|
||||
domainObjectWorker.schedule(new ReferenceRemovedJob(currentProgram, ref));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_ADDED:
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_REMOVED:
|
||||
Symbol[] symbols = symbolTable.getSymbols(rec.getStart());
|
||||
for (Symbol element : symbols) {
|
||||
symProvider.symbolChanged(element);
|
||||
refProvider.symbolChanged(element);
|
||||
}
|
||||
|
||||
Address address = rec.getStart();
|
||||
domainObjectWorker.schedule(
|
||||
new ExternalEntryChangedJob(currentProgram, address));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -336,7 +326,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
}
|
||||
}
|
||||
|
||||
void closeReferenceProvider() {
|
||||
void symbolProviderClosed() {
|
||||
domainObjectWorker.clearAllJobs();
|
||||
if (refProvider != null) {
|
||||
refProvider.closeComponent();
|
||||
}
|
||||
|
@ -514,4 +505,268 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
|||
action.setSelected(false);
|
||||
action.setSelected(true);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Table Update Jobs
|
||||
//==================================================================================================
|
||||
|
||||
private abstract class AbstractSymbolUpdateJob extends Job {
|
||||
|
||||
protected Program program;
|
||||
|
||||
AbstractSymbolUpdateJob(Program program) {
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run(TaskMonitor taskMonitor) {
|
||||
if (program != currentProgram) {
|
||||
return;
|
||||
}
|
||||
doRun();
|
||||
}
|
||||
|
||||
protected abstract void doRun();
|
||||
}
|
||||
|
||||
private class CodeAddedRemoveJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Address start;
|
||||
|
||||
CodeAddedRemoveJob(Program program, Address start) {
|
||||
super(program);
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: this code *should* be checking the entire address range to handle the case
|
||||
// where large address range was cleared. This implementation will handle the
|
||||
// case where individual code units are cleared. This feature has been this way
|
||||
// for many years. The assumption is that most users are not showing dynamic
|
||||
// symbols often, especially not when performing analysis or clearing large
|
||||
// address ranges. Checking each address of the changed range is very slow.
|
||||
// This code will need to be updated in the future if we decide updating the
|
||||
// dynamic symbols in the symbol table is worth the cost. For now, if the table
|
||||
// becomes out-of-date, then user can simply close and re-open the table to
|
||||
// trigger an update.
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(start);
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolAddedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
private Address address;
|
||||
|
||||
SymbolAddedJob(Program program, Symbol symbol, Address address) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
symProvider.symbolAdded(symbol);
|
||||
refProvider.symbolAdded(symbol);
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(address);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
refProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolRemovedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private long symbolId;
|
||||
private Address address;
|
||||
|
||||
SymbolRemovedJob(Program program, Address address, long symbolId) {
|
||||
super(program);
|
||||
this.address = address;
|
||||
this.symbolId = symbolId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
Symbol removedSymbol =
|
||||
symbolTable.createSymbolPlaceholder(address, symbolId);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(address);
|
||||
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
|
||||
symProvider.symbolAdded(primaryAtRemove);
|
||||
refProvider.symbolAdded(primaryAtRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
|
||||
SymbolChangedJob(Program program, Symbol symbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
// Note: should not need this check--the provider should be built to handle this
|
||||
// if (symbol.checkIsValid())
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolSourceChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
|
||||
SymbolSourceChangedJob(Program program, Symbol symbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
symProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolSetAsPrimaryJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
private Symbol oldPrimarySymbol;
|
||||
|
||||
SymbolSetAsPrimaryJob(Program program, Symbol symbol, Symbol oldPrimarySymbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
this.oldPrimarySymbol = oldPrimarySymbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
symProvider.symbolChanged(symbol);
|
||||
if (oldPrimarySymbol != null) {
|
||||
symProvider.symbolChanged(oldPrimarySymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceAddedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Reference reference;
|
||||
|
||||
ReferenceAddedJob(Program program, Reference reference) {
|
||||
super(program);
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
boolean isValid = toAddr.isMemoryAddress() || toAddr.isExternalAddress();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(reference);
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols() && symbol.isDynamic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceRemovedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Reference reference;
|
||||
|
||||
ReferenceRemovedJob(Program program, Reference reference) {
|
||||
super(program);
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
boolean isValid = toAddr.isMemoryAddress() || toAddr.isExternalAddress();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(reference);
|
||||
if (symbol != null) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
|
||||
if (symProvider.isShowingDynamicSymbols()) {
|
||||
long id = symbolTable.getDynamicSymbolID(reference.getToAddress());
|
||||
Symbol removedSymbol = symbolTable.createSymbolPlaceholder(toAddr, id);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExternalEntryChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Address address;
|
||||
|
||||
ExternalEntryChangedJob(Program program, Address address) {
|
||||
super(program);
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol[] symbols = symbolTable.getSymbols(address);
|
||||
for (Symbol element : symbols) {
|
||||
symProvider.symbolChanged(element);
|
||||
refProvider.symbolChanged(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import docking.action.DockingActionIf;
|
|||
import docking.action.ToggleDockingAction;
|
||||
import docking.widgets.filter.*;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.threaded.ThreadedTableModel;
|
||||
import ghidra.app.cmd.label.AddLabelCmd;
|
||||
import ghidra.app.cmd.label.CreateNamespacesCmd;
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
|
@ -273,7 +272,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
pressButtonByText(filterDialog1, "OK");
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
//
|
||||
// Functions: 'ghidra', 'func_with_parms'
|
||||
|
@ -294,7 +293,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
pressButtonByText(filterDialog2, "OK");
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
//
|
||||
// Entry Points: 'entry'
|
||||
|
@ -314,7 +313,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
});
|
||||
pressButtonByText(filterDialog3, "OK");
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
//
|
||||
// Locals: 'AnotherLocal', 'MyLocal'
|
||||
|
@ -348,7 +347,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testEditing() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
String symbolName = "ghidra";
|
||||
int row = findRow(symbolName);
|
||||
|
@ -367,7 +366,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
myTypeText(editor, ".Is.Cool");
|
||||
runSwing(() -> symbolTable.editingStopped(new ChangeEvent(symbolTable)));
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertTrue(!symbolTable.isEditing());
|
||||
|
||||
|
@ -391,7 +390,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
st.createLabel(sample.getNewAddress(0x01008500), "abc123", SourceType.USER_DEFINED);
|
||||
});
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
int testTimeoutMs = 100;
|
||||
symbolTable.setAutoLookupTimeout(testTimeoutMs);
|
||||
|
||||
|
@ -432,7 +431,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
assertTrue(deleteAction.isEnabled());
|
||||
performAction(deleteAction, true);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertNull(getUniqueSymbol(program, "ghidra"));
|
||||
Symbol myLocalSymbol = getUniqueSymbol(program, "MyLocal");
|
||||
|
@ -462,7 +461,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertNull("Delete action did not delete symbol: " + anotherLocalSymbol,
|
||||
anotherLocalSymbol);// AnotherLocal should have been promoted to global since user defined.
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
// 1 Data label removed
|
||||
int expected = rowCount - 1;
|
||||
|
@ -566,7 +565,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertFalse(clearPinnedAction.isEnabledForContext(actionContext));
|
||||
|
||||
performAction(setPinnedAction, actionContext, true);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
for (int selectedRow : selectedRows) {
|
||||
Symbol symbol = getSymbol(selectedRow);
|
||||
assertTrue(symbol.isPinned());
|
||||
|
@ -611,7 +610,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return st.createLabel(sample.getNewAddress(0x01007000), "Zeus",
|
||||
SourceType.USER_DEFINED);
|
||||
});
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
assertEquals(rowCount + 1, symbolTable.getRowCount());
|
||||
assertTrue(symbolModel.getRowIndex(sym) >= 0);
|
||||
|
||||
|
@ -619,7 +618,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return st.createLabel(sample.getNewAddress(0x01007100), "Athena",
|
||||
SourceType.USER_DEFINED);
|
||||
});
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
assertEquals(rowCount + 2, symbolTable.getRowCount());
|
||||
assertTrue(symbolModel.getRowIndex(sym) >= 0);
|
||||
}
|
||||
|
@ -648,7 +647,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return st.createLabel(sample.getNewAddress(0x01007000), "saaaa",
|
||||
SourceType.USER_DEFINED);
|
||||
});
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
assertTrue(symbolModel.getRowIndex(sym) >= 0);
|
||||
|
||||
// make sure we added one while the filter is on
|
||||
|
@ -672,7 +671,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
pressButtonByText(filterDialog1, "OK");
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
//
|
||||
// Functions: 'ghidra', 'func_with_parms'
|
||||
|
@ -696,7 +695,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertNull(getUniqueSymbol(program, "EXT_00000051"));
|
||||
|
||||
tx(program, () -> st.removeSymbolSpecial(sym));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
// entry symbol replaced by dynamic External Entry symbol
|
||||
assertNull(getUniqueSymbol(program, "entry"));
|
||||
|
@ -723,7 +722,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
rm.setPrimary(ref, true);
|
||||
});
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
row = symbolModel.getRowIndex(s);
|
||||
|
||||
|
@ -752,7 +751,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
manager.delete(ref);
|
||||
});
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
refCount = getRefCount(row);
|
||||
assertNotNull(refCount);
|
||||
|
@ -782,19 +781,19 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
ClearCmd cmd = new ClearCmd(program.getMemory(), new ClearOptions());
|
||||
applyCmd(program, cmd);
|
||||
waitForBusyTool(tool);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
// Externals are not cleared
|
||||
int clearedRowCount = 3;
|
||||
assertEquals(clearedRowCount, symbolTable.getRowCount());
|
||||
|
||||
undo(program);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertEquals(startRowCount, symbolTable.getRowCount());
|
||||
|
||||
redo(program);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertEquals(clearedRowCount, symbolTable.getRowCount());
|
||||
}
|
||||
|
@ -815,7 +814,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
pressButtonByText(filterDialog, "OK");
|
||||
waitForSwing();
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
@ -880,8 +879,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
performAction(refsToAction, true);
|
||||
waitForSwing();
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy(referenceTable);
|
||||
waitForNotBusy();
|
||||
waitForNotBusy();
|
||||
assertEquals(4, referenceTable.getRowCount());
|
||||
|
||||
assertReferencesAddressColumnValue(0, 0x54);
|
||||
|
@ -899,8 +898,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForSwing();
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy(referenceTable);
|
||||
waitForNotBusy();
|
||||
waitForNotBusy();
|
||||
assertEquals(3, referenceTable.getRowCount());
|
||||
|
||||
assertReferencesAddressColumnValue(0, 0x53);
|
||||
|
@ -917,8 +916,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForSwing();
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy(referenceTable);
|
||||
waitForNotBusy();
|
||||
waitForNotBusy();
|
||||
assertEquals(2, referenceTable.getRowCount());
|
||||
|
||||
// data
|
||||
|
@ -1085,33 +1084,33 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
addLabel("bob", null, addr("010058f6"));
|
||||
|
||||
addLabel("bob", "billy", addr("01005917"));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
int updatedRowCount = symbolTable.getRowCount();
|
||||
assertEquals(rowCount + 2, updatedRowCount);
|
||||
|
||||
// test ascending
|
||||
runSwing(() -> TableUtils.columnSelected(symbolTable, 0));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
setFilterOptions(TextFilterStrategy.STARTS_WITH, false);
|
||||
|
||||
myTypeText(textField, "bo");
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
// make sure both 'bob's are in the table
|
||||
assertEquals("Did not find two bobs.", 2, symbolTable.getRowCount());
|
||||
modelMatchesIgnoringCase("bob");
|
||||
|
||||
myTypeText(textField, "b");
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertEquals("Did not find two bobs.", 2, symbolTable.getRowCount());
|
||||
modelMatchesIgnoringCase("bob");
|
||||
|
||||
// test descending
|
||||
runSwing(() -> TableUtils.columnSelected(symbolTable, 0));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
assertEquals("Did not find two bobs in descending order.", 2, symbolTable.getRowCount());
|
||||
modelMatchesIgnoringCase("bob");
|
||||
|
@ -1250,12 +1249,12 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// use the version of triggerText that allows us to consume the event directly, bypassing
|
||||
// the focus system
|
||||
triggerText(symbolTable, text, consumer);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private void setName(Symbol symbol, String name, SourceType type) throws Exception {
|
||||
tx(program, () -> symbol.setName(name, SourceType.DEFAULT));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private Symbol getSymbol(int row) {
|
||||
|
@ -1315,7 +1314,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
private void setFilterOptions(TextFilterStrategy filterStrategy, boolean caseSensitive)
|
||||
throws Exception {
|
||||
filterPanel.setFilterOptions(new FilterOptions(filterStrategy, true, caseSensitive, false));
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private JTextField getFilterTextField() {
|
||||
|
@ -1351,7 +1350,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
pressButtonByText(filterDialog, "OK", true);
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private int getRowForSymbol(Symbol symbol) {
|
||||
|
@ -1390,7 +1389,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
Command command = new AddLabelCmd(address, label, namespace, SourceType.USER_DEFINED);
|
||||
tool.execute(command, program);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
|
@ -1399,7 +1398,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private void myTypeText(Component c, String text) throws Exception {
|
||||
triggerText(c, text);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
private void deleteTextFieldText(JTextField textField) {
|
||||
|
@ -1410,7 +1409,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForSwing();
|
||||
try {
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
catch (Exception exc) {
|
||||
// we don't care
|
||||
|
@ -1419,7 +1418,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private void typeBackspaceOnComponent(Component component) throws Exception {
|
||||
triggerActionKey(component, 0, KeyEvent.VK_BACK_SPACE);
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
// makes sure that all of the symbols in the model start with the given string
|
||||
|
@ -1459,10 +1458,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void waitForNotBusy(GTable table) throws Exception {
|
||||
private void waitForNotBusy() throws Exception {
|
||||
waitForProgram(program);
|
||||
ThreadedTableModel<?, ?> model = (ThreadedTableModel<?, ?>) table.getModel();
|
||||
waitForTableModel(model);
|
||||
waitForCondition(() -> !plugin.isBusy());
|
||||
}
|
||||
|
||||
private void openProgram(String name) throws Exception {
|
||||
|
@ -1583,9 +1581,9 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
filter.setFilter("Function Labels", true);
|
||||
filter.setFilter("Default (Functions)", true);
|
||||
filter.setFilter("Default (Labels)", true);
|
||||
symbolModel.setFilter(filter);
|
||||
runSwing(() -> symbolModel.setFilter(filter));
|
||||
|
||||
waitForNotBusy(symbolTable);
|
||||
waitForNotBusy();
|
||||
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
}
|
||||
|
|
|
@ -153,12 +153,32 @@ public class TableData<ROW_OBJECT> implements Iterable<ROW_OBJECT> {
|
|||
return -1;
|
||||
}
|
||||
|
||||
boolean remove(ROW_OBJECT o) {
|
||||
boolean remove(ROW_OBJECT t) {
|
||||
if (source != null) {
|
||||
source.remove(o);
|
||||
source.remove(t);
|
||||
}
|
||||
|
||||
return data.remove(o);
|
||||
if (sortContext.isUnsorted()) {
|
||||
return data.remove(t); // no sort; cannot binary search
|
||||
}
|
||||
|
||||
Comparator<ROW_OBJECT> comparator = sortContext.getComparator();
|
||||
int index = Collections.binarySearch(data, t, comparator);
|
||||
if (index > 0) {
|
||||
data.remove(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// At this point we have one of 2 conditions: the object is not in the list, or the object
|
||||
// does not work with the current sort comparator.
|
||||
//
|
||||
// There are cases where the comparator will not work for the object handed to this method,
|
||||
// such as when some db objects get deleted and the client uses a proxy object to perform
|
||||
// the delete. To handle these odd cases, we still have to brute-force search. If this
|
||||
// proves to be a bottleneck, then we can update how we handle item removal.
|
||||
//
|
||||
return data.remove(t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue