mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +00:00
Merge remote-tracking branch
'origin/GT-3226-dragonmacher-tables-slow-sorting' (closes #500)
This commit is contained in:
commit
3051601206
Binary file not shown.
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 58 KiB |
@ -31,7 +31,7 @@
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<P>The columns in the table are:</P>
|
||||
<P>Some of the columns in the table are:</P>
|
||||
|
||||
<CENTER>
|
||||
<TABLE border="1" width="73%" height="159">
|
||||
@ -54,13 +54,6 @@
|
||||
<TD width="89%" height="22">Symbol type (Function, External, Class, etc).</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="11%" height="15"><B>Datatype</B></TD>
|
||||
|
||||
<TD width="89%" height="15">Datatype (i.e., byte, float, etc.) applied at symbol
|
||||
address.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="11%" height="21"><B>Namespace</B></TD>
|
||||
|
||||
@ -89,7 +82,15 @@
|
||||
<P><I><IMG src="../../shared/note.png"> You can sort the table on any column by clicking on
|
||||
the column header. The column can be sorted in ascending or descending order.</I></P>
|
||||
|
||||
<P><I><IMG src="../../shared/tip.png"></I> The colors for <B><FONT color="#ff0000">bad
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/warning.png">
|
||||
Sorting a column in the symbol table when the program has a large number of symbols
|
||||
can be slow. If you do not need sorting, then you can control-click the sorted column
|
||||
to remove the sort.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><IMG src="../../shared/tip.png"> The colors for <B><FONT color="#ff0000">bad
|
||||
references</FONT></B>, <B><FONT color="#ff00ff">entry points</FONT></B>, <B>dead code</B>,
|
||||
<B><FONT color="#c0c0c0">offcut code</FONT></B>, <B><FONT color="#0000ff">function
|
||||
names</FONT></B>, <B><FONT color="#008040">local symbols</FONT></B>, <B><FONT color=
|
||||
@ -112,6 +113,15 @@
|
||||
|
||||
<P>The <I>Name Only</I> checkbox allows you to toggle whether to filter on only the name
|
||||
column or all the columns in the table.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/tip.png">
|
||||
Filtering the symbol table when the program has a large number of symbols can be slow.
|
||||
When only filtering on the symbol name, via the checkbox above, the overall filtering
|
||||
is considerably faster.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<P>The filter text field will accept basic globbing characters such as '<B>*</B>' and
|
||||
'<B>?</B>' within the filter text unless the "Regular Expression" filter strategy is
|
||||
|
@ -83,7 +83,8 @@
|
||||
<p><img border="0" src="../../shared/tip.png" alt="">
|
||||
To remove a sort column from a multiple column sort, Ctrl-left-click that column.
|
||||
This will even work when only one column is sorted, <b><i>thus effectively disabling
|
||||
sorting for the table</i></b>.
|
||||
sorting for the table</i></b>. <u>Disabling sorting can greatly increase the
|
||||
table's performance when the number of rows is large</u>
|
||||
</p>
|
||||
</blockquote>
|
||||
|
||||
@ -212,5 +213,11 @@
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
|
||||
<UL>
|
||||
<LI><A href="help/topics/Trees/GhidraTreeFilter.html">Table Filtering</A></LI>
|
||||
</UL>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -15,7 +15,11 @@
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Most trees and tables in Ghidra support filtering and have a text filter located at the
|
||||
bottom of the tree or table. Below is an example from the Data Type Manager. The filter is
|
||||
bottom of the tree or table. Tables also support the concept of
|
||||
<A HREF="#Column_Filters">Column Filters</A> described below.
|
||||
</P>
|
||||
|
||||
<P>Below is an example from the Data Type Manager. The filter is
|
||||
currently set to "Starts With", but you can select a different filter strategy.</P>
|
||||
|
||||
<CENTER>
|
||||
@ -270,6 +274,17 @@
|
||||
<BLOCKQUOTE>
|
||||
<P>Most filterable tables in Ghidra support advanced filtering based on column values. This
|
||||
allows for complex filtering where you can logically combine column specific clauses.</P>
|
||||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="../../shared/warning.png">
|
||||
Some columns in tables are not filterable via the filter text field below the table.
|
||||
For example, many numeric columns are ignored by the text filter because they can be
|
||||
slow to calculate and they are better filtered by using a range filter, as is available
|
||||
using column filters.
|
||||
</p>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BR>
|
||||
|
||||
|
||||
@ -458,5 +473,20 @@
|
||||
filter.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<!-- extra space at the bottom of the page (for readability) -->
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
|
||||
<UL>
|
||||
<LI><A href="help/topics/Tables/GhidraTableHeaders.html">Table Sorting</A></LI>
|
||||
</UL>
|
||||
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
@ -120,9 +120,14 @@ public class RenameLabelCmd implements Command {
|
||||
}
|
||||
else {
|
||||
s.setName(newName, source);
|
||||
if ((newName.length() != 0 && !newName.equals(s.getName())) ||
|
||||
(newName.length() == 0 && s.getSource() != SourceType.DEFAULT)) {
|
||||
errorMsg = "Rename failed - default names may not be used";
|
||||
|
||||
if (newName.length() == 0 && s.getSource() != SourceType.DEFAULT) {
|
||||
errorMsg = "Rename failed - cannot set non-default symbol name to \"\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!newName.equals(s.getName())) {
|
||||
errorMsg = "Rename failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -16,80 +16,34 @@
|
||||
package ghidra.app.context;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.*;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
|
||||
public class ProgramSymbolActionContext extends ProgramActionContext {
|
||||
|
||||
private final long[] symbolIDs;
|
||||
private List<Symbol> symbols = new ArrayList<Symbol>();
|
||||
|
||||
public ProgramSymbolActionContext(ComponentProvider provider, Program program, long[] symbolIDs,
|
||||
Component sourceComponent) {
|
||||
public ProgramSymbolActionContext(ComponentProvider provider, Program program,
|
||||
List<Symbol> symbols, Component sourceComponent) {
|
||||
super(provider, program, sourceComponent);
|
||||
this.symbolIDs = symbolIDs;
|
||||
this.symbols = symbols == null ? Collections.emptyList() : symbols;
|
||||
}
|
||||
|
||||
public int getSymbolCount() {
|
||||
return symbolIDs != null ? symbolIDs.length : 0;
|
||||
return symbols.size();
|
||||
}
|
||||
|
||||
public Symbol getFirstSymbol() {
|
||||
if (symbolIDs == null || symbolIDs.length == 0) {
|
||||
if (symbols.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return program.getSymbolTable().getSymbol(symbolIDs[0]);
|
||||
return symbols.get(0);
|
||||
}
|
||||
|
||||
public SymbolIterator getSymbols() {
|
||||
return new MySymbolIterator();
|
||||
}
|
||||
|
||||
private class MySymbolIterator implements SymbolIterator {
|
||||
|
||||
private int index = -1;
|
||||
private Symbol symbol = null;
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Symbol> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (symbol != null) {
|
||||
return true;
|
||||
}
|
||||
if (symbolIDs == null) {
|
||||
return false;
|
||||
}
|
||||
while (index < (symbolIDs.length - 1)) {
|
||||
symbol = program.getSymbolTable().getSymbol(symbolIDs[++index]);
|
||||
if (symbol != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol next() {
|
||||
if (hasNext()) {
|
||||
Symbol s = symbol;
|
||||
symbol = null;
|
||||
return s;
|
||||
}
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
public Iterable<Symbol> getSymbols() {
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,42 +15,51 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
class FunctionRowObject implements Comparable<FunctionRowObject> {
|
||||
|
||||
private final long key;
|
||||
private final Function function;
|
||||
|
||||
FunctionRowObject(long key) {
|
||||
this.key = key;
|
||||
FunctionRowObject(Function function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
Function getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (int) (key ^ (key >>> 32));
|
||||
return result;
|
||||
return (int) function.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (obj == null)
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long key = function.getID();
|
||||
FunctionRowObject other = (FunctionRowObject) obj;
|
||||
if (key != other.key)
|
||||
if (key != other.function.getID()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
long getKey() {
|
||||
return key;
|
||||
return function.getID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(FunctionRowObject o) {
|
||||
return ((Long) key).compareTo(o.key);
|
||||
return ((Long) function.getID()).compareTo(o.function.getID());
|
||||
}
|
||||
}
|
||||
|
@ -17,16 +17,17 @@ package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToAddressTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
||||
public class FunctionRowObjectToAddressTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, Address> {
|
||||
|
||||
@Override
|
||||
public Address map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
public Address map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -16,16 +16,18 @@
|
||||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToFunctionTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
||||
public class FunctionRowObjectToFunctionTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, Function> {
|
||||
|
||||
@Override
|
||||
public Function map(FunctionRowObject rowObject, Program program, ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
return functionManager.getFunction(rowObject.getKey());
|
||||
public Function map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
Function function = rowObject.getFunction();
|
||||
return function;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,19 +16,19 @@
|
||||
package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.FunctionSignatureFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class FunctionRowObjectToProgramLocationTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
||||
public class FunctionRowObjectToProgramLocationTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<FunctionRowObject, ProgramLocation> {
|
||||
|
||||
@Override
|
||||
public ProgramLocation map(FunctionRowObject rowObject, Program program,
|
||||
ServiceProvider serviceProvider) {
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ package ghidra.app.plugin.core.functionwindow;
|
||||
|
||||
import docking.widgets.table.DiscoverableTableUtils;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.LongIterator;
|
||||
@ -43,20 +45,19 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<FunctionRowObject> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<FunctionRowObject> descriptor =
|
||||
new TableColumnDescriptor<>();
|
||||
TableColumnDescriptor<FunctionRowObject> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new LabelTableColumn()));
|
||||
descriptor.addVisibleColumn(new NameTableColumn());
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new FunctionSignatureTableColumn()));
|
||||
//make function size a default column so that a user who wants to know the function size
|
||||
//won't add the "Byte Count" column (which only display the number of bytes in the code
|
||||
//unit at the function's entry point).
|
||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
||||
new FunctionBodySizeTableColumn()));
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new FunctionSignatureTableColumn()));
|
||||
|
||||
// Make function size a default column so that a user who wants to know the function size
|
||||
// won't add the "Byte Count" column (which only display the number of bytes in the code
|
||||
// unit at the function's entry point).
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new FunctionBodySizeTableColumn()));
|
||||
|
||||
// Function tag column is not something widely used, so make hidden by default
|
||||
descriptor.addHiddenColumn(
|
||||
@ -91,13 +92,15 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
if (functionMgr != null) {
|
||||
it = new FunctionKeyIterator(functionMgr);
|
||||
}
|
||||
|
||||
monitor.initialize(getKeyCount());
|
||||
int progress = 0;
|
||||
while (it.hasNext()) {
|
||||
monitor.setProgress(progress++);
|
||||
monitor.checkCanceled();
|
||||
long key = it.next();
|
||||
accumulator.add(new FunctionRowObject(key));
|
||||
Function f = functionMgr.getFunction(key);
|
||||
accumulator.add(new FunctionRowObject(f));
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,16 +136,16 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
}
|
||||
}
|
||||
|
||||
void functionAdded(Function function) {
|
||||
addObject(new FunctionRowObject(function.getID()));
|
||||
void functionAdded(Function f) {
|
||||
addObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
void functionRemoved(Function function) {
|
||||
removeObject(new FunctionRowObject(function.getID()));
|
||||
void functionRemoved(Function f) {
|
||||
removeObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
void update(Function function) {
|
||||
updateObject(new FunctionRowObject(function.getID()));
|
||||
void update(Function f) {
|
||||
updateObject(new FunctionRowObject(f));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -152,4 +155,25 @@ class FunctionTableModel extends AddressBasedTableModel<FunctionRowObject> {
|
||||
Function function = functionManager.getFunction(rowObject.getKey());
|
||||
return function != null ? function.getEntryPoint() : null;
|
||||
}
|
||||
|
||||
private class NameTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<FunctionRowObject, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(FunctionRowObject rowObject, Settings settings, Program data,
|
||||
ServiceProvider sp) throws IllegalArgumentException {
|
||||
|
||||
Function function = rowObject.getFunction();
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
return function.getName();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,11 +15,15 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.symboltree;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import ghidra.app.context.ProgramSymbolActionContext;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.SymbolNode;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
public class SymbolTreeActionContext extends ProgramSymbolActionContext {
|
||||
|
||||
@ -28,7 +31,7 @@ public class SymbolTreeActionContext extends ProgramSymbolActionContext {
|
||||
|
||||
SymbolTreeActionContext(SymbolTreeProvider provider, Program program, SymbolGTree tree,
|
||||
TreePath[] selectionPaths) {
|
||||
super(provider, program, getSymbolIDs(selectionPaths), tree);
|
||||
super(provider, program, getSymbols(selectionPaths), tree);
|
||||
this.selectionPaths = selectionPaths;
|
||||
}
|
||||
|
||||
@ -51,23 +54,23 @@ public class SymbolTreeActionContext extends ProgramSymbolActionContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static long[] getSymbolIDs(TreePath[] selectionPaths) {
|
||||
private static List<Symbol> getSymbols(TreePath[] selectionPaths) {
|
||||
if (selectionPaths == null) {
|
||||
return null;
|
||||
}
|
||||
long[] symbolIDs = new long[selectionPaths.length];
|
||||
int index = 0;
|
||||
|
||||
List<Symbol> symbols = new ArrayList<>();
|
||||
for (TreePath treePath : selectionPaths) {
|
||||
Object object = treePath.getLastPathComponent();
|
||||
if (object instanceof SymbolNode) {
|
||||
SymbolNode symbolNode = (SymbolNode) object;
|
||||
symbolIDs[index++] = symbolNode.getSymbolID();
|
||||
symbols.add(symbolNode.getSymbol());
|
||||
}
|
||||
else {
|
||||
// Do not return symbols if selection contains non-symbolNodes
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return symbolIDs;
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
|
@ -85,9 +85,9 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
void symbolRemoved(Symbol symbol) {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.symbolRemoved(symbolID);
|
||||
referenceKeyModel.symbolRemoved(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,5 +157,4 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
||||
public void updateTitle() {
|
||||
setSubTitle(generateSubTitle());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,36 +15,38 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
|
||||
class SymbolEditor extends DefaultCellEditor {
|
||||
|
||||
private JTextField symbolField = null;
|
||||
private JTextField symbolField = null;
|
||||
|
||||
SymbolEditor() {
|
||||
super(new JTextField());
|
||||
symbolField = (JTextField)super.getComponent();
|
||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||
}
|
||||
SymbolEditor() {
|
||||
super(new JTextField());
|
||||
symbolField = (JTextField) super.getComponent();
|
||||
symbolField.setBorder(BorderFactory.createEmptyBorder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return symbolField.getText().trim();
|
||||
}
|
||||
@Override
|
||||
public Object getCellEditorValue() {
|
||||
return symbolField.getText().trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
|
||||
if (value instanceof Symbol) {
|
||||
Symbol symbol = (Symbol) value;
|
||||
symbolField.setText(symbol.getName());
|
||||
}
|
||||
else {
|
||||
symbolField.setText("");
|
||||
}
|
||||
return symbolField;
|
||||
}
|
||||
@Override
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
|
||||
int row, int column) {
|
||||
|
||||
Symbol symbol = (Symbol) value;
|
||||
if (symbol != null) {
|
||||
symbolField.setText(symbol.getName());
|
||||
}
|
||||
else {
|
||||
symbolField.setText("");
|
||||
}
|
||||
return symbolField;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ import ghidra.util.table.*;
|
||||
|
||||
class SymbolPanel extends JPanel {
|
||||
|
||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = false;
|
||||
private static final boolean FILTER_NAME_ONLY_DEFAULT = true;
|
||||
|
||||
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
||||
|
||||
@ -46,15 +46,14 @@ class SymbolPanel extends JPanel {
|
||||
private GhidraTable symTable;
|
||||
private TableModelListener listener;
|
||||
private FilterDialog filterDialog;
|
||||
private PluginTool tool;
|
||||
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
|
||||
private GhidraTableFilterPanel<SymbolRowObject> tableFilterPanel;
|
||||
private GhidraThreadedTablePanel<Symbol> threadedTablePanel;
|
||||
private GhidraTableFilterPanel<Symbol> tableFilterPanel;
|
||||
|
||||
SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer,
|
||||
final PluginTool tool, GoToService gotoService) {
|
||||
|
||||
super(new BorderLayout());
|
||||
this.tool = tool;
|
||||
|
||||
this.symProvider = provider;
|
||||
this.tableModel = model;
|
||||
|
||||
@ -117,9 +116,9 @@ class SymbolPanel extends JPanel {
|
||||
return tableFilterPanel;
|
||||
}
|
||||
|
||||
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) {
|
||||
protected RowFilterTransformer<Symbol> updateRowDataTransformer(boolean nameOnly) {
|
||||
if (nameOnly) {
|
||||
return new NameOnlyRowTransformer(tableModel);
|
||||
return new NameOnlyRowTransformer();
|
||||
}
|
||||
|
||||
return new DefaultRowFilterTransformer<>(tableModel, symTable.getColumnModel());
|
||||
@ -174,21 +173,19 @@ class SymbolPanel extends JPanel {
|
||||
|
||||
if (selectedRowCount == 1) {
|
||||
int selectedRow = symTable.getSelectedRow();
|
||||
Object obj = symTable.getValueAt(selectedRow,
|
||||
symTable.convertColumnIndexToView(SymbolTableModel.LABEL_COL));
|
||||
if (obj instanceof Symbol) {
|
||||
symProvider.setCurrentSymbol((Symbol) obj);
|
||||
return;
|
||||
}
|
||||
Symbol symbol = symProvider.getSymbolForRow(selectedRow);
|
||||
symProvider.setCurrentSymbol(symbol);
|
||||
}
|
||||
else {
|
||||
symProvider.setCurrentSymbol(null);
|
||||
}
|
||||
symProvider.setCurrentSymbol(null);
|
||||
}
|
||||
|
||||
int getActualSymbolCount() {
|
||||
return symTable.getRowCount();
|
||||
}
|
||||
|
||||
List<SymbolRowObject> getSelectedSymbolKeys() {
|
||||
List<Symbol> getSelectedSymbols() {
|
||||
int[] rows = symTable.getSelectedRows();
|
||||
return tableModel.getRowObjects(rows);
|
||||
}
|
||||
@ -201,20 +198,16 @@ class SymbolPanel extends JPanel {
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private static class NameOnlyRowTransformer implements RowFilterTransformer<SymbolRowObject> {
|
||||
private static class NameOnlyRowTransformer implements RowFilterTransformer<Symbol> {
|
||||
private List<String> list = new ArrayList<>();
|
||||
private SymbolTableModel model;
|
||||
|
||||
NameOnlyRowTransformer(SymbolTableModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> transform(SymbolRowObject rowObject) {
|
||||
public List<String> transform(Symbol rowObject) {
|
||||
list.clear();
|
||||
Symbol symbol = model.getSymbolForRowObject(rowObject);
|
||||
if (symbol != null) {
|
||||
list.add(symbol.getName());
|
||||
if (rowObject != null) {
|
||||
// The toString() returns the name for the symbol, which may be cached. Calling
|
||||
// toString() will also avoid locking for cached values.
|
||||
list.add(rowObject.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -74,17 +74,13 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
List<SymbolRowObject> rowObjects = symbolPanel.getSelectedSymbolKeys();
|
||||
long[] symbolIDs = new long[rowObjects.size()];
|
||||
int index = 0;
|
||||
for (SymbolRowObject obj : rowObjects) {
|
||||
symbolIDs[index++] = obj.getKey();
|
||||
}
|
||||
return new ProgramSymbolActionContext(this, program, symbolIDs, getTable());
|
||||
|
||||
List<Symbol> symbols = symbolPanel.getSelectedSymbols();
|
||||
return new ProgramSymbolActionContext(this, program, symbols, getTable());
|
||||
}
|
||||
|
||||
void deleteSymbols() {
|
||||
List<SymbolRowObject> rowObjects = symbolPanel.getSelectedSymbolKeys();
|
||||
List<Symbol> rowObjects = symbolPanel.getSelectedSymbols();
|
||||
symbolKeyModel.delete(rowObjects);
|
||||
}
|
||||
|
||||
@ -93,13 +89,17 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
Symbol getCurrentSymbol() {
|
||||
List<SymbolRowObject> rowObjects = symbolPanel.getSelectedSymbolKeys();
|
||||
List<Symbol> rowObjects = symbolPanel.getSelectedSymbols();
|
||||
if (rowObjects != null && rowObjects.size() >= 1) {
|
||||
return symbolKeyModel.getSymbol(rowObjects.get(0).getKey());
|
||||
return rowObjects.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol getSymbolForRow(int row) {
|
||||
return symbolKeyModel.getRowObject(row);
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
plugin.getReferenceProvider().setCurrentSymbol(symbol);
|
||||
}
|
||||
@ -126,9 +126,9 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (isVisible()) {
|
||||
symbolKeyModel.symbolRemoved(symbolID);
|
||||
symbolKeyModel.symbolRemoved(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,8 +131,8 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
||||
checkRefs(symbol);
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
if (currentSymbol != null && currentSymbol.getID() == symbolID) {
|
||||
void symbolRemoved(Symbol symbol) {
|
||||
if (currentSymbol != null && currentSymbol.getID() == symbol.getID()) {
|
||||
setCurrentSymbol(null);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ class SymbolRenderer extends GhidraTableCellRenderer {
|
||||
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
|
||||
|
||||
super.getTableCellRendererComponent(data);
|
||||
|
||||
|
||||
Object value = data.getValue();
|
||||
int column = data.getColumnModelIndex();
|
||||
boolean isSelected = data.isSelected();
|
||||
|
@ -1,57 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.plugin.core.symtable;
|
||||
|
||||
class SymbolRowObject implements Comparable<SymbolRowObject> {
|
||||
|
||||
private final long key;
|
||||
|
||||
SymbolRowObject(long key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
long getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (int) (key ^ (key >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SymbolRowObject other = (SymbolRowObject) obj;
|
||||
if (key != other.key)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SymbolRowObject o) {
|
||||
return ((Long) key).compareTo(o.key);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -20,21 +19,17 @@ import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class SymbolRowObjectToAddressTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<SymbolRowObject, Address> {
|
||||
public class SymbolRowObjectToAddressTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<Symbol, Address> {
|
||||
|
||||
@Override
|
||||
public Address map(SymbolRowObject rowObject, Program data, ServiceProvider serviceProvider) {
|
||||
SymbolTable symbolTable = data.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(rowObject.getKey());
|
||||
if (symbol == null) {
|
||||
public Address map(Symbol rowObject, Program data, ServiceProvider serviceProvider) {
|
||||
if (rowObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return symbol.getAddress();
|
||||
return rowObject.getAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,23 +18,15 @@ package ghidra.app.plugin.core.symtable;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.ProgramLocationTableRowMapper;
|
||||
|
||||
public class SymbolRowObjectToProgramLocationTableRowMapper extends
|
||||
ProgramLocationTableRowMapper<SymbolRowObject, ProgramLocation> {
|
||||
public class SymbolRowObjectToProgramLocationTableRowMapper
|
||||
extends ProgramLocationTableRowMapper<Symbol, ProgramLocation> {
|
||||
|
||||
@Override
|
||||
public ProgramLocation map(SymbolRowObject rowObject, Program data,
|
||||
ServiceProvider serviceProvider) {
|
||||
SymbolTable symbolTable = data.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(rowObject.getKey());
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return symbol.getProgramLocation();
|
||||
public ProgramLocation map(Symbol rowObject, Program data, ServiceProvider serviceProvider) {
|
||||
return rowObject.getProgramLocation();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.app.cmd.function.DeleteFunctionCmd;
|
||||
@ -35,30 +34,26 @@ import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.LongArrayList;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.table.AddressBasedTableModel;
|
||||
import ghidra.util.table.column.*;
|
||||
import ghidra.util.table.field.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
|
||||
private static final Comparator<Symbol> NAME_COL_COMPARATOR = (s1, s2) -> {
|
||||
return s1.toString().compareToIgnoreCase(s2.toString());
|
||||
};
|
||||
|
||||
static final int LABEL_COL = 0;
|
||||
static final int LOCATION_COL = 1;
|
||||
static final int TYPE_COL = 2;
|
||||
static final int DATATYPE_COL = 3;
|
||||
static final int DATA_TYPE_COL = 3;
|
||||
static final int NAMESPACE_COL = 4;
|
||||
static final int SOURCE_COL = 5;
|
||||
static final int REFS_COL = 6;
|
||||
|
||||
static final String LABEL_COL_NAME = "Labels";
|
||||
static final String LOCATION_COL_NAME = "Location";
|
||||
static final String TYPE_COL_NAME = "Type";
|
||||
static final String DATATYPE_COL_NAME = "Datatype";
|
||||
static final String REFS_COL_NAME = "# Refs";
|
||||
static final String NAMESPACE_COL_NAME = "Namespace";
|
||||
static final String SOURCE_COL_NAME = "Source";
|
||||
|
||||
private SymbolProvider provider;
|
||||
private PluginTool tool;
|
||||
private SymbolTable symbolTable;
|
||||
@ -74,14 +69,13 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<SymbolRowObject> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<SymbolRowObject> descriptor = new TableColumnDescriptor<>();
|
||||
protected TableColumnDescriptor<Symbol> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Symbol> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(new NameTableColumn(), 1, true);
|
||||
descriptor.addVisibleColumn(new LocationTableColumn());
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new SymbolTypeTableColumn()));
|
||||
descriptor.addVisibleColumn(new DataTypeTableColumn());
|
||||
descriptor.addVisibleColumn(new NameTableColumn());
|
||||
descriptor.addVisibleColumn(new LocationTableColumn(), 1, true);
|
||||
descriptor.addVisibleColumn(new SymbolTypeTableColumn());
|
||||
descriptor.addHiddenColumn(new DataTypeTableColumn());
|
||||
descriptor.addVisibleColumn(new NamespaceTableColumn());
|
||||
descriptor.addVisibleColumn(new SourceTableColumn());
|
||||
descriptor.addVisibleColumn(new ReferenceCountTableColumn());
|
||||
@ -143,7 +137,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoad(Accumulator<SymbolRowObject> accumulator, TaskMonitor monitor)
|
||||
protected void doLoad(Accumulator<Symbol> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (symbolTable == null) {
|
||||
return;
|
||||
@ -163,7 +157,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
monitor.checkCanceled();
|
||||
Symbol s = it.next();
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
accumulator.add(new SymbolRowObject(s.getID()));
|
||||
accumulator.add(s);
|
||||
}
|
||||
}
|
||||
if (filter.acceptsDefaultLabelSymbols()) {
|
||||
@ -175,7 +169,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
Address a = addrIt.next();
|
||||
Symbol s = symbolTable.getPrimarySymbol(a);
|
||||
if (s.isDynamic() && filter.accepts(s, getProgram())) {
|
||||
accumulator.add(new SymbolRowObject(s.getID()));
|
||||
accumulator.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,32 +194,22 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolRowObject rowObject = filteredData.get(row);
|
||||
Symbol symbol = symbolTable.getSymbol(rowObject.getKey());
|
||||
Symbol symbol = filteredData.get(row);
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (columnIndex) {
|
||||
case LABEL_COL:
|
||||
try {
|
||||
String newName = aValue.toString();
|
||||
if (!symbol.getName().equals(newName)) {
|
||||
Command renameCmd =
|
||||
new RenameLabelCmd(symbol.getAddress(), symbol.getName(), newName,
|
||||
symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
||||
if (columnIndex == LABEL_COL) {
|
||||
String newName = aValue.toString();
|
||||
if (!symbol.getName().equals(newName)) {
|
||||
Command renameCmd = new RenameLabelCmd(symbol.getAddress(), symbol.getName(),
|
||||
newName, symbol.getParentNamespace(), SourceType.USER_DEFINED);
|
||||
|
||||
if (!tool.execute(renameCmd, getProgram())) {
|
||||
Msg.showError(getClass(), provider.getComponent(),
|
||||
"Error Renaming Symbol", renameCmd.getStatusMsg());
|
||||
}
|
||||
}
|
||||
if (!tool.execute(renameCmd, getProgram())) {
|
||||
Msg.showError(getClass(), provider.getComponent(), "Error Renaming Symbol",
|
||||
renameCmd.getStatusMsg());
|
||||
}
|
||||
catch (ConcurrentModificationException exc) {
|
||||
Msg.showError(getClass(), provider.getComponent(), "Invalid Symbol",
|
||||
"Symbol no longer valid.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,40 +246,37 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
|
||||
void symbolAdded(Symbol s) {
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
addObject(new SymbolRowObject(s.getID()));
|
||||
addObject(s);
|
||||
lastSymbol = s;
|
||||
}
|
||||
}
|
||||
|
||||
void symbolRemoved(long symbolID) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == symbolID) {
|
||||
void symbolRemoved(Symbol s) {
|
||||
if (lastSymbol != null && lastSymbol.getID() == s.getID()) {
|
||||
lastSymbol = null;
|
||||
}
|
||||
removeObject(new SymbolRowObject(symbolID));
|
||||
removeObject(s);
|
||||
}
|
||||
|
||||
void symbolChanged(Symbol s) {
|
||||
SymbolRowObject symbolRowObject = new SymbolRowObject(s.getID());
|
||||
Symbol Symbol = s;
|
||||
if (filter.accepts(s, getProgram())) {
|
||||
updateObject(symbolRowObject);
|
||||
updateObject(Symbol);
|
||||
}
|
||||
else {
|
||||
removeObject(symbolRowObject);
|
||||
removeObject(Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void delete(List<SymbolRowObject> rowObjects) {
|
||||
void delete(List<Symbol> rowObjects) {
|
||||
if (rowObjects == null || rowObjects.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
tool.setStatusInfo("");
|
||||
LongArrayList deleteList = new LongArrayList();
|
||||
List<Symbol> deleteList = new LinkedList<>();
|
||||
CompoundCmd cmd = new CompoundCmd("Delete symbol(s)");
|
||||
for (int i = 0; i < rowObjects.size(); i++) {
|
||||
Symbol symbol = symbolTable.getSymbol(rowObjects.get(i).getKey());
|
||||
if (symbol == null) {
|
||||
continue;
|
||||
}
|
||||
for (Symbol symbol : rowObjects) {
|
||||
if (symbol.isDynamic()) {
|
||||
Symbol[] symbols = symbolTable.getSymbols(symbol.getAddress());
|
||||
if (symbols.length == 1) {
|
||||
@ -303,7 +284,8 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
continue;//can't delete dynamic symbols...
|
||||
}
|
||||
}
|
||||
deleteList.add(rowObjects.get(i).getKey());
|
||||
|
||||
deleteList.add(symbol);
|
||||
String label = symbol.getName();
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
Function function = (Function) symbol.getObject();
|
||||
@ -323,9 +305,10 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
if (cmd.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tool.execute(cmd, getProgram())) {
|
||||
for (int k = 0; k < deleteList.size(); k++) {
|
||||
removeObject(new SymbolRowObject(deleteList.get(k)));
|
||||
for (Symbol s : deleteList) {
|
||||
removeObject(s);
|
||||
}
|
||||
updateNow();
|
||||
}
|
||||
@ -339,56 +322,16 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getSortedColumnClass(int columnIndex) {
|
||||
if (columnIndex == LOCATION_COL) {
|
||||
return Address.class;
|
||||
}
|
||||
return super.getSortedColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
public static int getPreferredWidth(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case LABEL_COL:
|
||||
return 140;
|
||||
case LOCATION_COL:
|
||||
return 40;
|
||||
case DATATYPE_COL:
|
||||
case TYPE_COL:
|
||||
case SOURCE_COL:
|
||||
return 30;
|
||||
case NAMESPACE_COL:
|
||||
return 80;
|
||||
case REFS_COL:
|
||||
return 20;
|
||||
}
|
||||
return 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getAddress(int row) {
|
||||
Symbol symbol = symbolTable.getSymbol(getRowObject(row).getKey());
|
||||
Symbol symbol = getRowObject(row);
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getAddress();
|
||||
}
|
||||
|
||||
Symbol getSymbolForRowObject(SymbolRowObject storageObject) {
|
||||
if (symbolTable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long key = storageObject.getKey();
|
||||
Symbol localSymbol = lastSymbol;
|
||||
if (localSymbol == null || localSymbol.getID() != key) {
|
||||
localSymbol = lastSymbol = symbolTable.getSymbol(key);
|
||||
}
|
||||
return localSymbol;
|
||||
}
|
||||
|
||||
AddressBasedLocation getSymbolLocation(SymbolRowObject rowObject) {
|
||||
Symbol s = getSymbolForRowObject(rowObject);
|
||||
private AddressBasedLocation getSymbolLocation(Symbol s) {
|
||||
if (s == null) {
|
||||
return new AddressBasedLocation();
|
||||
}
|
||||
@ -396,17 +339,36 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
if (type == SymbolType.PARAMETER || type == SymbolType.LOCAL_VAR) {
|
||||
// Must use special location object for variables which renders variable storage
|
||||
// location since this can't be obtained from just a variable storage address
|
||||
return new VariableSymbolLocation((Variable) s.getObject());
|
||||
Variable object = (Variable) s.getObject();
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
return new VariableSymbolLocation(object);
|
||||
}
|
||||
return new AddressBasedLocation(program, s.getAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comparator<Symbol> createSortComparator(int columnIndex) {
|
||||
DynamicTableColumn<Symbol, ?, ?> column = getColumn(columnIndex);
|
||||
if (column instanceof NameTableColumn) {
|
||||
// note: we use our own name comparator to increase sorting speed for the name
|
||||
// column. This works because this comparator is called for each *row object*
|
||||
// allowing the comparator to compare the Symbols based on name instead of
|
||||
// having to use the table model's code for getting a column value for the
|
||||
// row object. The code for retrieving a column value is slower than just
|
||||
// working with the row object directly. See
|
||||
// ThreadedTableModel.getCachedColumnValueForRow for more info.
|
||||
return NAME_COL_COMPARATOR;
|
||||
}
|
||||
return super.createSortComparator(columnIndex);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Table Column Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class NameTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Symbol> {
|
||||
private class NameTableColumn extends AbstractProgramBasedDynamicTableColumn<Symbol, Symbol> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -414,14 +376,18 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public Symbol getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
return getSymbolForRowObject(rowObject);
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
private class PinnedTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Boolean> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Boolean> {
|
||||
|
||||
private PinnedRenderer renderer = new PinnedRenderer();
|
||||
|
||||
@ -431,10 +397,10 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public Boolean getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.isPinned();
|
||||
@ -452,7 +418,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
private class LocationTableColumn
|
||||
extends AbstractProgramLocationTableColumn<SymbolRowObject, AddressBasedLocation> {
|
||||
extends AbstractProgramLocationTableColumn<Symbol, AddressBasedLocation> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -460,22 +426,44 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressBasedLocation getValue(SymbolRowObject rowObject, Settings settings,
|
||||
Program p, ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
return getSymbolLocation(rowObject);
|
||||
public AddressBasedLocation getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
return getSymbolLocation(symbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(SymbolRowObject rowObject, Settings settings,
|
||||
Program p, ServiceProvider svcProvider) {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
public ProgramLocation getProgramLocation(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getProgramLocation();
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolTypeTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Type";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: this call is slow. If we decide that filtering/sorting on this value is
|
||||
// important, then this should be cached
|
||||
return SymbolUtilities.getSymbolTypeDisplayName(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class VariableSymbolLocation extends AddressBasedLocation {
|
||||
|
||||
VariableSymbolLocation(Variable variable) {
|
||||
@ -484,7 +472,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
private class DataTypeTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -492,11 +480,10 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -522,7 +509,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
private class NamespaceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -530,19 +517,18 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return symbol.getParentNamespace().getName(true);
|
||||
}
|
||||
}
|
||||
|
||||
private class SourceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, SourceType> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, SourceType> {
|
||||
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<SourceType>() {
|
||||
@Override
|
||||
@ -570,9 +556,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceType getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public SourceType getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
|
||||
if (symbol == null) {
|
||||
return null;
|
||||
}
|
||||
@ -582,7 +568,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
private class ReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Integer> {
|
||||
|
||||
private ReferenceCountRenderer renderer = new ReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -590,19 +578,31 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Integer.valueOf(symbol.getReferenceCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class ReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class OffcutReferenceCountTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, Integer> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, Integer> {
|
||||
|
||||
private OffcutReferenceCountRenderer renderer = new OffcutReferenceCountRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -610,11 +610,9 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -637,10 +635,21 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
return Integer.valueOf(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class OffcutReferenceCountRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
||||
private class UserTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||
private class UserTableColumn extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -653,11 +662,10 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null) {
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -678,7 +686,7 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
private class OriginalNameColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<SymbolRowObject, String> {
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, String> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
@ -691,13 +699,17 @@ class SymbolTableModel extends AddressBasedTableModel<SymbolRowObject> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(SymbolRowObject rowObject, Settings settings, Program p,
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
Symbol symbol = getSymbolForRowObject(rowObject);
|
||||
if (symbol == null || !symbol.isExternal()) {
|
||||
if (!symbol.checkIsValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!symbol.isExternal()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SymbolType symbolType = symbol.getSymbolType();
|
||||
if (symbolType != SymbolType.FUNCTION && symbolType != SymbolType.LABEL) {
|
||||
return null;
|
||||
|
@ -37,8 +37,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.ProgramChangeRecord;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
@ -190,7 +189,10 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
if (!symProvider.isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED)) {
|
||||
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();
|
||||
return;
|
||||
@ -207,11 +209,12 @@ 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 = currentProgram.getSymbolTable().getPrimarySymbol(rec.getStart());
|
||||
symbol = symbolTable.getPrimarySymbol(rec.getStart());
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
@ -221,9 +224,9 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_ADDED:
|
||||
Address addAddr = rec.getStart();
|
||||
Symbol primaryAtAdd = currentProgram.getSymbolTable().getPrimarySymbol(addAddr);
|
||||
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(addAddr);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd.getID());
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolAdded(symbol);
|
||||
@ -233,10 +236,11 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
case ChangeManager.DOCR_SYMBOL_REMOVED:
|
||||
Address removeAddr = rec.getStart();
|
||||
Long symbolID = (Long) rec.getNewValue();
|
||||
symProvider.symbolRemoved(symbolID.longValue());
|
||||
refProvider.symbolRemoved(symbolID.longValue());
|
||||
Symbol primaryAtRemove =
|
||||
currentProgram.getSymbolTable().getPrimarySymbol(removeAddr);
|
||||
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);
|
||||
}
|
||||
@ -271,7 +275,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
break;
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_ADDED:
|
||||
Reference ref = (Reference) rec.getObject();
|
||||
symbol = currentProgram.getSymbolTable().getSymbol(ref);
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol != null) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
@ -281,11 +285,12 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
ref = (Reference) rec.getObject();
|
||||
Address toAddr = ref.getToAddress();
|
||||
if (toAddr.isMemoryAddress()) {
|
||||
symbol = currentProgram.getSymbolTable().getSymbol(ref);
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol == null) {
|
||||
long id = currentProgram.getSymbolTable().getDynamicSymbolID(
|
||||
ref.getToAddress());
|
||||
symProvider.symbolRemoved(id);
|
||||
|
||||
long id = symbolTable.getDynamicSymbolID(ref.getToAddress());
|
||||
removedSymbol = symbolTable.createSymbolPlaceholder(toAddr, id);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
}
|
||||
else {
|
||||
refProvider.symbolChanged(symbol);
|
||||
@ -295,19 +300,12 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_ADDED:
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_REMOVED:
|
||||
Symbol[] symbols = currentProgram.getSymbolTable().getSymbols(rec.getStart());
|
||||
Symbol[] symbols = symbolTable.getSymbols(rec.getStart());
|
||||
for (Symbol element : symbols) {
|
||||
symProvider.symbolChanged(element);
|
||||
refProvider.symbolChanged(element);
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_MEMORY_BLOCK_ADDED:
|
||||
case ChangeManager.DOCR_MEMORY_BLOCK_REMOVED:
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.exception.RollbackException;
|
||||
import junit.framework.AssertionFailedError;
|
||||
import utility.application.ApplicationLayout;
|
||||
import utility.function.*;
|
||||
import utility.function.ExceptionalCallback;
|
||||
import utility.function.ExceptionalFunction;
|
||||
|
||||
public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDockingTest {
|
||||
|
||||
@ -107,7 +108,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
* if found. If no language is found, an exception will be thrown.
|
||||
* @param oldLanguageName old language name string
|
||||
* @return the language compiler and spec
|
||||
* @throws LanguageNotFoundException
|
||||
* @throws LanguageNotFoundException if the language is not found
|
||||
*/
|
||||
public static LanguageCompilerSpecPair getLanguageCompilerSpecPair(String oldLanguageName)
|
||||
throws LanguageNotFoundException {
|
||||
@ -194,7 +195,14 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends Exception> void tx(Program p, ExceptionalCallback<E> c) throws E {
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic.
|
||||
*
|
||||
* @param p the program
|
||||
* @param c the code to execute
|
||||
*/
|
||||
public static <E extends Exception> void tx(Program p, ExceptionalCallback<E> c) {
|
||||
int txId = p.startTransaction("Test - Function in Transaction");
|
||||
boolean commit = true;
|
||||
try {
|
||||
@ -202,9 +210,9 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
p.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
catch (RollbackException e) {
|
||||
catch (Exception e) {
|
||||
commit = false;
|
||||
throw e;
|
||||
failWithException("Exception modifying program '" + p.getName() + "'", e);
|
||||
}
|
||||
finally {
|
||||
p.endTransaction(txId, commit);
|
||||
@ -213,27 +221,14 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic
|
||||
* logic. This method is calls {@link #tx(Program, ExceptionalCallback)}, but helps with
|
||||
* semantics.
|
||||
*
|
||||
* @param program the program
|
||||
* @param callback the code to execute
|
||||
* @param p the program
|
||||
* @param c the code to execute
|
||||
*/
|
||||
public <E extends Exception> void modifyProgram(Program program,
|
||||
ExceptionalConsumer<Program, E> callback) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
boolean commit = false;
|
||||
int tx = program.startTransaction("Test");
|
||||
try {
|
||||
callback.accept(program);
|
||||
commit = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
failWithException("Exception modifying program '" + program.getName() + "'", e);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(tx, commit);
|
||||
}
|
||||
public static <E extends Exception> void modifyProgram(Program p, ExceptionalCallback<E> c) {
|
||||
tx(p, c);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +239,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
* @param f the function for modifying the program and creating the desired result
|
||||
* @return the result
|
||||
*/
|
||||
public <R, E extends Exception> R createInProgram(Program program,
|
||||
public <R, E extends Exception> R modifyProgram(Program program,
|
||||
ExceptionalFunction<Program, R, E> f) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
|
@ -15,14 +15,19 @@
|
||||
*/
|
||||
package ghidra.util.table.field;
|
||||
|
||||
import docking.widgets.table.GTableCellRenderer;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.table.column.AbstractWrapperTypeColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
|
||||
public class FunctionBodySizeTableColumn
|
||||
extends ProgramBasedDynamicTableColumnExtensionPoint<Function, Integer> {
|
||||
|
||||
private FunctionBodySizeRenderer renderer = new FunctionBodySizeRenderer();
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return "Function Size";
|
||||
@ -34,4 +39,15 @@ public class FunctionBodySizeTableColumn
|
||||
return (int) rowObject.getBody().getNumAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GColumnRenderer<Integer> getColumnRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// this renderer disables the default text filtering; this column is only filterable
|
||||
// via the column constraint filtering
|
||||
private class FunctionBodySizeRenderer extends GTableCellRenderer
|
||||
implements AbstractWrapperTypeColumnRenderer<Integer> {
|
||||
// body is handled by parents
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ import javax.swing.event.TableModelEvent;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
import docking.widgets.table.sort.DefaultColumnComparator;
|
||||
import docking.widgets.table.sort.RowToColumnComparator;
|
||||
import docking.widgets.table.sort.RowBasedColumnComparator;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
@ -92,8 +92,8 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the given row object in this model; -1 if the model does not contain
|
||||
* the given object.
|
||||
* Returns the index of the given row object in this model; a negative value if the model
|
||||
* does not contain the given object.
|
||||
*
|
||||
* <p>Warning: if the this model has no sort applied, then performance will be O(n). If
|
||||
* sorted, then performance is O(log n). You can call {@link #isSorted()} to know when
|
||||
@ -132,6 +132,9 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
|
||||
@Override
|
||||
public int getPrimarySortColumnIndex() {
|
||||
if (sortState.isUnsorted()) {
|
||||
return -1;
|
||||
}
|
||||
return sortState.iterator().next().getColumnModelIndex();
|
||||
}
|
||||
|
||||
@ -327,8 +330,8 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
* @return the comparator
|
||||
*/
|
||||
protected Comparator<T> createSortComparator(int columnIndex) {
|
||||
return new RowToColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||
new StringBasedBackupRowToColumnComparator(columnIndex));
|
||||
return new RowBasedColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||
new StringBasedBackupRowToColumnComparator());
|
||||
}
|
||||
|
||||
private Comparator<T> createLastResortComparator(ComparatorLink parentChain) {
|
||||
@ -467,22 +470,16 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
}
|
||||
}
|
||||
|
||||
private class StringBasedBackupRowToColumnComparator implements Comparator<T> {
|
||||
|
||||
private int sortColumn;
|
||||
|
||||
StringBasedBackupRowToColumnComparator(int sortColumn) {
|
||||
this.sortColumn = sortColumn;
|
||||
}
|
||||
private class StringBasedBackupRowToColumnComparator implements Comparator<Object> {
|
||||
|
||||
@Override
|
||||
public int compare(T t1, T t2) {
|
||||
if (t1 == t2) {
|
||||
public int compare(Object c1, Object c2) {
|
||||
if (c1 == c2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String s1 = getColumStringValue(t1);
|
||||
String s2 = getColumStringValue(t2);
|
||||
String s1 = getColumStringValue(c1);
|
||||
String s2 = getColumStringValue(c2);
|
||||
|
||||
if (s1 == null || s2 == null) {
|
||||
return TableComparators.compareWithNullValues(s1, s2);
|
||||
@ -491,11 +488,10 @@ public abstract class AbstractSortedTableModel<T> extends AbstractGTableModel<T>
|
||||
return s1.compareToIgnoreCase(s2);
|
||||
}
|
||||
|
||||
private String getColumStringValue(T t) {
|
||||
private String getColumStringValue(Object columnValue) {
|
||||
// just use the toString(), which may or may not produce a good value (this will
|
||||
// catch the cases where the column value is itself a string)
|
||||
Object o = getColumnValueForRow(t, sortColumn);
|
||||
return o == null ? null : o.toString();
|
||||
return columnValue == null ? null : columnValue.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -34,6 +33,7 @@ public class AddRemoveListItem<T> {
|
||||
public boolean isRemove() {
|
||||
return isRemove;
|
||||
}
|
||||
|
||||
public boolean isChange() {
|
||||
return isAdd && isRemove;
|
||||
}
|
||||
@ -41,4 +41,16 @@ public class AddRemoveListItem<T> {
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
//@formatter:off
|
||||
return "{\n" +
|
||||
"\tvalue: " + value +",\n" +
|
||||
"\tisAdd: " + isAdd +",\n" +
|
||||
"\tisRemove: " + isRemove +"\n" +
|
||||
"}";
|
||||
//@formatter:on
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,13 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||
}
|
||||
}
|
||||
|
||||
if (columnUsesConstraintFilteringOnly(column)) {
|
||||
// This allows columns to be ignored for default text filtering while still being
|
||||
// filterable through the column constraints API
|
||||
return null;
|
||||
}
|
||||
|
||||
// note: this call can be slow when columns dynamically calculate values from the database
|
||||
Object value = model.getColumnValueForRow(rowObject, column);
|
||||
if (value == null) {
|
||||
return null;
|
||||
@ -96,7 +103,22 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean columnUsesConstraintFilteringOnly(int column) {
|
||||
if (!(model instanceof DynamicColumnTableModel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DynamicColumnTableModel<ROW_OBJECT> columnBasedModel =
|
||||
(DynamicColumnTableModel<ROW_OBJECT>) model;
|
||||
GColumnRenderer<Object> renderer = getColumnRenderer(columnBasedModel, column);
|
||||
if (renderer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||
return mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY;
|
||||
}
|
||||
|
||||
private String getRenderedColumnValue(Object columnValue, int columnIndex) {
|
||||
|
||||
if (!(model instanceof DynamicColumnTableModel)) {
|
||||
@ -110,11 +132,6 @@ public class DefaultRowFilterTransformer<ROW_OBJECT> implements RowFilterTransfo
|
||||
return null;
|
||||
}
|
||||
|
||||
ColumnConstraintFilterMode mode = renderer.getColumnConstraintFilterMode();
|
||||
if (mode == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
||||
return null; // this renderer does not support text
|
||||
}
|
||||
|
||||
Settings settings = columnBasedModel.getColumnSettings(columnIndex);
|
||||
String s = renderer.getFilterString(columnValue, settings);
|
||||
return s;
|
||||
|
@ -178,11 +178,11 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
||||
Comparator<Object> columnComparator = createSortComparatorForColumn(columnIndex);
|
||||
if (columnComparator != null) {
|
||||
// the given column has its own comparator; wrap and us that
|
||||
return new RowToColumnComparator<>(this, columnIndex, columnComparator);
|
||||
return new RowBasedColumnComparator<>(this, columnIndex, columnComparator);
|
||||
}
|
||||
|
||||
return new RowToColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||
new ColumnRenderedValueBackupRowComparator<>(this, columnIndex));
|
||||
return new RowBasedColumnComparator<>(this, columnIndex, new DefaultColumnComparator(),
|
||||
new ColumnRenderedValueBackupComparator<>(this, columnIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -441,7 +441,6 @@ public abstract class GDynamicColumnTableModel<ROW_TYPE, DATA_SOURCE>
|
||||
}
|
||||
|
||||
return column.getValue(t, columnSettings.get(column), dataSource, serviceProvider);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@ import ghidra.util.datastruct.WeakSet;
|
||||
public class GTableColumnModel
|
||||
implements TableColumnModel, PropertyChangeListener, ListSelectionListener {
|
||||
|
||||
private List<TableColumn> visibleList = new ArrayList<>();
|
||||
private VisibleColumns visibleColumns = new VisibleColumns();
|
||||
private List<TableColumn> completeList = new ArrayList<>();
|
||||
private int totalColumnWidth;
|
||||
private int columnMargin;
|
||||
@ -67,18 +67,8 @@ public class GTableColumnModel
|
||||
|
||||
void removeAllColumns() {
|
||||
|
||||
TableColumn[] asArray = visibleList.toArray(new TableColumn[visibleList.size()]);
|
||||
for (int i = 0; i < asArray.length; i++) {
|
||||
TableColumn column = asArray[i];
|
||||
visibleList.remove(column);
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, i, i));
|
||||
}
|
||||
|
||||
/*
|
||||
TODO replace the above snippet with this code, after the upcoming release
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleList.size() - 1));
|
||||
visibleList.clear();
|
||||
*/
|
||||
fireColumnRemoved(new TableColumnModelEvent(this, 0, visibleColumns.size() - 1));
|
||||
visibleColumns.clear();
|
||||
|
||||
// no need to fire the removed event for items in the complete list, as the clients
|
||||
// only know about the visible columns
|
||||
@ -90,7 +80,7 @@ public class GTableColumnModel
|
||||
|
||||
void dispose() {
|
||||
listeners.clear();
|
||||
visibleList.clear();
|
||||
visibleColumns.clear();
|
||||
completeList.clear();
|
||||
columnModelState.dispose();
|
||||
}
|
||||
@ -101,7 +91,7 @@ public class GTableColumnModel
|
||||
* @return true if the given column is visible.
|
||||
*/
|
||||
public boolean isVisible(TableColumn column) {
|
||||
return visibleList.contains(column);
|
||||
return visibleColumns.contains(column);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +109,7 @@ public class GTableColumnModel
|
||||
}
|
||||
|
||||
public void setVisible(TableColumn column, boolean visible) {
|
||||
boolean isVisible = visibleList.contains(column);
|
||||
boolean isVisible = visibleColumns.contains(column);
|
||||
|
||||
if (visible == isVisible) {
|
||||
return;
|
||||
@ -127,12 +117,12 @@ public class GTableColumnModel
|
||||
|
||||
if (visible) {
|
||||
int insertIndex = findVisibleInsertionIndex(column);
|
||||
visibleList.add(insertIndex, column);
|
||||
visibleColumns.add(insertIndex, column);
|
||||
fireColumnAdded(new TableColumnModelEvent(this, insertIndex, insertIndex));
|
||||
}
|
||||
else {
|
||||
int columnIndex = visibleList.indexOf(column);
|
||||
visibleList.remove(columnIndex);
|
||||
int columnIndex = visibleColumns.indexOf(column);
|
||||
visibleColumns.remove(columnIndex);
|
||||
// Adjust for the selection
|
||||
if (selectionModel != null) {
|
||||
selectionModel.removeIndexInterval(columnIndex, columnIndex);
|
||||
@ -146,12 +136,12 @@ public class GTableColumnModel
|
||||
}
|
||||
|
||||
private int findVisibleInsertionIndex(TableColumn column) {
|
||||
int completeIndex = completeList.indexOf(column);
|
||||
int completeIndex = visibleColumns.indexOf(column);
|
||||
|
||||
int size = visibleList.size();
|
||||
int size = visibleColumns.size();
|
||||
for (int i = completeIndex + 1; i < size; i++) {
|
||||
TableColumn nextColumn = completeList.get(i);
|
||||
int visibleIndex = visibleList.indexOf(nextColumn);
|
||||
int visibleIndex = visibleColumns.indexOf(nextColumn);
|
||||
if (visibleIndex != -1) {
|
||||
return visibleIndex;
|
||||
}
|
||||
@ -169,7 +159,7 @@ public class GTableColumnModel
|
||||
removeColumnWithModelIndex(aColumn.getModelIndex()); // dedup
|
||||
|
||||
completeList.add(aColumn);
|
||||
visibleList.add(aColumn);
|
||||
visibleColumns.add(aColumn);
|
||||
|
||||
aColumn.addPropertyChangeListener(this);
|
||||
|
||||
@ -205,7 +195,7 @@ public class GTableColumnModel
|
||||
}
|
||||
|
||||
completeList.remove(tableColumn);
|
||||
visibleList.remove(tableColumn);
|
||||
visibleColumns.remove(tableColumn);
|
||||
tableColumn.removePropertyChangeListener(this);
|
||||
}
|
||||
|
||||
@ -216,15 +206,15 @@ public class GTableColumnModel
|
||||
|
||||
@Override
|
||||
public TableColumn getColumn(int columnIndex) {
|
||||
if ((columnIndex < 0) || (columnIndex >= visibleList.size())) {
|
||||
if ((columnIndex < 0) || (columnIndex >= visibleColumns.size())) {
|
||||
return null;
|
||||
}
|
||||
return visibleList.get(columnIndex);
|
||||
return visibleColumns.get(columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return visibleList.size();
|
||||
return visibleColumns.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -232,8 +222,8 @@ public class GTableColumnModel
|
||||
if (columnIdentifier == null) {
|
||||
throw new IllegalArgumentException("Identifier is null");
|
||||
}
|
||||
for (int i = 0; i < visibleList.size(); i++) {
|
||||
TableColumn tableColumn = visibleList.get(i);
|
||||
for (int i = 0; i < visibleColumns.size(); i++) {
|
||||
TableColumn tableColumn = visibleColumns.get(i);
|
||||
if (columnIdentifier.equals(tableColumn.getIdentifier())) {
|
||||
return i;
|
||||
}
|
||||
@ -271,7 +261,7 @@ public class GTableColumnModel
|
||||
|
||||
@Override
|
||||
public Enumeration<TableColumn> getColumns() {
|
||||
return Collections.enumeration(visibleList);
|
||||
return visibleColumns.toEnumeration();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -355,8 +345,8 @@ public class GTableColumnModel
|
||||
}
|
||||
|
||||
// update the visible list
|
||||
TableColumn movedColumn = visibleList.remove(columnIndex);
|
||||
visibleList.add(newIndex, movedColumn);
|
||||
TableColumn movedColumn = visibleColumns.remove(columnIndex);
|
||||
visibleColumns.add(newIndex, movedColumn);
|
||||
|
||||
// update the complete list
|
||||
completeList.remove(movedColumn);
|
||||
@ -364,7 +354,7 @@ public class GTableColumnModel
|
||||
|
||||
// get the item at the index after the new index (since we are moving up, we know
|
||||
// that there are columns below the new index)
|
||||
TableColumn column = visibleList.get(newIndex + 1);
|
||||
TableColumn column = visibleColumns.get(newIndex + 1);
|
||||
|
||||
// find this column in the complete list and then place the moved column before that
|
||||
// position in the complete list
|
||||
@ -375,7 +365,7 @@ public class GTableColumnModel
|
||||
|
||||
// get the item at the index before the new index (since we are moving down, we know
|
||||
// that there are columns above the new index)
|
||||
TableColumn column = visibleList.get(newIndex - 1);
|
||||
TableColumn column = visibleColumns.get(newIndex - 1);
|
||||
|
||||
// find this column in the complete list and then place the moved column after that
|
||||
// position in the complete list
|
||||
@ -402,9 +392,9 @@ public class GTableColumnModel
|
||||
public void removeColumn(TableColumn column) {
|
||||
completeList.remove(column);
|
||||
|
||||
int index = visibleList.indexOf(column);
|
||||
int index = visibleColumns.indexOf(column);
|
||||
if (index >= 0) {
|
||||
visibleList.remove(index);
|
||||
visibleColumns.remove(index);
|
||||
// Adjust for the selection
|
||||
if (selectionModel != null) {
|
||||
selectionModel.removeIndexInterval(index, index);
|
||||
@ -459,7 +449,7 @@ public class GTableColumnModel
|
||||
*/
|
||||
private void recalcWidthCache() {
|
||||
totalColumnWidth = 0;
|
||||
for (TableColumn tableColumn : visibleList) {
|
||||
for (TableColumn tableColumn : visibleColumns.getColumns()) {
|
||||
totalColumnWidth += tableColumn.getWidth();
|
||||
}
|
||||
}
|
||||
@ -471,7 +461,7 @@ public class GTableColumnModel
|
||||
void restoreState(List<TableColumn> newCompleteList, List<Settings> newSettingsList,
|
||||
List<TableColumn> newVisibleList) {
|
||||
this.completeList = newCompleteList;
|
||||
this.visibleList = newVisibleList;
|
||||
this.visibleColumns = new VisibleColumns(newVisibleList);
|
||||
|
||||
TableModel model = table.getModel();
|
||||
if (model instanceof ConfigurableColumnTableModel) {
|
||||
@ -484,10 +474,6 @@ public class GTableColumnModel
|
||||
configurableModel.setAllColumnSettings(columnIndexAndSettings);
|
||||
}
|
||||
|
||||
// TODO: at some point in the future (like a year or more) we can remove this, when
|
||||
// we know the new code below it works
|
||||
// fireColumnMarginChanged(); // let the system know to rebuild the GUI (Java HACK!)
|
||||
|
||||
// signal a change; we've added/removed columns, but we don't need to be specific
|
||||
TableColumnModelEvent e = new TableColumnModelEvent(this, 0, getColumnCount() - 1);
|
||||
fireColumnAdded(e);
|
||||
@ -515,6 +501,74 @@ public class GTableColumnModel
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A small class to provide a method to quickly see if a column is visible by calling contains
|
||||
* on a hash set
|
||||
*/
|
||||
private class VisibleColumns {
|
||||
private Set<TableColumn> visibleSet = new HashSet<>();
|
||||
private List<TableColumn> visibleList = new ArrayList<>();
|
||||
|
||||
public VisibleColumns() {
|
||||
}
|
||||
|
||||
public VisibleColumns(List<TableColumn> newVisibleList) {
|
||||
this.visibleList = newVisibleList;
|
||||
visibleSet.addAll(visibleList);
|
||||
}
|
||||
|
||||
List<TableColumn> getColumns() {
|
||||
return visibleList;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return visibleList.size();
|
||||
}
|
||||
|
||||
public void remove(TableColumn column) {
|
||||
visibleList.remove(column);
|
||||
visibleSet.remove(column);
|
||||
}
|
||||
|
||||
public void add(TableColumn column) {
|
||||
visibleList.add(column);
|
||||
visibleSet.add(column);
|
||||
}
|
||||
|
||||
public Enumeration<TableColumn> toEnumeration() {
|
||||
return Collections.enumeration(visibleList);
|
||||
}
|
||||
|
||||
public TableColumn get(int index) {
|
||||
return visibleList.get(index);
|
||||
}
|
||||
|
||||
public int indexOf(TableColumn column) {
|
||||
return visibleList.indexOf(column);
|
||||
}
|
||||
|
||||
public TableColumn remove(int index) {
|
||||
|
||||
TableColumn column = visibleList.remove(index);
|
||||
visibleSet.remove(column);
|
||||
return column;
|
||||
}
|
||||
|
||||
public void add(int insertIndex, TableColumn column) {
|
||||
visibleList.add(insertIndex, column);
|
||||
visibleSet.add(column);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
visibleList.clear();
|
||||
visibleSet.clear();
|
||||
}
|
||||
|
||||
boolean contains(TableColumn c) {
|
||||
return visibleSet.contains(c);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Listener and event methods
|
||||
//==================================================================================================
|
||||
|
@ -40,7 +40,7 @@ public interface SortedTableModel extends TableModel {
|
||||
public boolean isSortable(int columnIndex);
|
||||
|
||||
/**
|
||||
* Returns the column index that is the primary sorted column
|
||||
* Returns the column index that is the primary sorted column; -1 if no column is sorted
|
||||
*
|
||||
* @return the index
|
||||
*/
|
||||
|
@ -20,6 +20,7 @@ import java.util.Comparator;
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.util.table.column.GColumnRenderer;
|
||||
import ghidra.util.table.column.GColumnRenderer.ColumnConstraintFilterMode;
|
||||
|
||||
/**
|
||||
* A special version of the backup comparator that uses the column's rendered value for
|
||||
@ -28,25 +29,40 @@ import ghidra.util.table.column.GColumnRenderer;
|
||||
*
|
||||
* @param <T> the row type
|
||||
*/
|
||||
public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T> {
|
||||
public class ColumnRenderedValueBackupComparator<T> implements Comparator<Object> {
|
||||
|
||||
protected int sortColumn;
|
||||
protected DynamicColumnTableModel<T> model;
|
||||
|
||||
public ColumnRenderedValueBackupRowComparator(DynamicColumnTableModel<T> model,
|
||||
// columns do not support sorting via their rendered value if the are marked
|
||||
// for column filtering only
|
||||
private boolean supportsColumnSorting = true;
|
||||
|
||||
public ColumnRenderedValueBackupComparator(DynamicColumnTableModel<T> model,
|
||||
int sortColumn) {
|
||||
this.model = model;
|
||||
this.sortColumn = sortColumn;
|
||||
|
||||
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||
@SuppressWarnings("unchecked")
|
||||
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||
if (renderer != null) {
|
||||
if (renderer.getColumnConstraintFilterMode() == ColumnConstraintFilterMode.USE_COLUMN_CONSTRAINTS_ONLY) {
|
||||
// this implies that the column has signaled that it does not support
|
||||
// filtering/sorting using its rendered value
|
||||
supportsColumnSorting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(T t1, T t2) {
|
||||
if (t1 == t2) {
|
||||
public int compare(Object c1, Object c2) {
|
||||
if (c1 == c2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String s1 = getRenderedColumnStringValue(t1);
|
||||
String s2 = getRenderedColumnStringValue(t2);
|
||||
String s1 = getRenderedColumnStringValue(c1);
|
||||
String s2 = getRenderedColumnStringValue(c2);
|
||||
|
||||
if (s1 == null || s2 == null) {
|
||||
return TableComparators.compareWithNullValues(s1, s2);
|
||||
@ -59,19 +75,23 @@ public class ColumnRenderedValueBackupRowComparator<T> implements Comparator<T>
|
||||
// unsafe. We happen know that we retrieved the value from the column that we are passing
|
||||
// it to, so the casting and usage is indeed safe.
|
||||
@SuppressWarnings("unchecked")
|
||||
private String getRenderedColumnStringValue(T t) {
|
||||
private String getRenderedColumnStringValue(Object columnValue) {
|
||||
|
||||
if (!supportsColumnSorting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DynamicTableColumn<T, ?, ?> column = model.getColumn(sortColumn);
|
||||
GColumnRenderer<Object> renderer = (GColumnRenderer<Object>) column.getColumnRenderer();
|
||||
Object o = model.getColumnValueForRow(t, sortColumn);
|
||||
if (renderer == null) {
|
||||
return o == null ? null : o.toString();
|
||||
return columnValue == null ? null : columnValue.toString();
|
||||
}
|
||||
|
||||
Settings settings = model.getColumnSettings(sortColumn);
|
||||
return renderer.getFilterString(o, settings);
|
||||
return renderer.getFilterString(columnValue, settings);
|
||||
}
|
||||
|
||||
// this may be overridden to use caching
|
||||
protected Object getColumnValue(T t) {
|
||||
return model.getColumnValueForRow(t, sortColumn);
|
||||
}
|
@ -27,12 +27,12 @@ import docking.widgets.table.TableComparators;
|
||||
*
|
||||
* @param <T> the row type
|
||||
*/
|
||||
public class RowToColumnComparator<T> implements Comparator<T> {
|
||||
public class RowBasedColumnComparator<T> implements Comparator<T> {
|
||||
|
||||
protected RowObjectTableModel<T> model;
|
||||
protected int sortColumn;
|
||||
protected Comparator<Object> columnComparator;
|
||||
protected Comparator<T> backupRowComparator = TableComparators.getNoSortComparator();
|
||||
protected Comparator<Object> backupRowComparator = TableComparators.getNoSortComparator();
|
||||
|
||||
/**
|
||||
* Constructs this class with the given column comparator that will get called after the
|
||||
@ -42,7 +42,7 @@ public class RowToColumnComparator<T> implements Comparator<T> {
|
||||
* @param sortColumn the column being sorted
|
||||
* @param comparator the column comparator to use for sorting
|
||||
*/
|
||||
public RowToColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||
public RowBasedColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||
Comparator<Object> comparator) {
|
||||
this.model = model;
|
||||
this.sortColumn = sortColumn;
|
||||
@ -59,8 +59,8 @@ public class RowToColumnComparator<T> implements Comparator<T> {
|
||||
* @param comparator the column comparator to use for sorting
|
||||
* @param backupRowComparator the backup row comparator
|
||||
*/
|
||||
public RowToColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||
Comparator<Object> comparator, Comparator<T> backupRowComparator) {
|
||||
public RowBasedColumnComparator(RowObjectTableModel<T> model, int sortColumn,
|
||||
Comparator<Object> comparator, Comparator<Object> backupRowComparator) {
|
||||
this.model = model;
|
||||
this.sortColumn = sortColumn;
|
||||
this.columnComparator = Objects.requireNonNull(comparator);
|
||||
@ -97,7 +97,7 @@ public class RowToColumnComparator<T> implements Comparator<T> {
|
||||
// backup comparator is not a stub and will do something reasonable for the sort,
|
||||
// depending upon how the model created this class.
|
||||
//
|
||||
return backupRowComparator.compare(t1, t2);
|
||||
return backupRowComparator.compare(value1, value2);
|
||||
}
|
||||
|
||||
protected Object getColumnValue(T t) {
|
@ -15,16 +15,16 @@
|
||||
*/
|
||||
package docking.widgets.table.threaded;
|
||||
|
||||
import docking.widgets.table.sort.ColumnRenderedValueBackupRowComparator;
|
||||
import docking.widgets.table.sort.RowToColumnComparator;
|
||||
import docking.widgets.table.sort.ColumnRenderedValueBackupComparator;
|
||||
import docking.widgets.table.sort.RowBasedColumnComparator;
|
||||
|
||||
/**
|
||||
* A version of {@link ColumnRenderedValueBackupRowComparator} that uses the
|
||||
* A version of {@link ColumnRenderedValueBackupComparator} that uses the
|
||||
* {@link ThreadedTableModel}'s cache for column lookups
|
||||
*
|
||||
* @param <T> the row type
|
||||
*/
|
||||
public class ThreadedBackupRowComparator<T> extends ColumnRenderedValueBackupRowComparator<T> {
|
||||
public class ThreadedBackupRowComparator<T> extends ColumnRenderedValueBackupComparator<T> {
|
||||
|
||||
private ThreadedTableModel<T, ?> threadedModel;
|
||||
|
||||
@ -34,7 +34,7 @@ public class ThreadedBackupRowComparator<T> extends ColumnRenderedValueBackupRow
|
||||
*
|
||||
* @param model the table model using this comparator
|
||||
* @param sortColumn the column being sorted
|
||||
* @see RowToColumnComparator
|
||||
* @see RowBasedColumnComparator
|
||||
*/
|
||||
public ThreadedBackupRowComparator(ThreadedTableModel<T, ?> model, int sortColumn) {
|
||||
super(model, sortColumn);
|
||||
|
@ -17,7 +17,7 @@ package docking.widgets.table.threaded;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import docking.widgets.table.sort.RowToColumnComparator;
|
||||
import docking.widgets.table.sort.RowBasedColumnComparator;
|
||||
|
||||
/**
|
||||
* A comparator for comparing table column values for threaded table models. This comparator
|
||||
@ -25,7 +25,7 @@ import docking.widgets.table.sort.RowToColumnComparator;
|
||||
*
|
||||
* @param <T> the row type
|
||||
*/
|
||||
public class ThreadedTableColumnComparator<T> extends RowToColumnComparator<T> {
|
||||
public class ThreadedTableColumnComparator<T> extends RowBasedColumnComparator<T> {
|
||||
private ThreadedTableModel<T, ?> threadedModel;
|
||||
|
||||
/**
|
||||
@ -35,7 +35,7 @@ public class ThreadedTableColumnComparator<T> extends RowToColumnComparator<T> {
|
||||
* @param model the table model using this comparator
|
||||
* @param sortColumn the column being sorted
|
||||
* @param comparator the column comparator to use for sorting
|
||||
* @see RowToColumnComparator
|
||||
* @see RowBasedColumnComparator
|
||||
*/
|
||||
public ThreadedTableColumnComparator(ThreadedTableModel<T, ?> model, int sortColumn,
|
||||
Comparator<Object> comparator) {
|
||||
@ -52,10 +52,10 @@ public class ThreadedTableColumnComparator<T> extends RowToColumnComparator<T> {
|
||||
* @param sortColumn the column being sorted
|
||||
* @param comparator the column comparator to use for sorting
|
||||
* @param backupRowComparator the backup row comparator
|
||||
* @see RowToColumnComparator
|
||||
* @see RowBasedColumnComparator
|
||||
*/
|
||||
public ThreadedTableColumnComparator(ThreadedTableModel<T, ?> model, int sortColumn,
|
||||
Comparator<Object> comparator, Comparator<T> backupRowComparator) {
|
||||
Comparator<Object> comparator, Comparator<Object> backupRowComparator) {
|
||||
super(model, sortColumn, comparator, backupRowComparator);
|
||||
this.threadedModel = model;
|
||||
}
|
||||
|
@ -214,7 +214,37 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||
protected abstract void doLoad(Accumulator<ROW_OBJECT> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException;
|
||||
|
||||
/**
|
||||
* This method will retrieve a column value for the given row object. Further, the retrieved
|
||||
* value will be cached. This is useful when sorting a table, as the same column value may
|
||||
* be requested multiple times.
|
||||
*
|
||||
* <p><u>Performance Notes</u>
|
||||
* <ul>
|
||||
* <li>This method uses a {@link HashMap} to cache column values for a row object. Further,
|
||||
* upon a key collision, the map will perform O(logn) lookups <b>if the
|
||||
* key (the row object) is {@link Comparable}</b>. If the key is not comparable, then
|
||||
* the collision lookups will be linear. So, make your row objects comparable
|
||||
* for maximum speed <b>when your table size becomes large</b> (for small tables there
|
||||
* is no observable impact).
|
||||
* <li>Even if your row objects are comparable, relying on this table model to convert your
|
||||
* row object into column values can be slow <b>for large tables</b>. This is because
|
||||
* the default column comparison framework for the tables will call this method
|
||||
* multiple times, resulting in many more method calls per column value lookup. For
|
||||
* large data, the repeated method calls start to become noticeable. For maximum
|
||||
* column sorting speed, use a comparator that works not on the column value, but on
|
||||
* the row value. To do this, return a comparator from your model's
|
||||
* {@link #createSortComparator(int)} method, instead of from the column itself or
|
||||
* by relying on column item implementing {@link Comparable}. This is possible any
|
||||
* time that a row object already has a field that is used for a given column.
|
||||
* </ul>
|
||||
*
|
||||
* @param rowObject the row object
|
||||
* @param columnIndex the column index for which to get a value
|
||||
* @return the column value
|
||||
*/
|
||||
Object getCachedColumnValueForRow(ROW_OBJECT rowObject, int columnIndex) {
|
||||
|
||||
Map<ROW_OBJECT, Map<Integer, Object>> cachedColumnValues = threadLocalColumnCache.get();
|
||||
|
||||
if (cachedColumnValues == null) {
|
||||
@ -239,7 +269,7 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||
}
|
||||
|
||||
void initializeCache() {
|
||||
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(20000));
|
||||
threadLocalColumnCache.set(new LRUMap<ROW_OBJECT, Map<Integer, Object>>(1000000));
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
@ -747,10 +777,6 @@ public abstract class ThreadedTableModel<ROW_OBJECT, DATA_SOURCE>
|
||||
return maxUpdateDelayMillis;
|
||||
}
|
||||
|
||||
protected Class<?> getSortedColumnClass(int columnIndex) {
|
||||
return getColumnClass(columnIndex);
|
||||
}
|
||||
|
||||
ThreadedTableModelUpdateMgr<ROW_OBJECT> getUpdateManager() {
|
||||
return updateManager;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public class TestThread extends Thread {
|
||||
/**
|
||||
* Returns true if the given thread name is the test thread name
|
||||
*
|
||||
* @param t the thread name to check
|
||||
* @param name the thread name to check
|
||||
* @return true if the given thread name is the test thread name
|
||||
*/
|
||||
public static boolean isTestThreadName(String name) {
|
||||
|
@ -48,7 +48,7 @@ public class LRUMap<K, V> implements Map<K, V> {
|
||||
protected HashMap<K, Entry<K, V>> map;
|
||||
private int cacheSize;
|
||||
private Entry<K, V> head;
|
||||
private long modificationID = 0;
|
||||
private volatile long modificationID = 0;
|
||||
|
||||
public LRUMap(int cacheSize) {
|
||||
this.cacheSize = cacheSize;
|
||||
@ -267,7 +267,7 @@ public class LRUMap<K, V> implements Map<K, V> {
|
||||
|
||||
/**
|
||||
* This is called after an item has been removed from the cache.
|
||||
* @param eldest the ite being removed
|
||||
* @param eldest the item being removed
|
||||
*/
|
||||
protected void eldestEntryRemoved(Map.Entry<K, V> eldest) {
|
||||
// this is just a way for subclasses to know when items are removed from the cache
|
||||
|
@ -217,7 +217,8 @@ public class CodeSymbol extends SymbolDB {
|
||||
*/
|
||||
@Override
|
||||
public boolean isValidParent(Namespace parent) {
|
||||
return SymbolType.LABEL.isValidParent(symbolMgr.getProgram(), parent, address, isExternal());
|
||||
return SymbolType.LABEL.isValidParent(symbolMgr.getProgram(), parent, address,
|
||||
isExternal());
|
||||
|
||||
// if (isExternal() != parent.isExternal()) {
|
||||
// return false;
|
||||
@ -241,15 +242,12 @@ public class CodeSymbol extends SymbolDB {
|
||||
// return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.symbol.Symbol#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
protected String doGetName() {
|
||||
if (getSource() == SourceType.DEFAULT && isExternal()) {
|
||||
return ExternalManagerDB.getDefaultExternalName(this);
|
||||
}
|
||||
return super.getName();
|
||||
return super.doGetName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +33,6 @@ import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
/**
|
||||
* Symbol class for functions.
|
||||
@ -237,11 +236,8 @@ public class FunctionSymbol extends SymbolDB {
|
||||
isExternal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.symbol.Symbol#getName()
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
protected String doGetName() {
|
||||
if (getSource() == SourceType.DEFAULT) {
|
||||
if (isExternal()) {
|
||||
return ExternalManagerDB.getDefaultExternalName(this);
|
||||
@ -251,17 +247,17 @@ public class FunctionSymbol extends SymbolDB {
|
||||
Symbol thunkedSymbol = getThunkedSymbol();
|
||||
if (thunkedSymbol instanceof FunctionSymbol) {
|
||||
FunctionSymbol thunkedFuncSym = (FunctionSymbol) thunkedSymbol;
|
||||
String name = thunkedFuncSym.getName();
|
||||
String thunkName = thunkedFuncSym.getName();
|
||||
if (thunkedFuncSym.getSource() == SourceType.DEFAULT &&
|
||||
thunkedFuncSym.getThunkedSymbol() == null) {
|
||||
// if thunking a default non-thunk function
|
||||
name = "thunk_" + name;
|
||||
thunkName = "thunk_" + thunkName;
|
||||
}
|
||||
return name;
|
||||
return thunkName;
|
||||
}
|
||||
return SymbolUtilities.getDefaultFunctionName(address);
|
||||
}
|
||||
return super.getName();
|
||||
return super.doGetName();
|
||||
}
|
||||
|
||||
// @Override
|
||||
@ -363,7 +359,7 @@ public class FunctionSymbol extends SymbolDB {
|
||||
checkIsValid();
|
||||
Reference[] refs = super.getReferences(monitor);
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitorAdapter.DUMMY_MONITOR;
|
||||
monitor = TaskMonitor.DUMMY;
|
||||
}
|
||||
if (monitor.isCancelled()) {
|
||||
return refs;
|
||||
|
@ -54,7 +54,7 @@ public class GlobalVariableSymbolDB extends VariableSymbolDB {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
protected String doGetName() {
|
||||
if (!checkIsValid()) {
|
||||
// TODO: SCR
|
||||
return "[Invalid VariableSymbol - Deleted!]";
|
||||
@ -63,7 +63,7 @@ public class GlobalVariableSymbolDB extends VariableSymbolDB {
|
||||
if (storage == null) {
|
||||
return Function.DEFAULT_LOCAL_PREFIX + "_!BAD!";
|
||||
}
|
||||
return super.getName();
|
||||
return super.doGetName();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,11 +30,13 @@ import ghidra.program.model.listing.CircularDependencyException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
|
||||
|
||||
/**
|
||||
* Base class for symbols
|
||||
@ -43,10 +45,25 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
private Record record;
|
||||
private boolean isDeleting = false;
|
||||
protected String name;
|
||||
protected Address address;
|
||||
protected SymbolManager symbolMgr;
|
||||
protected Lock lock;
|
||||
|
||||
/**
|
||||
* Creates a Symbol that is just a placeholder for use when trying to find symbols by using
|
||||
* {@link Symbol#getID()}. This is useful for locating symbols in Java collections when
|
||||
* a symbol has been deleted and the only remaining information is that symbol's ID.
|
||||
*
|
||||
* @param manager the manager for the new symbol
|
||||
* @param address the address of the symbol
|
||||
* @param id the id of the symbol
|
||||
* @return the fake symbol
|
||||
*/
|
||||
static SymbolDB createSymbolPlaceholder(SymbolManager manager, Address address, long id) {
|
||||
return new PlaceholderSymbolDB(manager, address, id);
|
||||
}
|
||||
|
||||
SymbolDB(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, Address address,
|
||||
Record record) {
|
||||
super(cache, record.getKey());
|
||||
@ -65,6 +82,11 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// prefer cached name for speed; it may be stale; call getName() for current value
|
||||
String temp = name;
|
||||
if (temp != null) {
|
||||
return temp;
|
||||
}
|
||||
return getName();
|
||||
}
|
||||
|
||||
@ -75,6 +97,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
@Override
|
||||
protected boolean refresh(Record rec) {
|
||||
name = null;
|
||||
if (record != null) {
|
||||
if (rec == null) {
|
||||
rec = symbolMgr.getSymbolRecord(key);
|
||||
@ -145,17 +168,29 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkIsValid();
|
||||
if (record != null) {
|
||||
return record.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL);
|
||||
if (name == null) {
|
||||
name = doGetName();
|
||||
}
|
||||
|
||||
return SymbolUtilities.getDynamicName(symbolMgr.getProgram(), address);
|
||||
return name;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The code for creating the name content for this symbol. This code will be called
|
||||
* with the symbol's lock.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
protected String doGetName() {
|
||||
if (record != null) {
|
||||
return record.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL);
|
||||
}
|
||||
return SymbolUtilities.getDynamicName(symbolMgr.getProgram(), address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Program getProgram() {
|
||||
return symbolMgr.getProgram();
|
||||
@ -238,7 +273,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
try {
|
||||
checkIsValid();
|
||||
if (monitor == null) {
|
||||
monitor = TaskMonitorAdapter.DUMMY_MONITOR;
|
||||
monitor = TaskMonitor.DUMMY;
|
||||
}
|
||||
|
||||
if (monitor.getMaximum() == 0) {
|
||||
@ -275,7 +310,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
@Override
|
||||
public Reference[] getReferences() {
|
||||
return getReferences(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
return getReferences(TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -470,7 +505,11 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
/**
|
||||
* Allow symbol implementations to validate the source when setting the name of
|
||||
* this symbol.
|
||||
* this symbol
|
||||
*
|
||||
* @param newName the new name
|
||||
* @param source the source type
|
||||
* @return the validated source type
|
||||
*/
|
||||
protected SourceType validateNameSource(String newName, SourceType source) {
|
||||
return source;
|
||||
@ -482,6 +521,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
lock.acquire();
|
||||
try {
|
||||
name = null;
|
||||
checkDeleted();
|
||||
checkEditOK();
|
||||
|
||||
@ -535,6 +575,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, newNamespace.getID());
|
||||
record.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, newName);
|
||||
name = newName;
|
||||
updateSymbolSource(record, source);
|
||||
updateRecord();
|
||||
|
||||
@ -613,10 +654,16 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Symbol s = (Symbol) obj;
|
||||
if (getID() == s.getID()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!getName().equals(s.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getAddress().equals(s.getAddress())) {
|
||||
return false;
|
||||
}
|
||||
@ -776,6 +823,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
/**
|
||||
* gets the generic symbol data 2 data.
|
||||
* @return the symbol data
|
||||
*/
|
||||
public int getSymbolData2() {
|
||||
lock.acquire();
|
||||
@ -852,10 +900,64 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
||||
|
||||
/**
|
||||
* Change the record and key associated with this symbol
|
||||
* @param the record.
|
||||
* @param record the record
|
||||
*/
|
||||
void setRecord(Record record) {
|
||||
this.record = record;
|
||||
keyChanged(record.getKey());
|
||||
}
|
||||
|
||||
private static class PlaceholderSymbolDB extends SymbolDB {
|
||||
|
||||
PlaceholderSymbolDB(SymbolManager symbolMgr, Address address, long key) {
|
||||
super(symbolMgr, null, address, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ((obj == null) || (!(obj instanceof Symbol))) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// this class is only ever equal if the id matches
|
||||
Symbol s = (Symbol) obj;
|
||||
if (getID() == s.getID()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymbolType getSymbolType() {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation() {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternal() {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject() {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimary() {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidParent(Namespace parent) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2274,6 +2274,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
|
||||
return new NamespaceDB(s, namespaceMgr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Symbol createSymbolPlaceholder(Address address, long id) {
|
||||
return SymbolDB.createSymbolPlaceholder(this, address, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a symbol, specifying all information for the record. This method is not on the
|
||||
* public interface and is only intended for program API internal use. The user of this
|
||||
@ -2675,5 +2680,4 @@ class SymbolMatcher implements Predicate<Symbol> {
|
||||
SymbolType type = s.getSymbolType();
|
||||
return type == type1;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -203,7 +203,7 @@ public class VariableSymbolDB extends SymbolDB {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
protected String doGetName() {
|
||||
if (!checkIsValid()) {
|
||||
// TODO: SCR
|
||||
return "[Invalid VariableSymbol - Deleted!]";
|
||||
@ -213,7 +213,7 @@ public class VariableSymbolDB extends SymbolDB {
|
||||
if (getSource() == SourceType.DEFAULT) {
|
||||
return getParamName();
|
||||
}
|
||||
String storedName = super.getName();
|
||||
String storedName = super.doGetName();
|
||||
if (SymbolUtilities.isDefaultParameterName(storedName)) {
|
||||
return getParamName();
|
||||
}
|
||||
@ -232,7 +232,7 @@ public class VariableSymbolDB extends SymbolDB {
|
||||
// TODO: we use to check for a default name and regenerate new default name but we should
|
||||
// not need to do this if source remains at default
|
||||
|
||||
return super.getName();
|
||||
return super.doGetName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -543,4 +543,15 @@ public interface SymbolTable {
|
||||
public Namespace createNameSpace(Namespace parent, String name, SourceType source)
|
||||
throws DuplicateNameException, InvalidInputException;
|
||||
|
||||
/**
|
||||
* Creates a Symbol that is just a placeholder for use when trying to find symbols by using
|
||||
* {@link Symbol#getID()}. This is useful for locating symbols in Java collections when
|
||||
* a symbol has been deleted and the only remaining information is that symbol's ID.
|
||||
*
|
||||
* @param address the address of the symbol
|
||||
* @param id the id of the symbol
|
||||
* @return the fake symbol
|
||||
*/
|
||||
public Symbol createSymbolPlaceholder(Address address, long id);
|
||||
|
||||
}
|
||||
|
@ -897,7 +897,11 @@ public class SymbolUtilities {
|
||||
if (symbol.isExternal()) {
|
||||
return "External Function";
|
||||
}
|
||||
|
||||
Function func = (Function) symbol.getObject();
|
||||
if (func == null) {
|
||||
return null; // symbol deleted
|
||||
}
|
||||
if (func.isThunk()) {
|
||||
return "Thunk Function";
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
ComponentProvider provider = getProvider("Symbol Table");
|
||||
tool.showComponentProvider(provider, true);
|
||||
|
||||
moveProviderToItsOwnWindow(provider, 950, 750);
|
||||
moveProviderToItsOwnWindow(provider, 950, 400);
|
||||
GTable table = getTable(provider);
|
||||
setColumnSizes(table);
|
||||
|
||||
@ -172,13 +172,28 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
TableColumn column = columnModel.getColumn(i);
|
||||
Object headerValue = column.getHeaderValue();
|
||||
if ("Name".equals(headerValue)) {
|
||||
column.setPreferredWidth(175);
|
||||
column.setPreferredWidth(300);
|
||||
}
|
||||
else if ("Reference Count".equals(headerValue)) {
|
||||
column.setPreferredWidth(15);
|
||||
column.setPreferredWidth(25);
|
||||
}
|
||||
else if ("Offcut Ref Count".equals(headerValue)) {
|
||||
column.setPreferredWidth(15);
|
||||
column.setPreferredWidth(25);
|
||||
}
|
||||
else if ("Namespace".equals(headerValue)) {
|
||||
column.setPreferredWidth(160);
|
||||
}
|
||||
else if ("Location".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Namespace".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Source".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
else if ("Type".equals(headerValue)) {
|
||||
column.setPreferredWidth(170);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user