Merge remote-tracking branch 'origin/GT_153_ghidravore_changing_graph_api--SQUASHED' into Ghidra_9.2

This commit is contained in:
ghidravore 2020-10-09 14:35:57 -04:00
commit 14d7e1f908
14 changed files with 292 additions and 283 deletions

View File

@ -15,8 +15,7 @@
*/
package ghidra.app.plugin.core.graph;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import docking.widgets.EventTrigger;
@ -29,8 +28,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
import ghidra.service.graph.GraphDisplay;
import ghidra.service.graph.GraphDisplayListener;
import ghidra.service.graph.*;
import ghidra.util.Swing;
/**
@ -40,7 +38,7 @@ public abstract class AddressBasedGraphDisplayListener
implements GraphDisplayListener, PluginEventListener, DomainObjectListener {
protected PluginTool tool;
private GraphDisplay graphDisplay;
protected GraphDisplay graphDisplay;
protected Program program;
private SymbolTable symbolTable;
private String name;
@ -63,8 +61,8 @@ public abstract class AddressBasedGraphDisplayListener
}
@Override
public void locationFocusChanged(String vertexId) {
Address address = getAddressForVertexId(vertexId);
public void locationFocusChanged(AttributedVertex vertex) {
Address address = getAddress(vertex);
if (address != null) {
ProgramLocation location = new ProgramLocation(program, address);
tool.firePluginEvent(new ProgramLocationPluginEvent(name, location, program));
@ -72,8 +70,8 @@ public abstract class AddressBasedGraphDisplayListener
}
@Override
public void selectionChanged(List<String> vertexIds) {
AddressSet addressSet = getAddressSetForVertices(vertexIds);
public void selectionChanged(Set<AttributedVertex> vertices) {
AddressSet addressSet = getAddresses(vertices);
if (addressSet != null) {
ProgramSelection selection = new ProgramSelection(addressSet);
ProgramSelectionPluginEvent event =
@ -99,16 +97,16 @@ public abstract class AddressBasedGraphDisplayListener
ProgramLocationPluginEvent ev = (ProgramLocationPluginEvent) event;
if (isMyProgram(ev.getProgram())) {
ProgramLocation location = ev.getLocation();
String id = getVertexIdForAddress(location.getAddress());
AttributedVertex vertex = getVertex(location.getAddress());
// update graph location, but tell it not to send out event
graphDisplay.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
graphDisplay.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
}
}
else if (event instanceof ProgramSelectionPluginEvent) {
ProgramSelectionPluginEvent ev = (ProgramSelectionPluginEvent) event;
if (isMyProgram(ev.getProgram())) {
ProgramSelection selection = ev.getSelection();
List<String> selectedVertices = getVertices(selection);
Set<AttributedVertex> selectedVertices = getVertices(selection);
if (selectedVertices != null) {
// since we are responding to an event, tell the GraphDisplay not to send event
graphDisplay.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
@ -117,7 +115,15 @@ public abstract class AddressBasedGraphDisplayListener
}
}
protected String getVertexIdForAddress(Address address) {
public AttributedVertex getVertex(Address address) {
if (address == null) {
return null;
}
String id = getVertexId(address);
return graphDisplay.getGraph().getVertex(id);
}
protected String getVertexId(Address address) {
// vertex ids for external locations use symbol names since they don't have meaningful addresses.
if (address.isExternalAddress()) {
Symbol s = symbolTable.getPrimarySymbol(address);
@ -153,13 +159,16 @@ public abstract class AddressBasedGraphDisplayListener
}
protected Address getAddressForVertexId(String vertexId) {
return getAddress(vertexId);
protected Address getAddress(AttributedVertex vertex) {
if (vertex == null) {
return null;
}
return getAddress(vertex.getId());
}
protected abstract List<String> getVertices(AddressSetView selection);
protected abstract Set<AttributedVertex> getVertices(AddressSetView selection);
protected abstract AddressSet getAddressSetForVertices(List<String> vertexIds);
protected abstract AddressSet getAddresses(Set<AttributedVertex> vertexIds);
private boolean isMyProgram(Program p) {
return p == program;
@ -192,15 +201,18 @@ public abstract class AddressBasedGraphDisplayListener
}
private void handleSymbolAddedOrRenamed(Address address, Symbol symbol) {
String id = getVertexIdForAddress(address);
graphDisplay.updateVertexName(id, symbol.getName());
AttributedVertex vertex = getVertex(address);
graphDisplay.updateVertexName(vertex, symbol.getName());
}
private void handleSymbolRemoved(Address address) {
String id = getVertexIdForAddress(address);
AttributedVertex vertex = getVertex(address);
if (vertex == null) {
return;
}
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
String displayName = symbol == null ? address.toString() : symbol.getName();
graphDisplay.updateVertexName(id, displayName);
graphDisplay.updateVertexName(vertex, displayName);
}
private void dispose() {

View File

@ -232,16 +232,15 @@ public class GraphAST extends GhidraScript {
}
@Override
protected List<String> getVertices(AddressSetView selection) {
List<String> ids = new ArrayList<String>();
return ids;
protected Set<AttributedVertex> getVertices(AddressSetView selection) {
return Collections.emptySet();
}
@Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) {
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet set = new AddressSet();
for (String id : vertexIds) {
Address address = getAddressForVertexId(id);
for (AttributedVertex vertex : vertices) {
Address address = getAddress(vertex);
if (address != null) {
set.add(address);
}
@ -250,7 +249,11 @@ public class GraphAST extends GhidraScript {
}
@Override
protected Address getAddressForVertexId(String vertexId) {
protected Address getAddress(AttributedVertex vertex) {
if (vertex == null) {
return null;
}
String vertexId = vertex.getId();
int firstcolon = vertexId.indexOf(':');
if (firstcolon == -1) {
return null;

View File

@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile.actions;
import static ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType.*;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.app.plugin.core.decompile.actions.ASTGraphTask.GraphType;
import ghidra.app.plugin.core.graph.AddressBasedGraphDisplayListener;
@ -26,8 +25,7 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeBlockBasic;
import ghidra.service.graph.GraphDisplay;
import ghidra.service.graph.GraphDisplayListener;
import ghidra.service.graph.*;
import ghidra.util.exception.AssertException;
/**
@ -45,33 +43,37 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
}
@Override
protected List<String> getVertices(AddressSetView selection) {
protected Set<AttributedVertex> getVertices(AddressSetView selection) {
if (graphType != CONTROL_FLOW_GRAPH) {
return null;
}
List<String> vertices = new ArrayList<>();
Set<AttributedVertex> vertices = new HashSet<>();
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
for (PcodeBlockBasic block : blocks) {
Address start = block.getStart();
Address stop = block.getStop();
if (selection.intersects(start, stop)) {
vertices.add(Integer.toString(block.getIndex()));
String id = Integer.toString(block.getIndex());
AttributedVertex vertex = graphDisplay.getGraph().getVertex(id);
if (vertex != null) {
vertices.add(vertex);
}
}
}
return vertices;
}
@Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) {
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
if (graphType != CONTROL_FLOW_GRAPH) {
return null;
}
AddressSet set = new AddressSet();
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
for (String vertixId : vertexIds) {
for (AttributedVertex vertex : vertices) {
try {
int index = Integer.parseInt(vertixId);
int index = Integer.parseInt(vertex.getId());
PcodeBlockBasic block = blocks.get(index);
Address start = block.getStart();
set.addRange(start, block.getStop());
@ -84,7 +86,7 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
}
@Override
protected String getVertexIdForAddress(Address address) {
protected String getVertexId(Address address) {
if (graphType != CONTROL_FLOW_GRAPH) {
return null;
}
@ -96,25 +98,25 @@ public class ASTGraphDisplayListener extends AddressBasedGraphDisplayListener {
return Integer.toString(block.getIndex());
}
}
return super.getVertexIdForAddress(address);
return super.getVertexId(address);
}
@Override
protected Address getAddressForVertexId(String vertexId) {
protected Address getAddress(AttributedVertex vertex) {
List<PcodeBlockBasic> blocks = hfunction.getBasicBlocks();
try {
int index = Integer.parseInt(vertexId);
int index = Integer.parseInt(vertex.getId());
PcodeBlockBasic block = blocks.get(index);
return block.getStart();
}
catch (NumberFormatException e) {
throw new AssertException("Bad vertex id, expected a number but got " + vertexId);
throw new AssertException("Bad vertex id, expected a number but got " + vertex.getId());
}
}
@Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
public GraphDisplayListener cloneWith(GraphDisplay display) {
return new ASTGraphDisplayListener(tool, graphDisplay, hfunction, graphType);
}

View File

@ -124,9 +124,9 @@ public class ASTGraphTask extends Task {
display.setGraph(graph, description, false, monitor);
// set the graph location
if (location != null) {
String id = displayListener.getVertexIdForAddress(location);
AttributedVertex vertex = displayListener.getVertex(location);
// update graph location, but don't have it send out event
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
}
}

View File

@ -15,7 +15,8 @@
*/
package ghidra.graph.export;
import java.util.*;
import java.util.Collections;
import java.util.Set;
import org.jgrapht.Graph;
@ -57,15 +58,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
// This display is not interactive, so N/A
}
@Override
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) {
// This display is not interactive, so N/A
}
@Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
// This display is not interactive, so N/A
}
/**
* set the {@link AttributedGraph} for visualization
@ -108,7 +101,7 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
}
@Override
public void updateVertexName(String id, String newName) {
public void updateVertexName(AttributedVertex vertex, String newName) {
// do nothing
}
@ -123,13 +116,28 @@ class ExportAttributedGraphDisplay implements GraphDisplay {
}
@Override
public String getFocusedVertexId() {
public AttributedVertex getFocusedVertex() {
return null;
}
@Override
public Set<String> getSelectedVertexIds() {
public Set<AttributedVertex> getSelectedVertices() {
return Collections.emptySet();
}
@Override
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
// not interactive, so N/A
}
@Override
public AttributedGraph getGraph() {
return null;
}
@Override
public void selectVertices(Set<AttributedVertex> vertexList, EventTrigger eventTrigger) {
// not interactive, so N/A
}
}

View File

@ -539,9 +539,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
});
Set<AttributedVertex> selected = selectedVertexState.getSelected();
List<String> selectedIds =
selected.stream().map(AttributedVertex::getId).collect(Collectors.toList());
notifySelectionChanged(selectedIds);
notifySelectionChanged(selected);
}
finally {
switchableSelectionListener.setEnabled(true);
@ -716,17 +714,18 @@ public class DefaultGraphDisplay implements GraphDisplay {
setFocusedVertex(vertex, EventTrigger.API_CALL);
}
protected void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
@Override
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
boolean changed = this.focusedVertex != vertex;
this.focusedVertex = vertex;
if (focusedVertex != null) {
if (changed && eventTrigger != EventTrigger.INTERNAL_ONLY) {
notifyLocationFocusChanged(focusedVertex.getId());
notifyLocationFocusChanged(focusedVertex);
}
// make sure the vertex is visible, even if the vertex has not changed
scrollToSelected(focusedVertex);
viewer.repaint();
}
viewer.repaint();
}
/**
@ -769,22 +768,22 @@ public class DefaultGraphDisplay implements GraphDisplay {
/**
* fire an event to notify the selected vertices changed
* @param vertexIds the list of vertexes
* @param selected the list of selected vertices
*/
private void notifySelectionChanged(List<String> vertexIds) {
Swing.runLater(() -> listener.selectionChanged(vertexIds));
private void notifySelectionChanged(Set<AttributedVertex> selected) {
Swing.runLater(() -> listener.selectionChanged(selected));
}
/**
* fire and event to say the focused vertex changed
* @param vertexId the id of the focused vertex
* @param vertex the new focused vertex
*/
private void notifyLocationFocusChanged(String vertexId) {
Swing.runLater(() -> listener.locationFocusChanged(vertexId));
private void notifyLocationFocusChanged(AttributedVertex vertex) {
Swing.runLater(() -> listener.locationFocusChanged(vertex));
}
@Override
public void selectVertices(List<String> vertexIdList, EventTrigger eventTrigger) {
public void selectVertices(Set<AttributedVertex> selected, EventTrigger eventTrigger) {
// if we are not to fire events, turn off the selection listener we provided to the
// graphing library.
switchableSelectionListener.setEnabled(eventTrigger != EventTrigger.INTERNAL_ONLY);
@ -792,8 +791,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
try {
MutableSelectedState<AttributedVertex> nodeSelectedState =
viewer.getSelectedVertexState();
Set<AttributedVertex> selected = getVertices(vertexIdList);
if (vertexIdList.isEmpty()) {
if (selected.isEmpty()) {
nodeSelectedState.clear();
}
else if (!Arrays.asList(nodeSelectedState.getSelectedObjects()).containsAll(selected)) {
@ -809,30 +807,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
}
/**
*
* @param vertexIds vertex ids of interest
* @return a {@code Set} containing the {@code AttributedVertex} for ths supplied ids
*/
private Set<AttributedVertex> getVertices(Collection<String> vertexIds) {
Set<String> vertexSet = new HashSet<>(vertexIds);
return graph.vertexSet()
.stream()
.filter(v -> vertexSet.contains(v.getId()))
.collect(Collectors.toSet());
}
@Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
Optional<AttributedVertex> vertexToFocus =
graph.vertexSet().stream().filter(v -> vertexID.equals(v.getId())).findFirst();
log.fine("picking address:" + vertexID + " returned " + vertexToFocus);
viewer.repaint();
vertexToFocus.ifPresent(v -> {
setFocusedVertex(v, eventTrigger);
});
viewer.repaint();
}
/**
* set the {@link AttributedGraph} for visualization
@ -1066,23 +1040,15 @@ public class DefaultGraphDisplay implements GraphDisplay {
/**
* process a request to update the name attribute value of the vertex with the
* supplied id
* @param id the vertix id
* @param vertex the vertex to update
* @param newName the new name of the vertex
*/
@Override
public void updateVertexName(String id, String newName) {
// find the vertex, if present, change the name
Optional<AttributedVertex> optional = graph.vertexSet()
.stream()
.filter(v -> v.getId().equals(id))
.findFirst();
if (optional.isPresent()) {
AttributedVertex vertex = optional.get();
vertex.setName(newName);
vertex.clearCache();
iconCache.evict(vertex);
viewer.repaint();
}
public void updateVertexName(AttributedVertex vertex, String newName) {
vertex.setName(newName);
vertex.clearCache();
iconCache.evict(vertex);
viewer.repaint();
}
/**
@ -1225,8 +1191,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
// vertices
if (e.getStateChange() == ItemEvent.SELECTED) {
Collection<AttributedVertex> selectedVertices = getVertices(e.getItem());
List<String> selectedVertexIds = toVertexIds(selectedVertices);
notifySelectionChanged(selectedVertexIds);
notifySelectionChanged(new HashSet<AttributedVertex>(selectedVertices));
if (selectedVertices.size() == 1) {
// if only one vertex was selected, make it the focused vertex
@ -1239,7 +1204,7 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
}
else if (e.getStateChange() == ItemEvent.DESELECTED) {
notifySelectionChanged(Collections.emptyList());
notifySelectionChanged(Collections.emptySet());
}
viewer.repaint();
}
@ -1255,14 +1220,13 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
@Override
public String getFocusedVertexId() {
return focusedVertex == null ? null : focusedVertex.getId();
public AttributedVertex getFocusedVertex() {
return focusedVertex;
}
@Override
public Set<String> getSelectedVertexIds() {
Set<AttributedVertex> selectedVertices = getSelectedVertices();
return selectedVertices.stream().map(v -> v.getId()).collect(Collectors.toSet());
public Set<AttributedVertex> getSelectedVertices() {
return viewer.getSelectedVertexState().getSelected();
}
public ActionContext getActionContext(MouseEvent e) {
@ -1284,9 +1248,6 @@ public class DefaultGraphDisplay implements GraphDisplay {
}
private Set<AttributedVertex> getSelectedVertices() {
return viewer.getSelectedVertexState().getSelected();
}
/**
* Use the hide selected action states to determine what vertices are shown:
@ -1321,4 +1282,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
viewer.repaint();
}
@Override
public AttributedGraph getGraph() {
return graph;
}
}

View File

@ -157,13 +157,13 @@ public class BlockGraphTask extends Task {
if (location != null) {
// initialize the graph location, but don't have the graph send an event
String id = listener.getVertexIdForAddress(location.getAddress());
display.setLocationFocus(id, EventTrigger.INTERNAL_ONLY);
AttributedVertex vertex = listener.getVertex(location.getAddress());
display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY);
}
if (selection != null && !selection.isEmpty()) {
List<String> selectedVertices = listener.getVertices(selection);
Set<AttributedVertex> selectedVertices = listener.getVertices(selection);
if (selectedVertices != null) {
// intialize the graph selection, but don't have the graph send an event
// initialize the graph selection, but don't have the graph send an event
display.selectVertices(selectedVertices, EventTrigger.INTERNAL_ONLY);
}
}

View File

@ -54,28 +54,28 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
}
@Override
protected String getVertexIdForAddress(Address address) {
protected String getVertexId(Address address) {
try {
CodeBlock[] blocks = blockModel.getCodeBlocksContaining(address, TaskMonitor.DUMMY);
if (blocks != null && blocks.length > 0) {
return super.getVertexIdForAddress(blocks[0].getFirstStartAddress());
return super.getVertexId(blocks[0].getFirstStartAddress());
}
}
catch (CancelledException e) {
// Will not happen with dummyMonitor
// Model has already done the work when the graph was created
}
return super.getVertexIdForAddress(address);
return super.getVertexId(address);
}
@Override
protected List<String> getVertices(AddressSetView addrSet) {
protected Set<AttributedVertex> getVertices(AddressSetView addrSet) {
if (addrSet.isEmpty()) {
return Collections.emptyList();
return Collections.emptySet();
}
// Identify all blocks which have an entry point within the selection address set
ArrayList<String> blockList = new ArrayList<String>();
Set<AttributedVertex> vertices = new HashSet<>();
try {
SymbolTable symTable = program.getSymbolTable();
CodeBlockIterator cbIter =
@ -91,7 +91,10 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
else {
addrString = addr.toString();
}
blockList.add(addrString);
AttributedVertex vertex = graphDisplay.getGraph().getVertex(addrString);
if (vertex != null) {
vertices.add(vertex);
}
}
}
catch (CancelledException e) {
@ -99,18 +102,18 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
// Model has already done the work when the graph was created
}
return blockList;
return vertices;
}
@Override
protected AddressSet getAddressSetForVertices(List<String> vertexIds) {
protected AddressSet getAddresses(Set<AttributedVertex> vertices) {
AddressSet addrSet = new AddressSet();
try {
// for each address string, translate it into a block
// and add it to the address set.
for (String vertexId : vertexIds) {
Address blockAddr = getAddressForVertexId(vertexId);
for (AttributedVertex vertex : vertices) {
Address blockAddr = getAddress(vertex);
if (!isValidAddress(blockAddr)) {
continue;
}
@ -150,8 +153,8 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
}
private void updateVertexName(VertexGraphActionContext context) {
String vertexId = context.getClickedVertex().getId();
Address address = getAddressForVertexId(vertexId);
AttributedVertex vertex = context.getClickedVertex();
Address address = getAddress(vertex);
Symbol symbol = program.getSymbolTable().getPrimarySymbol(address);
if (symbol == null) {
@ -165,8 +168,8 @@ public class BlockModelGraphDisplayListener extends AddressBasedGraphDisplayList
}
@Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
return new BlockModelGraphDisplayListener(tool, blockModel, graphDisplay);
public GraphDisplayListener cloneWith(GraphDisplay newGraphDisplay) {
return new BlockModelGraphDisplayListener(tool, blockModel, newGraphDisplay);
}
}

View File

@ -17,7 +17,8 @@ package ghidra.graph.program;
import static org.junit.Assert.*;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
@ -27,6 +28,7 @@ import ghidra.program.model.block.CodeBlockModel;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.service.graph.AttributedGraph;
import ghidra.service.graph.AttributedVertex;
import ghidra.util.task.TaskMonitor;
public class BlockGraphEventTest extends AbstractBlockGraphTest {
@ -54,13 +56,13 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest {
@Test
public void testGhidraLocationChanged() {
codeBrowser.goTo(new ProgramLocation(program, addr(0x1002239)));
assertEquals("01002239", display.getFocusedVertex());
assertEquals("01002239", display.getFocusedVertex().getId());
codeBrowser.goTo(new ProgramLocation(program, addr(0x1002200)));
assertEquals("01002200", display.getFocusedVertex());
assertEquals("01002200", display.getFocusedVertex().getId());
// also try a location that is not the start of a block
codeBrowser.goTo(new ProgramLocation(program, addr(0x100223a)));
assertEquals("01002239", display.getFocusedVertex());
assertEquals("01002239", display.getFocusedVertex().getId());
}
@ -71,38 +73,38 @@ public class BlockGraphEventTest extends AbstractBlockGraphTest {
@Test
public void testGhidraSelectionChanged() {
setSelection(addrSet(0x1002239, 0x1002241));
Set<String> selected = new HashSet<>(display.getSelectedVertices());
Set<AttributedVertex> selected = new HashSet<>(display.getSelectedVertices());
assertEquals(3, selected.size());
assertTrue(selected.contains("01002239"));
assertTrue(selected.contains("0100223c"));
assertTrue(selected.contains("0100223e"));
assertTrue(selected.contains(graph.getVertex("01002239")));
assertTrue(selected.contains(graph.getVertex("0100223c")));
assertTrue(selected.contains(graph.getVertex("0100223e")));
setSelection(new AddressSet(addr(0x1002200), addr(0x1002210)));
selected = new HashSet<>(display.getSelectedVertices());
assertEquals(2, selected.size());
assertTrue(selected.contains("01002200"));
assertTrue(selected.contains("01002203"));
assertTrue(selected.contains(graph.getVertex("01002200")));
assertTrue(selected.contains(graph.getVertex("01002203")));
}
@Test
public void testGraphNodeFocused() {
display.focusChanged("01002203");
display.focusChanged(graph.getVertex("01002203"));
assertEquals(addr(0x01002203), codeBrowser.getCurrentLocation().getAddress());
display.focusChanged("0100223c");
display.focusChanged(graph.getVertex("0100223c"));
assertEquals(addr(0x0100223c), codeBrowser.getCurrentLocation().getAddress());
}
@Test
public void testGraphNodesSelected() {
display.selectionChanged(Arrays.asList("01002239", "0100223c"));
display.selectionChanged(Set.of(graph.getVertex("01002239"), graph.getVertex("0100223c")));
ProgramSelection selection = codeBrowser.getCurrentSelection();
assertEquals(addr(0x01002239), selection.getMinAddress());
assertEquals(addr(0x0100223d), selection.getMaxAddress());
display.selectionChanged(Arrays.asList("01002200", "01002203"));
display.selectionChanged(Set.of(graph.getVertex("01002200"), graph.getVertex("01002203")));
selection = codeBrowser.getCurrentSelection();
assertEquals(addr(0x01002200), selection.getMinAddress());
assertEquals(addr(0x01002204), selection.getMaxAddress());

View File

@ -15,7 +15,8 @@
*/
package ghidra.graph.program;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
import docking.action.DockingAction;
import docking.widgets.EventTrigger;
@ -26,12 +27,11 @@ import ghidra.util.task.TaskMonitor;
public class TestGraphDisplay implements GraphDisplay {
private Set<String> definedVertexAttributes = new HashSet<>();
private Set<String> definedEdgeAttributes = new HashSet<>();
private String vertexAttributeName;
private AttributedGraph graph;
private String graphDescription;
private GraphDisplayListener listener;
private String currentFocusedVertex;
private List<String> currentSelection;
private AttributedVertex focusedVertex;
private Set<AttributedVertex> currentSelection;
@Override
public void setGraphDisplayListener(GraphDisplayListener listener) {
@ -39,20 +39,22 @@ public class TestGraphDisplay implements GraphDisplay {
}
@Override
public void setLocationFocus(String vertexID, EventTrigger eventTrigger) {
currentFocusedVertex = vertexID;
}
public String getFocusedVertex() {
return currentFocusedVertex;
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger) {
focusedVertex = vertex;
}
@Override
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger) {
public AttributedVertex getFocusedVertex() {
return focusedVertex;
}
@Override
public void selectVertices(Set<AttributedVertex> vertexList, EventTrigger eventTrigger) {
currentSelection = vertexList;
}
public List<String> getSelectedVertices() {
@Override
public Set<AttributedVertex> getSelectedVertices() {
return currentSelection;
}
@ -74,7 +76,7 @@ public class TestGraphDisplay implements GraphDisplay {
@Override
public void setVertexLabel(String attributeName, int alignment, int size, boolean monospace,
int maxLines) {
vertexAttributeName = attributeName;
// nothing
}
@Override
@ -91,7 +93,7 @@ public class TestGraphDisplay implements GraphDisplay {
}
@Override
public void updateVertexName(String id, String newName) {
public void updateVertexName(AttributedVertex vertex, String newName) {
// nothing
}
@ -100,16 +102,17 @@ public class TestGraphDisplay implements GraphDisplay {
return graphDescription;
}
@Override
public AttributedGraph getGraph() {
return graph;
}
public void focusChanged(String vertexId) {
listener.locationFocusChanged(vertexId);
public void focusChanged(AttributedVertex vertex) {
listener.locationFocusChanged(vertex);
}
public void selectionChanged(List<String> vertexIds) {
listener.selectionChanged(vertexIds);
public void selectionChanged(Set<AttributedVertex> vertices) {
listener.selectionChanged(vertices);
}
@Override
@ -117,14 +120,4 @@ public class TestGraphDisplay implements GraphDisplay {
// do nothing, actions are not supported by this display
}
@Override
public String getFocusedVertexId() {
return currentFocusedVertex;
}
@Override
public Set<String> getSelectedVertexIds() {
return new HashSet<String>(currentSelection);
}
}

View File

@ -15,7 +15,7 @@
*/
package ghidra.service.graph;
import java.util.List;
import java.util.Set;
public class DummyGraphDisplayListener implements GraphDisplayListener {
@ -24,19 +24,19 @@ public class DummyGraphDisplayListener implements GraphDisplayListener {
// I'm a dummy
}
@Override
public void selectionChanged(List<String> vertexIds) {
// I'm a dummy
}
@Override
public void locationFocusChanged(String vertexId) {
// I'm a dummy
}
@Override
public GraphDisplayListener cloneWith(GraphDisplay graphDisplay) {
return new DummyGraphDisplayListener();
}
@Override
public void selectionChanged(Set<AttributedVertex> vertices) {
// I'm a dummy
}
@Override
public void locationFocusChanged(AttributedVertex vertex) {
// I'm a dummy
}
}

View File

@ -15,7 +15,6 @@
*/
package ghidra.service.graph;
import java.util.List;
import java.util.Set;
import docking.action.DockingAction;
@ -46,41 +45,46 @@ public interface GraphDisplay {
/**
* Tells the graph display window to focus the vertex with the given id.
*
* @param vertexID the id of the vertex to focus
* @param vertex the vertex to focus
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
* graph location so that the GraphDisplay can decide if it should send out a notification via
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating
* the the location due to an event from the main application, we don't want to notify the
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more
* information.
*
* the {@link GraphDisplayListener#locationFocusChanged(AttributedVertex)}. For example, if we
* are updating the the location due to an event from the main application, we don't want to
* notify the application the graph changed to avoid event cycles. See {@link EventTrigger} for
* more information.
*/
public void setLocationFocus(String vertexID, EventTrigger eventTrigger);
public void setFocusedVertex(AttributedVertex vertex, EventTrigger eventTrigger);
/**
* Returns the currently focused vertexID or null if no vertex is focussed.
* @return the currently focused vertexID or null if no vertex is focussed.
* Returns the graph for this display
* @return the graph for this display
*/
public String getFocusedVertexId();
public AttributedGraph getGraph();
/**
* Returns the currently focused vertex or null if no vertex is focused
* @return the currently focused vertex or null if no vertex is focused.
*/
public AttributedVertex getFocusedVertex();
/**
* Tells the graph display window to select the vertices with the given ids
*
* @param vertexList the list of vertex ids to select
* @param vertexSet the set of vertices to select
* @param eventTrigger Provides a hint to the GraphDisplay as to why we are updating the
* graph location so that the GraphDisplay can decide if it should send out a notification via
* the {@link GraphDisplayListener#locationFocusChanged(String)}. For example, if we are updating
* the {@link GraphDisplayListener#selectionChanged(Set)}. For example, if we are updating
* the the location due to an event from the main application, we don't want to notify the
* application the graph changed to avoid event cycles. See {@link EventTrigger} for more
* information.
*/
public void selectVertices(List<String> vertexList, EventTrigger eventTrigger);
public void selectVertices(Set<AttributedVertex> vertexSet, EventTrigger eventTrigger);
/**
* Returns a list of vertex ids for all the currently selected vertices
* @return a list of vertex ids for all the currently selected vertices
* Returns a set of vertex ids for all the currently selected vertices
* @return a set of vertex ids for all the currently selected vertices
*/
public Set<String> getSelectedVertexIds();
public Set<AttributedVertex> getSelectedVertices();
/**
* Closes this graph display window.
@ -131,10 +135,10 @@ public interface GraphDisplay {
/**
* Updates a vertex to a new name
* @param id the vertex id
* @param newName the new name of the vertex
* @param vertex the vertex to rename
* @param newName the new name for the vertex
*/
public void updateVertexName(String id, String newName);
public void updateVertexName(AttributedVertex vertex, String newName);
/**
* Returns the description of the current graph
@ -148,4 +152,5 @@ public interface GraphDisplay {
* @param action the action to add.
*/
public void addAction(DockingAction action);
}

View File

@ -15,7 +15,7 @@
*/
package ghidra.service.graph;
import java.util.List;
import java.util.Set;
/**
* Interface for being notified when the user interacts with a visual graph display
@ -27,17 +27,17 @@ public interface GraphDisplayListener {
public void graphClosed();
/**
* Notification that the list of selected vertices has changed
* Notification that the set of selected vertices has changed
*
* @param vertexIds the list of vertex ids for the currently selected vertices
* @param vertices the set of currently selected vertices
*/
public void selectionChanged(List<String> vertexIds);
public void selectionChanged(Set<AttributedVertex> vertices);
/**
* Notification that the "focused" (active) vertex has changed
* @param vertexId the vertex id of the currently "focused" vertex
* @param vertex the vertex that is currently "focused"
*/
public void locationFocusChanged(String vertexId);
public void locationFocusChanged(AttributedVertex vertex);
/**
* Makes a new GraphDisplayListener of the same type as the specific

View File

@ -40,6 +40,12 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
private AttributedGraph graph;
private ComponentProvider graphComponentProvider;
private GraphDisplay display;
private AttributedVertex a;
private AttributedVertex b;
private AttributedVertex c;
private AttributedVertex d;
private AttributedVertex e;
private AttributedVertex f;
@Before
public void setUp() throws Exception {
@ -60,7 +66,7 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testSelectVertexAction() {
assertTrue(display.getSelectedVertexIds().isEmpty());
assertTrue(display.getSelectedVertices().isEmpty());
DockingActionIf action = getAction(tool, "Select Vertex");
VertexGraphActionContext context =
@ -68,25 +74,24 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getVertex("B"));
performAction(action, context, true);
Set<String> selectedVertexIds = display.getSelectedVertexIds();
assertEquals(1, selectedVertexIds.size());
assertTrue(selectedVertexIds.contains(graph.getVertex("B").getId()));
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(1, selectedVertices.size());
assertTrue(selectedVertices.contains(b));
// now try and select a second vertex
context = new VertexGraphActionContext(graphComponentProvider, graph, null, null,
graph.getVertex("D"));
context = new VertexGraphActionContext(graphComponentProvider, graph, null, null,d);
performAction(action, context, true);
selectedVertexIds = display.getSelectedVertexIds();
assertEquals(2, selectedVertexIds.size());
assertTrue(selectedVertexIds.contains(graph.getVertex("B").getId()));
assertTrue(selectedVertexIds.contains(graph.getVertex("D").getId()));
selectedVertices = display.getSelectedVertices();
assertEquals(2, selectedVertices.size());
assertTrue(selectedVertices.contains(b));
assertTrue(selectedVertices.contains(d));
}
@Test
public void testDeSelectVertexAction() {
display.selectVertices(Arrays.asList("A", "B", "C", "D"), EventTrigger.API_CALL);
assertEquals(4, display.getSelectedVertexIds().size());
select(a, b, c, d);
assertEquals(4, display.getSelectedVertices().size());
DockingActionIf action = getAction(tool, "Deselect Vertex");
VertexGraphActionContext context =
@ -94,18 +99,18 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getVertex("B"));
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
assertEquals(3, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains(graph.getVertex("A").getId()));
assertTrue(selectedVerticeIds.contains(graph.getVertex("D").getId()));
assertTrue(selectedVerticeIds.contains(graph.getVertex("D").getId()));
assertFalse(selectedVerticeIds.contains(graph.getVertex("B").getId()));
Set<AttributedVertex> selected = display.getSelectedVertices();
assertEquals(3, selected.size());
assertTrue(selected.contains(a));
assertTrue(selected.contains(c));
assertTrue(selected.contains(d));
assertFalse(selected.contains(b));
}
@Test
public void testSelectEdgeAction() {
assertTrue(display.getSelectedVertexIds().isEmpty());
assertTrue(display.getSelectedVertices().isEmpty());
DockingActionIf action = getAction(tool, "Select Edge");
EdgeGraphActionContext context =
@ -113,10 +118,10 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
assertEquals(2, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains(graph.getVertex("A").getId()));
assertTrue(selectedVerticeIds.contains(graph.getVertex("B").getId()));
assertTrue(selectedVerticeIds.contains(a));
assertTrue(selectedVerticeIds.contains(b));
}
@Test
@ -127,85 +132,85 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
assertEquals(2, selectedVerticeIds.size());
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(2, selectedVertices.size());
action = getAction(tool, "Deselect Edge");
performAction(action, context, true);
selectedVerticeIds = display.getSelectedVertexIds();
assertEquals(0, selectedVerticeIds.size());
selectedVertices = display.getSelectedVertices();
assertEquals(0, selectedVertices.size());
}
@Test
public void testSelectEdgeSource() {
display.setLocationFocus("D", EventTrigger.INTERNAL_ONLY);
setFocusedVertex(d);
DockingActionIf action = getAction(tool, "Edge Source");
EdgeGraphActionContext context =
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
performAction(action, context, true);
assertEquals("A", display.getFocusedVertexId());
assertEquals(a, display.getFocusedVertex());
}
@Test
public void testSelectEdgeTarget() {
display.setLocationFocus("D", EventTrigger.INTERNAL_ONLY);
setFocusedVertex(d);
DockingActionIf action = getAction(tool, "Edge Target");
EdgeGraphActionContext context =
new EdgeGraphActionContext(graphComponentProvider, graph, null, null,
graph.getEdge(graph.getVertex("A"), graph.getVertex("B")));
graph.getEdge(a, b));
performAction(action, context, true);
assertEquals("B", display.getFocusedVertexId());
assertEquals(b, display.getFocusedVertex());
}
@Test
public void testInvertSelection() {
display.selectVertices(List.of("A", "C", "E"), EventTrigger.INTERNAL_ONLY);
select(a, c, e);
DockingActionIf action = getAction(tool, "Invert Selection");
GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
assertEquals(3, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains("B"));
assertTrue(selectedVerticeIds.contains("D"));
assertTrue(selectedVerticeIds.contains("F"));
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(3, selectedVertices.size());
assertTrue(selectedVertices.contains(b));
assertTrue(selectedVertices.contains(d));
assertTrue(selectedVertices.contains(f));
}
@Test
public void testGrowSelectionOut() {
display.selectVertices(List.of("A"), EventTrigger.INTERNAL_ONLY);
select(a);
DockingActionIf action = getAction(tool, "Grow Selection To Targets");
GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
Set<AttributedVertex> selectedVerticeIds = display.getSelectedVertices();
assertEquals(3, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains("A"));
assertTrue(selectedVerticeIds.contains("B"));
assertTrue(selectedVerticeIds.contains("C"));
assertTrue(selectedVerticeIds.contains(a));
assertTrue(selectedVerticeIds.contains(b));
assertTrue(selectedVerticeIds.contains(c));
}
@Test
public void testGrowSelectionIn() {
display.selectVertices(List.of("D"), EventTrigger.INTERNAL_ONLY);
select(d);
DockingActionIf action = getAction(tool, "Grow Selection From Sources");
GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null);
performAction(action, context, true);
Set<String> selectedVerticeIds = display.getSelectedVertexIds();
assertEquals(3, selectedVerticeIds.size());
assertTrue(selectedVerticeIds.contains("D"));
assertTrue(selectedVerticeIds.contains("B"));
assertTrue(selectedVerticeIds.contains("C"));
Set<AttributedVertex> selectedVertices = display.getSelectedVertices();
assertEquals(3, selectedVertices.size());
assertTrue(selectedVertices.contains(d));
assertTrue(selectedVertices.contains(b));
assertTrue(selectedVertices.contains(c));
}
@Test
@ -216,7 +221,7 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(1, graphProviders.size());
DefaultGraphDisplayComponentProvider original = graphProviders.get(0);
display.selectVertices(List.of("B", "C", "D"), EventTrigger.INTERNAL_ONLY);
select(b, c, d);
DockingActionIf action = getAction(tool, "Create Subgraph");
GraphActionContext context =
new GraphActionContext(graphComponentProvider, graph, null, null);
@ -241,8 +246,8 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
assertFalse(contains(newGraph, "F"));
}
private boolean contains(AttributedGraph graph, String vertexId) {
return graph.getVertex(vertexId) != null;
private boolean contains(AttributedGraph g, String vertexId) {
return g.getVertex(vertexId) != null;
}
private void showGraph() throws Exception {
@ -253,6 +258,17 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
display.setGraphDisplayListener(new TestGraphDisplayListener("test"));
}
private void select(AttributedVertex... vertices) {
runSwing(() -> {
Set<AttributedVertex> vetexSet = new HashSet<>(Arrays.asList(vertices));
display.selectVertices(vetexSet, EventTrigger.INTERNAL_ONLY);
});
}
private void setFocusedVertex(AttributedVertex vertex) {
runSwing(() -> display.setFocusedVertex(vertex, EventTrigger.INTERNAL_ONLY));
}
class TestGraphDisplayListener implements GraphDisplayListener {
private String name;
@ -267,20 +283,20 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
}
@Override
public void selectionChanged(List<String> vertexIds) {
public void selectionChanged(Set<AttributedVertex> verrtices) {
StringBuilder buf = new StringBuilder();
buf.append(name);
buf.append(": selected: ");
for (String id : vertexIds) {
buf.append(id);
for (AttributedVertex vertex : verrtices) {
buf.append(vertex.getId());
buf.append(",");
}
listenerCalls.add(buf.toString());
}
@Override
public void locationFocusChanged(String vertexId) {
listenerCalls.add(name + ": focus: " + vertexId);
public void locationFocusChanged(AttributedVertex vertex) {
listenerCalls.add(name + ": focus: " + vertex.getId());
}
@Override
@ -292,12 +308,12 @@ public class GraphActionTest extends AbstractGhidraHeadedIntegrationTest {
private AttributedGraph createGraph() {
AttributedGraph g = new AttributedGraph();
AttributedVertex a = g.addVertex("A");
AttributedVertex b = g.addVertex("B");
AttributedVertex c = g.addVertex("C");
AttributedVertex d = g.addVertex("D");
AttributedVertex e = g.addVertex("E");
AttributedVertex f = g.addVertex("F");
a = g.addVertex("A");
b = g.addVertex("B");
c = g.addVertex("C");
d = g.addVertex("D");
e = g.addVertex("E");
f = g.addVertex("F");
g.addEdge(a, b);
g.addEdge(a, c);