GP-4643: Add a JIT-accelerated p-code emulator (API/scripting only)

This commit is contained in:
Dan 2025-01-03 10:27:38 -05:00
parent 20285e267d
commit a8fae1fe5b
320 changed files with 32638 additions and 630 deletions

View file

@ -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.
@ -29,7 +29,9 @@ import java.util.function.Supplier;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.rules.TestRule;
import generic.test.rule.*;
import ghidra.framework.Application;
import ghidra.framework.TestApplicationUtils;
import ghidra.util.SystemUtilities;
@ -41,11 +43,12 @@ import utility.application.ApplicationUtilities;
/**
* A root for system tests that provides known system information.
*
* <P>This class exists so that fast unit tests have a place to share data without having the
* slowness of more heavy weight concepts like {@link Application}, logging, etc.
* <P>
* This class exists so that fast unit tests have a place to share data without having the slowness
* of more heavy weight concepts like {@link Application}, logging, etc.
*
* <P> !! WARNING !!
* This test is meant to initialize quickly. All file I/O should be avoided.
* <P>
* !! WARNING !! This test is meant to initialize quickly. All file I/O should be avoided.
*/
public abstract class AbstractGTest {
@ -75,8 +78,25 @@ public abstract class AbstractGTest {
public TestName testName = new TestName();
/**
* Get the directory path within which all temporary test
* data files should be created.
* This rule handles the {@link Repeated} annotation
*
* <p>
* During batch mode, this rule should never be needed. This rule is included here as a
* convenience, in case a developer wants to use the {@link Repeated} annotation to diagnose a
* non-deterministic test failure. Without this rule, the annotation would be silently ignored.
*/
@Rule
public TestRule repeatedRule = new RepeatedTestRule();
/**
* This rule handles the {@link IgnoreUnfinished} annotation
*/
@Rule
public TestRule ignoreUnfinishedRule = new IgnoreUnfinishedRule();
/**
* Get the directory path within which all temporary test data files should be created.
*
* @return test directory path ending with a File.separator character
*/
private static String createTestDirectoryPath() {
@ -161,9 +181,9 @@ public abstract class AbstractGTest {
}
/**
* Compares the contents of two arrays to determine if they are equal. The contents must
* match in the same order. If <code>message</code>
* is <code>null</code>, then a generic error message will be printed.
* Compares the contents of two arrays to determine if they are equal. The contents must match
* in the same order. If <code>message</code> is <code>null</code>, then a generic error message
* will be printed.
*
* @param message The message to print upon failure; can be null
* @param expected The expected array.
@ -180,9 +200,9 @@ public abstract class AbstractGTest {
}
/**
* Compares the contents of two arrays to determine if they are equal. The contents do not have
* to be in the same order. If <code>message</code>
* is <code>null</code>, then a generic error message will be printed.
* Compares the contents of two arrays to determine if they are equal. The contents do not have
* to be in the same order. If <code>message</code> is <code>null</code>, then a generic error
* message will be printed.
*
* @param message The message to print upon failure; can be null
* @param expected The expected array.
@ -394,7 +414,7 @@ public abstract class AbstractGTest {
}
/**
* Waits for the given AtomicBoolean to return true. This is a convenience method for
* Waits for the given AtomicBoolean to return true. This is a convenience method for
* {@link #waitFor(BooleanSupplier)}.
*
* @param ab the atomic boolean
@ -441,8 +461,8 @@ public abstract class AbstractGTest {
* Waits for the given condition to return true
*
* @param condition the condition that returns true when satisfied
* @param failureMessageSupplier the function that will supply the failure message in the
* event of a timeout.
* @param failureMessageSupplier the function that will supply the failure message in the event
* of a timeout.
* @throws AssertionFailedError if the condition is not met within the timeout period
*/
public static void waitForCondition(BooleanSupplier condition,
@ -452,11 +472,12 @@ public abstract class AbstractGTest {
}
/**
* Waits for the given condition to return true. Most of the <code>waitForCondition()</code>
* methods throw an {@link AssertionFailedError} if the timeout period expires.
* This method allows you to setup a longer wait period by repeatedly calling this method.
* Waits for the given condition to return true. Most of the <code>waitForCondition()</code>
* methods throw an {@link AssertionFailedError} if the timeout period expires. This method
* allows you to setup a longer wait period by repeatedly calling this method.
*
* <P>Most clients should use {@link #waitForCondition(BooleanSupplier)}.
* <P>
* Most clients should use {@link #waitForCondition(BooleanSupplier)}.
*
* @param supplier the supplier that returns true when satisfied
*/
@ -496,8 +517,8 @@ public abstract class AbstractGTest {
}
/**
* Waits for the value returned by the supplier to be non-null, throwing an exception if
* that does not happen by the default timeout.
* Waits for the value returned by the supplier to be non-null, throwing an exception if that
* does not happen by the default timeout.
*
* @param supplier the supplier of the value
* @param failureMessage the message to print upon the timeout being reached
@ -509,8 +530,8 @@ public abstract class AbstractGTest {
}
/**
* Waits for the value returned by the supplier to be non-null, throwing an exception if
* that does not happen by the default timeout.
* Waits for the value returned by the supplier to be non-null, throwing an exception if that
* does not happen by the default timeout.
*
* @param supplier the supplier of the value
* @return the non-null value
@ -521,8 +542,8 @@ public abstract class AbstractGTest {
}
/**
* Waits for the value returned by the supplier to be non-null, throwing an exception if
* that does not happen by the default timeout.
* Waits for the value returned by the supplier to be non-null, throwing an exception if that
* does not happen by the default timeout.
*
* @param supplier the supplier of the value
* @return the non-null value
@ -533,12 +554,13 @@ public abstract class AbstractGTest {
}
/**
* Waits for the value returned by the supplier to be non-null. If the timeout period
* expires, then null will be returned. Most of the <code>waitXyz()</code> methods
* throw an {@link AssertionFailedError} if the timeout period expires. This method allows
* you to setup a longer wait period by repeatedly calling this method.
* Waits for the value returned by the supplier to be non-null. If the timeout period expires,
* then null will be returned. Most of the <code>waitXyz()</code> methods throw an
* {@link AssertionFailedError} if the timeout period expires. This method allows you to setup a
* longer wait period by repeatedly calling this method.
*
* <P>Most clients should use {@link #waitForValue(Supplier)}.
* <P>
* Most clients should use {@link #waitForValue(Supplier)}.
*
* @param supplier the supplier of the value
* @return the value; may be null
@ -549,8 +571,8 @@ public abstract class AbstractGTest {
}
/**
* Waits for the value returned by the supplier to be non-null, optionally
* throwing an exception if that does not happen by the given timeout.
* Waits for the value returned by the supplier to be non-null, optionally throwing an exception
* if that does not happen by the given timeout.
*
* @param supplier the supplier of the value
* @param failureMessage the message to print upon the timeout being reached

View file

@ -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.
@ -15,7 +15,7 @@
*/
package generic.test;
import static org.junit.Assert.*;
import static org.junit.Assert.fail;
import java.awt.*;
import java.awt.image.BufferedImage;
@ -39,8 +39,6 @@ import org.junit.rules.*;
import org.junit.runner.Description;
import generic.jar.ResourceFile;
import generic.test.rule.Repeated;
import generic.test.rule.RepeatedTestRule;
import generic.util.WindowUtilities;
import ghidra.GhidraTestApplicationLayout;
import ghidra.framework.*;
@ -111,17 +109,6 @@ public abstract class AbstractGenericTest extends AbstractGTest {
@Rule
public RuleChain ruleChain = RuleChain.outerRule(testName).around(watchman);// control rule ordering
/**
* This rule handles the {@link Repeated} annotation
*
* <p>
* During batch mode, this rule should never be needed. This rule is included here as a
* convenience, in case a developer wants to use the {@link Repeated} annotation to diagnose a
* non-deterministic test failure. Without this rule, the annotation would be silently ignored.
*/
@Rule
public TestRule repeatedRule = new RepeatedTestRule();
@After
public void resetLogging() {
if (logSettingsChanged) {
@ -237,9 +224,9 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* A callback for subclasses when a test has failed. This will be called
* <b>after</b> <code>tearDown()</code>. This means that any diagnostics will have to
* take into account items that have already been disposed.
* A callback for subclasses when a test has failed. This will be called <b>after</b>
* <code>tearDown()</code>. This means that any diagnostics will have to take into account items
* that have already been disposed.
*
* @param e the exception that happened when the test failed
*/
@ -258,10 +245,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* A convenience method to change the log level of the given logger name. The logger name is
* typically the class name that contains specialized logging. You may also pass a package
* name to get logging for all classes in that package.
* See {@link Configurator#setLevel(String, Level)}
* A convenience method to change the log level of the given logger name. The logger name is
* typically the class name that contains specialized logging. You may also pass a package name
* to get logging for all classes in that package. See
* {@link Configurator#setLevel(String, Level)}
* <P>
* The console appender's log level will be changed if needed to ensure that messages for the
* given log level are displayed.
@ -316,9 +303,8 @@ public abstract class AbstractGenericTest extends AbstractGTest {
* Returns the window parent of c. If c is a window, then c is returned.
*
* <P>
* Warning: this differs from
* {@link SwingUtilities#windowForComponent(Component)} in that the latter
* method will not return the given component if it is a window.
* Warning: this differs from {@link SwingUtilities#windowForComponent(Component)} in that the
* latter method will not return the given component if it is a window.
*
* @param c the component
* @return the window
@ -328,8 +314,8 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Load a text resource file into an ArrayList. Each line of the file is
* stored as an item in the list.
* Load a text resource file into an ArrayList. Each line of the file is stored as an item in
* the list.
*
* @param cls class where resource exists
* @param name resource filename
@ -372,15 +358,14 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Returns the file within the data directory of the TestResources module
* that matches the given relative path
* Returns the file within the data directory of the TestResources module that matches the given
* relative path
* <p>
* A {@link FileNotFoundException} is throw if the file does not exist.
*
* @param path path relative to the data directory of the TestResources
* module.
* @return the file within the data directory of the TestResources module
* that matches the given relative path
* @param path path relative to the data directory of the TestResources module.
* @return the file within the data directory of the TestResources module that matches the given
* relative path
* @throws FileNotFoundException if the given file does not exist
*/
public static File getTestDataFile(String path) throws FileNotFoundException {
@ -390,8 +375,8 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Returns a file that points to the location on disk of the given relative
* path name. The path is relative to the test resources directory.
* Returns a file that points to the location on disk of the given relative path name. The path
* is relative to the test resources directory.
*
* @param relativePath the path of the file
* @return a file that points to the location on disk of the relative path.
@ -406,15 +391,14 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Returns the file within the data directory of the TestResources module
* that matches the given relative path.
* Returns the file within the data directory of the TestResources module that matches the given
* relative path.
* <p>
* Null is returned if the file could not be found.
*
* @param path path relative to the data directory of the TestResources
* module.
* @return the file within the data directory of the TestResources module
* that matches the given relative path
* @param path path relative to the data directory of the TestResources module.
* @return the file within the data directory of the TestResources module that matches the given
* relative path
*/
public static File findTestDataFile(String path) {
try {
@ -445,9 +429,9 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* 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 classType exists within the ownerInstance.
* 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
* classType exists within the ownerInstance.
*
* @param <T> the type
* @param classType the class type of the desired field
@ -461,17 +445,15 @@ public abstract class AbstractGenericTest extends AbstractGTest {
/**
* Sets the instance field by the given name on the given object instance.
* <p>
* Note: if the field is static, then the <code>ownerInstance</code> field can
* be the class of the object that contains the variable.
* Note: if the field is static, then the <code>ownerInstance</code> field can be the class of
* the object that contains the variable.
*
* @param fieldName The name of the field to retrieve.
* @param ownerInstance The object instance from which to get the variable
* instance.
* @param ownerInstance The object instance from which to get the variable instance.
* @param value The value to use when setting the given field
* @throws RuntimeException if there is a problem accessing the field using
* reflection. A RuntimeException is used so that calling tests
* can avoid using a try/catch block, but will still fail when
* an error is encountered.
* @throws RuntimeException if there is a problem accessing the field using reflection. A
* RuntimeException is used so that calling tests can avoid using a try/catch block,
* but will still fail when an error is encountered.
* @see Field#set(Object, Object)
*/
public static void setInstanceField(String fieldName, Object ownerInstance, Object value)
@ -480,20 +462,18 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Gets the instance field by the given name on the given object instance.
* The value is a primitive wrapper if it is a primitive type.
* Gets the instance field by the given name on the given object instance. The value is a
* primitive wrapper if it is a primitive type.
* <p>
* Note: if the field is static, then the <code>ownerInstance</code> field can
* be the class of the object that contains the variable.
* Note: if the field is static, then the <code>ownerInstance</code> field can be the class of
* the object that contains the variable.
*
* @param fieldName The name of the field to retrieve.
* @param ownerInstance The object instance from which to get the variable
* instance.
* @param ownerInstance The object instance from which to get the variable instance.
* @return The field instance.
* @throws RuntimeException if there is a problem accessing the field using
* reflection. A RuntimeException is used so that calling tests
* can avoid using a try/catch block, but will still fail when
* an error is encountered.
* @throws RuntimeException if there is a problem accessing the field using reflection. A
* RuntimeException is used so that calling tests can avoid using a try/catch block,
* but will still fail when an error is encountered.
* @see Field#get(java.lang.Object)
* @since Tracker Id 267
*/
@ -503,22 +483,19 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Uses reflection to execute the constructor for the given class with the
* given parameters. The new instance of the given class will be returned.
* Uses reflection to execute the constructor for the given class with the given parameters. The
* new instance of the given class will be returned.
* <p>
*
* @param containingClass The class that contains the desired constructor.
* @param parameterTypes The parameter <b>types</b> that the constructor
* takes. This value can be null or zero length if there are no
* parameters to pass
* @param args The parameter values that should be passed to the
* constructor. This value can be null or zero length if there
* are no parameters to pass
* @param parameterTypes The parameter <b>types</b> that the constructor takes. This value can
* be null or zero length if there are no parameters to pass
* @param args The parameter values that should be passed to the constructor. This value can be
* null or zero length if there are no parameters to pass
* @return The new class instance
* @throws RuntimeException if there is a problem accessing the constructor
* using reflection. A RuntimeException is used so that calling
* tests can avoid using a try/catch block, but will still fail
* when an error is encountered.
* @throws RuntimeException if there is a problem accessing the constructor using reflection. A
* RuntimeException is used so that calling tests can avoid using a try/catch block,
* but will still fail when an error is encountered.
*/
public static Object invokeConstructor(Class<?> containingClass, Class<?>[] parameterTypes,
Object[] args) throws RuntimeException {
@ -527,26 +504,23 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Uses reflection to execute the method denoted by the given method name.
* If any value is returned from the method execution, then it will be
* returned from this method. Otherwise, <code>null</code> is returned.
* Uses reflection to execute the method denoted by the given method name. If any value is
* returned from the method execution, then it will be returned from this method. Otherwise,
* <code>null</code> is returned.
* <p>
* Note: if the method is static, then the <code>ownerInstance</code> field can
* be the class of the object that contains the method.
* Note: if the method is static, then the <code>ownerInstance</code> field can be the class of
* the object that contains the method.
*
* @param methodName The name of the method to execute.
* @param ownerInstance The object instance of which the method will be
* executed.
* @param ownerInstance The object instance of which the method will be executed.
* @param parameterTypes The parameter <b>types</b> that the method takes.
* @param args The parameter values that should be passed to the method.
* This value can be null or zero length if there are no
* parameters to pass
* @param args The parameter values that should be passed to the method. This value can be null
* or zero length if there are no parameters to pass
* @return The return value as returned from executing the method.
* @see Method#invoke(java.lang.Object, java.lang.Object[])
* @throws RuntimeException if there is a problem accessing the field using
* reflection. A RuntimeException is used so that calling tests
* can avoid using a try/catch block, but will still fail when
* an error is encountered.
* @throws RuntimeException if there is a problem accessing the field using reflection. A
* RuntimeException is used so that calling tests can avoid using a try/catch block,
* but will still fail when an error is encountered.
* @since Tracker Id 267
*/
public static Object invokeInstanceMethod(String methodName, Object ownerInstance,
@ -557,19 +531,16 @@ public abstract class AbstractGenericTest extends AbstractGTest {
/**
* This method is just a "pass through" method for
* {@link #invokeInstanceMethod(String, Object, Class[], Object[])} so that
* callers do not need to pass null to that method when the underlying
* instance method does not have any parameters.
* {@link #invokeInstanceMethod(String, Object, Class[], Object[])} so that callers do not need
* to pass null to that method when the underlying instance method does not have any parameters.
*
* @param methodName The name of the method to execute.
* @param ownerInstance The object instance of which the method will be
* executed.
* @param ownerInstance The object instance of which the method will be executed.
* @return The return value as returned from executing the method.
* @see Method#invoke(java.lang.Object, java.lang.Object[])
* @throws RuntimeException if there is a problem accessing the field using
* reflection. A RuntimeException is used so that calling tests
* can avoid using a try/catch block, but will still fail when
* an error is encountered.
* @throws RuntimeException if there is a problem accessing the field using reflection. A
* RuntimeException is used so that calling tests can avoid using a try/catch block,
* but will still fail when an error is encountered.
* @see #invokeInstanceMethod(String, Object, Class[], Object[])
*/
public static Object invokeInstanceMethod(String methodName, Object ownerInstance)
@ -578,8 +549,8 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Returns a string which is a printout of a stack trace for each thread
* running in the current JVM
* Returns a string which is a printout of a stack trace for each thread running in the current
* JVM
*
* @return the stack trace string
*/
@ -588,8 +559,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Prints the contents of the given collection by way of the
* {@link Object#toString()} method.
* Prints the contents of the given collection by way of the {@link Object#toString()} method.
*
* @param collection The contents of which to print
* @return A string representation of the given collection
@ -616,6 +586,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
/**
* Returns a font metrics for the given font using a generic buffered image graphics context.
*
* @param font the font
* @return the font metrics
*/
@ -628,10 +599,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Signals that the client expected the System Under Test (SUT) to report errors. Use this
* when you wish to verify that errors are reported and you do not want those errors to
* fail the test. The default value for this setting is false, which means that any
* errors reported will fail the running test.
* Signals that the client expected the System Under Test (SUT) to report errors. Use this when
* you wish to verify that errors are reported and you do not want those errors to fail the
* test. The default value for this setting is false, which means that any errors reported will
* fail the running test.
*
* @param expected true if errors are expected.
*/
@ -651,12 +622,12 @@ public abstract class AbstractGenericTest extends AbstractGTest {
//==================================================================================================
/**
* Returns the directory into which tests can write debug files, such as
* files containing print statements or image files.
* Returns the directory into which tests can write debug files, such as files containing print
* statements or image files.
*
* <P>
* This is not a temporary directory that will be deleted between tests,
* which is useful in that the debug files will persist after a test run.
* This is not a temporary directory that will be deleted between tests, which is useful in that
* the debug files will persist after a test run.
*
* <P>
* Examples of this directory:
@ -701,16 +672,14 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a <b>sub-directory</b> with the given name as a child of the Java
* temp directory. The given name will be the prefix of the new directory
* name, with any additional text as created by
* {@link Files#createTempDirectory(Path, String, java.nio.file.attribute.FileAttribute...)}.
* Any left-over test directories will be cleaned-up before creating the new
* directory.
* Creates a <b>sub-directory</b> with the given name as a child of the Java temp directory. The
* given name will be the prefix of the new directory name, with any additional text as created
* by {@link Files#createTempDirectory(Path, String, java.nio.file.attribute.FileAttribute...)}.
* Any left-over test directories will be cleaned-up before creating the new directory.
*
* <p>
* Note: you should not call this method multiple times, as each call will
* cleanup the previously created directories.
* Note: you should not call this method multiple times, as each call will cleanup the
* previously created directories.
*
* @param name the name of the directory to create
* @return the newly created directory
@ -738,14 +707,13 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file path with a filename that is under the system temp
* directory. The path returned will not point to an existing file. The
* suffix of the file will be <code>.tmp</code>.
* Creates a file path with a filename that is under the system temp directory. The path
* returned will not point to an existing file. The suffix of the file will be
* <code>.tmp</code>.
*
* @param name the filename
* @return a new file path
* @throws IOException if there is any problem ensuring that the created
* path is non-existent
* @throws IOException if there is any problem ensuring that the created path is non-existent
* @see #createTempFilePath(String, String)
*/
public String createTempFilePath(String name) throws IOException {
@ -754,16 +722,14 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file path with a filename that is under the system temp
* directory. The path returned will not point to an existing file. This
* method is the same as {@link #createTempFilePath(String)}, except that
* you must provide the extension.
* Creates a file path with a filename that is under the system temp directory. The path
* returned will not point to an existing file. This method is the same as
* {@link #createTempFilePath(String)}, except that you must provide the extension.
*
* @param name the filename
* @param extension the file extension
* @return a new file path
* @throws IOException if there is any problem ensuring that the created
* path is non-existent
* @throws IOException if there is any problem ensuring that the created path is non-existent
* @see #createTempFile(String, String)
*/
public String createTempFilePath(String name, String extension) throws IOException {
@ -773,10 +739,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a temp file for the current test, using the test name as a prefix
* for the filename. This method calls {@link #createTempFile(String)},
* which will cleanup any pre-existing temp files whose name pattern matches
* this test name. This helps to avoid old temp files from accumulating.
* Creates a temp file for the current test, using the test name as a prefix for the filename.
* This method calls {@link #createTempFile(String)}, which will cleanup any pre-existing temp
* files whose name pattern matches this test name. This helps to avoid old temp files from
* accumulating.
*
* @return the new temp file
* @throws IOException if there is a problem creating the new file
@ -786,10 +752,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a temp file for the current test, using the test name as a prefix
* for the filename. This method calls {@link #createTempFile(String)},
* which will cleanup any pre-existing temp files whose name pattern matches
* this test name. This helps to avoid old temp files from accumulating.
* Creates a temp file for the current test, using the test name as a prefix for the filename.
* This method calls {@link #createTempFile(String)}, which will cleanup any pre-existing temp
* files whose name pattern matches this test name. This helps to avoid old temp files from
* accumulating.
*
* @param suffix the suffix to provide for the temp file
* @return the new temp file
@ -800,15 +766,13 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file in the Application temp directory using the given name as a
* prefix and the given suffix. The final filename will also include the
* current test name, as well as any data added by
* {@link File#createTempFile(String, String, File)}. The file suffix will be
* Creates a file in the Application temp directory using the given name as a prefix and the
* given suffix. The final filename will also include the current test name, as well as any data
* added by {@link File#createTempFile(String, String, File)}. The file suffix will be
* <code>.tmp</code>
* <p>
* The file will be marked to delete on JVM exit. This will not work if the
* JVM is taken down the hard way, as when pressing the stop button in
* Eclipse.
* The file will be marked to delete on JVM exit. This will not work if the JVM is taken down
* the hard way, as when pressing the stop button in Eclipse.
*
* @param name the prefix to put on the file, before the test name
* @return the newly created file
@ -821,25 +785,22 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file in the Application temp directory using the given name as a
* prefix and the given suffix. The final filename will also include the
* current test name, as well as any data added by
* {@link File#createTempFile(String, String, File)}.
* Creates a file in the Application temp directory using the given name as a prefix and the
* given suffix. The final filename will also include the current test name, as well as any data
* added by {@link File#createTempFile(String, String, File)}.
* <p>
* The file will be marked to delete on JVM exit. This will not work if the
* JVM is taken down the hard way, as when pressing the stop button in
* Eclipse.
* The file will be marked to delete on JVM exit. This will not work if the JVM is taken down
* the hard way, as when pressing the stop button in Eclipse.
* <p>
* Note: This method <b>will</b> create the file on disk! If you need the
* file to not exist, then you must delete the file yourself. Alternatively,
* you could instead call {@link #createTempFilePath(String, String)}, which
* will ensure that the created temp file is deleted.
* Note: This method <b>will</b> create the file on disk! If you need the file to not exist,
* then you must delete the file yourself. Alternatively, you could instead call
* {@link #createTempFilePath(String, String)}, which will ensure that the created temp file is
* deleted.
*
* <p>
* Finally, this method will delete any files that match the given name and
* suffix values before creating the given temp file. <b>This is important,
* as it will delete any files already created by the test that match this
* info.</b>
* Finally, this method will delete any files that match the given name and suffix values before
* creating the given temp file. <b>This is important, as it will delete any files already
* created by the test that match this info.</b>
*
* @param name the prefix to put on the file, before the test name
* @param suffix the file suffix
@ -866,8 +827,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Delete any files under the Java temp directory that have the given text
* in their name.
* Delete any files under the Java temp directory that have the given text in their name.
*
* @param nameText the partial name text to match against the files
* @see #deleteMatchingTempFiles(String)
@ -880,8 +840,8 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Delete any files under the this test case's specific temp directory that
* match the give regex {@link Pattern}
* Delete any files under the this test case's specific temp directory that match the give regex
* {@link Pattern}
*
* @param namePattern the pattern to match against the files
* @see #deleteSimilarTempFiles(String)

View file

@ -0,0 +1,38 @@
/* ###
* 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 generic.test.rule;
import java.lang.annotation.*;
import ghidra.lifecycle.Unfinished.TODOException;
/**
* Ignore failures due to {@link TODOException}
*
* <p>
* As a matter of practice, tests ought not to be committed into source control with this
* annotation. Or, if they are, they should only have this for a short period. Production code ought
* not to be throwing {@link TODOException}, anyway, but the reality is, sometimes things are
* "production ready," despite having some unfinished components. This annotation allows tests that
* identify those unfinished portions to remain active, but ignored. During development, the
* developer may also apply this annotation to distinguish "real" failures from those already
* identified as "unfinished." The annotation ought to be removed when it's time to finish those
* components, so that failures due to unfinished code are quickly identified.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface IgnoreUnfinished {
}

View file

@ -0,0 +1,47 @@
/* ###
* 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 generic.test.rule;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import generic.test.AbstractGenericTest;
/**
* A test rule which processes the {@link IgnoreUnfinished} annotation
*
* <p>
* This must be included in your test case (or a superclass) as a field with the {@link Rule}
* annotation. It's included in the {@link AbstractGenericTest}, so most Ghidra test classes already
* have it.
*/
public class IgnoreUnfinishedRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
IgnoreUnfinished annot;
annot = description.getAnnotation(IgnoreUnfinished.class);
if (annot != null) {
return new IgnoreUnfinishedStatement(base);
}
annot = description.getTestClass().getAnnotation(IgnoreUnfinished.class);
if (annot != null) {
return new IgnoreUnfinishedStatement(base);
}
return base;
}
}

View file

@ -0,0 +1,44 @@
/* ###
* 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 generic.test.rule;
import org.junit.AssumptionViolatedException;
import org.junit.runners.model.Statement;
import ghidra.lifecycle.Unfinished.TODOException;
/**
* A JUnit test statement that ignores {@link TODOException}
*
* @see IgnoreUnfinished
*/
public class IgnoreUnfinishedStatement extends Statement {
private final Statement base;
public IgnoreUnfinishedStatement(Statement base) {
this.base = base;
}
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
}
catch (TODOException e) {
throw new AssumptionViolatedException("Unfinished", e);
}
}
}

View file

@ -0,0 +1,31 @@
/* ###
* 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.lifecycle;
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Target;
/**
* An annotation for experimental things
*
* <p>
* The items are intended to become part of the public API, but the interfaces are unstable, and
* there's no guarantee they will ever become public.
*/
@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE, PARAMETER })
public @interface Experimental {
}

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.lifecycle;
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Target;
/**
* An annotation for things internal to an implementation
*
* For organization, some interfaces and classes exist in packages outside where they are used, and
* method access is required. Java allows those methods to be accessed from any package. This
* annotation is applied to public methods which should not be accessed outside the implementation.
*
* A decent way to manually verify this is to ensure that any method marked with this annotation is
* not listed in the exported interface. Generally, this means no method should have both
* {@link Override} and {@link Internal} applied.
*/
@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE })
public @interface Internal {
// TODO: Is it possible to warn when used outside the jar?
// TODO: Is it possible to warn when also overrides an interface method?
}

View file

@ -0,0 +1,24 @@
/* ###
* 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.lifecycle;
/**
* The item is present for transitional purposes only and will soon be removed
*/
public @interface Transitional {
}

View file

@ -0,0 +1,56 @@
/* ###
* 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.lifecycle;
/**
* This serves both as a marker interface for classes missing important methods and as container for
* the {@link #TODO(String, Object...)} method.
*
* <p>
* TODO: It'd be nice to optionally ignore TODO exceptions, but this seems to require a dependency
* on JUnit, which is a no-no within {@code src/main}. Maybe there's a way via the abstract test
* case, or an interface mixin....
*/
public interface Unfinished {
public class TODOException extends UnsupportedOperationException {
public TODOException(String message) {
super(message);
}
public TODOException() {
this("TODO");
}
}
/**
* Perhaps a little better than returning {@code null} or throwing
* {@link UnsupportedOperationException} yourself, as references can be found in most IDEs.
*
* @param message A message describing the task that is yet to be done
* @param ignore variables involved in the implementation so far
*/
static <T> T TODO(String message, Object... ignore) {
throw new TODOException(message);
}
/**
* Perhaps a little better than returning {@code null} or throwing
* {@link UnsupportedOperationException} yourself, as references can be found in most IDEs.
*/
static <T> T TODO() {
throw new TODOException();
}
}

View file

@ -15,6 +15,8 @@
*/
package ghidra.util;
import java.util.Comparator;
public class MathUtilities {
private MathUtilities() {
@ -204,4 +206,20 @@ public class MathUtilities {
public static long unsignedMax(long a, int b) {
return (Long.compareUnsigned(a, b & 0x0ffffffffL) > 0) ? a : b;
}
public static <C> C cmin(C a, C b, Comparator<C> comp) {
return comp.compare(a, b) <= 0 ? a : b;
}
public static <C extends Comparable<C>> C cmin(C a, C b) {
return cmin(a, b, Comparator.naturalOrder());
}
public static <C> C cmax(C a, C b, Comparator<C> comp) {
return comp.compare(a, b) >= 0 ? a : b;
}
public static <C extends Comparable<C>> C cmax(C a, C b) {
return cmax(a, b, Comparator.naturalOrder());
}
}