mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-21 08:21:34 +08:00
Merge remote-tracking branch
'origin/GP-6748_ghidragon_decompiler_slow_workarounds' (#5730)
This commit is contained in:
@@ -41,6 +41,7 @@ icon.decompiler.action.provider.clone = icon.provider.clone
|
||||
icon.decompiler.action.provider.unreachable = eliminateUnreachable.png
|
||||
icon.decompiler.action.provider.readonly = readOnly.png
|
||||
icon.decompiler.action.export = page_edit.png
|
||||
icon.decompiler.action.display.lock = lock.gif
|
||||
|
||||
font.decompiler = font.monospaced
|
||||
font.decompiler.pcode.dfg = font.graphdisplay.default
|
||||
|
||||
@@ -4009,7 +4009,7 @@
|
||||
tokens (see <xref linkend="MouseActions"/>).
|
||||
</para>
|
||||
|
||||
<sect2 id="CrossHighlighting">
|
||||
<sect2 id="CrossHighlighting">
|
||||
<title>Cross-Highlighting</title>
|
||||
<para>
|
||||
The main window maintains a map between the individual variable and operator tokens displayed in
|
||||
@@ -4045,6 +4045,25 @@
|
||||
</sect2>
|
||||
</section>
|
||||
|
||||
<section id="LockDisplay">
|
||||
<title>Disabling Auto Refresh</title>
|
||||
<para>
|
||||
For very large functions, the decompile window can take a significant amount of time to re-decompile
|
||||
and update the display as changes are made to the program. This makes some actions frustrating,
|
||||
such as renaming or retyping variables.
|
||||
</para>
|
||||
<para>
|
||||
As a work around, you may use the lock toggle action
|
||||
<imageobject>
|
||||
<imagedata condition="noscaling" fileref="images/lock.gif" contentwidth="16px" contentdepth="16px"/>
|
||||
<imagedata condition="withscaling" fileref="images/lock.gif" contentwidth="0.15in" contentdepth="0.15in"/>
|
||||
</imageobject> .
|
||||
When selected, this action will prevent the decompiler from auto refreshing for each change.
|
||||
This may make the display appear to be broken since any changes will not appear to
|
||||
take effect until the display is manually refreshed.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="Snapshot">
|
||||
<title>Snapshot Windows</title>
|
||||
<para>
|
||||
@@ -4085,6 +4104,18 @@
|
||||
Double-clicking on specific tokens within the Snapshot window may also cause it to navigate
|
||||
to a new location (see <xref linkend="MouseDouble"/>).
|
||||
</para>
|
||||
<anchor id="EventsOut"/>
|
||||
<para>
|
||||
Normally, snapshot windows are completely disconnected from the location and selection
|
||||
events that synchronize the main components in the tool. To allow the snapshot window
|
||||
to export its location and selection, select the
|
||||
<imageobject>
|
||||
<imagedata condition="noscaling" fileref="images/locationOut.gif" contentwidth="16px" contentdepth="16px"/>
|
||||
<imagedata condition="withscaling" fileref="images/locationOut.gif" contentwidth="0.15in" contentdepth="0.15in"/>
|
||||
</imageobject>
|
||||
toolbar action.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="UndefinedFunction">
|
||||
|
||||
+26
-1
@@ -101,7 +101,7 @@
|
||||
tokens (see <a class="xref" href="DecompilerWindow.html#MouseActions" title="Mouse Actions">Mouse Actions</a>).
|
||||
</p>
|
||||
|
||||
<div class="sect2">
|
||||
<div class="sect2">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="CrossHighlighting"></a>Cross-Highlighting</h3></div></div></div>
|
||||
|
||||
@@ -145,6 +145,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
|
||||
<a name="LockDisplay"></a>Disabling Auto Refresh</h2></div></div></div>
|
||||
|
||||
<p>
|
||||
For very large functions, the decompile window can take a significant amount of time to re-decompile
|
||||
and update the display as changes are made to the program. This makes using some actions
|
||||
frustrating, such as renaming or retyping variables.
|
||||
</p>
|
||||
<p>
|
||||
As a work around, you may use the lock toggle action
|
||||
<img src="images/lock.gif" width="16" height="16">. When selected, this action will prevent
|
||||
the decompiler from auto refreshing for each change. This may make the display appear to be
|
||||
broken since any changes will not appear to take effect until the display is manually refreshed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
|
||||
<a name="Snapshot"></a>Snapshot Windows</h2></div></div></div>
|
||||
@@ -187,6 +204,14 @@
|
||||
Double-clicking on specific tokens within the Snapshot window may also cause it to navigate
|
||||
to a new location (see <a class="xref" href="DecompilerWindow.html#MouseDouble" title="Double-Click">Double-Click</a>).
|
||||
</p>
|
||||
<a name="EventsOut"></a>
|
||||
<p>
|
||||
Normally, snapshot windows are completely disconnected from the location and selection
|
||||
events that synchronize the main components in the tool. To allow the snapshot window
|
||||
to export its location and selection, select the
|
||||
<img src="images/locationOut.gif" width="16" height="16"> toolbar action.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 50 KiB |
+17
@@ -1388,6 +1388,23 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
fieldPanel.removeFocusListener(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the bounds of the content area of this decompiler panel. This includes the main
|
||||
* decompiler content panel and the line numbers panel}
|
||||
*/
|
||||
public Rectangle getViewContentBounds() {
|
||||
// The bounds we want includes both the extent size of the main decompiler view + the
|
||||
// area that displays the line numbers which is not inside the IndexedScrollPane. The width
|
||||
// of the line numbers panel can be found by looking at the x position of the scroller as
|
||||
// it is offset by the line number panel's width. We are also assuming there are no borders
|
||||
// internal to the DecompilerPanel. If that changes, we would also need to factor in the
|
||||
// insets.
|
||||
Rectangle bounds = scroller.getBounds();
|
||||
Dimension scrollerSize = scroller.getViewExtentSize();
|
||||
int lineNumberWidth = bounds.x;
|
||||
return new Rectangle(0, 0, scrollerSize.width + lineNumberWidth, scrollerSize.height);
|
||||
}
|
||||
|
||||
private void buildPanels() {
|
||||
removeAll();
|
||||
add(buildLeftComponent(), BorderLayout.WEST);
|
||||
|
||||
+2
-2
@@ -207,13 +207,13 @@ public class DecompilePlugin extends Plugin {
|
||||
}
|
||||
|
||||
void locationChanged(DecompilerProvider provider, ProgramLocation location) {
|
||||
if (provider == connectedProvider) {
|
||||
if (provider.shouldSendEvents()) {
|
||||
firePluginEvent(new ProgramLocationPluginEvent(name, location, location.getProgram()));
|
||||
}
|
||||
}
|
||||
|
||||
void selectionChanged(DecompilerProvider provider, ProgramSelection selection) {
|
||||
if (provider == connectedProvider) {
|
||||
if (provider.shouldSendEvents()) {
|
||||
firePluginEvent(new ProgramSelectionPluginEvent(name, selection, currentProgram));
|
||||
}
|
||||
}
|
||||
|
||||
+91
-24
@@ -15,17 +15,20 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.*;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.ViewerPosition;
|
||||
import generic.theme.GIcon;
|
||||
@@ -76,6 +79,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
|
||||
private static final Icon TOGGLE_READ_ONLY_DISABLED_ICON =
|
||||
new MultiIconBuilder(TOGGLE_READ_ONLY_ICON).addCenteredIcon(SLASH_ICON).build();
|
||||
private static final Icon LOCK_DISPLAY_ICON = new GIcon("icon.decompiler.action.display.lock");
|
||||
|
||||
private DockingAction pcodeGraphAction;
|
||||
private DockingAction astGraphAction;
|
||||
@@ -100,11 +104,16 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
|
||||
private SwingUpdateManager redecompileUpdater;
|
||||
private DecompilerProgramListener programListener;
|
||||
|
||||
private boolean lockDisplay;
|
||||
// Follow-up work can be items that need to happen after a pending decompile is finished, such
|
||||
// as updating highlights after a variable rename
|
||||
private SwingUpdateManager followUpWorkUpdater;
|
||||
private Queue<Callback> followUpWork = new ConcurrentLinkedQueue<>();
|
||||
private OverlayMessagePainter overlayPainter = new OverlayMessagePainter();
|
||||
private DockingAction refreshAction;
|
||||
|
||||
// only used by disconnected providers
|
||||
private boolean allowOutgoingEvents = false;
|
||||
|
||||
private ServiceListener serviceListener = new ServiceListener() {
|
||||
|
||||
@@ -140,7 +149,14 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
// TODO move the hl controller into the panel
|
||||
highlightController = new LocationClangHighlightController();
|
||||
decompilerPanel.setHighlightController(highlightController);
|
||||
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected);
|
||||
decorationPanel = new DecoratorPanel(decompilerPanel, isConnected) {
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paint(g);
|
||||
overlayPainter.paintOverlay(g, decompilerPanel.getViewContentBounds());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (!isConnected) {
|
||||
setTransient();
|
||||
@@ -333,7 +349,28 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
controller.setOptions(decompilerOptions);
|
||||
|
||||
if (currentLocation != null) {
|
||||
controller.refreshDisplay(program, currentLocation, null);
|
||||
if (lockDisplay) {
|
||||
overlayPainter.setMessage(getOverlayRefreshMessage());
|
||||
}
|
||||
else {
|
||||
controller.refreshDisplay(program, currentLocation, null);
|
||||
overlayPainter.setMessage("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getOverlayRefreshMessage() {
|
||||
KeyStroke keyStroke = refreshAction.getKeyBinding();
|
||||
if (keyStroke != null) {
|
||||
String name = KeyBindingUtils.parseKeyStroke(keyStroke);
|
||||
return name + " to refresh";
|
||||
}
|
||||
return "Refresh needed";
|
||||
}
|
||||
|
||||
private void updateOverlayMessage() {
|
||||
if (overlayPainter.isActive()) {
|
||||
overlayPainter.setMessage(getOverlayRefreshMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,6 +408,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
options.getName().equals(GhidraOptions.CATEGORY_BROWSER_FIELDS)) {
|
||||
doRefresh(true);
|
||||
}
|
||||
updateOverlayMessage();
|
||||
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
@@ -489,6 +528,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
*/
|
||||
void refresh() {
|
||||
controller.refreshDisplay(program, currentLocation, null);
|
||||
overlayPainter.setMessage("");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -790,28 +830,35 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
private void createActions(boolean isConnected) {
|
||||
String owner = plugin.getName();
|
||||
|
||||
new ToggleActionBuilder("Lock Display", owner)
|
||||
.toolBarIcon(LOCK_DISPLAY_ICON)
|
||||
.description("Lock display for auto-updates, only update on manual refresh")
|
||||
.helpLocation(new HelpLocation(HelpTopics.DECOMPILER, "LockDisplay"))
|
||||
.selected(false)
|
||||
.onAction(c -> toggleDisplayLock())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
if (!isConnected) {
|
||||
new ToggleActionBuilder("Decompiler Outgoing Events", owner)
|
||||
.toolBarIcon(Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON)
|
||||
.description("Send location and selection events")
|
||||
.helpLocation(new HelpLocation(HelpTopics.DECOMPILER, "EventsOut"))
|
||||
.selected(false)
|
||||
.onAction(c -> toggleOutgoingEvents())
|
||||
.buildAndInstallLocal(this);
|
||||
}
|
||||
|
||||
SelectAllAction selectAllAction =
|
||||
new SelectAllAction(owner, controller.getDecompilerPanel());
|
||||
|
||||
DockingAction refreshAction = new DockingAction("Refresh", owner) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
DecompileData decompileData = controller.getDecompileData();
|
||||
if (decompileData == null) {
|
||||
return false;
|
||||
}
|
||||
return decompileData.hasDecompileResults();
|
||||
}
|
||||
};
|
||||
refreshAction.setToolBarData(new ToolBarData(REFRESH_ICON, "A" /* first on toolbar */));
|
||||
refreshAction.setDescription("Push at any time to trigger a re-decompile");
|
||||
refreshAction
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ToolBarRedecompile")); // just use the default
|
||||
refreshAction = new ActionBuilder("Refresh", owner)
|
||||
.popupMenuPath("Refresh")
|
||||
.popupMenuIcon(REFRESH_ICON)
|
||||
.keyBinding("F5")
|
||||
.helpLocation(new HelpLocation(HelpTopics.DECOMPILER, "ToolBarRedecompile"))
|
||||
.description("Re-decompile and update the display")
|
||||
.onAction(c -> refresh())
|
||||
.buildAndInstallLocal(this);
|
||||
|
||||
displayUnreachableCodeToggle = new ToggleDockingAction("Toggle Unreachable Code", owner) {
|
||||
@Override
|
||||
@@ -1120,6 +1167,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
findReferencesToAddressAction.getPopupMenuData().setParentMenuGroup(referencesParentGroup);
|
||||
addLocalAction(findReferencesToAddressAction);
|
||||
|
||||
setGroupInfo(refreshAction, "comment6", subGroupPosition++);
|
||||
|
||||
//
|
||||
// Options
|
||||
//
|
||||
@@ -1200,6 +1249,24 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
graphServiceAdded();
|
||||
}
|
||||
|
||||
private void toggleOutgoingEvents() {
|
||||
allowOutgoingEvents = !allowOutgoingEvents;
|
||||
}
|
||||
|
||||
boolean shouldSendEvents() {
|
||||
if (isConnected()) {
|
||||
return true;
|
||||
}
|
||||
return allowOutgoingEvents;
|
||||
}
|
||||
|
||||
private void toggleDisplayLock() {
|
||||
lockDisplay = !lockDisplay;
|
||||
if (!lockDisplay) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the group and subgroup information for the given action.
|
||||
*/
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/* ###
|
||||
* 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.decompile;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import generic.theme.GColor;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import generic.theme.Gui;
|
||||
|
||||
/**
|
||||
* Class to overlay a message on the decompiler panel to indicate the display is stale and
|
||||
* needs to be refreshed manually.
|
||||
*/
|
||||
class OverlayMessagePainter {
|
||||
private static final int MARGIN = 10;
|
||||
private static final String FONT_ID = "font.graph.component.message";
|
||||
private final Color gradientColor = new GColor("color.bg.visualgraph.message");
|
||||
private String message;
|
||||
|
||||
void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
boolean isActive() {
|
||||
return !StringUtils.isBlank(message);
|
||||
}
|
||||
|
||||
void paintOverlay(Graphics g, Rectangle bounds) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
|
||||
// this composite softens the text and color of the message
|
||||
Composite originalComposite = g2.getComposite();
|
||||
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SrcOver.getRule(), .60f));
|
||||
|
||||
// set up font
|
||||
Font font = Gui.getFont(FONT_ID);
|
||||
g.setFont(font);
|
||||
Rectangle textBounds = font.getStringBounds(message, g2.getFontRenderContext()).getBounds();
|
||||
|
||||
int gh = textBounds.height * 3;
|
||||
int gy = bounds.height - gh;
|
||||
paintGradient(g2, 0, gy, bounds.width, gh);
|
||||
|
||||
// paint message
|
||||
g2.setPaint(Palette.BLACK);
|
||||
int textX = bounds.width - textBounds.width - MARGIN;
|
||||
int textY = bounds.height - textBounds.height / 2; //text at bottom; account for baseline
|
||||
g2.drawString(message, textX, textY);
|
||||
|
||||
g2.setComposite(originalComposite);
|
||||
}
|
||||
|
||||
private void paintGradient(Graphics2D g2, int x, int y, int w, int h) {
|
||||
Color[] colors = new Color[] { Color.WHITE, gradientColor };
|
||||
float[] fractions = new float[] { 0.0f, .95f };
|
||||
LinearGradientPaint gradiantPaint =
|
||||
new LinearGradientPaint(new Point(x, y), new Point(x, y + h), fractions, colors);
|
||||
g2.setPaint(gradiantPaint);
|
||||
g2.fillRect(x, y, w, h);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1300,6 +1300,9 @@ public abstract class AbstractDockingTest extends AbstractGuiTest {
|
||||
assertNotNull("Action cannot be null", action);
|
||||
assertNotNull("Action context cannot be null", context);
|
||||
|
||||
boolean isValid = runSwing(() -> action.isValidContext(context));
|
||||
assertTrue("Attempted to invoke action with invalid context", isValid);
|
||||
|
||||
runSwing(() -> {
|
||||
|
||||
action.isAddToPopup(context);
|
||||
|
||||
+22
-1
@@ -119,6 +119,10 @@ public class IndexedScrollPane extends JPanel implements IndexScrollListener {
|
||||
return new Dimension(comp.getPreferredSize().width, indexMapper.getViewHeight());
|
||||
}
|
||||
|
||||
public Dimension getViewExtentSize() {
|
||||
return viewport.getExtentSize();
|
||||
}
|
||||
|
||||
public void viewportStateChanged() {
|
||||
Dimension extentSize = viewport.getExtentSize();
|
||||
if (!extentSize.equals(visibleSize)) {
|
||||
@@ -241,7 +245,24 @@ public class IndexedScrollPane extends JPanel implements IndexScrollListener {
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportWidth() {
|
||||
return false;
|
||||
int prefWidth = comp.getPreferredSize().width;
|
||||
int scrollPaneWidth = getScrollPaneWidth();
|
||||
return scrollPaneWidth > prefWidth;
|
||||
}
|
||||
|
||||
private int getScrollPaneWidth() {
|
||||
Container myParent = getParent();
|
||||
if (myParent == null) {
|
||||
return 0;
|
||||
}
|
||||
if (myParent instanceof JViewport vp) {
|
||||
return vp.getExtentSize().width;
|
||||
}
|
||||
Container grandParent = myParent.getParent();
|
||||
if (grandParent == null) {
|
||||
return 0;
|
||||
}
|
||||
return grandParent.getSize().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+30
-49
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -24,9 +24,13 @@ import org.junit.Test;
|
||||
|
||||
import docking.ComponentProvider;
|
||||
import docking.DockableComponent;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesProvider;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||
import ghidra.app.plugin.core.programtree.ViewManagerComponentProvider;
|
||||
|
||||
public class DecompilePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
@@ -115,52 +119,6 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
image = tf.getImage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackwardSlice() {
|
||||
TextFormatter tf = new TextFormatter(16, 500, 4, 5, 0);
|
||||
TextFormatterContext hl = new TextFormatterContext(Palette.BLACK, Palette.YELLOW);
|
||||
TextFormatterContext red = new TextFormatterContext(Palette.RED, Palette.WHITE);
|
||||
TextFormatterContext blue = new TextFormatterContext(Palette.BLUE, Palette.WHITE);
|
||||
TextFormatterContext green = new TextFormatterContext(Palette.GREEN, Palette.WHITE);
|
||||
TextFormatterContext greenhl = new TextFormatterContext(Palette.GREEN, Palette.YELLOW);
|
||||
TextFormatterContext cursorhl =
|
||||
new TextFormatterContext(Palette.BLACK, Palette.YELLOW, Palette.RED);
|
||||
|
||||
tf.writeln(" |a| = |psParm2|->id;", hl, hl);
|
||||
tf.writeln(" b = |max_alpha|(|psParm1|->next,|psParm1|->id);", red, hl, hl);
|
||||
tf.writeln(" c = |max_beta|(psParm1->prev, |a|);", red, hl);
|
||||
tf.writeln(" c = c + b;");
|
||||
tf.writeln(" dStack8 = |0|;", green);
|
||||
tf.writeln(" |while| (psParm1->count != dStack8 && (sdword)dStack8) {", blue);
|
||||
tf.writeln(" |if| (c < (sdword)(dStack8 + b)) {", blue);
|
||||
tf.writeln(" c = c + |a|;", hl);
|
||||
tf.writeln(" }");
|
||||
tf.writeln(" |else| {", blue);
|
||||
tf.writeln(" |a| = |a| + |10|;", hl, hl, greenhl);
|
||||
tf.writeln(" }");
|
||||
tf.writeln(" dStack8 = dStack8 + |1|;", green);
|
||||
tf.writeln(" }");
|
||||
tf.writeln(" psParm1->count = |a| + c;", cursorhl);
|
||||
tf.writeln(" |return|;", blue);
|
||||
|
||||
image = tf.getImage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStructnotapplied() {
|
||||
Image listingImage = getListingImage();
|
||||
Image decompImage = getDecompilerNoStructImage();
|
||||
int listingWidth = listingImage.getWidth(null);
|
||||
int decompWidth = decompImage.getWidth(null);
|
||||
int height = Math.max(listingImage.getHeight(null), decompImage.getHeight(null));
|
||||
BufferedImage combined = createEmptyImage(listingWidth + decompWidth, height);
|
||||
Graphics2D g = combined.createGraphics();
|
||||
g.drawImage(listingImage, 0, 0, null);
|
||||
g.drawImage(decompImage, listingWidth, 0, null);
|
||||
g.dispose();
|
||||
image = combined;
|
||||
}
|
||||
|
||||
public void testStructApplied() {
|
||||
Image listingImage = getListingImage();
|
||||
Image decompImage = getDecompilerStructAppliedImage();
|
||||
@@ -177,14 +135,37 @@ public class DecompilePluginScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
@Test
|
||||
public void testEditFunctionSignature() {
|
||||
DecompilerProvider provider = (DecompilerProvider) getProvider("Decompiler");
|
||||
showProvider(provider.getClass());
|
||||
goToListing(0x401040);
|
||||
ComponentProvider provider = getProvider("Decompiler");
|
||||
int line = 2; // function signature line
|
||||
int charPos = 15; // function name
|
||||
setDecompilerLocation(provider, line, charPos);
|
||||
showProvider(provider.getClass());
|
||||
waitForSwing();
|
||||
performAction("Edit Function Signature", "DecompilePlugin", provider, false);
|
||||
captureDialog();
|
||||
}
|
||||
|
||||
private void setDecompilerLocation(DecompilerProvider provider, int line, int charPosition) {
|
||||
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
FieldPanel fp = panel.getFieldPanel();
|
||||
FieldLocation loc = loc(line, charPosition);
|
||||
|
||||
// scroll to the field to make sure it has been built so that we can get its point
|
||||
fp.scrollTo(loc);
|
||||
Point p = fp.getPointForLocation(loc);
|
||||
|
||||
click(fp, p, 1, true);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private FieldLocation loc(int lineNumber, int col) {
|
||||
FieldLocation loc = new FieldLocation(lineNumber - 1, 0, 0, col);
|
||||
return loc;
|
||||
}
|
||||
|
||||
private Image getListingImage() {
|
||||
Font font = new Font("Monospaced", Font.PLAIN, 12);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user