mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
parent
3ffbf09e52
commit
8216440278
11 changed files with 311 additions and 526 deletions
|
@ -13,56 +13,51 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
// List function names and entry point addresses to a file
|
// List function names and entry point addresses to a file in JSON format
|
||||||
//@category Functions
|
//@category Functions
|
||||||
|
|
||||||
import ghidra.app.plugin.core.script.Ingredient;
|
import java.io.File;
|
||||||
import ghidra.app.plugin.core.script.IngredientDescription;
|
import java.io.FileWriter;
|
||||||
import ghidra.app.script.GatherParamPanel;
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
|
|
||||||
import java.io.*;
|
public class ExportFunctionInfoScript extends GhidraScript {
|
||||||
|
|
||||||
public class ExportFunctionInfoScript extends GhidraScript implements Ingredient {
|
private static final String NAME = "name";
|
||||||
|
private static final String ENTRY = "entry";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
IngredientDescription[] ingredients = getIngredientDescriptions();
|
|
||||||
for (int i = 0; i < ingredients.length; i++) {
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
state.addParameter(ingredients[i].getID(), ingredients[i].getLabel(),
|
|
||||||
ingredients[i].getType(), ingredients[i].getDefaultValue());
|
File outputFile = askFile("Please Select Output File", "Choose");
|
||||||
}
|
JsonWriter jsonWriter = new JsonWriter(new FileWriter(outputFile));
|
||||||
if (!state.displayParameterGatherer("Script Options")) {
|
jsonWriter.beginArray();
|
||||||
return;
|
|
||||||
}
|
|
||||||
File outputNameFile = (File) state.getEnvironmentVar("FunctionNameOutputFile");
|
|
||||||
PrintWriter pWriter = new PrintWriter(new FileOutputStream(outputNameFile));
|
|
||||||
Listing listing = currentProgram.getListing();
|
Listing listing = currentProgram.getListing();
|
||||||
FunctionIterator iter = listing.getFunctions(true);
|
FunctionIterator iter = listing.getFunctions(true);
|
||||||
while (iter.hasNext() && !monitor.isCancelled()) {
|
while (iter.hasNext() && !monitor.isCancelled()) {
|
||||||
Function f = iter.next();
|
Function f = iter.next();
|
||||||
String fName = f.getName();
|
|
||||||
|
String name = f.getName();
|
||||||
Address entry = f.getEntryPoint();
|
Address entry = f.getEntryPoint();
|
||||||
if (entry == null) {
|
|
||||||
pWriter.println("/* FUNCTION_NAME_ " + fName + " FUNCTION_ADDR_ " +
|
JsonObject json = new JsonObject();
|
||||||
"NO_ENTRY_POINT" + " */");
|
json.addProperty(NAME, name);
|
||||||
println("WARNING: no entry point for " + fName);
|
json.addProperty(ENTRY, entry.toString());
|
||||||
}
|
|
||||||
else {
|
gson.toJson(json, jsonWriter);
|
||||||
pWriter.println("/* FUNCTION_NAME_ " + fName + " FUNCTION_ADDR_ " + entry + " */");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pWriter.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
jsonWriter.endArray();
|
||||||
public IngredientDescription[] getIngredientDescriptions() {
|
jsonWriter.close();
|
||||||
IngredientDescription[] retVal =
|
|
||||||
new IngredientDescription[] { new IngredientDescription("FunctionNameOutputFile",
|
|
||||||
"Output Function Name File", GatherParamPanel.FILE, "") };
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
println("Wrote functions to " + outputFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,153 +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.
|
|
||||||
*/
|
|
||||||
//searches for pre-defined patterns and free space in code images
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import ghidra.app.cmd.label.AddLabelCmd;
|
|
||||||
import ghidra.app.plugin.core.script.Ingredient;
|
|
||||||
import ghidra.app.plugin.core.script.IngredientDescription;
|
|
||||||
import ghidra.app.plugin.core.searchmem.RegExSearchData;
|
|
||||||
import ghidra.app.script.GatherParamPanel;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
|
||||||
import ghidra.program.model.address.*;
|
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
|
||||||
import ghidra.program.model.symbol.SourceType;
|
|
||||||
import ghidra.util.datastruct.ListAccumulator;
|
|
||||||
import ghidra.util.search.memory.*;
|
|
||||||
|
|
||||||
public class FindEmptySpaceScript extends GhidraScript implements Ingredient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() throws Exception {
|
|
||||||
IngredientDescription[] ingredients = getIngredientDescriptions();
|
|
||||||
for (IngredientDescription ingredient : ingredients) {
|
|
||||||
state.addParameter(ingredient.getID(), ingredient.getLabel(), ingredient.getType(),
|
|
||||||
ingredient.getDefaultValue());
|
|
||||||
}
|
|
||||||
if (!state.displayParameterGatherer("Empty Area Finder Options")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String emptyArea = (String) state.getEnvironmentVar("EmptyAreaData");
|
|
||||||
Integer threshold = (Integer) state.getEnvironmentVar("Threshold");
|
|
||||||
Integer align = (Integer) state.getEnvironmentVar("Alignment");
|
|
||||||
String stem = (String) state.getEnvironmentVar("NameStem");
|
|
||||||
|
|
||||||
findEmptyAreas(emptyArea, threshold, align, stem);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void findEmptyAreas(String emptyArea, Integer threshold, Integer align, String stem)
|
|
||||||
throws Exception {
|
|
||||||
String emptyAreaPlusThreshold = emptyArea + "{" + threshold + ",}";
|
|
||||||
if (align < currentProgram.getLanguage().getInstructionAlignment()) {
|
|
||||||
align = currentProgram.getLanguage().getInstructionAlignment();
|
|
||||||
println(
|
|
||||||
" Adjusting alignment to minimum instruction alignment for this processor; new alignment is " +
|
|
||||||
align + " bytes");
|
|
||||||
}
|
|
||||||
else if ((align % currentProgram.getLanguage().getInstructionAlignment()) != 0) {
|
|
||||||
align = align + (align % currentProgram.getLanguage().getInstructionAlignment());
|
|
||||||
println(
|
|
||||||
" Adjusting alignment to match processor instruction alignment; new alignment is " +
|
|
||||||
align + " bytes");
|
|
||||||
}
|
|
||||||
|
|
||||||
println(" Searching initialized memory for " + emptyAreaPlusThreshold +
|
|
||||||
"; minimum size = " + threshold + " bytes ; alignment = " + align +
|
|
||||||
" bytes; search limited to first 1000 matches");
|
|
||||||
|
|
||||||
AddressSetView addrs = currentProgram.getMemory().getLoadedAndInitializedAddressSet();
|
|
||||||
|
|
||||||
SearchInfo searchInfo = new SearchInfo(new RegExSearchData(emptyAreaPlusThreshold), 1000,
|
|
||||||
false, true, align, true, null);
|
|
||||||
RegExMemSearcherAlgorithm searcher =
|
|
||||||
new RegExMemSearcherAlgorithm(searchInfo, addrs, currentProgram, true);
|
|
||||||
|
|
||||||
ListAccumulator<MemSearchResult> accumulator = new ListAccumulator<>();
|
|
||||||
searcher.search(accumulator, monitor);
|
|
||||||
List<MemSearchResult> results = accumulator.asList();
|
|
||||||
List<Address> addresses =
|
|
||||||
results.stream().map(r -> r.getAddress()).collect(Collectors.toList());
|
|
||||||
|
|
||||||
int numMatches = 0;
|
|
||||||
long maxLen = 0;
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
println(" FAILURE: Could not find any empty areas with regexp = " +
|
|
||||||
emptyAreaPlusThreshold + "and alignment = " + align + " bytes");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//put matches into an address set, thereby coalescing ranges
|
|
||||||
AddressSet addrSet = new AddressSet();
|
|
||||||
for (MemSearchResult result : results) {
|
|
||||||
Address match = result.getAddress();
|
|
||||||
int len = result.getLength();
|
|
||||||
addrSet.addRange(match, match.addNoWrap(len));
|
|
||||||
}
|
|
||||||
|
|
||||||
//iterate over the set items that matched
|
|
||||||
for (AddressRange range : addrSet) {
|
|
||||||
long len = range.getLength();
|
|
||||||
addLabelAndExportSym(range.getMinAddress(), len, stem, "emptyArea", "size = " + len +
|
|
||||||
" bytes (alignment = " + align + " bytes; min size = " + threshold + " bytes)");
|
|
||||||
numMatches++;
|
|
||||||
if (len > maxLen) {
|
|
||||||
maxLen = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println(" Found " + numMatches +
|
|
||||||
" empty areas meeting size and alignment requirements; maximum length found = " +
|
|
||||||
maxLen + " bytes");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addLabelAndExportSym(Address matchAddr, long len, String stem, String tag,
|
|
||||||
String optComment) {
|
|
||||||
String label = stem + "_" + matchAddr + "_" + len;
|
|
||||||
label = label.replaceAll(":", "_");
|
|
||||||
String comment = "{@exportsym " + tag + " " + optComment + "}";
|
|
||||||
CodeUnit cd = currentProgram.getListing().getCodeUnitAt(matchAddr);
|
|
||||||
if (cd == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AddLabelCmd lcmd = new AddLabelCmd(matchAddr, label, false, SourceType.USER_DEFINED);
|
|
||||||
lcmd.applyTo(currentProgram);
|
|
||||||
String commentThere = cd.getComment(CodeUnit.EOL_COMMENT);
|
|
||||||
if (commentThere != null) {
|
|
||||||
comment = commentThere + "\n" + comment;
|
|
||||||
}
|
|
||||||
cd.setComment(CodeUnit.EOL_COMMENT, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IngredientDescription[] getIngredientDescriptions() {
|
|
||||||
|
|
||||||
IngredientDescription[] retVal = new IngredientDescription[] {
|
|
||||||
new IngredientDescription("EmptyAreaData", "Regular Expression Data Pattern",
|
|
||||||
GatherParamPanel.STRING, "\\xff"),
|
|
||||||
new IngredientDescription("Threshold", "Minimum Size (decimal bytes)",
|
|
||||||
GatherParamPanel.INTEGER, ""),
|
|
||||||
new IngredientDescription("Alignment", "Alignment (decimal bytes)",
|
|
||||||
GatherParamPanel.INTEGER, ""),
|
|
||||||
new IngredientDescription("NameStem", "Optional Label Stem", GatherParamPanel.STRING,
|
|
||||||
"EMPTY") };
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,97 +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.
|
|
||||||
*/
|
|
||||||
//Places header structure on overlay segments
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.core.script.Ingredient;
|
|
||||||
import ghidra.app.plugin.core.script.IngredientDescription;
|
|
||||||
import ghidra.app.script.GatherParamPanel;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
|
||||||
import ghidra.program.model.address.*;
|
|
||||||
import ghidra.program.model.data.DataType;
|
|
||||||
import ghidra.program.model.data.FileDataTypeManager;
|
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
|
||||||
|
|
||||||
public class OverlayHeadersScript extends GhidraScript implements Ingredient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() throws Exception {
|
|
||||||
// Get our configuration info and save for other scripts to use
|
|
||||||
IngredientDescription[] ingredients = getIngredientDescriptions();
|
|
||||||
for (IngredientDescription ingredient : ingredients) {
|
|
||||||
state.addParameter(ingredient.getID(), ingredient.getLabel(), ingredient.getType(),
|
|
||||||
ingredient.getDefaultValue());
|
|
||||||
}
|
|
||||||
if (!state.displayParameterGatherer("Script Options")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get our parameters for use here
|
|
||||||
String overlayName = (String) state.getEnvironmentVar("OverlayName");
|
|
||||||
File dataTypeArchive = (File) state.getEnvironmentVar("OverlayHeaderArchive");
|
|
||||||
String dataTypeName = (String) state.getEnvironmentVar("OverlayHeaderName"); // must include datatype category
|
|
||||||
|
|
||||||
// Create our history logger
|
|
||||||
Address histAddr = currentProgram.getMemory().getMinAddress();
|
|
||||||
String tmpString = "\nScript: OverlayHeaders()\n";
|
|
||||||
tmpString = tmpString + " Add " + dataTypeName + " structure\n from " +
|
|
||||||
dataTypeArchive.toString();
|
|
||||||
|
|
||||||
// Get the datatype that we want to place on the overlays
|
|
||||||
FileDataTypeManager dataTypeFileManager = openDataTypeArchive(dataTypeArchive, true);
|
|
||||||
DataType dataType = dataTypeFileManager.getDataType(dataTypeName);
|
|
||||||
dataTypeFileManager.close();
|
|
||||||
if (dataType == null) {
|
|
||||||
println("Can't find data type " + dataTypeName + " in " + dataTypeArchive.toString());
|
|
||||||
throw new Exception(
|
|
||||||
"Can't find data type " + dataTypeName + "\n in " + dataTypeArchive.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now iterate over overlays the lay down structure
|
|
||||||
AddressSetView searchSet = currentProgram.getMemory();
|
|
||||||
AddressRangeIterator addressRanges = searchSet.getAddressRanges(true);
|
|
||||||
monitor.initialize(searchSet.getNumAddresses());
|
|
||||||
int progressCount = 0;
|
|
||||||
while (addressRanges.hasNext() && !monitor.isCancelled()) {
|
|
||||||
AddressRange range = addressRanges.next();
|
|
||||||
Address startAddr = range.getMinAddress();
|
|
||||||
String rangeName = startAddr.toString();
|
|
||||||
if (rangeName.startsWith(overlayName)) {
|
|
||||||
try {
|
|
||||||
createData(startAddr, dataType);
|
|
||||||
}
|
|
||||||
catch (CodeUnitInsertionException ex) {
|
|
||||||
println("Error creating data type: " + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progressCount += range.getLength();
|
|
||||||
monitor.setProgress(progressCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IngredientDescription[] getIngredientDescriptions() {
|
|
||||||
IngredientDescription[] retVal = new IngredientDescription[] {
|
|
||||||
new IngredientDescription("OverlayName", "Overlay Name", GatherParamPanel.STRING, "ov"),
|
|
||||||
new IngredientDescription("OverlayHeaderArchive", "Overlay Header Archive",
|
|
||||||
GatherParamPanel.FILE, ""),
|
|
||||||
new IngredientDescription("OverlayHeaderName", "Overlay Header Name",
|
|
||||||
GatherParamPanel.STRING, "/overlay_header") };
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +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.plugin.core.script;
|
|
||||||
|
|
||||||
public interface Ingredient {
|
|
||||||
IngredientDescription [] getIngredientDescriptions();
|
|
||||||
}
|
|
|
@ -1,54 +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.plugin.core.script;
|
|
||||||
|
|
||||||
public class IngredientDescription {
|
|
||||||
private boolean visited;
|
|
||||||
private String id;
|
|
||||||
private String label;
|
|
||||||
private int type;
|
|
||||||
private Object defaultValue;
|
|
||||||
|
|
||||||
public IngredientDescription(String id, String label, int type, Object defaultValue) {
|
|
||||||
this.id = id;
|
|
||||||
this.label = label;
|
|
||||||
this.type = type;
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
visited = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean wasVisited() {
|
|
||||||
return visited;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLabel() {
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getID() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getDefaultValue() {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/* ###
|
||||||
|
* 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.plugin.core.script;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
import generic.json.Json;
|
||||||
|
import ghidra.framework.Application;
|
||||||
|
import ghidra.program.model.listing.Function;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.test.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@code ExportFunctionInfoScript}, which writes Ghidra function object info in JSON
|
||||||
|
* form for the entire program
|
||||||
|
*/
|
||||||
|
public class ExportFunctionInfoScriptTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
|
private TestEnv env;
|
||||||
|
private File script;
|
||||||
|
|
||||||
|
private Program program;
|
||||||
|
private Function f1;
|
||||||
|
private Function f2;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
|
||||||
|
program = buildProgram();
|
||||||
|
|
||||||
|
env = new TestEnv();
|
||||||
|
env.launchDefaultTool(program);
|
||||||
|
|
||||||
|
String scriptPath = "ghidra_scripts/ExportFunctionInfoScript.java";
|
||||||
|
script = Application.getModuleFile("Base", scriptPath).getFile(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Program buildProgram() throws Exception {
|
||||||
|
ToyProgramBuilder builder = new ToyProgramBuilder("Test", true, this);
|
||||||
|
builder.createMemory(".text", "0x1001000", 0x40);
|
||||||
|
|
||||||
|
f1 = builder.createFunction("0x1001000");
|
||||||
|
f2 = builder.createFunction("0x1001020");
|
||||||
|
|
||||||
|
return builder.getProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScript() throws Exception {
|
||||||
|
|
||||||
|
File outputFile = createTempFileForTest();
|
||||||
|
|
||||||
|
ScriptTaskListener listener = env.runScript(script);
|
||||||
|
|
||||||
|
chooseFile(outputFile);
|
||||||
|
|
||||||
|
waitForScriptCompletion(listener, 20000);
|
||||||
|
|
||||||
|
assertFunctionsInFile(outputFile, f1, f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFunctionsInFile(File file, Function... functions)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
List<Function> testFunctions = new ArrayList<>(List.of(f1, f2));
|
||||||
|
|
||||||
|
List<TestJsonFunction> jsons = readFromJson(file);
|
||||||
|
|
||||||
|
jsons.forEach(jsonFunction -> assertFunction(jsonFunction, testFunctions));
|
||||||
|
assertThat("Not all program functions written to json file",
|
||||||
|
testFunctions, is(empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TestJsonFunction> readFromJson(File file) throws Exception {
|
||||||
|
|
||||||
|
List<TestJsonFunction> results = new ArrayList<>();
|
||||||
|
Gson gson = new Gson();
|
||||||
|
BufferedReader br = new BufferedReader(new FileReader(file));
|
||||||
|
JsonReader reader = new JsonReader(br);
|
||||||
|
|
||||||
|
// the file is an array of objects
|
||||||
|
reader.beginArray();
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
TestJsonFunction function = gson.fromJson(reader, TestJsonFunction.class);
|
||||||
|
results.add(function);
|
||||||
|
}
|
||||||
|
reader.endArray();
|
||||||
|
reader.close();
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFunction(TestJsonFunction function, List<Function> testFunctions) {
|
||||||
|
Function match = null;
|
||||||
|
for (Function expected : testFunctions) {
|
||||||
|
if (function.matches(expected)) {
|
||||||
|
match = expected;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull("Unexpected function written to file", match);
|
||||||
|
testFunctions.remove(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void chooseFile(File file) throws Exception {
|
||||||
|
|
||||||
|
GhidraFileChooser chooser = waitForDialogComponent(GhidraFileChooser.class);
|
||||||
|
runSwing(() -> chooser.setSelectedFile(file));
|
||||||
|
waitForUpdateOnChooser(chooser);
|
||||||
|
pressButtonByText(chooser.getComponent(), "Choose");
|
||||||
|
waitForSwing();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestJsonFunction {
|
||||||
|
private String name;
|
||||||
|
private String entry;
|
||||||
|
|
||||||
|
boolean matches(Function expected) {
|
||||||
|
return name.equals(expected.getName()) &&
|
||||||
|
entry.equals(expected.getEntryPoint().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
// this is only for debug; not required
|
||||||
|
return Json.toString(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,57 +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.
|
|
||||||
*/
|
|
||||||
//Decompile an entire program
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.app.plugin.core.script.Ingredient;
|
|
||||||
import ghidra.app.plugin.core.script.IngredientDescription;
|
|
||||||
import ghidra.app.script.GatherParamPanel;
|
|
||||||
import ghidra.app.script.GhidraScript;
|
|
||||||
import ghidra.app.util.Option;
|
|
||||||
import ghidra.app.util.exporter.CppExporter;
|
|
||||||
|
|
||||||
public class Decompile extends GhidraScript implements Ingredient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() throws Exception {
|
|
||||||
IngredientDescription[] ingredients = getIngredientDescriptions();
|
|
||||||
for (IngredientDescription ingredient : ingredients) {
|
|
||||||
state.addParameter(ingredient.getID(), ingredient.getLabel(), ingredient.getType(),
|
|
||||||
ingredient.getDefaultValue());
|
|
||||||
}
|
|
||||||
if (!state.displayParameterGatherer("Script Options")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File outputFile = (File) state.getEnvironmentVar("COutputFile");
|
|
||||||
CppExporter cppExporter = new CppExporter();
|
|
||||||
List<Option> options = new ArrayList<Option>();
|
|
||||||
options.add(new Option(CppExporter.CREATE_HEADER_FILE, new Boolean(false)));
|
|
||||||
cppExporter.setOptions(options);
|
|
||||||
cppExporter.setExporterServiceProvider(state.getTool());
|
|
||||||
cppExporter.export(outputFile, currentProgram, null, monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IngredientDescription[] getIngredientDescriptions() {
|
|
||||||
IngredientDescription[] retVal = new IngredientDescription[] {
|
|
||||||
new IngredientDescription("COutputFile", "Output C File", GatherParamPanel.FILE, "") };
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,8 +18,8 @@ dependencies {
|
||||||
compile "org.apache.commons:commons-collections4:4.1"
|
compile "org.apache.commons:commons-collections4:4.1"
|
||||||
compile "org.apache.commons:commons-lang3:3.9"
|
compile "org.apache.commons:commons-lang3:3.9"
|
||||||
compile "org.apache.commons:commons-text:1.6"
|
compile "org.apache.commons:commons-text:1.6"
|
||||||
|
|
||||||
compile "commons-io:commons-io:2.6"
|
compile "commons-io:commons-io:2.6"
|
||||||
|
compile "com.google.code.gson:gson:2.8.6"
|
||||||
|
|
||||||
compileOnly "junit:junit:4.12"
|
compileOnly "junit:junit:4.12"
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,6 @@ public class ExtensionUtils {
|
||||||
public static String PROPERTIES_FILE_NAME = "extension.properties";
|
public static String PROPERTIES_FILE_NAME = "extension.properties";
|
||||||
public static String PROPERTIES_FILE_NAME_UNINSTALLED = "extension.properties.uninstalled";
|
public static String PROPERTIES_FILE_NAME_UNINSTALLED = "extension.properties.uninstalled";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a set of all extensions known to Ghidra, represented by
|
* Returns a set of all extensions known to Ghidra, represented by
|
||||||
* {@link ExtensionDetails} objects. This will include all installed
|
* {@link ExtensionDetails} objects. This will include all installed
|
||||||
|
@ -76,7 +75,7 @@ public class ExtensionUtils {
|
||||||
* Note that this method will only look in the known extension folder locations:
|
* Note that this method will only look in the known extension folder locations:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ApplicationLayout#getExtensionArchiveDir}</li>
|
* <li>{@link ApplicationLayout#getExtensionArchiveDir}</li>
|
||||||
* <li>{@link ApplicationLayout#getExtensionInstallationDir}</li>
|
* <li>{@link ApplicationLayout#getExtensionInstallationDirs}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* If users install extensions from other locations, the installed version of
|
* If users install extensions from other locations, the installed version of
|
||||||
* the extension will be known, but the source archive location will not be retained.
|
* the extension will be known, but the source archive location will not be retained.
|
||||||
|
@ -123,13 +122,14 @@ public class ExtensionUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all installed extensions. These are all the extensions found in
|
* Returns all installed extensions. These are all the extensions found in
|
||||||
* {@link ApplicationLayout#getExtensionInstallationDir}.
|
* {@link ApplicationLayout#getExtensionInstallationDirs}.
|
||||||
*
|
*
|
||||||
* @param includeUninstalled if true, include extensions that have been marked for removal
|
* @param includeUninstalled if true, include extensions that have been marked for removal
|
||||||
* @return set of installed extensions
|
* @return set of installed extensions
|
||||||
* @throws ExtensionException if the extension details cannot be retrieved
|
* @throws ExtensionException if the extension details cannot be retrieved
|
||||||
*/
|
*/
|
||||||
public static Set<ExtensionDetails> getInstalledExtensions(boolean includeUninstalled) throws ExtensionException {
|
public static Set<ExtensionDetails> getInstalledExtensions(boolean includeUninstalled)
|
||||||
|
throws ExtensionException {
|
||||||
|
|
||||||
ApplicationLayout layout = Application.getApplicationLayout();
|
ApplicationLayout layout = Application.getApplicationLayout();
|
||||||
|
|
||||||
|
@ -216,7 +216,6 @@ public class ExtensionUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,8 +516,10 @@ public class ExtensionUtils {
|
||||||
List<ResourceFile> tempFiles = Arrays.asList(rfiles);
|
List<ResourceFile> tempFiles = Arrays.asList(rfiles);
|
||||||
|
|
||||||
Optional<ResourceFile> file =
|
Optional<ResourceFile> file =
|
||||||
tempFiles.stream().filter(f -> f.getName().equals(PROPERTIES_FILE_NAME) ||
|
tempFiles.stream()
|
||||||
f.getName().equals(PROPERTIES_FILE_NAME_UNINSTALLED)).findFirst();
|
.filter(f -> f.getName().equals(PROPERTIES_FILE_NAME) ||
|
||||||
|
f.getName().equals(PROPERTIES_FILE_NAME_UNINSTALLED))
|
||||||
|
.findFirst();
|
||||||
if (file.isPresent()) {
|
if (file.isPresent()) {
|
||||||
return file.get();
|
return file.get();
|
||||||
}
|
}
|
||||||
|
@ -570,7 +571,8 @@ public class ExtensionUtils {
|
||||||
// eg: If errorFile is "/Users/johnG/Ghidra/Extensions/MyExtensionName/subdir1/problemFile"
|
// eg: If errorFile is "/Users/johnG/Ghidra/Extensions/MyExtensionName/subdir1/problemFile"
|
||||||
// And installDir is "/Users/johnG/Ghidra/Extensions"
|
// And installDir is "/Users/johnG/Ghidra/Extensions"
|
||||||
// We need to get "MyExtensionName"
|
// We need to get "MyExtensionName"
|
||||||
String extPath = errorFile.getAbsolutePath().substring(installDir.getAbsolutePath().length()+1);
|
String extPath = errorFile.getAbsolutePath()
|
||||||
|
.substring(installDir.getAbsolutePath().length() + 1);
|
||||||
int slashIndex = extPath.indexOf(File.separator);
|
int slashIndex = extPath.indexOf(File.separator);
|
||||||
String extName;
|
String extName;
|
||||||
if (slashIndex == -1) {
|
if (slashIndex == -1) {
|
||||||
|
@ -580,7 +582,8 @@ public class ExtensionUtils {
|
||||||
extName = extPath.substring(0, extPath.indexOf(File.separator));
|
extName = extPath.substring(0, extPath.indexOf(File.separator));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean success = restoreStateFiles(new File(installDir.getAbsolutePath() + File.separator + extName));
|
boolean success = restoreStateFiles(
|
||||||
|
new File(installDir.getAbsolutePath() + File.separator + extName));
|
||||||
|
|
||||||
installed.set(success);
|
installed.set(success);
|
||||||
}
|
}
|
||||||
|
@ -627,10 +630,13 @@ public class ExtensionUtils {
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
|
|
||||||
List<File> manifestFiles = new ArrayList<>();
|
List<File> manifestFiles = new ArrayList<>();
|
||||||
ExtensionUtils.findFilesWithName(new File(extension.getInstallPath()), ModuleUtilities.MANIFEST_FILE_NAME, manifestFiles);
|
ExtensionUtils.findFilesWithName(new File(extension.getInstallPath()),
|
||||||
|
ModuleUtilities.MANIFEST_FILE_NAME, manifestFiles);
|
||||||
for (File f : manifestFiles) {
|
for (File f : manifestFiles) {
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
File newFile = new File(f.getAbsolutePath().replace(ModuleUtilities.MANIFEST_FILE_NAME, ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED) );
|
File newFile = new File(f.getAbsolutePath()
|
||||||
|
.replace(ModuleUtilities.MANIFEST_FILE_NAME,
|
||||||
|
ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED));
|
||||||
if (!f.renameTo(newFile)) {
|
if (!f.renameTo(newFile)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -638,10 +644,13 @@ public class ExtensionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<File> propFiles = new ArrayList<>();
|
List<File> propFiles = new ArrayList<>();
|
||||||
ExtensionUtils.findFilesWithName(new File(extension.getInstallPath()), ExtensionUtils.PROPERTIES_FILE_NAME, propFiles);
|
ExtensionUtils.findFilesWithName(new File(extension.getInstallPath()),
|
||||||
|
ExtensionUtils.PROPERTIES_FILE_NAME, propFiles);
|
||||||
for (File f : propFiles) {
|
for (File f : propFiles) {
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
File newFile = new File(f.getAbsolutePath().replace(ExtensionUtils.PROPERTIES_FILE_NAME, ExtensionUtils.PROPERTIES_FILE_NAME_UNINSTALLED) );
|
File newFile = new File(f.getAbsolutePath()
|
||||||
|
.replace(ExtensionUtils.PROPERTIES_FILE_NAME,
|
||||||
|
ExtensionUtils.PROPERTIES_FILE_NAME_UNINSTALLED));
|
||||||
if (!f.renameTo(newFile)) {
|
if (!f.renameTo(newFile)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -672,7 +681,9 @@ public class ExtensionUtils {
|
||||||
findFilesWithName(rootDir, ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED, manifestFiles);
|
findFilesWithName(rootDir, ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED, manifestFiles);
|
||||||
for (File f : manifestFiles) {
|
for (File f : manifestFiles) {
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
File newFile = new File(f.getAbsolutePath().replace(ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED, ModuleUtilities.MANIFEST_FILE_NAME) );
|
File newFile = new File(f.getAbsolutePath()
|
||||||
|
.replace(ModuleUtilities.MANIFEST_FILE_NAME_UNINSTALLED,
|
||||||
|
ModuleUtilities.MANIFEST_FILE_NAME));
|
||||||
if (!f.renameTo(newFile)) {
|
if (!f.renameTo(newFile)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -683,7 +694,8 @@ public class ExtensionUtils {
|
||||||
findFilesWithName(rootDir, PROPERTIES_FILE_NAME_UNINSTALLED, propFiles);
|
findFilesWithName(rootDir, PROPERTIES_FILE_NAME_UNINSTALLED, propFiles);
|
||||||
for (File f : propFiles) {
|
for (File f : propFiles) {
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
File newFile = new File(f.getAbsolutePath().replace(PROPERTIES_FILE_NAME_UNINSTALLED, PROPERTIES_FILE_NAME) );
|
File newFile = new File(f.getAbsolutePath()
|
||||||
|
.replace(PROPERTIES_FILE_NAME_UNINSTALLED, PROPERTIES_FILE_NAME));
|
||||||
if (!f.renameTo(newFile)) {
|
if (!f.renameTo(newFile)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
@ -774,11 +786,11 @@ public class ExtensionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpacks a given zip file to {@link ApplicationLayout#getExtensionInstallationDir}. The
|
* Unpacks a given zip file to {@link ApplicationLayout#getExtensionInstallationDirs}. The
|
||||||
* file permissions in the original zip will be retained.
|
* file permissions in the original zip will be retained.
|
||||||
* <p>
|
* <p>
|
||||||
* Note: This method uses the Apache zip files since they keep track of permissions info;
|
* Note: This method uses the Apache zip files since they keep track of permissions info;
|
||||||
* the built-in java objects (ZipEntry et al.) do not.
|
* the built-in java objects (e.g., ZipEntry) do not.
|
||||||
*
|
*
|
||||||
* @param zipFile the zip file to unpack
|
* @param zipFile the zip file to unpack
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor
|
||||||
|
|
|
@ -4,9 +4,7 @@ eclipse.project.name = '_JsonDoclet'
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile('com.googlecode.json-simple:json-simple:1.1.1') {
|
compile "com.google.code.gson:gson:2.8.6"
|
||||||
exclude group: 'junit', module: 'junit'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.createJsondocs.dependsOn jar
|
rootProject.createJsondocs.dependsOn jar
|
||||||
|
|
|
@ -24,9 +24,7 @@ import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.ElementFilter;
|
import javax.lang.model.util.ElementFilter;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
import org.json.simple.JSONArray;
|
import com.google.gson.*;
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import com.sun.source.doctree.*;
|
import com.sun.source.doctree.*;
|
||||||
import com.sun.source.util.DocTrees;
|
import com.sun.source.util.DocTrees;
|
||||||
|
|
||||||
|
@ -35,8 +33,9 @@ import jdk.javadoc.doclet.*;
|
||||||
/**
|
/**
|
||||||
* Doclet that outputs javadoc in JSON format (instead of HTML). Things like Python can then
|
* Doclet that outputs javadoc in JSON format (instead of HTML). Things like Python can then
|
||||||
* read in the JSON and easily access all of the javadoc elements.
|
* read in the JSON and easily access all of the javadoc elements.
|
||||||
|
*
|
||||||
|
* To run: gradle zipJavadocs
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class JsonDoclet implements Doclet {
|
public class JsonDoclet implements Doclet {
|
||||||
|
|
||||||
private Reporter log;
|
private Reporter log;
|
||||||
|
@ -45,6 +44,11 @@ public class JsonDoclet implements Doclet {
|
||||||
private DocletEnvironment docEnv;
|
private DocletEnvironment docEnv;
|
||||||
private DocTrees docTrees;
|
private DocTrees docTrees;
|
||||||
|
|
||||||
|
private Gson gson = new GsonBuilder()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.serializeNulls()
|
||||||
|
.create();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Locale locale, Reporter reporter) {
|
public void init(Locale locale, Reporter reporter) {
|
||||||
this.log = reporter;
|
this.log = reporter;
|
||||||
|
@ -128,13 +132,13 @@ public class JsonDoclet implements Doclet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a class {@link TypeElement} to a {@link JSONObject}.
|
* Converts a class {@link TypeElement} to a {@link JsonObject}.
|
||||||
*
|
*
|
||||||
* @param classElement the class {@link TypeElement} to convert
|
* @param classElement the class {@link TypeElement} to convert
|
||||||
* @return A json object that represents the class.
|
* @return A json object that represents the class.
|
||||||
*/
|
*/
|
||||||
private JSONObject classToJson(TypeElement classElement) {
|
private JsonObject classToJson(TypeElement classElement) {
|
||||||
JSONObject classObj = new JSONObject();
|
JsonObject classObj = new JsonObject();
|
||||||
processClassAttributes(classElement, classObj);
|
processClassAttributes(classElement, classObj);
|
||||||
processFieldAndMethodAttributes(classElement, classObj);
|
processFieldAndMethodAttributes(classElement, classObj);
|
||||||
return classObj;
|
return classObj;
|
||||||
|
@ -146,11 +150,11 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param classElement the class element to parse
|
* @param classElement the class element to parse
|
||||||
* @param classObj the json object to populate
|
* @param classObj the json object to populate
|
||||||
*/
|
*/
|
||||||
private void processClassAttributes(TypeElement classElement, JSONObject classObj) {
|
private void processClassAttributes(TypeElement classElement, JsonObject classObj) {
|
||||||
classObj.put("name", classElement.getSimpleName().toString());
|
classObj.addProperty("name", classElement.getSimpleName().toString());
|
||||||
classObj.put("comment", getComment(docTrees.getDocCommentTree(classElement)));
|
classObj.addProperty("comment", getComment(docTrees.getDocCommentTree(classElement)));
|
||||||
classObj.put("javadoc", getJavadoc(docTrees.getDocCommentTree(classElement)));
|
classObj.addProperty("javadoc", getJavadoc(docTrees.getDocCommentTree(classElement)));
|
||||||
classObj.put("static", classElement.getModifiers().contains(Modifier.STATIC));
|
classObj.addProperty("static", classElement.getModifiers().contains(Modifier.STATIC));
|
||||||
addInterfaces(classElement, classObj);
|
addInterfaces(classElement, classObj);
|
||||||
addSuperClass(classElement, classObj);
|
addSuperClass(classElement, classObj);
|
||||||
}
|
}
|
||||||
|
@ -162,8 +166,8 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param typeElement the {@link TypeElement} to parse
|
* @param typeElement the {@link TypeElement} to parse
|
||||||
* @param obj the json object to populate
|
* @param obj the json object to populate
|
||||||
*/
|
*/
|
||||||
private void addInterfaces(TypeElement typeElement, JSONObject obj) {
|
private void addInterfaces(TypeElement typeElement, JsonObject obj) {
|
||||||
JSONArray interfaceArray = new JSONArray();
|
JsonArray interfaceArray = new JsonArray();
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
typeElement.getInterfaces()
|
typeElement.getInterfaces()
|
||||||
|
@ -176,7 +180,7 @@ public class JsonDoclet implements Doclet {
|
||||||
.forEach(ifaceTypeElement -> interfaceArray.add(ifaceTypeElement.getQualifiedName().toString()));
|
.forEach(ifaceTypeElement -> interfaceArray.add(ifaceTypeElement.getQualifiedName().toString()));
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
obj.put("implements", interfaceArray);
|
obj.add("implements", interfaceArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,12 +190,12 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param typeElement the {@link TypeElement} to parse
|
* @param typeElement the {@link TypeElement} to parse
|
||||||
* @param obj the json object to populate
|
* @param obj the json object to populate
|
||||||
*/
|
*/
|
||||||
private void addSuperClass(TypeElement typeElement, JSONObject obj) {
|
private void addSuperClass(TypeElement typeElement, JsonObject obj) {
|
||||||
if (typeElement.getSuperclass() instanceof DeclaredType) {
|
if (typeElement.getSuperclass() instanceof DeclaredType) {
|
||||||
DeclaredType declaredType = (DeclaredType) typeElement.getSuperclass();
|
DeclaredType declaredType = (DeclaredType) typeElement.getSuperclass();
|
||||||
if (declaredType.asElement() instanceof TypeElement) {
|
if (declaredType.asElement() instanceof TypeElement) {
|
||||||
TypeElement typeEl = (TypeElement) declaredType.asElement();
|
TypeElement typeEl = (TypeElement) declaredType.asElement();
|
||||||
obj.put("extends", typeEl.getQualifiedName().toString());
|
obj.addProperty("extends", typeEl.getQualifiedName().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,29 +206,29 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param classElement the class to parse
|
* @param classElement the class to parse
|
||||||
* @param classObj the json object to populate
|
* @param classObj the json object to populate
|
||||||
*/
|
*/
|
||||||
private void processFieldAndMethodAttributes(TypeElement classElement, JSONObject classObj) {
|
private void processFieldAndMethodAttributes(TypeElement classElement, JsonObject classObj) {
|
||||||
|
|
||||||
JSONArray fieldArray = new JSONArray();
|
JsonArray fieldArray = new JsonArray();
|
||||||
JSONArray methodArray = new JSONArray();
|
JsonArray methodArray = new JsonArray();
|
||||||
|
|
||||||
for (Element el : classElement.getEnclosedElements()) {
|
for (Element el : classElement.getEnclosedElements()) {
|
||||||
|
|
||||||
JSONObject obj = new JSONObject();
|
JsonObject obj = new JsonObject();
|
||||||
obj.put("name", el.getSimpleName().toString());
|
obj.addProperty("name", el.getSimpleName().toString());
|
||||||
obj.put("comment", getComment(docTrees.getDocCommentTree(el)));
|
obj.addProperty("comment", getComment(docTrees.getDocCommentTree(el)));
|
||||||
obj.put("javadoc", getJavadoc(docTrees.getDocCommentTree(el)));
|
obj.addProperty("javadoc", getJavadoc(docTrees.getDocCommentTree(el)));
|
||||||
obj.put("static", el.getModifiers().contains(Modifier.STATIC));
|
obj.addProperty("static", el.getModifiers().contains(Modifier.STATIC));
|
||||||
|
|
||||||
switch (el.getKind()) {
|
switch (el.getKind()) {
|
||||||
case FIELD:
|
case FIELD:
|
||||||
VariableElement varElement = (VariableElement) el;
|
VariableElement varElement = (VariableElement) el;
|
||||||
obj.put("type_long", getTypeLong(el.asType()));
|
obj.addProperty("type_long", getTypeLong(el.asType()));
|
||||||
obj.put("type_short", getTypeShort(el.asType()));
|
obj.addProperty("type_short", getTypeShort(el.asType()));
|
||||||
Object constantValue = varElement.getConstantValue();
|
Object constantValue = varElement.getConstantValue();
|
||||||
if (constantValue instanceof String) {
|
if (constantValue instanceof String) {
|
||||||
constantValue = "\"" + constantValue + "\"";
|
constantValue = "\"" + constantValue + "\"";
|
||||||
}
|
}
|
||||||
obj.put("constant_value", Objects.toString(constantValue, null)); // only applies to 'final'
|
obj.addProperty("constant_value", Objects.toString(constantValue, null)); // only applies to 'final'
|
||||||
fieldArray.add(obj);
|
fieldArray.add(obj);
|
||||||
break;
|
break;
|
||||||
case CONSTRUCTOR:
|
case CONSTRUCTOR:
|
||||||
|
@ -255,8 +259,8 @@ public class JsonDoclet implements Doclet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classObj.put("fields", fieldArray);
|
classObj.add("fields", fieldArray);
|
||||||
classObj.put("methods", methodArray);
|
classObj.add("methods", methodArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,14 +270,14 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param execElement the element to parse
|
* @param execElement the element to parse
|
||||||
* @param obj the json object
|
* @param obj the json object
|
||||||
*/
|
*/
|
||||||
private void addParams(ExecutableElement execElement, JSONObject obj) {
|
private void addParams(ExecutableElement execElement, JsonObject obj) {
|
||||||
|
|
||||||
JSONArray paramsArray = new JSONArray();
|
JsonArray paramsArray = new JsonArray();
|
||||||
for (VariableElement varElement : execElement.getParameters()) {
|
for (VariableElement varElement : execElement.getParameters()) {
|
||||||
JSONObject paramObj = new JSONObject();
|
JsonObject paramObj = new JsonObject();
|
||||||
paramObj.put("name", varElement.getSimpleName().toString());
|
paramObj.addProperty("name", varElement.getSimpleName().toString());
|
||||||
paramObj.put("type_long", getTypeLong(varElement.asType()));
|
paramObj.addProperty("type_long", getTypeLong(varElement.asType()));
|
||||||
paramObj.put("type_short", getTypeShort(varElement.asType()));
|
paramObj.addProperty("type_short", getTypeShort(varElement.asType()));
|
||||||
String comment = "";
|
String comment = "";
|
||||||
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
||||||
if (commentTree != null) {
|
if (commentTree != null) {
|
||||||
|
@ -287,10 +291,10 @@ public class JsonDoclet implements Doclet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paramObj.put("comment", comment);
|
paramObj.addProperty("comment", comment);
|
||||||
paramsArray.add(paramObj);
|
paramsArray.add(paramObj);
|
||||||
}
|
}
|
||||||
obj.put("params", paramsArray);
|
obj.add("params", paramsArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -300,11 +304,11 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param execElement the element to parse
|
* @param execElement the element to parse
|
||||||
* @param obj the json object
|
* @param obj the json object
|
||||||
*/
|
*/
|
||||||
private void addReturn(ExecutableElement execElement, JSONObject obj) {
|
private void addReturn(ExecutableElement execElement, JsonObject obj) {
|
||||||
TypeMirror returnType = execElement.getReturnType();
|
TypeMirror returnType = execElement.getReturnType();
|
||||||
JSONObject returnObj = new JSONObject();
|
JsonObject returnObj = new JsonObject();
|
||||||
returnObj.put("type_long", getTypeLong(returnType));
|
returnObj.addProperty("type_long", getTypeLong(returnType));
|
||||||
returnObj.put("type_short", getTypeShort(returnType));
|
returnObj.addProperty("type_short", getTypeShort(returnType));
|
||||||
String comment = "";
|
String comment = "";
|
||||||
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
||||||
if (commentTree != null) {
|
if (commentTree != null) {
|
||||||
|
@ -314,8 +318,8 @@ public class JsonDoclet implements Doclet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
returnObj.put("comment", comment);
|
returnObj.addProperty("comment", comment);
|
||||||
obj.put("return", returnObj);
|
obj.add("return", returnObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -325,14 +329,14 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param execElement the element to parse
|
* @param execElement the element to parse
|
||||||
* @param obj the json object
|
* @param obj the json object
|
||||||
*/
|
*/
|
||||||
private void addExceptions(ExecutableElement execElement, JSONObject obj) {
|
private void addExceptions(ExecutableElement execElement, JsonObject obj) {
|
||||||
JSONArray throwsArray = new JSONArray();
|
JsonArray throwsArray = new JsonArray();
|
||||||
for (TypeMirror thrownType : execElement.getThrownTypes()) {
|
for (TypeMirror thrownType : execElement.getThrownTypes()) {
|
||||||
JSONObject throwObj = new JSONObject();
|
JsonObject throwObj = new JsonObject();
|
||||||
String typeLong = getTypeLong(thrownType);
|
String typeLong = getTypeLong(thrownType);
|
||||||
String typeShort = getTypeShort(thrownType);
|
String typeShort = getTypeShort(thrownType);
|
||||||
throwObj.put("type_long", typeLong);
|
throwObj.addProperty("type_long", typeLong);
|
||||||
throwObj.put("type_short", typeShort);
|
throwObj.addProperty("type_short", typeShort);
|
||||||
String comment = "";
|
String comment = "";
|
||||||
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
DocCommentTree commentTree = docTrees.getDocCommentTree(execElement);
|
||||||
if (commentTree != null) {
|
if (commentTree != null) {
|
||||||
|
@ -346,10 +350,10 @@ public class JsonDoclet implements Doclet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throwObj.put("comment", comment);
|
throwObj.addProperty("comment", comment);
|
||||||
throwsArray.add(throwObj);
|
throwsArray.add(throwObj);
|
||||||
}
|
}
|
||||||
obj.put("throws", throwsArray);
|
obj.add("throws", throwsArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -446,11 +450,12 @@ public class JsonDoclet implements Doclet {
|
||||||
* @param qualifiedName The qualified class name. This name will get converted into a directory
|
* @param qualifiedName The qualified class name. This name will get converted into a directory
|
||||||
* structure.
|
* structure.
|
||||||
*/
|
*/
|
||||||
private void writeJsonToFile(JSONObject json, Name qualifiedName) {
|
private void writeJsonToFile(JsonObject json, Name qualifiedName) {
|
||||||
|
|
||||||
File jsonFile = new File(destDir, qualifiedName.toString().replace('.', '/') + ".json");
|
File jsonFile = new File(destDir, qualifiedName.toString().replace('.', '/') + ".json");
|
||||||
jsonFile.getParentFile().mkdirs();
|
jsonFile.getParentFile().mkdirs();
|
||||||
try (PrintWriter writer = new PrintWriter(new FileWriter(jsonFile))) {
|
try (PrintWriter writer = new PrintWriter(new FileWriter(jsonFile))) {
|
||||||
writer.println(json.toJSONString());
|
writer.println(gson.toJson(json));
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue