mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-12 23:23:17 +00:00
Merge remote-tracking branch 'origin/GT-2732-dragonmacher'
This commit is contained in:
commit
d3b2ef3928
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.hover;
|
||||
|
||||
import static ghidra.util.HTMLUtilities.HTML;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
@ -24,7 +26,7 @@ import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToHelper;
|
||||
import ghidra.app.services.CodeFormatService;
|
||||
import ghidra.app.util.ToolTipUtils;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -32,6 +34,7 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.*;
|
||||
import ghidra.util.HTMLUtilities;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
|
||||
/**
|
||||
@ -131,7 +134,6 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
|
||||
if (!enabled || programLocation == null || panel == null) {
|
||||
return null;
|
||||
}
|
||||
panel.setProgram(program);
|
||||
|
||||
Address refAddr = programLocation.getRefAddress();
|
||||
if (refAddr != null && refAddr.isExternalAddress()) {
|
||||
@ -140,22 +142,24 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
|
||||
|
||||
previewLocation =
|
||||
getPreviewLocation(program, programLocation, programLocation.getRefAddress());
|
||||
|
||||
if (previewLocation != null) {
|
||||
boolean toolTipForLocation = panel.goTo(previewLocation);
|
||||
|
||||
// only continue if there was a valid location to go to
|
||||
if (toolTipForLocation) {
|
||||
|
||||
Rectangle bounds = panel.getBounds();
|
||||
bounds.x = WINDOW_OFFSET;
|
||||
bounds.y = WINDOW_OFFSET;
|
||||
panel.setBounds(bounds);
|
||||
return panel;
|
||||
}
|
||||
if (previewLocation == null) {
|
||||
return null;
|
||||
}
|
||||
panel.setProgram(null);
|
||||
return null;
|
||||
|
||||
boolean validLocation = panel.goTo(previewLocation);
|
||||
if (validLocation) {
|
||||
panel.setProgram(program);
|
||||
|
||||
Rectangle bounds = panel.getBounds();
|
||||
bounds.x = WINDOW_OFFSET;
|
||||
bounds.y = WINDOW_OFFSET;
|
||||
panel.setBounds(bounds);
|
||||
return panel;
|
||||
}
|
||||
|
||||
// At this point we have a program location, but we could not go there. This can happen
|
||||
// if the location is not in memory.
|
||||
return createOutOfMemoryToolTipComponent(previewLocation);
|
||||
}
|
||||
|
||||
protected JComponent createExternalToolTipComponent(Program program, Address extAddr) {
|
||||
@ -183,6 +187,60 @@ public abstract class AbstractReferenceHover extends AbstractConfigurableHover {
|
||||
return toolTip;
|
||||
}
|
||||
|
||||
protected JComponent createOutOfMemoryToolTipComponent(ProgramLocation location) {
|
||||
|
||||
/*
|
||||
Format
|
||||
|
||||
Address: ram:1234
|
||||
Address not in memory
|
||||
|
||||
|
||||
Or, when multiple symbols at destination
|
||||
|
||||
|
||||
Address: ram:1234
|
||||
Symbols (3):
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
Address not in memory
|
||||
|
||||
*/
|
||||
|
||||
String newline = HTMLUtilities.HTML_NEW_LINE;
|
||||
StringBuilder buffy = new StringBuilder(HTML);
|
||||
buffy.append("Address: ");
|
||||
String addressString = location.getAddress().toString(true, false);
|
||||
addressString = HTMLUtilities.friendlyEncodeHTML(addressString);
|
||||
buffy.append(addressString);
|
||||
buffy.append(newline);
|
||||
|
||||
Program p = location.getProgram();
|
||||
SymbolTable st = p.getSymbolTable();
|
||||
Symbol[] symbols = st.getSymbols(location.getAddress());
|
||||
|
||||
if (symbols.length > 1) {
|
||||
buffy.append("Symbols (").append(symbols.length).append("): ").append(newline);
|
||||
String pad = HTMLUtilities.spaces(4);
|
||||
SymbolInspector inspector = new SymbolInspector(tool, null);
|
||||
for (Symbol s : symbols) {
|
||||
ColorAndStyle style = inspector.getColorAndStyle(s);
|
||||
String name = s.getName(true);
|
||||
name = HTMLUtilities.friendlyEncodeHTML(pad);
|
||||
String html = style.toHtml(name);
|
||||
buffy.append(pad).append(html).append(newline);
|
||||
}
|
||||
}
|
||||
|
||||
String message = "Address not in memory";
|
||||
message = HTMLUtilities.italic(message);
|
||||
message = HTMLUtilities.colorString(Color.GRAY, message);
|
||||
buffy.append(message);
|
||||
toolTip.setTipText(buffy.toString());
|
||||
return toolTip;
|
||||
}
|
||||
|
||||
public void programClosed(Program program) {
|
||||
if (panel != null && panel.getProgram() == program) {
|
||||
panel.setProgram(null);
|
||||
|
@ -16,6 +16,9 @@
|
||||
package ghidra.app.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
||||
/**
|
||||
* A container class to hold a color and a style value.
|
||||
@ -37,4 +40,36 @@ public class ColorAndStyle {
|
||||
public int getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
public boolean isBold() {
|
||||
return (style & Font.BOLD) != 0;
|
||||
}
|
||||
|
||||
public boolean isItalic() {
|
||||
return (style & Font.ITALIC) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given text with HTML markup for each attribute and color defined by this
|
||||
* class. The returned result will <b>not</b> be prepended with <code><HTML></code>.
|
||||
*
|
||||
* @param text the text to wrap
|
||||
* @return the wrapped text
|
||||
*/
|
||||
public String toHtml(String text) {
|
||||
|
||||
String html = text;
|
||||
if (isBold()) {
|
||||
html = HTMLUtilities.bold(html);
|
||||
}
|
||||
if (isItalic()) {
|
||||
html = HTMLUtilities.italic(html);
|
||||
}
|
||||
|
||||
if (color != null) {
|
||||
html = HTMLUtilities.colorString(color, html);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ public class SymbolInspector implements OptionsChangeListener {
|
||||
private Map<String, Object> cache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs a new symbol inspector.
|
||||
* It uses the tool to get the CATEGORY_BROWSER_DISPLAY options.
|
||||
* Constructs a new symbol inspector
|
||||
* It uses the tool to get the CATEGORY_BROWSER_DISPLAY options
|
||||
*
|
||||
* @param serviceProvider a service provider for getting services
|
||||
* @param repaintComp the component to repaint when the options change
|
||||
*/
|
||||
@ -56,8 +57,8 @@ public class SymbolInspector implements OptionsChangeListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new symbol inspector.
|
||||
* @param ownerPlugin the plugin which owns this instance (may be null)
|
||||
* Constructs a new symbol inspector
|
||||
*
|
||||
* @param options the options from which to get colors
|
||||
* @param repaintComp the component to repaint when the options change
|
||||
*/
|
||||
@ -338,8 +339,9 @@ public class SymbolInspector implements OptionsChangeListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ScreenElement corresponding to the type of the symbol.
|
||||
* Get the ScreenElement corresponding to the type of the symbol
|
||||
* @param s symbol to inspect
|
||||
* @return the screen element
|
||||
*/
|
||||
public ScreenElement getScreenElement(Symbol s) {
|
||||
|
||||
@ -447,7 +449,7 @@ public class SymbolInspector implements OptionsChangeListener {
|
||||
String optionName = se.getStyleOptionName();
|
||||
Integer style = (Integer) cache.get(optionName);
|
||||
if (style == null) {
|
||||
style = new Integer(optionsObject.getInt(se.getStyleOptionName(), -1));
|
||||
style = Integer.valueOf(optionsObject.getInt(se.getStyleOptionName(), -1));
|
||||
cache.put(optionName, style);
|
||||
}
|
||||
return style.intValue();
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
@ -30,6 +32,7 @@ import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.model.UndoableDomainObject;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.generic.function.*;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.*;
|
||||
@ -102,7 +105,8 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
* If the language no longer exists, and suitable replacement language will be returned
|
||||
* if found. If no language is found, an exception will be thrown.
|
||||
* @param oldLanguageName old language name string
|
||||
* @return
|
||||
* @return the language compiler and spec
|
||||
* @throws LanguageNotFoundException
|
||||
*/
|
||||
public static LanguageCompilerSpecPair getLanguageCompilerSpecPair(String oldLanguageName)
|
||||
throws LanguageNotFoundException {
|
||||
@ -142,6 +146,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
* Creates an in-memory program with the given language
|
||||
* @param name the program name
|
||||
* @param languageString a language string of the format <tt>x86:LE:32:default</tt>
|
||||
* @param compilerSpecID the ID
|
||||
* @param consumer a consumer for the program
|
||||
* @return a new program
|
||||
* @throws Exception if there is any issue creating the language
|
||||
@ -174,8 +179,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
waitForSwing();
|
||||
|
||||
if (!status) {
|
||||
Msg.error(AbstractGhidraHeadedIntegrationTest.class,
|
||||
"Could not apply command: " + cmd.getStatusMsg());
|
||||
Msg.error(null, "Could not apply command: " + cmd.getStatusMsg());
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -189,6 +193,76 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
}
|
||||
}
|
||||
|
||||
public static <E extends Exception> void tx(Program p, ExceptionalCallback<E> c) throws E {
|
||||
int txId = p.startTransaction("Test - Function in Transaction");
|
||||
boolean commit = true;
|
||||
try {
|
||||
c.call();
|
||||
p.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
catch (RollbackException e) {
|
||||
commit = false;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
p.endTransaction(txId, commit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic
|
||||
*
|
||||
* @param program the program
|
||||
* @param callback the code to execute
|
||||
*/
|
||||
public <E extends Exception> void modifyProgram(Program program,
|
||||
ExceptionalConsumer<Program, E> callback) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
boolean commit = false;
|
||||
int tx = program.startTransaction("Test");
|
||||
try {
|
||||
callback.accept(program);
|
||||
commit = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
failWithException("Exception modifying program '" + program.getName() + "'", e);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(tx, commit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a convenient method for modifying the current program, handling the transaction
|
||||
* logic and returning a new item as a result
|
||||
*
|
||||
* @param program the program
|
||||
* @param f the function for modifying the program and creating the desired result
|
||||
* @return the result
|
||||
*/
|
||||
public <R, E extends Exception> R createInProgram(Program program,
|
||||
ExceptionalFunction<Program, R, E> f) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
R result = null;
|
||||
boolean commit = false;
|
||||
int tx = program.startTransaction("Test");
|
||||
try {
|
||||
result = f.apply(program);
|
||||
commit = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
failWithException("Exception modifying program '" + program.getName() + "'", e);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(tx, commit);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undo the last transaction on the domain object and wait for all events to be flushed.
|
||||
* @param dobj The domain object upon which to perform the undo.
|
||||
@ -371,6 +445,7 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
*
|
||||
* @param program the program to search.
|
||||
* @param name the name of the symbol to find.
|
||||
* @param namespace the parent namespace; may be null
|
||||
* @return the symbol with the given name if and only if it is the only one in that namespace
|
||||
*/
|
||||
public Symbol getUniqueSymbol(Program program, String name, Namespace namespace) {
|
||||
@ -389,6 +464,8 @@ public abstract class AbstractGhidraHeadlessIntegrationTest extends AbstractDock
|
||||
* sleeping.
|
||||
*
|
||||
* <P><B>Do not leave this call in your test when committing changes.</B>
|
||||
* @param p the program
|
||||
* @param address the address
|
||||
*
|
||||
* @throws Exception if there is an issue create a {@link TestEnv}
|
||||
*/
|
||||
|
@ -25,6 +25,8 @@ import org.junit.After;
|
||||
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.generic.function.ExceptionalConsumer;
|
||||
import ghidra.generic.function.ExceptionalFunction;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
@ -68,6 +70,7 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
||||
* Override this method if you need to build your own program.
|
||||
*
|
||||
* @return the program to use for this test.
|
||||
* @throws Exception if an exceptioun is thrown opening the program
|
||||
*/
|
||||
protected Program getProgram() throws Exception {
|
||||
return env.getProgram(getProgramName());
|
||||
@ -163,13 +166,13 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
||||
*
|
||||
* @param callback the code to execute
|
||||
*/
|
||||
public void modifyProgram(ExceptionalCallback<Program> callback) {
|
||||
public <E extends Exception> void modifyProgram(ExceptionalConsumer<Program, E> callback) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
boolean commit = false;
|
||||
int tx = program.startTransaction("Test");
|
||||
try {
|
||||
callback.call(program);
|
||||
callback.accept(program);
|
||||
commit = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -187,7 +190,7 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
||||
* @param f the function for modifying the program and creating the desired result
|
||||
* @return the result
|
||||
*/
|
||||
public <R> R createInProgram(ExceptionalFunction<Program, R> f) {
|
||||
public <R, E extends Exception> R createInProgram(ExceptionalFunction<Program, R, E> f) {
|
||||
assertNotNull("Program cannot be null", program);
|
||||
|
||||
R result = null;
|
||||
@ -205,18 +208,4 @@ public abstract class AbstractProgramBasedTest extends AbstractGhidraHeadedInteg
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
// TODO promote this functional interface (do this later; there is an outstanding branch
|
||||
// that has other functional interfaces with which to fraternize)
|
||||
public interface ExceptionalCallback<T> {
|
||||
public void call(T t) throws Exception;
|
||||
}
|
||||
|
||||
public interface ExceptionalFunction<T, R> {
|
||||
public R apply(T t) throws Exception;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* ###
|
||||
* 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.generic.function;
|
||||
|
||||
/**
|
||||
* A generic functional interface that is more semantically sound than {@link Runnable}. Use
|
||||
* anywhere you wish to have a generic callback function and you need to throw an exception.
|
||||
*
|
||||
* @param <E> the exception of your choice
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExceptionalCallback<E extends Exception> {
|
||||
|
||||
/**
|
||||
* The method that will be called
|
||||
*
|
||||
* @throws Exception if the call throws an exception
|
||||
*/
|
||||
public void call() throws E;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/* ###
|
||||
* 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.generic.function;
|
||||
|
||||
/**
|
||||
* A generic functional interface that allows you to consume an item and potentially throw
|
||||
* an exception.
|
||||
*
|
||||
* @param <T> the input type
|
||||
* @param <E> the exception of your choice
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExceptionalConsumer<T, E extends Exception> {
|
||||
|
||||
/**
|
||||
* The method that will be called
|
||||
*
|
||||
* @param t the input
|
||||
* @throws E if the call throws an exception
|
||||
*/
|
||||
public void accept(T t) throws E;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/* ###
|
||||
* 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.generic.function;
|
||||
|
||||
/**
|
||||
* A generic functional interface that allows you to consume an item, return a result,
|
||||
* and potentially throw an exception.
|
||||
*
|
||||
* @param <I> the input type
|
||||
* @param <R> the result type
|
||||
* @param <E> the exception of your choice
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExceptionalFunction<I, R, E extends Exception> {
|
||||
|
||||
/**
|
||||
* The method that will be called
|
||||
*
|
||||
* @param i the input
|
||||
* @return the result of the call
|
||||
* @throws E if the call throws an exception
|
||||
*/
|
||||
public R apply(I i) throws E;
|
||||
}
|
Loading…
Reference in New Issue
Block a user