Merge remote-tracking branch 'origin/GT-2732-dragonmacher'

This commit is contained in:
ghidravore 2019-05-02 16:23:17 -04:00
commit d3b2ef3928
8 changed files with 309 additions and 43 deletions

View File

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

View File

@ -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>&lt;HTML&gt;</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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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