Merge remote-tracking branch

'origin/GT-3226-dragonmacher-tables-slow-sorting' (closes #500)
This commit is contained in:
Ryan Kurtz 2019-10-29 13:03:54 -04:00
commit 3051601206
48 changed files with 1151 additions and 953 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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
}
}

View File

@ -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();
}
}
}

View File

@ -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
}
}

View File

@ -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;

View File

@ -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);
}
/**

View File

@ -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
//==================================================================================================

View File

@ -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
*/

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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";
}

View File

@ -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);
}
}
});