mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-926 - Function Graph - Layouts - Created generic
layout provider to use jung layouts by name
This commit is contained in:
parent
2da717d56a
commit
c8e359ddec
28 changed files with 882 additions and 254 deletions
|
@ -25,10 +25,14 @@ eclipse.project.name = 'Features Graph FunctionGraph'
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
api project(":Base")
|
api project(":Base")
|
||||||
|
|
||||||
|
api project(":GraphServices")
|
||||||
|
|
||||||
testImplementation "org.jmockit:jmockit:1.44"
|
testImplementation "org.jmockit:jmockit:1.44"
|
||||||
|
|
||||||
helpPath project(path: ":Base", configuration: 'helpPath')
|
helpPath project(path: ":Base", configuration: 'helpPath')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functiongraph;
|
package ghidra.app.plugin.core.functiongraph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
|
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.layout.jungrapht.JgtLayoutFactory;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.layout.jungrapht.JgtNamedLayoutProvider;
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,8 +31,20 @@ public class DiscoverableFGLayoutFinder implements FGLayoutFinder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FGLayoutProvider> findLayouts() {
|
public List<FGLayoutProvider> findLayouts() {
|
||||||
List<FGLayoutProvider> instances = ClassSearcher.getInstances(FGLayoutProvider.class);
|
|
||||||
return instances;
|
List<FGLayoutProvider> list = new ArrayList<>();
|
||||||
|
|
||||||
|
// add discovered layouts
|
||||||
|
List<FGLayoutProvider> instances =
|
||||||
|
ClassSearcher.getInstances(FGLayoutProvider.class);
|
||||||
|
list.addAll(instances);
|
||||||
|
|
||||||
|
// add hand-picked, generated layout providers
|
||||||
|
List<String> jgtLayoutNames = JgtLayoutFactory.getSupportedLayoutNames();
|
||||||
|
for (String name : jgtLayoutNames) {
|
||||||
|
list.add(new JgtNamedLayoutProvider(name));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.app.plugin.core.functiongraph;
|
||||||
|
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -47,14 +46,19 @@ import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.program.util.ProgramSelection;
|
import ghidra.program.util.ProgramSelection;
|
||||||
import ghidra.util.*;
|
import ghidra.util.HelpLocation;
|
||||||
|
import ghidra.util.SystemUtilities;
|
||||||
import resources.Icons;
|
import resources.Icons;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
class FGActionManager {
|
class FGActionManager {
|
||||||
private static final String EDGE_HOVER_HIGHLIGHT = "EDGE_HOVER_HIGHLIGHT";
|
private static final String EDGE_HOVER_HIGHLIGHT = "EDGE_HOVER_HIGHLIGHT";
|
||||||
private static final String EDGE_SELECTION_HIGHLIGHT = "EDGE_SELECTION_HIGHLIGHT";
|
private static final String EDGE_SELECTION_HIGHLIGHT = "EDGE_SELECTION_HIGHLIGHT";
|
||||||
|
|
||||||
|
// save state key names
|
||||||
private static final String LAYOUT_NAME = "LAYOUT_NAME";
|
private static final String LAYOUT_NAME = "LAYOUT_NAME";
|
||||||
|
private static final String COMPLEX_LAYOUT_NAME = "COMPLEX_LAYOUT_NAME";
|
||||||
|
private static final String LAYOUT_CLASS_NAME = "LAYOUT_CLASS_NAME";
|
||||||
|
|
||||||
private static final ImageIcon EDIT_ICON = ResourceManager.loadImage("images/id.png");
|
private static final ImageIcon EDIT_ICON = ResourceManager.loadImage("images/id.png");
|
||||||
private static final ImageIcon FULL_SCREEN_ICON =
|
private static final ImageIcon FULL_SCREEN_ICON =
|
||||||
|
@ -71,7 +75,7 @@ class FGActionManager {
|
||||||
private MultiStateDockingAction<EdgeDisplayType> vertexHoverModeAction;
|
private MultiStateDockingAction<EdgeDisplayType> vertexHoverModeAction;
|
||||||
private MultiStateDockingAction<EdgeDisplayType> vertexFocusModeAction;
|
private MultiStateDockingAction<EdgeDisplayType> vertexFocusModeAction;
|
||||||
|
|
||||||
private MultiStateDockingAction<Class<? extends FGLayoutProvider>> layoutAction;
|
private MultiStateDockingAction<FGLayoutProvider> layoutAction;
|
||||||
|
|
||||||
FGActionManager(FunctionGraphPlugin plugin, FGController controller, FGProvider provider) {
|
FGActionManager(FunctionGraphPlugin plugin, FGController controller, FGProvider provider) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
@ -860,13 +864,12 @@ class FGActionManager {
|
||||||
@Override
|
@Override
|
||||||
protected void doActionPerformed(ActionContext context) {
|
protected void doActionPerformed(ActionContext context) {
|
||||||
// this callback is when the user clicks the button
|
// this callback is when the user clicks the button
|
||||||
Class<? extends FGLayoutProvider> currentUserData = getCurrentUserData();
|
FGLayoutProvider currentUserData = getCurrentUserData();
|
||||||
changeLayout(currentUserData);
|
changeLayout(currentUserData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionStateChanged(
|
public void actionStateChanged(ActionState<FGLayoutProvider> newActionState,
|
||||||
ActionState<Class<? extends FGLayoutProvider>> newActionState,
|
|
||||||
EventTrigger trigger) {
|
EventTrigger trigger) {
|
||||||
changeLayout(newActionState.getUserData());
|
changeLayout(newActionState.getUserData());
|
||||||
if (trigger != EventTrigger.API_CALL) {
|
if (trigger != EventTrigger.API_CALL) {
|
||||||
|
@ -878,34 +881,34 @@ class FGActionManager {
|
||||||
layoutAction.setSubGroup("2"); // 2 after refresh, which is 1
|
layoutAction.setSubGroup("2"); // 2 after refresh, which is 1
|
||||||
layoutAction.setHelpLocation(layoutHelpLocation);
|
layoutAction.setHelpLocation(layoutHelpLocation);
|
||||||
|
|
||||||
List<ActionState<Class<? extends FGLayoutProvider>>> actionStates =
|
// This icon will display when the action has no icon. This allows actions with no good
|
||||||
|
// icon to be blank in the menu, but to use this icon on the toolbar.
|
||||||
|
layoutAction.setDefaultIcon(ResourceManager.loadImage("images/preferences-system.png"));
|
||||||
|
|
||||||
|
List<ActionState<FGLayoutProvider>> actionStates =
|
||||||
loadActionStatesForLayoutProviders();
|
loadActionStatesForLayoutProviders();
|
||||||
|
|
||||||
for (ActionState<Class<? extends FGLayoutProvider>> actionState : actionStates) {
|
for (ActionState<FGLayoutProvider> actionState : actionStates) {
|
||||||
layoutAction.addActionState(actionState);
|
layoutAction.addActionState(actionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.addLocalAction(layoutAction);
|
provider.addLocalAction(layoutAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeLayout(Class<? extends FGLayoutProvider> layoutClass) {
|
private void changeLayout(FGLayoutProvider layout) {
|
||||||
FGLayoutProvider layoutInstance = getLayoutInstance(layoutClass);
|
controller.changeLayout(layout);
|
||||||
if (layoutInstance == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
controller.changeLayout(layoutInstance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ActionState<Class<? extends FGLayoutProvider>>> loadActionStatesForLayoutProviders() {
|
private List<ActionState<FGLayoutProvider>> loadActionStatesForLayoutProviders() {
|
||||||
|
|
||||||
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
|
List<FGLayoutProvider> layoutInstances = plugin.getLayoutProviders();
|
||||||
List<ActionState<Class<? extends FGLayoutProvider>>> list = new ArrayList<>();
|
List<ActionState<FGLayoutProvider>> list = new ArrayList<>();
|
||||||
HelpLocation layoutHelpLocation =
|
HelpLocation layoutHelpLocation =
|
||||||
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
new HelpLocation("FunctionGraphPlugin", "Function_Graph_Action_Layout");
|
||||||
for (FGLayoutProvider layout : layoutInstances) {
|
for (FGLayoutProvider layout : layoutInstances) {
|
||||||
|
|
||||||
ActionState<Class<? extends FGLayoutProvider>> layoutState = new ActionState<>(
|
ActionState<FGLayoutProvider> layoutState = new ActionState<>(
|
||||||
layout.getLayoutName(), layout.getActionIcon(), layout.getClass());
|
layout.getLayoutName(), layout.getActionIcon(), layout);
|
||||||
layoutState.setHelpLocation(layoutHelpLocation);
|
layoutState.setHelpLocation(layoutHelpLocation);
|
||||||
list.add(layoutState);
|
list.add(layoutState);
|
||||||
}
|
}
|
||||||
|
@ -913,29 +916,20 @@ class FGActionManager {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FGLayoutProvider getLayoutInstance(Class<? extends FGLayoutProvider> layoutClass) {
|
private void setLayoutActionStateByClassName(String layoutClassName, String layoutName) {
|
||||||
FGLayoutProvider layoutInstance = null;
|
|
||||||
try {
|
|
||||||
Constructor<? extends FGLayoutProvider> constructor =
|
|
||||||
layoutClass.getConstructor((Class<?>[]) null);
|
|
||||||
layoutInstance = constructor.newInstance((Object[]) null);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
Msg.showError(this, provider.getComponent(), "Unable to Create Graph Layout",
|
|
||||||
"Unable to create layout: " + layoutClass.getName(), e);
|
|
||||||
}
|
|
||||||
return layoutInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
if (layoutName == null) {
|
||||||
private void setLayoutActionStateByClassName(String layoutClassName) {
|
return; // this may be null when coming from an older version of Ghidra
|
||||||
try {
|
|
||||||
Class<?> classInstance = Class.forName(layoutClassName);
|
|
||||||
layoutAction.setCurrentActionStateByUserData(
|
|
||||||
(Class<? extends FGLayoutProvider>) classInstance);
|
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
|
||||||
// give up...leave the action the way it was
|
List<ActionState<FGLayoutProvider>> states = layoutAction.getAllActionStates();
|
||||||
|
for (ActionState<FGLayoutProvider> state : states) {
|
||||||
|
FGLayoutProvider layoutProvider = state.getUserData();
|
||||||
|
String stateLayoutName = layoutProvider.getLayoutName();
|
||||||
|
if (stateLayoutName.equals(layoutName)) {
|
||||||
|
layoutAction.setCurrentActionState(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1216,11 +1210,11 @@ class FGActionManager {
|
||||||
togglePopups.setSelected(visible);
|
togglePopups.setSelected(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCurrentActionState(ActionState<Class<? extends FGLayoutProvider>> state) {
|
void setCurrentActionState(ActionState<FGLayoutProvider> state) {
|
||||||
layoutAction.setCurrentActionState(state);
|
layoutAction.setCurrentActionState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionState<Class<? extends FGLayoutProvider>> getCurrentLayoutState() {
|
ActionState<FGLayoutProvider> getCurrentLayoutState() {
|
||||||
return layoutAction.getCurrentState();
|
return layoutAction.getCurrentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,14 +1227,26 @@ class FGActionManager {
|
||||||
vertexFocusModeAction.getCurrentState().getUserData());
|
vertexFocusModeAction.getCurrentState().getUserData());
|
||||||
vertexFocusModeAction.setCurrentActionStateByUserData(selectedState);
|
vertexFocusModeAction.setCurrentActionStateByUserData(selectedState);
|
||||||
|
|
||||||
String layoutClassName =
|
FGLayoutProvider layoutProvider = layoutAction.getCurrentUserData();
|
||||||
saveState.getString(LAYOUT_NAME, layoutAction.getCurrentUserData().getName());
|
SaveState layoutState = saveState.getSaveState(COMPLEX_LAYOUT_NAME);
|
||||||
setLayoutActionStateByClassName(layoutClassName);
|
if (layoutState != null) {
|
||||||
|
String layoutName = layoutState.getString(LAYOUT_NAME, layoutProvider.getLayoutName());
|
||||||
|
String layoutClassName =
|
||||||
|
layoutState.getString(LAYOUT_CLASS_NAME, layoutProvider.getClass().getName());
|
||||||
|
setLayoutActionStateByClassName(layoutClassName, layoutName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeConfigState(SaveState saveState) {
|
void writeConfigState(SaveState saveState) {
|
||||||
saveState.putEnum(EDGE_HOVER_HIGHLIGHT, vertexHoverModeAction.getCurrentUserData());
|
saveState.putEnum(EDGE_HOVER_HIGHLIGHT, vertexHoverModeAction.getCurrentUserData());
|
||||||
saveState.putEnum(EDGE_SELECTION_HIGHLIGHT, vertexFocusModeAction.getCurrentUserData());
|
saveState.putEnum(EDGE_SELECTION_HIGHLIGHT, vertexFocusModeAction.getCurrentUserData());
|
||||||
saveState.putString(LAYOUT_NAME, layoutAction.getCurrentUserData().getName());
|
|
||||||
|
FGLayoutProvider layoutProvider = layoutAction.getCurrentUserData();
|
||||||
|
|
||||||
|
SaveState layoutState = new SaveState(COMPLEX_LAYOUT_NAME);
|
||||||
|
String layoutName = layoutProvider.getLayoutName();
|
||||||
|
layoutState.putString(LAYOUT_NAME, layoutName);
|
||||||
|
layoutState.putString(LAYOUT_CLASS_NAME, layoutProvider.getClass().getName());
|
||||||
|
saveState.putSaveState(COMPLEX_LAYOUT_NAME, layoutState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,12 +161,10 @@ public class FunctionGraph extends GroupingVisualGraph<FGVertex, FGEdge> {
|
||||||
settings.clearVertexLocation(vertex);
|
settings.clearVertexLocation(vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO make private?
|
|
||||||
public void clearSavedVertexLocations() {
|
public void clearSavedVertexLocations() {
|
||||||
settings.clearVertexLocations(this);
|
settings.clearVertexLocations(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO make private?
|
|
||||||
public void clearAllUserLayoutSettings() {
|
public void clearAllUserLayoutSettings() {
|
||||||
clearSavedVertexLocations();
|
clearSavedVertexLocations();
|
||||||
settings.clearGroupSettings(this);
|
settings.clearGroupSettings(this);
|
||||||
|
@ -585,6 +583,15 @@ public class FunctionGraph extends GroupingVisualGraph<FGVertex, FGEdge> {
|
||||||
Collection<FGVertex> v = getVertices();
|
Collection<FGVertex> v = getVertices();
|
||||||
Collection<FGEdge> e = getEdges();
|
Collection<FGEdge> e = getEdges();
|
||||||
FunctionGraph newGraph = new FunctionGraph(getFunction(), getSettings(), v, e);
|
FunctionGraph newGraph = new FunctionGraph(getFunction(), getSettings(), v, e);
|
||||||
|
|
||||||
|
FGLayout originalLayout = getLayout();
|
||||||
|
FGLayout newLayout = originalLayout.cloneLayout(newGraph);
|
||||||
|
|
||||||
|
// setSize() must be called after setGraphLayout() due to callbacks performed when
|
||||||
|
// setSize() is called
|
||||||
|
newGraph.setGraphLayout(newLayout);
|
||||||
|
newLayout.setSize(originalLayout.getSize());
|
||||||
|
|
||||||
newGraph.setOptions(getOptions());
|
newGraph.setOptions(getOptions());
|
||||||
return newGraph;
|
return newGraph;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import javax.swing.Icon;
|
||||||
|
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public abstract class ExperimentalLayoutProvider extends FGLayoutProvider {
|
public abstract class ExperimentalLayoutProvider extends FGLayoutProviderExtensionPoint {
|
||||||
|
|
||||||
private static final Icon ICON = ResourceManager.loadImage("images/package_development.png");
|
private static final Icon ICON = ResourceManager.loadImage("images/package_development.png");
|
||||||
|
|
||||||
|
|
|
@ -13,25 +13,20 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.functiongraph.graph.jung.renderer;
|
package ghidra.app.plugin.core.functiongraph.graph.layout;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||||
import ghidra.graph.viewer.shape.ArticulatedEdgeTransformer;
|
import ghidra.graph.viewer.layout.LayoutProviderExtensionPoint;
|
||||||
import ghidra.program.model.symbol.FlowType;
|
|
||||||
|
|
||||||
public class FGArticulatedEdgeTransformer extends ArticulatedEdgeTransformer<FGVertex, FGEdge> {
|
/**
|
||||||
|
* A version of {@link FGLayoutProvider} that is discoverable at runtime. Layouts that do not wish
|
||||||
@Override
|
* to be discoverable should implement {@link FGLayoutProvider} directly, not this interface.
|
||||||
public int getOverlapOffset(FGEdge edge) {
|
*/
|
||||||
|
//@formatter:off
|
||||||
FlowType flowType = edge.getFlowType();
|
public abstract class FGLayoutProviderExtensionPoint
|
||||||
if (!flowType.isUnConditional() && flowType.isJump()) {
|
extends FGLayoutProvider
|
||||||
return -OVERLAPPING_EDGE_OFFSET;
|
implements LayoutProviderExtensionPoint<FGVertex, FGEdge, FunctionGraph> {
|
||||||
}
|
//@formatter:on
|
||||||
else if (flowType.isFallthrough()) {
|
|
||||||
return OVERLAPPING_EDGE_OFFSET;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.functiongraph.graph.layout.jungrapht;
|
||||||
|
|
||||||
|
import static ghidra.service.graph.LayoutAlgorithmNames.*;
|
||||||
|
|
||||||
|
import java.awt.Shape;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.*;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm.Builder;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.sugiyama.Layering;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.util.VertexBoundsFunctionConsumer;
|
||||||
|
import org.jungrapht.visualization.layout.model.Rectangle;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||||
|
import ghidra.graph.viewer.vertex.VisualGraphVertexShapeTransformer;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that supplies Jung graph layouts to the Function Graph API.
|
||||||
|
*
|
||||||
|
* @param <V> the vertex type
|
||||||
|
* @param <E> the edge type
|
||||||
|
*/
|
||||||
|
public class JgtLayoutFactory<V extends FGVertex, E extends FGEdge> {
|
||||||
|
|
||||||
|
private static List<String> layoutNames = List.of(
|
||||||
|
COMPACT_HIERARCHICAL,
|
||||||
|
HIERACHICAL,
|
||||||
|
MIN_CROSS_TOP_DOWN,
|
||||||
|
MIN_CROSS_LONGEST_PATH,
|
||||||
|
MIN_CROSS_NETWORK_SIMPLEX,
|
||||||
|
MIN_CROSS_COFFMAN_GRAHAM,
|
||||||
|
VERT_MIN_CROSS_TOP_DOWN,
|
||||||
|
VERT_MIN_CROSS_LONGEST_PATH,
|
||||||
|
VERT_MIN_CROSS_NETWORK_SIMPLEX,
|
||||||
|
VERT_MIN_CROSS_COFFMAN_GRAHAM);
|
||||||
|
|
||||||
|
private Predicate<E> favoredEdgePredicate;
|
||||||
|
private Comparator<E> edgeTypeComparator;
|
||||||
|
private Predicate<V> rootPredicate;
|
||||||
|
|
||||||
|
public JgtLayoutFactory(Comparator<E> comparator, Predicate<E> favoredEdgePredicate,
|
||||||
|
Predicate<V> rootPredicate) {
|
||||||
|
this.edgeTypeComparator = comparator;
|
||||||
|
this.favoredEdgePredicate = favoredEdgePredicate;
|
||||||
|
this.rootPredicate = rootPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getSupportedLayoutNames() {
|
||||||
|
return layoutNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayoutAlgorithm<V> getLayout(String name) {
|
||||||
|
|
||||||
|
Builder<V, ?, ?> layoutBuilder = doGetLayout(name);
|
||||||
|
LayoutAlgorithm<V> layout = layoutBuilder.build();
|
||||||
|
|
||||||
|
if (layout instanceof TreeLayout) {
|
||||||
|
((TreeLayout<V>) layout).setRootPredicate(rootPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout instanceof VertexBoundsFunctionConsumer) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
VertexBoundsFunctionConsumer<FGVertex> boundsLayout =
|
||||||
|
(VertexBoundsFunctionConsumer<FGVertex>) layout;
|
||||||
|
Function<FGVertex, Rectangle> vertexBoundsFunction = new FGVertexShapeFunction();
|
||||||
|
boundsLayout.setVertexBoundsFunction(vertexBoundsFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should not need to set the max level, since our graphs do not get too many vertices
|
||||||
|
// layoutAlgorithm.setMaxLevelCrossFunction(...);
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LayoutAlgorithm.Builder<V, ?, ?> doGetLayout(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case COMPACT_HIERARCHICAL:
|
||||||
|
return TidierTreeLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator);
|
||||||
|
case HIERACHICAL:
|
||||||
|
return EdgeAwareTreeLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder();
|
||||||
|
case MIN_CROSS_TOP_DOWN:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.layering(Layering.TOP_DOWN)
|
||||||
|
.threaded(false);
|
||||||
|
case MIN_CROSS_LONGEST_PATH:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.layering(Layering.LONGEST_PATH)
|
||||||
|
.threaded(false);
|
||||||
|
case MIN_CROSS_NETWORK_SIMPLEX:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.layering(Layering.NETWORK_SIMPLEX)
|
||||||
|
.threaded(false);
|
||||||
|
case MIN_CROSS_COFFMAN_GRAHAM:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.layering(Layering.COFFMAN_GRAHAM)
|
||||||
|
.threaded(false);
|
||||||
|
case VERT_MIN_CROSS_TOP_DOWN:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.favoredEdgePredicate(favoredEdgePredicate)
|
||||||
|
.layering(Layering.TOP_DOWN)
|
||||||
|
.threaded(false);
|
||||||
|
case VERT_MIN_CROSS_LONGEST_PATH:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.favoredEdgePredicate(favoredEdgePredicate)
|
||||||
|
.layering(Layering.LONGEST_PATH)
|
||||||
|
.threaded(false);
|
||||||
|
case VERT_MIN_CROSS_NETWORK_SIMPLEX:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.favoredEdgePredicate(favoredEdgePredicate)
|
||||||
|
.layering(Layering.NETWORK_SIMPLEX)
|
||||||
|
.threaded(false);
|
||||||
|
case VERT_MIN_CROSS_COFFMAN_GRAHAM:
|
||||||
|
return EiglspergerLayoutAlgorithm
|
||||||
|
.<V, E> edgeAwareBuilder()
|
||||||
|
.edgeComparator(edgeTypeComparator)
|
||||||
|
.favoredEdgePredicate(favoredEdgePredicate)
|
||||||
|
.layering(Layering.COFFMAN_GRAHAM)
|
||||||
|
.threaded(false);
|
||||||
|
default:
|
||||||
|
Msg.error(this, "Unknown graph layout type: '" + name + "'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FGVertexShapeFunction implements Function<FGVertex, Rectangle> {
|
||||||
|
|
||||||
|
private VisualGraphVertexShapeTransformer<FGVertex> vgShaper =
|
||||||
|
new VisualGraphVertexShapeTransformer<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Rectangle apply(FGVertex v) {
|
||||||
|
|
||||||
|
Shape shape = vgShaper.apply(v);
|
||||||
|
java.awt.Rectangle r = shape.getBounds();
|
||||||
|
return Rectangle.of(r.x, r.y, r.width, r.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,259 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.functiongraph.graph.layout.jungrapht;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.jgrapht.graph.AbstractBaseGraph;
|
||||||
|
import org.jgrapht.graph.DefaultGraphType;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.LayoutAlgorithm;
|
||||||
|
import org.jungrapht.visualization.layout.algorithms.util.EdgeArticulationFunctionSupplier;
|
||||||
|
import org.jungrapht.visualization.layout.model.LayoutModel;
|
||||||
|
import org.jungrapht.visualization.layout.model.Point;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.layout.AbstractFGLayout;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||||
|
import ghidra.graph.VisualGraph;
|
||||||
|
import ghidra.graph.viewer.layout.*;
|
||||||
|
import ghidra.program.model.symbol.FlowType;
|
||||||
|
import ghidra.program.model.symbol.RefType;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layout that delegates work to the Jung layout specified in the constructor.
|
||||||
|
*/
|
||||||
|
public class JgtNamedLayout extends AbstractFGLayout {
|
||||||
|
|
||||||
|
private static Function<FGEdge, List<Point>> DUMMY_ARTICULATOR = e -> Collections.emptyList();
|
||||||
|
|
||||||
|
JgtNamedLayout(FunctionGraph graph, String layoutName) {
|
||||||
|
super(graph, layoutName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractVisualGraphLayout<FGVertex, FGEdge> createClonedFGLayout(
|
||||||
|
FunctionGraph newGraph) {
|
||||||
|
return new JgtNamedLayout(newGraph, layoutName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Point2D getVertexLocation(FGVertex v, Column col, Row<FGVertex> row,
|
||||||
|
java.awt.Rectangle bounds) {
|
||||||
|
return getCenteredVertexLocation(v, col, row, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GridLocationMap<FGVertex, FGEdge> performInitialGridLayout(
|
||||||
|
VisualGraph<FGVertex, FGEdge> visualGraph) throws CancelledException {
|
||||||
|
|
||||||
|
FGEdgeComparator edgeComparator = new FGEdgeComparator();
|
||||||
|
Predicate<FGEdge> favoredEdgePredicate = getFavoredEdgePredicate();
|
||||||
|
Predicate<FGVertex> rootPredicate = null;
|
||||||
|
|
||||||
|
JgtLayoutFactory<FGVertex, FGEdge> layoutProvider =
|
||||||
|
new JgtLayoutFactory<>(edgeComparator, favoredEdgePredicate, rootPredicate);
|
||||||
|
|
||||||
|
LayoutAlgorithm<FGVertex> layout = layoutProvider.getLayout(layoutName);
|
||||||
|
|
||||||
|
FGTempGraph jGraph = buildGraph(visualGraph);
|
||||||
|
|
||||||
|
VisualGraphLayout<FGVertex, FGEdge> vgLayout = visualGraph.getLayout();
|
||||||
|
Dimension layoutSize = vgLayout.getSize();
|
||||||
|
|
||||||
|
LayoutModel<FGVertex> layoutModel =
|
||||||
|
LayoutModel.<FGVertex> builder()
|
||||||
|
.graph(jGraph)
|
||||||
|
.size(layoutSize.width, layoutSize.height)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
layoutModel.accept(layout);
|
||||||
|
|
||||||
|
GridLocationMap<FGVertex, FGEdge> grid = convertToGrid(jGraph, layoutModel, layout);
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GridLocationMap<FGVertex, FGEdge> convertToGrid(FGTempGraph jGraph,
|
||||||
|
LayoutModel<FGVertex> layoutModel,
|
||||||
|
LayoutAlgorithm<FGVertex> layoutAlgorithm)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
|
GridLocationMap<FGVertex, FGEdge> grid = new GridLocationMap<>();
|
||||||
|
|
||||||
|
Map<Double, Integer> columns = new TreeMap<>();
|
||||||
|
Map<Double, Integer> rows = new TreeMap<>();
|
||||||
|
|
||||||
|
Set<FGVertex> jungVertices = jGraph.vertexSet();
|
||||||
|
for (FGVertex fgVertex : jungVertices) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
Point point = layoutModel.get(fgVertex);
|
||||||
|
columns.put(point.x, 0);
|
||||||
|
rows.put(point.y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function<FGEdge, List<Point>> articulator = getArticulator(layoutAlgorithm);
|
||||||
|
Set<FGEdge> edges = jGraph.edgeSet();
|
||||||
|
for (FGEdge fgEdge : edges) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
List<Point> ariculations = articulator.apply(fgEdge);
|
||||||
|
for (Point point : ariculations) {
|
||||||
|
columns.put(point.x, 0);
|
||||||
|
rows.put(point.y, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate the real coordinates to grid coordinates (row and column indices)
|
||||||
|
int counter = 0;
|
||||||
|
for (Double x : columns.keySet()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
columns.put(x, counter++);
|
||||||
|
}
|
||||||
|
|
||||||
|
counter = 0;
|
||||||
|
for (Double y : rows.keySet()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
rows.put(y, counter++);
|
||||||
|
}
|
||||||
|
|
||||||
|
jungVertices = jGraph.vertexSet();
|
||||||
|
for (FGVertex fgVertex : jungVertices) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
Point point = layoutModel.get(fgVertex);
|
||||||
|
grid.set(fgVertex, rows.get(point.y), columns.get(point.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
edges = jGraph.edgeSet();
|
||||||
|
for (FGEdge fgEdge : edges) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
|
||||||
|
List<java.awt.Point> newPoints = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Point> articulations = articulator.apply(fgEdge);
|
||||||
|
for (Point point : articulations) {
|
||||||
|
|
||||||
|
Integer col = columns.get(point.x);
|
||||||
|
Integer row = rows.get(point.y);
|
||||||
|
newPoints.add(new java.awt.Point(col, row));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The jung layout will provide articulations at the vertex points. We do not want to
|
||||||
|
// use these values, since we may move the vertices during layout. Our renderer will
|
||||||
|
// connect the articulation endpoints to the vertices when drawing, so we do not need
|
||||||
|
// these points provided by jung.
|
||||||
|
if (!articulations.isEmpty()) {
|
||||||
|
newPoints.remove(0);
|
||||||
|
newPoints.remove(newPoints.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.setArticulations(fgEdge, newPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<FGEdge, List<Point>> getArticulator(
|
||||||
|
LayoutAlgorithm<FGVertex> layout) {
|
||||||
|
|
||||||
|
if (layout instanceof EdgeArticulationFunctionSupplier) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
EdgeArticulationFunctionSupplier<FGEdge> supplier =
|
||||||
|
(EdgeArticulationFunctionSupplier<FGEdge>) layout;
|
||||||
|
return supplier.getEdgeArticulationFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DUMMY_ARTICULATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FGTempGraph buildGraph(VisualGraph<FGVertex, FGEdge> visualGraph) {
|
||||||
|
|
||||||
|
FGTempGraph tempGraph = new FGTempGraph();
|
||||||
|
|
||||||
|
Collection<FGVertex> vertices = visualGraph.getVertices();
|
||||||
|
for (FGVertex v : vertices) {
|
||||||
|
tempGraph.addVertex(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<FGEdge> edges = visualGraph.getEdges();
|
||||||
|
for (FGEdge e : edges) {
|
||||||
|
tempGraph.addEdge(e.getStart(), e.getEnd(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<FGEdge> getFavoredEdgePredicate() {
|
||||||
|
return e -> e.getFlowType().equals(RefType.FALL_THROUGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FGTempGraph extends AbstractBaseGraph<FGVertex, FGEdge> {
|
||||||
|
|
||||||
|
protected FGTempGraph() {
|
||||||
|
super(null, null, DefaultGraphType.directedPseudograph());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FGEdgeComparator implements Comparator<FGEdge> {
|
||||||
|
|
||||||
|
// TODO we can update the edge priority used when layout out the graph, which is what the
|
||||||
|
// generic graphing does. In order to change edge priorities, we would have to verify
|
||||||
|
// the effects on the layout are desirable for the Function Graph.
|
||||||
|
// private Map<String, Integer> edgePriorityMap = new HashMap<>();
|
||||||
|
|
||||||
|
public FGEdgeComparator() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// populate map with RefType values; defined in priority order
|
||||||
|
|
||||||
|
int priority = 0;
|
||||||
|
edgePriorityMap.put("Fall-Through", priority++);
|
||||||
|
edgePriorityMap.put("Conditional-Return", priority++);
|
||||||
|
edgePriorityMap.put("Unconditional-Jump", priority++);
|
||||||
|
edgePriorityMap.put("Conditional-Jump", priority++);
|
||||||
|
edgePriorityMap.put("Unconditional-Call", priority++);
|
||||||
|
edgePriorityMap.put("Conditional-Call", priority++);
|
||||||
|
edgePriorityMap.put("Terminator", priority++);
|
||||||
|
edgePriorityMap.put("Computed", priority++);
|
||||||
|
edgePriorityMap.put("Indirection", priority++);
|
||||||
|
edgePriorityMap.put("Entry", priority++);
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(FGEdge e1, FGEdge e2) {
|
||||||
|
return priority(e1).compareTo(priority(e2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer priority(FGEdge e) {
|
||||||
|
FlowType type = e.getFlowType();
|
||||||
|
if (type == RefType.FALL_THROUGH) {
|
||||||
|
return 1; // lower is more preferred
|
||||||
|
}
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.plugin.core.functiongraph.graph.layout.jungrapht;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
|
||||||
|
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layout provider that allows us to specify a Jung layout by name.
|
||||||
|
*/
|
||||||
|
public class JgtNamedLayoutProvider extends FGLayoutProvider {
|
||||||
|
|
||||||
|
private String layoutName;
|
||||||
|
|
||||||
|
public JgtNamedLayoutProvider(String layoutName) {
|
||||||
|
this.layoutName = layoutName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLayoutName() {
|
||||||
|
return layoutName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getActionIcon() {
|
||||||
|
return null; // no good icon
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriorityLevel() {
|
||||||
|
// low priority than other layouts; other layouts use 200, 101 and 100
|
||||||
|
return 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FGLayout getFGLayout(FunctionGraph graph, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
JgtNamedLayout layout = new JgtNamedLayout(graph, layoutName);
|
||||||
|
layout.setTaskMonitor(monitor);
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return layoutName;
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,7 +51,6 @@ import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.OptionsService;
|
import ghidra.framework.plugintool.util.OptionsService;
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.graph.viewer.layout.LayoutProvider;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
|
@ -675,12 +674,9 @@ public class FGController implements ProgramLocationListener, ProgramSelectionLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
String previousLayoutName = previousLayout.getLayoutName();
|
||||||
Class<? extends LayoutProvider> previousLayoutClass = previousLayout.getClass();
|
String newLayoutName = newLayout.getLayoutName();
|
||||||
@SuppressWarnings("rawtypes")
|
if (previousLayoutName.equals(newLayoutName)) {
|
||||||
Class<? extends LayoutProvider> newLayoutClass = newLayout.getClass();
|
|
||||||
|
|
||||||
if (previousLayoutClass == newLayoutClass) {
|
|
||||||
view.relayout();
|
view.relayout();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -34,9 +34,13 @@ public class UngroupAllVertexFunctionGraphJob implements GraphJob {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GraphJobListener listener) {
|
public void execute(GraphJobListener listener) {
|
||||||
controller.ungroupAllVertices();
|
try {
|
||||||
isFinished = true;
|
controller.ungroupAllVertices();
|
||||||
listener.jobFinished(this);
|
}
|
||||||
|
finally {
|
||||||
|
isFinished = true;
|
||||||
|
listener.jobFinished(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/* ###
|
|
||||||
* IP: GHIDRA
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package ghidra.app.plugin.core.functiongraph.graph.jung.renderer;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
|
||||||
|
|
||||||
public class DNLArticulatedEdgeTransformer extends FGArticulatedEdgeTransformer {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOverlapOffset(FGEdge edge) {
|
|
||||||
// we position all locations ourself manually--no need to offset them to avoid overlapping
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -27,14 +27,11 @@ import org.apache.commons.collections4.BidiMap;
|
||||||
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
|
||||||
import org.apache.commons.collections4.map.LazyMap;
|
import org.apache.commons.collections4.map.LazyMap;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
import edu.uci.ics.jung.visualization.renderers.Renderer.EdgeLabel;
|
import edu.uci.ics.jung.visualization.renderers.Renderer.EdgeLabel;
|
||||||
import ghidra.app.decompiler.DecompInterface;
|
import ghidra.app.decompiler.DecompInterface;
|
||||||
import ghidra.app.decompiler.DecompileOptions;
|
import ghidra.app.decompiler.DecompileOptions;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
import ghidra.app.plugin.core.functiongraph.graph.FGEdge;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
import ghidra.app.plugin.core.functiongraph.graph.FunctionGraph;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.jung.renderer.DNLArticulatedEdgeTransformer;
|
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
import ghidra.app.plugin.core.functiongraph.graph.vertex.FGVertex;
|
||||||
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
|
import ghidra.app.plugin.core.functiongraph.graph.vertex.GroupedFunctionGraphVertex;
|
||||||
import ghidra.graph.VisualGraph;
|
import ghidra.graph.VisualGraph;
|
||||||
|
@ -102,11 +99,6 @@ public class DecompilerNestedLayout extends AbstractFGLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Function<FGEdge, Shape> getEdgeShapeTransformer() {
|
|
||||||
return new DNLArticulatedEdgeTransformer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
|
public EdgeLabel<FGVertex, FGEdge> getEdgeLabelRenderer() {
|
||||||
return new DNLEdgeLabelRenderer<>(getCondenseFactor());
|
return new DNLEdgeLabelRenderer<>(getCondenseFactor());
|
||||||
|
|
|
@ -22,7 +22,7 @@ import ghidra.framework.options.Options;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
public class DecompilerNestedLayoutProvider extends FGLayoutProvider {
|
public class DecompilerNestedLayoutProvider extends FGLayoutProviderExtensionPoint {
|
||||||
|
|
||||||
private static final Icon ICON =
|
private static final Icon ICON =
|
||||||
ResourceManager.loadImage("images/function_graph_code_flow.png");
|
ResourceManager.loadImage("images/function_graph_code_flow.png");
|
||||||
|
|
|
@ -55,6 +55,7 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
||||||
private MultipleActionDockingToolbarButton multipleButton;
|
private MultipleActionDockingToolbarButton multipleButton;
|
||||||
|
|
||||||
private boolean performActionOnPrimaryButtonClick = true;
|
private boolean performActionOnPrimaryButtonClick = true;
|
||||||
|
private Icon defaultIcon;
|
||||||
private boolean useCheckboxForIcons;
|
private boolean useCheckboxForIcons;
|
||||||
|
|
||||||
// A listener that will get called when the button (not the popup) is clicked. Toolbar
|
// A listener that will get called when the button (not the popup) is clicked. Toolbar
|
||||||
|
@ -139,6 +140,17 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
||||||
this.useCheckboxForIcons = useCheckboxForIcons;
|
this.useCheckboxForIcons = useCheckboxForIcons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the icon to use if the active action state does not supply an icon. This is useful if
|
||||||
|
* you wish for your action states to not use icon, but desire the action itself to have an
|
||||||
|
* icon.
|
||||||
|
*
|
||||||
|
* @param icon the icon
|
||||||
|
*/
|
||||||
|
public void setDefaultIcon(Icon icon) {
|
||||||
|
this.defaultIcon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void actionPerformed(ActionContext context) {
|
public final void actionPerformed(ActionContext context) {
|
||||||
if (!performActionOnPrimaryButtonClick) {
|
if (!performActionOnPrimaryButtonClick) {
|
||||||
|
@ -270,6 +282,11 @@ public abstract class MultiStateDockingAction<T> extends DockingAction {
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defaultIcon != null) {
|
||||||
|
return defaultIcon;
|
||||||
|
}
|
||||||
|
|
||||||
return EMPTY_ICON;
|
return EMPTY_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1411,6 +1411,11 @@ public class SaveState {
|
||||||
return getAsType(name, null, SaveState.class);
|
return getAsType(name, null, SaveState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return XmlUtilities.toString(saveToXml());
|
||||||
|
}
|
||||||
|
|
||||||
private <T> T getAsType(String name, T defaultValue, Class<T> clazz) {
|
private <T> T getAsType(String name, T defaultValue, Class<T> clazz) {
|
||||||
if (map.containsKey(name)) {
|
if (map.containsKey(name)) {
|
||||||
Object value = map.get(name);
|
Object value = map.get(name);
|
||||||
|
|
|
@ -88,7 +88,14 @@ public abstract class AbstractAnimatorJob implements GraphJob {
|
||||||
void start() {
|
void start() {
|
||||||
trace("start() - " + getClass().getSimpleName());
|
trace("start() - " + getClass().getSimpleName());
|
||||||
|
|
||||||
animator = createAnimator();
|
try {
|
||||||
|
animator = createAnimator();
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
Msg.error(this, "Unexepected exception creating animator", t);
|
||||||
|
emergencyFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
trace("\tcreated animator - " + animator);
|
trace("\tcreated animator - " + animator);
|
||||||
if (animator == null) {
|
if (animator == null) {
|
||||||
|
@ -143,6 +150,27 @@ public abstract class AbstractAnimatorJob implements GraphJob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void emergencyFinish() {
|
||||||
|
trace("emergencyFinish()");
|
||||||
|
if (isFinished) {
|
||||||
|
trace("\talready finished");
|
||||||
|
return; // already called
|
||||||
|
}
|
||||||
|
|
||||||
|
isFinished = true;
|
||||||
|
|
||||||
|
// a null listener implies we were shortcut before we were started
|
||||||
|
trace("\tmaybe notify finished...");
|
||||||
|
if (finishedListener != null) {
|
||||||
|
trace("\tlistener is not null--calling");
|
||||||
|
finishedListener.jobFinished(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busyListener != null) {
|
||||||
|
busyListener.setBusy(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void stop() {
|
protected void stop() {
|
||||||
trace("stop()");
|
trace("stop()");
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,12 @@ public class FitGraphToViewJob<V extends VisualVertex, E extends VisualEdge<V>>
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(GraphJobListener listener) {
|
public void execute(GraphJobListener listener) {
|
||||||
doExecute();
|
try {
|
||||||
listener.jobFinished(this);
|
doExecute();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
listener.jobFinished(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doExecute() {
|
private void doExecute() {
|
||||||
|
|
|
@ -40,7 +40,7 @@ public abstract class AbstractLayoutProvider<V extends VisualVertex,
|
||||||
E extends VisualEdge<V>,
|
E extends VisualEdge<V>,
|
||||||
G extends VisualGraph<V, E>>
|
G extends VisualGraph<V, E>>
|
||||||
|
|
||||||
implements LayoutProvider<V, E, G> {
|
implements LayoutProviderExtensionPoint<V, E, G> {
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -326,11 +326,12 @@ public abstract class AbstractVisualGraphLayout<V extends VisualVertex,
|
||||||
condenseVertices(rows, vertexLayoutLocations, transformer, centerX, centerY);
|
condenseVertices(rows, vertexLayoutLocations, transformer, centerX, centerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<E, List<Point2D>> edgeLayoutArticulations = positionEdgeArticulationsInLayoutSpace(
|
Map<E, List<Point2D>> edgeLayoutArticulations =
|
||||||
transformer, vertexLayoutLocations, edges, layoutLocations);
|
positionEdgeArticulationsInLayoutSpace(transformer, vertexLayoutLocations, edges,
|
||||||
|
layoutLocations);
|
||||||
|
|
||||||
if (isCondensed) {
|
if (isCondensed) {
|
||||||
// note: some layouts will not condense the edges, as they perform custom routing
|
// note: some layouts will not condense the edges, as they perform custom routing
|
||||||
List<Row<V>> rows = gridLocations.rows();
|
List<Row<V>> rows = gridLocations.rows();
|
||||||
condenseEdges(rows, edgeLayoutArticulations, centerX, centerY);
|
condenseEdges(rows, edgeLayoutArticulations, centerX, centerY);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,14 @@ public class Column {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + "[x=" + x + ", width=" + width + ", padded width=" +
|
|
||||||
getPaddedWidth(false) + "]";
|
//@formatter:off
|
||||||
|
return getClass().getSimpleName() + "{\n" +
|
||||||
|
"\tcolumn: " + index + ",\n" +
|
||||||
|
"\tx: " + x + ",\n" +
|
||||||
|
"\twidth: " + width + ",\n" +
|
||||||
|
"\tpadded width: " + getPaddedWidth(false) + "\n" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import javax.swing.Icon;
|
||||||
import ghidra.graph.VisualGraph;
|
import ghidra.graph.VisualGraph;
|
||||||
import ghidra.graph.viewer.VisualEdge;
|
import ghidra.graph.viewer.VisualEdge;
|
||||||
import ghidra.graph.viewer.VisualVertex;
|
import ghidra.graph.viewer.VisualVertex;
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -40,9 +39,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
public interface LayoutProvider<V extends VisualVertex,
|
public interface LayoutProvider<V extends VisualVertex,
|
||||||
E extends VisualEdge<V>,
|
E extends VisualEdge<V>,
|
||||||
G extends VisualGraph<V, E>>
|
G extends VisualGraph<V, E>> {
|
||||||
|
|
||||||
extends ExtensionPoint {
|
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.graph.viewer.layout;
|
||||||
|
|
||||||
|
import ghidra.graph.VisualGraph;
|
||||||
|
import ghidra.graph.viewer.VisualEdge;
|
||||||
|
import ghidra.graph.viewer.VisualVertex;
|
||||||
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of {@link LayoutProvider} that is discoverable at runtime. Layouts that do not wish
|
||||||
|
* to be discoverable should implement {@link LayoutProvider} directly, not this interface.
|
||||||
|
*
|
||||||
|
* @param <V> the vertex type
|
||||||
|
* @param <E> the edge type
|
||||||
|
* @param <G> the graph type
|
||||||
|
*/
|
||||||
|
//@formatter:off
|
||||||
|
public interface LayoutProviderExtensionPoint<V extends VisualVertex,
|
||||||
|
E extends VisualEdge<V>,
|
||||||
|
G extends VisualGraph<V, E>>
|
||||||
|
|
||||||
|
extends LayoutProvider<V, E, G>, ExtensionPoint {
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ import ghidra.graph.viewer.GraphViewerUtils;
|
||||||
*
|
*
|
||||||
* <p>This class maintains a collection of vertices on this row, organized by column index. You
|
* <p>This class maintains a collection of vertices on this row, organized by column index. You
|
||||||
* can get the column of a vertex from {@link #getColumn(Object) getColumn(V)}.
|
* can get the column of a vertex from {@link #getColumn(Object) getColumn(V)}.
|
||||||
|
*
|
||||||
|
* @param <V> the vertex type
|
||||||
*/
|
*/
|
||||||
public class Row<V> {
|
public class Row<V> {
|
||||||
|
|
||||||
|
@ -170,9 +172,16 @@ public class Row<V> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getSimpleName() + "[row=" + index + ", y=" + y + ", height=" + height +
|
|
||||||
", padded height=" + getPaddedHeight(false) + ", column count=" + getColumnCount() +
|
//@formatter:off
|
||||||
"]";
|
return getClass().getSimpleName() + "{\n" +
|
||||||
|
"\trow: " + index + ",\n" +
|
||||||
|
"\ty: " + y + ",\n" +
|
||||||
|
"\theight: " + height + ",\n" +
|
||||||
|
"\tpadded height: " + getPaddedHeight(false) + ",\n" +
|
||||||
|
"\tcolumn count: " + getColumnCount() + "\n" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
|
|
@ -20,19 +20,15 @@ import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
import edu.uci.ics.jung.graph.Graph;
|
import edu.uci.ics.jung.graph.Graph;
|
||||||
import edu.uci.ics.jung.visualization.Layer;
|
import edu.uci.ics.jung.visualization.Layer;
|
||||||
import edu.uci.ics.jung.visualization.RenderContext;
|
import edu.uci.ics.jung.visualization.RenderContext;
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
|
import ghidra.graph.viewer.edge.VisualEdgeRenderer;
|
||||||
import ghidra.graph.viewer.shape.ArticulatedEdgeTransformer;
|
|
||||||
|
|
||||||
public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||||
extends VisualEdgeRenderer<V, E> {
|
extends VisualEdgeRenderer<V, E> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public Shape getEdgeShape(RenderContext<V, E> rc, Graph<V, E> graph, E e, float x1, float y1,
|
public Shape getEdgeShape(RenderContext<V, E> rc, Graph<V, E> graph, E e, float x1, float y1,
|
||||||
float x2, float y2, boolean isLoop, Shape vertexShape) {
|
float x2, float y2, boolean isLoop, Shape vertexShape) {
|
||||||
|
@ -44,18 +40,14 @@ public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdg
|
||||||
GeneralPath path = new GeneralPath();
|
GeneralPath path = new GeneralPath();
|
||||||
path.moveTo(x1, y1);
|
path.moveTo(x1, y1);
|
||||||
|
|
||||||
int offset = 0;
|
// TODO investigate using the transformer directly
|
||||||
Function<? super E, Shape> edgeShapeTransformer = rc.getEdgeShapeTransformer();
|
// Function<? super E, Shape> edgeShapeTransformer = rc.getEdgeShapeTransformer();
|
||||||
if (edgeShapeTransformer instanceof ArticulatedEdgeTransformer) {
|
|
||||||
offset = ((ArticulatedEdgeTransformer<V, E>) edgeShapeTransformer).getOverlapOffset(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Point2D> articulations = e.getArticulationPoints();
|
List<Point2D> articulations = e.getArticulationPoints();
|
||||||
offset = updateOffsetForLeftOrRightHandSizeEdge(rc, offset, x1, articulations);
|
|
||||||
for (Point2D point : articulations) {
|
for (Point2D point : articulations) {
|
||||||
double x = point.getX();
|
double x = point.getX();
|
||||||
double y = point.getY();
|
double y = point.getY();
|
||||||
Point2D offsetPoint = new Point2D.Double(x + offset, y + offset);
|
Point2D offsetPoint = new Point2D.Double(x, y);
|
||||||
point = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, offsetPoint);
|
point = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, offsetPoint);
|
||||||
|
|
||||||
x = point.getX();
|
x = point.getX();
|
||||||
|
@ -69,26 +61,4 @@ public class ArticulatedEdgeRenderer<V extends VisualVertex, E extends VisualEdg
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int updateOffsetForLeftOrRightHandSizeEdge(RenderContext<V, E> rc, int offset, float x,
|
|
||||||
List<Point2D> articulations) {
|
|
||||||
|
|
||||||
int size = articulations.size();
|
|
||||||
if (size == 0) {
|
|
||||||
// no articulations or start to destination only, with no angles
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Point2D start = articulations.get(0);
|
|
||||||
start = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, start);
|
|
||||||
double delta = x - start.getX();
|
|
||||||
if (delta == 0) {
|
|
||||||
// don't move the edge when it is directly below the vertex (this prevents having
|
|
||||||
// a slightly skewed/misaligned edge)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isLeft = delta > 0;
|
|
||||||
return isLeft ? -offset : offset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ import java.awt.Shape;
|
||||||
import java.awt.geom.*;
|
import java.awt.geom.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
import ghidra.graph.viewer.*;
|
import ghidra.graph.viewer.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
@ -30,25 +31,13 @@ import ghidra.util.SystemUtilities;
|
||||||
* @param <E> the edge type
|
* @param <E> the edge type
|
||||||
*/
|
*/
|
||||||
public class ArticulatedEdgeTransformer<V extends VisualVertex, E extends VisualEdge<V>>
|
public class ArticulatedEdgeTransformer<V extends VisualVertex, E extends VisualEdge<V>>
|
||||||
extends ParallelEdgeShapeTransformer<V, E> {
|
implements Function<E, Shape> {
|
||||||
|
|
||||||
protected static final int OVERLAPPING_EDGE_OFFSET = 10;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a value by which to offset edges that overlap. This is used to make the edges
|
* Get the shape for this edge
|
||||||
* easier to see.
|
|
||||||
*
|
*
|
||||||
* @param edge the edge
|
* @param e the edge
|
||||||
* @return the offset value
|
* @return the edge shape
|
||||||
*/
|
|
||||||
public int getOverlapOffset(E edge) {
|
|
||||||
// not sure what the correct default behavior is
|
|
||||||
return OVERLAPPING_EDGE_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the shape for this edge, returning either the shared instance or, in
|
|
||||||
* the case of self-loop edges, the Loop shared instance.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Shape apply(E e) {
|
public Shape apply(E e) {
|
||||||
|
@ -76,7 +65,9 @@ public class ArticulatedEdgeTransformer<V extends VisualVertex, E extends Visual
|
||||||
final double originX = p1.getX();
|
final double originX = p1.getX();
|
||||||
final double originY = p1.getY();
|
final double originY = p1.getY();
|
||||||
|
|
||||||
int offset = getOverlapOffset(e);
|
// TODO pretty sure this is not needed; keeping for a bit as a reminder of how it used to be
|
||||||
|
// int offset = getOverlapOffset(e);
|
||||||
|
int offset = 0;
|
||||||
GeneralPath path = new GeneralPath();
|
GeneralPath path = new GeneralPath();
|
||||||
path.moveTo(0, 0);
|
path.moveTo(0, 0);
|
||||||
for (Point2D pt : articulations) {
|
for (Point2D pt : articulations) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.graph.viewer.vertex;
|
package ghidra.graph.viewer.vertex;
|
||||||
|
|
||||||
import static ghidra.graph.viewer.GraphViewerUtils.PAINT_ZOOM_THRESHOLD;
|
import static ghidra.graph.viewer.GraphViewerUtils.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
|
@ -61,8 +61,9 @@ public class VisualVertexRenderer<V extends VisualVertex, E extends VisualEdge<V
|
||||||
public void paintVertex(RenderContext<V, E> rc, Layout<V, E> layout, V vertex) {
|
public void paintVertex(RenderContext<V, E> rc, Layout<V, E> layout, V vertex) {
|
||||||
|
|
||||||
Graph<V, E> graph = layout.getGraph();
|
Graph<V, E> graph = layout.getGraph();
|
||||||
if (!rc.getVertexIncludePredicate().apply(
|
if (!rc.getVertexIncludePredicate()
|
||||||
Context.<Graph<V, E>, V> getInstance(graph, vertex))) {
|
.apply(
|
||||||
|
Context.<Graph<V, E>, V> getInstance(graph, vertex))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,58 +78,120 @@ public abstract class RefType {
|
||||||
static final byte __DYNAMICDATA = 127;
|
static final byte __DYNAMICDATA = 127;
|
||||||
|
|
||||||
public static final FlowType INVALID =
|
public static final FlowType INVALID =
|
||||||
new FlowType.Builder(__INVALID, "INVALID").setHasFall().build();
|
new FlowType.Builder(__INVALID, "INVALID")
|
||||||
|
.setHasFall()
|
||||||
|
.build();
|
||||||
public static final FlowType FLOW =
|
public static final FlowType FLOW =
|
||||||
new FlowType.Builder(__UNKNOWNFLOW, "FLOW").setHasFall().build();
|
new FlowType.Builder(__UNKNOWNFLOW, "FLOW")
|
||||||
|
.setHasFall()
|
||||||
|
.build();
|
||||||
public static final FlowType FALL_THROUGH =
|
public static final FlowType FALL_THROUGH =
|
||||||
new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH").setHasFall().build();
|
new FlowType.Builder(__FALL_THROUGH, "FALL_THROUGH")
|
||||||
|
.setHasFall()
|
||||||
|
.build();
|
||||||
public static final FlowType UNCONDITIONAL_JUMP =
|
public static final FlowType UNCONDITIONAL_JUMP =
|
||||||
new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP").setIsJump().build();
|
new FlowType.Builder(__UNCONDITIONAL_JUMP, "UNCONDITIONAL_JUMP")
|
||||||
public static final FlowType CONDITIONAL_JUMP = new FlowType.Builder(__CONDITIONAL_JUMP,
|
.setIsJump()
|
||||||
"CONDITIONAL_JUMP").setHasFall().setIsJump().setIsConditional().build();
|
.build();
|
||||||
public static final FlowType UNCONDITIONAL_CALL = new FlowType.Builder(__UNCONDITIONAL_CALL,
|
public static final FlowType CONDITIONAL_JUMP =
|
||||||
"UNCONDITIONAL_CALL").setHasFall().setIsCall().build();
|
new FlowType.Builder(__CONDITIONAL_JUMP, "CONDITIONAL_JUMP")
|
||||||
public static final FlowType CONDITIONAL_CALL = new FlowType.Builder(__CONDITIONAL_CALL,
|
.setHasFall()
|
||||||
"CONDITIONAL CALL").setHasFall().setIsCall().setIsConditional().build();
|
.setIsJump()
|
||||||
|
.setIsConditional()
|
||||||
|
.build();
|
||||||
|
public static final FlowType UNCONDITIONAL_CALL =
|
||||||
|
new FlowType.Builder(__UNCONDITIONAL_CALL, "UNCONDITIONAL_CALL")
|
||||||
|
.setHasFall()
|
||||||
|
.setIsCall()
|
||||||
|
.build();
|
||||||
|
public static final FlowType CONDITIONAL_CALL =
|
||||||
|
new FlowType.Builder(__CONDITIONAL_CALL, "CONDITIONAL_CALL")
|
||||||
|
.setHasFall()
|
||||||
|
.setIsCall()
|
||||||
|
.setIsConditional()
|
||||||
|
.build();
|
||||||
public static final FlowType TERMINATOR =
|
public static final FlowType TERMINATOR =
|
||||||
new FlowType.Builder(__TERMINATOR, "TERMINATOR").setIsTerminal().build();
|
new FlowType.Builder(__TERMINATOR, "TERMINATOR")
|
||||||
|
.setIsTerminal()
|
||||||
|
.build();
|
||||||
public static final FlowType COMPUTED_JUMP =
|
public static final FlowType COMPUTED_JUMP =
|
||||||
new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP").setIsJump().setIsComputed().build();
|
new FlowType.Builder(__COMPUTED_JUMP, "COMPUTED_JUMP")
|
||||||
|
.setIsJump()
|
||||||
|
.setIsComputed()
|
||||||
|
.build();
|
||||||
public static final FlowType CONDITIONAL_TERMINATOR =
|
public static final FlowType CONDITIONAL_TERMINATOR =
|
||||||
new FlowType.Builder(__CONDITIONAL_TERMINATOR,
|
new FlowType.Builder(__CONDITIONAL_TERMINATOR, "CONDITIONAL_TERMINATOR")
|
||||||
"CONDITIONAL_TERMINATOR").setHasFall().setIsTerminal().setIsConditional().build();
|
.setHasFall()
|
||||||
public static final FlowType COMPUTED_CALL = new FlowType.Builder(__COMPUTED_CALL,
|
.setIsTerminal()
|
||||||
"COMPUTED_CALL").setHasFall().setIsCall().setIsComputed().build();
|
.setIsConditional()
|
||||||
public static final FlowType CALL_TERMINATOR = new FlowType.Builder(__CALL_TERMINATOR,
|
.build();
|
||||||
"CALL_TERMINATOR").setIsCall().setIsTerminal().build();
|
public static final FlowType COMPUTED_CALL =
|
||||||
|
new FlowType.Builder(__COMPUTED_CALL, "COMPUTED_CALL")
|
||||||
|
.setHasFall()
|
||||||
|
.setIsCall()
|
||||||
|
.setIsComputed()
|
||||||
|
.build();
|
||||||
|
public static final FlowType CALL_TERMINATOR =
|
||||||
|
new FlowType.Builder(__CALL_TERMINATOR, "CALL_TERMINATOR")
|
||||||
|
.setIsCall()
|
||||||
|
.setIsTerminal()
|
||||||
|
.build();
|
||||||
public static final FlowType COMPUTED_CALL_TERMINATOR =
|
public static final FlowType COMPUTED_CALL_TERMINATOR =
|
||||||
new FlowType.Builder(__COMPUTED_CALL_TERMINATOR,
|
new FlowType.Builder(__COMPUTED_CALL_TERMINATOR, "COMPUTED_CALL_TERMINATOR")
|
||||||
"COMPUTED_CALL_TERMINATOR").setIsCall().setIsTerminal().setIsComputed().build();
|
.setIsCall()
|
||||||
|
.setIsTerminal()
|
||||||
|
.setIsComputed()
|
||||||
|
.build();
|
||||||
public static final FlowType CONDITIONAL_CALL_TERMINATOR =
|
public static final FlowType CONDITIONAL_CALL_TERMINATOR =
|
||||||
new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR,
|
new FlowType.Builder(__CONDITIONAL_CALL_TERMINATOR, "CONDITIONAL_CALL_TERMINATOR")
|
||||||
"CONDITIONAL_CALL_TERMINATOR").setIsCall().setIsTerminal().setIsConditional().build();
|
.setIsCall()
|
||||||
|
.setIsTerminal()
|
||||||
|
.setIsConditional()
|
||||||
|
.build();
|
||||||
public static final FlowType CONDITIONAL_COMPUTED_CALL = new FlowType.Builder(
|
public static final FlowType CONDITIONAL_COMPUTED_CALL = new FlowType.Builder(
|
||||||
__CONDITIONAL_COMPUTED_CALL,
|
__CONDITIONAL_COMPUTED_CALL, "CONDITIONAL_COMPUTED_CALL")
|
||||||
"CONDITIONAL_COMPUTED_CALL").setHasFall().setIsCall().setIsComputed().setIsConditional().build();
|
.setHasFall()
|
||||||
public static final FlowType CONDITIONAL_COMPUTED_JUMP = new FlowType.Builder(
|
.setIsCall()
|
||||||
__CONDITIONAL_COMPUTED_JUMP,
|
.setIsComputed()
|
||||||
"CONDITIONAL_COMPUTED_JUMP").setHasFall().setIsJump().setIsComputed().setIsConditional().build();
|
.setIsConditional()
|
||||||
public static final FlowType JUMP_TERMINATOR = new FlowType.Builder(__JUMP_TERMINATOR,
|
.build();
|
||||||
"JUMP_TERMINATOR").setIsJump().setIsTerminal().build();
|
public static final FlowType CONDITIONAL_COMPUTED_JUMP =
|
||||||
|
new FlowType.Builder(__CONDITIONAL_COMPUTED_JUMP, "CONDITIONAL_COMPUTED_JUMP")
|
||||||
|
.setHasFall()
|
||||||
|
.setIsJump()
|
||||||
|
.setIsComputed()
|
||||||
|
.setIsConditional()
|
||||||
|
.build();
|
||||||
|
public static final FlowType JUMP_TERMINATOR =
|
||||||
|
new FlowType.Builder(__JUMP_TERMINATOR, "JUMP_TERMINATOR")
|
||||||
|
.setIsJump()
|
||||||
|
.setIsTerminal()
|
||||||
|
.build();
|
||||||
public static final FlowType INDIRECTION =
|
public static final FlowType INDIRECTION =
|
||||||
new FlowType.Builder(__INDIRECTION, "INDIRECTION").build();
|
new FlowType.Builder(__INDIRECTION, "INDIRECTION")
|
||||||
|
.build();
|
||||||
public static final FlowType CALL_OVERRIDE_UNCONDITIONAL =
|
public static final FlowType CALL_OVERRIDE_UNCONDITIONAL =
|
||||||
new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL,
|
new FlowType.Builder(__CALL_OVERRIDE_UNCONDITIONAL, "CALL_OVERRIDE_UNCONDITIONAL")
|
||||||
"CALL_OVERRIDE_UNCONDITIONAL").setHasFall().setIsCall().setIsOverride().build();
|
.setHasFall()
|
||||||
|
.setIsCall()
|
||||||
|
.setIsOverride()
|
||||||
|
.build();
|
||||||
public static final FlowType JUMP_OVERRIDE_UNCONDITIONAL =
|
public static final FlowType JUMP_OVERRIDE_UNCONDITIONAL =
|
||||||
new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL,
|
new FlowType.Builder(__JUMP_OVERRIDE_UNCONDITIONAL, "JUMP_OVERRIDE_UNCONDITIONAL")
|
||||||
"JUMP_OVERRIDE_UNCONDITIONAL").setIsJump().setIsOverride().build();
|
.setIsJump()
|
||||||
|
.setIsOverride()
|
||||||
|
.build();
|
||||||
public static final FlowType CALLOTHER_OVERRIDE_CALL =
|
public static final FlowType CALLOTHER_OVERRIDE_CALL =
|
||||||
new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL,
|
new FlowType.Builder(__CALLOTHER_OVERRIDE_CALL, "CALLOTHER_OVERRIDE_CALL")
|
||||||
"CALLOTHER_OVERRIDE_CALL").setHasFall().setIsCall().setIsOverride().build();
|
.setHasFall()
|
||||||
|
.setIsCall()
|
||||||
|
.setIsOverride()
|
||||||
|
.build();
|
||||||
public static final FlowType CALLOTHER_OVERRIDE_JUMP =
|
public static final FlowType CALLOTHER_OVERRIDE_JUMP =
|
||||||
new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP,
|
new FlowType.Builder(__CALLOTHER_OVERRIDE_JUMP, "CALLOTHER_OVERRIDE_JUMP")
|
||||||
"CALLOTHER_OVERRIDE_JUMP").setIsJump().setIsOverride().build();
|
.setIsJump()
|
||||||
|
.setIsOverride()
|
||||||
|
.build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type is unknown.
|
* Reference type is unknown.
|
||||||
|
@ -154,30 +216,36 @@ public abstract class RefType {
|
||||||
* Reference type assigned when data is being read.
|
* Reference type assigned when data is being read.
|
||||||
*/
|
*/
|
||||||
public static final RefType READ = new DataRefType(__READ, "READ", DataRefType.READX);
|
public static final RefType READ = new DataRefType(__READ, "READ", DataRefType.READX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned when data is being written.
|
* Reference type assigned when data is being written.
|
||||||
*/
|
*/
|
||||||
public static final RefType WRITE = new DataRefType(__WRITE, "WRITE", DataRefType.WRITEX);
|
public static final RefType WRITE = new DataRefType(__WRITE, "WRITE", DataRefType.WRITEX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned when data is read and written.
|
* Reference type assigned when data is read and written.
|
||||||
*/
|
*/
|
||||||
public static final RefType READ_WRITE =
|
public static final RefType READ_WRITE =
|
||||||
new DataRefType(__READ_WRITE, "READ_WRITE", DataRefType.READX | DataRefType.WRITEX);
|
new DataRefType(__READ_WRITE, "READ_WRITE", DataRefType.READX | DataRefType.WRITEX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned when data is being read.
|
* Reference type assigned when data is being read.
|
||||||
*/
|
*/
|
||||||
public static final RefType READ_IND =
|
public static final RefType READ_IND =
|
||||||
new DataRefType(__READ_IND, "READ_IND", DataRefType.READX | DataRefType.INDX);
|
new DataRefType(__READ_IND, "READ_IND", DataRefType.READX | DataRefType.INDX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned when data is being written.
|
* Reference type assigned when data is being written.
|
||||||
*/
|
*/
|
||||||
public static final RefType WRITE_IND =
|
public static final RefType WRITE_IND =
|
||||||
new DataRefType(__WRITE_IND, "WRITE_IND", DataRefType.WRITEX | DataRefType.INDX);
|
new DataRefType(__WRITE_IND, "WRITE_IND", DataRefType.WRITEX | DataRefType.INDX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned when data is read and written.
|
* Reference type assigned when data is read and written.
|
||||||
*/
|
*/
|
||||||
public static final RefType READ_WRITE_IND = new DataRefType(__READ_WRITE_IND, "READ_WRITE_IND",
|
public static final RefType READ_WRITE_IND = new DataRefType(__READ_WRITE_IND, "READ_WRITE_IND",
|
||||||
DataRefType.READX | DataRefType.WRITEX | DataRefType.INDX);
|
DataRefType.READX | DataRefType.WRITEX | DataRefType.INDX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned for stack variable being read.
|
* Reference type assigned for stack variable being read.
|
||||||
* @deprecated use {@link RefType#READ} instead
|
* @deprecated use {@link RefType#READ} instead
|
||||||
|
@ -185,6 +253,7 @@ public abstract class RefType {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final RefType STACK_READ =
|
public static final RefType STACK_READ =
|
||||||
new DataRefType(__STACK_READ, "STACK_READ", DataRefType.READX);
|
new DataRefType(__STACK_READ, "STACK_READ", DataRefType.READX);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference type assigned for stack variable being written.
|
* Reference type assigned for stack variable being written.
|
||||||
* @deprecated use {@link RefType#WRITE} instead
|
* @deprecated use {@link RefType#WRITE} instead
|
||||||
|
@ -202,16 +271,14 @@ public abstract class RefType {
|
||||||
private byte type;
|
private byte type;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
protected RefType(byte type, String name) {
|
protected RefType(byte type, String name) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the int value for this RefType object.
|
* Get the int value for this RefType object
|
||||||
|
* @return the value
|
||||||
*/
|
*/
|
||||||
public byte getValue() {
|
public byte getValue() {
|
||||||
return type;
|
return type;
|
||||||
|
@ -219,20 +286,23 @@ public abstract class RefType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the reference is to data
|
* Returns true if the reference is to data
|
||||||
|
* @return true if the reference is to data
|
||||||
*/
|
*/
|
||||||
public boolean isData() {
|
public boolean isData() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the reference is a read.
|
* Returns true if the reference is a read
|
||||||
|
* @return true if the reference is a read
|
||||||
*/
|
*/
|
||||||
public boolean isRead() {
|
public boolean isRead() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the reference is a write.
|
* Returns true if the reference is a write
|
||||||
|
* @return true if the reference is a write
|
||||||
*/
|
*/
|
||||||
public boolean isWrite() {
|
public boolean isWrite() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -240,6 +310,7 @@ public abstract class RefType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the reference is indirect
|
* Returns true if the reference is indirect
|
||||||
|
* @return true if the reference is indirect
|
||||||
*/
|
*/
|
||||||
public boolean isIndirect() {
|
public boolean isIndirect() {
|
||||||
if (this == INDIRECTION) {
|
if (this == INDIRECTION) {
|
||||||
|
@ -249,22 +320,24 @@ public abstract class RefType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the reference is an instruction flow reference.
|
* Returns true if the reference is an instruction flow reference
|
||||||
|
* @return true if the reference is an instruction flow reference
|
||||||
*/
|
*/
|
||||||
public boolean isFlow() {
|
public boolean isFlow() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if this flow type is one that does not cause
|
* Return true if this flow type is one that does not cause a break in control flow
|
||||||
* a break in control flow.
|
* @return if this flow type is one that does not cause a break in control flow
|
||||||
*/
|
*/
|
||||||
public final boolean isFallthrough() {
|
public final boolean isFallthrough() {
|
||||||
return this == FALL_THROUGH;
|
return this == FALL_THROUGH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this flow type can fall through.
|
* Returns true if this flow type can fall through
|
||||||
|
* @return true if can fall through
|
||||||
*/
|
*/
|
||||||
public boolean hasFallthrough() {
|
public boolean hasFallthrough() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -272,6 +345,7 @@ public abstract class RefType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the flow is call
|
* Returns true if the flow is call
|
||||||
|
* @return true if is a call
|
||||||
*/
|
*/
|
||||||
public boolean isCall() {
|
public boolean isCall() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -279,50 +353,60 @@ public abstract class RefType {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the flow is jump
|
* Returns true if the flow is jump
|
||||||
|
* @return true if is a jump
|
||||||
*/
|
*/
|
||||||
public boolean isJump() {
|
public boolean isJump() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the flow is an unconditional call or jump.
|
* Returns true if the flow is an unconditional call or jump
|
||||||
|
* @return true if unconditional
|
||||||
*/
|
*/
|
||||||
public boolean isUnConditional() {
|
public boolean isUnConditional() {
|
||||||
return !isConditional();
|
return !isConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the flow is a conditional call or jump.
|
* Returns true if the flow is a conditional call or jump
|
||||||
|
* @return true if is conditional
|
||||||
*/
|
*/
|
||||||
public boolean isConditional() {
|
public boolean isConditional() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the flow is a computed call or compute jump.
|
* Returns true if the flow is a computed call or compute jump
|
||||||
|
* @return true if is computed
|
||||||
*/
|
*/
|
||||||
public boolean isComputed() {
|
public boolean isComputed() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns true if this instruction terminates.
|
* Returns true if this instruction terminates
|
||||||
|
* @return true if terminal
|
||||||
*/
|
*/
|
||||||
public boolean isTerminal() {
|
public boolean isTerminal() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* True if this is an override reference
|
||||||
* @return true precisely when the reference is an overriding reference
|
* @return true if this is an override reference
|
||||||
*/
|
*/
|
||||||
public boolean isOverride() {
|
public boolean isOverride() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see java.lang.Object#equals(java.lang.Object)
|
* Returns name of ref-type
|
||||||
|
* @return the name
|
||||||
*/
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null || !getClass().equals(obj.getClass())) {
|
if (obj == null || !getClass().equals(obj.getClass())) {
|
||||||
|
@ -332,28 +416,13 @@ public abstract class RefType {
|
||||||
return type == other.type;
|
return type == other.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.lang.Object#hashCode()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @see java.lang.Object#toString()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns name of ref-type
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue