Task Launcher - updated timeout feature to ignore interruptions

This commit is contained in:
dragonmacher 2019-05-23 10:42:08 -04:00
parent fb2a4a0363
commit 2cf9f7dded
11 changed files with 240 additions and 344 deletions

View file

@ -115,10 +115,9 @@ public class DomainFileArchiveNode extends ArchiveNode {
@Override @Override
public Icon getIcon(boolean expanded) { public Icon getIcon(boolean expanded) {
DomainFile df = ((DomainFileArchive) archive).getDomainFile();
ImageIcon baseIcon = archive.getIcon(expanded); ImageIcon baseIcon = archive.getIcon(expanded);
BackgroundIcon bgIcon = new BackgroundIcon(24, 16, isVersioned);
BackgroundIcon bgIcon = new BackgroundIcon(24, 16, df.isVersioned());
MultiIcon multiIcon = new MultiIcon(bgIcon); MultiIcon multiIcon = new MultiIcon(bgIcon);
multiIcon.addIcon(baseIcon); multiIcon.addIcon(baseIcon);

View file

@ -497,6 +497,16 @@ public class DummyTool implements Tool {
//do nothing //do nothing
} }
@Override
public void setStatusInfo(String text, boolean beep) {
//do nothing
}
@Override
public void clearStatusInfo() {
//do nothing
}
@Override @Override
public void addAction(DockingActionIf action) { public void addAction(DockingActionIf action) {
//do nothing //do nothing

View file

@ -15,9 +15,9 @@
*/ */
package docking; package docking;
import java.awt.Frame; import java.awt.*;
import java.awt.Window;
import java.util.*; import java.util.*;
import java.util.List;
import javax.swing.JFrame; import javax.swing.JFrame;
@ -78,6 +78,20 @@ public abstract class AbstractDockingTool implements DockingTool {
winMgr.setStatusText(text); winMgr.setStatusText(text);
} }
@Override
public void setStatusInfo(String text, boolean beep) {
winMgr.setStatusText(text);
if (beep) {
Toolkit tk = getToolFrame().getToolkit();
tk.beep();
}
}
@Override
public void clearStatusInfo() {
winMgr.setStatusText("");
}
@Override @Override
public void addAction(DockingActionIf action) { public void addAction(DockingActionIf action) {
actionMgr.addToolAction(action); actionMgr.addToolAction(action);

View file

@ -83,6 +83,18 @@ public interface DockingTool {
*/ */
public void setStatusInfo(String text); public void setStatusInfo(String text);
/**
* Set the status information
* @param text string to be displayed in the Status display area
* @param beep whether to be or not
*/
public void setStatusInfo(String text, boolean beep);
/**
* Clear the status information
*/
public void clearStatusInfo();
/** /**
* Adds the action to the tool. * Adds the action to the tool.
* @param action the action to be added. * @param action the action to be added.

View file

@ -16,8 +16,8 @@
package generic.test; package generic.test;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.ArrayList;
import java.util.Map.Entry; import java.util.List;
import utilities.util.reflection.ReflectionUtilities; import utilities.util.reflection.ReflectionUtilities;
@ -44,18 +44,7 @@ public class TestUtils {
* @return the stack trace string * @return the stack trace string
*/ */
public static String createStackTraceForAllThreads() { public static String createStackTraceForAllThreads() {
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); return ReflectionUtilities.createStackTraceForAllThreads();
Set<Entry<Thread, StackTraceElement[]>> entrySet = allStackTraces.entrySet();
StringBuilder builder = new StringBuilder();
for (Entry<Thread, StackTraceElement[]> entry : entrySet) {
builder.append("Thread: " + entry.getKey().getName()).append('\n');
StackTraceElement[] value = entry.getValue();
for (StackTraceElement stackTraceElement : value) {
builder.append('\t').append("at ").append(stackTraceElement).append('\n');
}
}
return builder.toString();
} }
/** /**
@ -69,11 +58,11 @@ public class TestUtils {
* @param ownerInstance The object instance from which to get the * @param ownerInstance The object instance from which to get the
* variable instance. * variable instance.
* @param value The value to use when setting the given field * @param value The value to use when setting the given field
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
* @see Field#set(Object, Object)) * @see Field#set(Object, Object)
*/ */
public static void setInstanceField(String fieldName, Object ownerInstance, Object value) public static void setInstanceField(String fieldName, Object ownerInstance, Object value)
throws RuntimeException { throws RuntimeException {
@ -110,7 +99,7 @@ public class TestUtils {
* @param ownerInstance The object instance from which to get the * @param ownerInstance The object instance from which to get the
* variable instance. * variable instance.
* @return The field instance. * @return The field instance.
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -193,7 +182,7 @@ public class TestUtils {
* to pass * to pass
* @return The return value as returned from executing the method. * @return The return value as returned from executing the method.
* @see Method#invoke(java.lang.Object, java.lang.Object[]) * @see Method#invoke(java.lang.Object, java.lang.Object[])
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -259,7 +248,7 @@ public class TestUtils {
* This value can be null or zero length if there are no parameters * This value can be null or zero length if there are no parameters
* to pass * to pass
* @return The return value as returned from executing the method. * @return The return value as returned from executing the method.
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -301,7 +290,7 @@ public class TestUtils {
* @param parameterType The parameter types that the method takes. * @param parameterType The parameter types that the method takes.
* @param arg The parameter value that should be passed to the method. * @param arg The parameter value that should be passed to the method.
* @return The return value as returned from executing the method. * @return The return value as returned from executing the method.
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -350,10 +339,9 @@ public class TestUtils {
* @param methodName The name of the method to execute. * @param methodName The name of the method to execute.
* @param ownerInstance The object instance of which the method will be * @param ownerInstance The object instance of which the method will be
* executed. * executed.
* @param parameterType The parameter types that the method takes. * @param args The parameter value that should be passed to the method.
* @param arg The parameter value that should be passed to the method.
* @return The return value as returned from executing the method. * @return The return value as returned from executing the method.
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -385,7 +373,7 @@ public class TestUtils {
* executed. * executed.
* @return The return value as returned from executing the method. * @return The return value as returned from executing the method.
* @see Method#invoke(java.lang.Object, java.lang.Object[]) * @see Method#invoke(java.lang.Object, java.lang.Object[])
* @throws A RuntimeException if there is a problem accessing the field * @throws RuntimeException if there is a problem accessing the field
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -409,7 +397,7 @@ public class TestUtils {
* This value can be null or zero length if there are no parameters * This value can be null or zero length if there are no parameters
* to pass * to pass
* @return The new class instance * @return The new class instance
* @throws A RuntimeException if there is a problem accessing the constructor * @throws RuntimeException if there is a problem accessing the constructor
* using reflection. A RuntimeException is used so that calling * using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail * tests can avoid using a try/catch block, but will still fail
* when an error is encountered. * when an error is encountered.
@ -492,9 +480,10 @@ public class TestUtils {
* Get the first field object contained within object ownerInstance which has the type classType. * Get the first field object contained within object ownerInstance which has the type classType.
* This method is only really useful if it is known that only a single field of * This method is only really useful if it is known that only a single field of
* classType exists within the ownerInstance. * classType exists within the ownerInstance.
* @param <T> *
* @param classType * @param <T> the type
* @param ownerInstance * @param classType the class type of the desired field
* @param ownerInstance the object instance that owns the field
* @return field object of type classType or null * @return field object of type classType or null
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -533,8 +522,9 @@ public class TestUtils {
* Get the first field specification contained within containingClass which has the type classType. * Get the first field specification contained within containingClass which has the type classType.
* This method is only really useful if it is known that only a single field of * This method is only really useful if it is known that only a single field of
* classType exists within the containingClass hierarchy. * classType exists within the containingClass hierarchy.
* @param classType *
* @param containingClass * @param classType the class
* @param containingClass the class that contains a field of the given type
* @return field which corresponds to type classType or null * @return field which corresponds to type classType or null
*/ */
public static Field locateFieldByTypeOnClass(Class<?> classType, Class<?> containingClass) { public static Field locateFieldByTypeOnClass(Class<?> classType, Class<?> containingClass) {

View file

@ -21,11 +21,13 @@ import java.awt.*;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.util.*; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.swing.*; import javax.swing.ImageIcon;
import javax.swing.JComponent;
import org.jdom.Element; import org.jdom.Element;
@ -213,11 +215,6 @@ public abstract class PluginTool extends AbstractDockingTool
// placeholder // placeholder
} }
@Override
public DockingWindowManager getWindowManager() {
return winMgr;
}
private void setDefaultOptionValues() { private void setDefaultOptionValues() {
Options toolOptions = optionsMgr.getOptions("Tool"); Options toolOptions = optionsMgr.getOptions("Tool");
boolean windowsOnTop = toolOptions.getBoolean(DOCKING_WINDOWS_ON_TOP, false); boolean windowsOnTop = toolOptions.getBoolean(DOCKING_WINDOWS_ON_TOP, false);
@ -318,47 +315,6 @@ public abstract class PluginTool extends AbstractDockingTool
popupListeners.remove(listener); popupListeners.remove(listener);
} }
/**
* Adds the action to the tool.
* @param action the action to be added.
*/
@Override
public void addAction(DockingActionIf action) {
actionMgr.addToolAction(action);
}
/**
* Add an action that is associated with the given provider. The action
* works only in the context of the provider, and not across the tool
* as for a "global" action.
* @param provider provider that has a visible component in the tool
* @param action local action to associate with the provider
*/
@Override
public void addLocalAction(ComponentProvider provider, DockingActionIf action) {
actionMgr.addLocalAction(provider, action);
}
/**
* Removes the given action from the tool
* @param action the action to be removed.
*/
@Override
public void removeAction(DockingActionIf action) {
actionMgr.removeToolAction(action);
}
/**
* Adds a visible component to the tool.
* @param provider The component provider that provides the component to be added.
* @param show flag to initially show the component.
*/
@Override
public void addComponentProvider(final ComponentProvider provider, final boolean show) {
Runnable r = () -> winMgr.addComponent(provider, show);
SystemUtilities.runSwingNow(r);
}
/** /**
* Set whether a component's header should be shown; the header is the component that * Set whether a component's header should be shown; the header is the component that
* is dragged in order to move the component within the tool, or out of the tool * is dragged in order to move the component within the tool, or out of the tool
@ -371,76 +327,6 @@ public abstract class PluginTool extends AbstractDockingTool
winMgr.showComponentHeader(provider, b); winMgr.showComponentHeader(provider, b);
} }
@Override
public boolean isActive(ComponentProvider provider) {
return winMgr.isActiveProvider(provider);
}
/**
* Hides or shows the component associated with the given provider.
* @param provider the provider of the component to be hidden or shown.
* @param visibleState true to show the component, false to hide it.
*/
@Override
public void showComponentProvider(final ComponentProvider provider,
final boolean visibleState) {
Runnable r = () -> winMgr.showComponent(provider, visibleState);
SystemUtilities.runSwingNow(r);
}
@Override
public void toFront(final ComponentProvider provider) {
Runnable r = () -> winMgr.toFront(provider);
SystemUtilities.runSwingNow(r);
}
@Override
public void removeComponentProvider(final ComponentProvider provider) {
Runnable r = () -> actionMgr.removeComponent(provider);
SystemUtilities.runSwingNow(r);
}
@Override
public void updateTitle(ComponentProvider provider) {
winMgr.updateTitle(provider);
}
@Override
public boolean isVisible(ComponentProvider provider) {
return winMgr.isVisible(provider);
}
@Override
public boolean isVisible() {
return winMgr.isVisible();
}
/**
* @see ghidra.framework.model.Tool#setVisible(boolean)
*/
@Override
public void setVisible(boolean visibility) {
winMgr.setVisible(visibility);
}
@Override
public void toFront() {
JFrame frame = winMgr.getRootFrame();
if (frame.getExtendedState() == Frame.ICONIFIED) {
frame.setExtendedState(Frame.NORMAL);
}
frame.toFront();
}
/**
* Returns the tool's frame
* @return the tool's frame
*/
@Override
public JFrame getToolFrame() {
return winMgr.getRootFrame();
}
/** Install any services that are not provided by plugins */ /** Install any services that are not provided by plugins */
private void installServices() { private void installServices() {
serviceMgr.addService(ProjectDataService.class, serviceMgr.addService(ProjectDataService.class,
@ -489,31 +375,6 @@ public abstract class PluginTool extends AbstractDockingTool
serviceMgr.removeServiceListener(listener); serviceMgr.removeServiceListener(listener);
} }
/**
* Set the status information.
* @param text string to be displayed in the Status display area
* @param beep whether to be or not
*/
public void setStatusInfo(String text, boolean beep) {
winMgr.setStatusText(text);
if (beep) {
Toolkit tk = getToolFrame().getToolkit();
tk.beep();
}
}
@Override
public void setStatusInfo(String text) {
winMgr.setStatusText(text);
}
/**
* Clear the status information.
*/
public void clearStatusInfo() {
winMgr.setStatusText("");
}
/** /**
* Sets the provider that should get the default focus when no component has focus. * Sets the provider that should get the default focus when no component has focus.
* @param provider the provider that should get the default focus when no component has focus. * @param provider the provider that should get the default focus when no component has focus.
@ -1037,30 +898,6 @@ public abstract class PluginTool extends AbstractDockingTool
winMgr.removeStatusItem(c); winMgr.removeStatusItem(c);
} }
@Override
public List<DockingActionIf> getDockingActionsByFullActionName(String fullActionName) {
Set<DockingActionIf> set = new HashSet<>();
set.addAll(actionMgr.getDockingActionsByFullActionName(fullActionName));
set.addAll(winMgr.getActions(fullActionName));
return new ArrayList<>(set);
}
@Override
public List<DockingActionIf> getDockingActionsByOwnerName(String owner) {
List<DockingActionIf> actions = actionMgr.getActions(owner);
return actions;
}
@Override
public List<DockingActionIf> getAllActions() {
return actionMgr.getAllActions();
}
@Override
public void removeLocalAction(ComponentProvider provider, DockingActionIf action) {
actionMgr.removeProviderAction(provider, action);
}
protected void addExitAction() { protected void addExitAction() {
DockingAction exitAction = new DockingAction("Exit Ghidra", "Tool") { DockingAction exitAction = new DockingAction("Exit Ghidra", "Tool") {
@Override @Override
@ -1584,18 +1421,6 @@ public abstract class PluginTool extends AbstractDockingTool
return pluginMgr.getUndoRedoToolState(domainObject); return pluginMgr.getUndoRedoToolState(domainObject);
} }
/**
* Shows the dialog using the active top-level window (often the tool's root frame)
* as a parent. Also, remembers any
* size and location adjustments made by the user for the next time the dialog is shown.
*
* @param dialogComponent the DialogComponentProvider object to be shown in a dialog.
*/
@Override
public void showDialog(DialogComponentProvider dialogComponent) {
DockingWindowManager.showDialog(dialogComponent);
}
/** /**
* Shows the dialog using the tool's currently active window as a parent. Also, * Shows the dialog using the tool's currently active window as a parent. Also,
* remembers any size and location adjustments made by the user for the next * remembers any size and location adjustments made by the user for the next
@ -1679,11 +1504,6 @@ public abstract class PluginTool extends AbstractDockingTool
winMgr.removePreferenceState(name); winMgr.removePreferenceState(name);
} }
@Override
public Window getProviderWindow(ComponentProvider componentProvider) {
return winMgr.getProviderWindow(componentProvider);
}
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.processors.sleigh; package ghidra.app.plugin.processors.sleigh;
import static utilities.util.FileUtilities.existsAndIsCaseDependent; import static utilities.util.FileUtilities.*;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@ -99,6 +99,11 @@ public class SleighLanguageProvider implements LanguageProvider {
return getNewSleigh(languageId); return getNewSleigh(languageId);
} }
@Override
public boolean isLanguageLoaded(LanguageID languageId) {
return languages.get(languageId) != null;
}
private Language getNewSleigh(LanguageID languageId) { private Language getNewSleigh(LanguageID languageId) {
SleighLanguageDescription description = descriptions.get(languageId); SleighLanguageDescription description = descriptions.get(languageId);
SleighLanguage lang = languages.get(languageId); SleighLanguage lang = languages.get(languageId);
@ -108,31 +113,31 @@ public class SleighLanguageProvider implements LanguageProvider {
languages.put(languageId, lang); languages.put(languageId, lang);
} }
catch (SleighException e) { catch (SleighException e) {
Msg.showError(this, null, "Error", "Can't read language spec " + Msg.showError(this, null, "Error",
description.getSlaFile().getAbsolutePath(), e); "Can't read language spec " + description.getSlaFile().getAbsolutePath(), e);
throw e; throw e;
} }
catch (FileNotFoundException e) { catch (FileNotFoundException e) {
Msg.showError(this, null, "Error", "Can't read language spec " + Msg.showError(this, null, "Error",
description.getSlaFile().getAbsolutePath(), e); "Can't read language spec " + description.getSlaFile().getAbsolutePath(), e);
throw new SleighException( throw new SleighException(
"File not found - language probably did not compile properly", e); "File not found - language probably did not compile properly", e);
} }
catch (UnknownInstructionException e) { catch (UnknownInstructionException e) {
Msg.showError(this, null, "Error", "Can't read language spec " + Msg.showError(this, null, "Error",
description.getSlaFile().getAbsolutePath(), e); "Can't read language spec " + description.getSlaFile().getAbsolutePath(), e);
throw new SleighException( throw new SleighException(
"Unknown instruction - language probably did not compile properly", e); "Unknown instruction - language probably did not compile properly", e);
} }
catch (SAXException e) { catch (SAXException e) {
Msg.showError(this, null, "Error", "Can't read language spec " + Msg.showError(this, null, "Error",
description.getSlaFile().getAbsolutePath(), e); "Can't read language spec " + description.getSlaFile().getAbsolutePath(), e);
throw new SleighException( throw new SleighException(
"SAXException - language probably did not compile properly", e); "SAXException - language probably did not compile properly", e);
} }
catch (IOException e) { catch (IOException e) {
Msg.showError(this, null, "Error", "Can't read language spec " + Msg.showError(this, null, "Error",
description.getSlaFile().getAbsolutePath(), e); "Can't read language spec " + description.getSlaFile().getAbsolutePath(), e);
throw new SleighException( throw new SleighException(
"IOException - language probably did not compile properly", e); "IOException - language probably did not compile properly", e);
} }
@ -162,7 +167,8 @@ public class SleighLanguageProvider implements LanguageProvider {
@Override @Override
public void fatalError(SAXParseException exception) throws SAXException { public void fatalError(SAXParseException exception) throws SAXException {
Msg.error(SleighLanguageProvider.this, "Fatal error parsing " + specFile, exception); Msg.error(SleighLanguageProvider.this, "Fatal error parsing " + specFile,
exception);
} }
@Override @Override
@ -256,8 +262,8 @@ public class SleighLanguageProvider implements LanguageProvider {
truncatedSpaceMap = new HashMap<String, Integer>(); truncatedSpaceMap = new HashMap<String, Integer>();
} }
if (truncatedSpaceMap.put(spaceName, truncatedSize) != null) { if (truncatedSpaceMap.put(spaceName, truncatedSize) != null) {
throw new SleighException("truncated space '" + spaceName + throw new SleighException(
"' alread specified"); "truncated space '" + spaceName + "' alread specified");
} }
parser.end(element); parser.end(element);
} }
@ -267,15 +273,15 @@ public class SleighLanguageProvider implements LanguageProvider {
final String compilerSpecName = compiler.getAttribute("name"); final String compilerSpecName = compiler.getAttribute("name");
final String compilerSpecFilename = compiler.getAttribute("spec"); final String compilerSpecFilename = compiler.getAttribute("spec");
final ResourceFile compilerSpecFile = final ResourceFile compilerSpecFile =
findFile(parentDirectory, compilerSpecFilename, ".cspec"); findFile(parentDirectory, compilerSpecFilename, ".cspec");
FileResolutionResult result = existsAndIsCaseDependent(compilerSpecFile); FileResolutionResult result = existsAndIsCaseDependent(compilerSpecFile);
if (!result.isOk()) { if (!result.isOk()) {
throw new SleighException("cspec file " + compilerSpecFile + throw new SleighException("cspec file " + compilerSpecFile +
" is not properly case dependent: " + result.getMessage()); " is not properly case dependent: " + result.getMessage());
} }
final SleighCompilerSpecDescription sleighCompilerSpecDescription = final SleighCompilerSpecDescription sleighCompilerSpecDescription =
new SleighCompilerSpecDescription(compilerSpecID, compilerSpecName, new SleighCompilerSpecDescription(compilerSpecID, compilerSpecName,
compilerSpecFile); compilerSpecFile);
compilerSpecs.add(sleighCompilerSpecDescription); compilerSpecs.add(sleighCompilerSpecDescription);
parser.end(compiler); parser.end(compiler);
} }
@ -301,11 +307,10 @@ public class SleighLanguageProvider implements LanguageProvider {
// skip the language end tag // skip the language end tag
parser.end(languageEnter); parser.end(languageEnter);
description = description = new SleighLanguageDescription(id, descriptionText,
new SleighLanguageDescription(id, descriptionText, Processor.findOrPossiblyCreateProcessor(processorName), endian, instructionEndian,
Processor.findOrPossiblyCreateProcessor(processorName), endian, size, variant, version, minorVersion, deprecated, truncatedSpaceMap, compilerSpecs,
instructionEndian, size, variant, version, minorVersion, deprecated, externalNameMap);
truncatedSpaceMap, compilerSpecs, externalNameMap);
final ResourceFile defsFile = new ResourceFile(parentDirectory, ldefs); final ResourceFile defsFile = new ResourceFile(parentDirectory, ldefs);
FileResolutionResult result = existsAndIsCaseDependent(defsFile); FileResolutionResult result = existsAndIsCaseDependent(defsFile);
if (!result.isOk()) { if (!result.isOk()) {
@ -337,8 +342,7 @@ public class SleighLanguageProvider implements LanguageProvider {
String slaspecfilename = slabase + ".slaspec"; String slaspecfilename = slabase + ".slaspec";
ResourceFile slaspecFile = ResourceFile slaspecFile = findFile(parentDirectory, slaspecfilename, ".slaspec");
findFile(parentDirectory, slaspecfilename, ".slaspec");
result = existsAndIsCaseDependent(slaspecFile); result = existsAndIsCaseDependent(slaspecFile);
if (!result.isOk()) { if (!result.isOk()) {
throw new SleighException("sla file source " + slaspecFile + throw new SleighException("sla file source " + slaspecFile +

View file

@ -33,7 +33,7 @@ public interface LanguageProvider extends ExtensionPoint {
* @return the {@link Language} with the given name * @return the {@link Language} with the given name
*/ */
Language getLanguage(LanguageID languageId); Language getLanguage(LanguageID languageId);
/** /**
* Returns a list of language descriptions provided by this provider * Returns a list of language descriptions provided by this provider
*/ */
@ -45,4 +45,11 @@ public interface LanguageProvider extends ExtensionPoint {
*/ */
boolean hadLoadFailure(); boolean hadLoadFailure();
/**
* Returns true if the given language has been successfully loaded
*
* @param languageId the name of the language to be retrieved
* @return true if the given language has been successfully loaded
*/
boolean isLanguageLoaded(LanguageID languageId);
} }

View file

@ -102,68 +102,42 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
//@formatter:on //@formatter:on
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getLanguage(ghidra.program.model.lang.LanguageID)
*/
@Override
public Language getLanguage(LanguageID languageID) throws LanguageNotFoundException { public Language getLanguage(LanguageID languageID) throws LanguageNotFoundException {
LanguageInfo info = languageMap.get(languageID); LanguageInfo info = languageMap.get(languageID);
if (info == null) { if (info == null) {
throw new LanguageNotFoundException(languageID); throw new LanguageNotFoundException(languageID);
} }
//@formatter:off
TaskBuilder.withRunnable(monitor -> {
info.getLanguage(); // load and cache
})
.setTitle("Loading language '" + languageID + "'")
.setCanCancel(false)
.setHasProgress(false)
.launchModal()
;
//@formatter:on
return info.getLanguage(); return info.getLanguage();
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getLanguageDescription(ghidra.program.model.lang.LanguageID)
*/
@Override
public LanguageDescription getLanguageDescription(LanguageID languageID) public LanguageDescription getLanguageDescription(LanguageID languageID)
throws LanguageNotFoundException { throws LanguageNotFoundException {
LanguageInfo info = languageMap.get(languageID); LanguageInfo info = languageMap.get(languageID);
if (info == null) { if (info == null) {
throw new LanguageNotFoundException(languageID); throw new LanguageNotFoundException(languageID);
} }
return info.ld; return info.description;
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getLanguageDescriptions(boolean)
*/
@Override
public List<LanguageDescription> getLanguageDescriptions(boolean includeDeprecatedLanguages) { public List<LanguageDescription> getLanguageDescriptions(boolean includeDeprecatedLanguages) {
List<LanguageDescription> languageDescriptions = new ArrayList<>(); List<LanguageDescription> languageDescriptions = new ArrayList<>();
for (LanguageInfo info : languageInfos) { for (LanguageInfo info : languageInfos) {
if (includeDeprecatedLanguages || !info.ld.isDeprecated()) { if (includeDeprecatedLanguages || !info.description.isDeprecated()) {
languageDescriptions.add(info.ld); languageDescriptions.add(info.description);
} }
} }
return languageDescriptions; return languageDescriptions;
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getLanguageDescriptions(ghidra.program.model.lang.Processor,
* ghidra.program.model.lang.Endian, java.lang.Integer,
* java.lang.String)
*/
@Override
public List<LanguageDescription> getLanguageDescriptions(Processor processor, Endian endianess, public List<LanguageDescription> getLanguageDescriptions(Processor processor, Endian endianess,
Integer size, String variant) { Integer size, String variant) {
List<LanguageDescription> languageDescriptions = new ArrayList<>(); List<LanguageDescription> languageDescriptions = new ArrayList<>();
for (LanguageInfo info : languageInfos) { for (LanguageInfo info : languageInfos) {
LanguageDescription description = info.ld; LanguageDescription description = info.description;
if (processor != null && processor != description.getProcessor()) { if (processor != null && processor != description.getProcessor()) {
continue; continue;
} }
@ -206,7 +180,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
List<LanguageDescription> languageDescriptions = new ArrayList<>(); List<LanguageDescription> languageDescriptions = new ArrayList<>();
for (LanguageInfo info : languageInfos) { for (LanguageInfo info : languageInfos) {
LanguageDescription description = info.ld; LanguageDescription description = info.description;
if (!languageMatchesExternalProcessor(description, externalProcessorName, if (!languageMatchesExternalProcessor(description, externalProcessorName,
externalTool)) { externalTool)) {
@ -300,16 +274,13 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getLanguageDescriptions(ghidra.program.model.lang.Processor)
*/
@Override
public List<LanguageDescription> getLanguageDescriptions(Processor processor) { public List<LanguageDescription> getLanguageDescriptions(Processor processor) {
ArrayList<LanguageDescription> list = new ArrayList<>(); ArrayList<LanguageDescription> list = new ArrayList<>();
for (LanguageInfo info : languageInfos) { for (LanguageInfo info : languageInfos) {
if (info.ld.getProcessor().equals(processor)) { if (info.description.getProcessor().equals(processor)) {
list.add(info.ld); list.add(info.description);
} }
} }
return list; return list;
@ -350,16 +321,13 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
return returnValue; return returnValue;
} }
/** @Override
* @see ghidra.program.model.lang.LanguageService#getDefaultLanguage(ghidra.program.model.lang.Processor)
*/
@Override
public Language getDefaultLanguage(Processor processor) throws LanguageNotFoundException { public Language getDefaultLanguage(Processor processor) throws LanguageNotFoundException {
if (processor == null) { if (processor == null) {
throw new IllegalArgumentException("processor == null not allowed"); throw new IllegalArgumentException("processor == null not allowed");
} }
for (LanguageInfo info : languageInfos) { for (LanguageInfo info : languageInfos) {
if (info.ld.getProcessor().equals(processor)) { if (info.description.getProcessor().equals(processor)) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Language language = info.getLanguage(); Language language = info.getLanguage();
log.debug("getDefaultLanguage(" + language.getLanguageID() + ") took " + log.debug("getDefaultLanguage(" + language.getLanguageID() + ") took " +
@ -385,7 +353,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
continue; continue;
} }
languageInfos.add(info); languageInfos.add(info);
LanguageID id = info.ld.getLanguageID(); LanguageID id = info.description.getLanguageID();
if (languageMap.containsKey(id)) { if (languageMap.containsKey(id)) {
throw new IllegalStateException("Duplicate language ID encountered: " + id); throw new IllegalStateException("Duplicate language ID encountered: " + id);
} }
@ -393,22 +361,42 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
} }
} }
class LanguageInfo { private class LanguageInfo {
LanguageDescription ld;
LanguageProvider lp; private LanguageProvider provider;
LanguageDescription description;
LanguageInfo(LanguageDescription ld, LanguageProvider lp) { LanguageInfo(LanguageDescription ld, LanguageProvider lp) {
this.ld = ld; this.description = ld;
this.lp = lp; this.provider = lp;
} }
Language getLanguage() { // synchronized to prevent multiple clients from trying to load the language at once
return lp.getLanguage(ld.getLanguageID()); synchronized Language getLanguage() {
LanguageID id = description.getLanguageID();
if (provider.isLanguageLoaded(id)) {
// already loaded; no need to create a task
return provider.getLanguage(id);
}
//@formatter:off
TaskBuilder.withRunnable(monitor -> {
provider.getLanguage(id); // load and cache
})
.setTitle("Loading language '" + id + "'")
.setCanCancel(false)
.setHasProgress(false)
.launchModal()
;
//@formatter:on
return provider.getLanguage(id);
} }
@Override @Override
public String toString() { public String toString() {
return ld.getLanguageID().getIdAsString(); return description.getLanguageID().getIdAsString();
} }
@Override @Override
@ -417,19 +405,16 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
return false; return false;
} }
LanguageInfo otherInfo = (LanguageInfo) obj; LanguageInfo otherInfo = (LanguageInfo) obj;
return ld.getLanguageID().equals(otherInfo.ld.getLanguageID()); return description.getLanguageID().equals(otherInfo.description.getLanguageID());
} }
@Override @Override
public int hashCode() { public int hashCode() {
return ld.getLanguageID().hashCode(); return description.getLanguageID().hashCode();
} }
} }
/** @Override
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
@Override
public void stateChanged(ChangeEvent e) { public void stateChanged(ChangeEvent e) {
// NOTE: this is only intended to pickup new language providers // NOTE: this is only intended to pickup new language providers
// which is not really supported with the introduction of Sleigh. // which is not really supported with the introduction of Sleigh.

View file

@ -101,6 +101,36 @@ public class Swing {
} }
} }
/**
* Calls the given runnable on the Swing thread in the future by putting the request on
* the back of the event queue.
*
* @param r the runnable
*/
public static void runLater(Runnable r) {
doRun(r, false, SWING_RUN_ERROR_MSG);
}
/**
* Runs the given runnable now if the caller is on the Swing thread. Otherwise, the
* runnable will be posted later.
*
* @param r the runnable
*/
public static void runIfSwingOrRunLater(Runnable r) {
if (isInHeadlessMode()) {
r.run();
return;
}
if (SwingUtilities.isEventDispatchThread()) {
r.run();
}
else {
SwingUtilities.invokeLater(r);
}
}
/** /**
* Calls the given suppler on the Swing thread, blocking with a * Calls the given suppler on the Swing thread, blocking with a
* {@link SwingUtilities#invokeAndWait(Runnable)} if not on the Swing thread. * {@link SwingUtilities#invokeAndWait(Runnable)} if not on the Swing thread.
@ -136,8 +166,29 @@ public class Swing {
runNow(r, SWING_TIMEOUT_SECONDS_VALUE, TimeUnit.SECONDS); runNow(r, SWING_TIMEOUT_SECONDS_VALUE, TimeUnit.SECONDS);
} }
catch (UnableToSwingException e) { catch (UnableToSwingException e) {
throw new RuntimeException("Timed-out waiting to run a Swing task--potential deadlock!",
e); //
// Special Cases: if we are in production mode, then this is most likely a deadlock.
// In that case, log the thread state. In development mode, it is possible for this
// to happen while debugging. In that case, log a message, and then post the work
// to be done without a timeout.
//
String warning = "Timed-out waiting to run a Swing task--potential deadlock!";
if (SystemUtilities.isInReleaseMode()) {
Throwable threadDump = ReflectionUtilities.createJavaFilteredThrowable();
Msg.error(Swing.class, warning + "\nThreads State:\n" + threadDump);
throw new RuntimeException(warning, e);
}
//
// dev or testing mode
//
// note: using Swing.class for the originator does not work (presumably it conflicts
// with another logger sharing its name. So, use the full name here.
String originator = Swing.class.getName();
Msg.debug(originator, warning + " Ignore this message if debugging");
doRun(r, true, SWING_RUN_ERROR_MSG);
} }
} }
@ -172,7 +223,7 @@ public class Swing {
runLater(() -> { runLater(() -> {
if (!waitFor(start)) { if (!waitFor(start)) {
return; // must have timed-out return; // interrupted or timed-out
} }
try { try {
@ -185,53 +236,36 @@ public class Swing {
}); });
if (!waitFor(start, timeout, unit)) { if (!waitFor(start, timeout, unit)) {
throw new UnableToSwingException( // Special case: if the wait() returns false, then it was interrupted. If the
"Timed-out waiting for Swing thread lock in " + timeout + " " + unit); // timeout occurred, an exception would have been thrown. Interrupts are expected,
// so just exit.
return;
} }
waitFor(end); waitFor(end);
} }
/** private static boolean waitFor(CyclicBarrier barrier, long timeout, TimeUnit unit)
* Calls the given runnable on the Swing thread in the future by putting the request on throws UnableToSwingException {
* the back of the event queue.
*
* @param r the runnable
*/
public static void runLater(Runnable r) {
doRun(r, false, SWING_RUN_ERROR_MSG);
}
public static void runIfSwingOrRunLater(Runnable r) {
if (isInHeadlessMode()) {
r.run();
return;
}
if (SwingUtilities.isEventDispatchThread()) {
r.run();
}
else {
SwingUtilities.invokeLater(r);
}
}
private static boolean waitFor(CyclicBarrier barrier, long timeout, TimeUnit unit) {
try { try {
barrier.await(timeout, unit); barrier.await(timeout, unit);
return true; return true;
} }
catch (InterruptedException | BrokenBarrierException | TimeoutException e) { catch (InterruptedException e) {
// our Swing tasks may be interrupted from the framework // our Swing tasks may be interrupted from the framework
} }
catch (BrokenBarrierException | TimeoutException e) {
throw new UnableToSwingException(
"Timed-out waiting for Swing thread lock in " + timeout + " " + unit);
}
// timed-out or was interrupted // timed-out or was interrupted
return false; return false;
} }
private static boolean waitFor(CyclicBarrier barrier) { private static boolean waitFor(CyclicBarrier barrier) {
try { try {
barrier.await(); barrier.await();
return true; return true;

View file

@ -18,6 +18,7 @@ package utilities.util.reflection;
import java.io.*; import java.io.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
@ -158,8 +159,8 @@ public class ReflectionUtilities {
* Get the first field specification contained within containingClass which has the type classType. * Get the first field specification contained within containingClass which has the type classType.
* This method is only really useful if it is known that only a single field of * This method is only really useful if it is known that only a single field of
* classType exists within the containingClass hierarchy. * classType exists within the containingClass hierarchy.
* @param classType * @param classType the class
* @param containingClass * @param containingClass the class that contains a field of the given type
* @return field which corresponds to type classType or null * @return field which corresponds to type classType or null
*/ */
public static Field locateFieldByTypeOnClass(Class<?> classType, Class<?> containingClass) { public static Field locateFieldByTypeOnClass(Class<?> classType, Class<?> containingClass) {
@ -255,12 +256,11 @@ public class ReflectionUtilities {
/** /**
* Finds the first occurrence of the given pattern and then stops filtering when it finds * Finds the first occurrence of the given pattern and then stops filtering when it finds
* something that is not that pattern. * something that is not that pattern
* *
* @param trace the trace to update * @param trace the trace to update
* @param patterns the non-regex patterns used to perform a * @param pattern the non-regex patterns used to perform a
* {@link String#contains(CharSequence)} on each {@link StackTraceElement} * {@link String#contains(CharSequence)} on each {@link StackTraceElement} line
* line.
* @return the updated trace * @return the updated trace
*/ */
public static StackTraceElement[] movePastStackTracePattern(StackTraceElement[] trace, public static StackTraceElement[] movePastStackTracePattern(StackTraceElement[] trace,
@ -355,6 +355,7 @@ public class ReflectionUtilities {
* lines (e.g., AWT, Swing, Security, etc). * lines (e.g., AWT, Swing, Security, etc).
* This can be useful for emitting diagnostic stack traces with reduced noise. * This can be useful for emitting diagnostic stack traces with reduced noise.
* *
* @param t the throwable to filter
* @return the throwable * @return the throwable
*/ */
public static Throwable filterJavaThrowable(Throwable t) { public static Throwable filterJavaThrowable(Throwable t) {
@ -375,6 +376,26 @@ public class ReflectionUtilities {
return false; return false;
} }
/**
* Returns a string which is a printout of a stack trace for each thread running in the
* current JVM
* @return the stack trace string
*/
public static String createStackTraceForAllThreads() {
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
Set<Entry<Thread, StackTraceElement[]>> entrySet = allStackTraces.entrySet();
StringBuilder builder = new StringBuilder();
for (Entry<Thread, StackTraceElement[]> entry : entrySet) {
builder.append("Thread: " + entry.getKey().getName()).append('\n');
StackTraceElement[] value = entry.getValue();
for (StackTraceElement stackTraceElement : value) {
builder.append('\t').append("at ").append(stackTraceElement).append('\n');
}
}
return builder.toString();
}
/** /**
* Returns an ordered set of interfaces and classes that are shared amongst the items in * Returns an ordered set of interfaces and classes that are shared amongst the items in
* the list. * the list.