Merge tag 'Ghidra_9.0.2_build' into stable

This commit is contained in:
ghidra1 2019-04-03 15:41:31 -04:00
commit 5de10ed2a9
143 changed files with 1831 additions and 2863 deletions

1
.gitignore vendored
View File

@ -23,6 +23,7 @@ ghidra.repos.config
**/dist
repositories/
*.sla
**/data/build.xml
# Misc files
*.setting

View File

@ -1,24 +1,54 @@
# Developer's Guide: Getting Started
# Developer's Guide
## Catalog of Dependencies
The following is a list of dependencies, in no particular order.
This guide includes instructions for obtaining each of these at the relevant step(s).
You may not need all of these, depending on which portions you are building or developing.
* JDK 11 - We test and build using OpenJDK 11.0.2.
* Eclipse - It must support JDK 11. Eclipse 2018-12 or later should work. Other IDEs may work, but we have not tested them.
* Gradle 5.0 - Later versions may work, but you'll need to modify our version check.
* A C/C++ compiler - We use Visual Studio 2017 on Windows, GCC on Linux, and Xcode (Clang) on macOS.
* Git - We use git-bash on Windows. Most Linux distros have git in their repos. Xcode provides git on macOS.
* Bash - This is moot on Linux and macOS. On Windows, we use MinGW. This may be distributed with Git for Windows.
* Bison and Flex - We use MinGW on Windows. Most Linux distros have these in their reposs. Xcode provides these for macOS.
* dex2jar
* AXMLPrinter2
* HFS Explorer 0.21
* Yet Another Java Service Wrapper 12.12 - Only to build Ghidra package.
* Eclipse PDE - Only for the GhidraDev plugin.
* Eclipse CDT 8.6.0 - Only for the GhidraDev plugin
* PyDev 6.3.1 - Only for the GhidraDev plugin
There are many others automatically downloaded by Gradle from Maven Central and Bintray JCenter when building and/or setting up the development environment.
## Install Development and Build Tools
If you're on Windows, install Git.
You may also need to install MinGW.
Many of the commands given below must be executed in Bash.
Install OpenJDK 11 and make sure it's the default java.
Install Eclipse, at least version 2018-12, and ensure it is launched using OpenJDK 11.
Technically, you can launch with any JRE/JDK, but it's up to you ensure OpenJDK 11 is properly configured in Eclipse.
Install Eclipse.
You can launch Eclipse with any JRE/JDK, but you'll need to ensure Eclipse knows about your JDK 11 installation.
In Eclipse, select Window -> Prefereces (Eclipse -> Preferences on macOS), then navigate to Java -> Installed JREs, and ensure a JDK 11 is configured.
Optionally install Gradle 5.0, and ensure it is launched using OpenJDK 11.
These instructions assume you are using the gradle wrapper, so adjust the commands accordingly if you choose to use your own Gradle installation.
Install Gradle, add it to your `PATH`, and ensure it is launched using JDK 11.
## Setup Repositories
## Setup Source and Dependency Repositories
Of course, you may choose any directory for your working copy, but these instructions will assume you have cloned the repo to `~/git`.
You may choose any directory for your working copy, but these instructions will assume you have cloned the source to `~/git/ghidra`.
Be sure to adjust the commands to match your chosen working directory if different than suggested:
```bash
mkdir ~/git
cd ~/git
git clone git@github.com:NationalSecurityAgency/ghidra.git
```
Ghidra's build uses artifact named as available in Maven Central and Bintray JCenter, when possible.
Ghidra's build uses artifacts named as available in Maven Central and Bintray JCenter, when possible.
Unfortunately, in some cases, the artifact or the particular version we desire is not available.
So, in addition to mavenCentral and jcenter, you must configure a flatDir-style repository for manually-downloaded dependencies.
@ -36,7 +66,7 @@ allprojects {
}
```
You should also create the `~/flatRepo` folder to hold the manually-downloaded dependencies:
Create the `~/flatRepo` folder to hold the manually-downloaded dependencies:
```bash
mkdir ~/flatRepo
@ -83,34 +113,44 @@ cp csframework.jar hfsx_dmglib.jar hfsx.jar iharder-base64.jar ~/flatRepo/
## Import Gradle Project
At this point, you may import Ghidra into Eclipse using the integrated BuildShip plugin.
If you prefer another IDE, there's no reason it shouldn't work, but you're on your own.
Note that the GhidraDevPlugin requires Eclipse PDE.
Close this project to clean up the errors, unless you are developing the GhidraDevPlugin.
If you want just to build Ghidra, you may skip ahead to Building Ghidra.
Otherwise, import Ghidra into Eclipse using the integrated BuildShip plugin.
Select File -> Import, expand Gradle, and select "Existing Gradle Project."
Select the root of the source repo as the root Gradle project.
Be sure to select Gradle 5.0, or point it at your local installation.
You may see build path errors until the environment is properly prepared, as described below.
*Alternatively*, you may have Gradle generate the Eclipse projects and import those instead.
This is the way to go if you'd prefer not to activate Gradle's BuildShip plugin.
From the project root:
```bash
gradle eclipse
```
Select File -> Import, expand General, and select "Existing Projects into Workspace."
Select the root of the source repo, and select "Search for nested projects."
Select all, and Finish.
You may see build path errors until the environment is properly prepared, as described below.
## Prepare the Environment
There are a few preparatory tasks you should execute before, or immediately after, importing the project.
These tasks will build and index the online help, and place it somewhere accessible to Ghidra when launched from Eclipse, among other things.
This task also attempts to unpack some SDKs and/or larger dependencies required by Ghidra.
We do not provide these packages out-of-the-box because of technical and legal constraints on our distributing them.
These include the Eclipse CDT, PyDev for Eclipse, and "Yet another Java service wrapper."
If you would like to build the dependent modules, please see the relevant sections below.
For now, we will exclude the affected unpack tasks.
From the project root, execute:
```bash
./gradlew prepDev -x yajswDevUnpack
gradle prepDev -x yajswDevUnpack
```
The `prepDev` tasks primarily include generating some source, indexing our built-in help, and unpacking some dependencies.
Regarding `yajswDevUnpack`, please see the relevant sections on GhidraServer below.
For now, we exclude the unpack task.
Optionally, to pre-compile all the language modules, you may also execute:
```bash
./gradlew sleighCompile
gradle sleighCompile
```
Refresh the Gradle project in Eclipse.
Refresh the projects in Eclipse.
You should not see any errors at this point, and you can accomplish many development tasks.
However, some features of Ghidra will not be functional until further steps are taken.
@ -118,7 +158,7 @@ However, some features of Ghidra will not be functional until further steps are
Some of Ghidra's components are built for the native platform.
We currently support Linux, macOS, and Windows 64-bit x86 systems.
Others should be possible, but we do not support them.
Others should be possible, but we do not test on them.
#### decompile
@ -128,18 +168,18 @@ Now build using Gradle:
On Linux:
```bash
./gradlew decompileLinux64Executable
gradle decompileLinux64Executable
```
On macOS:
```bash
./gradlew decompileOsx64Executable
gradle decompileOsx64Executable
```
On Windows:
```cmd
gradlew decompileWin64Executable
gradle decompileWin64Executable
```
#### demangler_gnu
@ -149,80 +189,61 @@ Build using Gradle:
On Linux:
```bash
./gradlew demangler_gnuLinux64Executable
gradle demangler_gnuLinux64Executable
```
On macOS:
```bash
./gradlew demangler_gnuOsx64Executable
gradle demangler_gnuOsx64Executable
```
On Windows:
```cmd
gradlew demangler_gnuWin64Executable
gradle demangler_gnuWin64Executable
```
#### sleigh
The sleigh compiler has been ported to Java, and Ghidra will automatically compile slaspec files that it finds are out of date.
The native sleigh compiler may still be useful for those who'd like quicker feedback by compiling from the command line. To build the native sleigh compiler, install bison and flex.
The sleigh compiler has been ported to Java and integrated with Ghidra.
The native sleigh compiler may still be useful for those who'd like quicker feedback by compiling from the command line.
To build the native sleigh compiler, install bison and flex.
Now, use Gradle:
On Linux:
```bash
./gradlew sleighLinux64Executable
gradle sleighLinux64Executable
```
On macOS:
```bash
./gradlew sleighOsx64Executable
gradle sleighOsx64Executable
```
On Windows:
```cmd
gradlew sleighWin64Executable
gradle sleighWin64Executable
```
### Get Dependencies for GhidraDev
## Run Ghidra from Eclipse
Building the GhidraDev plugin for Eclipse requires the CDT and PyDev plugins for Eclipse.
Download `cdt-8.6.0.zip` from The Eclipse Foundation, and place it in a directory named:
`ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/`.
`ghidra.bin` must be a sibling of `ghidra`.
To respect the CDT project's resources, you will need to download the file using a browser, or at the very least, locate a suitable mirror on your own:
To run or debug Ghidra from Eclipse, use the provided launch configuration (usually under the "Run" or "Debug" buttons).
If the launcher does not appear, it probably has not been marked as a favorite.
Click the dropdown next to the "Run" button and select "Run Configurations."
Then expand "Java Application" on the left to find the "Ghidra" launcher.
```bash
cd ~/Downloads # Or wherever
curl -OL http://$CHOOSE_YOUR_MIRROR/pub/eclipse/tools/cdt/releases/8.6/cdt-8.6.0.zip
mkdir -p ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
cp ~/Downloads/cdt-8.6.0.zip ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
```
Download `PyDev 6.3.1.zip` from www.pydev.org, and place it in the same directory:
# Building Ghidra
```bash
cd ~/Downloads # Or wherever
curl -OL https://sourceforge.net/projects/pydev/files/pydev/PyDev%206.3.1/PyDev%206.3.1.zip
cp ~/Downloads/'PyDev 6.3.1.zip' ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
```
To build the full Ghidra distribution, you must also build the GhidraServer.
Use Gradle to unpack the dependencies for development and building.
First, you will need to uncomment the GhidraDev project in the ```settings.gradle``` file.
Then, from your clone:
```bash
./gradlew cdtUnpack pyDevUnpack
```
### Get Dependencies for GhidraServer
## Get Dependencies for GhidraServer
Building the GhidraServer requires "Yet another Java service wrapper" (yajsw) version 12.12.
Note that building the full Ghidra package requires building the GhidraServer.
Download `yajsw-stable-12.12.zip` from their project on www.sourceforge.net, and place it in a directory named:
`ghidra.bin/Ghidra/Features/GhidraSerer/`:
`ghidra.bin/Ghidra/Features/GhidraServer/`. Note that `ghidra.bin` must be a sibling of `ghidra`:
```bash
cd ~/Downloads # Or wherever
@ -235,23 +256,23 @@ Use Gradle to unpack the wrapper for development.
From your clone:
```bash
./gradlew yajswDevUnpack
gradle yajswDevUnpack
```
# Build the full Ghidra package
## Building the Package
If you've followed all of the steps above, except perhaps importing to Eclipse, you should be able to produce a build.
Before building, you may want to update the version and release name.
These properties are kept in `Ghidra/application.properties`.
If you want it included, you must also build the GhidraDevPlugin module first.
We do not yet have instructions for building the GhidraDevPlugin.
It should be relatively straightforward for anyone familiar with Eclipse PDE.
Some supporting data will also be missing.
See the sections below for instructions to produce these components.
You may also be able to copy some of this data from a previous official distribution.
To build the full package, use Gradle:
```bash
./gradlew buildGhidra
gradle buildGhidra
```
The output will be placed in `build/dist/`.
@ -262,9 +283,8 @@ To test it, unzip it where you like, and execute `./ghidraRun`.
Some features of Ghidra require the curation of rather extensive data bases.
These include the Data Type Archives and Function ID Databases, both of which require collecting header files and libraries for the relevant SDKs and platforms.
Much of this work is done by hand, and the results are simply copied into the build.
We intend to document these procedures as soon as we can.
In the meantime, those artifacts can always be extracted from our binary release.
Much of this work is done by hand.
Until this process is documented, those artifacts can be extracted from an official distribution and combined with your build output.
## Building Data Type Archives
@ -273,3 +293,51 @@ TODO
## Building FID Databases
TODO
# Developing / Building the GhidraDev Plugin
First, install the Eclipse Plugin Development Environment (PDE).
By default, the GhidraDev project is excluded from the build.
To enable it, uncomment it in `settings.gradle`.
```bash
${EDITOR:-vi} settings.gradle
```
You will need some additional runtime dependencies:
## Get Dependencies for GhidraDev
Building the GhidraDev plugin for Eclipse requires the CDT and PyDev plugins for Eclipse.
Download `cdt-8.6.0.zip` from The Eclipse Foundation, and place it in a directory named:
`ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/`. Note that
`ghidra.bin` must be a sibling of `ghidra`.
To respect the Eclipse Project's resources, you may need to download the file using a browser, or at the very least, locate a suitable mirror on your own:
```bash
cd ~/Downloads # Or wherever
curl -OL https://$CHOOSE_YOUR_MIRROR/pub/eclipse/tools/cdt/releases/8.6/cdt-8.6.0.zip
mkdir -p ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
cp ~/Downloads/cdt-8.6.0.zip ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
```
Download `PyDev 6.3.1.zip` from www.pydev.org, and place it in the same directory:
```bash
cd ~/Downloads # Or wherever
curl -OL https://sourceforge.net/projects/pydev/files/pydev/PyDev%206.3.1/PyDev%206.3.1.zip
cp ~/Downloads/'PyDev 6.3.1.zip' ~/git/ghidra.bin/GhidraBuild/EclipsePlugins/GhidraDev/buildDependencies/
```
Use Gradle to unpack the dependencies.
Note that these tasks will not work until you enable the GhidraDev project in `settings.gradle`.
From your clone:
```bash
gradle cdtUnpack pyDevUnpack
```
## Import the GhidraDev Project
If you're using BuildShip, simply refresh the Gradle project in Eclipse.
If you're not using BuildShip, re-run `gradle eclipse` and import the new project.

View File

@ -6,6 +6,45 @@
</HEAD>
<BODY>
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
<blockquote><p><u>Bugs</u></p></blockquote>
<blockquote>
<ul>
<li><I>Analysis.</I> Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li>
<li><I>Decompiler. </I> Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li>
<li><I>Decompiler. </I> Fixed decompiler handling of Function Definition data types. (Issue #247) </li>
<li><I>Decompiler. </I> Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li>
<li><I>Diff. </I> Fixed exceptions that can occur in the Diff View for programs with overlays. </li>
<li><I>Documentation. </I> Corrected the spelling of "listener" throughout the source code. (Issue #235) </li>
<li><I>Exporter. </I> Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li>
<li><I>GUI. </I> Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
<li><I>GUI.</I> Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
<li><I>Languages. </I> The ARM Thumb CMP.W and LSL isntructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
<li><I>Multi-User:Ghidra Server. </I> Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
<li><I>Scripting.</I> MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
</ul>
</blockquote>
<blockquote><p><u>Security</u></p></blockquote>
<blockquote>
<ul>
<li><I>Basic Infrastructure. </I> Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
</ul>
</blockquote>
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
<blockquote><p><u>New Features</u></p></blockquote>

View File

@ -22,7 +22,7 @@
<file_extension extension=".java" icon="images/famfamfam_silk_icons_v013/page_white_cup.png" />
<file_extension extension=".kext" icon="images/famfamfam_silk_icons_v013/bullet_pink.png" />
<file_extension extension=".lib" icon="images/famfamfam_silk_icons_v013/server.png" />
<file_extension extension=".obj" icon="images/oxygen/16x16/subrip.png" />
<file_extension extension=".obj" icon="images/oxygen/16x16/application-x-subrip.png" />
<file_extension extension=".p" icon="images/oxygen/16x16/text-x-pascal.png" />
<file_extension extension=".pdf" icon="images/oxygen/16x16/application-pdf.png" />
<file_extension extension=".plist" icon="images/oxygen/16x16/insert-table.png" />

View File

@ -39,37 +39,59 @@
//@category Analysis
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.address.AddressRangeIterator;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.block.PartitionCodeSubModel;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.OperandType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.*;
import ghidra.program.util.ContextEvaluator;
import ghidra.program.util.ContextEvaluatorAdapter;
import ghidra.program.util.OperandFieldLocation;
import ghidra.program.util.SymbolicPropogator;
import ghidra.program.util.VarnodeContext;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class MultiInstructionMemReference extends GhidraScript {
Address memReferenceLocation = null;
private Address curInstrloc;
private Object[] inputObjects;
private Object[] resultObjects;
private Register singleRegister;
private boolean registerInOut;
@Override
public void run() throws Exception {
long numInstructions = currentProgram.getListing().getNumInstructions();
monitor.initialize((int) (numInstructions));
monitor.setMessage("Multi-Instruction Reference Markup");
int currentOpIndex = 0;
int currentOpIndex = -1;
Address start = currentLocation.getAddress();
if ((currentSelection == null || currentSelection.isEmpty()) &&
currentLocation instanceof OperandFieldLocation) {
currentOpIndex = ((OperandFieldLocation) currentLocation).getOperandIndex();
OperandFieldLocation operandLocation = (OperandFieldLocation) currentLocation;
currentOpIndex = operandLocation.getOperandIndex();
int subOpIndex = operandLocation.getSubOperandIndex();
singleRegister = getRegister(start, currentOpIndex, subOpIndex);
}
// set up the address set to restrict processing
@ -81,6 +103,34 @@ public class MultiInstructionMemReference extends GhidraScript {
findMemRefAtOperand(currentOpIndex, refLocationsSet);
}
/**
* Get the register at the location
*
* @param opIndex index into operands for instruction
* @param subOpIndex index into operands for an operand location
*
* @return register if there is one at the location
*/
private Register getRegister(Address addr, int opIndex, int subOpIndex) {
if (addr == null) {
return null;
}
Instruction instr = currentProgram.getListing().getInstructionContaining(addr);
if (instr == null) {
return null;
}
List<Object> defOpRep = instr.getDefaultOperandRepresentationList(opIndex);
if (subOpIndex >= 0 && subOpIndex < defOpRep.size()) {
Object obj = defOpRep.get(subOpIndex);
if (obj instanceof Register) {
return (Register) obj;
}
}
return instr.getRegister(opIndex);
}
@SuppressWarnings("unused")
private boolean isSingleInstructions(AddressSet restrictedSet) {
if (restrictedSet.isEmpty()) {
@ -106,35 +156,90 @@ public class MultiInstructionMemReference extends GhidraScript {
// use context to fill out addresses on certain instructions
ContextEvaluator eval = new ContextEvaluatorAdapter() {
@Override
public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) {
// if the requested reference was on an input op-object, get context before exec
return checkContext(true, opIndex, context, instr);
}
@Override
public boolean evaluateContext(VarnodeContext context, Instruction instr) {
// TODO: could look at instructions like LEA, that are an address to create a reference to something.
// if the requested reference was on an output op-object, get context after exec
return checkContext(false, opIndex, context, instr);
}
private boolean checkContext(boolean input, final int opIndex, VarnodeContext context, Instruction instr) {
if (instr.getMinAddress().equals(curInstrloc)) {
if (checkInstructionMatch(opIndex, context, instr)) {
if (checkInstructionMatch(opIndex, input, context, instr)) {
return true;
}
// if instruction is in delayslot, assume reference is good.
if (instr.getDelaySlotDepth() > 0) {
instr = instr.getNext();
return checkInstructionMatch(opIndex, context, instr);
return checkInstructionMatch(opIndex, input, context, instr);
}
}
return false;
}
private boolean checkInstructionMatch(final int opIdx, VarnodeContext context,
Instruction instr) {
int firstIndex = opIdx;
if (instr.getRegister(firstIndex) == null) {
firstIndex = 0;
@Override
public boolean evaluateReference(VarnodeContext context, Instruction instr, int pcodeop, Address address,
int size, RefType refType) {
return super.evaluateReference(context, instr, pcodeop, address, size, refType);
}
for (int index = firstIndex; index < instr.getNumOperands(); index++) {
Object[] opObjects = instr.getOpObjects(index);
private boolean checkInstructionMatch(final int opIdx, boolean input, VarnodeContext context,
Instruction instr) {
List<Object> list = Arrays.asList(input ? inputObjects : resultObjects);
for (int index = opIdx; index < instr.getNumOperands(); index++)
{
if (getRefsForOperand(context, instr, list, index)) {
// register is both an in/out check if symbolic on out
if (registerInOut) {
break;
}
return true;
}
}
if (addSymbolicRefs(input, context, instr, list)) {
return true;
}
return false;
}
/**
* Check the current operand for references based on input/outputs
*
* @param context - context holding values
* @param instr - instruction under consideration
* @param list - input/output lists
* @param opIndex - index of operand to check
*
* @return true if a reference was found
*/
private boolean getRefsForOperand(VarnodeContext context, Instruction instr, List<Object> list, int opIndex) {
Object[] opObjects = instr.getOpObjects(opIndex);
for (int indexOpObj = 0; indexOpObj < opObjects.length; indexOpObj++) {
if (!(opObjects[indexOpObj] instanceof Register)) {
continue;
}
Register reg = (Register) opObjects[indexOpObj];
// if operand has a single register and this isn't it
if (singleRegister != null && !reg.equals(singleRegister)) {
continue;
}
// check that the register is on the correct input/output list
if (!list.contains(reg)) {
continue;
}
RegisterValue rval = context.getRegisterValue(reg);
if (rval == null) {
continue;
@ -144,15 +249,61 @@ public class MultiInstructionMemReference extends GhidraScript {
continue;
}
long offset = uval.longValue();
AddressSpace space = instr.getMinAddress().getAddressSpace();
Address addr = space.getTruncatedAddress(offset, true);
// assume that they want the reference, don't worry it isn't in memory
makeReference(instr, index, addr, monitor);
makeReference(instr, opIndex, addr);
return true;
}
return false;
}
private boolean addSymbolicRefs(boolean input, VarnodeContext context, Instruction instr, List<Object> list) {
// get the value of the single register to see if this is the value desired
if (singleRegister == null) {
return false;
}
// check that the register is on the correct input/output list
if (!list.contains(singleRegister)) {
return false;
}
Varnode registerVarnodeValue = context.getRegisterVarnodeValue(singleRegister);
if (!context.isSymbol(registerVarnodeValue) && !registerVarnodeValue.isRegister()) {
return false;
}
Address symAddr = registerVarnodeValue.getAddress();
if (symAddr == context.BAD_ADDRESS) {
return false;
}
String valStr = "";
if (registerVarnodeValue.isRegister()) {
valStr = context.getRegister(registerVarnodeValue).toString();
} else {
// is an offset from a space
String name = symAddr.getAddressSpace().getName();
BigInteger offset = symAddr.getOffsetAsBigInteger();
valStr = name + " + 0x" + offset.toString(16);
}
Address lastSetLocation = context.getLastSetLocation(singleRegister, null);
String comment = instr.getComment(Instruction.EOL_COMMENT);
if (comment == null) {
comment = "";
}
String inoutChar = (input ? " " : "\'");
String lastStr = (lastSetLocation != null ? " @" + lastSetLocation : "");
String markup = singleRegister+inoutChar+"= "+ valStr + lastStr;
if (comment.replace('\'',' ').contains(markup.replace('\'',' '))) {
return false;
}
comment = (comment.trim().length()==0 ? markup : comment + "\n" + markup);
instr.setComment(Instruction.EOL_COMMENT, comment);
return false;
}
@ -188,8 +339,14 @@ public class MultiInstructionMemReference extends GhidraScript {
}
}
// if the instruction attempting to markup is in the delayslot, backup an instruction
Instruction instr = currentProgram.getListing().getInstructionAt(curInstrloc);
if (instr != null) {
inputObjects = instr.getInputObjects();
resultObjects = instr.getResultObjects();
registerInOut = checkRegisterInOut(singleRegister, inputObjects, resultObjects);
}
// if the instruction attempting to markup is in the delayslot, backup an instruction
if (instr != null && instr.isInDelaySlot()) {
instr = instr.getPrevious();
if (instr != null) {
@ -209,16 +366,24 @@ public class MultiInstructionMemReference extends GhidraScript {
}
}
/**
* @param instruction
* @param space
* @param scalar
* @param nextInstr
* @param addend
* @param taskMonitor
private boolean checkRegisterInOut(Register reg, Object[] in, Object[] out) {
if (reg == null || in == null || out == null) {
return false;
}
List<Object> inList = Arrays.asList(in);
List<Object> outList = Arrays.asList(out);
return inList.contains(reg) && outList.contains(reg);
}
/** Make the reference on the instruction at the correct location.
*
* @param instruction to receive reference
* @param space reference created in this space
* @param scalar used as offset into address space
*/
private void makeReference(Instruction instruction, int opIndex, Address addr,
TaskMonitor taskMonitor) {
private void makeReference(Instruction instruction, int opIndex, Address addr) {
if (instruction.getPrototype().hasDelaySlots()) {
instruction = instruction.getNext();
if (instruction == null) {
@ -239,6 +404,13 @@ public class MultiInstructionMemReference extends GhidraScript {
opIndex = instruction.getNumOperands() - 1;
}
// check if it already has the reference
Reference[] referencesFrom = instruction.getReferencesFrom();
boolean hasRef = Arrays.stream(referencesFrom).anyMatch(p -> p.getToAddress().equals(addr));
if (hasRef) {
return;
}
if (opIndex == -1) {
instruction.addMnemonicReference(addr, RefType.DATA, SourceType.ANALYSIS);
}

View File

@ -306,6 +306,12 @@
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
only supports one address space. This option will be intialized to the "default"
address space.</LI>
<LI><B>Record Size</B> - Specifies the size (in bytes) of each record in the
output file. The default 16.</LI>
<LI><B>Align To Record Size</B> - If checked, this will ensure that <b>only</b> records matching
the record size will be output. eg: if you set the record size to 16 but there are
18 bytes selected, you will see only one line of 16 bytes in the output; the remaining
2 bytes will be dropped.</LI>
</UL>
</BLOCKQUOTE>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,20 +15,28 @@
*/
package foundation;
import ghidra.app.factory.*;
import ghidra.app.util.*;
import ghidra.framework.*;
import ghidra.framework.data.*;
import ghidra.framework.main.datatree.*;
import ghidra.app.factory.GhidraToolStateFactory;
import ghidra.app.util.GhidraFileOpenDataFlavorHandlerService;
import ghidra.framework.ModuleInitializer;
import ghidra.framework.PluggableServiceRegistry;
import ghidra.framework.data.ToolStateFactory;
import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService;
import ghidra.program.database.*;
public class FoundationInitializer implements ModuleInitializer {
@Override
public void run() {
PluggableServiceRegistry.registerPluggableService( ToolStateFactory.class, new GhidraToolStateFactory() );
PluggableServiceRegistry.registerPluggableService( DataFlavorHandlerService.class, new GhidraDataFlavorHandlerService() );
PluggableServiceRegistry.registerPluggableService( FileOpenDataFlavorHandlerService.class, new GhidraFileOpenDataFlavorHandlerService() );
PluggableServiceRegistry.registerPluggableService( DataTypeArchiveMergeManagerFactory.class, new GhidraDataTypeArchiveMergeManagerFactory() );
PluggableServiceRegistry.registerPluggableService( ProgramMultiUserMergeManagerFactory.class, new GhidraProgramMultiUserMergeManagerFactory() );
PluggableServiceRegistry.registerPluggableService(ToolStateFactory.class,
new GhidraToolStateFactory());
PluggableServiceRegistry.registerPluggableService(GhidraDataFlavorHandlerService.class,
new GhidraDataFlavorHandlerService());
PluggableServiceRegistry.registerPluggableService(
GhidraFileOpenDataFlavorHandlerService.class,
new GhidraFileOpenDataFlavorHandlerService());
PluggableServiceRegistry.registerPluggableService(DataTypeArchiveMergeManagerFactory.class,
new GhidraDataTypeArchiveMergeManagerFactory());
PluggableServiceRegistry.registerPluggableService(ProgramMultiUserMergeManagerFactory.class,
new GhidraProgramMultiUserMergeManagerFactory());
}
@Override

View File

@ -67,11 +67,17 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
protected static final int MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE = 4;
protected static final String MINSPECULATIVEREFADDRESS_OPTION_NAME =
"Min speculative reference";
"Speculative reference min";
protected static final String MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
"Minimum speculative reference address for offsets and parameters";
protected static final int MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 1024;
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_NAME =
"Speculative reference max";
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
"Maxmimum speculative reference address offset from the end of memory for offsets and parameters";
protected static final int MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 256;
protected final static int NOTIFICATION_INTERVAL = 100;
protected boolean checkParamRefsOption = OPTION_DEFAULT_VALUE;
@ -80,6 +86,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
protected int maxThreadCount = MAXTHREADCOUNT_OPTION_DEFAULT_VALUE;
protected long minStoreLoadRefAddress = MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE;
protected long minSpeculativeRefAddress = MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
protected long maxSpeculativeRefAddress = MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
protected boolean followConditional = false;
@ -391,7 +398,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
throws CancelledException {
ContextEvaluator eval = new ConstantPropagationContextEvaluator(trustWriteMemOption,
minStoreLoadRefAddress, minSpeculativeRefAddress);
minStoreLoadRefAddress, minSpeculativeRefAddress, maxSpeculativeRefAddress);
return symEval.flowConstants(flowStart, flowSet, eval, true, monitor);
}
@ -461,9 +468,13 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
MINKNOWNREFADDRESS_OPTION_DESCRIPTION);
long size = program.getAddressFactory().getDefaultAddressSpace().getSize();
minSpeculativeRefAddress = size * 8;
minSpeculativeRefAddress = size * 16;
options.registerOption(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress, null,
MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
maxSpeculativeRefAddress = size * 8;
options.registerOption(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress, null,
MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
}
@Override
@ -479,6 +490,8 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
options.getLong(MINKNOWNREFADDRESS_OPTION_NAME, minStoreLoadRefAddress);
minSpeculativeRefAddress =
options.getLong(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress);
maxSpeculativeRefAddress =
options.getLong(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress);
}
}

View File

@ -42,7 +42,9 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
protected AddressSet destSet = new AddressSet();
private boolean trustMemoryWrite = false;
private long minStoreLoadOffset = 4;
private long minSpeculativeOffset = 1024;
private long minSpeculativeOffset = 1024; // from the beginning of memory
private long maxSpeculativeOffset = 256; // from the end of memory
public ConstantPropagationContextEvaluator() {
}
@ -55,10 +57,10 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
}
public ConstantPropagationContextEvaluator(boolean trustWriteMemOption,
long minStoreLoadRefAddress, long minSpeculativeRefAddress) {
long minStoreLoadRefAddress, long minSpeculativeRefAddress, long maxSpeculativeRefAddress) {
this(trustWriteMemOption);
this.minStoreLoadOffset = minStoreLoadRefAddress;
this.minSpeculativeOffset = minSpeculativeRefAddress;
this.maxSpeculativeOffset = maxSpeculativeRefAddress;
}
/**
@ -85,7 +87,7 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
long wordOffset = constant.getOffset();
if (((wordOffset >= 0 && wordOffset < minSpeculativeOffset) ||
(Math.abs(maxAddrOffset - wordOffset) < minSpeculativeOffset)) &&
(Math.abs(maxAddrOffset - wordOffset) < maxSpeculativeOffset)) &&
!space.isExternalSpace()) {
return null;
}

View File

@ -93,7 +93,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
private volatile boolean isArchiving;
private volatile boolean isRestoring;
private TaskListener archivingListener;
private TaskListener restoringListner;
private TaskListener restoringListener;
//////////////////////////////////////////////////////////////////
@ -282,7 +282,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
isRestoring = true;
restoringListner = new TaskListener() {
restoringListener = new TaskListener() {
@Override
public void taskCompleted(Task task) {
isRestoring = false;
@ -295,7 +295,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
};
Task task = new RestoreTask(lastRestoreLocator, archiveJar, this);
task.addTaskListener(restoringListner);
task.addTaskListener(restoringListener);
new TaskLauncher(task, tool.getToolFrame());
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,12 @@
*/
package ghidra.app.plugin.core.datamgr;
import java.util.ArrayList;
import java.util.List;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.core.datamgr.archive.ProjectArchive;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
@ -24,13 +29,6 @@ import ghidra.framework.main.datatable.DomainFileProvider;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.listing.Program;
import java.util.ArrayList;
import java.util.List;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
public class DataTypesActionContext extends ProgramActionContext implements DomainFileProvider {
private final GTreeNode clickedNode;
private final boolean isToolbarAction;

View File

@ -242,6 +242,10 @@ public class MyProgramChangesDisplayPlugin extends ProgramPlugin implements Doma
public void dispose() {
worker.dispose();
if (currentProgram != null) {
currentProgram.removeTransactionListener(transactionListener);
currentProgram.removeListener(this);
}
tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +15,13 @@
*/
package ghidra.app.util;
import ghidra.framework.main.datatree.*;
import java.awt.datatransfer.DataFlavor;
public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHandlerService {
import ghidra.framework.main.datatree.*;
@Override
protected void doRegisterDataFlavorHandlers() {
public class GhidraFileOpenDataFlavorHandlerService {
public GhidraFileOpenDataFlavorHandlerService() {
try {
DataFlavor linuxFileUrlFlavor =
@ -34,15 +32,15 @@ public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHa
// should never happen as it is using java.lang.String
}
LocalTreeNodeFlavorHandler localHandler = new LocalTreeNodeFlavorHandler();
LocalTreeNodeHandler localHandler = new LocalTreeNodeHandler();
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor,
localHandler);
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor,
localHandler);
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListFlavorHandler());
FileOpenDropHandler.addDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localHandler);
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
new LocalVersionInfoHandler());
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler());
}
}

View File

@ -1,48 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import ghidra.app.services.FileImporterService;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import util.CollectionUtils;
final class JavaFileListFlavorHandler implements FileOpenDataFlavorHandler {
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
List<File> files = CollectionUtils.asList((List<?>) obj, File.class);
FileImporterService im = tool.getService(FileImporterService.class);
if (im == null) {
tool.setStatusInfo("ERROR: Could not get importer service.");
return;
}
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
if (files.size() == 1 && files.get(0).isFile()) {
im.importFile(rootFolder, files.get(0));
}
else {
im.importFiles(rootFolder, files);
}
}
}

View File

@ -96,6 +96,15 @@ public class Option {
this.listener = listener;
}
/**
* Override if you want to provide a custom widget for selecting your
* options.
* <p>
* Important! If you override this you MUST also override the {@link #copy()}
* method so it returns a new instance of your custom editor.
*
* @return the custom editor
*/
public Component getCustomEditorComponent() {
return null;
}

View File

@ -25,17 +25,17 @@ import ghidra.util.Msg;
public class Compare {
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception {
int index = 0;
BufferedReader reader = new BufferedReader(new FileReader(actualFile));
boolean hasFailure = false;
try {
try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) {
int excess = 0;
while (true) {
String actualLine = reader.readLine();
if (actualLine == null) {
break;
}
if (index >= expectedList.size()) {
++excess;
continue;
@ -73,8 +73,5 @@ public class Compare {
Assert.fail("One or more failures--see output for data");
}
}
finally {
reader.close();
}
}
}

View File

@ -15,10 +15,15 @@
*/
package ghidra.app.util.exporter;
import java.awt.BorderLayout;
import java.awt.Component;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import docking.widgets.textfield.HintTextField;
import ghidra.app.util.*;
import ghidra.app.util.opinion.IntelHexRecord;
import ghidra.app.util.opinion.IntelHexRecordWriter;
@ -29,18 +34,55 @@ import ghidra.program.model.mem.*;
import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor;
/**
* Exports the current program (or program selection) as bytes in Intel Hex format.
* <p>
* The output defaults to lines of 16-bytes but this is configurable using the
* {@link #recordSizeOption} attribute. This allows users to select any record size
* up to the max of 0xFF. Users may also choose to <code>Drop Extra Bytes</code>, which will
* cause only lines that match the max record size to be printed; any other
* bytes will be dropped. If this option is not set, every byte will be represented in the output.
*/
public class IntelHexExporter extends Exporter {
protected final static int MAX_BYTES_PER_LINE = 0x00000010;
protected Option option;
/** Option allowing the user to select the address space */
protected Option addressSpaceOption;
/** Option allowing the user to select the number of bytes in each line of output */
protected RecordSizeOption recordSizeOption;
private static final int DEFAULT_RECORD_SIZE = 0x10;
/**
* Constructs a new Intel Hex exporter.
* Constructs a new Intel Hex exporter. This will use a record size of 16 (the default)
* and will export ALL bytes in the program or selection (even if the total length
* is not a multiple of 16.
*/
public IntelHexExporter() {
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
}
/**
* Constructs a new Intel Hex exporter with a custom record size.
*
* @param recordSize the record size to use when writing to the output file
* @param dropBytes if true, bytes at the end of the file that don't match the specified
* record size will be dropped
*/
public IntelHexExporter(int recordSize, boolean dropBytes) {
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
recordSizeOption.setRecordSize(recordSize);
recordSizeOption.setDropBytes(dropBytes);
}
/**
* Constructor
*
* @param name the name of the exporter
* @param extension the extension to use for the output file
* @param help location of Ghidra help
*/
protected IntelHexExporter(String name, String extension, HelpLocation help) {
super(name, extension, help);
}
@ -55,16 +97,49 @@ public class IntelHexExporter extends Exporter {
}
Program program = (Program) domainObject;
option = new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
addressSpaceOption =
new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
if (recordSizeOption == null) {
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
}
optionsList.add(addressSpaceOption);
optionsList.add(recordSizeOption);
optionsList.add(option);
return optionsList;
}
@Override
public void setOptions(List<Option> options) throws OptionException {
if (!options.isEmpty()) {
option = options.get(0);
addressSpaceOption = options.get(0);
recordSizeOption = (RecordSizeOption) options.get(1);
}
}
/**
* Verifier for a {@link HintTextField} that ensures input is a numeric value between
* 0 and 0xFF.
* <p>
* Input may be specified in either decimal or hex.
*/
private class BoundedIntegerVerifier extends InputVerifier {
@Override
public boolean verify(JComponent input) {
HintTextField field = (HintTextField) input;
String text = field.getText();
int val;
try {
val = Integer.decode(text);
}
catch (NumberFormatException e) {
return false;
}
return val <= 0xFF && val >= 0;
}
}
@ -84,11 +159,11 @@ public class IntelHexExporter extends Exporter {
return false;
}
if (option == null) {
if (addressSpaceOption == null || recordSizeOption == null) {
getOptions(() -> program);
}
PrintWriter writer = new PrintWriter(new FileOutputStream(file));
try (PrintWriter writer = new PrintWriter(new FileOutputStream(file))) {
Memory memory = program.getMemory();
@ -106,11 +181,9 @@ public class IntelHexExporter extends Exporter {
throw new ExporterException(e);
}
finally {
// Close the PrintWriter
//
writer.close();
option = null;
addressSpaceOption = null;
recordSizeOption = null;
}
}
return true;
@ -118,15 +191,19 @@ public class IntelHexExporter extends Exporter {
protected List<IntelHexRecord> dumpMemory(Program program, Memory memory,
AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException {
IntelHexRecordWriter writer = new IntelHexRecordWriter(MAX_BYTES_PER_LINE);
int size = (int) recordSizeOption.getValue();
boolean dropBytes = recordSizeOption.dropExtraBytes();
IntelHexRecordWriter writer = new IntelHexRecordWriter(size, dropBytes);
AddressSet set = new AddressSet(addrSetView);
MemoryBlock[] blocks = memory.getBlocks();
for (int i = 0; i < blocks.length; ++i) {
if (!blocks[i].isInitialized() ||
blocks[i].getStart().getAddressSpace() != option.getValue()) {
set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd()));
for (MemoryBlock block : blocks) {
if (!block.isInitialized() ||
block.getStart().getAddressSpace() != addressSpaceOption.getValue()) {
set.delete(new AddressRangeImpl(block.getStart(), block.getEnd()));
}
}
@ -148,4 +225,113 @@ public class IntelHexExporter extends Exporter {
}
return writer.finish(entryPoint);
}
/**
* Option for exporting Intel Hex records that allows users to specify a record size for the
* output. Users may also optionally select the <code>Drop Extra Bytes</code> option that
* will cause only those records that match the maximum size to be output to the file.
*
* @see RecordSizeComponent
*/
private class RecordSizeOption extends Option {
private final RecordSizeComponent comp = new RecordSizeComponent(DEFAULT_RECORD_SIZE);
public RecordSizeOption(String name, Class<?> valueClass) {
super(name, valueClass);
}
public RecordSizeOption(String name, Class<?> valueClass, Object value, String arg,
String group) {
super(name, valueClass, value, arg, group);
}
@Override
public Component getCustomEditorComponent() {
return comp;
}
@Override
public Option copy() {
return new RecordSizeOption(getName(), getValueClass(), getValue(), getArg(),
getGroup());
}
@Override
public Object getValue() {
return comp.getValue();
}
@Override
public Class<?> getValueClass() {
return Integer.class;
}
public boolean dropExtraBytes() {
return comp.dropExtraBytes();
}
public void setRecordSize(int recordSize) {
comp.setRecordSize(recordSize);
}
public void setDropBytes(boolean dropBytes) {
comp.setDropBytes(dropBytes);
}
}
/**
* Component that displays two widgets for setting export options:
*
* <ul>
* <li><code>input</code>: a {@link HintTextField} for entering numeric digits; these
* represent the record size for each line of output</li>
* <li>dropCb: a {@link JCheckBox} for specifying a setting that enforces that every line in
* the output matches the specified record size</li>
* </ul>
*
* Note: If the <code>Drop Extra Bytes</code> option is set, any bytes that are left over
* after outputting all lines that match the record size will be omitted from the output.
*/
private class RecordSizeComponent extends JPanel {
private HintTextField input;
private JCheckBox dropCb;
public RecordSizeComponent(int recordSize) {
setLayout(new BorderLayout());
input = new HintTextField(Integer.toString(recordSize), false, new BoundedIntegerVerifier());
dropCb = new JCheckBox("Align To Record Size");
input.setText(Integer.toString(recordSize));
add(input, BorderLayout.CENTER);
add(dropCb, BorderLayout.EAST);
}
public int getValue() {
String val = input.getText();
if (!input.isFieldValid()) {
// If the user clears the input field, revert to the default
// record size (16).
return DEFAULT_RECORD_SIZE;
}
return Integer.valueOf(val);
}
public boolean dropExtraBytes() {
return dropCb.isSelected();
}
public void setRecordSize(int recordSize) {
input.setText(Integer.toString(recordSize));
}
public void setDropBytes(boolean dropBytes) {
dropCb.setSelected(dropBytes);
}
}
}

View File

@ -20,21 +20,31 @@ import java.util.*;
import ghidra.program.model.address.*;
public class IntelHexRecordWriter {
private final int maxBytesPerLine;
private final boolean dropExtraBytes;
private Address startAddress = null;
private Long oldSegment = null;
private ArrayList<Byte> bytes = new ArrayList<Byte>();
private ArrayList<Byte> bytes = new ArrayList<>();
private Boolean isSegmented = null;
private ArrayList<IntelHexRecord> results = new ArrayList<IntelHexRecord>();
private ArrayList<IntelHexRecord> results = new ArrayList<>();
private boolean done = false;
public IntelHexRecordWriter(int maxBytesPerLine) {
/**
* Constructor
*
* @param maxBytesPerLine the maximum number of bytes to write per line in the hex output
* @param dropExtraBytes if true, only lines matching {@link #maxBytesPerLine} will be output;
* remaining bytes will be left out
*/
public IntelHexRecordWriter(int maxBytesPerLine, boolean dropExtraBytes) {
if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) {
throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH");
}
this.maxBytesPerLine = maxBytesPerLine;
this.dropExtraBytes = dropExtraBytes;
}
public void addByte(Address address, byte b) {
@ -117,6 +127,14 @@ public class IntelHexRecordWriter {
}
public List<IntelHexRecord> finish(Address entryPoint) {
// Before finalizing things, write out any remaining bytes that haven't yet been written, if
// the user has specified to do so via the drop extra bytes option (false =
// write out everything).
if (bytes.size() > 0 && !dropExtraBytes) {
emitData();
}
if (entryPoint != null && isSegmented != null) {
final long offset = entryPoint.getOffset();
byte[] data = new byte[4];

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +15,7 @@
*/
package ghidra.app.util.viewer.field;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.Highlight;
import docking.widgets.fieldpanel.support.HighlightFactory;
import ghidra.app.util.HighlightProvider;
@ -37,19 +37,15 @@ public class FieldHighlightFactory implements HighlightFactory {
* @param fieldFactoryClass the class of the field factory that generated the field to be rendered.
* @param obj the object that holds the information that will be rendered (usually a code unit)
*/
public FieldHighlightFactory(HighlightProvider provider, Class<? extends FieldFactory> fieldFactoryClass, Object obj) {
public FieldHighlightFactory(HighlightProvider provider,
Class<? extends FieldFactory> fieldFactoryClass, Object obj) {
this.provider = provider;
this.fieldFactoryClass = fieldFactoryClass;
this.obj = obj;
}
/**
* Returns the highlights for the given text.
* @param text the text to be considered for highlighting.
* @return an array of highlights to be rendered.
*/
public Highlight[] getHighlights(String text, int cursorTextOffset) {
@Override
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
return provider.getHighlights(text, obj, fieldFactoryClass, cursorTextOffset);
}
}

View File

@ -51,7 +51,8 @@ public class OptionsGui extends JPanel {
private static final Color DARK_ORANGE = new Color(255, 128, 0);
private static final Color DARK_RED = new Color(130, 0, 75);
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
private static final HighlightFactory hlFactory = (text, cursorTextOffset) -> NO_HIGHLIGHTS;
private static final HighlightFactory hlFactory =
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
public static final ScreenElement COMMENT_AUTO =
new ScreenElement("Comment, Automatic", Color.LIGHT_GRAY);

View File

@ -26,6 +26,7 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import docking.*;
import docking.event.mouse.GMouseListenerAdapter;
import docking.widgets.tree.support.GTreeSelectionEvent;
import docking.widgets.tree.support.GTreeSelectionListener;
import ghidra.framework.main.datatree.ClearCutAction;
@ -540,10 +541,11 @@ implements GTreeSelectionListener, ActionListener {
protected void addTreeListeners() {
if (type == OPEN) {
treePanel.addTreeMouseListener(new MouseAdapter() {
treePanel.addTreeMouseListener(new GMouseListenerAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2 && okButton.isEnabled()) {
public void doubleClickTriggered(MouseEvent e) {
if (okButton.isEnabled()) {
okCallback();
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,9 +17,10 @@ package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
@Override
protected void doRegisterDataFlavorHandlers() {
public class GhidraDataFlavorHandlerService {
public GhidraDataFlavorHandlerService() {
try {
DataFlavor linuxFileUrlFlavor =
new DataFlavor("application/x-java-serialized-object;class=java.lang.String");
@ -31,15 +31,12 @@ public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
// should never happen as it is using java.lang.String
}
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
LocalTreeNodeHandler localNodeHandler = new LocalTreeNodeHandler();
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localNodeHandler);
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler());
DataTreeDragNDropHandler.addActiveDataFlavorHandler(
VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
}
}

View File

@ -19,13 +19,15 @@
package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService;
import ghidra.framework.main.FrontEndTool;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg;
import util.CollectionUtils;
@ -33,24 +35,43 @@ import util.CollectionUtils;
* A drag-and-drop handler for trees that is specific to List&ltFile&gt. (see
* {@link DataFlavor#javaFileListFlavor}).
*/
final class JavaFileListHandler implements DataFlavorHandler {
@Override
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);
public final class JavaFileListHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
FileImporterService im = tool.getService(FileImporterService.class);
if (im == null) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find importer service");
@Override
public void handle(PluginTool tool, Object transferData, DropTargetDropEvent e, DataFlavor f) {
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, null, "Could Not Import", "Could not find Importer Service");
return;
}
List<File> fileList = CollectionUtils.asList((List<?>) transferData, File.class);
DomainFolder folder = tool.getProject().getProjectData().getRootFolder();
doImport(importer, folder, transferData);
}
@Override
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find Importer Service");
return;
}
DomainFolder folder = getDomainFolder(destinationNode);
doImport(importer, folder, transferData);
}
private void doImport(FileImporterService importer, DomainFolder folder, Object files) {
List<File> fileList = CollectionUtils.asList((List<?>) files, File.class);
if (fileList.size() == 1 && fileList.get(0).isFile()) {
im.importFile(folder, fileList.get(0));
importer.importFile(folder, fileList.get(0));
}
else {
im.importFiles(folder, fileList);
importer.importFiles(folder, fileList);
}
}

View File

@ -27,7 +27,6 @@ import java.util.function.Function;
import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider;
@ -38,11 +37,11 @@ import ghidra.util.Msg;
* duty in that it opens files for DataTrees and for Tools (signaled via the interfaces it
* implements).
*/
public final class LinuxFileUrlHandler implements DataFlavorHandler, FileOpenDataFlavorHandler {
public final class LinuxFileUrlHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override
// This is for the DataFlavorHandler interface for handling node drops in DataTrees
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);

View File

@ -193,11 +193,11 @@ public class DiffUtility extends SimpleDiffUtility {
return otherProgram.getSymbolTable().createExternalLibrary(namespace.getName(), source);
}
else if (namespace instanceof GhidraClass) {
return otherProgram.getSymbolTable().createClass(otherParentNamespace,
namespace.getName(), source);
return otherProgram.getSymbolTable()
.createClass(otherParentNamespace, namespace.getName(), source);
}
return otherProgram.getSymbolTable().createNameSpace(otherParentNamespace,
namespace.getName(), source);
return otherProgram.getSymbolTable()
.createNameSpace(otherParentNamespace, namespace.getName(), source);
}
// /**
@ -329,11 +329,11 @@ public class DiffUtility extends SimpleDiffUtility {
if (toAddr == null) {
return null;
}
return otherProgram.getReferenceManager().getReference(fromAddr, toAddr,
ref.getOperandIndex());
return otherProgram.getReferenceManager()
.getReference(fromAddr, toAddr, ref.getOperandIndex());
}
Reference otherRef = otherProgram.getReferenceManager().getPrimaryReferenceFrom(fromAddr,
ref.getOperandIndex());
Reference otherRef = otherProgram.getReferenceManager()
.getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
return otherRef;
}
@ -357,11 +357,11 @@ public class DiffUtility extends SimpleDiffUtility {
if (toAddr1 == null) {
return null;
}
return program.getReferenceManager().getReference(fromAddr1, toAddr1,
p2Ref.getOperandIndex());
return program.getReferenceManager()
.getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
}
Reference p1Ref = program.getReferenceManager().getPrimaryReferenceFrom(fromAddr1,
p2Ref.getOperandIndex());
Reference p1Ref = program.getReferenceManager()
.getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
return p1Ref;
}
@ -385,8 +385,9 @@ public class DiffUtility extends SimpleDiffUtility {
otherAddr = getCompatibleAddress(program, addr, otherProgram);
}
// FIXME Should this be passing the Namespace?
return otherProgram.getExternalManager().addExtLocation(extLoc.getLibraryName(),
extLoc.getLabel(), otherAddr, extLoc.getSource());
return otherProgram.getExternalManager()
.addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
extLoc.getSource());
}
/**
@ -707,6 +708,9 @@ public class DiffUtility extends SimpleDiffUtility {
Address refAddress = getCompatibleAddress(program, location.refAddr, otherProgram);
if (address != null) {
if (byteAddress == null) {
byteAddress = address; // Make sure the byte address isn't null.
}
ProgramLocation otherLocation = new ProgramLocation(otherProgram, address, byteAddress,
location.getComponentPath(), refAddress, 0, 0, 0);
return otherLocation;

View File

@ -2555,7 +2555,7 @@ public class ProgramDiffDetails {
}
private void addColorAddress(StyledDocument doc, Address addr) {
String text = addr.toString();
String text = (addr != null) ? addr.toString() : "no matching address";
color(ADDRESS_COLOR);
try {
doc.insertString(doc.getLength(), text, textAttrSet);

View File

@ -43,7 +43,7 @@ public class SymbolicPropogator {
// 1. How are "register-relative" varnodes distinguished based upon target space ? Not sure how we handle wrapping/truncation concerns.
// 1) The offset is the only thing that could be used as a reference.
private static final int _POINTER_MIN_BOUNDS = 0x7fff;
private static final int _POINTER_MIN_BOUNDS = 0x100;
// mask for sub-piece extraction
private static long[] maskSize = { 0xffL, 0xffL, 0xffffL, 0xffffffL, 0xffffffffL, 0xffffffffffL,
@ -1836,7 +1836,7 @@ public class SymbolicPropogator {
// see if the offset is a large constant offset from the symbolic space
long offset = refLocation.getOffset();
if (checkPossibleOffsetAddr(offset)) {
if (evaluator != null) {
// symbolic spaces will have the name of the symbolic space be the register space
// String spaceName = refLocation.getAddress().getAddressSpace().getName();
// Register register = vContext.getRegister(spaceName);
@ -1850,7 +1850,7 @@ public class SymbolicPropogator {
// }
// } else
if (evaluator == null) {
if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) {
Address constant = program.getAddressFactory().getAddress(
(int) targetSpaceID.getOffset(), offset);
Address newTarget = evaluator.evaluateConstant(vContext, instruction,
@ -2051,7 +2051,7 @@ public class SymbolicPropogator {
*/
private int getReferenceSpaceID(Instruction instruction, long offset) {
// TODO: this should be passed to the client callback to make the decision
if (offset <= 4096 && offset >= -1) {
if (offset <= 4 && offset >= -1) {
return -1; // don't make speculative reference to certain offset values
}

View File

@ -312,7 +312,7 @@ public class VarnodeContext implements ProcessorContext {
/**
* Return true if this varnode is stored in the symbolic stack space
*/
private boolean isStackSymbolicSpace(Varnode varnode) {
public boolean isStackSymbolicSpace(Varnode varnode) {
// symbolic spaces are off of a register, find the space
AddressSpace regSpace = addrFactory.getAddressSpace(varnode.getSpace());
@ -785,7 +785,9 @@ public class VarnodeContext implements ProcessorContext {
* return the location that this register was last set
* This is a transient thing, so it should only be used as a particular flow is being processed...
*
* @param reg
* @param reg register to find last set location
* @param bval value to look for to differentiate set locations, null if don't care
*
* @return address that the register was set.
*/
public Address getLastSetLocation(Register reg, BigInteger bval) {
@ -1256,6 +1258,13 @@ public class VarnodeContext implements ProcessorContext {
// too big anyway,already extended as far as it will go.
vnodeVal = createConstantVarnode(vnodeVal.getOffset(), out.getSize());
}
} else if (vnodeVal.isRegister() && vnodeVal.getSize() < out.getSize()) {
Register reg = getRegister(vnodeVal);
if (reg == null) {
throw notFoundExc;
}
int spaceID = getAddressSpace(reg.getName());
vnodeVal = createVarnode(0,spaceID,out.getSize());
}
return vnodeVal;
}

View File

@ -623,7 +623,7 @@ public class OptionsTest extends AbstractGenericTest {
@Override
public int hashCode() {
return 1;// set so that this listener gets called after the storingOptionsListnere
return 1;// set so that this listener gets called after the storingOptionsListener
}
}

View File

@ -15,8 +15,6 @@
*/
package ghidra.app.plugin.core.byteviewer;
import ghidra.util.ColorUtils;
import java.awt.*;
import java.math.BigInteger;
@ -28,6 +26,7 @@ import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.HighlightFactory;
import docking.widgets.fieldpanel.support.RowColLocation;
import ghidra.util.ColorUtils;
/**
* Fields for the ByteViewer. This class extends the SimpleTextField to include
@ -52,7 +51,8 @@ public class ByteField extends SimpleTextField {
* @param hlFactory the factory used to create highlights
*/
public ByteField(String text, FontMetrics fontMetrics, int startX, int width,
boolean allowCursorAtEnd, int fieldOffset, BigInteger index, HighlightFactory hlFactory) {
boolean allowCursorAtEnd, int fieldOffset, BigInteger index,
HighlightFactory hlFactory) {
super(text, fontMetrics, startX, width, allowCursorAtEnd, hlFactory);
this.fieldOffset = fieldOffset;
@ -64,7 +64,7 @@ public class ByteField extends SimpleTextField {
public void paint(JComponent c, Graphics g, PaintContext context,
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
paintSelection(g, colorManager, 0);
paintHighlights(g, hlFactory.getHighlights(text, -1));
paintHighlights(g, hlFactory.getHighlights(this, text, -1));
g.setFont(metrics.getFont());
if (foregroundColor == null) {
foregroundColor = context.getForeground();

View File

@ -213,7 +213,7 @@ class FieldFactory {
}
@Override
public Highlight[] getHighlights(String text, int cursorTextOffset) {
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
return provider.getHighlights(text, null, null, -1);
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +15,6 @@
*/
package ghidra.app.plugin.core.byteviewer;
import ghidra.app.plugin.core.format.ByteBlockInfo;
import java.awt.Color;
import java.awt.FontMetrics;
import java.math.BigInteger;
@ -26,6 +23,7 @@ import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.field.SimpleTextField;
import docking.widgets.fieldpanel.support.Highlight;
import docking.widgets.fieldpanel.support.HighlightFactory;
import ghidra.app.plugin.core.format.ByteBlockInfo;
/**
* Implementation for the index/address field.
@ -145,7 +143,7 @@ class IndexFieldFactory {
}
@Override
public Highlight[] getHighlights(String text, int cursorTextOffset) {
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
return NO_HIGHLIGHTS;
}
}

View File

@ -7333,8 +7333,12 @@ bool RuleConditionalMove::BoolExpress::evaluatePropagation(FlowBlock *root,FlowB
if (root == branch) return true; // Can always propagate if there is no branch
if (op->getParent() != branch) return true; // Can propagate if value formed before branch
mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed
if (in0->isFree() && !in0->isConstant()) return false;
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false;
if ((optype==2) && in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
if (optype == 2) {
if (in1->isFree() && !in1->isConstant()) return false;
if (in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
}
return true;
}

View File

@ -1,5 +1,6 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -197,4 +198,8 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
return lineNumberFieldElement.getStringWidth();
}
public int getLineNumber() {
String text = lineNumberFieldElement.getText().trim();
return Integer.parseInt(text);
}
}

View File

@ -76,8 +76,9 @@ public class DecompilerHoverProvider extends AbstractHoverProvider {
Varnode vn = token.getVarnode();
if (vn != null) {
if (vn.getHigh() instanceof HighGlobal) {
reference = vn.getAddress();
HighVariable highVar = vn.getHigh();
if (highVar instanceof HighGlobal) {
reference = highVar.getRepresentative().getAddress();
}
}

View File

@ -51,7 +51,7 @@ public class DecompilerManager {
this.decompilerController = decompilerController;
runManager = new RunManager("Decompiler", null);
decompiler = new Decompiler(options, options.getDefaultTimeout());
decompiler = new Decompiler(options, 0);
updateManager = new SwingUpdateManager(500, () -> doPendingDecompile());
}

View File

@ -37,11 +37,11 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.pcode.*;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.*;
@ -175,6 +175,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
if (clipboard != null) {
clipboard.selectionChanged(null);
}
// don't highlight search results across functions
currentSearchLocation = null;
}
private void setLocation(DecompileData oldData, DecompileData newData) {
@ -419,6 +422,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
}
HighVariable highVar = vn.getHigh();
if (highVar instanceof HighGlobal) {
vn = highVar.getRepresentative();
}
if (vn.isAddress()) {
Address addr = vn.getAddress();
if (addr.isMemoryAddress()) {
@ -696,9 +703,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
}
class SearchHighlightFactory implements HighlightFactory {
@Override
public Highlight[] getHighlights(String text, int cursorTextOffset) {
if (currentSearchLocation == null || cursorTextOffset == -1) {
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
if (currentSearchLocation == null) {
return new Highlight[0];
}
ClangTextField cField = (ClangTextField) field;
int highlightLine = cField.getLineNumber();
FieldLocation searchCursorLocation =
((FieldBasedSearchLocation) currentSearchLocation).getFieldLocation();
int searchLineNumber = searchCursorLocation.getIndex().intValue() + 1;
if (highlightLine != searchLineNumber) {
// only highlight the match on the actual line
return new Highlight[0];
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,4 +38,9 @@ public class FieldBasedSearchLocation extends SearchLocation {
public CursorPosition getCursorPosition() {
return new DecompilerCursorPosition(fieldLocation);
}
@Override
protected String fieldsToString() {
return super.fieldsToString() + ", fieldLocation=" + fieldLocation;
}
}

View File

@ -1,5 +1,6 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -77,13 +78,20 @@ public class FindAction extends DockingAction {
if (text != null) {
dialog.setSearchText(text);
}
// show over the root frame, so the user can still see the Decompiler window
tool.showDialog(dialog);
}
protected FindDialog getFindDialog() {
if (findDialog == null) {
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher());
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()) {
@Override
protected void dialogClosed() {
// clear the search results when the dialog is closed
decompilerPanel.setSearchResults(null);
}
};
findDialog.setHelpLocation(new HelpLocation("DecompilePlugin", "Find"));
}
return findDialog;

View File

@ -1,73 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
+ Compile sleigh languages within this module.
+ Eclipse: right-click on this file and choose menu item "Run As->Ant Build"
+ From command line (requires ant install)
+ - cd to data directory containing this file
+ - run ant
-->
<project name="privateBuildDeveloper" default="sleigh-compile">
<property name="sleigh.compile.class" value="ghidra.pcodeCPort.slgh_compile.SleighCompile"/>
<property name="repo.dir" value="../../../.." />
<property name="repo.marker.dir" value="${repo.dir}/.git" />
<condition property="devmode">
<available file="${repo.marker.dir}" type="dir" />
</condition>
<target name="build-sleigh-classpath-dev" if="devmode">
<property name="framework.path" value="${repo.dir}/../ghidra.git/Ghidra/Framework"/>
<property name="libs.path" value="${repo.dir}/../ghidra.bin.git/ExternalLibraries/libsForRuntime"/>
<path id="sleigh.class.path">
<pathelement location="${framework.path}/SoftwareModeling/bin"/>
<pathelement location="${framework.path}/Generic/bin"/>
<pathelement location="${framework.path}/Utility/bin"/>
<fileset dir="${libs.path}">
<include name="*.jar"/>
</fileset>
</path>
<available classname="${sleigh.compile.class}" classpathref="sleigh.class.path" property="sleigh.compile.exists"/>
</target>
<target name="build-sleigh-classpath-dist" unless="devmode">
<property name="framework.path" value="../../../Framework"/>
<path id="sleigh.class.path">
<fileset dir="${framework.path}/SoftwareModeling/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${framework.path}/Generic/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${framework.path}/Utility/lib">
<include name="*.jar"/>
</fileset>
</path>
<available classname="${sleigh.compile.class}" classpathref="sleigh.class.path" property="sleigh.compile.exists"/>
</target>
<target name="sleigh-compile" depends="build-sleigh-classpath-dist, build-sleigh-classpath-dev">
<fail unless="sleigh.compile.exists" />
<java classname="${sleigh.compile.class}"
classpathref="sleigh.class.path"
fork="true"
failonerror="true">
<jvmarg value="-Xmx2048M"/>
<arg value="-a"/>
<arg value="./languages"/>
</java>
</target>
</project>

View File

@ -75,9 +75,9 @@ of Visual Studio and DIA SDK.</li>
<p>First, check to see if you already have the <i>msdia140.dll</i> library installed on your system.
It is generally installed installed with Microsoft Visual Studio 2017 when C/C++ development support
is included.
is included (<DISTRO> may be Community, Professional, or other VS 2017 distribution package name).
<pre>
C:\\Program Files (x86)\Microsoft Visual Studio\2017\DIA SDK\bin\amd64\msdia140.dll
C:\Program Files (x86)\Microsoft Visual Studio\2017\<DISTRO>\DIA SDK\bin\amd64\msdia140.dll
</pre>
<p>This file is commonly located here, although it may be installed in other locations as well. Any 64-bit
copy may be registered provided it is the correct version. There is no need to register more than
@ -86,8 +86,8 @@ one.
<h3>Register 'msdia140.dll' in the Windows registry</h3>
<p>Please register 64-bit <i>msdia140.dll</i> even if you already had a copy of it on your computer
since it is not registered by the Visual Studio installation process. You will need administrative rights/privileges in order to register the DLL in the Windows
registry.
since it is not registered by the Visual Studio installation process. You will need administrative
rights/privileges in order to register the DLL in the Windows registry.
<blockquote>
<ol>
<li>Start a command prompt as an administrator:</li>

View File

@ -576,6 +576,9 @@ public class PdbParserNEW {
}
catch (Exception e) {
if (!isXML) {
if (hasErrors()) {
throw new PdbException(getErrorAndWarningMessages());
}
throw new PdbException("PDB Execution failure of " + PDB_EXE + ".\n" +
"This was likely caused by severe execution failure which can occur if executed\n" +
"on an unsupported platform. It may be neccessary to rebuild the PDB executable\n" +

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,9 +25,7 @@ void warning( const char * msg )
void fatal( const char * msg )
{
fprintf( stderr, "ERROR:\n" );
fprintf( stderr, msg );
fprintf( stderr, "\n" );
fprintf( stderr, "ERROR: %s\n", msg );
exit( -1 );
}

View File

@ -46,9 +46,8 @@ int init(const char * szFilename, const char * szSignature, const char * szAge)
if (hr < 0) {
switch (hr) {
case REGDB_E_CLASSNOTREG:
fatal("Unable to locate the DIA SDK. It is required to load PDB files.\n\n" \
"* Open {Ghidra install root}/docs/README_PDB.html\n"
" and follow the instructions therein.\n");
fatal("Unable to locate the DIA SDK. It is required to load PDB files.\n" \
"* See docs/README_PDB.html for DLL registration instructions.\n");
break;
default:
char msg[256];

View File

@ -72,7 +72,7 @@
<PostBuildEvent>
<Command>
rem copy /y /v /b "$(VSInstallDir)DIA SDK\bin\amd64\msdia*.dll" "$(OutDir)"
set OS_DIR="$(SolutionDir)..\..\os"
set OS_DIR=$(SolutionDir)..\..\os
if exist "%OS_DIR%\win64" (
mkdir "%OS_DIR%"
mkdir "%OS_DIR%\win64"
@ -104,7 +104,7 @@ if exist "%OS_DIR%\win64" (
<PostBuildEvent>
<Command>
rem copy /y /v /b "$(VSInstallDir)DIA SDK\bin\amd64\msdia*.dll" "$(OutDir)"
set OS_DIR="$(SolutionDir)..\..\os"
set OS_DIR=$(SolutionDir)..\..\os
if exist "%OS_DIR%\win64" (
mkdir "%OS_DIR%"
mkdir "%OS_DIR%\win64"

View File

@ -441,6 +441,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
Address primaryByteAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
byteAddr, primaryProgram);
if (primaryByteAddr == null) {
primaryByteAddr = primaryAddr; // Make sure the byte address isn't null.
}
Address primaryRefAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
refAddr, primaryProgram);
ProgramLocation newP1Location = new ProgramLocation(primaryProgram, primaryAddr,
@ -463,7 +466,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
}
ProgramLocation previousP1LocationAsP2 = DiffUtility
.getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram);
if (previousP1LocationAsP2 != null) {
diffListingPanel.setCursorPosition(previousP1LocationAsP2);
}
if (diffDetailsProvider != null && diffDetails != null) {
diffDetailsProvider.locationChanged(previousP1Location);
}
@ -749,7 +754,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
runSwing(() -> {
MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll();
selectionMarkers.add(p2SelectionAsP1);
selectionMarkers.add(p2Selection);
});
diffListingPanel.setSelection(p2SelectionAsP1);
@ -798,9 +803,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Limit the apply to the selection in the view.
AddressSet p2SelectionAsP1 =
DiffUtility.getCompatibleAddressSet(p2Selection, primaryProgram);
AddressSet p1ApplySet =
p2SelectionAsP1.intersect(p1ViewAddrSet).subtract(addressesOnlyInP1).subtract(
compatibleOnlyInP2);
AddressSet p1ApplySet = p2SelectionAsP1.intersect(p1ViewAddrSet)
.subtract(addressesOnlyInP1)
.subtract(compatibleOnlyInP2);
if (p1ApplySet.isEmpty()) {
Msg.showInfo(getClass(), tool.getToolFrame(), "Apply Differences",
(p2Selection.isEmpty()) ? "No diff selection in the current view."
@ -860,7 +865,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Right side markers need p1 addresses since they use p1 indexMap.
MarkerSet diffMarkers = getDiffMarkers(); // Get right side markers for program 2.
diffMarkers.clearAll();
diffMarkers.add(p2DiffSetAsP1);
diffMarkers.add(p2DiffSet);
MarkerSet codeViewerDiffMarkers = getCodeViewerMarkers(); // Get left side markers for program 1.
codeViewerDiffMarkers.clearAll();
@ -884,7 +889,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet p1DiffHighlightSet =
DiffUtility.getCompatibleAddressSet(p2Highlight, primaryProgram);
p2DiffHighlight = p2Highlight;
diffMarkers.add(p1DiffHighlightSet);
diffMarkers.add(p2Highlight);
codeViewerDiffMarkers.add(p1DiffHighlightSet);
}
@ -1603,7 +1608,14 @@ public class ProgramDiffPlugin extends ProgramPlugin
diffListingPanel.goTo(currentLocation);
MarkerSet cursorMarkers = getCursorMarkers();
cursorMarkers.setAddressSet(new AddressSet(currentLocation.getAddress()));
Address currentP2Address = currentLocation.getAddress();
if (currentLocation.getProgram() != secondaryDiffProgram) { // Make sure address is from P2.
currentP2Address = SimpleDiffUtility.getCompatibleAddress(currentLocation.getProgram(),
currentLocation.getAddress(), secondaryDiffProgram);
}
if (currentP2Address != null) {
cursorMarkers.setAddressSet(new AddressSet(currentP2Address));
}
updatePgm2Enablement();

View File

@ -47,7 +47,12 @@ public class ActionContext {
}
/**
* For Testing
* Constructor
*
* @param provider the ComponentProvider that generated this context.
* @param contextObject an optional contextObject that the ComponentProvider can provide
* @param sourceObject an optional source object; this can be anything that actions wish to
* later retrieve
*/
public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) {
this(provider, contextObject);
@ -55,8 +60,8 @@ public class ActionContext {
}
/**
* Returns the {@link #ComponentProvider} that generated this ActionContext
* @return
* Returns the {@link ComponentProvider} that generated this ActionContext
* @return the provider
*/
public ComponentProvider getComponentProvider() {
return provider;

View File

@ -170,8 +170,11 @@ public abstract class ComponentProvider implements HelpDescriptor, ActionContext
*/
public boolean isFocusedProvider() {
DockingWindowManager dwm = DockingWindowManager.getInstance(getComponent());
if (dwm == null) {
return false; // can happen in testing
}
ComponentPlaceholder placeholder = dwm.getFocusedComponent();
return placeholder.getProvider() == this;
return placeholder != null && placeholder.getProvider() == this;
}
/**

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -60,6 +59,10 @@ public class SearchLocation {
@Override
public String toString() {
return searchText + "[start=" + startIndexInclusive + ", end=" + endIndexInclusive + "]";
return searchText + "[" + fieldsToString() + "]";
}
protected String fieldsToString() {
return startIndexInclusive + ", end=" + endIndexInclusive;
}
}

View File

@ -63,7 +63,7 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
table.addSelectionListener(t -> objectSelected(t));
table.setItemPickListner(t -> objectPicked(t));
table.setItemPickListener(t -> objectPicked(t));
return table;
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -129,8 +128,8 @@ public class ClippingTextField implements TextField {
int x = findX(col) + startX;
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() +
textElement.getHeightBelow());
return new Rectangle(x, -textElement.getHeightAbove(), 2,
textElement.getHeightAbove() + textElement.getHeightBelow());
}
/**
@ -315,7 +314,7 @@ public class ClippingTextField implements TextField {
if (cursorLoc != null) {
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
}
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset));
paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
}
protected void paintSelection(Graphics g, FieldBackgroundColorManager colorManager, int row,
@ -344,10 +343,10 @@ public class ClippingTextField implements TextField {
}
protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) {
int startCol = Math.max(highlights[i].getStart(), 0);
int endCol = Math.min(highlights[i].getEnd(), getString().length());
Color c = highlights[i].getColor();
for (Highlight highlight : highlights) {
int startCol = Math.max(highlight.getStart(), 0);
int endCol = Math.min(highlight.getEnd(), getString().length());
Color c = highlight.getColor();
if (endCol >= startCol) {
int start = findX(startCol);
int end = findX(endCol + 1);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -142,8 +141,8 @@ public class ReverseClippingTextField implements TextField {
int x = findX(col) + textStartX;
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() +
textElement.getHeightBelow());
return new Rectangle(x, -textElement.getHeightAbove(), 2,
textElement.getHeightAbove() + textElement.getHeightBelow());
}
/**
@ -336,8 +335,7 @@ public class ReverseClippingTextField implements TextField {
private void paintDots(Graphics g, int x) {
int pos = 1; // skip one pixel
for (int i = 0; i < 3; i++) {
if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next
// field.
if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next field
g.drawRect(x + pos, -2, 1, 1);
pos += 4; // add in size of dot and padding
}
@ -349,14 +347,14 @@ public class ReverseClippingTextField implements TextField {
if (cursorLoc != null) {
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
}
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset));
paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
}
protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) {
int startCol = Math.max(highlights[i].getStart() - startingCharIndex, 0);
int endCol = Math.min(highlights[i].getEnd() - startingCharIndex, getString().length());
Color c = highlights[i].getColor();
for (Highlight highlight : highlights) {
int startCol = Math.max(highlight.getStart() - startingCharIndex, 0);
int endCol = Math.min(highlight.getEnd() - startingCharIndex, getString().length());
Color c = highlight.getColor();
if (endCol >= startCol) {
int start = findX(startCol);
int end = findX(endCol + 1);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -202,7 +201,7 @@ public class SimpleTextField implements Field {
public void paint(JComponent c, Graphics g, PaintContext context,
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
paintSelection(g, colorManager, 0);
paintHighlights(g, hlFactory.getHighlights(text, -1));
paintHighlights(g, hlFactory.getHighlights(this, text, -1));
g.setFont(metrics.getFont());
if (foregroundColor == null) {
foregroundColor = context.getForeground();
@ -226,10 +225,10 @@ public class SimpleTextField implements Field {
}
protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) {
int startCol = Math.max(highlights[i].getStart(), 0);
int endCol = Math.min(highlights[i].getEnd(), text.length());
Color c = highlights[i].getColor();
for (Highlight highlight : highlights) {
int startCol = Math.max(highlight.getStart(), 0);
int endCol = Math.min(highlight.getEnd(), text.length());
Color c = highlight.getColor();
if (endCol >= startCol) {
int start = findX(startCol);
int end = findX(endCol + 1);

View File

@ -268,7 +268,7 @@ public class VerticalLayoutTextField implements TextField {
cursorRow = cursorLoc.row();
}
Highlight[] highlights = hlFactory.getHighlights(getText(), cursorTextOffset);
Highlight[] highlights = hlFactory.getHighlights(this, getText(), cursorTextOffset);
int columns = 0;
int n = subFields.size();
Color fieldBackgroundColor = colorManager.getBackgroundColor();

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +16,6 @@
package docking.widgets.fieldpanel.internal;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigInteger;
import java.util.ArrayList;
@ -33,11 +30,8 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
public class TestBigLayoutModel implements LayoutModel {
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
private static final HighlightFactory hlFactory = new HighlightFactory() {
public Highlight[] getHighlights(String text, int cursorTextOffset) {
return NO_HIGHLIGHTS;
}
};
private static final HighlightFactory hlFactory =
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
ArrayList<LayoutModelListener> listeners = new ArrayList<LayoutModelListener>();
FontMetrics fm;
@ -86,14 +80,12 @@ public class TestBigLayoutModel implements LayoutModel {
if (index.compareTo(numIndexes) >= 0) {
return null;
}
String text =
name + ": This is line " + index +
String text = name + ": This is line " + index +
" More text to make line longer abcdefghijklmnopqrstuvwxyzabcdefghijk";
FieldElement fe1 = new TextFieldElement(new AttributedString(text, Color.BLACK, fm), 0, 0);
FieldElement fe2 =
new TextFieldElement(new AttributedString("More text", Color.BLACK, fm), 0, 0);
SingleRowLayout layout =
new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory),
SingleRowLayout layout = new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory),
new ClippingTextField(330, 100, fe2, hlFactory));
if (index.intValue() >= startBigSizes && index.intValue() <= endBigSizes) {
@ -143,12 +135,7 @@ public class TestBigLayoutModel implements LayoutModel {
contentPane.setLayout(new BorderLayout());
contentPane.add(scrollPanel);
JButton button = new JButton("Hit Me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.updateData(1000, 2000);
}
});
button.addActionListener(e -> model.updateData(1000, 2000));
contentPane.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,13 +15,18 @@
*/
package docking.widgets.fieldpanel.support;
import docking.widgets.fieldpanel.field.Field;
public interface HighlightFactory {
/**
* Returns the highlights for the given text.
* @param text the text to be considered for highlighting.
* Returns the highlights for the given text
*
* @param field the field that is requesting the highlight
* @param text the text to be considered for highlighting
* @param cursorTextOffset the position in the given text of the cursor. A -1 indicates the
* cursor is not in this field.
* @return an array of highlights to be rendered.
* @return an array of highlights to be rendered
*/
public Highlight[] getHighlights(String text, int cursorTextOffset);
public Highlight[] getHighlights(Field field, String text, int cursorTextOffset);
}

View File

@ -133,7 +133,7 @@ public class GTableWidget<T> extends JPanel {
listener.itemPicked(gFilterTable.getSelectedRowObject());
}
public void setItemPickListner(TableItemPickedListener<T> listener) {
public void setItemPickListener(TableItemPickedListener<T> listener) {
this.listener = listener;
}

View File

@ -867,7 +867,7 @@ public class GTree extends JPanel implements BusyListener {
tree.getModel().addTreeModelListener(listener);
}
public void removeGTModelListner(TreeModelListener listener) {
public void removeGTModelListener(TreeModelListener listener) {
tree.getModel().removeTreeModelListener(listener);
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,11 +18,12 @@ package docking.widgets.tree.support;
import java.awt.datatransfer.*;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import docking.widgets.tree.GTreeNode;
/**
* A transferable for sharing data via drag/drop and clipboard operations for GTrees.
* A transferable for sharing data via drag/drop and clipboard operations for GTrees
*/
public class GTreeNodeTransferable implements Transferable {
private final List<GTreeNode> selectedData;
@ -31,13 +31,14 @@ public class GTreeNodeTransferable implements Transferable {
/**
* Creates this transferable based upon the selected data and uses the given transfer
* handler to perform {@link Transferable} operations.
* @param handler the handler used to perform transfer operations.
* @param selectedData The
* handler to perform {@link Transferable} operations
*
* @param handler the handler used to perform transfer operations
* @param selectedData The selected tree nodes
*/
public GTreeNodeTransferable(GTreeTransferHandler handler, List<GTreeNode> selectedData) {
this.selectedData = selectedData;
this.transferHandler = handler;
this.transferHandler = Objects.requireNonNull(handler);
this.selectedData = Objects.requireNonNull(selectedData);
}
/**
@ -49,36 +50,40 @@ public class GTreeNodeTransferable implements Transferable {
}
/**
* Gets the transfer data from the selection based upon the given flavor.
* @param transferNodes The nodes from which to get the data.
* @param flavor The flavor of data to retreive from the given selection.
* Gets the transfer data from the selection based upon the given flavor
* @param flavor The flavor of data to retrieve from the given selection.
* @return the transfer data from the selection based upon the given flavor.
* @throws UnsupportedFlavorException if the given flavor is not one of the supported flavors
* returned by {@link #getSupportedDataFlavors(List)}.
* returned by {@link #getTransferDataFlavors()}
*/
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
return transferHandler.getTransferData(selectedData, flavor);
}
/**
* Returns the DataFlavors for the types of data that this transferable supports, based upon
* the given selection.
* @param transferNodes The nodes to base the DataFlavor selection upon.
* the given selection
*
* @return the DataFlavors for the types of data that this transferable supports, based upon
* the given selection.
* the given selection
*/
@Override
public DataFlavor[] getTransferDataFlavors() {
return transferHandler.getSupportedDataFlavors(selectedData);
}
/**
* A convenience method to determine if this transferable supports the given flavor.
* @return true if this transferable supports the given flavor.
* A convenience method to determine if this transferable supports the given flavor
* @return true if this transferable supports the given flavor
*/
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
DataFlavor[] flavors = transferHandler.getSupportedDataFlavors(selectedData);
for(int i=0;i<flavors.length;i++) {
if (flavors[i].equals(flavor)) {
for (DataFlavor f : flavors) {
if (f.equals(flavor)) {
return true;
}
}

View File

@ -15,7 +15,7 @@
*/
package docking.widgets.fieldpanel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import java.awt.*;
@ -40,11 +40,8 @@ public class FlowLayoutTextFieldTest extends AbstractGenericTest {
@Before
public void setUp() throws Exception {
HighlightFactory factory = new HighlightFactory() {
@Override
public Highlight[] getHighlights(String text, int cursorTextOffset) {
HighlightFactory factory = (field, text, cursorTextOffset) -> {
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
}
};
Font font = new Font("Times New Roman", 0, 14);

View File

@ -15,7 +15,7 @@
*/
package docking.widgets.fieldpanel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import java.awt.*;
@ -40,11 +40,8 @@ public class VerticalLayoutTextFieldTest extends AbstractGenericTest {
@Before
public void setUp() throws Exception {
HighlightFactory factory = new HighlightFactory() {
@Override
public Highlight[] getHighlights(String text, int cursorTextOffset) {
HighlightFactory factory = (field, text, cursorTextOffset) -> {
return new Highlight[] { new Highlight(4, 4, Color.YELLOW) };
}
};
Font font = new Font("Times New Roman", 0, 14);

View File

@ -254,7 +254,7 @@ public interface FileSystem {
/**
* Adds the given listener to be notified of file system changes.
* @param listener the listner to be added.
* @param listener the listener to be added.
*/
public void addFileSystemListener(FileSystemListener listener);

View File

@ -461,8 +461,8 @@ public class Application {
return null;
}
File file = getModuleFile(module, "build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(),
exactFilename);
File file = getModuleFile(module,
"build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), exactFilename);
if (file == null) {
file = getModuleFile(module, "os/" + Platform.CURRENT_PLATFORM.getDirectoryName(),
@ -471,7 +471,8 @@ public class Application {
// Allow win32 to be used for win64 as fallback
if (file == null && Platform.CURRENT_PLATFORM == Platform.WIN_64) {
file = getModuleFile(module, "build/os/" + Platform.WIN_32.getDirectoryName(), exactFilename);
file = getModuleFile(module, "build/os/" + Platform.WIN_32.getDirectoryName(),
exactFilename);
}
if (file == null && Platform.CURRENT_PLATFORM == Platform.WIN_64) {
file = getModuleFile(module, "os/" + Platform.WIN_32.getDirectoryName(), exactFilename);
@ -492,7 +493,8 @@ public class Application {
private File getOSFileInAnyModule(String path) throws FileNotFoundException {
File file = findModuleFile("build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
File file =
findModuleFile("build/os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
if (file == null) {
file = findModuleFile("os/" + Platform.CURRENT_PLATFORM.getDirectoryName(), path);
@ -657,7 +659,7 @@ public class Application {
/**
* Return the module root directory for the module with the given name.
* @param moduleName the name of the module.
* @return the module root directory for the module with the given name.
* @return the module root directory for the module with the given name or null if not found.
*/
public static ResourceFile getModuleRootDir(String moduleName) {
checkAppInitialized();

View File

@ -15,7 +15,7 @@
*/
package ghidra.graph.viewer.edge;
public interface PathHighlightListner {
public interface PathHighlightListener {
/**
* Called when the a path is highlighted.

View File

@ -99,7 +99,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
private CompletableFuture<ChkDominanceAlgorithm<V, E>> postDominanceFuture;
private CompletableFuture<Circuits> circuitFuture;
private PathHighlightListner listener = isHover -> {
private PathHighlightListener listener = isHover -> {
// stub
};
@ -108,7 +108,7 @@ public class VisualGraphPathHighlighter<V extends VisualVertex, E extends Visual
private SwingUpdateManager focusedVertexUpdater =
new SwingUpdateManager(() -> doUpdateFocusedVertex());
public VisualGraphPathHighlighter(VisualGraph<V, E> graph, PathHighlightListner listener) {
public VisualGraphPathHighlighter(VisualGraph<V, E> graph, PathHighlightListener listener) {
this.graph = graph;
if (listener != null) {
this.listener = listener;

View File

@ -16,8 +16,7 @@
package help.validator.location;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.Path;
import javax.help.HelpSet;
@ -43,21 +42,13 @@ public class DirectoryHelpModuleLocation extends HelpModuleLocation {
@Override
public GhidraTOCFile loadSourceTOCFile() {
try (DirectoryStream<Path> ds = Files.newDirectoryStream(helpDir, "TOC_Source*.xml")) {
for (Path file : ds) {
Path sourceTocPath = helpDir.resolve("TOC_Source.xml");
try {
return GhidraTOCFile.createGhidraTOCFile(file);
return GhidraTOCFile.createGhidraTOCFile(sourceTocPath);
}
catch (Exception e) {
throw new AssertException("Unexpected error loading source TOC file!: " + file,
throw new AssertException("Unexpected error loading source TOC file!: " + sourceTocPath,
e);
}
}
}
catch (IOException e) {
throw new AssertException("Error reading help path: " + helpDir);
}
throw new AssertException("Help module has no TOC_Source.xml file: " + helpDir);
}
}

View File

@ -17,11 +17,11 @@ package help.validator.location;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.*;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import javax.help.HelpSet;
import javax.help.HelpSetException;
@ -32,6 +32,12 @@ import help.validator.model.GhidraTOCFile;
public class JarHelpModuleLocation extends HelpModuleLocation {
/*
* format of 'helpDir':
* jar:file:///.../ghidra-prep/Ghidra/Features/Base/build/libs/Base.jar!/help
*/
private static final Pattern JAR_FILENAME_PATTERN = Pattern.compile(".*/(\\w*)\\.jar!/.*");
private static Map<String, String> env = new HashMap<String, String>();
static {
env.put("create", "false");
@ -69,19 +75,51 @@ public class JarHelpModuleLocation extends HelpModuleLocation {
@Override
public HelpSet loadHelpSet() {
try (DirectoryStream<Path> ds = Files.newDirectoryStream(helpDir, "*_HelpSet.hs");) {
for (Path path : ds) {
return new GHelpSet(null, path.toUri().toURL());
// Our convention is to name the help set after the module
File jarFile = getJarFile();
String moduleName = getModuleName(jarFile);
Path hsPath = helpDir.resolve(moduleName + "_HelpSet.hs");
try {
return new GHelpSet(null, hsPath.toUri().toURL());
}
catch (MalformedURLException | HelpSetException e) {
throw new AssertException(
"Pre-built help jar file is missing it's help set: " + helpDir, e);
}
catch (IOException e) {
throw new AssertException("No _HelpSet.hs file found for help directory: " + helpDir);
}
catch (HelpSetException e) {
throw new AssertException("Error loading help set for " + helpDir);
}
throw new AssertException("Pre-built help jar file is missing it's help set: " + helpDir);
private File getJarFile() {
// format: jar:file:///.../Ghidra/Features/<module>/build/libs/<module>.jar!/help
String uriString = helpDir.toUri().toString();
int start = uriString.indexOf("file:/");
String chopped = uriString.substring(start);
int end = chopped.indexOf("!");
chopped = chopped.substring(0, end);
return new File(chopped);
}
private String getModuleName(File jarFile) {
String name = jarFile.getName();
int dotIndex = name.indexOf('.');
return name.substring(0, dotIndex);
}
@Override
public Path getHelpModuleLocation() {
// start format: jar:file:///.../Ghidra/Features/Base/build/libs/Base.jar!/help
// format: file:///.../Ghidra/Features/<module>/build/libs/<module>.jar
File jarFile = getJarFile();
String moduleName = getModuleName(jarFile);
String fullPath = jarFile.getPath();
int moduleNameStart = fullPath.indexOf(moduleName);
int end = moduleNameStart + moduleName.length();
String moduleString = fullPath.substring(0, end);
Path modulePath = Paths.get(moduleString);
return modulePath;
}
@Override

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +15,14 @@
*/
package ghidra.app.util;
import ghidra.framework.plugintool.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import ghidra.framework.plugintool.PluginTool;
/**
* Interface for classes that will handle drop actions for files dropped onto the tool
*/
public interface FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f);
}

View File

@ -1,37 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util;
import ghidra.framework.PluggableServiceRegistry;
import ghidra.framework.main.datatree.DataTreeDragNDropHandler;
import ghidra.framework.main.datatree.VersionInfoTransferable;
public class FileOpenDataFlavorHandlerService {
static {
PluggableServiceRegistry.registerPluggableService(FileOpenDataFlavorHandlerService.class, new FileOpenDataFlavorHandlerService());
}
public static void registerDataFlavorHandlers() {
FileOpenDataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(FileOpenDataFlavorHandlerService.class);
factory.doRegisterDataFlavorHandlers();
}
protected void doRegisterDataFlavorHandlers() {
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor, new LocalTreeNodeFlavorHandler());
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalTreeNodeFlavorHandler());
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +15,6 @@
*/
package ghidra.app.util;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.CascadedDropTarget;
import java.awt.Component;
import java.awt.Container;
import java.awt.datatransfer.DataFlavor;
@ -33,6 +29,8 @@ import javax.swing.CellRendererPane;
import docking.DropTargetHandler;
import docking.dnd.DropTgtAdapter;
import docking.dnd.Droppable;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.CascadedDropTarget;
/**
* Handles drag/drop events on a given component such that a file
@ -44,9 +42,6 @@ import docking.dnd.Droppable;
public class FileOpenDropHandler implements DropTargetHandler, Droppable, ContainerListener {
private static HashMap<DataFlavor, FileOpenDataFlavorHandler> handlers =
new HashMap<DataFlavor, FileOpenDataFlavorHandler>();
static {
FileOpenDataFlavorHandlerService.registerDataFlavorHandlers();
}
private DropTgtAdapter dropTargetAdapter;
private DropTarget globalDropTarget;
@ -75,11 +70,13 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
/**
* Dispose this drop handler.
*/
@Override
public void dispose() {
deinitializeComponents(component);
globalDropTarget.removeDropTargetListener(dropTargetAdapter);
}
@Override
public boolean isDropOk(DropTargetDragEvent e) {
Set<DataFlavor> flavors = handlers.keySet();
for (DataFlavor dataFlavor : flavors) {
@ -90,6 +87,7 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
return false;
}
@Override
public void add(Object obj, DropTargetDropEvent e, DataFlavor f) {
FileOpenDataFlavorHandler handler = handlers.get(f);
if (handler != null) {
@ -97,25 +95,29 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
}
@Override
public void dragUnderFeedback(boolean ok, DropTargetDragEvent e) {
// nothing to display or do
}
@Override
public void undoDragUnderFeedback() {
// nothing to display or do
}
private void initializeComponents(Component comp) {
if (comp instanceof CellRendererPane)
if (comp instanceof CellRendererPane) {
return;
}
if (comp instanceof Container) {
Container c = (Container) comp;
c.addContainerListener(this);
Component comps[] = c.getComponents();
for (Component element : comps)
for (Component element : comps) {
initializeComponents(element);
}
}
DropTarget primaryDropTarget = comp.getDropTarget();
if (primaryDropTarget != null) {
new CascadedDropTarget(comp, primaryDropTarget, globalDropTarget);
@ -123,16 +125,18 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
private void deinitializeComponents(Component comp) {
if (comp instanceof CellRendererPane)
if (comp instanceof CellRendererPane) {
return;
}
if (comp instanceof Container) {
Container c = (Container) comp;
c.removeContainerListener(this);
Component comps[] = c.getComponents();
for (Component element : comps)
for (Component element : comps) {
deinitializeComponents(element);
}
}
DropTarget dt = comp.getDropTarget();
if (dt instanceof CascadedDropTarget) {
CascadedDropTarget target = (CascadedDropTarget) dt;
@ -141,15 +145,18 @@ public class FileOpenDropHandler implements DropTargetHandler, Droppable, Contai
}
}
@Override
public void componentAdded(ContainerEvent e) {
initializeComponents(e.getChild());
}
@Override
public void componentRemoved(ContainerEvent e) {
deinitializeComponents(e.getChild());
}
public static void addDataFlavorHandler(DataFlavor dataFlavor, FileOpenDataFlavorHandler handler) {
public static void addDataFlavorHandler(DataFlavor dataFlavor,
FileOpenDataFlavorHandler handler) {
handlers.put(dataFlavor, handler);
}

View File

@ -1,72 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.app.util;
import ghidra.framework.main.GetVersionedObjectTask;
import ghidra.framework.main.datatree.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.util.List;
final class LocalTreeNodeFlavorHandler implements FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
domainFiles[i] = (DomainFile) files.get(i);
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
DomainFileNode node = (DomainFileNode) files.get(i);
domainFiles[i] = node.getDomainFile();
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(VersionInfoTransferable.localVersionInfoFlavor)) {
VersionInfo info = (VersionInfo) obj;
Project project = tool.getProject();
ProjectData projectData = project.getProjectData();
DomainFile file = projectData.getFile(info.getDomainFilePath());
DomainObject versionedObj = getVersionedObject(tool, file, info.getVersionNumber());
if (versionedObj != null) {
DomainFile domainFile = versionedObj.getDomainFile();
if (domainFile != null) {
tool.acceptDomainFiles(new DomainFile[] { domainFile });
}
versionedObj.release(this);
}
}
}
private DomainObject getVersionedObject(PluginTool tool, DomainFile file, int versionNumber) {
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file, versionNumber);
tool.execute(task, 250);
return task.getVersionedObject();
}
}

View File

@ -1,47 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.app.util;
import ghidra.framework.main.*;
import ghidra.framework.main.datatree.*;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
final class LocalVersionInfoFlavorHandler implements
FileOpenDataFlavorHandler {
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
VersionInfo info = (VersionInfo) obj;
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
GetVersionedObjectTask task = new GetVersionedObjectTask(this, file,
info.getVersionNumber());
tool.execute(task, 250);
DomainObject versionedObj = task.getVersionedObject();
if (versionedObj != null) {
DomainFile vfile = versionedObj.getDomainFile();
tool.acceptDomainFiles(new DomainFile[] {vfile});
versionedObj.release(this);
}
}
}

View File

@ -30,6 +30,7 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
private Component comp;
private boolean isActiveProject;
private ProjectData projectData;
private boolean isTransient;
public ProjectDataActionContext(ComponentProvider provider, ProjectData projectData,
Object contextObject, List<DomainFolder> selectedFolders,
@ -112,4 +113,20 @@ public class ProjectDataActionContext extends ActionContext implements DomainFil
}
return false;
}
/**
* Transient data is that which will appear in a temporary project dialog
* @param isTransient true if transient
*/
public void setTransient(boolean isTransient) {
this.isTransient = isTransient;
}
/**
* Transient data is that which will appear in a temporary project dialog
* @return true if transient
*/
public boolean isTransient() {
return isTransient;
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,20 +25,42 @@ public abstract class ProjectDataContextAction extends DockingAction {
}
@Override
public final boolean isEnabledForContext(ActionContext actionContext) {
public boolean isEnabledForContext(ActionContext actionContext) {
if (!(actionContext instanceof ProjectDataActionContext)) {
return false;
}
ProjectDataActionContext context = (ProjectDataActionContext) actionContext;
if (ignoreTransientProject(context)) {
return false;
}
return isEnabledForContext(context);
}
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
if (supportsTransientProjectData()) {
return false;
}
return context.isTransient();
}
/**
* Signals that this action can work on normal project data, as well as transient data.
* Transient data is that which will appear in a temporary project dialog.
*
* @return true if this action works on transient project data
*/
protected boolean supportsTransientProjectData() {
return false;
}
protected boolean isEnabledForContext(ProjectDataActionContext context) {
return context.hasOneOrMoreFilesAndFolders();
}
@Override
public final void actionPerformed(ActionContext context) {
public void actionPerformed(ActionContext context) {
actionPerformed((ProjectDataActionContext) context);
}
@ -59,7 +80,7 @@ public abstract class ProjectDataContextAction extends DockingAction {
@Override
public boolean isAddToPopup(ActionContext context) {
if (!(context instanceof ProjectDataActionContext)) {
if (!isEnabledForContext(context)) {
return false;
}
return isAddToPopup((ProjectDataActionContext) context);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,10 +29,32 @@ public abstract class ProjectDataContextToggleAction extends ToggleDockingAction
if (!(actionContext instanceof ProjectDataActionContext)) {
return false;
}
ProjectDataActionContext context = (ProjectDataActionContext) actionContext;
if (ignoreTransientProject(context)) {
return false;
}
return isEnabledForContext(context);
}
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
if (supportsTransientProjectData()) {
return false;
}
return context.isTransient();
}
/**
* Signals that this action can work on normal project data, as well as transient data.
* Transient data is that which will appear in a temporary project dialog.
*
* @return true if this action works on transient project data
*/
protected boolean supportsTransientProjectData() {
return false;
}
protected boolean isEnabledForContext(ProjectDataActionContext context) {
return context.hasOneOrMoreFilesAndFolders();
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +15,9 @@
*/
package ghidra.framework.main.datatable;
import ghidra.framework.main.datatree.ProjectDataTreeActionContext;
import docking.ActionContext;
import docking.action.DockingAction;
import ghidra.framework.main.datatree.ProjectDataTreeActionContext;
public abstract class ProjectDataTreeContextAction extends DockingAction {
@ -31,10 +30,32 @@ public abstract class ProjectDataTreeContextAction extends DockingAction {
if (!(actionContext instanceof ProjectDataTreeActionContext)) {
return false;
}
ProjectDataTreeActionContext context = (ProjectDataTreeActionContext) actionContext;
if (ignoreTransientProject(context)) {
return false;
}
return isEnabledForContext(context);
}
protected boolean ignoreTransientProject(ProjectDataActionContext context) {
if (supportsTransientProjectData()) {
return false;
}
return context.isTransient();
}
/**
* Signals that this action can work on normal project data, as well as transient data.
* Transient data is that which will appear in a temporary project dialog.
*
* @return true if this action works on transient project data
*/
protected boolean supportsTransientProjectData() {
return false;
}
protected boolean isEnabledForContext(ProjectDataTreeActionContext context) {
return context.hasOneOrMoreFilesAndFolders();
}
@ -60,7 +81,7 @@ public abstract class ProjectDataTreeContextAction extends DockingAction {
@Override
public boolean isAddToPopup(ActionContext context) {
if (!(context instanceof ProjectDataTreeActionContext)) {
if (!isEnabledForContext(context)) {
return false;
}
return isAddToPopup((ProjectDataTreeActionContext) context);

View File

@ -1,38 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.framework.main.datatree;
import ghidra.framework.PluggableServiceRegistry;
public class DataFlavorHandlerService {
static {
PluggableServiceRegistry.registerPluggableService(DataFlavorHandlerService.class, new DataFlavorHandlerService());
}
public static void registerDataFlavorHandlers() {
DataFlavorHandlerService factory = PluggableServiceRegistry.getPluggableService(DataFlavorHandlerService.class);
factory.doRegisterDataFlavorHandlers();
}
protected void doRegisterDataFlavorHandlers() {
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler();
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
DataTreeDragNDropHandler.addActiveDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
}
}

View File

@ -16,8 +16,7 @@
package ghidra.framework.main.datatree;
import java.awt.Component;
import java.awt.event.*;
import java.util.List;
import java.awt.event.KeyEvent;
import javax.swing.JTree;
import javax.swing.KeyStroke;
@ -30,20 +29,13 @@ import docking.widgets.tree.support.GTreeRenderer;
import ghidra.framework.main.FrontEndTool;
/**
* Tree that shows the folders and domain files in a Project.
* Tree that shows the folders and domain files in a Project
*/
public class DataTree extends GTree {
static {
DataFlavorHandlerService.registerDataFlavorHandlers();
}
private boolean isActive;
private DataTreeDragNDropHandler dragNDropHandler;
/**
* Constructor
* @param folder root domain folder for the project.
*/
DataTree(FrontEndTool tool, GTreeRootNode root) {
super(root);
@ -53,30 +45,11 @@ public class DataTree extends GTree {
docking.ToolTipManager.sharedInstance().registerComponent(this);
//When the user right clicks, change selection to what the mouse was under
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent evt) {
if (evt.getButton() == MouseEvent.BUTTON3) {
//Find the would-be newly selected path
TreePath newPath = getPathForLocation(evt.getX(), evt.getY());
if (newPath == null) {
return;
}
//Determine if the path is already selected--If so, do not change the selection
TreePath[] paths = getSelectionPaths();
if (paths != null) {
for (TreePath element : paths) {
if (element.equals(newPath)) {
return;
}
}
}
}
}
});
if (tool != null) {
dragNDropHandler = new DataTreeDragNDropHandler(tool, this, isActive);
setDragNDropHandler(dragNDropHandler);
}
initializeKeyEvents();
}
@ -92,74 +65,10 @@ public class DataTree extends GTree {
KeyStroke.getKeyStroke(KeyEvent.VK_X, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
}
void setProjectActive(boolean b) {
dragNDropHandler.setProjectActive(b);
void setProjectActive(boolean isActive) {
if (dragNDropHandler != null) {
dragNDropHandler.setProjectActive(isActive);
}
/**
* Return true if this path has all of its subpaths expanded.
*/
public boolean allPathsExpanded(TreePath path) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (node.isLeaf()) {
return true;
}
if (isCollapsed(path)) {
return false;
}
boolean allLeaves = true;
List<GTreeNode> children = node.getChildren();
for (GTreeNode child : children) {
if (child.isLeaf()) {
continue;
}
allLeaves = false;
if (!isExpanded(child.getTreePath())) {
return false;
}
if (!allPathsExpanded(child.getTreePath())) {
return false;
}
}
if (allLeaves) {
return isExpanded(path);
}
return true;
}
/**
* Return true if this path has all of its subpaths collapsed.
*/
public boolean allPathsCollapsed(TreePath path) {
GTreeNode node = (GTreeNode) path.getLastPathComponent();
if (isExpanded(path)) {
return false;
}
boolean allLeaves = true; // variable for knowing whether all children are leaves
node.getChildren();
for (GTreeNode child : node) {
if (child.isLeaf()) {
continue;
}
allLeaves = false;
if (!isCollapsed(child.getTreePath())) {
return false;
}
if (!allPathsCollapsed(child.getTreePath())) {
return false;
}
}
if (allLeaves) {
return isCollapsed(path);
}
return true;
}
public void clearSelection() {
@ -183,20 +92,7 @@ public class DataTree extends GTree {
getJTree().stopEditing();
}
//////////////////////////////////////////////////////////////////////
// *** private methods
//////////////////////////////////////////////////////////////////////
/**
* Tree cell renderer to use the appropriate icons for the
* DataTreeNodes.
*/
private class DataTreeCellRenderer extends GTreeRenderer {
/**
* Configures the renderer based on the passed in components.
* The icon is set according to value, expanded, and leaf
* parameters.
*/
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row, boolean doesHaveFocus) {
@ -209,7 +105,5 @@ public class DataTree extends GTree {
}
return this;
}
}
}

View File

@ -23,6 +23,7 @@ import javax.swing.tree.TreePath;
import docking.dnd.GClipboard;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeNodeTransferable;
import ghidra.util.Msg;
/**
@ -35,14 +36,8 @@ public class DataTreeClipboardUtils {
* Static instance of a callback handler that is notified when the clipboard is changed
* and our data is discarded.
*/
private static final ClipboardOwner DATATREE_CLIPBOARD_OWNER = new ClipboardOwner() {
@Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
// This is called when something other than this class modifies the clipboard
// and our data is discarded.
clearCuttables(contents);
}
};
private static final ClipboardOwner DATATREE_CLIPBOARD_OWNER =
(clipboard, contents) -> clearCuttables(contents);
/**
* Pushes the GTreeNodes in the specified TreePath array to the clipboard.
@ -59,8 +54,9 @@ public class DataTreeClipboardUtils {
GTreeNode node = (GTreeNode) element.getLastPathComponent();
list.add(node);
}
DataTreeNodeTransferable contents =
new DataTreeNodeTransferable(tree.getDragNDropHandler(), list);
GTreeNodeTransferable contents =
new GTreeNodeTransferable(tree.getDragNDropHandler(), list);
try {
clipboard.setContents(contents, DATATREE_CLIPBOARD_OWNER);

View File

@ -30,9 +30,7 @@ import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
private static Map<DataFlavor, DataFlavorHandler> activeProjectDropFlavorHandlerMap =
new HashMap<>();
private static Map<DataFlavor, DataFlavorHandler> inactiveProjectDropFlavorHandlerMap =
private static Map<DataFlavor, DataTreeFlavorHandler> activeProjectDropFlavorHandlerMap =
new HashMap<>();
public static DataFlavor localDomainFileTreeFlavor = createLocalTreeNodeFlavor();
public static DataFlavor localDomainFileFlavor = createLocalTreeFlavor();
@ -79,29 +77,31 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
public void drop(GTreeNode destination, Transferable transferable, int dropAction) {
DataFlavor[] transferDataFlavors = transferable.getTransferDataFlavors();
for (DataFlavor dataFlavor : transferDataFlavors) {
DataFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
DataTreeFlavorHandler flavorHandler = getFlavorHandler(dataFlavor);
if (flavorHandler != null) {
try {
Object transferData = transferable.getTransferData(dataFlavor);
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
}
catch (UnsupportedFlavorException e) {
throw new AssertException(
"Got unsupported flavor from using a supported flavor");
}
catch (IOException e) {
Msg.showError(this, null, "IO Error", "Error during drop", e);
}
handleDrop(destination, transferable, dropAction, dataFlavor, flavorHandler);
return;
}
}
}
private DataFlavorHandler getFlavorHandler(DataFlavor flavor) {
if (isActiveProject) {
return activeProjectDropFlavorHandlerMap.get(flavor);
private void handleDrop(GTreeNode destination, Transferable transferable, int dropAction,
DataFlavor dataFlavor, DataTreeFlavorHandler flavorHandler) {
try {
Object transferData = transferable.getTransferData(dataFlavor);
flavorHandler.handle(tool, tree, destination, transferData, dropAction);
}
return inactiveProjectDropFlavorHandlerMap.get(flavor);
catch (UnsupportedFlavorException e) {
throw new AssertException("Got unsupported flavor from using a supported flavor");
}
catch (IOException e) {
Msg.showError(this, null, "IO Error", "Error during drop", e);
}
}
private DataTreeFlavorHandler getFlavorHandler(DataFlavor flavor) {
return activeProjectDropFlavorHandlerMap.get(flavor);
}
@Override
@ -134,13 +134,6 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
@Override
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> transferNodes) {
return allSupportedFlavors;
// Set<DataFlavor> keySet = null;
// if (isActiveProject) {
// keySet = activeProjectDropFlavorHandlerMap.keySet();
// } else {
// keySet = inactiveProjectDropFlavorHandlerMap.keySet();
// }
// return keySet.toArray(new DataFlavor[keySet.size()]);
}
@Override
@ -196,24 +189,15 @@ public class DataTreeDragNDropHandler implements GTreeDragNDropHandler {
return false;
}
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
public static void addActiveDataFlavorHandler(DataFlavor flavor, DataTreeFlavorHandler handler) {
activeProjectDropFlavorHandlerMap.put(flavor, handler);
}
public static void addInactiveDataFlavorHandler(DataFlavor flavor, DataFlavorHandler handler) {
inactiveProjectDropFlavorHandlerMap.put(flavor, handler);
}
public static DataFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
public static DataTreeFlavorHandler removeActiveDataFlavorHandler(DataFlavor flavor) {
return activeProjectDropFlavorHandlerMap.remove(flavor);
}
public static DataFlavorHandler removeInctiveDataFlavorHandler(DataFlavor flavor) {
return inactiveProjectDropFlavorHandlerMap.remove(flavor);
}
public void setProjectActive(boolean b) {
isActiveProject = b;
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,10 +15,13 @@
*/
package ghidra.framework.main.datatree;
import ghidra.framework.main.FrontEndTool;
import docking.widgets.tree.GTreeNode;
import ghidra.framework.plugintool.PluginTool;
public interface DataFlavorHandler {
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
/**
* Interface for classes that will handle drop actions for {@link DataTree}s.
*/
public interface DataTreeFlavorHandler {
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction);
}

View File

@ -1,30 +0,0 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.framework.main.datatree;
import java.util.List;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeNodeTransferable;
import docking.widgets.tree.support.GTreeTransferHandler;
public class DataTreeNodeTransferable extends GTreeNodeTransferable {
public DataTreeNodeTransferable(GTreeTransferHandler handler, List<GTreeNode> selectedData) {
super(handler, selectedData);
}
}

View File

@ -15,29 +15,55 @@
*/
package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDropEvent;
import java.io.IOException;
import java.util.List;
import javax.swing.SwingUtilities;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeState;
import ghidra.framework.main.FrontEndTool;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateFileException;
import ghidra.util.exception.FileInUseException;
import ghidra.util.task.*;
final class LocalTreeNodeHandler implements DataFlavorHandler {
public final class LocalTreeNodeHandler
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
private DataTree dataTree;
private GTreeState treeState;
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
if (f.equals(DataTreeDragNDropHandler.localDomainFileFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
domainFiles[i] = (DomainFile) files.get(i);
}
tool.acceptDomainFiles(domainFiles);
}
else if (f.equals(DataTreeDragNDropHandler.localDomainFileTreeFlavor)) {
List<?> files = (List<?>) obj;
DomainFile[] domainFiles = new DomainFile[files.size()];
for (int i = 0; i < files.size(); i++) {
DomainFileNode node = (DomainFileNode) files.get(i);
domainFiles[i] = node.getDomainFile();
}
tool.acceptDomainFiles(domainFiles);
}
}
@Override
@SuppressWarnings("unchecked")
public void handle(FrontEndTool tool, DataTree tree, GTreeNode destinationNode,
public void handle(PluginTool tool, DataTree tree, GTreeNode destinationNode,
Object transferData, int dropAction) {
this.dataTree = tree;
@ -52,12 +78,9 @@ final class LocalTreeNodeHandler implements DataFlavorHandler {
new TaskLauncher(task, dataTree, 1000);
if (treeState != null) { // is set to null if drag results in a task
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SystemUtilities.runSwingLater(() -> {
treeState.updateStateForMovedNodes();
dataTree.restoreTreeState(treeState);
}
});
}
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,23 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package ghidra.framework.main.datatree;
import ghidra.framework.client.*;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.util.task.TaskLauncher;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.IOException;
import docking.widgets.tree.GTreeNode;
import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.client.*;
import ghidra.framework.main.GetVersionedObjectTask;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.task.TaskLauncher;
final class LocalVersionInfoHandler implements DataFlavorHandler {
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
public final class LocalVersionInfoHandler
implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
VersionInfo info = (VersionInfo) obj;
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
GetVersionedObjectTask task =
new GetVersionedObjectTask(this, file, info.getVersionNumber());
tool.execute(task, 250);
DomainObject versionedObj = task.getVersionedObject();
if (versionedObj != null) {
DomainFile vfile = versionedObj.getDomainFile();
tool.acceptDomainFiles(new DomainFile[] { vfile });
versionedObj.release(this);
}
}
@Override
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);
@ -42,10 +60,13 @@ final class LocalVersionInfoHandler implements DataFlavorHandler {
}
DomainFile file = tool.getProject().getProjectData().getFile(info.getDomainFilePath());
if (file != null) {
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder), dataTree, 500);
new TaskLauncher(new CopyFileVersionTask(file, info.getVersionNumber(), folder),
dataTree, 500);
}
}
catch (NotConnectedException exc) {}
catch (NotConnectedException exc) {
// not sure why we squash this?
}
catch (IOException exc) {
ClientUtil.handleException(rep, exc, "Repository Connection", tool.getToolFrame());
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +15,13 @@
*/
package ghidra.framework.main.datatree;
import ghidra.framework.main.datatable.ProjectDataActionContext;
import ghidra.framework.model.*;
import java.util.List;
import javax.swing.tree.TreePath;
import docking.ComponentProvider;
import ghidra.framework.main.datatable.ProjectDataActionContext;
import ghidra.framework.model.*;
public class ProjectDataTreeActionContext extends ProjectDataActionContext {
@ -52,7 +50,7 @@ public class ProjectDataTreeActionContext extends ProjectDataActionContext {
return selectionPaths;
}
public DataTree getDataTree() {
public DataTree getTree() {
return tree;
}
}

View File

@ -55,33 +55,26 @@ public class ProjectDataTreePanel extends JPanel {
private ChangeManager changeMgr;
private boolean isActiveProject;
private FrontEndTool tool; // may be null if the panel is inside of the
// these may be null if the panel is inside of a dialog
private FrontEndTool tool;
private FrontEndPlugin plugin;
// data tree dialog
/**
* Construct an empty panel that is going to be used as the active
* panel.
* @param tool front end tool
* Construct an empty panel that is going to be used as the active panel
* @param plugin front end plugin
*/
public ProjectDataTreePanel(FrontEndPlugin plugin) {
this(null, true, plugin, null);
}
/**
* Construct a new DataTreePanel.
* Constructor
*
* @param projectName name of project
* @param projectData object that provides access to all the user data
* folders in a project
* @param isActiveProject true if the project is active, and the
* data tree may be modified
* @param tool front end tool; will be null if the panel is used in a dialog
* @param actionMgr class to handle enablement of actions; an ActionManager
* is passed in when several data tree panels all need to use the
* same ActionManager, e.g., the viewed projects in the front end tool;
* actionMgr will be null if the panel is used in a dialog
* @param plugin front end plugin; will be null if the panel is used in a dialog
* @param filter optional filter that is used to hide programs from view
*/
public ProjectDataTreePanel(String projectName, boolean isActiveProject, FrontEndPlugin plugin,
DomainFileFilter filter) {
@ -211,9 +204,6 @@ public class ProjectDataTreePanel extends JPanel {
}
}
/**
* Set the help location for the data tree.
*/
public void setHelpLocation(HelpLocation helpLocation) {
HelpService help = Help.getHelpService();
help.registerHelp(tree, helpLocation);
@ -228,8 +218,9 @@ public class ProjectDataTreePanel extends JPanel {
}
/**
* Get the number of selected items in the tree.
* These could be either DomainFile's or DomainFolder's.
* Get the number of selected items in the tree. These could be either files or folders.
*
* @return the number of selected items in the tree.
*/
public int getSelectedItemCount() {
return tree.getSelectionCount();
@ -279,47 +270,31 @@ public class ProjectDataTreePanel extends JPanel {
tree.removeGTreeSelectionListener(l);
}
/**
* Add a mouse listener to the data tree.
*/
public void addTreeMouseListener(MouseListener l) {
tree.addMouseListener(l);
}
/**
* Remove the mouse listener from the data tree.
*/
public void removeTreeMouseListener(MouseListener l) {
tree.removeMouseListener(l);
}
/**
* Set the preferred size of the scroll pane that contains the
* data tree.
*/
public void setPreferredTreePanelSize(Dimension d) {
tree.setPreferredSize(d);
}
/**
* Get the project data that this data tree panel is operating on.
*/
public ProjectData getProjectData() {
return projectData;
}
/**
* Notification that the project was renamed; update the root node name
* and reload the node.
* and reload the node
* @param newName the new project name
*/
public void projectRenamed(String newName) {
updateProjectName(newName);
}
/**
* Notification that panel is being disposed.
*
*/
public void dispose() {
if (projectData != null) {
projectData.removeDomainFolderChangeListener(changeMgr);
@ -328,10 +303,12 @@ public class ProjectDataTreePanel extends JPanel {
}
/**
* Get the data tree node that is selected.
* @param e mouse event for the popup; may be null if this is being
* called as a result of the key binding pressed
* @return null if there is no selection
* Get the data tree node that is selected
*
* @param provider the provider with which to construct the new context
* @param e mouse event for the popup; may be null if this is being called as a result of
* the key binding pressed
* @return the new context; null if there is no selection
*/
public ActionContext getActionContext(ComponentProvider provider, MouseEvent e) {
if (root instanceof NoProjectNode) {
@ -360,13 +337,13 @@ public class ProjectDataTreePanel extends JPanel {
}
}
return new ProjectDataTreeActionContext(provider, projectData, selectionPaths,
domainFolderList, domainFileList, tree, isActiveProject);
ProjectDataTreeActionContext context = new ProjectDataTreeActionContext(provider,
projectData, selectionPaths, domainFolderList, domainFileList, tree, isActiveProject);
boolean isTransient = tool == null; // null for stand-alone dialog, not the project's tree
context.setTransient(isTransient);
return context;
}
/**
* get the data tree for this data tree panel.
*/
public DataTree getDataTree() {
return tree;
}
@ -380,17 +357,10 @@ public class ProjectDataTreePanel extends JPanel {
tree.setFilterVisible(enabled);
}
/**
* Return true if this data tree panel is listening to domain folder
* changes.
*/
boolean domainFolderListenerAdded() {
return changeMgr != null;
}
/**
* Get the folder change listener.
*/
DomainFolderChangeListener getFolderChangeListener() {
return changeMgr;
}

View File

@ -33,7 +33,7 @@ public class ProjectDataCollapseAction extends ProjectDataTreeContextAction {
@Override
protected void actionPerformed(ProjectDataTreeActionContext context) {
DataTree tree = context.getDataTree();
DataTree tree = context.getTree();
TreePath[] paths = context.getSelectionPaths();
collapse(tree, paths[0]);
}

View File

@ -40,7 +40,7 @@ public class ProjectDataCopyAction extends ProjectDataCopyCutBaseAction {
protected void actionPerformed(ProjectDataTreeActionContext context) {
TreePath[] paths = adjustSelectionPaths(context.getSelectionPaths());
DataTreeClipboardUtils.setClipboardContents(context.getDataTree(), paths);
DataTreeClipboardUtils.setClipboardContents(context.getTree(), paths);
}

View File

@ -40,7 +40,7 @@ public class ProjectDataCutAction extends ProjectDataCopyCutBaseAction {
protected void actionPerformed(ProjectDataTreeActionContext context) {
TreePath[] paths = adjustSelectionPaths(context.getSelectionPaths());
DataTreeClipboardUtils.setClipboardContents(context.getDataTree(), paths);
DataTreeClipboardUtils.setClipboardContents(context.getTree(), paths);
markNodesCut(paths);
}

View File

@ -33,7 +33,7 @@ public class ProjectDataExpandAction extends ProjectDataTreeContextAction {
@Override
protected void actionPerformed(ProjectDataTreeActionContext context) {
DataTree tree = context.getDataTree();
DataTree tree = context.getTree();
TreePath[] paths = context.getSelectionPaths();
expand(tree, paths[0]);
}

View File

@ -41,6 +41,12 @@ public class ProjectDataNewFolderAction extends ProjectDataContextAction {
markHelpUnnecessary();
}
@Override
protected boolean supportsTransientProjectData() {
// we allow the user to create new folders even in transient projects
return true;
}
@Override
protected void actionPerformed(ProjectDataActionContext context) {
createNewFolder(context);

View File

@ -44,7 +44,7 @@ public class ProjectDataPasteAction extends ProjectDataCopyCutBaseAction {
GTreeNode node = (GTreeNode) context.getContextObject();
DomainFolderNode destNode = getFolderForNode(node);
paste(context.getDataTree(), destNode);
paste(context.getTree(), destNode);
}
@Override

View File

@ -50,6 +50,10 @@ public class ProjectDataReadOnlyAction extends ProjectDataContextToggleAction {
if (context.getFolderCount() != 0 || context.getFileCount() != 1) {
return false;
}
if (ignoreTransientProject(context)) {
return false;
}
DomainFile domainFile = context.getSelectedFiles().get(0);
setSelected(domainFile.isReadOnly());
return true;

View File

@ -36,7 +36,7 @@ public class ProjectDataSelectAction extends ProjectDataTreeContextAction {
@Override
protected void actionPerformed(ProjectDataTreeActionContext context) {
DataTree tree = context.getDataTree();
DataTree tree = context.getTree();
TreePath[] paths = context.getSelectionPaths();
GTreeNode node = (GTreeNode) paths[0].getLastPathComponent();
selectAllChildren(tree, node);

View File

@ -59,12 +59,12 @@ public class GProgressBar extends JPanel {
private Timer updateTimer;
private EmptyBorderButton cancelButton;
private CancelledListener cancelledListner;
private CancelledListener cancelledListener;
public GProgressBar(CancelledListener cancelledListner, boolean includeTextField,
public GProgressBar(CancelledListener cancelledListener, boolean includeTextField,
boolean includeCancelButton, boolean includeAnimatedIcon, float fontSize) {
super(new BorderLayout(5, 1));
this.cancelledListner = cancelledListner;
this.cancelledListener = cancelledListener;
this.fontSize = fontSize;
buildProgressPanel(includeTextField, includeCancelButton, includeAnimatedIcon);
@ -199,13 +199,13 @@ public class GProgressBar extends JPanel {
}
public void cancel() {
if (cancelledListner != null) {
cancelledListner.cancelled();
if (cancelledListener != null) {
cancelledListener.cancelled();
}
}
public void setCancelledListener(CancelledListener listener) {
this.cancelledListner = listener;
this.cancelledListener = listener;
}
private void buildProgressPanel(boolean includeTextField, boolean includeCancelButton,

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/Framework SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java"/>
<listEntry value="/Framework Utility/src/main/java/ghidra/GhidraLauncher.java"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="1"/>
@ -11,9 +11,17 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ghidra.pcodeCPort.slgh_compile.SleighCompile"/>
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-a &quot;${project_loc}/data/languages&quot;"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework SoftwareModeling"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djava.awt.headless=true -Xmx2048M -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;5&quot; projectName=&quot;Framework Utility&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ghidra.GhidraLauncher"/>
<listAttribute key="org.eclipse.jdt.launching.MODULEPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11&quot; path=&quot;4&quot; type=&quot;4&quot;/&gt;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.pcodeCPort.slgh_compile.SleighCompileLauncher -i &quot;${project_loc}/build/data/sleighArgs.txt&quot; -a &quot;${project_loc}/data/languages&quot;"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework Utility"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djava.awt.headless=true -Xmx1048M -XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -Xdock:name=&quot;Sleigh&quot; -Dvisualvm.display.name=Sleigh"/>
</launchConfiguration>

View File

@ -23,6 +23,10 @@ dependencies {
antlr "org.antlr:antlr:3.5.2"
}
// Define classpath needed to execute sleigh compiler during development
// (see gradleScripts/processorUtils.gradle)
ext.sleighDevClasspath = project.sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':')
def genSrcDir = 'generated-src/antlr/main'
generateGrammarSource {

View File

@ -1473,8 +1473,10 @@ expr_apply returns [Object value]
;
expr_operands returns [VectorSTL<ExprTree> value]
scope Return;
@init {
$value = new VectorSTL<ExprTree>();
$Return::noReturn = false;
}
: (e=expr { value.push_back(e); })*
;

View File

@ -412,11 +412,39 @@ public class SleighLanguage implements Language {
String languageName = specName + ".slaspec";
ResourceFile languageFile = new ResourceFile(slaFile.getParentFile(), languageName);
// see gradleScripts/processorUtils.gradle for sleighArgs.txt generation
ResourceFile sleighArgsFile = null;
ResourceFile languageModule = Application.getModuleContainingResourceFile(languageFile);
if (languageModule != null) {
if (SystemUtilities.isInReleaseMode()) {
sleighArgsFile = new ResourceFile(languageModule, "data/sleighArgs.txt");
}
else {
sleighArgsFile = new ResourceFile(languageModule, "build/data/sleighArgs.txt");
}
}
Map<String, String> defineMap;
String[] args;
if (sleighArgsFile != null && sleighArgsFile.isFile()) {
args = new String[] { "-i", sleighArgsFile.getAbsolutePath(),
languageFile.getAbsolutePath(), description.getSlaFile().getAbsolutePath() };
defineMap = new HashMap<>();
}
else {
args = new String[] { languageFile.getAbsolutePath(),
description.getSlaFile().getAbsolutePath() };
defineMap = ModuleDefinitionsMap.getModuleMap();
}
try {
int returnCode = SleighCompileLauncher.runMain(
new String[] { languageFile.getAbsolutePath(),
description.getSlaFile().getAbsolutePath() },
ModuleDefinitionsMap.getModuleMap());
StringBuilder buf = new StringBuilder();
for (String str : args) {
buf.append(str);
buf.append(" ");
}
Msg.debug(this, "Sleigh compile: " + buf);
int returnCode = SleighCompileLauncher.runMain(args, defineMap);
if (returnCode != 0) {
throw new SleighException("Errors compiling " + languageFile.getAbsolutePath() +
" -- please check log messages for details");

Some files were not shown because too many files have changed in this diff Show More