mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GT-3571 Numerous changes to datatype resolve/replace addressing
performance and conflict handling. Corrected composite merge deficiencies. Added datatype resolve/equivalence caches for performance improvement. Added deferred pointer resolution for structures and unions. Corrected datatype parent/child update
This commit is contained in:
parent
d5cc72fd14
commit
c2d9629f57
113 changed files with 3515 additions and 1853 deletions
|
@ -32,8 +32,7 @@ import ghidra.framework.plugintool.ModalPluginTool;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginException;
|
import ghidra.framework.plugintool.util.PluginException;
|
||||||
import ghidra.program.model.listing.DomainObjectChangeSet;
|
import ghidra.program.model.listing.DomainObjectChangeSet;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.*;
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
|
||||||
|
@ -460,6 +459,16 @@ public abstract class MergeManager implements DomainObjectMergeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display error message dialog in a blocking fashion.
|
||||||
|
* @param originator message originator
|
||||||
|
* @param title dialog title
|
||||||
|
* @param msg dialog message
|
||||||
|
*/
|
||||||
|
public static void displayErrorAndWait(Object originator, String title, String msg) {
|
||||||
|
Swing.runNow(() -> Msg.showError(originator, null, title, msg));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block until the user completes the current merge operation, or
|
* Block until the user completes the current merge operation, or
|
||||||
* cancels the merge process.
|
* cancels the merge process.
|
||||||
|
|
|
@ -22,9 +22,7 @@ import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ghidra.app.merge.MergeConstants;
|
import ghidra.app.merge.*;
|
||||||
import ghidra.app.merge.MergeResolver;
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.framework.data.DomainObjectMergeManager;
|
import ghidra.framework.data.DomainObjectMergeManager;
|
||||||
import ghidra.program.database.data.DataTypeManagerDB;
|
import ghidra.program.database.data.DataTypeManagerDB;
|
||||||
|
@ -753,7 +751,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set category path. If name conflict occurs within new category
|
* Set category path. If name conflict occurs within new category
|
||||||
* the specified dt will remain within its' current category
|
* the specified dt will remain within its current category
|
||||||
* @param dt datatype whoose category is to changed
|
* @param dt datatype whoose category is to changed
|
||||||
* @param newPath new category path
|
* @param newPath new category path
|
||||||
*/
|
*/
|
||||||
|
@ -860,8 +858,8 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
if (!myDtAddedList.contains(Long.valueOf(dataTypeID))) {
|
if (!myDtAddedList.contains(Long.valueOf(dataTypeID))) {
|
||||||
existingDt = dtms[RESULT].getDataType(dataTypeID);
|
existingDt = dtms[RESULT].getDataType(dataTypeID);
|
||||||
if (existingDt != null) {
|
if (existingDt != null) {
|
||||||
Msg.warn(this, " ** WARNING ** : Unexpectedly found data type \"" +
|
Msg.warn(this, "Unexpectedly found data type \"" + existingDt.getPathName() +
|
||||||
existingDt.getPathName() + "\" when trying to add it.");
|
"\" when trying to add it.");
|
||||||
return existingDt;
|
return existingDt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -923,6 +921,9 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
fixUpList.add(new FixUpInfo(id, baseID, -1, resolvedDataTypes));
|
fixUpList.add(new FixUpInfo(id, baseID, -1, resolvedDataTypes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
resolvedDataTypes.put(baseID, resolvedDt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Added in My, but hasn't processed yet, so fixup later.
|
// Added in My, but hasn't processed yet, so fixup later.
|
||||||
|
@ -1176,6 +1177,10 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
if (comps.length != 0) {
|
if (comps.length != 0) {
|
||||||
lastOffset = comps[comps.length - 1].getOffset();
|
lastOffset = comps[comps.length - 1].getOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track dependency errors to avoid duplicate popups
|
||||||
|
HashMap<Long, String> badIdDtMsgs = new HashMap<>();
|
||||||
|
|
||||||
for (DataTypeComponent sourceComp : comps) {
|
for (DataTypeComponent sourceComp : comps) {
|
||||||
DataType sourceCompDt = sourceComp.getDataType();
|
DataType sourceCompDt = sourceComp.getDataType();
|
||||||
BitFieldDataType bfDt = null;
|
BitFieldDataType bfDt = null;
|
||||||
|
@ -1231,6 +1236,9 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
try {
|
try {
|
||||||
if (resultCompDt != null) {
|
if (resultCompDt != null) {
|
||||||
|
|
||||||
|
long dtId = dtms[RESULT].getID(resultCompDt);
|
||||||
|
String badMsg = badIdDtMsgs.get(Long.valueOf(dtId));
|
||||||
|
|
||||||
int length = resultCompDt.getLength();
|
int length = resultCompDt.getLength();
|
||||||
if (length <= 0) {
|
if (length <= 0) {
|
||||||
length = sourceComp.getLength();
|
length = sourceComp.getLength();
|
||||||
|
@ -1242,7 +1250,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
|
destStruct.addBitField(resultCompDt, bfDt.getDeclaredBitSize(),
|
||||||
sourceComp.getFieldName(), comment);
|
sourceComp.getFieldName(), comment);
|
||||||
}
|
}
|
||||||
else {
|
else if (badMsg == null) {
|
||||||
try {
|
try {
|
||||||
// If I have compDt, it should now be from result DTM.
|
// If I have compDt, it should now be from result DTM.
|
||||||
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
|
destStruct.add(resultCompDt, length, sourceComp.getFieldName(),
|
||||||
|
@ -1250,14 +1258,17 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
displayError(destStruct, e);
|
displayError(destStruct, e);
|
||||||
DataType badDt = BadDataType.dataType;
|
badMsg = "Couldn't add " + resultCompDt.getDisplayName() +
|
||||||
comment = "Couldn't add " + resultCompDt.getDisplayName() +
|
" here. " + e.getMessage();
|
||||||
" here. " + e.getMessage() + " " +
|
if (e.getCause() instanceof DataTypeDependencyException) {
|
||||||
((comment != null) ? (" " + comment) : "");
|
badIdDtMsgs.put(dtId, badMsg);
|
||||||
destStruct.add(badDt, sourceComp.getLength(),
|
}
|
||||||
sourceComp.getFieldName(), comment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (badMsg != null) {
|
||||||
|
destStruct.add(BadDataType.dataType, sourceComp.getLength(),
|
||||||
|
sourceComp.getFieldName(), badMsg + " " + comment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (bfDt != null) {
|
else if (bfDt != null) {
|
||||||
destStruct.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
|
destStruct.insertBitFieldAt(sourceComp.getOffset(), sourceComp.getLength(),
|
||||||
|
@ -1265,33 +1276,40 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
sourceComp.getFieldName(), comment);
|
sourceComp.getFieldName(), comment);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
if (badMsg == null) {
|
||||||
// If I have compDt, it should now be from result DTM.
|
try {
|
||||||
// If not last component must constrain length to original component size
|
// If I have compDt, it should now be from result DTM.
|
||||||
int offset = sourceComp.getOffset();
|
// If not last component must constrain length to original component size
|
||||||
if (offset < lastOffset && length > sourceComp.getLength()) {
|
int offset = sourceComp.getOffset();
|
||||||
// The data type is too big, so adjust the component length to what will fit.
|
if (offset < lastOffset && length > sourceComp.getLength()) {
|
||||||
int extraBytesNeeded = length - sourceComp.getLength();
|
// The data type is too big, so adjust the component length to what will fit.
|
||||||
length = sourceComp.getLength();
|
int extraBytesNeeded = length - sourceComp.getLength();
|
||||||
// Output a warning indicating the structure has a data type that doesn't fit.
|
length = sourceComp.getLength();
|
||||||
String message =
|
// Output a warning indicating the structure has a data type that doesn't fit.
|
||||||
"Structure Merge: Not enough undefined bytes to fit " +
|
String message =
|
||||||
resultCompDt.getPathName() + " in structure " +
|
"Structure Merge: Not enough undefined bytes to fit " +
|
||||||
destStruct.getPathName() + " at offset 0x" +
|
resultCompDt.getPathName() + " in structure " +
|
||||||
Integer.toHexString(offset) + "." + "\nIt needs " +
|
destStruct.getPathName() + " at offset 0x" +
|
||||||
extraBytesNeeded + " more byte(s) to be able to fit.";
|
Integer.toHexString(offset) + "." + "\nIt needs " +
|
||||||
Msg.warn(this, message);
|
extraBytesNeeded + " more byte(s) to be able to fit.";
|
||||||
|
Msg.warn(this, message);
|
||||||
|
}
|
||||||
|
destStruct.insertAtOffset(sourceComp.getOffset(), resultCompDt,
|
||||||
|
length, sourceComp.getFieldName(), comment);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
displayError(destStruct, e);
|
||||||
|
badMsg = "Couldn't add " + resultCompDt.getDisplayName() +
|
||||||
|
" here. " + e.getMessage();
|
||||||
|
if (e.getCause() instanceof DataTypeDependencyException) {
|
||||||
|
badIdDtMsgs.put(dtId, badMsg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
destStruct.insertAtOffset(sourceComp.getOffset(), resultCompDt, length,
|
|
||||||
sourceComp.getFieldName(), comment);
|
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
if (badMsg != null) {
|
||||||
displayError(destStruct, e);
|
destStruct.insertAtOffset(sourceComp.getOffset(), BadDataType.dataType,
|
||||||
DataType badDt = BadDataType.dataType;
|
sourceComp.getLength(), sourceComp.getFieldName(),
|
||||||
comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " +
|
badMsg + " " + comment);
|
||||||
e.getMessage() + " " + ((comment != null) ? (" " + comment) : "");
|
|
||||||
destStruct.insertAtOffset(sourceComp.getOffset(), badDt,
|
|
||||||
sourceComp.getLength(), sourceComp.getFieldName(), comment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1370,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
String msg = "Some of your changes to " + destComposite.getName() +
|
String msg = "Some of your changes to " + destComposite.getName() +
|
||||||
" cannot be merged.\nProblem: " + e.getMessage();
|
" cannot be merged.\nProblem: " + e.getMessage();
|
||||||
String typeName = (destComposite instanceof Union) ? "Union" : "Structure";
|
String typeName = (destComposite instanceof Union) ? "Union" : "Structure";
|
||||||
Msg.showError(this, null, typeName + " Update Failed", msg);
|
MergeManager.displayErrorAndWait(this, typeName + " Update Failed", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUnion(long sourceDtID, Union sourceDt, Union destUnion,
|
private void updateUnion(long sourceDtID, Union sourceDt, Union destUnion,
|
||||||
|
@ -1836,6 +1854,13 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getNumDefinedComponents(Composite c) {
|
||||||
|
if (c instanceof Structure) {
|
||||||
|
return ((Structure) c).getNumDefinedComponents();
|
||||||
|
}
|
||||||
|
return c.getNumComponents();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean compositeDataTypeWasChanged(Composite c1, Composite c2) {
|
private boolean compositeDataTypeWasChanged(Composite c1, Composite c2) {
|
||||||
DataTypeManager dtm1 = c1.getDataTypeManager();
|
DataTypeManager dtm1 = c1.getDataTypeManager();
|
||||||
DataTypeManager dtm2 = c2.getDataTypeManager();
|
DataTypeManager dtm2 = c2.getDataTypeManager();
|
||||||
|
@ -1843,34 +1868,65 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
c1.isDefaultAligned() != c2.isDefaultAligned() ||
|
c1.isDefaultAligned() != c2.isDefaultAligned() ||
|
||||||
c1.isMachineAligned() != c2.isMachineAligned() ||
|
c1.isMachineAligned() != c2.isMachineAligned() ||
|
||||||
c1.getMinimumAlignment() != c2.getMinimumAlignment() ||
|
c1.getMinimumAlignment() != c2.getMinimumAlignment() ||
|
||||||
c1.getPackingValue() != c2.getPackingValue() ||
|
c1.getPackingValue() != c2.getPackingValue()) {
|
||||||
(!c1.isInternallyAligned() && (c1.getLength() != c2.getLength()))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c1.getNumComponents() != c2.getNumComponents()) {
|
|
||||||
|
int c1ComponentCnt = getNumDefinedComponents(c1);
|
||||||
|
int c2ComponentCnt = getNumDefinedComponents(c2);
|
||||||
|
if (c1ComponentCnt != c2ComponentCnt) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int nComponents = c1.getNumComponents();
|
|
||||||
for (int i = 0; i < nComponents; i++) {
|
boolean checkOffsets = false;
|
||||||
DataTypeComponent dtc1 = c1.getComponent(i);
|
|
||||||
DataTypeComponent dtc2 = c2.getComponent(i);
|
if (c1 instanceof Structure) {
|
||||||
if (dtm1.getID(dtc1.getDataType()) != dtm2.getID(dtc2.getDataType())) {
|
if (!((Structure) c1).isInternallyAligned()) {
|
||||||
|
if (c1.getNumComponents() != c2.getNumComponents()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
checkOffsets = true;
|
||||||
|
}
|
||||||
|
DataTypeComponent flexDtc1 = ((Structure) c1).getFlexibleArrayComponent();
|
||||||
|
DataTypeComponent flexDtc2 = ((Structure) c2).getFlexibleArrayComponent();
|
||||||
|
if (flexDtc1 != null && flexDtc2 != null) {
|
||||||
|
if (isChangedComponent(flexDtc1, flexDtc2, dtm1, dtm2, false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (flexDtc1 != null || flexDtc2 != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String fname1 = dtc1.getFieldName();
|
}
|
||||||
String fname2 = dtc2.getFieldName();
|
|
||||||
String comment1 = dtc1.getComment();
|
DataTypeComponent[] c1Components = c1.getDefinedComponents();
|
||||||
String comment2 = dtc2.getComment();
|
DataTypeComponent[] c2Components = c2.getDefinedComponents();
|
||||||
if (fname1 != null && !fname1.equals(fname2) ||
|
for (int i = 0; i < c1ComponentCnt; i++) {
|
||||||
fname2 != null && !fname2.equals(fname1) ||
|
DataTypeComponent dtc1 = c1Components[i];
|
||||||
comment1 != null && !comment1.equals(comment2) ||
|
DataTypeComponent dtc2 = c2Components[i];
|
||||||
comment2 != null && !comment2.equals(comment1)) {
|
if (isChangedComponent(dtc1, dtc2, dtm1, dtm2, checkOffsets)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isChangedComponent(DataTypeComponent dtc1, DataTypeComponent dtc2,
|
||||||
|
DataTypeManager dtm1, DataTypeManager dtm2, boolean checkOffsets) {
|
||||||
|
|
||||||
|
if (checkOffsets && dtc1.getOffset() != dtc2.getOffset()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (dtm1.getID(dtc1.getDataType()) != dtm2.getID(dtc2.getDataType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!Objects.equals(dtc1.getFieldName(), dtc2.getFieldName()) ||
|
||||||
|
!Objects.equals(dtc1.getComment(), dtc2.getComment())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean dataTypeSourceWasChanged(long id, DataTypeManager dtm) {
|
private boolean dataTypeSourceWasChanged(long id, DataTypeManager dtm) {
|
||||||
return dataTypeSourceWasChanged(id, dtms[ORIGINAL], dtm);
|
return dataTypeSourceWasChanged(id, dtms[ORIGINAL], dtm);
|
||||||
}
|
}
|
||||||
|
@ -1899,7 +1955,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
"\n Source Archive = " + sourceArchive2.getName() + " ";
|
"\n Source Archive = " + sourceArchive2.getName() + " ";
|
||||||
Msg.error(this, msg);
|
Msg.error(this, msg);
|
||||||
}
|
}
|
||||||
if (!SystemUtilities.isEqual(universalID1, universalID2)) {
|
if (!Objects.equals(universalID1, universalID2)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2226,10 +2282,9 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
UniversalID resultDtUniversalID = resultDt.getUniversalID();
|
UniversalID resultDtUniversalID = resultDt.getUniversalID();
|
||||||
UniversalID myDtUniversalID = myDt.getUniversalID();
|
UniversalID myDtUniversalID = myDt.getUniversalID();
|
||||||
// UniversalID can be null if data type is BuiltIn.
|
// UniversalID can be null if data type is BuiltIn.
|
||||||
if (!resultSourceArchive.getSourceArchiveID()
|
if (!resultSourceArchive.getSourceArchiveID().equals(
|
||||||
.equals(
|
mySourceArchive.getSourceArchiveID()) ||
|
||||||
mySourceArchive.getSourceArchiveID()) ||
|
!Objects.equals(resultDtUniversalID, myDtUniversalID)) {
|
||||||
!SystemUtilities.isEqual(resultDtUniversalID, myDtUniversalID)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (resultDt.isEquivalent(myDt)) {
|
if (resultDt.isEquivalent(myDt)) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,13 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.merge.datatypes;
|
package ghidra.app.merge.datatypes;
|
||||||
|
|
||||||
import ghidra.app.merge.MergeConstants;
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import ghidra.program.model.data.Enum;
|
|
||||||
import ghidra.program.model.listing.FunctionSignature;
|
|
||||||
import ghidra.util.UniversalID;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -31,6 +23,13 @@ import javax.swing.JPanel;
|
||||||
import javax.swing.JTextPane;
|
import javax.swing.JTextPane;
|
||||||
import javax.swing.text.*;
|
import javax.swing.text.*;
|
||||||
|
|
||||||
|
import ghidra.app.merge.MergeConstants;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.data.Enum;
|
||||||
|
import ghidra.program.model.listing.FunctionSignature;
|
||||||
|
import ghidra.util.StringUtilities;
|
||||||
|
import ghidra.util.UniversalID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Panel to show the contents of a Data Type.
|
* Panel to show the contents of a Data Type.
|
||||||
*
|
*
|
||||||
|
@ -39,56 +38,59 @@ import javax.swing.text.*;
|
||||||
class DataTypePanel extends JPanel {
|
class DataTypePanel extends JPanel {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
public Color SOURCE_COLOR = new Color(0, 140, 0);
|
public Color SOURCE_COLOR = new Color(0, 140, 0);
|
||||||
private DataType dataType;
|
private DataType dataType;
|
||||||
private JTextPane textPane;
|
private JTextPane textPane;
|
||||||
private StyledDocument doc;
|
private StyledDocument doc;
|
||||||
private SimpleAttributeSet pathAttrSet;
|
private SimpleAttributeSet pathAttrSet;
|
||||||
private SimpleAttributeSet nameAttrSet;
|
private SimpleAttributeSet nameAttrSet;
|
||||||
private SimpleAttributeSet sourceAttrSet;
|
private SimpleAttributeSet sourceAttrSet;
|
||||||
|
private SimpleAttributeSet offsetAttrSet;
|
||||||
private SimpleAttributeSet contentAttrSet;
|
private SimpleAttributeSet contentAttrSet;
|
||||||
private SimpleAttributeSet fieldNameAttrSet;
|
private SimpleAttributeSet fieldNameAttrSet;
|
||||||
private SimpleAttributeSet commentAttrSet;
|
private SimpleAttributeSet commentAttrSet;
|
||||||
private SimpleAttributeSet deletedAttrSet;
|
private SimpleAttributeSet deletedAttrSet;
|
||||||
|
|
||||||
DataTypePanel(DataType dataType) {
|
DataTypePanel(DataType dataType) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
create();
|
create();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDataType(DataType dataType) {
|
void setDataType(DataType dataType) {
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
textPane.setText("");
|
textPane.setText("");
|
||||||
|
|
||||||
if (dataType instanceof Composite) {
|
if (dataType instanceof Composite) {
|
||||||
formatCompositeText((Composite)dataType);
|
formatCompositeText((Composite) dataType);
|
||||||
}
|
}
|
||||||
else if (dataType instanceof Enum) {
|
else if (dataType instanceof Enum) {
|
||||||
formatEnumText((Enum)dataType);
|
formatEnumText((Enum) dataType);
|
||||||
}
|
}
|
||||||
else if (dataType instanceof TypeDef) {
|
else if (dataType instanceof TypeDef) {
|
||||||
formatTypeDefText((TypeDef)dataType);
|
formatTypeDefText((TypeDef) dataType);
|
||||||
}
|
}
|
||||||
else if (dataType instanceof FunctionDefinition) {
|
else if (dataType instanceof FunctionDefinition) {
|
||||||
formatFunctionDef((FunctionDefinition)dataType);
|
formatFunctionDef((FunctionDefinition) dataType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
formatDataType(dataType);
|
formatDataType(dataType);
|
||||||
}
|
}
|
||||||
textPane.setCaretPosition(0);
|
textPane.setCaretPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create() {
|
private void create() {
|
||||||
textPane = new JTextPane();
|
textPane = new JTextPane();
|
||||||
doc = textPane.getStyledDocument();
|
doc = textPane.getStyledDocument();
|
||||||
add(textPane, BorderLayout.CENTER);
|
add(textPane, BorderLayout.CENTER);
|
||||||
textPane.setEditable(false);
|
textPane.setEditable(false);
|
||||||
|
|
||||||
pathAttrSet = new SimpleAttributeSet();
|
pathAttrSet = new SimpleAttributeSet();
|
||||||
pathAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
pathAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||||
pathAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
|
pathAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
|
||||||
pathAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
pathAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||||
pathAttrSet.addAttribute(StyleConstants.Foreground, MergeConstants.CONFLICT_COLOR);
|
pathAttrSet.addAttribute(StyleConstants.Foreground, MergeConstants.CONFLICT_COLOR);
|
||||||
|
|
||||||
nameAttrSet = new SimpleAttributeSet();
|
nameAttrSet = new SimpleAttributeSet();
|
||||||
nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
nameAttrSet.addAttribute(StyleConstants.FontFamily, "Tahoma");
|
||||||
nameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
|
nameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(11));
|
||||||
|
@ -100,11 +102,16 @@ class DataTypePanel extends JPanel {
|
||||||
sourceAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
sourceAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||||
sourceAttrSet.addAttribute(StyleConstants.Foreground, SOURCE_COLOR);
|
sourceAttrSet.addAttribute(StyleConstants.Foreground, SOURCE_COLOR);
|
||||||
|
|
||||||
|
offsetAttrSet = new SimpleAttributeSet();
|
||||||
|
offsetAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced");
|
||||||
|
offsetAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
||||||
|
offsetAttrSet.addAttribute(StyleConstants.Foreground, Color.BLACK);
|
||||||
|
|
||||||
contentAttrSet = new SimpleAttributeSet();
|
contentAttrSet = new SimpleAttributeSet();
|
||||||
contentAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced");
|
contentAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced");
|
||||||
contentAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
contentAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
||||||
contentAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE);
|
contentAttrSet.addAttribute(StyleConstants.Foreground, Color.BLUE);
|
||||||
|
|
||||||
fieldNameAttrSet = new SimpleAttributeSet();
|
fieldNameAttrSet = new SimpleAttributeSet();
|
||||||
fieldNameAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced");
|
fieldNameAttrSet.addAttribute(StyleConstants.FontFamily, "Monospaced");
|
||||||
fieldNameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
fieldNameAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
||||||
|
@ -120,18 +127,18 @@ class DataTypePanel extends JPanel {
|
||||||
deletedAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
deletedAttrSet.addAttribute(StyleConstants.FontSize, new Integer(12));
|
||||||
deletedAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
deletedAttrSet.addAttribute(StyleConstants.Bold, Boolean.TRUE);
|
||||||
deletedAttrSet.addAttribute(StyleConstants.Foreground, Color.RED);
|
deletedAttrSet.addAttribute(StyleConstants.Foreground, Color.RED);
|
||||||
|
|
||||||
setDataType(dataType);
|
setDataType(dataType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatPath(DataType dt) {
|
private void formatPath(DataType dt) {
|
||||||
insertString("Path: " + dt.getCategoryPath()+ "\n\n", pathAttrSet);
|
insertString("Path: " + dt.getCategoryPath() + "\n\n", pathAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatSourceArchive(DataType dt) {
|
private void formatSourceArchive(DataType dt) {
|
||||||
insertString("Source Archive: " + getSourceArchiveName(dt) + "\n", sourceAttrSet);
|
insertString("Source Archive: " + getSourceArchiveName(dt) + "\n", sourceAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSourceArchiveName(DataType dt) {
|
private String getSourceArchiveName(DataType dt) {
|
||||||
SourceArchive sourceArchive = dt.getSourceArchive();
|
SourceArchive sourceArchive = dt.getSourceArchive();
|
||||||
UniversalID sourceID = (sourceArchive != null) ? sourceArchive.getSourceArchiveID() : null;
|
UniversalID sourceID = (sourceArchive != null) ? sourceArchive.getSourceArchiveID() : null;
|
||||||
|
@ -140,96 +147,141 @@ class DataTypePanel extends JPanel {
|
||||||
}
|
}
|
||||||
return sourceArchive.getName();
|
return sourceArchive.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatAlignment(Composite composite) {
|
private void formatAlignment(Composite composite) {
|
||||||
StringBuffer alignmentBuffer = new StringBuffer();
|
StringBuffer alignmentBuffer = new StringBuffer();
|
||||||
if (!composite.isInternallyAligned()) {
|
if (!composite.isInternallyAligned()) {
|
||||||
alignmentBuffer.append( "Unaligned" );
|
alignmentBuffer.append("Unaligned");
|
||||||
}
|
}
|
||||||
else if (composite.isDefaultAligned()) {
|
else if (composite.isDefaultAligned()) {
|
||||||
alignmentBuffer.append( "Aligned" );
|
alignmentBuffer.append("Aligned");
|
||||||
}
|
}
|
||||||
else if (composite.isMachineAligned()) {
|
else if (composite.isMachineAligned()) {
|
||||||
alignmentBuffer.append( "Machine aligned");
|
alignmentBuffer.append("Machine aligned");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
long alignment = composite.getMinimumAlignment();
|
long alignment = composite.getMinimumAlignment();
|
||||||
alignmentBuffer.append( "align(" + alignment + ")" );
|
alignmentBuffer.append("align(" + alignment + ")");
|
||||||
}
|
}
|
||||||
if (composite.isInternallyAligned()) {
|
if (composite.isInternallyAligned()) {
|
||||||
long packingValue = composite.getPackingValue();
|
long packingValue = composite.getPackingValue();
|
||||||
if (packingValue != Composite.NOT_PACKING) {
|
if (packingValue != Composite.NOT_PACKING) {
|
||||||
alignmentBuffer.append( " pack(" + packingValue + ")" );
|
alignmentBuffer.append(" pack(" + packingValue + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertString(alignmentBuffer.toString() + "\n\n", sourceAttrSet);
|
insertString(alignmentBuffer.toString() + "\n\n", sourceAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void formatAlignmentValue(Composite composite) {
|
private void insertAlignment(Composite composite) {
|
||||||
// StringBuffer alignmentBuffer = new StringBuffer();
|
StringBuffer alignmentBuffer = new StringBuffer();
|
||||||
// alignmentBuffer.append( "Alignment: " );
|
alignmentBuffer.append("Alignment: ");
|
||||||
//
|
alignmentBuffer.append(Integer.toString(composite.getAlignment()));
|
||||||
// DataTypeManager dataTypeManager = composite.getDataTypeManager();
|
insertString(alignmentBuffer.toString() + "\n", sourceAttrSet);
|
||||||
// DataOrganization dataOrganization = null;
|
}
|
||||||
// if (dataTypeManager != null) {
|
|
||||||
// dataOrganization = dataTypeManager.getDataOrganization();
|
private void insertLength(Composite composite) {
|
||||||
// }
|
StringBuffer lengthBuffer = new StringBuffer();
|
||||||
// if (dataOrganization == null) {
|
lengthBuffer.append("Length: ");
|
||||||
// dataOrganization = DataOrganization.getDefaultOrganization();
|
lengthBuffer.append(Integer.toString(composite.getLength()));
|
||||||
// }
|
insertString(lengthBuffer.toString() + "\n", sourceAttrSet);
|
||||||
// int alignment = dataOrganization.getAlignment(composite, composite.getLength());
|
}
|
||||||
// alignmentBuffer.append( "" + alignment );
|
|
||||||
//
|
private int max(String str, int length) {
|
||||||
// insertString("\n" + alignmentBuffer.toString() + "\n", sourceAttrSet);
|
if (str == null) {
|
||||||
// }
|
return length;
|
||||||
|
}
|
||||||
|
return Math.max(str.length(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDataTypeName(DataTypeComponent dtc) {
|
||||||
|
DataType dt = dtc.getDataType();
|
||||||
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append(dt.getName());
|
||||||
|
if (dtc.isFlexibleArrayComponent()) {
|
||||||
|
buffer.append("[0]");
|
||||||
|
}
|
||||||
|
else if (dt instanceof BitFieldDataType &&
|
||||||
|
!((Composite) dtc.getParent()).isInternallyAligned()) {
|
||||||
|
BitFieldDataType bfDt = (BitFieldDataType) dt;
|
||||||
|
buffer.append("(");
|
||||||
|
buffer.append(Integer.toString(bfDt.getBitOffset()));
|
||||||
|
buffer.append(")");
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderComponent(DataTypeComponent dtc, int dtNameWidth, int fieldNameWidth,
|
||||||
|
int offsetWidth) {
|
||||||
|
String fieldName = dtc.getFieldName();
|
||||||
|
if (fieldName == null) {
|
||||||
|
fieldName = "";
|
||||||
|
}
|
||||||
|
String comment = dtc.getComment();
|
||||||
|
if (comment == null) {
|
||||||
|
comment = "";
|
||||||
|
}
|
||||||
|
offsetWidth += 2; // factor in 0x prefix
|
||||||
|
String offsetStr = "";
|
||||||
|
if (offsetWidth > 0) {
|
||||||
|
if (!dtc.isFlexibleArrayComponent()) {
|
||||||
|
offsetStr = "0x" + Integer.toHexString(dtc.getOffset());
|
||||||
|
offsetStr = StringUtilities.pad(offsetStr, ' ', offsetWidth - offsetStr.length());
|
||||||
|
offsetStr += ": ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offsetStr = StringUtilities.pad(offsetStr, ' ', offsetWidth + 2);
|
||||||
|
}
|
||||||
|
insertString(" " + offsetStr + " ", offsetAttrSet);
|
||||||
|
}
|
||||||
|
fieldName = pad(fieldName, fieldNameWidth);
|
||||||
|
String typeName = pad(getDataTypeName(dtc), dtNameWidth);
|
||||||
|
|
||||||
|
insertString(" " + typeName + " ", contentAttrSet);
|
||||||
|
insertString(fieldName + " ", fieldNameAttrSet);
|
||||||
|
insertString(comment, commentAttrSet);
|
||||||
|
insertString("\n", contentAttrSet);
|
||||||
|
}
|
||||||
|
|
||||||
private void formatCompositeText(Composite comp) {
|
private void formatCompositeText(Composite comp) {
|
||||||
formatSourceArchive(comp);
|
formatSourceArchive(comp);
|
||||||
formatPath(comp);
|
formatPath(comp);
|
||||||
formatAlignment(comp);
|
formatAlignment(comp);
|
||||||
insertString(comp.getDisplayName(), nameAttrSet);
|
insertString(comp.getDisplayName(), nameAttrSet);
|
||||||
insertString(" { \n", contentAttrSet);
|
insertString(" { \n", contentAttrSet);
|
||||||
|
|
||||||
DataTypeComponent[] components = comp.getComponents();
|
|
||||||
int maxLength=0;
|
|
||||||
int maxFieldNameLength=0;
|
|
||||||
for (int i=0; i<components.length; i++) {
|
|
||||||
String name = components[i].getDataType().getDisplayName();
|
|
||||||
if (name.length() > maxLength) {
|
|
||||||
maxLength = name.length();
|
|
||||||
}
|
|
||||||
String fieldName = components[i].getFieldName();
|
|
||||||
if (fieldName == null) {
|
|
||||||
fieldName = " ";
|
|
||||||
}
|
|
||||||
if (fieldName.length() > maxFieldNameLength) {
|
|
||||||
maxFieldNameLength = fieldName.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<components.length; i++) {
|
boolean showComponentOffset = false;
|
||||||
String fieldName = components[i].getFieldName();
|
|
||||||
if (fieldName == null) {
|
DataTypeComponent[] components = comp.getDefinedComponents();
|
||||||
fieldName = "";
|
DataTypeComponent flexDtc = null;
|
||||||
}
|
if (comp instanceof Structure) {
|
||||||
String comment = components[i].getComment();
|
showComponentOffset = !comp.isInternallyAligned();
|
||||||
if (comment == null) {
|
flexDtc = ((Structure) comp).getFlexibleArrayComponent();
|
||||||
comment = "";
|
}
|
||||||
}
|
|
||||||
fieldName = pad(fieldName, maxFieldNameLength);
|
int offsetLength = showComponentOffset ? Integer.toHexString(comp.getLength()).length() : 0;
|
||||||
String typeName = pad(components[i].getDataType().getDisplayName(), maxLength);
|
int maxDtNameLength = 10;
|
||||||
|
int maxFieldNameLength = 1;
|
||||||
insertString(" " + typeName + " ", contentAttrSet);
|
for (int i = 0; i < components.length; i++) {
|
||||||
insertString(fieldName + " ", fieldNameAttrSet);
|
maxDtNameLength = max(getDataTypeName(components[i]), maxDtNameLength);
|
||||||
insertString(comment, commentAttrSet);
|
maxFieldNameLength = max(components[i].getFieldName(), maxFieldNameLength);
|
||||||
if (i < components.length-1) {
|
}
|
||||||
insertString("\n", contentAttrSet);
|
if (flexDtc != null) {
|
||||||
}
|
maxDtNameLength = max(getDataTypeName(flexDtc), maxDtNameLength);
|
||||||
}
|
maxFieldNameLength = max(flexDtc.getFieldName(), maxFieldNameLength);
|
||||||
insertString("\n }\n", contentAttrSet);
|
}
|
||||||
// formatAlignmentValue(comp);
|
|
||||||
}
|
for (int i = 0; i < components.length; i++) {
|
||||||
|
renderComponent(components[i], maxDtNameLength, maxFieldNameLength, offsetLength);
|
||||||
|
}
|
||||||
|
if (flexDtc != null) {
|
||||||
|
renderComponent(flexDtc, maxDtNameLength, maxFieldNameLength, offsetLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertString("}\n\n", contentAttrSet);
|
||||||
|
insertAlignment(comp);
|
||||||
|
insertLength(comp);
|
||||||
|
}
|
||||||
|
|
||||||
private void formatEnumText(Enum enuum) {
|
private void formatEnumText(Enum enuum) {
|
||||||
formatSourceArchive(enuum);
|
formatSourceArchive(enuum);
|
||||||
|
@ -238,104 +290,106 @@ class DataTypePanel extends JPanel {
|
||||||
insertString(" { \n", contentAttrSet);
|
insertString(" { \n", contentAttrSet);
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
String[] names = enuum.getNames();
|
String[] names = enuum.getNames();
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
for (int i=0; i<names.length; i++) {
|
for (int i = 0; i < names.length; i++) {
|
||||||
if (names[i].length() > maxLength) {
|
if (names[i].length() > maxLength) {
|
||||||
maxLength = names[i].length();
|
maxLength = names[i].length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long[] values = enuum.getValues();
|
long[] values = enuum.getValues();
|
||||||
Arrays.sort(values);
|
Arrays.sort(values);
|
||||||
|
|
||||||
for (int i=0; i<values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
String name = enuum.getName(values[i]);
|
String name = enuum.getName(values[i]);
|
||||||
name = pad(name, maxLength);
|
name = pad(name, maxLength);
|
||||||
sb.append(" " + name + " = 0x" + Long.toHexString(values[i]) + " ");
|
sb.append(" " + name + " = 0x" + Long.toHexString(values[i]) + " ");
|
||||||
if (i < values.length-1) {
|
if (i < values.length - 1) {
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.append("\n }\n");
|
sb.append("\n }\n");
|
||||||
insertString(sb.toString(), contentAttrSet);
|
insertString(sb.toString(), contentAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatTypeDefText(TypeDef td) {
|
private void formatTypeDefText(TypeDef td) {
|
||||||
formatSourceArchive(td);
|
formatSourceArchive(td);
|
||||||
formatPath(td);
|
formatPath(td);
|
||||||
insertString(td.getDisplayName(), nameAttrSet);
|
insertString(td.getDisplayName(), nameAttrSet);
|
||||||
insertString("\n", contentAttrSet);
|
insertString("\n", contentAttrSet);
|
||||||
insertString(" TypeDef on " + td.getDataType().getDisplayName(),
|
insertString(" TypeDef on " + td.getDataType().getDisplayName(), contentAttrSet);
|
||||||
contentAttrSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatFunctionDef(FunctionDefinition fd) {
|
private void formatFunctionDef(FunctionDefinition fd) {
|
||||||
formatSourceArchive(fd);
|
formatSourceArchive(fd);
|
||||||
formatPath(fd);
|
formatPath(fd);
|
||||||
ParameterDefinition[] vars = fd.getArguments();
|
ParameterDefinition[] vars = fd.getArguments();
|
||||||
|
|
||||||
DataType returnType = fd.getReturnType();
|
DataType returnType = fd.getReturnType();
|
||||||
insertString(returnType.getDisplayName(), contentAttrSet);
|
insertString(returnType.getDisplayName(), contentAttrSet);
|
||||||
insertString(" " + fd.getDisplayName(), nameAttrSet);
|
insertString(" " + fd.getDisplayName(), nameAttrSet);
|
||||||
insertString(" (", contentAttrSet);
|
insertString(" (", contentAttrSet);
|
||||||
boolean hasVarArgs = fd.hasVarArgs();
|
boolean hasVarArgs = fd.hasVarArgs();
|
||||||
if ((vars.length == 0) && !hasVarArgs) {
|
if ((vars.length == 0) && !hasVarArgs) {
|
||||||
insertString(")", contentAttrSet);
|
insertString(")", contentAttrSet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
for (int i=0; i<vars.length; i++) {
|
for (int i = 0; i < vars.length; i++) {
|
||||||
String typeName = vars[i].getDataType().getDisplayName();
|
String typeName = vars[i].getDataType().getDisplayName();
|
||||||
if (typeName.length() > maxLength) {
|
if (typeName.length() > maxLength) {
|
||||||
maxLength = typeName.length();
|
maxLength = typeName.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
for (int i=0; i<vars.length; i++) {
|
for (int i = 0; i < vars.length; i++) {
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
String name = vars[i].getDataType().getDisplayName();
|
String name = vars[i].getDataType().getDisplayName();
|
||||||
name = pad(name, maxLength);
|
name = pad(name, maxLength);
|
||||||
|
|
||||||
sb.append(" " + name + " " + vars[i].getName());
|
sb.append(" " + name + " " + vars[i].getName());
|
||||||
if ((i < vars.length-1) || (vars.length > 0 && hasVarArgs)) {
|
if ((i < vars.length - 1) || (vars.length > 0 && hasVarArgs)) {
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasVarArgs) {
|
if (hasVarArgs) {
|
||||||
if (vars.length > 0) {
|
if (vars.length > 0) {
|
||||||
sb.append( "\n" ).append( " " );
|
sb.append("\n").append(" ");
|
||||||
}
|
}
|
||||||
sb.append( FunctionSignature.VAR_ARGS_DISPLAY_STRING );
|
sb.append(FunctionSignature.VAR_ARGS_DISPLAY_STRING);
|
||||||
}
|
}
|
||||||
sb.append(")");
|
sb.append(")");
|
||||||
insertString(sb.toString(), contentAttrSet);
|
insertString(sb.toString(), contentAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatDataType(DataType dt) {
|
private void formatDataType(DataType dt) {
|
||||||
if (dt == null) {
|
if (dt == null) {
|
||||||
insertString("\n\nDeleted", deletedAttrSet);
|
insertString("\n\nDeleted", deletedAttrSet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
formatSourceArchive(dt);
|
formatSourceArchive(dt);
|
||||||
formatPath(dt);
|
formatPath(dt);
|
||||||
insertString(dt.getDisplayName(), nameAttrSet);
|
insertString(dt.getDisplayName(), nameAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String pad(String str, int length) {
|
private String pad(String str, int length) {
|
||||||
StringBuffer sb = new StringBuffer(str);
|
StringBuffer sb = new StringBuffer(str);
|
||||||
int len = length - str.length();
|
int len = length - str.length();
|
||||||
for (int i=0; i<len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
sb.append(" ");
|
sb.append(" ");
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertString(String str, SimpleAttributeSet attrSet) {
|
private void insertString(String str, SimpleAttributeSet attrSet) {
|
||||||
int offset = doc.getLength();
|
int offset = doc.getLength();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
doc.insertString(offset, str, attrSet);
|
doc.insertString(offset, str, attrSet);
|
||||||
} catch (BadLocationException e1) {
|
|
||||||
}
|
}
|
||||||
}
|
catch (BadLocationException e1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.label.GIconLabel;
|
import docking.widgets.label.GIconLabel;
|
||||||
import ghidra.app.merge.MergeConstants;
|
import ghidra.app.merge.MergeConstants;
|
||||||
import ghidra.app.merge.util.ConflictCountPanel;
|
import ghidra.app.merge.util.ConflictCountPanel;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.framework.data.DomainObjectMergeManager;
|
import ghidra.framework.data.DomainObjectMergeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.merge.datatypes;
|
package ghidra.app.merge.datatypes;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.ArchiveType;
|
import ghidra.program.model.data.ArchiveType;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
|
@ -24,7 +24,6 @@ import javax.swing.table.AbstractTableModel;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.support.FieldRange;
|
import docking.widgets.fieldpanel.support.FieldRange;
|
||||||
import docking.widgets.fieldpanel.support.FieldSelection;
|
import docking.widgets.fieldpanel.support.FieldSelection;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.SettingsImpl;
|
import ghidra.docking.settings.SettingsImpl;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
@ -1062,7 +1061,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager
|
||||||
* @return true if a sub-component is in the indicated category.
|
* @return true if a sub-component is in the indicated category.
|
||||||
*/
|
*/
|
||||||
boolean hasSubDtInCategory(Composite parentDt, String catPath) {
|
boolean hasSubDtInCategory(Composite parentDt, String catPath) {
|
||||||
DataTypeComponent components[] = parentDt.getComponents();
|
DataTypeComponent components[] = parentDt.getDefinedComponents();
|
||||||
// FUTURE Add a structure to keep track of which composites were searched so they aren't searched multiple times.
|
// FUTURE Add a structure to keep track of which composites were searched so they aren't searched multiple times.
|
||||||
for (DataTypeComponent component : components) {
|
for (DataTypeComponent component : components) {
|
||||||
DataType subDt = component.getDataType();
|
DataType subDt = component.getDataType();
|
||||||
|
@ -1087,7 +1086,7 @@ class CompositeViewerModel extends AbstractTableModel implements DataTypeManager
|
||||||
* @return true if the composite data type has the data type as a sub-component.
|
* @return true if the composite data type has the data type as a sub-component.
|
||||||
*/
|
*/
|
||||||
protected boolean hasSubDt(Composite parentDt, DataTypePath dtPath) {
|
protected boolean hasSubDt(Composite parentDt, DataTypePath dtPath) {
|
||||||
DataTypeComponent components[] = parentDt.getComponents();
|
DataTypeComponent components[] = parentDt.getDefinedComponents();
|
||||||
for (DataTypeComponent component : components) {
|
for (DataTypeComponent component : components) {
|
||||||
DataType subDt = component.getDataType();
|
DataType subDt = component.getDataType();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ import javax.swing.JLabel;
|
||||||
|
|
||||||
import docking.widgets.table.GTableCellRenderer;
|
import docking.widgets.table.GTableCellRenderer;
|
||||||
import docking.widgets.table.GTableCellRenderingData;
|
import docking.widgets.table.GTableCellRenderingData;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.util.ToolTipUtils;
|
import ghidra.app.util.ToolTipUtils;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
|
|
|
@ -25,7 +25,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.label.GDHtmlLabel;
|
import docking.widgets.label.GDHtmlLabel;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.util.ToolTipUtils;
|
import ghidra.app.util.ToolTipUtils;
|
||||||
import ghidra.app.util.html.HTMLDataTypeRepresentation;
|
import ghidra.app.util.html.HTMLDataTypeRepresentation;
|
||||||
import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation;
|
import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation;
|
||||||
|
|
|
@ -17,9 +17,9 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -26,11 +26,9 @@ import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import resources.MultiIcon;
|
import resources.MultiIcon;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
|
@ -26,7 +26,6 @@ import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||||
|
|
|
@ -26,10 +26,10 @@ import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.GTreeState;
|
import docking.widgets.tree.GTreeState;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
|
|
@ -32,8 +32,7 @@ import ghidra.app.plugin.core.datamgr.archive.*;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class FindReferencesToFieldAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
Composite composite = (Composite) dataTypeNode.getDataType();
|
Composite composite = (Composite) dataTypeNode.getDataType();
|
||||||
DataTypeComponent[] components = composite.getComponents();
|
DataTypeComponent[] components = composite.getDefinedComponents();
|
||||||
List<String> names = new ArrayList<>();
|
List<String> names = new ArrayList<>();
|
||||||
for (DataTypeComponent dataTypeComponent : components) {
|
for (DataTypeComponent dataTypeComponent : components) {
|
||||||
if (dataTypeComponent.isBitFieldComponent()) {
|
if (dataTypeComponent.isBitFieldComponent()) {
|
||||||
|
|
|
@ -18,9 +18,9 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,11 +17,9 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
|
@ -25,10 +25,10 @@ import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.GTreeState;
|
import docking.widgets.tree.GTreeState;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
|
|
@ -17,10 +17,10 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
|
|
|
@ -17,9 +17,9 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -26,11 +26,9 @@ import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import resources.MultiIcon;
|
import resources.MultiIcon;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
import resources.icons.EmptyIcon;
|
import resources.icons.EmptyIcon;
|
||||||
|
|
|
@ -18,8 +18,8 @@ package ghidra.app.plugin.core.datamgr.actions;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.action.DockingAction;
|
import docking.action.DockingAction;
|
||||||
|
|
|
@ -20,8 +20,7 @@ import java.io.IOException;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import ghidra.program.model.data.ArchiveType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.DuplicateFileException;
|
import ghidra.util.exception.DuplicateFileException;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
|
@ -29,7 +29,6 @@ import docking.widgets.combobox.GhidraComboBox;
|
||||||
import docking.widgets.label.GLabel;
|
import docking.widgets.label.GLabel;
|
||||||
import ghidra.app.plugin.core.compositeeditor.*;
|
import ghidra.app.plugin.core.compositeeditor.*;
|
||||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
|
import ghidra.app.plugin.core.function.EditFunctionSignatureDialog;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
|
|
@ -22,7 +22,6 @@ import javax.swing.Icon;
|
||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.task.SwingUpdateManager;
|
import ghidra.util.task.SwingUpdateManager;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ import docking.widgets.tree.*;
|
||||||
import docking.widgets.tree.internal.DefaultGTreeDataTransformer;
|
import docking.widgets.tree.internal.DefaultGTreeDataTransformer;
|
||||||
import docking.widgets.tree.support.GTreeRenderer;
|
import docking.widgets.tree.support.GTreeRenderer;
|
||||||
import ghidra.app.plugin.core.datamgr.*;
|
import ghidra.app.plugin.core.datamgr.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.*;
|
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
|
||||||
|
import ghidra.app.plugin.core.datamgr.archive.FileArchive;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -269,7 +270,7 @@ public class DataTypeArchiveGTree extends GTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCompositeStrings(Composite composite, List<String> results) {
|
private void addCompositeStrings(Composite composite, List<String> results) {
|
||||||
DataTypeComponent[] components = composite.getComponents();
|
DataTypeComponent[] components = composite.getDefinedComponents();
|
||||||
for (DataTypeComponent component : components) {
|
for (DataTypeComponent component : components) {
|
||||||
String fieldName = component.getFieldName();
|
String fieldName = component.getFieldName();
|
||||||
if (fieldName != null) {
|
if (fieldName != null) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.app.util.ToolTipUtils;
|
import ghidra.app.util.ToolTipUtils;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.Stack;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.app.services.DataTypeReference;
|
import ghidra.app.services.DataTypeReference;
|
||||||
import ghidra.app.services.DataTypeReferenceFinder;
|
import ghidra.app.services.DataTypeReferenceFinder;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -819,7 +818,7 @@ public final class ReferenceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
Composite composite = (Composite) baseParent;
|
Composite composite = (Composite) baseParent;
|
||||||
DataTypeComponent[] components = composite.getComponents();
|
DataTypeComponent[] components = composite.getDefinedComponents();
|
||||||
String name = path.pop();
|
String name = path.pop();
|
||||||
for (DataTypeComponent component : components) {
|
for (DataTypeComponent component : components) {
|
||||||
if (component.getFieldName().equals(name)) {
|
if (component.getFieldName().equals(name)) {
|
||||||
|
@ -1021,7 +1020,7 @@ public final class ReferenceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
Composite c = (Composite) dt;
|
Composite c = (Composite) dt;
|
||||||
DataTypeComponent[] components = c.getComponents();
|
DataTypeComponent[] components = c.getDefinedComponents();
|
||||||
for (DataTypeComponent component : components) {
|
for (DataTypeComponent component : components) {
|
||||||
if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) {
|
if (SystemUtilities.isEqual(component.getFieldName(), fieldName)) {
|
||||||
return component;
|
return component;
|
||||||
|
|
|
@ -243,7 +243,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
|
public DataTypeComponentImpl insertAtOffset(int offset, DataType dataType, int length,
|
||||||
String newName, String comment) {
|
String newName, String comment) throws IllegalArgumentException {
|
||||||
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
|
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Offset " + offset + " is not in " + getDisplayName() + ".");
|
"Offset " + offset + " is not in " + getDisplayName() + ".");
|
||||||
|
@ -304,7 +304,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent addPositive(DataType dataType, int length, String newName,
|
public DataTypeComponent addPositive(DataType dataType, int length, String newName,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
|
|
||||||
validateDataType(dataType);
|
validateDataType(dataType);
|
||||||
checkAncestry(dataType);
|
checkAncestry(dataType);
|
||||||
|
@ -325,7 +325,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent addNegative(DataType dataType, int length, String newName,
|
public DataTypeComponent addNegative(DataType dataType, int length, String newName,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
|
|
||||||
validateDataType(dataType);
|
validateDataType(dataType);
|
||||||
checkAncestry(dataType);
|
checkAncestry(dataType);
|
||||||
|
@ -682,7 +682,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent replace(int index, DataType dataType, int length, String newName,
|
public DataTypeComponent replace(int index, DataType dataType, int length, String newName,
|
||||||
String comment) {
|
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
|
||||||
if (index < 0 || index >= numComponents) {
|
if (index < 0 || index >= numComponents) {
|
||||||
throw new ArrayIndexOutOfBoundsException(index);
|
throw new ArrayIndexOutOfBoundsException(index);
|
||||||
}
|
}
|
||||||
|
@ -695,7 +695,7 @@ public abstract class BiDirectionDataType extends StructureDataType
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length,
|
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length,
|
||||||
String newName, String comment) {
|
String newName, String comment) throws IllegalArgumentException {
|
||||||
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
|
if (offset < splitOffset - negativeLength || offset >= splitOffset + positiveLength) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Offset " + offset + " is not in " + getDisplayName() + ".");
|
"Offset " + offset + " is not in " + getDisplayName() + ".");
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
/* ###
|
||||||
|
* 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.bin.format.dwarf4.next;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import static ghidra.program.model.data.DataTypeConflictHandler.ConflictResult.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link DataTypeConflictHandler conflict handler} attempts to match
|
||||||
|
* conflicting {@link Composite composite data types} (structure or union) when
|
||||||
|
* they have compatible data layouts. (Data types that are exactly equiv will
|
||||||
|
* not be subjected to conflict handling and will never reach here)
|
||||||
|
* <p>
|
||||||
|
* A default/empty sized structure, or structures with the same size are
|
||||||
|
* candidates for matching.
|
||||||
|
* <p>
|
||||||
|
* Structures that have a subset of the other's field definition are candidates
|
||||||
|
* for matching.
|
||||||
|
* <p>
|
||||||
|
* When a candidate data type is matched with an existing data type, this
|
||||||
|
* conflict handler will specify that the new data type is:
|
||||||
|
* <p>
|
||||||
|
* <ul>
|
||||||
|
* <li>discarded and replaced by the existing data type
|
||||||
|
* ({@link ConflictResult#USE_EXISTING})
|
||||||
|
* <li>used to overwrite the existing data type
|
||||||
|
* ({@link ConflictResult#REPLACE_EXISTING})
|
||||||
|
* </ul>
|
||||||
|
* or the candidate data type was <b>NOT</b> matched with an existing data type,
|
||||||
|
* and the new data type is:
|
||||||
|
* <p>
|
||||||
|
* <ul>
|
||||||
|
* <li>kept, but renamed with a .conflictNNNN suffix to make it unique
|
||||||
|
* ({@link ConflictResult#RENAME_AND_ADD})
|
||||||
|
* </ul>
|
||||||
|
* <b>NOTE:</b> structures with alignment (instead of being statically laid out)
|
||||||
|
* are not treated specially and will not match other aligned or non-aligned
|
||||||
|
* structures.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class DWARFDataTypeConflictHandler extends DataTypeConflictHandler {
|
||||||
|
|
||||||
|
static final DWARFDataTypeConflictHandler INSTANCE = new DWARFDataTypeConflictHandler();
|
||||||
|
|
||||||
|
private DWARFDataTypeConflictHandler() {
|
||||||
|
// do not create instances of this class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if src can overwrite the target composite based on size
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* @param target
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isSizeCompatible(Composite src, Composite target) {
|
||||||
|
return target.isNotYetDefined() || (src.getLength() == target.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the given composite is either empty or filled with default
|
||||||
|
* values (no defined components).
|
||||||
|
*
|
||||||
|
* @param composite composite to check
|
||||||
|
* @return true if empty or default and false otherwise
|
||||||
|
*/
|
||||||
|
private boolean isCompositeDefault(Composite composite) {
|
||||||
|
return composite.isNotYetDefined()
|
||||||
|
|| ((composite instanceof Structure) && ((Structure) composite).getNumDefinedComponents() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCompositePart(Composite full, Composite part, Set<Long> visitedDataTypes) {
|
||||||
|
if (full instanceof Structure && part instanceof Structure) {
|
||||||
|
return isStructurePart((Structure) full, (Structure) part, visitedDataTypes);
|
||||||
|
} else if (full instanceof Union && part instanceof Union) {
|
||||||
|
return isUnionPart((Union) full, (Union) part, visitedDataTypes);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if one union is a subset of another union.
|
||||||
|
* <p>
|
||||||
|
* Each component of the candidate partial union must be present in the 'full'
|
||||||
|
* union and must be 'equiv'.
|
||||||
|
* <p>
|
||||||
|
* Order of components is ignored, except for unnamed components, which receive
|
||||||
|
* a default name created using their ordinal position.
|
||||||
|
*
|
||||||
|
* @param full {@link Union} datatype that is expected to be a
|
||||||
|
* superset of the next param.
|
||||||
|
* @param part {@link Union} datatype that is expected to be a
|
||||||
|
* subset of the previous param.
|
||||||
|
* @param visitedDataTypes identity map of datatypes to prevent loops.
|
||||||
|
* @return true if part is a subset (or equal) to full.
|
||||||
|
*/
|
||||||
|
private boolean isUnionPart(Union full, Union part, Set<Long> visitedDataTypes) {
|
||||||
|
if (full.getLength() < part.getLength()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, DataTypeComponent> fullComponentsByName = new HashMap<>();
|
||||||
|
for (DataTypeComponent dtc : full.getComponents()) {
|
||||||
|
String name = dtc.getFieldName();
|
||||||
|
if (name == null) {
|
||||||
|
name = dtc.getDefaultFieldName();
|
||||||
|
}
|
||||||
|
fullComponentsByName.put(name, dtc);
|
||||||
|
}
|
||||||
|
for (DataTypeComponent dtc : part.getComponents()) {
|
||||||
|
String name = dtc.getFieldName();
|
||||||
|
if (name == null) {
|
||||||
|
name = dtc.getDefaultFieldName();
|
||||||
|
}
|
||||||
|
DataTypeComponent fullDTC = fullComponentsByName.get(name);
|
||||||
|
if (fullDTC == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DataType partDT = dtc.getDataType();
|
||||||
|
DataType fullDT = fullDTC.getDataType();
|
||||||
|
if (doRelaxedCompare(partDT, fullDT, visitedDataTypes) == RENAME_AND_ADD) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if one structure is a partial definition of another structure.
|
||||||
|
* <p> Each defined component in the candidate partial structure must be present
|
||||||
|
* in the 'full' structure and must be equiv. <p> The order and sparseness of
|
||||||
|
* the candidate partial structure is not important, only that all of its
|
||||||
|
* defined components are present in the full structure. <p>
|
||||||
|
*/
|
||||||
|
private boolean isStructurePart(Structure full, Structure part, Set<Long> visitedDataTypes) {
|
||||||
|
// Both structures should be equal in length
|
||||||
|
if (full.getLength() != part.getLength()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponent[] partComps = part.getDefinedComponents();
|
||||||
|
|
||||||
|
// Find a match in the full structure's component list for each
|
||||||
|
// component in the partial structure.
|
||||||
|
// Use resolveConflict() == USE_EXISTING to test for equiv in addition to
|
||||||
|
// isEquiv().
|
||||||
|
// Ensure that two components in the partial struct don't map to the same
|
||||||
|
// component in the full structure.
|
||||||
|
for (DataTypeComponent partDTC : partComps) {
|
||||||
|
DataTypeComponent fullDTCAt = (partDTC.getDataType() instanceof BitFieldDataType)
|
||||||
|
? getBitfieldByOffsets(full, partDTC)
|
||||||
|
: full.getComponentAt(partDTC.getOffset());
|
||||||
|
if (fullDTCAt == null || fullDTCAt.getOffset() != partDTC.getOffset()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DataType partDT = partDTC.getDataType();
|
||||||
|
DataType fullDT = fullDTCAt.getDataType();
|
||||||
|
if (doRelaxedCompare(partDT, fullDT, visitedDataTypes) == RENAME_AND_ADD) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( part.getFlexibleArrayComponent() != null ) {
|
||||||
|
return full.getFlexibleArrayComponent() != null
|
||||||
|
&& doRelaxedCompare(part.getFlexibleArrayComponent().getDataType(),
|
||||||
|
full.getFlexibleArrayComponent().getDataType(), visitedDataTypes) != RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataTypeComponent getBitfieldByOffsets(Structure full, DataTypeComponent partDTC) {
|
||||||
|
DataTypeComponent fullDTC = full.getComponentAt(partDTC.getOffset());
|
||||||
|
if (fullDTC == null || fullDTC.getOffset() != partDTC.getOffset()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BitFieldDataType partBF = (BitFieldDataType) partDTC.getDataType();
|
||||||
|
|
||||||
|
|
||||||
|
int fullNumComp = full.getNumComponents();
|
||||||
|
for(int fullOrdinal = fullDTC.getOrdinal(); fullOrdinal < fullNumComp; fullOrdinal++) {
|
||||||
|
fullDTC = full.getComponent(fullOrdinal);
|
||||||
|
if (fullDTC.getOffset() != partDTC.getOffset()
|
||||||
|
|| !(fullDTC.getDataType() instanceof BitFieldDataType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BitFieldDataType fullBF = (BitFieldDataType) fullDTC.getDataType();
|
||||||
|
if ( fullBF.getBitOffset() == partBF.getBitOffset() ) {
|
||||||
|
return fullDTC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strict compare will compare its parameters. The contents of these datatypes
|
||||||
|
* (ie. contents of structs, pointers, arrays) will be compared with relaxed
|
||||||
|
* typedef checking.
|
||||||
|
*/
|
||||||
|
private ConflictResult doStrictCompare(DataType addedDataType, DataType existingDataType,
|
||||||
|
Set<Long> visitedDataTypes) {
|
||||||
|
if (!addVisited(existingDataType, addedDataType, visitedDataTypes)) {
|
||||||
|
return USE_EXISTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof Composite && addedDataType instanceof Composite) {
|
||||||
|
Composite existingComposite = (Composite) existingDataType;
|
||||||
|
Composite addedComposite = (Composite) addedDataType;
|
||||||
|
|
||||||
|
// Check to see if we are adding a default/empty data type
|
||||||
|
if ((isCompositeDefault(addedComposite)) && isSizeCompatible(existingComposite, addedComposite)) {
|
||||||
|
return USE_EXISTING;
|
||||||
|
}
|
||||||
|
// Check to see if the existing type is a default/empty data type
|
||||||
|
if ((isCompositeDefault(existingComposite)) && isSizeCompatible(addedComposite, existingComposite)) {
|
||||||
|
return REPLACE_EXISTING;
|
||||||
|
}
|
||||||
|
// Check to see if the added type is part of the existing type first to
|
||||||
|
// generate more USE_EXISTINGS when possible.
|
||||||
|
if (isCompositePart(existingComposite, addedComposite, visitedDataTypes)) {
|
||||||
|
return USE_EXISTING;
|
||||||
|
}
|
||||||
|
// Check to see if the existing type is a part of the added type
|
||||||
|
if (isCompositePart(addedComposite, existingComposite, visitedDataTypes)) {
|
||||||
|
return REPLACE_EXISTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof TypeDef && addedDataType instanceof TypeDef) {
|
||||||
|
TypeDef addedTypeDef = (TypeDef) addedDataType;
|
||||||
|
TypeDef existingTypeDef = (TypeDef) existingDataType;
|
||||||
|
return doRelaxedCompare(addedTypeDef.getBaseDataType(), existingTypeDef.getBaseDataType(),
|
||||||
|
visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof Array && addedDataType instanceof Array) {
|
||||||
|
Array addedArray = (Array) addedDataType;
|
||||||
|
Array existingArray = (Array) existingDataType;
|
||||||
|
|
||||||
|
if (addedArray.getNumElements() != existingArray.getNumElements()
|
||||||
|
|| addedArray.getElementLength() != existingArray.getElementLength()) {
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return doRelaxedCompare(addedArray.getDataType(), existingArray.getDataType(), visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof Pointer && addedDataType instanceof Pointer) {
|
||||||
|
return doRelaxedCompare(((Pointer) addedDataType).getDataType(), ((Pointer) existingDataType).getDataType(),
|
||||||
|
visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof FunctionDefinition && addedDataType instanceof FunctionDefinition) {
|
||||||
|
return compareFuncDef((FunctionDefinition) addedDataType, (FunctionDefinition) existingDataType,
|
||||||
|
visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType instanceof BitFieldDataType && addedDataType instanceof BitFieldDataType) {
|
||||||
|
BitFieldDataType existingBF = (BitFieldDataType) existingDataType;
|
||||||
|
BitFieldDataType addedBF = (BitFieldDataType) addedDataType;
|
||||||
|
if (existingBF.getDeclaredBitSize() != addedBF.getDeclaredBitSize()) {
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
return existingBF.getPrimitiveBaseDataType().isEquivalent(addedBF.getPrimitiveBaseDataType()) ? USE_EXISTING
|
||||||
|
: RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDataType.isEquivalent(addedDataType)) {
|
||||||
|
return USE_EXISTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConflictResult compareFuncDef(FunctionDefinition addedFunc, FunctionDefinition existingFunc,
|
||||||
|
Set<Long> visitedDataTypes) {
|
||||||
|
if (doRelaxedCompare(addedFunc.getReturnType(), existingFunc.getReturnType(),
|
||||||
|
visitedDataTypes) == RENAME_AND_ADD) {
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
ParameterDefinition[] addedArgs = addedFunc.getArguments();
|
||||||
|
ParameterDefinition[] existingArgs = existingFunc.getArguments();
|
||||||
|
|
||||||
|
if (addedArgs.length != existingArgs.length) {
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < addedArgs.length; i++) {
|
||||||
|
ParameterDefinition addedParam = addedArgs[i];
|
||||||
|
ParameterDefinition existingParam = existingArgs[i];
|
||||||
|
|
||||||
|
if (doRelaxedCompare(addedParam.getDataType(), existingParam.getDataType(),
|
||||||
|
visitedDataTypes) == RENAME_AND_ADD) {
|
||||||
|
return RENAME_AND_ADD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return USE_EXISTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relaxed compare will take liberties in skipping typedefs to try to compare
|
||||||
|
* the types that the typedef are hiding. This is useful when comparing types
|
||||||
|
* that were embedded in differently compiled files, where you might end up with
|
||||||
|
* a raw basetype in one file and a typedef to a basetype in another file.
|
||||||
|
*/
|
||||||
|
private ConflictResult doRelaxedCompare(DataType addedDataType, DataType existingDataType,
|
||||||
|
Set<Long> visitedDataTypes) {
|
||||||
|
|
||||||
|
// unwrap typedefs, possibly asymmetrically. (ie. only unwrap added vs.
|
||||||
|
// existing)
|
||||||
|
if (addedDataType instanceof TypeDef) {
|
||||||
|
return doRelaxedCompare(((TypeDef) addedDataType).getBaseDataType(), existingDataType, visitedDataTypes);
|
||||||
|
}
|
||||||
|
if (existingDataType instanceof TypeDef) {
|
||||||
|
return doRelaxedCompare(addedDataType, ((TypeDef) existingDataType).getBaseDataType(), visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doStrictCompare(addedDataType, existingDataType, visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getDTPairKey(DataType dataType1, DataType dataType2) {
|
||||||
|
return ((long) System.identityHashCode(dataType1) << 32)
|
||||||
|
+ ((long) System.identityHashCode(dataType2) & 0xffffffffL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addVisited(DataType dataType1, DataType dataType2, Set<Long> visitedDataTypes) {
|
||||||
|
long key = getDTPairKey(dataType1, dataType2);
|
||||||
|
return visitedDataTypes.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConflictResult resolveConflict(DataType addedDataType, DataType existingDataType) {
|
||||||
|
Set<Long> visitedDataTypes = new HashSet<>();
|
||||||
|
return doStrictCompare(addedDataType, existingDataType, visitedDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldUpdate(DataType sourceDataType, DataType localDataType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataTypeConflictHandler getSubsequentHandler() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -408,8 +408,8 @@ public class DWARFDataTypeImporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType result = dataTypeManager.addDataType(enumDT,
|
DataType result =
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
dataTypeManager.addDataType(enumDT, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
return new DWARFDataType(result, dni, diea.getOffset());
|
return new DWARFDataType(result, dni, diea.getOffset());
|
||||||
}
|
}
|
||||||
|
@ -1339,7 +1339,7 @@ public class DWARFDataTypeImporter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return dataType.toString() + "|" + (dni != null ? dni.toString() : "na") + "|" +
|
return dataType.getName() + " | " + (dni != null ? dni.toString() : "na") + " | " +
|
||||||
hexOffsets();
|
hexOffsets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,38 +16,14 @@
|
||||||
package ghidra.app.util.bin.format.dwarf4.next;
|
package ghidra.app.util.bin.format.dwarf4.next;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import ghidra.app.util.bin.format.dwarf4.DIEAggregate;
|
import ghidra.app.util.bin.format.dwarf4.*;
|
||||||
import ghidra.app.util.bin.format.dwarf4.DWARFException;
|
|
||||||
import ghidra.app.util.bin.format.dwarf4.DWARFUtil;
|
|
||||||
import ghidra.app.util.bin.format.dwarf4.DebugInfoEntry;
|
|
||||||
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding;
|
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding;
|
||||||
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
|
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
|
||||||
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
|
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
|
||||||
import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeImporter.DWARFDataType;
|
import ghidra.app.util.bin.format.dwarf4.next.DWARFDataTypeImporter.DWARFDataType;
|
||||||
import ghidra.program.model.data.AbstractIntegerDataType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.ArrayDataType;
|
|
||||||
import ghidra.program.model.data.Category;
|
|
||||||
import ghidra.program.model.data.CategoryPath;
|
|
||||||
import ghidra.program.model.data.DataType;
|
|
||||||
import ghidra.program.model.data.DataTypeConflictHandler;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.program.model.data.DataTypePath;
|
|
||||||
import ghidra.program.model.data.FunctionDefinition;
|
|
||||||
import ghidra.program.model.data.FunctionDefinitionDataType;
|
|
||||||
import ghidra.program.model.data.GenericCallingConvention;
|
|
||||||
import ghidra.program.model.data.ParameterDefinition;
|
|
||||||
import ghidra.program.model.data.ParameterDefinitionImpl;
|
|
||||||
import ghidra.program.model.data.Pointer;
|
|
||||||
import ghidra.program.model.data.PointerDataType;
|
|
||||||
import ghidra.program.model.data.TypedefDataType;
|
|
||||||
import ghidra.program.model.data.WideChar16DataType;
|
|
||||||
import ghidra.program.model.data.WideChar32DataType;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
@ -155,8 +131,8 @@ public class DWARFDataTypeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit the DataType to the database
|
// Commit the DataType to the database
|
||||||
DataType post = dataTypeManager.resolve(pre.dataType,
|
DataType post =
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
dataTypeManager.resolve(pre.dataType, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
// While walking the pre and post DataType graph in lockstep, use the mapping of
|
// While walking the pre and post DataType graph in lockstep, use the mapping of
|
||||||
// pre_impl->offset to cache offset->post_datatype for later re-use.
|
// pre_impl->offset to cache offset->post_datatype for later re-use.
|
||||||
|
|
|
@ -116,7 +116,7 @@ public class DWARFParser {
|
||||||
CategoryPath origCompositeNSCP =
|
CategoryPath origCompositeNSCP =
|
||||||
new CategoryPath(origCategoryPath, compositeDataType.getName());
|
new CategoryPath(origCategoryPath, compositeDataType.getName());
|
||||||
CategoryPath destCompositeNSCP = new CategoryPath(newCP, compositeDataType.getName());
|
CategoryPath destCompositeNSCP = new CategoryPath(newCP, compositeDataType.getName());
|
||||||
for (DataTypeComponent component : compositeDataType.getComponents()) {
|
for (DataTypeComponent component : compositeDataType.getDefinedComponents()) {
|
||||||
DataType dtcDT = component.getDataType();
|
DataType dtcDT = component.getDataType();
|
||||||
if (dtcDT instanceof Array || dtcDT instanceof Pointer) {
|
if (dtcDT instanceof Array || dtcDT instanceof Pointer) {
|
||||||
dtcDT = DataTypeUtils.getNamedBaseDataType(dtcDT);
|
dtcDT = DataTypeUtils.getNamedBaseDataType(dtcDT);
|
||||||
|
|
|
@ -187,11 +187,10 @@ public class DWARFProgram implements Closeable {
|
||||||
this.nameLengthCutoffSize = Math.max(MIN_NAME_LENGTH_CUTOFF,
|
this.nameLengthCutoffSize = Math.max(MIN_NAME_LENGTH_CUTOFF,
|
||||||
Math.min(importOptions.getNameLengthCutoff(), MAX_NAME_LENGTH_CUTOFF));
|
Math.min(importOptions.getNameLengthCutoff(), MAX_NAME_LENGTH_CUTOFF));
|
||||||
|
|
||||||
|
|
||||||
monitor.setMessage("Reading DWARF debug string table");
|
monitor.setMessage("Reading DWARF debug string table");
|
||||||
this.debugStrings = StringTable.readStringTable(
|
this.debugStrings = StringTable.readStringTable(
|
||||||
sectionProvider.getSectionAsByteProvider(DWARFSectionNames.DEBUG_STR));
|
sectionProvider.getSectionAsByteProvider(DWARFSectionNames.DEBUG_STR));
|
||||||
Msg.info(this, "Read DWARF debug string table, " + debugStrings.getByteCount() + " bytes.");
|
// Msg.info(this, "Read DWARF debug string table, " + debugStrings.getByteCount() + " bytes.");
|
||||||
|
|
||||||
this.attributeFactory = new DWARFAttributeFactory(this);
|
this.attributeFactory = new DWARFAttributeFactory(this);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.html;
|
package ghidra.app.util.html;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.HTMLUtilities;
|
import ghidra.util.HTMLUtilities;
|
||||||
|
|
||||||
public class MissingArchiveDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
|
public class MissingArchiveDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
|
||||||
|
|
|
@ -626,7 +626,7 @@ public class DataTypesXmlMgr {
|
||||||
attrs.addAttribute("SIZE", struct.isNotYetDefined() ? 0 : struct.getLength(), true);
|
attrs.addAttribute("SIZE", struct.isNotYetDefined() ? 0 : struct.getLength(), true);
|
||||||
writer.startElement("STRUCTURE", attrs);
|
writer.startElement("STRUCTURE", attrs);
|
||||||
writeRegularComment(writer, struct.getDescription());
|
writeRegularComment(writer, struct.getDescription());
|
||||||
DataTypeComponent[] members = struct.getComponents();
|
DataTypeComponent[] members = struct.getDefinedComponents();
|
||||||
for (DataTypeComponent member : members) {
|
for (DataTypeComponent member : members) {
|
||||||
writerMember(writer, member);
|
writerMember(writer, member);
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getStructureComponent(Structure testInfoStruct, String fieldName) {
|
protected int getStructureComponent(Structure testInfoStruct, String fieldName) {
|
||||||
for (DataTypeComponent component : testInfoStruct.getComponents()) {
|
for (DataTypeComponent component : testInfoStruct.getDefinedComponents()) {
|
||||||
if (fieldName.equals(component.getFieldName())) {
|
if (fieldName.equals(component.getFieldName())) {
|
||||||
return component.getOffset();
|
return component.getOffset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import static org.junit.Assert.*;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import docking.widgets.OptionDialog;
|
|
||||||
import ghidra.program.database.*;
|
import ghidra.program.database.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
|
@ -297,11 +296,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setErrorsExpected(true);
|
executeMerge();
|
||||||
|
|
||||||
executeMerge(true);
|
close(waitForWindow("Structure Update Failed")); // expected dependency error on Foo
|
||||||
|
|
||||||
setErrorsExpected(false);
|
waitForCompletion();
|
||||||
|
|
||||||
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||||
|
|
||||||
|
@ -380,12 +379,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
chooseOption(DataTypeMergeManager.OPTION_LATEST);// LATEST CoolUnion
|
chooseOption(DataTypeMergeManager.OPTION_LATEST);// LATEST CoolUnion
|
||||||
|
|
||||||
setErrorsExpected(true);
|
|
||||||
|
|
||||||
chooseOption(DataTypeMergeManager.OPTION_MY);// MY Foo
|
chooseOption(DataTypeMergeManager.OPTION_MY);// MY Foo
|
||||||
waitForCompletion();
|
|
||||||
|
|
||||||
setErrorsExpected(false);
|
close(waitForWindow("Structure Update Failed")); // expected dependency error on Foo
|
||||||
|
|
||||||
|
waitForCompletion();
|
||||||
|
|
||||||
checkConflictCount(0);
|
checkConflictCount(0);
|
||||||
|
|
||||||
|
@ -534,21 +532,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
chooseOption(DataTypeMergeManager.OPTION_LATEST);// Latest CoolUnion
|
chooseOption(DataTypeMergeManager.OPTION_LATEST);// Latest CoolUnion
|
||||||
|
|
||||||
setErrorsExpected(true);
|
|
||||||
|
|
||||||
chooseOption(DataTypeMergeManager.OPTION_MY);// My Bar
|
chooseOption(DataTypeMergeManager.OPTION_MY);// My Bar
|
||||||
|
|
||||||
//
|
close(waitForWindow("Structure Update Failed")); // expected dependency error on Bar
|
||||||
// This last choice shows an error dialog
|
|
||||||
//
|
|
||||||
OptionDialog errorDialog =
|
|
||||||
waitForDialogComponent(null, OptionDialog.class, DEFAULT_WINDOW_TIMEOUT);
|
|
||||||
|
|
||||||
setErrorsExpected(false);
|
waitForCompletion();
|
||||||
|
|
||||||
assertNotNull(errorDialog);
|
|
||||||
errorDialog.close();
|
|
||||||
window.setVisible(false);
|
|
||||||
|
|
||||||
checkConflictCount(0);
|
checkConflictCount(0);
|
||||||
|
|
||||||
|
@ -1094,6 +1082,172 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
|
||||||
checkConflictCount(1);
|
checkConflictCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConflictUpdate7() throws Exception {
|
||||||
|
|
||||||
|
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "TD",
|
||||||
|
IntegerDataType.dataType);
|
||||||
|
|
||||||
|
mtf.initialize("notepad2", new ProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
|
||||||
|
"Structure_1");
|
||||||
|
s1.setFlexibleArrayComponent(td, null, null);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
|
||||||
|
"Structure_1");
|
||||||
|
s1.setFlexibleArrayComponent(IntegerDataType.dataType, "flex1", "cmt1");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge();
|
||||||
|
|
||||||
|
chooseOption(DataTypeMergeManager.OPTION_MY);// MY Structure_1
|
||||||
|
|
||||||
|
waitForCompletion();
|
||||||
|
|
||||||
|
checkConflictCount(0);
|
||||||
|
|
||||||
|
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||||
|
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
|
||||||
|
assertNotNull(s1);
|
||||||
|
DataTypeComponent[] dtcs = s1.getComponents();
|
||||||
|
assertEquals(4, dtcs.length);
|
||||||
|
|
||||||
|
DataTypeComponent flexDtc = s1.getFlexibleArrayComponent();
|
||||||
|
assertNotNull(flexDtc);
|
||||||
|
assertTrue(IntegerDataType.class == flexDtc.getDataType().getClass());
|
||||||
|
assertEquals("flex1", flexDtc.getFieldName());
|
||||||
|
assertEquals("cmt1", flexDtc.getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConflictUpdate8() throws Exception {
|
||||||
|
|
||||||
|
TypeDef td = new TypedefDataType(new CategoryPath("/Category1/Category2"), "TD",
|
||||||
|
IntegerDataType.dataType);
|
||||||
|
|
||||||
|
mtf.initialize("notepad2", new OriginalProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
|
||||||
|
"Structure_1");
|
||||||
|
s1.setFlexibleArrayComponent(IntegerDataType.dataType, null, null);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
|
||||||
|
"Structure_1");
|
||||||
|
s1.setFlexibleArrayComponent(td, "flex1", "cmt1");
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) {
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"),
|
||||||
|
"Structure_1");
|
||||||
|
s1.insertBitFieldAt(3, 2, 6, td, 2, "bf1", "my bf1");
|
||||||
|
s1.insertBitFieldAt(3, 2, 4, td, 2, "bf2", "my bf2");
|
||||||
|
s1.clearFlexibleArrayComponent();
|
||||||
|
}
|
||||||
|
catch (InvalidDataTypeException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge();
|
||||||
|
|
||||||
|
chooseOption(DataTypeMergeManager.OPTION_MY);// MY Structure_1
|
||||||
|
|
||||||
|
waitForCompletion();
|
||||||
|
|
||||||
|
checkConflictCount(0);
|
||||||
|
|
||||||
|
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||||
|
|
||||||
|
Structure s1 =
|
||||||
|
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2"), "Structure_1");
|
||||||
|
assertNotNull(s1);
|
||||||
|
|
||||||
|
DataTypeComponent flexDtc = s1.getFlexibleArrayComponent();
|
||||||
|
assertNull(flexDtc);
|
||||||
|
|
||||||
|
DataTypeComponent[] dtcs = s1.getComponents();
|
||||||
|
assertEquals(7, dtcs.length);
|
||||||
|
|
||||||
|
assertEquals(4, dtcs[3].getOffset()); // base on original 2-byte length 1st byte remains undefined
|
||||||
|
assertEquals("bf1", dtcs[3].getFieldName());
|
||||||
|
assertEquals("my bf1", dtcs[3].getComment());
|
||||||
|
|
||||||
|
DataType dt = dtcs[3].getDataType();
|
||||||
|
assertTrue(dt instanceof BitFieldDataType);
|
||||||
|
BitFieldDataType bfDt = (BitFieldDataType) dt;
|
||||||
|
assertTrue(td.isEquivalent(bfDt.getBaseDataType()));
|
||||||
|
assertEquals(2, bfDt.getDeclaredBitSize());
|
||||||
|
assertEquals(6, bfDt.getBitOffset());
|
||||||
|
|
||||||
|
assertEquals(4, dtcs[4].getOffset()); // base on original 2-byte length 1st byte remains undefined
|
||||||
|
assertEquals("bf2", dtcs[4].getFieldName());
|
||||||
|
assertEquals("my bf2", dtcs[4].getComment());
|
||||||
|
|
||||||
|
dt = dtcs[4].getDataType();
|
||||||
|
assertTrue(dt instanceof BitFieldDataType);
|
||||||
|
bfDt = (BitFieldDataType) dt;
|
||||||
|
assertTrue(td.isEquivalent(bfDt.getBaseDataType()));
|
||||||
|
assertEquals(2, bfDt.getDeclaredBitSize());
|
||||||
|
assertEquals(4, bfDt.getBitOffset());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditUnions() throws Exception {
|
public void testEditUnions() throws Exception {
|
||||||
|
|
||||||
|
|
|
@ -639,6 +639,8 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
chooseOption(DataTypeMergeManager.OPTION_MY);// Foo keeps its Bar, which creates Foo.conflict.
|
chooseOption(DataTypeMergeManager.OPTION_MY);// Foo keeps its Bar, which creates Foo.conflict.
|
||||||
|
|
||||||
|
close(waitForWindow("Structure Update Failed")); // expected dependency error on Bar (2 occurances of Bar use)
|
||||||
|
|
||||||
waitForCompletion();
|
waitForCompletion();
|
||||||
|
|
||||||
// should be two .conflict data types
|
// should be two .conflict data types
|
||||||
|
@ -1604,7 +1606,9 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
// Conflict on Bar
|
// Conflict on Bar
|
||||||
chooseOption(DataTypeMergeManager.OPTION_MY);// choose MY Bar
|
chooseOption(DataTypeMergeManager.OPTION_MY);// choose MY Bar
|
||||||
chooseOption(DataTypeMergeManager.OPTION_MY);// choose MY Foo
|
|
||||||
|
// NOTE: while Foo grows because of Bar it was not explicitly change in
|
||||||
|
// MY so no conflict should be detected for Foo
|
||||||
|
|
||||||
waitForCompletion();
|
waitForCompletion();
|
||||||
|
|
||||||
|
@ -2004,9 +2008,6 @@ public class DataTypeMerge4Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
|
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
|
||||||
DataTypeComponent[] dtcs = foo.getDefinedComponents();
|
DataTypeComponent[] dtcs = foo.getDefinedComponents();
|
||||||
for (DataTypeComponent dtc : dtcs) {
|
|
||||||
System.out.println(dtc.getDataType().getDisplayName());
|
|
||||||
}
|
|
||||||
assertEquals(3, dtcs.length);
|
assertEquals(3, dtcs.length);
|
||||||
assertEquals("Structure Foo was the wrong size.", 18, foo.getLength());
|
assertEquals("Structure Foo was the wrong size.", 18, foo.getLength());
|
||||||
|
|
||||||
|
|
|
@ -1011,6 +1011,8 @@ public class DataTypeMerge6Test extends AbstractDataTypeMergeTest {
|
||||||
setupStructureInUnionAndViceVersa();
|
setupStructureInUnionAndViceVersa();
|
||||||
executeMerge();
|
executeMerge();
|
||||||
|
|
||||||
|
close(waitForWindow("Union Update Failed")); // expected dependency error on CoolUnion
|
||||||
|
|
||||||
waitForCompletion();
|
waitForCompletion();
|
||||||
|
|
||||||
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||||
|
|
|
@ -30,7 +30,7 @@ import ghidra.util.task.TaskMonitorAdapter;
|
||||||
public class DataTypeMergeUseForAllTest extends AbstractDataTypeMergeTest {
|
public class DataTypeMergeUseForAllTest extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDataTypeDeletedChangedDoNotUseForAll() throws Exception {
|
public void testDataTypeDeletedChangedDoNotUseForAll() throws Exception {
|
||||||
|
|
||||||
setupTestDataTypeDeletedChangedUseForAll();
|
setupTestDataTypeDeletedChangedUseForAll();
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ public class DataTypeMergeUseForAllTest extends AbstractDataTypeMergeTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDataTypeDeletedChangedUseForAllPickLatest() throws Exception {
|
public void testDataTypeDeletedChangedUseForAllPickLatest() throws Exception {
|
||||||
|
|
||||||
setupTestDataTypeDeletedChangedUseForAll();
|
setupTestDataTypeDeletedChangedUseForAll();
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ public class DataTypeMergeUseForAllTest extends AbstractDataTypeMergeTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDataTypeDeletedChangedUseForAllPickMy() throws Exception {
|
public void testDataTypeDeletedChangedUseForAllPickMy() throws Exception {
|
||||||
|
|
||||||
setupTestDataTypeDeletedChangedUseForAll();
|
setupTestDataTypeDeletedChangedUseForAll();
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ public class DataTypeMergeUseForAllTest extends AbstractDataTypeMergeTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDataTypeDeletedChangedUseForAllPickOriginal() throws Exception {
|
public void testDataTypeDeletedChangedUseForAllPickOriginal() throws Exception {
|
||||||
|
|
||||||
setupTestDataTypeDeletedChangedUseForAll();
|
setupTestDataTypeDeletedChangedUseForAll();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,498 @@
|
||||||
|
/* ###
|
||||||
|
* 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.bin.format.dwarf4.next;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import ghidra.program.database.ProgramBuilder;
|
||||||
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
|
||||||
|
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for the {@link DataTypeConflictHandler conflict handler} stuff.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class DWARFConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
private ProgramDB program;
|
||||||
|
private DataTypeManager dataMgr;
|
||||||
|
private int transactionID;
|
||||||
|
|
||||||
|
private CategoryPath root = new CategoryPath(CategoryPath.ROOT, "conflict_test");
|
||||||
|
|
||||||
|
public DWARFConflictHandlerTest() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startTransaction() {
|
||||||
|
transactionID = program.startTransaction("Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endTransaction() {
|
||||||
|
program.endTransaction(transactionID, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this);
|
||||||
|
dataMgr = program.getDataTypeManager();
|
||||||
|
startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
endTransaction();
|
||||||
|
program.release(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDataType createPopulated(DataTypeManager dtm) {
|
||||||
|
StructureDataType struct = new StructureDataType(root, "struct1", 0, dtm);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "char1", null);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "char2", null);
|
||||||
|
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDataType createPopulated2(DataTypeManager dtm) {
|
||||||
|
StructureDataType struct = new StructureDataType(root, "struct1", 0, dtm);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "blah2", null);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "blah3", null);
|
||||||
|
struct.add(new CharDataType(dataMgr), 1, "blah4", null);
|
||||||
|
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDataType createPopulated2Partial(DataTypeManager dtm) {
|
||||||
|
StructureDataType struct = createPopulated2(dtm);
|
||||||
|
struct.clearComponent(2);
|
||||||
|
struct.clearComponent(1);
|
||||||
|
|
||||||
|
return struct;
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDataType createStub(DataTypeManager dtm, int size) {
|
||||||
|
return new StructureDataType(root, "struct1", size, dtm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert a particular ConflictResult outcome when adding two structs to the DTM.
|
||||||
|
* <p>
|
||||||
|
* Create a copy of the "addingStruct" before adding it because the impl instance can be
|
||||||
|
* modified during the conflict resolution by the DTM when it tries to DataType.clone()
|
||||||
|
* it before renaming the clone().
|
||||||
|
* <p>
|
||||||
|
* @param existingStruct
|
||||||
|
* @param addingStruct
|
||||||
|
* @param expectedResult
|
||||||
|
*/
|
||||||
|
private void assertStruct(Composite existingStruct, Composite addingStruct,
|
||||||
|
ConflictResult expectedResult) {
|
||||||
|
DataType existingResult =
|
||||||
|
dataMgr.addDataType(existingStruct, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
DataType existingResult_copy = existingResult.copy(null);
|
||||||
|
|
||||||
|
DataType addingCopy = addingStruct.copy(null);
|
||||||
|
DataType addedResult =
|
||||||
|
dataMgr.addDataType(addingStruct, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
switch (expectedResult) {
|
||||||
|
case USE_EXISTING:
|
||||||
|
assertEquals("DataType name should match", existingResult.getName(),
|
||||||
|
addedResult.getName());
|
||||||
|
assertEquals("DataType CategoryPath should match", existingResult.getCategoryPath(),
|
||||||
|
addedResult.getCategoryPath());
|
||||||
|
assertEquals("DataType length should match", existingResult.getLength(),
|
||||||
|
addedResult.getLength());
|
||||||
|
assertTrue("Added DataType should be equiv to existing DataType",
|
||||||
|
addedResult.isEquivalent(existingResult));
|
||||||
|
break;
|
||||||
|
case REPLACE_EXISTING:
|
||||||
|
assertEquals("DataType name should match", addingCopy.getName(),
|
||||||
|
addedResult.getName());
|
||||||
|
assertEquals("DataType CategoryPath should match", addingCopy.getCategoryPath(),
|
||||||
|
addedResult.getCategoryPath());
|
||||||
|
assertEquals("DataType length should match", addingCopy.getLength(),
|
||||||
|
addedResult.getLength());
|
||||||
|
assertTrue("Added DataType should be equiv to its impl before it was added",
|
||||||
|
addedResult.isEquivalent(addingCopy));
|
||||||
|
assertFalse("Added DataType should not be equiv to existing DataType",
|
||||||
|
addedResult.isEquivalent(existingResult_copy));
|
||||||
|
// NOTE: direct member replacement works in most cases
|
||||||
|
// assertTrue("Overwritten DataType should have a deleted flag",
|
||||||
|
// existingResult.isDeleted());
|
||||||
|
break;
|
||||||
|
case RENAME_AND_ADD:
|
||||||
|
Assert.assertNotEquals("DataType name should have changed", addingCopy.getName(),
|
||||||
|
addedResult.getName());
|
||||||
|
assertEquals("DataType CategoryPath should not changed",
|
||||||
|
addingCopy.getCategoryPath(), addedResult.getCategoryPath());
|
||||||
|
assertEquals("DataType length should not change", addingCopy.getLength(),
|
||||||
|
addedResult.getLength());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler to ensure that adding a empty conflicting structure resolves to a previous
|
||||||
|
* populated structure.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAddEmptyStructResolveToPopulatedStruct1() {
|
||||||
|
assertStruct(createPopulated(dataMgr), createStub(dataMgr, 0), ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddEmptyStructResolveToPopulatedStruct2() {
|
||||||
|
assertStruct(createPopulated(null), createStub(null, 0), ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler to ensure that adding a populated structure replaces an existing
|
||||||
|
* 'empty' structure. 'Empty' means either 0 byte length or 1 byte length structs
|
||||||
|
* as previous versions of Ghidra did not allow truly empty structs.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedStructOverwriteStub1() {
|
||||||
|
assertStruct(createStub(dataMgr, 0), createPopulated(dataMgr),
|
||||||
|
ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedStructOverwriteStub2() {
|
||||||
|
assertStruct(createStub(null, 0), createPopulated(null), ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedStructOverwriteSameSizedStub() {
|
||||||
|
StructureDataType populated = createPopulated(dataMgr);
|
||||||
|
assertStruct(createStub(dataMgr, populated.getLength()), populated,
|
||||||
|
ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddStubStructUseSameSizedPopulated() {
|
||||||
|
StructureDataType populated = createPopulated(dataMgr);
|
||||||
|
assertStruct(populated, createStub(dataMgr, populated.getLength()),
|
||||||
|
ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddStubStructCreateConflict() {
|
||||||
|
StructureDataType populated = createPopulated(dataMgr);
|
||||||
|
assertStruct(populated, createStub(dataMgr, populated.getLength() + 1),
|
||||||
|
ConflictResult.RENAME_AND_ADD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPartialStructResolveToPopulatedStruct() {
|
||||||
|
assertStruct(createPopulated2(dataMgr), createPopulated2Partial(dataMgr),
|
||||||
|
ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedStructOverwritePartialStruct() {
|
||||||
|
assertStruct(createPopulated2Partial(dataMgr), createPopulated2(dataMgr),
|
||||||
|
ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddStubUnionResolveToPopulated() {
|
||||||
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah2", null);
|
||||||
|
|
||||||
|
Union stub = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
|
||||||
|
assertStruct(populated, stub, ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedUnionOverwriteStub() {
|
||||||
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah2", null);
|
||||||
|
|
||||||
|
Union stub = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
|
||||||
|
assertStruct(stub, populated, ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPopulatedUnionOverwritePartial() {
|
||||||
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah2", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah3", null);
|
||||||
|
|
||||||
|
Union partial = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
partial.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
|
||||||
|
assertStruct(partial, populated, ConflictResult.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddConflictUnion() {
|
||||||
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah2", null);
|
||||||
|
populated.add(new IntegerDataType(dataMgr), 4, "blah3", null);
|
||||||
|
|
||||||
|
Union populated2 = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated2.add(new CharDataType(dataMgr), 1, "blahA", null);
|
||||||
|
|
||||||
|
assertStruct(populated, populated2, ConflictResult.RENAME_AND_ADD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddPartialUnionWithStubStructResolveToExisting() {
|
||||||
|
Structure s1a = createPopulated(dataMgr);
|
||||||
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
||||||
|
populated.add(s1a, s1a.getLength(), "blah2", null);
|
||||||
|
populated.add(s1a, s1a.getLength(), null, null);
|
||||||
|
|
||||||
|
Structure s1b = createStub(dataMgr, 0);
|
||||||
|
Union partial = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
partial.add(s1b, s1b.getLength(), "blah2", null);
|
||||||
|
|
||||||
|
assertStruct(populated, partial, ConflictResult.USE_EXISTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler to ensure that adding a conflicting typedef to a conflicting stub structure
|
||||||
|
* (when there is already a typedef to a populated structure) correctly uses the
|
||||||
|
* existing populated structure and existing typedef to the populated structure.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTypedefToStubUseExistingTypedefToPopulatedStructure() {
|
||||||
|
StructureDataType populatedStructure = createPopulated(dataMgr);
|
||||||
|
int origPopStructLen = populatedStructure.getLength();
|
||||||
|
TypeDef populatedTD = new TypedefDataType(root, "typedef1", populatedStructure, dataMgr);
|
||||||
|
dataMgr.addDataType(populatedTD, null);
|
||||||
|
|
||||||
|
StructureDataType stubStructure = createStub(dataMgr, 0);
|
||||||
|
TypeDef stubTD = new TypedefDataType(root, "typedef1", stubStructure, dataMgr);
|
||||||
|
DataType stubTDResult = dataMgr.addDataType(stubTD, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
assertTrue(stubTDResult instanceof TypeDef);
|
||||||
|
|
||||||
|
assertEquals(populatedTD.getPathName(), stubTDResult.getPathName());
|
||||||
|
|
||||||
|
DataType stubTDResultRefdDT = ((TypeDef) stubTDResult).getDataType();
|
||||||
|
assertEquals(stubTDResultRefdDT.getLength(), origPopStructLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler to ensure that adding truly conflicting structures and typedefs
|
||||||
|
* are treated as new data types and are renamed to a different name when added.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTypedefConflictToConflictStruct() {
|
||||||
|
StructureDataType struct1a = createPopulated(dataMgr);
|
||||||
|
TypeDef td1a = new TypedefDataType(root, "typedef1", struct1a, dataMgr);
|
||||||
|
DataType td1a_result = dataMgr.addDataType(td1a, null);
|
||||||
|
String td1a_result_path = td1a_result.getPathName();
|
||||||
|
|
||||||
|
DataType s1a_result = ((TypeDef) td1a_result).getDataType();
|
||||||
|
String s1a_result_path = s1a_result.getPathName();
|
||||||
|
|
||||||
|
StructureDataType struct1b = createPopulated2(dataMgr);
|
||||||
|
TypeDef td1b = new TypedefDataType(root, "typedef1", struct1b, dataMgr);
|
||||||
|
DataType td1b_result = dataMgr.addDataType(td1b, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
String td1b_result_path = td1b_result.getPathName();
|
||||||
|
|
||||||
|
DataType s1b_result = ((TypeDef) td1b_result).getDataType();
|
||||||
|
String s1b_result_path = s1b_result.getPathName();
|
||||||
|
|
||||||
|
Assert.assertNotEquals(td1a_result_path, td1b_result_path);
|
||||||
|
Assert.assertNotEquals(s1a_result_path, s1b_result_path);
|
||||||
|
assertFalse(td1a_result.isDeleted());
|
||||||
|
assertFalse(s1a_result.isDeleted());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler when adding a conflicting typedef impl that is referred to multiple
|
||||||
|
* times during a single addDataType() call.
|
||||||
|
* <p>
|
||||||
|
* Success is if the fields of struct2 are all the same datatype, probably named typedef1.conflict.
|
||||||
|
* <p>
|
||||||
|
* A failure would be if the fields of struct2 are different types, ie. field1 is typedef1.conflict,
|
||||||
|
* field2 is typedef1.conflict1, field3 is typedef1.conflict2.
|
||||||
|
* <p>
|
||||||
|
* This test is useful because the typedef impl that is referred to multiple times causes
|
||||||
|
* equiv checking and conflict resolution each time it is referred to, and if a precondition
|
||||||
|
* for those checks changes in some way and causes it to operate differently, this test will
|
||||||
|
* fail.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTypedefConflictToConflictStructMultiRef() {
|
||||||
|
StructureDataType struct1a = createPopulated(dataMgr);
|
||||||
|
TypeDef td1a = new TypedefDataType(root, "typedef1", struct1a, dataMgr);
|
||||||
|
DataType td1a_result = dataMgr.addDataType(td1a, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
StructureDataType struct1b = createPopulated2(dataMgr);
|
||||||
|
TypeDef td1b = new TypedefDataType(root, "typedef1", struct1b, dataMgr);
|
||||||
|
|
||||||
|
// Struct2 is used to create multiple references to the same conflicting typedef impl.
|
||||||
|
StructureDataType struct2 = new StructureDataType(root, "struct2", 0, dataMgr);
|
||||||
|
struct2.add(td1b, "typedef1_instance1", "first");
|
||||||
|
struct2.add(td1b, "typedef1_instance2", "second");
|
||||||
|
struct2.add(td1b, "typedef1_instance3", "third");
|
||||||
|
|
||||||
|
Structure struct2_result =
|
||||||
|
(Structure) dataMgr.addDataType(struct2, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
TypeDef td1b_result = (TypeDef) struct2_result.getComponent(0).getDataType();
|
||||||
|
String td1b_conflict_name = td1b_result.getPathName();
|
||||||
|
Assert.assertNotEquals(td1b_conflict_name, td1a_result.getPathName());
|
||||||
|
|
||||||
|
for (DataTypeComponent dtc : struct2_result.getComponents()) {
|
||||||
|
DataType dtcDT = dtc.getDataType();
|
||||||
|
String dtcDTName = dtcDT.getPathName();
|
||||||
|
assertEquals(dtcDTName, td1b_conflict_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler when adding a conflicting typedef impl (but equiv) that is referred to multiple
|
||||||
|
* times during a single addDataType() call.
|
||||||
|
* <p>
|
||||||
|
* Success is if the fields of struct2 are all the original typedef1 type.
|
||||||
|
* <p>
|
||||||
|
* A failure would be if the fields of struct2 are different types.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testTypedefToStubUseExistingTypedefToPopulatedStructureMultiRef() {
|
||||||
|
StructureDataType struct1a = createPopulated(dataMgr);
|
||||||
|
TypeDef td1a = new TypedefDataType(root, "typedef1", struct1a, dataMgr);
|
||||||
|
DataType td1a_result = dataMgr.addDataType(td1a, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
String origtd1Name = td1a_result.getPathName();
|
||||||
|
|
||||||
|
StructureDataType struct1b = createStub(dataMgr, 0);
|
||||||
|
TypeDef td1b = new TypedefDataType(root, "typedef1", struct1b, dataMgr);
|
||||||
|
PointerDataType ptd = new PointerDataType(td1b, program.getDefaultPointerSize(), dataMgr);
|
||||||
|
|
||||||
|
// Struct2 is used to create multiple references to the same conflicting typedef impl.
|
||||||
|
// Use a pointer to the typedef, otherwise struct2's size will be wrong when the
|
||||||
|
// conflicting struct1 impl size changes from 0 to 10.
|
||||||
|
StructureDataType struct2 = new StructureDataType(root, "struct2", 0, dataMgr);
|
||||||
|
struct2.add(ptd, "typedef1_instance1", "first");
|
||||||
|
struct2.add(ptd, "typedef1_instance2", "second");
|
||||||
|
struct2.add(ptd, "typedef1_instance3", "third");
|
||||||
|
|
||||||
|
Structure struct2_result =
|
||||||
|
(Structure) dataMgr.addDataType(struct2, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
for (DataTypeComponent dtc : struct2_result.getComponents()) {
|
||||||
|
Pointer pr = (Pointer) dtc.getDataType();
|
||||||
|
TypeDef tr = (TypeDef) pr.getDataType();
|
||||||
|
String dtcDTName = tr.getPathName();
|
||||||
|
assertEquals(origtd1Name, dtcDTName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the {@link DWARFDataTypeConflictHandler#INSTANCE}
|
||||||
|
* conflict handler when adding a typedef to a populated when there is already a typedef
|
||||||
|
* to a stub structure.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAddTypedefToPopulatedStructReplaceTypedefToStubStructure() {
|
||||||
|
StructureDataType struct1a = createStub(dataMgr, 0);
|
||||||
|
TypeDef td1a = new TypedefDataType(root, "typedef1", struct1a, dataMgr);
|
||||||
|
DataType td1a_result = dataMgr.addDataType(td1a, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
String td1a_pathname = td1a_result.getPathName();
|
||||||
|
String struct1a_pathname = ((TypeDef) td1a_result).getDataType().getPathName();
|
||||||
|
|
||||||
|
StructureDataType struct1b = createPopulated(dataMgr);
|
||||||
|
TypeDef td1b = new TypedefDataType(root, "typedef1", struct1b, dataMgr);
|
||||||
|
|
||||||
|
DataType td1b_result = dataMgr.addDataType(td1b, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
String td1b_pathname = td1b_result.getPathName();
|
||||||
|
String struct1b_pathname = ((TypeDef) td1b_result).getDataType().getPathName();
|
||||||
|
|
||||||
|
assertEquals("Typedef should have same name as previous typedef", td1a_pathname,
|
||||||
|
td1b_pathname);
|
||||||
|
assertEquals("Typedef target should have same name as previous typedef target",
|
||||||
|
struct1a_pathname, struct1b_pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveDataTypeStructConflict() throws Exception {
|
||||||
|
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
|
||||||
|
int id = dtm.startTransaction("");
|
||||||
|
Category otherRoot = dataMgr.getRootCategory();
|
||||||
|
Category subc = otherRoot.createCategory("subc");
|
||||||
|
|
||||||
|
Structure struct = new StructureDataType(subc.getCategoryPath(), "struct1", 10);
|
||||||
|
|
||||||
|
DataType resolvedStruct = dtm.resolve(struct, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
assertTrue(struct.isEquivalent(resolvedStruct));
|
||||||
|
assertEquals("/subc/struct1", resolvedStruct.getPathName());
|
||||||
|
|
||||||
|
struct.replace(0, dtm.resolve(new PointerDataType(resolvedStruct, 4, dtm),
|
||||||
|
DWARFDataTypeConflictHandler.INSTANCE), 4);
|
||||||
|
|
||||||
|
// NOTE: placing a DB dataType in an Impl datatype results in an invalid
|
||||||
|
// Impl type if one of its children refer to a deleted datatype. The
|
||||||
|
// 'struct' instance is such a case.
|
||||||
|
|
||||||
|
DataType resolvedStructA = dtm.resolve(struct, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
|
||||||
|
// Update struct with the expected result (old empty struct was replaced)
|
||||||
|
struct.replace(0, new PointerDataType(resolvedStructA, 4, dtm), 4);
|
||||||
|
|
||||||
|
assertTrue(struct.isEquivalent(resolvedStructA));
|
||||||
|
assertEquals("/subc/struct1", resolvedStructA.getPathName());
|
||||||
|
|
||||||
|
dtm.endTransaction(id, true);
|
||||||
|
dtm.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveDataTypeNonStructConflict() throws Exception {
|
||||||
|
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
|
||||||
|
int id = dtm.startTransaction("");
|
||||||
|
Category otherRoot = dataMgr.getRootCategory();
|
||||||
|
Category subc = otherRoot.createCategory("subc");
|
||||||
|
|
||||||
|
EnumDataType e = new EnumDataType(subc.getCategoryPath(), "Enum", 2);
|
||||||
|
|
||||||
|
DataType resolvedEnum = dtm.resolve(e, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
assertTrue(e.isEquivalent(resolvedEnum));
|
||||||
|
assertEquals("/subc/Enum", resolvedEnum.getPathName());
|
||||||
|
|
||||||
|
e.add("xyz", 1);
|
||||||
|
|
||||||
|
resolvedEnum = dtm.resolve(e, DWARFDataTypeConflictHandler.INSTANCE);
|
||||||
|
assertTrue(e.isEquivalent(resolvedEnum));
|
||||||
|
assertEquals("/subc/Enum.conflict", resolvedEnum.getPathName());
|
||||||
|
|
||||||
|
dtm.endTransaction(id, true);
|
||||||
|
dtm.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -433,8 +433,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
|
||||||
* other. (gcc linking options can cause types from different namespaces to be
|
* other. (gcc linking options can cause types from different namespaces to be
|
||||||
* forced into the root namespace)
|
* forced into the root namespace)
|
||||||
* <p>
|
* <p>
|
||||||
* Currently this causes a collision and a failure, resulting in just one of the structures
|
* Currently this produces two structures one renamed with .conflict.
|
||||||
* being defined, which isn't great but there is no other workable solution.
|
|
||||||
* <p>
|
* <p>
|
||||||
* If this test starts failing it means this behavior in Ghidra's DTM has changed and
|
* If this test starts failing it means this behavior in Ghidra's DTM has changed and
|
||||||
* the DWARF logic needs to be examined in light of those changes.
|
* the DWARF logic needs to be examined in light of those changes.
|
||||||
|
@ -460,7 +459,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
|
||||||
DataType dt1b = dwarfDTM.getDataType(struct1bDIE.getOffset(), null);
|
DataType dt1b = dwarfDTM.getDataType(struct1bDIE.getOffset(), null);
|
||||||
|
|
||||||
assertEquals("mystruct", dt1a.getName());
|
assertEquals("mystruct", dt1a.getName());
|
||||||
assertNull(dt1b);
|
assertEquals("mystruct.conflict", dt1b.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -497,8 +496,10 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
|
||||||
* datatype name as the impl struct. The embedded db struct needs to be empty and default
|
* datatype name as the impl struct. The embedded db struct needs to be empty and default
|
||||||
* sized (1 byte), and the outer impl struct needs to be bigger.
|
* sized (1 byte), and the outer impl struct needs to be bigger.
|
||||||
* <p>
|
* <p>
|
||||||
* Currently the DTM resolve() will return the new outer struct, but its field that
|
* Currently the DTM resolve() will ignore the conflict handlers attempt to
|
||||||
* refs the conflicting 1-byte struct has been changed to undefined (but name is still there).
|
* replace since it will result in cyclic dependency issue. It will instead
|
||||||
|
* rename the new structure as a conflict with its field refering to the original
|
||||||
|
* structure.
|
||||||
* <p>
|
* <p>
|
||||||
* This situation happens in DWARF when there is a base class and a derived class
|
* This situation happens in DWARF when there is a base class and a derived class
|
||||||
* that have the same name. They are in different namespaces, but during compilation
|
* that have the same name. They are in different namespaces, but during compilation
|
||||||
|
@ -515,10 +516,11 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
|
||||||
StructureDataType x2 = new StructureDataType(rootCP, "X", 4);
|
StructureDataType x2 = new StructureDataType(rootCP, "X", 4);
|
||||||
x2.replaceAtOffset(0, x, 1, "f1", null);
|
x2.replaceAtOffset(0, x, 1, "f1", null);
|
||||||
Structure x3 = (Structure) dataMgr.resolve(x2, DataTypeConflictHandler.REPLACE_HANDLER);
|
Structure x3 = (Structure) dataMgr.resolve(x2, DataTypeConflictHandler.REPLACE_HANDLER);
|
||||||
|
assertEquals("X.conflict", x3.getName());
|
||||||
DataTypeComponent dtc = x3.getComponent(0);
|
DataTypeComponent dtc = x3.getComponent(0);
|
||||||
DataType dtcDT = dtc.getDataType();
|
DataType dtcDT = dtc.getDataType();
|
||||||
assertEquals("f1", dtc.getFieldName());
|
assertEquals("f1", dtc.getFieldName());
|
||||||
assertEquals("undefined", dtcDT.getName()); // undefined field is current behavior
|
assertEquals("X", dtcDT.getName()); // undefined field is current behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -699,7 +701,7 @@ public class DWARFDataTypeImporterTest extends DWARFTestBase {
|
||||||
|
|
||||||
List<DataTypeComponent> getBitFieldComponents(Structure struct) {
|
List<DataTypeComponent> getBitFieldComponents(Structure struct) {
|
||||||
List<DataTypeComponent> results = new ArrayList<>();
|
List<DataTypeComponent> results = new ArrayList<>();
|
||||||
for (DataTypeComponent dtc : struct.getComponents()) {
|
for (DataTypeComponent dtc : struct.getDefinedComponents()) {
|
||||||
if (dtc.getDataType() instanceof BitFieldDataType) {
|
if (dtc.getDataType() instanceof BitFieldDataType) {
|
||||||
results.add(dtc);
|
results.add(dtc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
|
|
@ -90,14 +90,6 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
return struct;
|
return struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
private StructureDataType createPopulated2Partial(DataTypeManager dtm) {
|
|
||||||
StructureDataType struct = createPopulated2(dtm);
|
|
||||||
struct.clearComponent(2);
|
|
||||||
struct.clearComponent(1);
|
|
||||||
|
|
||||||
return struct;
|
|
||||||
}
|
|
||||||
|
|
||||||
private StructureDataType createStub(DataTypeManager dtm, int size) {
|
private StructureDataType createStub(DataTypeManager dtm, int size) {
|
||||||
return new StructureDataType(root, "struct1", size, dtm);
|
return new StructureDataType(root, "struct1", size, dtm);
|
||||||
}
|
}
|
||||||
|
@ -145,8 +137,9 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addedResult.isEquivalent(addingCopy));
|
addedResult.isEquivalent(addingCopy));
|
||||||
assertFalse("Added DataType should not be equiv to existing DataType",
|
assertFalse("Added DataType should not be equiv to existing DataType",
|
||||||
addedResult.isEquivalent(existingResult_copy));
|
addedResult.isEquivalent(existingResult_copy));
|
||||||
assertTrue("Overwritten DataType should have a deleted flag",
|
// NOTE: direct member replacement works in most cases
|
||||||
existingResult.isDeleted());
|
// assertTrue("Overwritten DataType should have a deleted flag",
|
||||||
|
// existingResult.isDeleted());
|
||||||
break;
|
break;
|
||||||
case RENAME_AND_ADD:
|
case RENAME_AND_ADD:
|
||||||
Assert.assertNotEquals("DataType name should have changed", addingCopy.getName(),
|
Assert.assertNotEquals("DataType name should have changed", addingCopy.getName(),
|
||||||
|
@ -171,19 +164,9 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddEmptyStructResolveToPopulatedStruct2() {
|
public void testAddEmptyStructResolveToPopulatedStruct2() {
|
||||||
assertStruct(createPopulated(dataMgr), createStub(dataMgr, 1), ConflictResult.USE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddEmptyStructResolveToPopulatedStruct3() {
|
|
||||||
assertStruct(createPopulated(null), createStub(null, 0), ConflictResult.USE_EXISTING);
|
assertStruct(createPopulated(null), createStub(null, 0), ConflictResult.USE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddEmptyStructResolveToPopulatedStruct4() {
|
|
||||||
assertStruct(createPopulated(null), createStub(null, 1), ConflictResult.USE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
|
* Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
|
||||||
* conflict handler to ensure that adding a populated structure replaces an existing
|
* conflict handler to ensure that adding a populated structure replaces an existing
|
||||||
|
@ -198,34 +181,9 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddPopulatedStructOverwriteStub2() {
|
public void testAddPopulatedStructOverwriteStub2() {
|
||||||
assertStruct(createStub(dataMgr, 1), createPopulated(dataMgr),
|
|
||||||
ConflictResult.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPopulatedStructOverwriteStub3() {
|
|
||||||
assertStruct(createStub(null, 0), createPopulated(null), ConflictResult.REPLACE_EXISTING);
|
assertStruct(createStub(null, 0), createPopulated(null), ConflictResult.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPopulatedStructOverwriteStub4() {
|
|
||||||
assertStruct(createStub(null, 1), createPopulated(null), ConflictResult.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPopulatedStructOverwriteSameSizedStub() {
|
|
||||||
StructureDataType populated = createPopulated(dataMgr);
|
|
||||||
assertStruct(createStub(dataMgr, populated.getLength()), populated,
|
|
||||||
ConflictResult.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddStubStructUseSameSizedPopulated() {
|
|
||||||
StructureDataType populated = createPopulated(dataMgr);
|
|
||||||
assertStruct(populated, createStub(dataMgr, populated.getLength()),
|
|
||||||
ConflictResult.USE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddStubStructCreateConflict() {
|
public void testAddStubStructCreateConflict() {
|
||||||
StructureDataType populated = createPopulated(dataMgr);
|
StructureDataType populated = createPopulated(dataMgr);
|
||||||
|
@ -233,18 +191,6 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
ConflictResult.RENAME_AND_ADD);
|
ConflictResult.RENAME_AND_ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPartialStructResolveToPopulatedStruct() {
|
|
||||||
assertStruct(createPopulated2(dataMgr), createPopulated2Partial(dataMgr),
|
|
||||||
ConflictResult.USE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPopulatedStructOverwritePartialStruct() {
|
|
||||||
assertStruct(createPopulated2Partial(dataMgr), createPopulated2(dataMgr),
|
|
||||||
ConflictResult.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddStubUnionResolveToPopulated() {
|
public void testAddStubUnionResolveToPopulated() {
|
||||||
Union populated = new UnionDataType(root, "union1", dataMgr);
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
@ -267,19 +213,6 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertStruct(stub, populated, ConflictResult.REPLACE_EXISTING);
|
assertStruct(stub, populated, ConflictResult.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPopulatedUnionOverwritePartial() {
|
|
||||||
Union populated = new UnionDataType(root, "union1", dataMgr);
|
|
||||||
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
|
||||||
populated.add(new IntegerDataType(dataMgr), 4, "blah2", null);
|
|
||||||
populated.add(new IntegerDataType(dataMgr), 4, "blah3", null);
|
|
||||||
|
|
||||||
Union partial = new UnionDataType(root, "union1", dataMgr);
|
|
||||||
partial.add(new CharDataType(dataMgr), 1, "blah1", null);
|
|
||||||
|
|
||||||
assertStruct(partial, populated, ConflictResult.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddConflictUnion() {
|
public void testAddConflictUnion() {
|
||||||
Union populated = new UnionDataType(root, "union1", dataMgr);
|
Union populated = new UnionDataType(root, "union1", dataMgr);
|
||||||
|
@ -293,21 +226,6 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertStruct(populated, populated2, ConflictResult.RENAME_AND_ADD);
|
assertStruct(populated, populated2, ConflictResult.RENAME_AND_ADD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAddPartialUnionWithStubStructResolveToExisting() {
|
|
||||||
Structure s1a = createPopulated(dataMgr);
|
|
||||||
Union populated = new UnionDataType(root, "union1", dataMgr);
|
|
||||||
populated.add(new CharDataType(dataMgr), 1, "blah1", null);
|
|
||||||
populated.add(s1a, s1a.getLength(), "blah2", null);
|
|
||||||
populated.add(s1a, s1a.getLength(), null, null);
|
|
||||||
|
|
||||||
Structure s1b = createStub(dataMgr, 0);
|
|
||||||
Union partial = new UnionDataType(root, "union1", dataMgr);
|
|
||||||
partial.add(s1b, s1b.getLength(), "blah2", null);
|
|
||||||
|
|
||||||
assertStruct(populated, partial, ConflictResult.USE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
|
* Tests the {@link DataTypeConflictHandler#REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER RESORAAH}
|
||||||
* conflict handler to ensure that adding a conflicting typedef to a conflicting stub structure
|
* conflict handler to ensure that adding a conflicting typedef to a conflicting stub structure
|
||||||
|
@ -477,40 +395,6 @@ public class ConflictHandlerTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
struct1a_pathname, struct1b_pathname);
|
struct1a_pathname, struct1b_pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testResolveDataTypeStructConflict() throws Exception {
|
|
||||||
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
|
|
||||||
int id = dtm.startTransaction("");
|
|
||||||
Category otherRoot = dataMgr.getRootCategory();
|
|
||||||
Category subc = otherRoot.createCategory("subc");
|
|
||||||
|
|
||||||
Structure struct = new StructureDataType(subc.getCategoryPath(), "struct1", 10);
|
|
||||||
|
|
||||||
DataType resolvedStruct = dtm.resolve(struct,
|
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
|
||||||
assertTrue(struct.isEquivalent(resolvedStruct));
|
|
||||||
assertEquals("/subc/struct1", resolvedStruct.getPathName());
|
|
||||||
|
|
||||||
struct.replace(0, dtm.resolve(new PointerDataType(resolvedStruct, 4, dtm),
|
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER), 4);
|
|
||||||
|
|
||||||
// NOTE: placing a DB dataType in an Impl datatype results in an invalid
|
|
||||||
// Impl type if one of its children refer to a deleted datatype. The
|
|
||||||
// 'struct' instance is such a case.
|
|
||||||
|
|
||||||
DataType resolvedStructA = dtm.resolve(struct,
|
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
|
||||||
|
|
||||||
// Update struct with the expected result (old empty struct was replaced)
|
|
||||||
struct.replace(0, new PointerDataType(resolvedStructA, 4, dtm), 4);
|
|
||||||
|
|
||||||
assertTrue(struct.isEquivalent(resolvedStructA));
|
|
||||||
assertEquals("/subc/struct1", resolvedStructA.getPathName());
|
|
||||||
|
|
||||||
dtm.endTransaction(id, true);
|
|
||||||
dtm.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveDataTypeNonStructConflict() throws Exception {
|
public void testResolveDataTypeNonStructConflict() throws Exception {
|
||||||
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
|
DataTypeManager dtm = new StandAloneDataTypeManager("Test");
|
||||||
|
|
|
@ -15,10 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.base.project;
|
package ghidra.base.project;
|
||||||
|
|
||||||
import static generic.test.AbstractGenericTest.*;
|
import static generic.test.AbstractGTest.*;
|
||||||
import static generic.test.TestUtils.argTypes;
|
import static generic.test.AbstractGenericTest.getInstanceField;
|
||||||
import static generic.test.TestUtils.args;
|
import static generic.test.AbstractGenericTest.invokeInstanceMethod;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static generic.test.TestUtils.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -68,7 +69,7 @@ public class FakeSharedProject {
|
||||||
|
|
||||||
LocalFileSystem fs = repo.getSharedFileSystem();
|
LocalFileSystem fs = repo.getSharedFileSystem();
|
||||||
if (fs != null) {
|
if (fs != null) {
|
||||||
// first project will keeps its' versioned file system
|
// first project will keeps its versioned file system
|
||||||
setVersionedFileSystem(fs);
|
setVersionedFileSystem(fs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,24 +27,24 @@ public class RenameStructureFieldTask extends RenameTask {
|
||||||
|
|
||||||
private Structure structure;
|
private Structure structure;
|
||||||
public int offset;
|
public int offset;
|
||||||
|
|
||||||
public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerPanel panel,
|
public RenameStructureFieldTask(PluginTool tool, Program program, DecompilerPanel panel,
|
||||||
ClangToken token, Structure structure, int offset) {
|
ClangToken token, Structure structure, int offset) {
|
||||||
super(tool, program, panel, token, token.getText());
|
super(tool, program, panel, token, token.getText());
|
||||||
this.structure = structure;
|
this.structure = structure;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commit() throws DuplicateNameException, InvalidInputException {
|
public void commit() throws DuplicateNameException, InvalidInputException {
|
||||||
if (structure.isNotYetDefined()) {
|
if (structure.isNotYetDefined()) {
|
||||||
DataType newtype = new Undefined1DataType();
|
DataType newtype = new Undefined1DataType();
|
||||||
structure.insert(0,newtype);
|
structure.insert(0, newtype);
|
||||||
}
|
}
|
||||||
DataTypeComponent comp = structure.getComponentAt(offset);
|
DataTypeComponent comp = structure.getComponentAt(offset);
|
||||||
if (comp.getDataType() == DataType.DEFAULT) { // Is this just a placeholder
|
if (comp.getDataType() == DataType.DEFAULT) { // Is this just a placeholder
|
||||||
DataType newtype = new Undefined1DataType();
|
DataType newtype = new Undefined1DataType();
|
||||||
structure.replaceAtOffset(offset, newtype,1, newName, "Created by retype action");
|
structure.replaceAtOffset(offset, newtype, 1, newName, "Created by retype action");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
comp.setFieldName(newName);
|
comp.setFieldName(newName);
|
||||||
|
@ -59,9 +59,8 @@ public class RenameStructureFieldTask extends RenameTask {
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(String newNm) {
|
public boolean isValid(String newNm) {
|
||||||
newName = newNm;
|
newName = newNm;
|
||||||
DataTypeComponent[] comp = structure.getComponents();
|
DataTypeComponent[] comp = structure.getDefinedComponents();
|
||||||
for (DataTypeComponent element : comp) {
|
for (DataTypeComponent element : comp) {
|
||||||
// if (comp[i].getDataType() == DataType.DEFAULT) continue; // Placeholder, don't compare name
|
|
||||||
String fieldname = element.getFieldName();
|
String fieldname = element.getFieldName();
|
||||||
if (fieldname == null) {
|
if (fieldname == null) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -261,7 +261,7 @@ public class DataTypeDependencyOrderer {
|
||||||
}
|
}
|
||||||
else if (dataType instanceof Structure) {
|
else if (dataType instanceof Structure) {
|
||||||
Structure struct = (Structure) dataType;
|
Structure struct = (Structure) dataType;
|
||||||
DataTypeComponent dtcomps[] = struct.getComponents();
|
DataTypeComponent dtcomps[] = struct.getDefinedComponents();
|
||||||
for (DataTypeComponent dtcomp : dtcomps) {
|
for (DataTypeComponent dtcomp : dtcomps) {
|
||||||
addDependent(entry, dtcomp.getDataType());
|
addDependent(entry, dtcomp.getDataType());
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import ghidra.util.timer.GTimerMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>BlockStreamServer</code> provides a block stream server implementation intended for
|
* <code>BlockStreamServer</code> provides a block stream server implementation intended for
|
||||||
* integration with the RMI GhidraServer implementation. The default instance will obtain its'
|
* integration with the RMI GhidraServer implementation. The default instance will obtain its
|
||||||
* port from the {@link ServerPortFactory} while all instances will bind to the default
|
* port from the {@link ServerPortFactory} while all instances will bind to the default
|
||||||
* {@link InetAddress#getLocalHost()} or the host address specified via the RMI property
|
* {@link InetAddress#getLocalHost()} or the host address specified via the RMI property
|
||||||
* <code>java.rmi.server.hostname</code> which is set via the GhidraServer -ip command
|
* <code>java.rmi.server.hostname</code> which is set via the GhidraServer -ip command
|
||||||
|
|
|
@ -141,7 +141,7 @@ class DefaultCompositeMember extends CompositeMember {
|
||||||
* @param baseDataType bitfield base datatype
|
* @param baseDataType bitfield base datatype
|
||||||
* @param bitSize bitfield size in bits
|
* @param bitSize bitfield size in bits
|
||||||
* @param bitOffsetWithinBaseType offset of bitfield within base type
|
* @param bitOffsetWithinBaseType offset of bitfield within base type
|
||||||
* @throws InvalidDataTypeException
|
* @throws InvalidDataTypeException invalid baseDataType for bitfield
|
||||||
*/
|
*/
|
||||||
private DefaultCompositeMember(int componentOffset, DataType baseDataType, int bitSize,
|
private DefaultCompositeMember(int componentOffset, DataType baseDataType, int bitSize,
|
||||||
int bitOffsetWithinBaseType) throws InvalidDataTypeException {
|
int bitOffsetWithinBaseType) throws InvalidDataTypeException {
|
||||||
|
@ -340,10 +340,10 @@ class DefaultCompositeMember extends CompositeMember {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGoodAlignment(Composite testCompsosite, int preferredSize) {
|
private boolean isGoodAlignment(Composite testComposite, int preferredSize) {
|
||||||
boolean alignOK = true;
|
boolean alignOK = true;
|
||||||
if (preferredSize > 0 && testCompsosite.getNumComponents() != 0) {
|
if (preferredSize > 0 && testComposite.getNumComponents() != 0) {
|
||||||
alignOK = (testCompsosite.getLength() == preferredSize);
|
alignOK = (testComposite.getLength() == preferredSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alignOK && isStructureContainer()) {
|
if (alignOK && isStructureContainer()) {
|
||||||
|
@ -351,7 +351,7 @@ class DefaultCompositeMember extends CompositeMember {
|
||||||
Structure struct = (Structure) memberDataType;
|
Structure struct = (Structure) memberDataType;
|
||||||
DataTypeComponent[] unalignedComponents = struct.getDefinedComponents();
|
DataTypeComponent[] unalignedComponents = struct.getDefinedComponents();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (DataTypeComponent dtc : testCompsosite.getComponents()) {
|
for (DataTypeComponent dtc : testComposite.getComponents()) {
|
||||||
DataTypeComponent unalignedDtc = unalignedComponents[index++];
|
DataTypeComponent unalignedDtc = unalignedComponents[index++];
|
||||||
if (!isComponentUnchanged(dtc, unalignedDtc)) {
|
if (!isComponentUnchanged(dtc, unalignedDtc)) {
|
||||||
alignOK = false;
|
alignOK = false;
|
||||||
|
|
|
@ -79,12 +79,9 @@ class PdbDataTypeParser {
|
||||||
return programDataTypeMgr;
|
return programDataTypeMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flushDataTypeCache(TaskMonitor monitor) throws CancelledException {
|
void flushDataTypeCache() throws CancelledException {
|
||||||
for (DataType dt : dataTypeCache.values()) {
|
programDataTypeMgr.addDataTypes(dataTypeCache.values(),
|
||||||
monitor.checkCanceled();
|
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER, monitor);
|
||||||
programDataTypeMgr.resolve(dt,
|
|
||||||
DataTypeConflictHandler.REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -294,7 +294,7 @@ public class PdbParser {
|
||||||
|
|
||||||
// Ensure that all data types are resolved
|
// Ensure that all data types are resolved
|
||||||
if (dataTypeParser != null) {
|
if (dataTypeParser != null) {
|
||||||
dataTypeParser.flushDataTypeCache(monitor);
|
dataTypeParser.flushDataTypeCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import ghidra.program.model.data.DataType;
|
||||||
/**
|
/**
|
||||||
* <code>WrappedDataType</code> provide the ability to wrap
|
* <code>WrappedDataType</code> provide the ability to wrap
|
||||||
* a {@link DataType} with additional information not conveyed
|
* a {@link DataType} with additional information not conveyed
|
||||||
* by the datatype on its' own.
|
* by the datatype on its own.
|
||||||
* <P>
|
* <P>
|
||||||
* Note that a BitFieldDataType instance may be specified as the datatype
|
* Note that a BitFieldDataType instance may be specified as the datatype
|
||||||
* in order to convey bitfield related information.
|
* in order to convey bitfield related information.
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class FieldIndexTable extends IndexTable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new or existing secondary index. An existing index must have
|
* Construct a new or existing secondary index. An existing index must have
|
||||||
* its' root ID specified within the tableRecord.
|
* its root ID specified within the tableRecord.
|
||||||
* @param primaryTable primary table.
|
* @param primaryTable primary table.
|
||||||
* @param indexTableRecord specifies the index parameters.
|
* @param indexTableRecord specifies the index parameters.
|
||||||
* @throws IOException thrown if an IO error occurs
|
* @throws IOException thrown if an IO error occurs
|
||||||
|
@ -244,8 +244,9 @@ public class FieldIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() throws IOException {
|
public boolean hasNext() throws IOException {
|
||||||
if (hasNext)
|
if (hasNext) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
hasPrev = false; // TODO ???
|
hasPrev = false; // TODO ???
|
||||||
indexKey = (IndexField) indexIterator.next();
|
indexKey = (IndexField) indexIterator.next();
|
||||||
int skipCnt = 0;
|
int skipCnt = 0;
|
||||||
|
@ -259,8 +260,9 @@ public class FieldIndexTable extends IndexTable {
|
||||||
indexKey = (IndexField) indexIterator.next();
|
indexKey = (IndexField) indexIterator.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indexKey == null)
|
if (indexKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
hasNext = true;
|
hasNext = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -268,8 +270,9 @@ public class FieldIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPrevious() throws IOException {
|
public boolean hasPrevious() throws IOException {
|
||||||
if (hasPrev)
|
if (hasPrev) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
hasNext = false; // TODO ???
|
hasNext = false; // TODO ???
|
||||||
indexKey = (IndexField) indexIterator.previous();
|
indexKey = (IndexField) indexIterator.previous();
|
||||||
int skipCnt = 0;
|
int skipCnt = 0;
|
||||||
|
@ -283,8 +286,9 @@ public class FieldIndexTable extends IndexTable {
|
||||||
indexKey = (IndexField) indexIterator.previous();
|
indexKey = (IndexField) indexIterator.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (indexKey == null)
|
if (indexKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
hasPrev = true;
|
hasPrev = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -321,12 +325,13 @@ public class FieldIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean delete() throws IOException {
|
public boolean delete() throws IOException {
|
||||||
if (lastKey == null)
|
if (lastKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
synchronized (db) {
|
synchronized (db) {
|
||||||
long[] keys = findPrimaryKeys(lastKey.getIndexField());
|
long[] keys = findPrimaryKeys(lastKey.getIndexField());
|
||||||
for (int i = 0; i < keys.length; i++) {
|
for (long key : keys) {
|
||||||
primaryTable.deleteRecord(keys[i]);
|
primaryTable.deleteRecord(key);
|
||||||
}
|
}
|
||||||
lastKey = null;
|
lastKey = null;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -48,7 +48,7 @@ class FixedIndexTable extends IndexTable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new or existing secondary index. An existing index must have
|
* Construct a new or existing secondary index. An existing index must have
|
||||||
* its' root ID specified within the tableRecord.
|
* its root ID specified within the tableRecord.
|
||||||
* @param primaryTable primary table.
|
* @param primaryTable primary table.
|
||||||
* @param indexTableRecord specifies the index parameters.
|
* @param indexTableRecord specifies the index parameters.
|
||||||
* @throws IOException thrown if an IO error occurs
|
* @throws IOException thrown if an IO error occurs
|
||||||
|
@ -66,11 +66,13 @@ class FixedIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
long[] findPrimaryKeys(Field indexValue) throws IOException {
|
long[] findPrimaryKeys(Field indexValue) throws IOException {
|
||||||
if (!indexValue.getClass().equals(fieldType.getClass()))
|
if (!indexValue.getClass().equals(fieldType.getClass())) {
|
||||||
throw new IllegalArgumentException("Incorrect indexed field type");
|
throw new IllegalArgumentException("Incorrect indexed field type");
|
||||||
|
}
|
||||||
Record indexRecord = indexTable.getRecord(indexValue.getLongValue());
|
Record indexRecord = indexTable.getRecord(indexValue.getLongValue());
|
||||||
if (indexRecord == null)
|
if (indexRecord == null) {
|
||||||
return emptyKeyArray;
|
return emptyKeyArray;
|
||||||
|
}
|
||||||
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
||||||
return indexBuffer.getPrimaryKeys();
|
return indexBuffer.getPrimaryKeys();
|
||||||
}
|
}
|
||||||
|
@ -83,11 +85,13 @@ class FixedIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
int getKeyCount(Field indexValue) throws IOException {
|
int getKeyCount(Field indexValue) throws IOException {
|
||||||
if (!indexValue.getClass().equals(fieldType.getClass()))
|
if (!indexValue.getClass().equals(fieldType.getClass())) {
|
||||||
throw new IllegalArgumentException("Incorrect indexed field type");
|
throw new IllegalArgumentException("Incorrect indexed field type");
|
||||||
|
}
|
||||||
Record indexRecord = indexTable.getRecord(indexValue.getLongValue());
|
Record indexRecord = indexTable.getRecord(indexValue.getLongValue());
|
||||||
if (indexRecord == null)
|
if (indexRecord == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
||||||
return indexBuffer.keyCount;
|
return indexBuffer.keyCount;
|
||||||
}
|
}
|
||||||
|
@ -236,8 +240,9 @@ class FixedIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() throws IOException {
|
public boolean hasNext() throws IOException {
|
||||||
if (hasNext)
|
if (hasNext) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
long key = indexIterator.next();
|
long key = indexIterator.next();
|
||||||
keyField = fieldType.newField();
|
keyField = fieldType.newField();
|
||||||
|
@ -253,8 +258,9 @@ class FixedIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPrevious() throws IOException {
|
public boolean hasPrevious() throws IOException {
|
||||||
if (hasPrev)
|
if (hasPrev) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
long key = indexIterator.previous();
|
long key = indexIterator.previous();
|
||||||
keyField = fieldType.newField();
|
keyField = fieldType.newField();
|
||||||
|
@ -297,14 +303,15 @@ class FixedIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean delete() throws IOException {
|
public boolean delete() throws IOException {
|
||||||
if (lastKey == null)
|
if (lastKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
synchronized (db) {
|
synchronized (db) {
|
||||||
IndexBuffer indexBuf = getIndexBuffer(lastKey);
|
IndexBuffer indexBuf = getIndexBuffer(lastKey);
|
||||||
if (indexBuf != null) {
|
if (indexBuf != null) {
|
||||||
long[] keys = indexBuf.getPrimaryKeys();
|
long[] keys = indexBuf.getPrimaryKeys();
|
||||||
for (int i = 0; i < keys.length; i++) {
|
for (long key : keys) {
|
||||||
primaryTable.deleteRecord(keys[i]);
|
primaryTable.deleteRecord(key);
|
||||||
}
|
}
|
||||||
// The following does not actually delete the index record since it
|
// The following does not actually delete the index record since it
|
||||||
// should already have been removed with the removal of all associated
|
// should already have been removed with the removal of all associated
|
||||||
|
|
|
@ -63,14 +63,15 @@ abstract class IndexTable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new or existing secondary index. An existing index must have
|
* Construct a new or existing secondary index. An existing index must have
|
||||||
* its' root ID specified within the tableRecord.
|
* its root ID specified within the tableRecord.
|
||||||
* @param primaryTable primary table.
|
* @param primaryTable primary table.
|
||||||
* @param indexTableRecord specifies the index parameters.
|
* @param indexTableRecord specifies the index parameters.
|
||||||
* @throws IOException thrown if IO error occurs
|
* @throws IOException thrown if IO error occurs
|
||||||
*/
|
*/
|
||||||
IndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException {
|
IndexTable(Table primaryTable, TableRecord indexTableRecord) throws IOException {
|
||||||
if (!primaryTable.useLongKeys())
|
if (!primaryTable.useLongKeys()) {
|
||||||
throw new AssertException("Only long-key tables may be indexed");
|
throw new AssertException("Only long-key tables may be indexed");
|
||||||
|
}
|
||||||
this.db = primaryTable.getDBHandle();
|
this.db = primaryTable.getDBHandle();
|
||||||
this.primaryTable = primaryTable;
|
this.primaryTable = primaryTable;
|
||||||
this.indexTableRecord = indexTableRecord;
|
this.indexTableRecord = indexTableRecord;
|
||||||
|
@ -90,8 +91,9 @@ abstract class IndexTable {
|
||||||
static IndexTable getIndexTable(DBHandle db, TableRecord indexTableRecord) throws IOException {
|
static IndexTable getIndexTable(DBHandle db, TableRecord indexTableRecord) throws IOException {
|
||||||
String name = indexTableRecord.getName();
|
String name = indexTableRecord.getName();
|
||||||
Table primaryTable = db.getTable(name);
|
Table primaryTable = db.getTable(name);
|
||||||
if (primaryTable == null)
|
if (primaryTable == null) {
|
||||||
throw new AssertException("Table not found: " + name);
|
throw new AssertException("Table not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
if (indexTableRecord.getSchema().getKeyFieldType() instanceof IndexField) {
|
if (indexTableRecord.getSchema().getKeyFieldType() instanceof IndexField) {
|
||||||
return new FieldIndexTable(primaryTable, indexTableRecord);
|
return new FieldIndexTable(primaryTable, indexTableRecord);
|
||||||
|
@ -111,8 +113,9 @@ abstract class IndexTable {
|
||||||
* @throws IOException thrown if IO error occurs
|
* @throws IOException thrown if IO error occurs
|
||||||
*/
|
*/
|
||||||
static IndexTable createIndexTable(Table primaryTable, int indexColumn) throws IOException {
|
static IndexTable createIndexTable(Table primaryTable, int indexColumn) throws IOException {
|
||||||
if (primaryTable.getRecordCount() != 0)
|
if (primaryTable.getRecordCount() != 0) {
|
||||||
throw new AssertException();
|
throw new AssertException();
|
||||||
|
}
|
||||||
return new FieldIndexTable(primaryTable, indexColumn);
|
return new FieldIndexTable(primaryTable, indexColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,8 +616,9 @@ abstract class IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean delete() throws IOException {
|
public boolean delete() throws IOException {
|
||||||
if (lastKey == null)
|
if (lastKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
synchronized (db) {
|
synchronized (db) {
|
||||||
long key = lastKey.getLongValue();
|
long key = lastKey.getLongValue();
|
||||||
primaryTable.deleteRecord(key);
|
primaryTable.deleteRecord(key);
|
||||||
|
|
|
@ -50,7 +50,7 @@ class VarIndexTable extends IndexTable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new or existing secondary index. An existing index must have
|
* Construct a new or existing secondary index. An existing index must have
|
||||||
* its' root ID specified within the tableRecord.
|
* its root ID specified within the tableRecord.
|
||||||
* @param primaryTable primary table.
|
* @param primaryTable primary table.
|
||||||
* @param indexTableRecord specifies the index parameters.
|
* @param indexTableRecord specifies the index parameters.
|
||||||
* @throws IOException thrown if an IO error occurs
|
* @throws IOException thrown if an IO error occurs
|
||||||
|
@ -69,11 +69,13 @@ class VarIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
long[] findPrimaryKeys(Field indexValue) throws IOException {
|
long[] findPrimaryKeys(Field indexValue) throws IOException {
|
||||||
if (!indexValue.getClass().equals(fieldType.getClass()))
|
if (!indexValue.getClass().equals(fieldType.getClass())) {
|
||||||
throw new IllegalArgumentException("Incorrect indexed field type");
|
throw new IllegalArgumentException("Incorrect indexed field type");
|
||||||
|
}
|
||||||
Record indexRecord = indexTable.getRecord(indexValue);
|
Record indexRecord = indexTable.getRecord(indexValue);
|
||||||
if (indexRecord == null)
|
if (indexRecord == null) {
|
||||||
return emptyKeyArray;
|
return emptyKeyArray;
|
||||||
|
}
|
||||||
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
||||||
return indexBuffer.getPrimaryKeys();
|
return indexBuffer.getPrimaryKeys();
|
||||||
}
|
}
|
||||||
|
@ -87,11 +89,13 @@ class VarIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
int getKeyCount(Field indexValue) throws IOException {
|
int getKeyCount(Field indexValue) throws IOException {
|
||||||
if (!indexValue.getClass().equals(fieldType.getClass()))
|
if (!indexValue.getClass().equals(fieldType.getClass())) {
|
||||||
throw new IllegalArgumentException("Incorrect indexed field type");
|
throw new IllegalArgumentException("Incorrect indexed field type");
|
||||||
|
}
|
||||||
Record indexRecord = indexTable.getRecord(indexValue);
|
Record indexRecord = indexTable.getRecord(indexValue);
|
||||||
if (indexRecord == null)
|
if (indexRecord == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
IndexBuffer indexBuffer = new IndexBuffer(indexValue, indexRecord.getBinaryData(0));
|
||||||
return indexBuffer.keyCount;
|
return indexBuffer.keyCount;
|
||||||
}
|
}
|
||||||
|
@ -237,11 +241,13 @@ class VarIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() throws IOException {
|
public boolean hasNext() throws IOException {
|
||||||
if (hasNext)
|
if (hasNext) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
Field key = indexIterator.next();
|
Field key = indexIterator.next();
|
||||||
if (key == null)
|
if (key == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
keyField = key;
|
keyField = key;
|
||||||
hasNext = true;
|
hasNext = true;
|
||||||
hasPrev = false;
|
hasPrev = false;
|
||||||
|
@ -250,11 +256,13 @@ class VarIndexTable extends IndexTable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPrevious() throws IOException {
|
public boolean hasPrevious() throws IOException {
|
||||||
if (hasPrev)
|
if (hasPrev) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
Field key = indexIterator.previous();
|
Field key = indexIterator.previous();
|
||||||
if (key == null)
|
if (key == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
keyField = key;
|
keyField = key;
|
||||||
hasNext = false;
|
hasNext = false;
|
||||||
hasPrev = true;
|
hasPrev = true;
|
||||||
|
@ -290,14 +298,15 @@ class VarIndexTable extends IndexTable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean delete() throws IOException {
|
public boolean delete() throws IOException {
|
||||||
if (lastKey == null)
|
if (lastKey == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
synchronized (db) {
|
synchronized (db) {
|
||||||
IndexBuffer indexBuf = getIndexBuffer(lastKey);
|
IndexBuffer indexBuf = getIndexBuffer(lastKey);
|
||||||
if (indexBuf != null) {
|
if (indexBuf != null) {
|
||||||
long[] keys = indexBuf.getPrimaryKeys();
|
long[] keys = indexBuf.getPrimaryKeys();
|
||||||
for (int i = 0; i < keys.length; i++) {
|
for (long key : keys) {
|
||||||
primaryTable.deleteRecord(keys[i]);
|
primaryTable.deleteRecord(key);
|
||||||
}
|
}
|
||||||
// The following does not actually delete the index record since it
|
// The following does not actually delete the index record since it
|
||||||
// should already have been removed with the removal of all associated
|
// should already have been removed with the removal of all associated
|
||||||
|
|
|
@ -207,8 +207,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
* @throws IOException if an I/O error occurs during file creation
|
* @throws IOException if an I/O error occurs during file creation
|
||||||
*/
|
*/
|
||||||
public LocalBufferFile(File file, int bufferSize) throws IOException {
|
public LocalBufferFile(File file, int bufferSize) throws IOException {
|
||||||
if (file.exists())
|
if (file.exists()) {
|
||||||
throw new DuplicateFileException("File " + file + " already exists");
|
throw new DuplicateFileException("File " + file + " already exists");
|
||||||
|
}
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.bufferSize = bufferSize;
|
this.bufferSize = bufferSize;
|
||||||
this.blockSize = bufferSize + BUFFER_PREFIX_SIZE;
|
this.blockSize = bufferSize + BUFFER_PREFIX_SIZE;
|
||||||
|
@ -293,8 +294,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
@Override
|
@Override
|
||||||
public int getParameter(String name) throws NoSuchElementException {
|
public int getParameter(String name) throws NoSuchElementException {
|
||||||
Object obj = userParms.get(name);
|
Object obj = userParms.get(name);
|
||||||
if (obj == null)
|
if (obj == null) {
|
||||||
throw new NoSuchElementException(name);
|
throw new NoSuchElementException(name);
|
||||||
|
}
|
||||||
return ((Integer) obj).intValue();
|
return ((Integer) obj).intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +411,7 @@ public class LocalBufferFile implements BufferFile {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set random access file (raf) position to the file block containing the specified buffer
|
* Set random access file (raf) position to the file block containing the specified buffer
|
||||||
* identified by its' bufferIndex. It is important to understand the distinction between
|
* identified by its bufferIndex. It is important to understand the distinction between
|
||||||
* blocks and buffers, where buffers are stored within file blocks which are slightly larger.
|
* blocks and buffers, where buffers are stored within file blocks which are slightly larger.
|
||||||
* In addition, the first file block stores the file header and is not used to store a buffer.
|
* In addition, the first file block stores the file header and is not used to store a buffer.
|
||||||
* @param bufferIndex buffer index
|
* @param bufferIndex buffer index
|
||||||
|
@ -449,24 +451,27 @@ public class LocalBufferFile implements BufferFile {
|
||||||
|
|
||||||
// Check magic number
|
// Check magic number
|
||||||
long magicNumber = raf.readLong();
|
long magicNumber = raf.readLong();
|
||||||
if (magicNumber != MAGIC_NUMBER)
|
if (magicNumber != MAGIC_NUMBER) {
|
||||||
throw new IOException("Unrecognized file format");
|
throw new IOException("Unrecognized file format");
|
||||||
|
}
|
||||||
|
|
||||||
// Read file ID
|
// Read file ID
|
||||||
fileId = raf.readLong();
|
fileId = raf.readLong();
|
||||||
|
|
||||||
// Check file format version
|
// Check file format version
|
||||||
int headerFormatVersion = raf.readInt();
|
int headerFormatVersion = raf.readInt();
|
||||||
if (headerFormatVersion != HEADER_FORMAT_VERSION)
|
if (headerFormatVersion != HEADER_FORMAT_VERSION) {
|
||||||
throw new IOException("Unrecognized file format");
|
throw new IOException("Unrecognized file format");
|
||||||
|
}
|
||||||
|
|
||||||
// Read buffer size, free buffer count, and first free buffer index
|
// Read buffer size, free buffer count, and first free buffer index
|
||||||
blockSize = raf.readInt();
|
blockSize = raf.readInt();
|
||||||
bufferSize = blockSize - BUFFER_PREFIX_SIZE;
|
bufferSize = blockSize - BUFFER_PREFIX_SIZE;
|
||||||
int firstFreeBufferIndex = raf.readInt();
|
int firstFreeBufferIndex = raf.readInt();
|
||||||
long len = raf.length();
|
long len = raf.length();
|
||||||
if ((len % blockSize) != 0)
|
if ((len % blockSize) != 0) {
|
||||||
throw new IOException("Corrupt file");
|
throw new IOException("Corrupt file");
|
||||||
|
}
|
||||||
bufferCount = (int) (len / blockSize) - 1;
|
bufferCount = (int) (len / blockSize) - 1;
|
||||||
|
|
||||||
// Read user-defined integer parameters values
|
// Read user-defined integer parameters values
|
||||||
|
@ -488,13 +493,13 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
private void writeHeader() throws IOException {
|
private void writeHeader() throws IOException {
|
||||||
|
|
||||||
if (readOnly)
|
if (readOnly) {
|
||||||
throw new IOException("File is read-only");
|
throw new IOException("File is read-only");
|
||||||
|
}
|
||||||
|
|
||||||
// Output free list
|
// Output free list
|
||||||
int prev = -1;
|
int prev = -1;
|
||||||
for (int i = 0; i < freeIndexes.length; i++) {
|
for (int index : freeIndexes) {
|
||||||
int index = freeIndexes[i];
|
|
||||||
putFreeBlock(index, prev);
|
putFreeBlock(index, prev);
|
||||||
prev = index;
|
prev = index;
|
||||||
}
|
}
|
||||||
|
@ -512,15 +517,15 @@ public class LocalBufferFile implements BufferFile {
|
||||||
String[] parmNames = getParameterNames();
|
String[] parmNames = getParameterNames();
|
||||||
raf.writeInt(parmNames.length);
|
raf.writeInt(parmNames.length);
|
||||||
int cnt = VER1_FIXED_HEADER_LENGTH;
|
int cnt = VER1_FIXED_HEADER_LENGTH;
|
||||||
for (int i = 0; i < parmNames.length; i++) {
|
for (String parmName : parmNames) {
|
||||||
byte[] nameBytes = parmNames[i].getBytes(STRING_ENCODING);
|
byte[] nameBytes = parmName.getBytes(STRING_ENCODING);
|
||||||
cnt += 8 + nameBytes.length;
|
cnt += 8 + nameBytes.length;
|
||||||
if (cnt > bufferSize) {
|
if (cnt > bufferSize) {
|
||||||
throw new IOException("Buffer size too small");
|
throw new IOException("Buffer size too small");
|
||||||
}
|
}
|
||||||
raf.writeInt(nameBytes.length);
|
raf.writeInt(nameBytes.length);
|
||||||
raf.write(nameBytes);
|
raf.write(nameBytes);
|
||||||
raf.writeInt(getParameter(parmNames[i]));
|
raf.writeInt(getParameter(parmName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,8 +629,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
|
|
||||||
byte[] data = buf.data;
|
byte[] data = buf.data;
|
||||||
boolean empty = buf.isEmpty();
|
boolean empty = buf.isEmpty();
|
||||||
if (!empty && data.length != bufferSize)
|
if (!empty && data.length != bufferSize) {
|
||||||
throw new IllegalArgumentException("Bad buffer size");
|
throw new IllegalArgumentException("Bad buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
int blockIndex = buf.getId() + 1;
|
int blockIndex = buf.getId() + 1;
|
||||||
|
|
||||||
|
@ -644,10 +650,12 @@ public class LocalBufferFile implements BufferFile {
|
||||||
@Override
|
@Override
|
||||||
public synchronized DataBuffer get(DataBuffer buf, int index) throws IOException {
|
public synchronized DataBuffer get(DataBuffer buf, int index) throws IOException {
|
||||||
|
|
||||||
if (index > bufferCount)
|
if (index > bufferCount) {
|
||||||
throw new EOFException("Buffer index too large (" + index + " > " + bufferCount + ")");
|
throw new EOFException("Buffer index too large (" + index + " > " + bufferCount + ")");
|
||||||
if (raf == null)
|
}
|
||||||
|
if (raf == null) {
|
||||||
throw new ClosedException();
|
throw new ClosedException();
|
||||||
|
}
|
||||||
|
|
||||||
seekBufferBlock(index);
|
seekBufferBlock(index);
|
||||||
|
|
||||||
|
@ -684,18 +692,22 @@ public class LocalBufferFile implements BufferFile {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void put(DataBuffer buf, int index) throws IOException {
|
public synchronized void put(DataBuffer buf, int index) throws IOException {
|
||||||
|
|
||||||
if (readOnly)
|
if (readOnly) {
|
||||||
throw new IOException("File is read-only");
|
throw new IOException("File is read-only");
|
||||||
if (raf == null)
|
}
|
||||||
|
if (raf == null) {
|
||||||
throw new ClosedException();
|
throw new ClosedException();
|
||||||
|
}
|
||||||
|
|
||||||
if (index > MAX_BUFFER_INDEX)
|
if (index > MAX_BUFFER_INDEX) {
|
||||||
throw new EOFException("Buffer index too large, exceeds max-int");
|
throw new EOFException("Buffer index too large, exceeds max-int");
|
||||||
|
}
|
||||||
|
|
||||||
byte[] data = buf.data;
|
byte[] data = buf.data;
|
||||||
boolean empty = buf.isEmpty();
|
boolean empty = buf.isEmpty();
|
||||||
if (!empty && data.length != bufferSize)
|
if (!empty && data.length != bufferSize) {
|
||||||
throw new IllegalArgumentException("Bad buffer size");
|
throw new IllegalArgumentException("Bad buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
seekBufferBlock(index);
|
seekBufferBlock(index);
|
||||||
|
|
||||||
|
@ -741,8 +753,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
void truncate(int indexCount) throws IOException {
|
void truncate(int indexCount) throws IOException {
|
||||||
|
|
||||||
if (readOnly)
|
if (readOnly) {
|
||||||
throw new IOException("File is read-only");
|
throw new IOException("File is read-only");
|
||||||
|
}
|
||||||
|
|
||||||
long size = (indexCount + 1) * blockSize;
|
long size = (indexCount + 1) * blockSize;
|
||||||
raf.setLength(size);
|
raf.setLength(size);
|
||||||
|
@ -756,8 +769,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
boolean flush() throws IOException {
|
boolean flush() throws IOException {
|
||||||
|
|
||||||
if (raf == null || readOnly || temporary)
|
if (raf == null || readOnly || temporary) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// write header
|
// write header
|
||||||
writeHeader();
|
writeHeader();
|
||||||
|
@ -824,8 +838,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean setReadOnly() throws IOException {
|
public synchronized boolean setReadOnly() throws IOException {
|
||||||
|
|
||||||
if (!flush())
|
if (!flush()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
raf.close();
|
raf.close();
|
||||||
raf = new RandomAccessFile(file, "r");
|
raf = new RandomAccessFile(file, "r");
|
||||||
|
@ -893,8 +908,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean delete() {
|
public synchronized boolean delete() {
|
||||||
|
|
||||||
if (raf == null || readOnly)
|
if (raf == null || readOnly) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -1376,11 +1392,13 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
public static void copyFile(BufferFile srcFile, BufferFile destFile, ChangeMap changeMap,
|
public static void copyFile(BufferFile srcFile, BufferFile destFile, ChangeMap changeMap,
|
||||||
TaskMonitor monitor) throws IOException, CancelledException {
|
TaskMonitor monitor) throws IOException, CancelledException {
|
||||||
if (destFile.isReadOnly())
|
if (destFile.isReadOnly()) {
|
||||||
throw new IOException("File is read-only");
|
throw new IOException("File is read-only");
|
||||||
|
}
|
||||||
|
|
||||||
if (srcFile.getBufferSize() != destFile.getBufferSize())
|
if (srcFile.getBufferSize() != destFile.getBufferSize()) {
|
||||||
throw new IOException("Buffer sizes differ");
|
throw new IOException("Buffer sizes differ");
|
||||||
|
}
|
||||||
|
|
||||||
if (monitor == null) {
|
if (monitor == null) {
|
||||||
monitor = TaskMonitorAdapter.DUMMY_MONITOR;
|
monitor = TaskMonitorAdapter.DUMMY_MONITOR;
|
||||||
|
@ -1405,8 +1423,7 @@ public class LocalBufferFile implements BufferFile {
|
||||||
if (headerTransferRequired) {
|
if (headerTransferRequired) {
|
||||||
destFile.clearParameters();
|
destFile.clearParameters();
|
||||||
String[] parmNames = srcFile.getParameterNames();
|
String[] parmNames = srcFile.getParameterNames();
|
||||||
for (int i = 0; i < parmNames.length; i++) {
|
for (String name : parmNames) {
|
||||||
String name = parmNames[i];
|
|
||||||
destFile.setParameter(name, srcFile.getParameter(name));
|
destFile.setParameter(name, srcFile.getParameter(name));
|
||||||
}
|
}
|
||||||
monitor.setProgress(srcBlockCnt + 1);
|
monitor.setProgress(srcBlockCnt + 1);
|
||||||
|
@ -1512,12 +1529,13 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
public static void cleanupOldPreSaveFiles(File dir, long beforeNow) {
|
public static void cleanupOldPreSaveFiles(File dir, long beforeNow) {
|
||||||
File[] oldFiles = dir.listFiles(new BufferFileFilter(null, PRESAVE_FILE_EXT));
|
File[] oldFiles = dir.listFiles(new BufferFileFilter(null, PRESAVE_FILE_EXT));
|
||||||
if (oldFiles == null)
|
if (oldFiles == null) {
|
||||||
return;
|
return;
|
||||||
for (int i = 0; i < oldFiles.length; i++) {
|
}
|
||||||
if ((beforeNow == 0 || oldFiles[i].lastModified() < beforeNow) &&
|
for (File oldFile : oldFiles) {
|
||||||
oldFiles[i].delete()) {
|
if ((beforeNow == 0 || oldFile.lastModified() < beforeNow) &&
|
||||||
Msg.info(LocalBufferFile.class, "Removed old presave file: " + oldFiles[i]);
|
oldFile.delete()) {
|
||||||
|
Msg.info(LocalBufferFile.class, "Removed old presave file: " + oldFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1529,8 +1547,9 @@ public class LocalBufferFile implements BufferFile {
|
||||||
*/
|
*/
|
||||||
static int getRecommendedBufferSize(int requestedBufferSize) {
|
static int getRecommendedBufferSize(int requestedBufferSize) {
|
||||||
int size = (requestedBufferSize + BUFFER_PREFIX_SIZE) & -MINIMUM_BLOCK_SIZE;
|
int size = (requestedBufferSize + BUFFER_PREFIX_SIZE) & -MINIMUM_BLOCK_SIZE;
|
||||||
if (size <= 0)
|
if (size <= 0) {
|
||||||
size = MINIMUM_BLOCK_SIZE;
|
size = MINIMUM_BLOCK_SIZE;
|
||||||
|
}
|
||||||
return size - BUFFER_PREFIX_SIZE;
|
return size - BUFFER_PREFIX_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,28 +17,33 @@ package ghidra.util;
|
||||||
|
|
||||||
public class UniversalID {
|
public class UniversalID {
|
||||||
private long id;
|
private long id;
|
||||||
|
|
||||||
public UniversalID(long id) {
|
public UniversalID(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getValue() {
|
public long getValue() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object obj ) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == this) {
|
if (obj == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (obj == null || !(obj instanceof UniversalID)) {
|
if (obj == null || !(obj instanceof UniversalID)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return ((UniversalID)obj).id == id;
|
return ((UniversalID) obj).id == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (int)id;
|
return (int) (id ^ (id >>> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Long.toString(id);
|
return Long.toString(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.datamgr.archive;
|
package ghidra.app.plugin.core.datamgr.archive;
|
||||||
|
|
||||||
import ghidra.program.model.data.ArchiveType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
|
|
||||||
public class BuiltInSourceArchive implements SourceArchive {
|
public class BuiltInSourceArchive implements SourceArchive {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import db.util.ErrorHandler;
|
import db.util.ErrorHandler;
|
||||||
import ghidra.framework.store.LockException;
|
|
||||||
import ghidra.program.database.*;
|
import ghidra.program.database.*;
|
||||||
import ghidra.program.database.data.DataTypeManagerDB;
|
import ghidra.program.database.data.DataTypeManagerDB;
|
||||||
import ghidra.program.database.map.*;
|
import ghidra.program.database.map.*;
|
||||||
|
@ -2140,7 +2139,7 @@ public class CodeManager implements ErrorHandler, ManagerDB {
|
||||||
}
|
}
|
||||||
if (dt instanceof Structure) {
|
if (dt instanceof Structure) {
|
||||||
Structure structDt = (Structure) dt;
|
Structure structDt = (Structure) dt;
|
||||||
for (DataTypeComponent component : structDt.getComponents()) {
|
for (DataTypeComponent component : structDt.getDefinedComponents()) {
|
||||||
if (containsAddressComponents(component.getDataType())) {
|
if (containsAddressComponents(component.getDataType())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,9 +184,6 @@ class ArrayDB extends DataTypeDB implements Array {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalent(DataType dt) {
|
public boolean isEquivalent(DataType dt) {
|
||||||
if (dt == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (dt == this) {
|
if (dt == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,12 +252,14 @@ class CategoryDB extends DatabaseObject implements Category {
|
||||||
mgr.lock.acquire();
|
mgr.lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
dt = dt.clone(dt.getDataTypeManager());
|
if (!getCategoryPath().equals(dt.getCategoryPath())) {
|
||||||
try {
|
dt = dt.clone(dt.getDataTypeManager());
|
||||||
dt.setCategoryPath(getCategoryPath());
|
try {
|
||||||
}
|
dt.setCategoryPath(getCategoryPath());
|
||||||
catch (DuplicateNameException e) {
|
}
|
||||||
// can't happen here because we made a copy
|
catch (DuplicateNameException e) {
|
||||||
|
// can't happen here because we made a copy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DataType resolvedDataType = mgr.resolve(dt, handler);
|
DataType resolvedDataType = mgr.resolve(dt, handler);
|
||||||
return resolvedDataType;
|
return resolvedDataType;
|
||||||
|
@ -427,6 +429,7 @@ class CategoryDB extends DatabaseObject implements Category {
|
||||||
@Override
|
@Override
|
||||||
public Category copyCategory(Category category, DataTypeConflictHandler handler,
|
public Category copyCategory(Category category, DataTypeConflictHandler handler,
|
||||||
TaskMonitor monitor) {
|
TaskMonitor monitor) {
|
||||||
|
// TODO: source archive handling is not documented
|
||||||
boolean isInSameArchive = (mgr == category.getDataTypeManager());
|
boolean isInSameArchive = (mgr == category.getDataTypeManager());
|
||||||
mgr.lock.acquire();
|
mgr.lock.acquire();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -45,11 +45,13 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for a composite data type (structure or union).
|
* Constructor for a composite data type (structure or union).
|
||||||
* @param dataMgr the data type manager containing this data type.
|
*
|
||||||
* @param cache DataTypeDB object cache
|
* @param dataMgr the data type manager containing this data type.
|
||||||
|
* @param cache DataTypeDB object cache
|
||||||
* @param compositeAdapter the database adapter for this data type.
|
* @param compositeAdapter the database adapter for this data type.
|
||||||
* @param componentAdapter the database adapter for the components of this data type.
|
* @param componentAdapter the database adapter for the components of this data
|
||||||
* @param record the database record for this data type.
|
* type.
|
||||||
|
* @param record the database record for this data type.
|
||||||
*/
|
*/
|
||||||
CompositeDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache,
|
CompositeDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache,
|
||||||
CompositeDBAdapter compositeAdapter, ComponentDBAdapter componentAdapter,
|
CompositeDBAdapter compositeAdapter, ComponentDBAdapter componentAdapter,
|
||||||
|
@ -61,19 +63,21 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform initialization of instance fields during instantiation
|
* Perform initialization of instance fields during instantiation or instance
|
||||||
* or instance refresh
|
* refresh
|
||||||
*/
|
*/
|
||||||
protected abstract void initialize();
|
protected abstract void initialize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the preferred length for a new component. For Unions and internally aligned
|
* Get the preferred length for a new component. For Unions and internally
|
||||||
* structures the preferred component length for a fixed-length dataType will be the
|
* aligned structures the preferred component length for a fixed-length dataType
|
||||||
* length of that dataType. Otherwise the length returned will be no larger than the
|
* will be the length of that dataType. Otherwise the length returned will be no
|
||||||
* specified length.
|
* larger than the specified length.
|
||||||
|
*
|
||||||
* @param dataType new component datatype
|
* @param dataType new component datatype
|
||||||
* @param length constrained length or -1 to force use of dataType size. Dynamic types
|
* @param length constrained length or -1 to force use of dataType size.
|
||||||
* such as string must have a positive length specified.
|
* Dynamic types such as string must have a positive length
|
||||||
|
* specified.
|
||||||
* @return preferred component length
|
* @return preferred component length
|
||||||
*/
|
*/
|
||||||
protected int getPreferredComponentLength(DataType dataType, int length) {
|
protected int getPreferredComponentLength(DataType dataType, int length) {
|
||||||
|
@ -106,12 +110,13 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle replacement of datatype which may impact bitfield datatype.
|
* Handle replacement of datatype which may impact bitfield datatype.
|
||||||
|
*
|
||||||
* @param bitfieldComponent bitfield component
|
* @param bitfieldComponent bitfield component
|
||||||
* @param oldDt affected datatype which has been removed or replaced
|
* @param oldDt affected datatype which has been removed or replaced
|
||||||
* @param newDt replacement datatype
|
* @param newDt replacement datatype
|
||||||
* @param true if bitfield component was modified
|
* @param true if bitfield component was modified
|
||||||
* @throws InvalidDataTypeException if bitfield was based upon oldDt but new datatype is
|
* @throws InvalidDataTypeException if bitfield was based upon oldDt but new
|
||||||
* invalid for a bitfield
|
* datatype is invalid for a bitfield
|
||||||
*/
|
*/
|
||||||
protected boolean updateBitFieldDataType(DataTypeComponentDB bitfieldComponent, DataType oldDt,
|
protected boolean updateBitFieldDataType(DataTypeComponentDB bitfieldComponent, DataType oldDt,
|
||||||
DataType newDt) throws InvalidDataTypeException {
|
DataType newDt) throws InvalidDataTypeException {
|
||||||
|
@ -252,24 +257,36 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method throws an exception if the indicated data type is an ancestor
|
* This method throws an exception if the indicated data type is an ancestor of
|
||||||
* of this data type. In other words, the specified data type has a component
|
* this data type. In other words, the specified data type has a component or
|
||||||
* or sub-component containing this data type.
|
* sub-component containing this data type.
|
||||||
|
*
|
||||||
* @param dataType the data type
|
* @param dataType the data type
|
||||||
* @throws IllegalArgumentException if the data type is an ancestor of this
|
* @throws DataTypeDependencyException if the data type is an ancestor of this
|
||||||
* data type.
|
* data type.
|
||||||
*/
|
*/
|
||||||
protected void checkAncestry(DataType dataType) {
|
protected void checkAncestry(DataType dataType) throws DataTypeDependencyException {
|
||||||
if (this.equals(dataType)) {
|
if (this.equals(dataType)) {
|
||||||
throw new IllegalArgumentException(
|
throw new DataTypeDependencyException(
|
||||||
"Data type " + getDisplayName() + " can't contain itself.");
|
"Data type " + getDisplayName() + " can't contain itself.");
|
||||||
}
|
}
|
||||||
else if (DataTypeUtilities.isSecondPartOfFirst(dataType, this)) {
|
else if (DataTypeUtilities.isSecondPartOfFirst(dataType, this)) {
|
||||||
throw new IllegalArgumentException("Data type " + dataType.getDisplayName() + " has " +
|
throw new DataTypeDependencyException("Data type " + dataType.getDisplayName() +
|
||||||
getDisplayName() + " within it.");
|
" has " + getDisplayName() + " within it.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected DataType doCheckedResolve(DataType dt, DataTypeConflictHandler handler)
|
||||||
|
throws DataTypeDependencyException {
|
||||||
|
if (dt instanceof Pointer) {
|
||||||
|
pointerPostResolveRequired = true;
|
||||||
|
return resolve(((Pointer) dt).newPointer(DataType.DEFAULT));
|
||||||
|
}
|
||||||
|
dt = resolve(dt, handler);
|
||||||
|
checkAncestry(dt);
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doSetNameRecord(String name) throws IOException {
|
protected void doSetNameRecord(String name) throws IOException {
|
||||||
record.setString(CompositeDBAdapter.COMPOSITE_NAME_COL, name);
|
record.setString(CompositeDBAdapter.COMPOSITE_NAME_COL, name);
|
||||||
|
@ -277,8 +294,9 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method throws an exception if the indicated data type is not
|
* This method throws an exception if the indicated data type is not a valid
|
||||||
* a valid data type for a component of this composite data type.
|
* data type for a component of this composite data type.
|
||||||
|
*
|
||||||
* @param dataType the data type to be checked.
|
* @param dataType the data type to be checked.
|
||||||
* @throws IllegalArgumentException if the data type is invalid.
|
* @throws IllegalArgumentException if the data type is invalid.
|
||||||
*/
|
*/
|
||||||
|
@ -576,6 +594,30 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract DataTypeComponentDB[] getDefinedComponents();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postPointerResolve(DataType definitionDt, DataTypeConflictHandler handler) {
|
||||||
|
Composite composite = (Composite) definitionDt;
|
||||||
|
DataTypeComponent[] definedComponents = composite.getDefinedComponents();
|
||||||
|
DataTypeComponentDB[] myDefinedComponents = getDefinedComponents();
|
||||||
|
if (definedComponents.length != myDefinedComponents.length) {
|
||||||
|
throw new IllegalArgumentException("mismatched definition datatype");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < definedComponents.length; i++) {
|
||||||
|
DataTypeComponent dtc = definedComponents[i];
|
||||||
|
DataType dt = dtc.getDataType();
|
||||||
|
if (dt instanceof Pointer) {
|
||||||
|
DataTypeComponentDB myDtc = myDefinedComponents[i];
|
||||||
|
myDtc.getDataType().removeParent(this);
|
||||||
|
dt = dataMgr.resolve(dt, handler);
|
||||||
|
myDtc.setDataType(dt);
|
||||||
|
dt.addParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that this composite data type's alignment has changed.
|
* Notification that this composite data type's alignment has changed.
|
||||||
*/
|
*/
|
||||||
|
@ -649,10 +691,12 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the internal alignment of components within this composite based on the current
|
* Adjusts the internal alignment of components within this composite based on
|
||||||
* settings of the internal alignment, packing, alignment type and minimum alignment value.
|
* the current settings of the internal alignment, packing, alignment type and
|
||||||
* This method should be called whenever any of the above settings are changed or whenever
|
* minimum alignment value. This method should be called whenever any of the
|
||||||
* a components data type is changed or a component is added or removed.
|
* above settings are changed or whenever a components data type is changed or a
|
||||||
|
* component is added or removed.
|
||||||
|
*
|
||||||
* @param notify
|
* @param notify
|
||||||
*/
|
*/
|
||||||
protected abstract void adjustInternalAlignment(boolean notify);
|
protected abstract void adjustInternalAlignment(boolean notify);
|
||||||
|
@ -665,11 +709,14 @@ abstract class CompositeDB extends DataTypeDB implements Composite {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump all components for use in {@link #toString()} representation.
|
* Dump all components for use in {@link #toString()} representation.
|
||||||
|
*
|
||||||
* @param buffer string buffer
|
* @param buffer string buffer
|
||||||
* @param pad padding to be used with each component output line
|
* @param pad padding to be used with each component output line
|
||||||
*/
|
*/
|
||||||
protected void dumpComponents(StringBuilder buffer, String pad) {
|
protected void dumpComponents(StringBuilder buffer, String pad) {
|
||||||
for (DataTypeComponent dtc : getComponents()) {
|
// limit output of filler components for unaligned structures
|
||||||
|
DataTypeComponent[] components = getDefinedComponents();
|
||||||
|
for (DataTypeComponent dtc : components) {
|
||||||
DataType dataType = dtc.getDataType();
|
DataType dataType = dtc.getDataType();
|
||||||
buffer.append(pad + dtc.getOffset());
|
buffer.append(pad + dtc.getOffset());
|
||||||
buffer.append(pad + dataType.getName());
|
buffer.append(pad + dataType.getName());
|
||||||
|
|
|
@ -128,6 +128,9 @@ class CompositeDBAdapterV2V3 extends CompositeDBAdapter {
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
throw new ReadOnlyException();
|
throw new ReadOnlyException();
|
||||||
}
|
}
|
||||||
|
if (internalAlignment == UNALIGNED) {
|
||||||
|
length = 0; // aligned structures always start empty
|
||||||
|
}
|
||||||
long tableKey = compositeTable.getKey();
|
long tableKey = compositeTable.getKey();
|
||||||
// if (tableKey <= DataManager.VOID_DATATYPE_ID) {
|
// if (tableKey <= DataManager.VOID_DATATYPE_ID) {
|
||||||
// tableKey = DataManager.VOID_DATATYPE_ID +1;
|
// tableKey = DataManager.VOID_DATATYPE_ID +1;
|
||||||
|
|
|
@ -30,7 +30,6 @@ import docking.framework.DockingApplicationConfiguration;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
import ghidra.GhidraApplicationLayout;
|
import ghidra.GhidraApplicationLayout;
|
||||||
import ghidra.GhidraLaunchable;
|
import ghidra.GhidraLaunchable;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.ApplicationConfiguration;
|
import ghidra.framework.ApplicationConfiguration;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -472,8 +471,7 @@ public class DataTypeArchiveTransformer implements GhidraLaunchable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataTypeComponent getNamedComponent(Composite composite, String fieldName) {
|
private static DataTypeComponent getNamedComponent(Composite composite, String fieldName) {
|
||||||
DataTypeComponent[] components = composite.getComponents();
|
for (DataTypeComponent dataTypeComponent : composite.getDefinedComponents()) {
|
||||||
for (DataTypeComponent dataTypeComponent : components) {
|
|
||||||
if (fieldName.equals(dataTypeComponent.getFieldName())) {
|
if (fieldName.equals(dataTypeComponent.getFieldName())) {
|
||||||
return dataTypeComponent; // found match so return it.
|
return dataTypeComponent; // found match so return it.
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
import db.Record;
|
import db.Record;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.docking.settings.SettingsDefinition;
|
import ghidra.docking.settings.SettingsDefinition;
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DBObjectCache;
|
||||||
|
@ -46,6 +45,7 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
private volatile Settings defaultSettings;
|
private volatile Settings defaultSettings;
|
||||||
private final static SettingsDefinition[] EMPTY_DEFINITIONS = new SettingsDefinition[0];
|
private final static SettingsDefinition[] EMPTY_DEFINITIONS = new SettingsDefinition[0];
|
||||||
protected boolean resolving;
|
protected boolean resolving;
|
||||||
|
protected boolean pointerPostResolveRequired;
|
||||||
protected Lock lock;
|
protected Lock lock;
|
||||||
private volatile String name;
|
private volatile String name;
|
||||||
private volatile Category category;
|
private volatile Category category;
|
||||||
|
@ -64,41 +64,46 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to either read the name from the database record or compute
|
* Subclasses implement this to either read the name from the database record or
|
||||||
* if it is a derived name such as a pointer or array. Implementers can assume that
|
* compute if it is a derived name such as a pointer or array. Implementers can
|
||||||
* the database lock will be acquired when this method is called.
|
* assume that the database lock will be acquired when this method is called.
|
||||||
*/
|
*/
|
||||||
protected abstract String doGetName();
|
protected abstract String doGetName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to read the category path from the database record.Implementers can assume that
|
* Subclasses implement this to read the category path from the database
|
||||||
* the database lock will be acquired when this method is called.
|
* record.Implementers can assume that the database lock will be acquired when
|
||||||
|
* this method is called.
|
||||||
*/
|
*/
|
||||||
protected abstract long doGetCategoryID();
|
protected abstract long doGetCategoryID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to update the category path ID to the database. Implementers can assume that
|
* Subclasses implement this to update the category path ID to the database.
|
||||||
* the database lock will be acquired when this method is called.
|
* Implementers can assume that the database lock will be acquired when this
|
||||||
|
* method is called.
|
||||||
*/
|
*/
|
||||||
protected abstract void doSetCategoryPathRecord(long categoryID) throws IOException;
|
protected abstract void doSetCategoryPathRecord(long categoryID) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to update the to the database. Implementers can assume that
|
* Subclasses implement this to update the to the database. Implementers can
|
||||||
* the database lock will be acquired when this method is called.
|
* assume that the database lock will be acquired when this method is called.
|
||||||
|
*
|
||||||
* @param newName new data type name
|
* @param newName new data type name
|
||||||
*/
|
*/
|
||||||
protected abstract void doSetNameRecord(String newName)
|
protected abstract void doSetNameRecord(String newName)
|
||||||
throws IOException, InvalidNameException;
|
throws IOException, InvalidNameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to read the source archive id from the record. Implementers can assume that
|
* Subclasses implement this to read the source archive id from the record.
|
||||||
* the database lock will be acquired when this method is called.
|
* Implementers can assume that the database lock will be acquired when this
|
||||||
|
* method is called.
|
||||||
*/
|
*/
|
||||||
protected abstract UniversalID getSourceArchiveID();
|
protected abstract UniversalID getSourceArchiveID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses implement this to update the source archive id from the record. Implementers can assume that
|
* Subclasses implement this to update the source archive id from the record.
|
||||||
* the database lock will be acquired when this method is called.
|
* Implementers can assume that the database lock will be acquired when this
|
||||||
|
* method is called.
|
||||||
*/
|
*/
|
||||||
protected abstract void setSourceArchiveID(UniversalID id);
|
protected abstract void setSourceArchiveID(UniversalID id);
|
||||||
|
|
||||||
|
@ -145,9 +150,9 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current name without refresh.
|
* Get the current name without refresh. This is intended to be used for event
|
||||||
* This is intended to be used for event generation when an old-name
|
* generation when an old-name is needed.
|
||||||
* is needed.
|
*
|
||||||
* @return old name
|
* @return old name
|
||||||
*/
|
*/
|
||||||
protected final String getOldName() {
|
protected final String getOldName() {
|
||||||
|
@ -177,13 +182,12 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the data in the form of the appropriate Object for
|
* Set the data in the form of the appropriate Object for this DataType.
|
||||||
* this DataType.
|
|
||||||
*
|
*
|
||||||
* @param buf the data buffer.
|
* @param buf the data buffer.
|
||||||
* @param settings the display settings for the current value.
|
* @param settings the display settings for the current value.
|
||||||
* @param length the number of bytes to set the value from.
|
* @param length the number of bytes to set the value from.
|
||||||
* @param value the new value to set object
|
* @param value the new value to set object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setValue(MemBuffer buf, Settings settings, int length, Object value) {
|
public void setValue(MemBuffer buf, Settings settings, int length, Object value) {
|
||||||
|
@ -219,9 +223,7 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged(ChangeEvent e) {
|
public void stateChanged(ChangeEvent e) {
|
||||||
if (dataMgr != null) {
|
dataMgr.dataTypeChanged(this);
|
||||||
dataMgr.dataTypeChanged(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,21 +268,26 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataType resolve(DataType dt) {
|
protected DataType resolve(DataType dt) {
|
||||||
return resolve(dt, null);
|
return resolve(dt, dataMgr.getCurrentConflictHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataType resolve(DataType dt, DataTypeConflictHandler handler) {
|
protected DataType resolve(DataType dt, DataTypeConflictHandler handler) {
|
||||||
if (dataMgr != null) {
|
// complex types should keep equivalence checks to a minimum while resolving
|
||||||
resolving = true;
|
// and when post-resolve required for pointers
|
||||||
|
resolving = true;
|
||||||
|
try {
|
||||||
dt = dataMgr.resolve(dt, handler);
|
dt = dataMgr.resolve(dt, handler);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
resolving = false;
|
resolving = false;
|
||||||
}
|
}
|
||||||
return dt;
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected void postPointerResolve(DataType definitionDt, DataTypeConflictHandler handler) {
|
||||||
* @see ghidra.program.model.data.DataType#getCategoryPath()
|
throw new UnsupportedOperationException("post-resolve of pointers not implemented");
|
||||||
*/
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CategoryPath getCategoryPath() {
|
public CategoryPath getCategoryPath() {
|
||||||
validate(lock);
|
validate(lock);
|
||||||
|
@ -374,9 +381,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.data.DataType#setNameAndCategory(ghidra.program.model.data.CategoryPath, java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setNameAndCategory(CategoryPath path, String name)
|
public void setNameAndCategory(CategoryPath path, String name)
|
||||||
throws InvalidNameException, DuplicateNameException {
|
throws InvalidNameException, DuplicateNameException {
|
||||||
|
@ -388,12 +392,14 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
"DataType named " + name + " already exists in category " + path.getPath());
|
"DataType named " + name + " already exists in category " + path.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a name that would not cause a duplicate in either the current path or
|
// generate a name that would not cause a duplicate in either the current path
|
||||||
// the new path. Use the new name if possible.
|
// or
|
||||||
|
// the new path. Use the new name if possible.
|
||||||
String uniqueName = dataMgr.getUniqueName(path, getCategoryPath(), name);
|
String uniqueName = dataMgr.getUniqueName(path, getCategoryPath(), name);
|
||||||
doSetName(uniqueName);
|
doSetName(uniqueName);
|
||||||
|
|
||||||
// set the path - this is guaranteed to work since we make a name that won't conflict
|
// set the path - this is guaranteed to work since we make a name that won't
|
||||||
|
// conflict
|
||||||
doSetCategoryPath(path);
|
doSetCategoryPath(path);
|
||||||
|
|
||||||
// now, if necessary, rename it to the desired name - guaranteed to work since
|
// now, if necessary, rename it to the desired name - guaranteed to work since
|
||||||
|
@ -409,15 +415,13 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the path for this datatype
|
* Updates the path for this datatype
|
||||||
|
*
|
||||||
* @param dt the dataType whose path has changed.
|
* @param dt the dataType whose path has changed.
|
||||||
*/
|
*/
|
||||||
protected void updatePath(DataTypeDB dt) {
|
protected void updatePath(DataTypeDB dt) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.data.DataType#addParent(ghidra.program.model.data.DataType)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void addParent(DataType dt) {
|
public void addParent(DataType dt) {
|
||||||
if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) {
|
if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) {
|
||||||
|
@ -425,9 +429,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.data.DataType#removeParent(ghidra.program.model.data.DataType)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void removeParent(DataType dt) {
|
public void removeParent(DataType dt) {
|
||||||
if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) {
|
if (dt instanceof DataTypeDB && dt.getDataTypeManager() == dataMgr) {
|
||||||
|
@ -455,9 +456,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.data.DataType#getParents()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public DataType[] getParents() {
|
public DataType[] getParents() {
|
||||||
List<DataType> parents = dataMgr.getParentDataTypes(key);
|
List<DataType> parents = dataMgr.getParentDataTypes(key);
|
||||||
|
@ -465,9 +463,6 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
return parents.toArray(array);
|
return parents.toArray(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dependsOn(DataType dt) {
|
public boolean dependsOn(DataType dt) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -522,9 +517,10 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a String briefly describing this DataType.
|
* Sets a String briefly describing this DataType. <br>
|
||||||
* <br>If a data type that extends this class wants to allow the description to be changed,
|
* If a data type that extends this class wants to allow the description to be
|
||||||
* then it must override this method.
|
* changed, then it must override this method.
|
||||||
|
*
|
||||||
* @param description a one-liner describing this DataType.
|
* @param description a one-liner describing this DataType.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -533,11 +529,15 @@ abstract class DataTypeDB extends DatabaseObject implements DataType, ChangeList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setUniversalID is a package level method that allows you to change a data type's
|
* setUniversalID is a package level method that allows you to change a data
|
||||||
* universal ID. This is only intended to be used when transforming a newly parsed data type
|
* type's universal ID. This is only intended to be used when transforming a
|
||||||
* archive so that it can be used as a replacement of the archive from a previous software release.
|
* newly parsed data type archive so that it can be used as a replacement of the
|
||||||
* @param oldUniversalID the old universal ID value that the user is already referencing
|
* archive from a previous software release.
|
||||||
* with their data types. This is the universal ID that we want the new data type to be known by.
|
*
|
||||||
|
* @param oldUniversalID the old universal ID value that the user is already
|
||||||
|
* referencing with their data types. This is the
|
||||||
|
* universal ID that we want the new data type to be known
|
||||||
|
* by.
|
||||||
*/
|
*/
|
||||||
abstract void setUniversalID(UniversalID oldUniversalID);
|
abstract void setUniversalID(UniversalID oldUniversalID);
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,7 +18,6 @@ package ghidra.program.database.data;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.model.address.GlobalNamespace;
|
import ghidra.program.model.address.GlobalNamespace;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -157,30 +156,37 @@ public class DataTypeUtilities {
|
||||||
if (firstDataType.equals(secondDataType)) {
|
if (firstDataType.equals(secondDataType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (firstDataType instanceof Array) {
|
if (firstDataType instanceof Array) {
|
||||||
DataType elementDataType = ((Array) firstDataType).getDataType();
|
DataType elementDataType = ((Array) firstDataType).getDataType();
|
||||||
return isSecondPartOfFirst(elementDataType, secondDataType);
|
return isSecondPartOfFirst(elementDataType, secondDataType);
|
||||||
}
|
}
|
||||||
else if (firstDataType instanceof TypeDef) {
|
if (firstDataType instanceof TypeDef) {
|
||||||
DataType innerDataType = ((TypeDef) firstDataType).getDataType();
|
DataType innerDataType = ((TypeDef) firstDataType).getDataType();
|
||||||
return isSecondPartOfFirst(innerDataType, secondDataType);
|
return isSecondPartOfFirst(innerDataType, secondDataType);
|
||||||
}
|
}
|
||||||
else if (firstDataType instanceof Composite) {
|
if (firstDataType instanceof Composite) {
|
||||||
Composite compositeDataType = (Composite) firstDataType;
|
Composite compositeDataType = (Composite) firstDataType;
|
||||||
int numComponents = compositeDataType.getNumComponents();
|
for (DataTypeComponent dtc : compositeDataType.getDefinedComponents()) {
|
||||||
for (int i = 0; i < numComponents; i++) {
|
|
||||||
DataTypeComponent dtc = compositeDataType.getComponent(i);
|
|
||||||
DataType dataTypeToCheck = dtc.getDataType();
|
DataType dataTypeToCheck = dtc.getDataType();
|
||||||
if (isSecondPartOfFirst(dataTypeToCheck, secondDataType)) {
|
if (isSecondPartOfFirst(dataTypeToCheck, secondDataType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (firstDataType instanceof Structure) {
|
||||||
|
DataTypeComponent flexDtc = ((Structure) firstDataType).getFlexibleArrayComponent();
|
||||||
|
if (flexDtc != null && isSecondPartOfFirst(flexDtc.getDataType(), secondDataType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID.
|
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID
|
||||||
|
* @param dataType1 first data type
|
||||||
|
* @param dataType2 second data type
|
||||||
|
* @return true if types correspond to the same type from a source archive
|
||||||
*/
|
*/
|
||||||
public static boolean isSameDataType(DataType dataType1, DataType dataType2) {
|
public static boolean isSameDataType(DataType dataType1, DataType dataType2) {
|
||||||
UniversalID id1 = dataType1.getUniversalID();
|
UniversalID id1 = dataType1.getUniversalID();
|
||||||
|
@ -203,10 +209,15 @@ public class DataTypeUtilities {
|
||||||
/**
|
/**
|
||||||
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR
|
* Returns true if the two dataTypes have the same sourceArchive and the same UniversalID OR
|
||||||
* are equivalent
|
* are equivalent
|
||||||
|
* @param dataType1 first data type (if invoked by DB object or manager, this argument
|
||||||
|
* must correspond to the DataTypeDB).
|
||||||
|
* @param dataType2 second data type
|
||||||
|
* @return true if types correspond to the same type from a source archive
|
||||||
|
* or they are equivelent, otherwise false
|
||||||
*/
|
*/
|
||||||
public static boolean isSameOrEquivalentDataType(DataType dataType1, DataType dataType2) {
|
public static boolean isSameOrEquivalentDataType(DataType dataType1, DataType dataType2) {
|
||||||
// if they contain datatypes that have same ids, then they represent the same dataType
|
// if they contain datatypes that have same ids, then they represent the same dataType
|
||||||
if (DataTypeUtilities.isSameDataType(dataType1, dataType2)) {
|
if (isSameDataType(dataType1, dataType2)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// otherwise, check if they are equivalent
|
// otherwise, check if they are equivalent
|
||||||
|
|
|
@ -46,7 +46,7 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
private List<BitGroup> bitGroups;
|
private List<BitGroup> bitGroups;
|
||||||
|
|
||||||
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter,
|
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter,
|
||||||
EnumValueDBAdapter valueAdapter, Record record) throws IOException {
|
EnumValueDBAdapter valueAdapter, Record record) {
|
||||||
super(dataMgr, cache, record);
|
super(dataMgr, cache, record);
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
this.valueAdapter = valueAdapter;
|
this.valueAdapter = valueAdapter;
|
||||||
|
@ -86,8 +86,8 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
|
|
||||||
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
||||||
|
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (long id : ids) {
|
||||||
Record rec = valueAdapter.getRecord(ids[i]);
|
Record rec = valueAdapter.getRecord(id);
|
||||||
String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL);
|
String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL);
|
||||||
long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL);
|
long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL);
|
||||||
addToCache(valueName, value);
|
addToCache(valueName, value);
|
||||||
|
@ -252,10 +252,10 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
|
|
||||||
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
||||||
|
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (long id : ids) {
|
||||||
Record rec = valueAdapter.getRecord(ids[i]);
|
Record rec = valueAdapter.getRecord(id);
|
||||||
if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) {
|
if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) {
|
||||||
valueAdapter.removeRecord(ids[i]);
|
valueAdapter.removeRecord(id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,8 +285,8 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
valueMap = new HashMap<>();
|
valueMap = new HashMap<>();
|
||||||
|
|
||||||
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
long[] ids = valueAdapter.getValueIdsInEnum(key);
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (long id : ids) {
|
||||||
valueAdapter.removeRecord(ids[i]);
|
valueAdapter.removeRecord(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int oldLength = getLength();
|
int oldLength = getLength();
|
||||||
|
@ -298,11 +298,11 @@ class EnumDB extends DataTypeDB implements Enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] names = enumm.getNames();
|
String[] names = enumm.getNames();
|
||||||
for (int i = 0; i < names.length; i++) {
|
for (String name2 : names) {
|
||||||
long value = enumm.getValue(names[i]);
|
long value = enumm.getValue(name2);
|
||||||
valueAdapter.createRecord(key, names[i], value);
|
valueAdapter.createRecord(key, name2, value);
|
||||||
adapter.updateRecord(record, true);
|
adapter.updateRecord(record, true);
|
||||||
addToCache(names[i], value);
|
addToCache(name2, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldLength != newLength) {
|
if (oldLength != newLength) {
|
||||||
|
|
|
@ -177,15 +177,24 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doReplaceWith(FunctionDefinition functionDefinition) {
|
private void doReplaceWith(FunctionDefinition functionDefinition) {
|
||||||
setArguments(functionDefinition.getArguments());
|
|
||||||
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
setReturnType(functionDefinition.getReturnType());
|
checkDeleted();
|
||||||
|
|
||||||
|
setArguments(functionDefinition.getArguments());
|
||||||
|
try {
|
||||||
|
setReturnType(functionDefinition.getReturnType());
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
setReturnType(DEFAULT);
|
||||||
|
}
|
||||||
|
setVarArgs(functionDefinition.hasVarArgs());
|
||||||
|
setGenericCallingConvention(functionDefinition.getGenericCallingConvention());
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
finally {
|
||||||
setReturnType(DEFAULT);
|
lock.release();
|
||||||
}
|
}
|
||||||
setVarArgs(functionDefinition.hasVarArgs());
|
|
||||||
setGenericCallingConvention(functionDefinition.getGenericCallingConvention());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -251,7 +260,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
DataType type =
|
DataType type =
|
||||||
ParameterDefinitionImpl.validateDataType(args[i].getDataType(), dataMgr, false);
|
ParameterDefinitionImpl.validateDataType(args[i].getDataType(), dataMgr, false);
|
||||||
DataType resolvedDt = resolve(type);
|
DataType resolvedDt = resolve(type, dataMgr.getCurrentConflictHandler());
|
||||||
paramAdapter.createRecord(dataMgr.getID(resolvedDt), key, i, args[i].getName(),
|
paramAdapter.createRecord(dataMgr.getID(resolvedDt), key, i, args[i].getName(),
|
||||||
args[i].getComment(), args[i].getLength());
|
args[i].getComment(), args[i].getLength());
|
||||||
resolvedDt.addParent(this);
|
resolvedDt.addParent(this);
|
||||||
|
@ -278,7 +287,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
type = DataType.DEFAULT;
|
type = DataType.DEFAULT;
|
||||||
}
|
}
|
||||||
DataType resolvedDt = resolve(type);
|
DataType resolvedDt = resolve(type, dataMgr.getCurrentConflictHandler());
|
||||||
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_RETURN_ID_COL,
|
record.setLongValue(FunctionDefinitionDBAdapter.FUNCTION_DEF_RETURN_ID_COL,
|
||||||
dataMgr.getID(resolvedDt));
|
dataMgr.getID(resolvedDt));
|
||||||
funDefAdapter.updateRecord(record, true);
|
funDefAdapter.updateRecord(record, true);
|
||||||
|
@ -332,22 +341,35 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalent(DataType dt) {
|
public boolean isEquivalent(DataType dataType) {
|
||||||
if (dt == this) {
|
|
||||||
|
if (dataType == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(dt instanceof FunctionDefinition)) {
|
if (!(dataType instanceof FunctionDefinition)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
if (resolving) {
|
if (resolving) { // actively resolving children
|
||||||
if (dt.getUniversalID().equals(getUniversalID())) {
|
if (dataType.getUniversalID().equals(getUniversalID())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dt.getPathName());
|
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dataType.getPathName());
|
||||||
}
|
}
|
||||||
return isEquivalentSignature((FunctionSignature) dt);
|
|
||||||
|
Boolean isEquivalent = dataMgr.getCachedEquivalence(this, dataType);
|
||||||
|
if (isEquivalent != null) {
|
||||||
|
return isEquivalent;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
isEquivalent = isEquivalentSignature((FunctionSignature) dataType);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dataMgr.putCachedEquivalence(this, dataType, isEquivalent);
|
||||||
|
}
|
||||||
|
return isEquivalent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -360,15 +382,15 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
if ((DataTypeUtilities.equalsIgnoreConflict(signature.getName(), getName())) &&
|
if ((DataTypeUtilities.equalsIgnoreConflict(signature.getName(), getName())) &&
|
||||||
((comment == null && myComment == null) ||
|
((comment == null && myComment == null) ||
|
||||||
(comment != null && comment.equals(myComment))) &&
|
(comment != null && comment.equals(myComment))) &&
|
||||||
(DataTypeUtilities.isSameOrEquivalentDataType(signature.getReturnType(),
|
(DataTypeUtilities.isSameOrEquivalentDataType(getReturnType(),
|
||||||
getReturnType())) &&
|
signature.getReturnType())) &&
|
||||||
(getGenericCallingConvention() == signature.getGenericCallingConvention()) &&
|
(getGenericCallingConvention() == signature.getGenericCallingConvention()) &&
|
||||||
(hasVarArgs() == signature.hasVarArgs())) {
|
(hasVarArgs() == signature.hasVarArgs())) {
|
||||||
ParameterDefinition[] args = signature.getArguments();
|
ParameterDefinition[] args = signature.getArguments();
|
||||||
ParameterDefinition[] thisArgs = this.getArguments();
|
ParameterDefinition[] thisArgs = this.getArguments();
|
||||||
if (args.length == thisArgs.length) {
|
if (args.length == thisArgs.length) {
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
if (!args[i].isEquivalent(thisArgs[i])) {
|
if (!thisArgs[i].isEquivalent(args[i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,7 +472,7 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
param.getDataType().removeParent(this);
|
param.getDataType().removeParent(this);
|
||||||
paramAdapter.removeRecord(param.getKey());
|
paramAdapter.removeRecord(param.getKey());
|
||||||
}
|
}
|
||||||
DataType rdt = resolve(dt);
|
DataType rdt = resolve(dt, dataMgr.getCurrentConflictHandler());
|
||||||
rdt.addParent(this);
|
rdt.addParent(this);
|
||||||
paramAdapter.createRecord(dataMgr.getID(rdt), key, ordinal, name, comment,
|
paramAdapter.createRecord(dataMgr.getID(rdt), key, ordinal, name, comment,
|
||||||
dt.getLength());
|
dt.getLength());
|
||||||
|
@ -652,4 +674,9 @@ class FunctionDefinitionDB extends DataTypeDB implements FunctionDefinition {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getPrototypeString(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,14 +40,15 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>isEquivalentActive</code> is used to break cyclical recursion
|
* <code>isEquivalentActive</code> is used to break cyclical recursion when
|
||||||
* when performing an {@link #isEquivalent(DataType)} checks on pointers
|
* performing an {@link #isEquivalent(DataType)} checks on pointers which must
|
||||||
* which must also check the base datatype equivelency.
|
* also check the base datatype equivelency.
|
||||||
*/
|
*/
|
||||||
private ThreadLocal<Boolean> isEquivalentActive = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
private ThreadLocal<Boolean> isEquivalentActive = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
*
|
||||||
* @param dataMgr
|
* @param dataMgr
|
||||||
* @param cache
|
* @param cache
|
||||||
* @param adapter
|
* @param adapter
|
||||||
|
@ -307,6 +308,15 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: The pointer deep-dive equivalence checking on the referenced datatype can
|
||||||
|
// cause types containing pointers (composites, functions) to conflict when in
|
||||||
|
// reality the referenced type simply has multiple implementations which differ.
|
||||||
|
// Although without doing this Ghidra may fail to resolve dependencies which differ
|
||||||
|
// from those already contained within a datatype manager.
|
||||||
|
// Ghidra's rigid datatype relationships prevent the flexibility to handle
|
||||||
|
// multiple implementations of a named datatype without inducing a conflicted
|
||||||
|
// datatype hierarchy.
|
||||||
|
|
||||||
if (isEquivalentActive.get()) {
|
if (isEquivalentActive.get()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -359,11 +369,12 @@ class PointerDB extends DataTypeDB implements Pointer {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.data.DataType#setCategoryPath(ghidra.program.model.data.CategoryPath)
|
* @see ghidra.program.model.data.DataType#setCategoryPath(ghidra.program.model.data.CategoryPath)
|
||||||
*
|
*
|
||||||
* Note: this does get called, but in a tricky way. If externally, someone calls
|
* Note: this does get called, but in a tricky way. If externally, someone
|
||||||
* setCategoryPath, nothing happens because it is overridden in this class to do nothing.
|
* calls setCategoryPath, nothing happens because it is overridden in this
|
||||||
* However, if updatePath is called, then this method calls super.setCategoryPath which
|
* class to do nothing. However, if updatePath is called, then this method
|
||||||
* bypasses the "overriddenness" of setCategoryPath, resulting in this method getting called.
|
* calls super.setCategoryPath which bypasses the "overriddenness" of
|
||||||
|
* setCategoryPath, resulting in this method getting called.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void doSetCategoryPathRecord(long categoryID) throws IOException {
|
protected void doSetCategoryPathRecord(long categoryID) throws IOException {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.database.data;
|
package ghidra.program.database.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,8 @@ import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import db.*;
|
import db.*;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
import ghidra.program.model.data.DataTypeManager;
|
||||||
|
import ghidra.program.model.data.SourceArchive;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,9 @@ package ghidra.program.database.data;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import db.Record;
|
import db.Record;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.program.database.DBObjectCache;
|
import ghidra.program.database.DBObjectCache;
|
||||||
import ghidra.program.database.DatabaseObject;
|
import ghidra.program.database.DatabaseObject;
|
||||||
import ghidra.program.model.data.ArchiveType;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.util.Lock;
|
import ghidra.util.Lock;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,7 @@ package ghidra.program.database.data;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.ArchiveType;
|
|
||||||
import ghidra.program.model.data.DataTypeManager;
|
|
||||||
import ghidra.program.model.lang.CompilerSpec;
|
import ghidra.program.model.lang.CompilerSpec;
|
||||||
import ghidra.program.model.lang.CompilerSpecID;
|
import ghidra.program.model.lang.CompilerSpecID;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
|
|
|
@ -26,7 +26,6 @@ import ghidra.program.model.data.AlignedStructurePacker.StructurePackResult;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.InvalidInputException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structure implementation for the Database.
|
* Structure implementation for the Database.
|
||||||
|
@ -47,6 +46,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
*
|
||||||
* @param dataMgr
|
* @param dataMgr
|
||||||
* @param cache
|
* @param cache
|
||||||
* @param compositeAdapter
|
* @param compositeAdapter
|
||||||
|
@ -96,73 +96,59 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent add(DataType dataType, int length, String name, String comment) {
|
public DataTypeComponent add(DataType dataType, int length, String name, String comment)
|
||||||
return doAdd(dataType, length, false, name, comment, true);
|
throws IllegalArgumentException {
|
||||||
|
try {
|
||||||
|
return doAdd(dataType, length, name, comment, true);
|
||||||
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeComponent doAdd(DataType dataType, int length, boolean isFlexibleArray,
|
private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment,
|
||||||
String name, String comment, boolean alignAndNotify) {
|
boolean validateAlignAndNotify)
|
||||||
|
throws DataTypeDependencyException, IllegalArgumentException {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
|
||||||
validateDataType(dataType);
|
if (validateAlignAndNotify) {
|
||||||
|
validateDataType(dataType);
|
||||||
dataType = resolve(dataType);
|
dataType = resolve(dataType, null);
|
||||||
checkAncestry(dataType);
|
checkAncestry(dataType);
|
||||||
|
}
|
||||||
|
|
||||||
DataTypeComponentDB dtc = null;
|
DataTypeComponentDB dtc = null;
|
||||||
try {
|
try {
|
||||||
if (dataType == DataType.DEFAULT && !isFlexibleArray) {
|
if (dataType == DataType.DEFAULT) {
|
||||||
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, key,
|
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, key,
|
||||||
numComponents, structLength);
|
numComponents, structLength);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
int componentLength = getPreferredComponentLength(dataType, length);
|
||||||
int offset = structLength;
|
|
||||||
int ordinal = numComponents;
|
|
||||||
|
|
||||||
int componentLength;
|
|
||||||
if (isFlexibleArray) {
|
|
||||||
// assume trailing flexible array component
|
|
||||||
offset = -1;
|
|
||||||
ordinal = -1;
|
|
||||||
if (flexibleArrayComponent != null) {
|
|
||||||
flexibleArrayComponent.getDataType().removeParent(this);
|
|
||||||
componentAdapter.removeRecord(flexibleArrayComponent.getKey());
|
|
||||||
flexibleArrayComponent = null;
|
|
||||||
}
|
|
||||||
componentLength = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
componentLength = getPreferredComponentLength(dataType, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key,
|
Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key,
|
||||||
componentLength, ordinal, offset, name, comment);
|
componentLength, numComponents, structLength, name, comment);
|
||||||
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
|
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
|
||||||
dataType.addParent(this);
|
dataType.addParent(this);
|
||||||
if (isFlexibleArray) {
|
components.add(dtc);
|
||||||
flexibleArrayComponent = dtc;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
components.add(dtc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!isFlexibleArray) {
|
|
||||||
|
|
||||||
int structureGrowth = dtc.getLength();
|
int structureGrowth = dtc.getLength();
|
||||||
if (!isInternallyAligned() && length > 0) {
|
if (!isInternallyAligned() && length > 0) {
|
||||||
structureGrowth = length;
|
structureGrowth = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++numComponents;
|
||||||
|
structLength += structureGrowth;
|
||||||
|
|
||||||
|
if (validateAlignAndNotify) {
|
||||||
|
|
||||||
record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL,
|
record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL,
|
||||||
++numComponents);
|
numComponents);
|
||||||
structLength += structureGrowth;
|
|
||||||
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength);
|
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength);
|
||||||
compositeAdapter.updateRecord(record, true);
|
compositeAdapter.updateRecord(record, true);
|
||||||
}
|
|
||||||
if (alignAndNotify) {
|
|
||||||
adjustInternalAlignment(false);
|
adjustInternalAlignment(false);
|
||||||
notifySizeChanged();
|
notifySizeChanged();
|
||||||
}
|
}
|
||||||
|
@ -177,6 +163,60 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DataTypeComponent doAddFlexArray(DataType dataType, String name, String comment,
|
||||||
|
boolean validateAlignAndNotify)
|
||||||
|
throws DataTypeDependencyException, IllegalArgumentException {
|
||||||
|
lock.acquire();
|
||||||
|
try {
|
||||||
|
checkDeleted();
|
||||||
|
|
||||||
|
if (validateAlignAndNotify) {
|
||||||
|
validateDataType(dataType);
|
||||||
|
dataType = resolve(dataType, null);
|
||||||
|
if (isInvalidFlexArrayDataType(dataType)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported flexType: " + dataType.getDisplayName());
|
||||||
|
}
|
||||||
|
checkAncestry(dataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypeComponentDB dtc = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
int oldLength = structLength;
|
||||||
|
|
||||||
|
if (flexibleArrayComponent != null) {
|
||||||
|
flexibleArrayComponent.getDataType().removeParent(this);
|
||||||
|
componentAdapter.removeRecord(flexibleArrayComponent.getKey());
|
||||||
|
flexibleArrayComponent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Record rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key, 0,
|
||||||
|
-1, -1, name, comment);
|
||||||
|
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
|
||||||
|
dataType.addParent(this);
|
||||||
|
flexibleArrayComponent = dtc;
|
||||||
|
|
||||||
|
if (validateAlignAndNotify) {
|
||||||
|
adjustInternalAlignment(false);
|
||||||
|
if (oldLength != structLength) {
|
||||||
|
notifySizeChanged();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dataMgr.dataTypeChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
dataMgr.dbError(e);
|
||||||
|
}
|
||||||
|
return dtc;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void growStructure(int amount) {
|
public void growStructure(int amount) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
@ -237,7 +277,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
if (idx > 0) {
|
if (idx > 0) {
|
||||||
DataTypeComponentDB existingDtc = components.get(idx);
|
DataTypeComponentDB existingDtc = components.get(idx);
|
||||||
if (existingDtc.isBitFieldComponent()) {
|
if (existingDtc.isBitFieldComponent()) {
|
||||||
// must shift down to eliminate possible overlap with previous component
|
// must shift down to eliminate possible overlap with previous component
|
||||||
DataTypeComponentDB previousDtc = components.get(idx - 1);
|
DataTypeComponentDB previousDtc = components.get(idx - 1);
|
||||||
if (previousDtc.getEndOffset() == existingDtc.getOffset()) {
|
if (previousDtc.getEndOffset() == existingDtc.getOffset()) {
|
||||||
shiftOffsets(idx, 0, 1);
|
shiftOffsets(idx, 0, 1);
|
||||||
|
@ -268,6 +308,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
notifySizeChanged();
|
notifySizeChanged();
|
||||||
return dtc;
|
return dtc;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dataMgr.dbError(e);
|
dataMgr.dbError(e);
|
||||||
}
|
}
|
||||||
|
@ -642,9 +685,11 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create copy of structure for target dtm (source archive information is discarded).
|
* Create copy of structure for target dtm (source archive information is
|
||||||
* WARNING! copying unaligned structures which contain bitfields can produce
|
* discarded). WARNING! copying unaligned structures which contain bitfields can
|
||||||
* invalid results when switching endianess due to the differences in packing order.
|
* produce invalid results when switching endianess due to the differences in
|
||||||
|
* packing order.
|
||||||
|
*
|
||||||
* @param dtm target data type manager
|
* @param dtm target data type manager
|
||||||
* @return cloned structure
|
* @return cloned structure
|
||||||
*/
|
*/
|
||||||
|
@ -660,7 +705,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
/**
|
/**
|
||||||
* Create cloned structure for target dtm preserving source archive information.
|
* Create cloned structure for target dtm preserving source archive information.
|
||||||
* WARNING! cloning unaligned structures which contain bitfields can produce
|
* WARNING! cloning unaligned structures which contain bitfields can produce
|
||||||
* invalid results when switching endianess due to the differences in packing order.
|
* invalid results when switching endianess due to the differences in packing
|
||||||
|
* order.
|
||||||
|
*
|
||||||
* @param dtm target data type manager
|
* @param dtm target data type manager
|
||||||
* @return cloned structure
|
* @return cloned structure
|
||||||
*/
|
*/
|
||||||
|
@ -746,12 +793,13 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backup from specified ordinal to the first component which contains
|
* Backup from specified ordinal to the first component which contains the
|
||||||
* the specified offset. For normal components the specified
|
* specified offset. For normal components the specified ordinal will be
|
||||||
* ordinal will be returned, however for bit-fields the ordinal of the first
|
* returned, however for bit-fields the ordinal of the first bit-field
|
||||||
* bit-field containing the specified offset will be returned.
|
* containing the specified offset will be returned.
|
||||||
|
*
|
||||||
* @param ordinal component ordinal
|
* @param ordinal component ordinal
|
||||||
* @param offset offset within structure
|
* @param offset offset within structure
|
||||||
* @return index of first defined component containing specific offset.
|
* @return index of first defined component containing specific offset.
|
||||||
*/
|
*/
|
||||||
private int backupToFirstComponentContainingOffset(int index, int offset) {
|
private int backupToFirstComponentContainingOffset(int index, int offset) {
|
||||||
|
@ -771,12 +819,13 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advance from specified ordinal to the last component which contains
|
* Advance from specified ordinal to the last component which contains the
|
||||||
* the specified offset. For normal components the specified
|
* specified offset. For normal components the specified ordinal will be
|
||||||
* ordinal will be returned, however for bit-fields the ordinal of the last
|
* returned, however for bit-fields the ordinal of the last bit-field containing
|
||||||
* bit-field containing the specified offset will be returned.
|
* the specified offset will be returned.
|
||||||
|
*
|
||||||
* @param ordinal component ordinal
|
* @param ordinal component ordinal
|
||||||
* @param offset offset within structure
|
* @param offset offset within structure
|
||||||
* @return index of last defined component containing specific offset.
|
* @return index of last defined component containing specific offset.
|
||||||
*/
|
*/
|
||||||
private int advanceToLastComponentContainingOffset(int index, int offset) {
|
private int advanceToLastComponentContainingOffset(int index, int offset) {
|
||||||
|
@ -887,11 +936,11 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent[] getDefinedComponents() {
|
public DataTypeComponentDB[] getDefinedComponents() {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
return components.toArray(new DataTypeComponent[components.size()]);
|
return components.toArray(new DataTypeComponentDB[components.size()]);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
|
@ -899,13 +948,14 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final DataTypeComponent insertAtOffset(int offset, DataType dataType, int length) {
|
public final DataTypeComponent insertAtOffset(int offset, DataType dataType, int length)
|
||||||
|
throws IllegalArgumentException {
|
||||||
return insertAtOffset(offset, dataType, length, null, null);
|
return insertAtOffset(offset, dataType, length, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
|
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
|
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
throw new IllegalArgumentException("Offset cannot be negative.");
|
throw new IllegalArgumentException("Offset cannot be negative.");
|
||||||
|
@ -978,6 +1028,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
notifySizeChanged();
|
notifySizeChanged();
|
||||||
return dtc;
|
return dtc;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dataMgr.dbError(e);
|
dataMgr.dbError(e);
|
||||||
}
|
}
|
||||||
|
@ -989,7 +1042,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name,
|
public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
@ -1021,19 +1074,23 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
adjustInternalAlignment(true);
|
adjustInternalAlignment(true);
|
||||||
return replaceComponent;
|
return replaceComponent;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final DataTypeComponent replace(int ordinal, DataType dataType, int length) {
|
public final DataTypeComponent replace(int ordinal, DataType dataType, int length)
|
||||||
|
throws IllegalArgumentException {
|
||||||
return replace(ordinal, dataType, length, null, null);
|
return replace(ordinal, dataType, length, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name,
|
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
throw new IllegalArgumentException("Offset cannot be negative.");
|
throw new IllegalArgumentException("Offset cannot be negative.");
|
||||||
}
|
}
|
||||||
|
@ -1071,6 +1128,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
adjustInternalAlignment(true);
|
adjustInternalAlignment(true);
|
||||||
return replaceComponent;
|
return replaceComponent;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
@ -1079,12 +1139,14 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
/**
|
/**
|
||||||
* Replaces the internal components of this structure with components of the
|
* Replaces the internal components of this structure with components of the
|
||||||
* given structure.
|
* given structure.
|
||||||
|
*
|
||||||
* @param dataType the structure to get the component information from.
|
* @param dataType the structure to get the component information from.
|
||||||
* @throws IllegalArgumentException if any of the component data types
|
* @throws IllegalArgumentException if any of the component data types are not
|
||||||
* are not allowed to replace a component in this composite data type.
|
* allowed to replace a component in this
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
* composite data type. For example, suppose
|
||||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
* dt1 contains dt2. Therefore it is not valid
|
||||||
* dependency.
|
* to replace a dt2 component with dt1 since
|
||||||
|
* this would cause a cyclic dependency.
|
||||||
* @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType)
|
* @see ghidra.program.database.data.DataTypeDB#replaceWith(ghidra.program.model.data.DataType)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -1092,92 +1154,130 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
if (!(dataType instanceof Structure)) {
|
if (!(dataType instanceof Structure)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
doReplaceWith((Structure) dataType, true, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param struct
|
|
||||||
* @param notify
|
|
||||||
* @param handler
|
|
||||||
*/
|
|
||||||
void doReplaceWith(Structure struct, boolean notify, DataTypeConflictHandler handler) {
|
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
boolean isResolveCacheOwner = dataMgr.activateResolveCache();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
doReplaceWith((Structure) dataType, true, dataMgr.getCurrentConflictHandler());
|
||||||
int oldLength = structLength;
|
}
|
||||||
int oldMinAlignment = getMinimumAlignment();
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
for (int i = 0; i < components.size(); i++) {
|
|
||||||
DataTypeComponentDB dtc = components.get(i);
|
|
||||||
dtc.getDataType().removeParent(this);
|
|
||||||
componentAdapter.removeRecord(dtc.getKey());
|
|
||||||
}
|
|
||||||
components.clear();
|
|
||||||
numComponents = 0;
|
|
||||||
structLength = 0;
|
|
||||||
|
|
||||||
if (flexibleArrayComponent != null) {
|
|
||||||
flexibleArrayComponent.getDataType().removeParent(this);
|
|
||||||
componentAdapter.removeRecord(flexibleArrayComponent.getKey());
|
|
||||||
flexibleArrayComponent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAlignment(struct, false);
|
|
||||||
|
|
||||||
if (struct.isInternallyAligned()) {
|
|
||||||
doReplaceWithAligned(struct);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
doReplaceWithUnaligned(struct);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataTypeComponent flexComponent = struct.getFlexibleArrayComponent();
|
|
||||||
if (flexComponent != null) {
|
|
||||||
setFlexibleArrayComponent(flexComponent.getDataType(), flexComponent.getFieldName(),
|
|
||||||
flexComponent.getComment());
|
|
||||||
}
|
|
||||||
|
|
||||||
record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents);
|
|
||||||
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength);
|
|
||||||
|
|
||||||
compositeAdapter.updateRecord(record, false);
|
|
||||||
|
|
||||||
if (notify) {
|
|
||||||
if (oldMinAlignment != getMinimumAlignment()) {
|
|
||||||
notifyAlignmentChanged();
|
|
||||||
}
|
|
||||||
else if (oldLength != structLength) {
|
|
||||||
notifySizeChanged();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dataMgr.dataTypeChanged(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
dataMgr.dbError(e);
|
dataMgr.dbError(e);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
if (isResolveCacheOwner) {
|
||||||
|
dataMgr.flushResolveCacheAndClearQueue(null);
|
||||||
|
}
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doReplaceWithAligned(Structure struct) {
|
/**
|
||||||
|
* Perform component replacement.
|
||||||
|
*
|
||||||
|
* @param struct
|
||||||
|
* @param notify
|
||||||
|
* @param handler
|
||||||
|
* @return true if fully completed else false if pointer component post resolve
|
||||||
|
* required
|
||||||
|
* @throws DataTypeDependencyException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
void doReplaceWith(Structure struct, boolean notify, DataTypeConflictHandler handler)
|
||||||
|
throws DataTypeDependencyException, IOException {
|
||||||
|
|
||||||
|
// pre-resolved component types to catch dependency issues early
|
||||||
|
DataTypeComponent flexComponent = struct.getFlexibleArrayComponent();
|
||||||
|
DataTypeComponent[] otherComponents = struct.getDefinedComponents();
|
||||||
|
DataType[] resolvedDts = new DataType[otherComponents.length];
|
||||||
|
for (int i = 0; i < otherComponents.length; i++) {
|
||||||
|
resolvedDts[i] = doCheckedResolve(otherComponents[i].getDataType(), handler);
|
||||||
|
}
|
||||||
|
DataType resolvedFlexDt = null;
|
||||||
|
if (flexComponent != null) {
|
||||||
|
resolvedFlexDt = doCheckedResolve(flexComponent.getDataType(), handler);
|
||||||
|
if (isInvalidFlexArrayDataType(resolvedFlexDt)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported flexType: " + resolvedFlexDt.getDisplayName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldLength = structLength;
|
||||||
|
int oldMinAlignment = getMinimumAlignment();
|
||||||
|
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
DataTypeComponentDB dtc = components.get(i);
|
||||||
|
dtc.getDataType().removeParent(this);
|
||||||
|
componentAdapter.removeRecord(dtc.getKey());
|
||||||
|
}
|
||||||
|
components.clear();
|
||||||
|
numComponents = 0;
|
||||||
|
structLength = 0;
|
||||||
|
|
||||||
|
if (flexibleArrayComponent != null) {
|
||||||
|
flexibleArrayComponent.getDataType().removeParent(this);
|
||||||
|
componentAdapter.removeRecord(flexibleArrayComponent.getKey());
|
||||||
|
flexibleArrayComponent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAlignment(struct, false);
|
||||||
|
|
||||||
|
if (struct.isInternallyAligned()) {
|
||||||
|
doReplaceWithAligned(struct, resolvedDts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doReplaceWithUnaligned(struct, resolvedDts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flexComponent != null) {
|
||||||
|
doAddFlexArray(resolvedFlexDt, flexComponent.getFieldName(), flexComponent.getComment(),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL, numComponents);
|
||||||
|
record.setIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL, structLength);
|
||||||
|
|
||||||
|
compositeAdapter.updateRecord(record, false);
|
||||||
|
|
||||||
|
adjustInternalAlignment(false);
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
if (oldMinAlignment != getMinimumAlignment()) {
|
||||||
|
notifyAlignmentChanged();
|
||||||
|
}
|
||||||
|
else if (oldLength != structLength) {
|
||||||
|
notifySizeChanged();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dataMgr.dataTypeChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointerPostResolveRequired) {
|
||||||
|
dataMgr.queuePostResolve(this, struct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doReplaceWithAligned(Structure struct, DataType[] resolvedDts) {
|
||||||
// assumes components is clear and that alignment characteristics have been set
|
// assumes components is clear and that alignment characteristics have been set
|
||||||
DataTypeComponent[] otherComponents = struct.getDefinedComponents();
|
DataTypeComponent[] otherComponents = struct.getDefinedComponents();
|
||||||
for (int i = 0; i < otherComponents.length; i++) {
|
for (int i = 0; i < otherComponents.length; i++) {
|
||||||
DataTypeComponent dtc = otherComponents[i];
|
DataTypeComponent dtc = otherComponents[i];
|
||||||
DataType dt = dtc.getDataType();
|
DataType dt = dtc.getDataType();
|
||||||
int length = (dt instanceof Dynamic) ? dtc.getLength() : -1;
|
int length = (dt instanceof Dynamic) ? dtc.getLength() : -1;
|
||||||
doAdd(dt, length, false, dtc.getFieldName(), dtc.getComment(), false);
|
try {
|
||||||
|
doAdd(resolvedDts[i], length, dtc.getFieldName(), dtc.getComment(), false);
|
||||||
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new AssertException(e); // ancestry check already performed by caller
|
||||||
|
}
|
||||||
}
|
}
|
||||||
adjustInternalAlignment(false);
|
|
||||||
dataMgr.dataTypeChanged(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doReplaceWithUnaligned(Structure struct) throws IOException {
|
private void doReplaceWithUnaligned(Structure struct, DataType[] resolvedDts)
|
||||||
|
throws IOException {
|
||||||
// assumes components is clear and that alignment characteristics have been set.
|
// assumes components is clear and that alignment characteristics have been set.
|
||||||
if (struct.isNotYetDefined()) {
|
if (struct.isNotYetDefined()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1190,8 +1290,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
for (int i = 0; i < otherComponents.length; i++) {
|
for (int i = 0; i < otherComponents.length; i++) {
|
||||||
DataTypeComponent dtc = otherComponents[i];
|
DataTypeComponent dtc = otherComponents[i];
|
||||||
|
|
||||||
DataType dt = resolve(dtc.getDataType());
|
DataType dt = resolvedDts[i]; // ancestry check already performed by caller
|
||||||
checkAncestry(dt);
|
|
||||||
|
|
||||||
int length = getPreferredComponentLength(dt, dtc.getLength());
|
int length = getPreferredComponentLength(dt, dtc.getLength());
|
||||||
|
|
||||||
|
@ -1205,6 +1304,28 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
adjustComponents(false);
|
adjustComponents(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postPointerResolve(DataType definitionDt, DataTypeConflictHandler handler) {
|
||||||
|
|
||||||
|
Structure struct = (Structure) definitionDt;
|
||||||
|
if (struct.hasFlexibleArrayComponent() != hasFlexibleArrayComponent()) {
|
||||||
|
throw new IllegalArgumentException("mismatched definition datatype");
|
||||||
|
}
|
||||||
|
|
||||||
|
super.postPointerResolve(definitionDt, handler);
|
||||||
|
|
||||||
|
if (flexibleArrayComponent != null) {
|
||||||
|
DataTypeComponent flexDtc = struct.getFlexibleArrayComponent();
|
||||||
|
DataType dt = flexDtc.getDataType();
|
||||||
|
if (dt instanceof Pointer) {
|
||||||
|
flexibleArrayComponent.getDataType().removeParent(this);
|
||||||
|
dt = dataMgr.resolve(dt, handler);
|
||||||
|
flexibleArrayComponent.setDataType(dt);
|
||||||
|
dt.addParent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dataTypeDeleted(DataType dt) {
|
public void dataTypeDeleted(DataType dt) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
@ -1261,7 +1382,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
DataTypeComponentDB dtc = components.get(i);
|
DataTypeComponentDB dtc = components.get(i);
|
||||||
int nextIndex = i + 1;
|
int nextIndex = i + 1;
|
||||||
if (dtc.getDataType() == dt) {
|
if (dtc.getDataType() == dt) {
|
||||||
// assume no impact to bitfields since base types
|
// assume no impact to bitfields since base types
|
||||||
// should not change size
|
// should not change size
|
||||||
int dtLen = dt.getLength();
|
int dtLen = dt.getLength();
|
||||||
int dtcLen = dtc.getLength();
|
int dtcLen = dtc.getLength();
|
||||||
|
@ -1304,62 +1425,74 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalent(DataType dataType) {
|
public boolean isEquivalent(DataType dataType) {
|
||||||
|
|
||||||
if (dataType == this) {
|
if (dataType == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (dataType == null || !(dataType instanceof Structure)) {
|
if (!(dataType instanceof Structure)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
if (resolving) {
|
if (resolving) { // actively resolving children
|
||||||
if (dataType.getUniversalID().equals(getUniversalID())) {
|
if (dataType.getUniversalID().equals(getUniversalID())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dataType.getPathName());
|
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dataType.getPathName());
|
||||||
}
|
}
|
||||||
Structure struct = (Structure) dataType;
|
|
||||||
if (isInternallyAligned() != struct.isInternallyAligned() ||
|
Boolean isEquivalent = dataMgr.getCachedEquivalence(this, dataType);
|
||||||
isDefaultAligned() != struct.isDefaultAligned() ||
|
if (isEquivalent != null) {
|
||||||
isMachineAligned() != struct.isMachineAligned() ||
|
return isEquivalent;
|
||||||
getMinimumAlignment() != struct.getMinimumAlignment() ||
|
|
||||||
getPackingValue() != struct.getPackingValue() ||
|
|
||||||
(!isInternallyAligned() && (getLength() != struct.getLength()))) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataTypeComponent myFlexComp = getFlexibleArrayComponent();
|
try {
|
||||||
DataTypeComponent otherFlexComp = struct.getFlexibleArrayComponent();
|
isEquivalent = false;
|
||||||
if (myFlexComp != null) {
|
Structure struct = (Structure) dataType;
|
||||||
if (otherFlexComp == null || !myFlexComp.isEquivalent(otherFlexComp)) {
|
if (isInternallyAligned() != struct.isInternallyAligned() ||
|
||||||
|
isDefaultAligned() != struct.isDefaultAligned() ||
|
||||||
|
isMachineAligned() != struct.isMachineAligned() ||
|
||||||
|
getMinimumAlignment() != struct.getMinimumAlignment() ||
|
||||||
|
getPackingValue() != struct.getPackingValue() ||
|
||||||
|
(!isInternallyAligned() && (getLength() != struct.getLength()))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (otherFlexComp != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int myNumComps = getNumComponents();
|
DataTypeComponent myFlexComp = getFlexibleArrayComponent();
|
||||||
int otherNumComps = struct.getNumComponents();
|
DataTypeComponent otherFlexComp = struct.getFlexibleArrayComponent();
|
||||||
if (myNumComps != otherNumComps) {
|
if (myFlexComp != null) {
|
||||||
return false;
|
if (otherFlexComp == null || !myFlexComp.isEquivalent(otherFlexComp)) {
|
||||||
}
|
return false;
|
||||||
for (int i = 0; i < myNumComps; i++) {
|
}
|
||||||
DataTypeComponent myDtc = getComponent(i);
|
}
|
||||||
DataTypeComponent otherDtc = struct.getComponent(i);
|
else if (otherFlexComp != null) {
|
||||||
|
|
||||||
if (!myDtc.isEquivalent(otherDtc)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int myNumComps = getNumComponents();
|
||||||
|
int otherNumComps = struct.getNumComponents();
|
||||||
|
if (myNumComps != otherNumComps) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < myNumComps; i++) {
|
||||||
|
DataTypeComponent myDtc = getComponent(i);
|
||||||
|
DataTypeComponent otherDtc = struct.getComponent(i);
|
||||||
|
if (!myDtc.isEquivalent(otherDtc)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isEquivalent = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dataMgr.putCachedEquivalence(this, dataType, isEquivalent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param definedComponentIndex the index of the defined component that is consuming the bytes.
|
* @param definedComponentIndex the index of the defined component that is
|
||||||
* @param numBytes the number of undefined bytes to consume
|
* consuming the bytes.
|
||||||
|
* @param numBytes the number of undefined bytes to consume
|
||||||
* @return the number of bytes actually consumed
|
* @return the number of bytes actually consumed
|
||||||
*/
|
*/
|
||||||
private int consumeBytesAfter(int definedComponentIndex, int numBytes) {
|
private int consumeBytesAfter(int definedComponentIndex, int numBytes) {
|
||||||
|
@ -1422,19 +1555,20 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the indicated component with a new component containing the
|
* Replace the indicated component with a new component containing the specified
|
||||||
* specified data type. Flex-array component not handled.
|
* data type. Flex-array component not handled.
|
||||||
* @param origDtc the original data type component in this structure.
|
*
|
||||||
|
* @param origDtc the original data type component in this structure.
|
||||||
* @param resolvedDataType the data type of the new component
|
* @param resolvedDataType the data type of the new component
|
||||||
* @param length the length of the new component
|
* @param length the length of the new component
|
||||||
* @param name the field name of the new component
|
* @param name the field name of the new component
|
||||||
* @param comment the comment for the new component
|
* @param comment the comment for the new component
|
||||||
* @return the new component or null if the new component couldn't fit.
|
* @return the new component or null if the new component couldn't fit.
|
||||||
*/
|
*/
|
||||||
private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType,
|
private DataTypeComponent replaceComponent(DataTypeComponent origDtc, DataType resolvedDataType,
|
||||||
int length, String name, String comment, boolean doNotify) {
|
int length, String name, String comment, boolean doNotify) {
|
||||||
|
|
||||||
// FIXME: Unsure how o support replace operation with bit-fields. Within unaligned structure
|
// FIXME: Unsure how to support replace operation with bit-fields. Within unaligned structure
|
||||||
// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift
|
// the packing behavior for bit-fields prevents a one-for-one replacement and things may shift
|
||||||
// around which the unaligned structure tries to avoid. Insert and delete are less of a concern
|
// around which the unaligned structure tries to avoid. Insert and delete are less of a concern
|
||||||
// since movement already can occur, although insert at offset may not retain the offset if it
|
// since movement already can occur, although insert at offset may not retain the offset if it
|
||||||
|
@ -1506,8 +1640,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of Undefined bytes beginning at the indicated component
|
* Gets the number of Undefined bytes beginning at the indicated component
|
||||||
* ordinal. Undefined bytes that have a field name or comment specified are
|
* ordinal. Undefined bytes that have a field name or comment specified are also
|
||||||
* also included.
|
* included.
|
||||||
|
*
|
||||||
* @param ordinal the component ordinal to begin checking at.
|
* @param ordinal the component ordinal to begin checking at.
|
||||||
* @return the number of contiguous undefined bytes
|
* @return the number of contiguous undefined bytes
|
||||||
*/
|
*/
|
||||||
|
@ -1653,7 +1788,8 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
comp.setLength(len, true);
|
comp.setLength(len, true);
|
||||||
shiftOffsets(nextIndex, -bytesNeeded, 0);
|
shiftOffsets(nextIndex, -bytesNeeded, 0);
|
||||||
}
|
}
|
||||||
else if (comp.getOrdinal() == getLastDefinedComponentIndex()) { // we are the last defined component, grow structure
|
else if (comp.getOrdinal() == getLastDefinedComponentIndex()) {
|
||||||
|
// we are the last defined component, grow structure
|
||||||
doGrowStructure(bytesNeeded - bytesAvailable);
|
doGrowStructure(bytesNeeded - bytesAvailable);
|
||||||
comp.setLength(len, true);
|
comp.setLength(len, true);
|
||||||
shiftOffsets(nextIndex, -bytesNeeded, 0);
|
shiftOffsets(nextIndex, -bytesNeeded, 0);
|
||||||
|
@ -1711,8 +1847,9 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>ComponentComparator</code> provides ability to compare two DataTypeComponent objects
|
* <code>ComponentComparator</code> provides ability to compare two
|
||||||
* based upon their ordinal. Intended to be used to sort components based upon ordinal.
|
* DataTypeComponent objects based upon their ordinal. Intended to be used to
|
||||||
|
* sort components based upon ordinal.
|
||||||
*/
|
*/
|
||||||
private static class ComponentComparator implements Comparator<DataTypeComponent> {
|
private static class ComponentComparator implements Comparator<DataTypeComponent> {
|
||||||
@Override
|
@Override
|
||||||
|
@ -1722,15 +1859,17 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust the alignment, packing and padding of components within this structure based upon the
|
* Adjust the alignment, packing and padding of components within this structure
|
||||||
* current alignment and packing attributes for this structure. This method should be
|
* based upon the current alignment and packing attributes for this structure.
|
||||||
* called to basically fix up the layout of the internal components of the structure
|
* This method should be called to basically fix up the layout of the internal
|
||||||
* after other code has changed the attributes of the structure.
|
* components of the structure after other code has changed the attributes of
|
||||||
* <BR>When switching between internally aligned and unaligned this method corrects the
|
* the structure. <BR>
|
||||||
* component ordinal numbering also.
|
* When switching between internally aligned and unaligned this method corrects
|
||||||
* @param notify if true this method will do data type change notification
|
* the component ordinal numbering also.
|
||||||
* when it changes the layout of the components or when it changes the
|
*
|
||||||
* overall size of the structure.
|
* @param notify if true this method will do data type change notification when
|
||||||
|
* it changes the layout of the components or when it changes the
|
||||||
|
* overall size of the structure.
|
||||||
* @return true if the structure was changed by this method.
|
* @return true if the structure was changed by this method.
|
||||||
*/
|
*/
|
||||||
private boolean adjustComponents(boolean notify) {
|
private boolean adjustComponents(boolean notify) {
|
||||||
|
@ -1858,7 +1997,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pack(int packingSize) throws InvalidInputException {
|
public void pack(int packingSize) {
|
||||||
setPackingValue(packingSize);
|
setPackingValue(packingSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1873,7 +2012,7 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent getFlexibleArrayComponent() {
|
public DataTypeComponentDB getFlexibleArrayComponent() {
|
||||||
return flexibleArrayComponent;
|
return flexibleArrayComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1885,12 +2024,17 @@ class StructureDB extends CompositeDB implements Structure {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name,
|
public DataTypeComponent setFlexibleArrayComponent(DataType flexType, String name,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
if (isInvalidFlexArrayDataType(flexType)) {
|
if (isInvalidFlexArrayDataType(flexType)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Unsupported flexType: " + flexType.getDisplayName());
|
"Unsupported flexType: " + flexType.getDisplayName());
|
||||||
}
|
}
|
||||||
return doAdd(flexType, 0, true, name, comment, true);
|
try {
|
||||||
|
return doAddFlexArray(flexType, name, comment, true);
|
||||||
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,8 +27,6 @@ import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database implementation for the Union data type.
|
* Database implementation for the Union data type.
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class UnionDB extends CompositeDB implements Union {
|
class UnionDB extends CompositeDB implements Union {
|
||||||
|
|
||||||
|
@ -38,6 +36,7 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
*
|
||||||
* @param dataMgr
|
* @param dataMgr
|
||||||
* @param cache
|
* @param cache
|
||||||
* @param compositeAdapter
|
* @param compositeAdapter
|
||||||
|
@ -57,8 +56,8 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long[] ids = componentAdapter.getComponentIdsInComposite(key);
|
long[] ids = componentAdapter.getComponentIdsInComposite(key);
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (long id : ids) {
|
||||||
Record rec = componentAdapter.getRecord(ids[i]);
|
Record rec = componentAdapter.getRecord(id);
|
||||||
components.add(new DataTypeComponentDB(dataMgr, componentAdapter, this, rec));
|
components.add(new DataTypeComponentDB(dataMgr, componentAdapter, this, rec));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,14 +83,18 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent add(DataType dataType, int length, String name, String comment) {
|
public DataTypeComponent add(DataType dataType, int length, String componentName,
|
||||||
|
String comment) throws IllegalArgumentException {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
DataTypeComponent dtc = doAdd(dataType, length, name, comment);
|
DataTypeComponent dtc = doAdd(dataType, length, componentName, comment, true);
|
||||||
adjustLength(true, true);
|
adjustLength(true, true);
|
||||||
return dtc;
|
return dtc;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
@ -117,14 +120,17 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment) {
|
private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment,
|
||||||
|
boolean validateAlignAndNotify) throws DataTypeDependencyException {
|
||||||
|
|
||||||
validateDataType(dataType);
|
validateDataType(dataType);
|
||||||
|
|
||||||
dataType = adjustBitField(dataType);
|
dataType = adjustBitField(dataType);
|
||||||
|
|
||||||
dataType = resolve(dataType);
|
if (validateAlignAndNotify) {
|
||||||
checkAncestry(dataType);
|
dataType = resolve(dataType);
|
||||||
|
checkAncestry(dataType);
|
||||||
|
}
|
||||||
|
|
||||||
length = getPreferredComponentLength(dataType, length);
|
length = getPreferredComponentLength(dataType, length);
|
||||||
|
|
||||||
|
@ -161,7 +167,7 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
|
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
|
||||||
String comment) {
|
String comment) throws IllegalArgumentException {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
@ -183,6 +189,9 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
adjustLength(true, true);
|
adjustLength(true, true);
|
||||||
return dtc;
|
return dtc;
|
||||||
}
|
}
|
||||||
|
catch (DataTypeDependencyException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
|
@ -232,56 +241,75 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces the internal components of this union with components of the
|
|
||||||
* given union.
|
|
||||||
* @param dataType the union to get the component information from.
|
|
||||||
* @throws IllegalArgumentException if any of the component data types
|
|
||||||
* are not allowed to replace a component in this composite data type.
|
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
|
||||||
* to replace a dt2 component with dt1 since this would cause a cyclic
|
|
||||||
* dependency.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void replaceWith(DataType dataType) {
|
public void replaceWith(DataType dataType) {
|
||||||
if (!(dataType instanceof Union)) {
|
if (!(dataType instanceof Union)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
doReplaceWith((Union) dataType, true, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
void doReplaceWith(Union union, boolean notify, DataTypeConflictHandler handler) {
|
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
boolean isResolveCacheOwner = dataMgr.activateResolveCache();
|
||||||
try {
|
try {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
doReplaceWith((Union) dataType, true, dataMgr.getCurrentConflictHandler());
|
||||||
long oldMinAlignment = getMinimumAlignment();
|
}
|
||||||
for (int i = 0; i < components.size(); i++) {
|
catch (DataTypeDependencyException e) {
|
||||||
DataTypeComponentDB dtc = components.get(i);
|
throw new IllegalArgumentException(e.getMessage(), e);
|
||||||
dtc.getDataType().removeParent(this);
|
|
||||||
removeComponent(dtc.getKey());
|
|
||||||
}
|
|
||||||
components.clear();
|
|
||||||
|
|
||||||
setAlignment(union, notify);
|
|
||||||
|
|
||||||
for (DataTypeComponent dtc : union.getComponents()) {
|
|
||||||
DataType dt = dtc.getDataType();
|
|
||||||
doAdd(dt, dtc.getLength(), dtc.getFieldName(), dtc.getComment());
|
|
||||||
}
|
|
||||||
|
|
||||||
adjustLength(notify, true); // TODO: VERIFY! is it always appropriate to set update time??
|
|
||||||
|
|
||||||
if (notify && (oldMinAlignment != getMinimumAlignment())) {
|
|
||||||
notifyAlignmentChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
if (isResolveCacheOwner) {
|
||||||
|
dataMgr.flushResolveCacheAndClearQueue(null);
|
||||||
|
}
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void doReplaceWith(Union union, boolean notify, DataTypeConflictHandler handler)
|
||||||
|
throws DataTypeDependencyException {
|
||||||
|
|
||||||
|
// pre-resolved component types to catch dependency issues early
|
||||||
|
DataTypeComponent[] otherComponents = union.getComponents();
|
||||||
|
DataType[] resolvedDts = new DataType[otherComponents.length];
|
||||||
|
for (int i = 0; i < otherComponents.length; i++) {
|
||||||
|
resolvedDts[i] = doCheckedResolve(otherComponents[i].getDataType(), handler);
|
||||||
|
checkAncestry(resolvedDts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldLength = unionLength;
|
||||||
|
int oldMinAlignment = getMinimumAlignment();
|
||||||
|
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
DataTypeComponentDB dtc = components.get(i);
|
||||||
|
dtc.getDataType().removeParent(this);
|
||||||
|
removeComponent(dtc.getKey());
|
||||||
|
}
|
||||||
|
components.clear();
|
||||||
|
|
||||||
|
setAlignment(union, false);
|
||||||
|
|
||||||
|
for (int i = 0; i < otherComponents.length; i++) {
|
||||||
|
DataTypeComponent dtc = otherComponents[i];
|
||||||
|
doAdd(resolvedDts[i], dtc.getLength(), dtc.getFieldName(), dtc.getComment(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustLength(false, false);
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
if (oldMinAlignment != getMinimumAlignment()) {
|
||||||
|
notifyAlignmentChanged();
|
||||||
|
}
|
||||||
|
else if (oldLength != unionLength) {
|
||||||
|
notifySizeChanged();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dataMgr.dataTypeChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointerPostResolveRequired) {
|
||||||
|
dataMgr.queuePostResolve(this, union);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPartOf(DataType dataType) {
|
public boolean isPartOf(DataType dataType) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
@ -320,6 +348,11 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumDefinedComponents() {
|
||||||
|
return getNumComponents();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent getComponent(int ordinal) {
|
public DataTypeComponent getComponent(int ordinal) {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
@ -336,17 +369,22 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataTypeComponent[] getComponents() {
|
public DataTypeComponentDB[] getComponents() {
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
return components.toArray(new DataTypeComponent[components.size()]);
|
return components.toArray(new DataTypeComponentDB[components.size()]);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataTypeComponentDB[] getDefinedComponents() {
|
||||||
|
return getComponents();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataType copy(DataTypeManager dtm) {
|
public DataType copy(DataTypeManager dtm) {
|
||||||
UnionDataType union = new UnionDataType(getCategoryPath(), getName(), dtm);
|
UnionDataType union = new UnionDataType(getCategoryPath(), getName(), dtm);
|
||||||
|
@ -419,7 +457,7 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
baseDataType = resolve(baseDataType);
|
baseDataType = resolve(baseDataType);
|
||||||
|
|
||||||
// Both aligned and unaligned bitfields use same adjustment
|
// Both aligned and unaligned bitfields use same adjustment
|
||||||
// unaligned must force bitfield placement at byte offset 0
|
// unaligned must force bitfield placement at byte offset 0
|
||||||
int bitSize = bitfieldDt.getDeclaredBitSize();
|
int bitSize = bitfieldDt.getDeclaredBitSize();
|
||||||
int effectiveBitSize =
|
int effectiveBitSize =
|
||||||
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
|
BitFieldDataType.getEffectiveBitSize(bitSize, baseDataType.getLength());
|
||||||
|
@ -515,40 +553,54 @@ class UnionDB extends CompositeDB implements Union {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalent(DataType dt) {
|
public boolean isEquivalent(DataType dataType) {
|
||||||
if (dt == this) {
|
|
||||||
|
if (dataType == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (dt == null || !(dt instanceof Union)) {
|
if (!(dataType instanceof Union)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkIsValid();
|
checkIsValid();
|
||||||
if (resolving) {
|
if (resolving) { // actively resolving children
|
||||||
if (dt.getUniversalID().equals(getUniversalID())) {
|
if (dataType.getUniversalID().equals(getUniversalID())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dt.getPathName());
|
return DataTypeUtilities.equalsIgnoreConflict(getPathName(), dataType.getPathName());
|
||||||
}
|
}
|
||||||
Union union = (Union) dt;
|
|
||||||
if (isInternallyAligned() != union.isInternallyAligned() ||
|
Boolean isEquivalent = dataMgr.getCachedEquivalence(this, dataType);
|
||||||
isDefaultAligned() != union.isDefaultAligned() ||
|
if (isEquivalent != null) {
|
||||||
isMachineAligned() != union.isMachineAligned() ||
|
return isEquivalent;
|
||||||
getMinimumAlignment() != union.getMinimumAlignment() ||
|
|
||||||
getPackingValue() != union.getPackingValue()) {
|
|
||||||
// rely on component match instead of checking length
|
|
||||||
// since dynamic component sizes could affect length
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
DataTypeComponent[] myComps = getComponents();
|
|
||||||
DataTypeComponent[] otherComps = union.getComponents();
|
try {
|
||||||
if (myComps.length != otherComps.length) {
|
isEquivalent = false;
|
||||||
return false;
|
Union union = (Union) dataType;
|
||||||
}
|
if (isInternallyAligned() != union.isInternallyAligned() ||
|
||||||
for (int i = 0; i < myComps.length; i++) {
|
isDefaultAligned() != union.isDefaultAligned() ||
|
||||||
if (!myComps[i].isEquivalent(otherComps[i])) {
|
isMachineAligned() != union.isMachineAligned() ||
|
||||||
|
getMinimumAlignment() != union.getMinimumAlignment() ||
|
||||||
|
getPackingValue() != union.getPackingValue()) {
|
||||||
|
// rely on component match instead of checking length
|
||||||
|
// since dynamic component sizes could affect length
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
DataTypeComponent[] myComps = getComponents();
|
||||||
|
DataTypeComponent[] otherComps = union.getComponents();
|
||||||
|
if (myComps.length != otherComps.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < myComps.length; i++) {
|
||||||
|
if (!myComps[i].isEquivalent(otherComps[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isEquivalent = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dataMgr.putCachedEquivalence(this, dataType, isEquivalent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,7 +261,6 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
|
||||||
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
|
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
// FIXME:
|
|
||||||
Namespace libraryScope = getLibraryScope(extLibraryName);
|
Namespace libraryScope = getLibraryScope(extLibraryName);
|
||||||
if (libraryScope == null) {
|
if (libraryScope == null) {
|
||||||
libraryScope = addExternalName(extLibraryName, null,
|
libraryScope = addExternalName(extLibraryName, null,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.block;
|
package ghidra.program.model.block;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.model.address.AddressSet;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -23,9 +25,6 @@ import ghidra.program.model.symbol.FlowType;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <CODE>IsolatedEntryCodeSubModel</CODE> (S-model) defines subroutines with a
|
* <CODE>IsolatedEntryCodeSubModel</CODE> (S-model) defines subroutines with a
|
||||||
* unique entry point, which may share code with other subroutines. Each entry-
|
* unique entry point, which may share code with other subroutines. Each entry-
|
||||||
|
@ -34,7 +33,7 @@ import java.util.LinkedList;
|
||||||
* the set of addresses contained within each subroutine. Unlike the
|
* the set of addresses contained within each subroutine. Unlike the
|
||||||
* OverlapCodeSubModel, the address set of a IsolatedEntryCodeSubModel
|
* OverlapCodeSubModel, the address set of a IsolatedEntryCodeSubModel
|
||||||
* subroutine is permitted to span entry-points of other subroutines based upon
|
* subroutine is permitted to span entry-points of other subroutines based upon
|
||||||
* the possible flows from its' entry- point.
|
* the possible flows from its entry- point.
|
||||||
*
|
*
|
||||||
* @see ghidra.program.model.block.CodeBlockModel
|
* @see ghidra.program.model.block.CodeBlockModel
|
||||||
* @see ghidra.program.model.block.OverlapCodeSubModel
|
* @see ghidra.program.model.block.OverlapCodeSubModel
|
||||||
|
@ -79,13 +78,15 @@ public class IsolatedEntrySubModel extends OverlapCodeSubModel {
|
||||||
|
|
||||||
// Create address list which contains all other entry points for this M-model sub
|
// Create address list which contains all other entry points for this M-model sub
|
||||||
CodeBlock mSub = modelM.getCodeBlockAt(mStartAddr, monitor);
|
CodeBlock mSub = modelM.getCodeBlockAt(mStartAddr, monitor);
|
||||||
if (mSub == null)
|
if (mSub == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
Address[] mEntryPts = mSub.getStartAddresses();
|
Address[] mEntryPts = mSub.getStartAddresses();
|
||||||
ArrayList<Address> startSet = new ArrayList<Address>();
|
ArrayList<Address> startSet = new ArrayList<Address>();
|
||||||
for (int i = 0; i < mEntryPts.length; i++) {
|
for (Address mEntryPt : mEntryPts) {
|
||||||
if (!mStartAddr.equals(mEntryPts[i]))
|
if (!mStartAddr.equals(mEntryPt)) {
|
||||||
startSet.add(mEntryPts[i]);
|
startSet.add(mEntryPt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a holder for the blockSet
|
// create a holder for the blockSet
|
||||||
|
@ -100,20 +101,25 @@ public class IsolatedEntrySubModel extends OverlapCodeSubModel {
|
||||||
// Build model-S subroutine from basic blocks
|
// Build model-S subroutine from basic blocks
|
||||||
while (!todoList.isEmpty()) {
|
while (!todoList.isEmpty()) {
|
||||||
|
|
||||||
if (monitor.isCancelled())
|
if (monitor.isCancelled()) {
|
||||||
throw new CancelledException();
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
// Get basic block at the specified address
|
// Get basic block at the specified address
|
||||||
Address a = todoList.removeLast();
|
Address a = todoList.removeLast();
|
||||||
if (addrSet.contains(a) || startSet.contains(a)) // <<-- only difference from Model-O
|
if (addrSet.contains(a) || startSet.contains(a))
|
||||||
continue; // already processed this block or encountered another Model-M entry point
|
{
|
||||||
|
continue; // already processed this block or encountered another Model-M entry point
|
||||||
|
}
|
||||||
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
||||||
if (bblock == null)
|
if (bblock == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the block contains instructions
|
// Verify that the block contains instructions
|
||||||
if (listing.getInstructionAt(a) == null)
|
if (listing.getInstructionAt(a) == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Add basic block to subroutine address set
|
// Add basic block to subroutine address set
|
||||||
addrSet.add(bblock);
|
addrSet.add(bblock);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.block;
|
package ghidra.program.model.block;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Listing;
|
import ghidra.program.model.listing.Listing;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
@ -23,15 +25,12 @@ import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <CODE>OverlapCodeSubModel</CODE> (O-model) defines subroutines with a
|
* <CODE>OverlapCodeSubModel</CODE> (O-model) defines subroutines with a
|
||||||
* unique entry point, which may share code with other subroutines. Each entry-
|
* unique entry point, which may share code with other subroutines. Each entry-
|
||||||
* point may either be a source or called entry-point and is identified using
|
* point may either be a source or called entry-point and is identified using
|
||||||
* the MultEntSubModel. This model defines the set of addresses contained
|
* the MultEntSubModel. This model defines the set of addresses contained
|
||||||
* within each subroutine based upon the possible flows from its' entry- point.
|
* within each subroutine based upon the possible flows from its entry- point.
|
||||||
* Flows which encounter another entry-point are terminated.
|
* Flows which encounter another entry-point are terminated.
|
||||||
* <P>
|
* <P>
|
||||||
* NOTE: This differs from the original definition of an entry point, however,
|
* NOTE: This differs from the original definition of an entry point, however,
|
||||||
|
@ -104,20 +103,25 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
// Build model-O subroutine from basic blocks
|
// Build model-O subroutine from basic blocks
|
||||||
while (!todoList.isEmpty()) {
|
while (!todoList.isEmpty()) {
|
||||||
|
|
||||||
if (monitor.isCancelled())
|
if (monitor.isCancelled()) {
|
||||||
throw new CancelledException();
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
|
||||||
// Get basic block at the specified address
|
// Get basic block at the specified address
|
||||||
Address a = todoList.removeLast();
|
Address a = todoList.removeLast();
|
||||||
if (addrSet.contains(a))
|
if (addrSet.contains(a))
|
||||||
continue; // already processed this block
|
{
|
||||||
|
continue; // already processed this block
|
||||||
|
}
|
||||||
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
CodeBlock bblock = bbModel.getFirstCodeBlockContaining(a, monitor);
|
||||||
if (bblock == null)
|
if (bblock == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that the block contains instructions
|
// Verify that the block contains instructions
|
||||||
if (listing.getInstructionAt(a) == null)
|
if (listing.getInstructionAt(a) == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Add basic block to subroutine address set
|
// Add basic block to subroutine address set
|
||||||
addrSet.add(bblock);
|
addrSet.add(bblock);
|
||||||
|
@ -140,7 +144,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlockAt(ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlockAt(ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlock getCodeBlockAt(Address addr, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlock getCodeBlockAt(Address addr, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
// First check out the Block cache
|
// First check out the Block cache
|
||||||
CodeBlock block = foundOSubs.getBlockAt(addr);
|
CodeBlock block = foundOSubs.getBlockAt(addr);
|
||||||
|
@ -170,7 +175,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
* contains the address empty array otherwise.
|
* contains the address empty array otherwise.
|
||||||
* @throws CancelledException if the monitor cancels the operation.
|
* @throws CancelledException if the monitor cancels the operation.
|
||||||
*/
|
*/
|
||||||
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlock[] getCodeBlocksContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
// First check out the Block cache
|
// First check out the Block cache
|
||||||
CodeBlock[] blocks = foundOSubs.getBlocksContaining(addr);
|
CodeBlock[] blocks = foundOSubs.getBlocksContaining(addr);
|
||||||
|
@ -179,8 +185,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeBlock modelMSub = modelM.getFirstCodeBlockContaining(addr, monitor);
|
CodeBlock modelMSub = modelM.getFirstCodeBlockContaining(addr, monitor);
|
||||||
if (modelMSub == null)
|
if (modelMSub == null) {
|
||||||
return emptyBlockArray;
|
return emptyBlockArray;
|
||||||
|
}
|
||||||
Address[] entPts = modelMSub.getStartAddresses();
|
Address[] entPts = modelMSub.getStartAddresses();
|
||||||
|
|
||||||
// Single-entry MSub same as OSub
|
// Single-entry MSub same as OSub
|
||||||
|
@ -195,8 +202,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
ArrayList<CodeBlock> blockList = new ArrayList<CodeBlock>();
|
ArrayList<CodeBlock> blockList = new ArrayList<CodeBlock>();
|
||||||
for (int i = 0; i < cnt; i++) {
|
for (int i = 0; i < cnt; i++) {
|
||||||
CodeBlock block = getSubroutine(entPts[i], monitor);
|
CodeBlock block = getSubroutine(entPts[i], monitor);
|
||||||
if (block.contains(addr))
|
if (block.contains(addr)) {
|
||||||
blockList.add(block);
|
blockList.add(block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return blockList.toArray(new CodeBlock[blockList.size()]);
|
return blockList.toArray(new CodeBlock[blockList.size()]);
|
||||||
}
|
}
|
||||||
|
@ -205,7 +213,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getFirstCodeBlockContaining(ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getFirstCodeBlockContaining(ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlock getFirstCodeBlockContaining(Address addr, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
// First check out the Block cache
|
// First check out the Block cache
|
||||||
CodeBlock block = foundOSubs.getFirstBlockContaining(addr);
|
CodeBlock block = foundOSubs.getFirstBlockContaining(addr);
|
||||||
|
@ -214,8 +223,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeBlock modelMSub = modelM.getFirstCodeBlockContaining(addr, monitor);
|
CodeBlock modelMSub = modelM.getFirstCodeBlockContaining(addr, monitor);
|
||||||
if (modelMSub == null)
|
if (modelMSub == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
Address[] entPts = modelMSub.getStartAddresses();
|
Address[] entPts = modelMSub.getStartAddresses();
|
||||||
|
|
||||||
// Single-entry MSub same as OSub
|
// Single-entry MSub same as OSub
|
||||||
|
@ -227,8 +237,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
// Return first OSub which contains addr
|
// Return first OSub which contains addr
|
||||||
for (int i = 0; i < cnt; i++) {
|
for (int i = 0; i < cnt; i++) {
|
||||||
block = getSubroutine(entPts[i], monitor);
|
block = getSubroutine(entPts[i], monitor);
|
||||||
if (block != null && block.contains(addr))
|
if (block != null && block.contains(addr)) {
|
||||||
return block;
|
return block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -236,14 +247,16 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlocks(ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlocks(ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlockIterator getCodeBlocks(TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlockIterator getCodeBlocks(TaskMonitor monitor) throws CancelledException {
|
||||||
return new SingleEntSubIterator(this, monitor);
|
return new SingleEntSubIterator(this, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlocksContaining(ghidra.program.model.address.AddressSetView, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getCodeBlocksContaining(ghidra.program.model.address.AddressSetView, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlockIterator getCodeBlocksContaining(AddressSetView addrSet, TaskMonitor monitor) throws CancelledException {
|
||||||
return new SingleEntSubIterator(this, addrSet, monitor);
|
return new SingleEntSubIterator(this, addrSet, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +272,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getProgram()
|
* @see ghidra.program.model.block.CodeBlockModel#getProgram()
|
||||||
*/
|
*/
|
||||||
public Program getProgram() {
|
@Override
|
||||||
|
public Program getProgram() {
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,14 +288,16 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getName(ghidra.program.model.block.CodeBlock)
|
* @see ghidra.program.model.block.CodeBlockModel#getName(ghidra.program.model.block.CodeBlock)
|
||||||
*/
|
*/
|
||||||
public String getName(CodeBlock block) {
|
@Override
|
||||||
|
public String getName(CodeBlock block) {
|
||||||
// get the start address for the block
|
// get the start address for the block
|
||||||
// look up the symbol in the symbol table.
|
// look up the symbol in the symbol table.
|
||||||
// it should have one if anyone calls it.
|
// it should have one if anyone calls it.
|
||||||
// if not, make up a label
|
// if not, make up a label
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
Address start = block.getFirstStartAddress();
|
Address start = block.getFirstStartAddress();
|
||||||
|
|
||||||
|
@ -309,7 +325,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
*
|
*
|
||||||
* @return flow type of this node
|
* @return flow type of this node
|
||||||
*/
|
*/
|
||||||
public FlowType getFlowType(CodeBlock block) {
|
@Override
|
||||||
|
public FlowType getFlowType(CodeBlock block) {
|
||||||
/* If there are multiple unique ways out of the node, then we
|
/* If there are multiple unique ways out of the node, then we
|
||||||
should return FlowType.UNKNOWN (or FlowType.MULTIFLOW ?).
|
should return FlowType.UNKNOWN (or FlowType.MULTIFLOW ?).
|
||||||
Possible considerations for the future which are particularly
|
Possible considerations for the future which are particularly
|
||||||
|
@ -318,8 +335,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
(as opposed to jumping within the subroutine).
|
(as opposed to jumping within the subroutine).
|
||||||
Might want to consider FlowType.MULTITERMINAL for multiple returns? */
|
Might want to consider FlowType.MULTITERMINAL for multiple returns? */
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return RefType.FLOW;
|
return RefType.FLOW;
|
||||||
}
|
}
|
||||||
|
@ -327,10 +345,12 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlockReferenceIterator getSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return new SubroutineSourceReferenceIterator(block, monitor);
|
return new SubroutineSourceReferenceIterator(block, monitor);
|
||||||
}
|
}
|
||||||
|
@ -338,10 +358,12 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getNumSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getNumSources(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public int getNumSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public int getNumSources(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return SubroutineSourceReferenceIterator.getNumSources(block, monitor);
|
return SubroutineSourceReferenceIterator.getNumSources(block, monitor);
|
||||||
}
|
}
|
||||||
|
@ -349,7 +371,8 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public CodeBlockReferenceIterator getDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
// destinations of Fallthroughs are the follow on block
|
// destinations of Fallthroughs are the follow on block
|
||||||
// destinations of all others are the instruction's operand referents
|
// destinations of all others are the instruction's operand referents
|
||||||
|
|
||||||
|
@ -367,8 +390,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
// nor that any destination is a good destination unless the instruction
|
// nor that any destination is a good destination unless the instruction
|
||||||
// is looked at.
|
// is looked at.
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return new SubroutineDestReferenceIterator(block, monitor);
|
return new SubroutineDestReferenceIterator(block, monitor);
|
||||||
}
|
}
|
||||||
|
@ -376,10 +400,12 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getNumDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
* @see ghidra.program.model.block.CodeBlockModel#getNumDestinations(ghidra.program.model.block.CodeBlock, ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
public int getNumDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
@Override
|
||||||
|
public int getNumDestinations(CodeBlock block, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
if (!(block.getModel() instanceof OverlapCodeSubModel))
|
if (!(block.getModel() instanceof OverlapCodeSubModel)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
return SubroutineDestReferenceIterator.getNumDestinations(block, monitor);
|
return SubroutineDestReferenceIterator.getNumDestinations(block, monitor);
|
||||||
}
|
}
|
||||||
|
@ -393,8 +419,9 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
*/
|
*/
|
||||||
protected CodeBlock createSub(AddressSetView addrSet, Address entryPt) {
|
protected CodeBlock createSub(AddressSetView addrSet, Address entryPt) {
|
||||||
|
|
||||||
if (addrSet.isEmpty())
|
if (addrSet.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Address[] entryPts = new Address[1];
|
Address[] entryPts = new Address[1];
|
||||||
entryPts[0] = entryPt;
|
entryPts[0] = entryPt;
|
||||||
|
@ -408,6 +435,7 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getBasicBlockModel()
|
* @see ghidra.program.model.block.CodeBlockModel#getBasicBlockModel()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public CodeBlockModel getBasicBlockModel() {
|
public CodeBlockModel getBasicBlockModel() {
|
||||||
return modelM.getBasicBlockModel();
|
return modelM.getBasicBlockModel();
|
||||||
}
|
}
|
||||||
|
@ -415,6 +443,7 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#getName()
|
* @see ghidra.program.model.block.CodeBlockModel#getName()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return OVERLAP_MODEL_NAME;
|
return OVERLAP_MODEL_NAME;
|
||||||
}
|
}
|
||||||
|
@ -422,6 +451,7 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.SubroutineBlockModel#getBaseSubroutineModel()
|
* @see ghidra.program.model.block.SubroutineBlockModel#getBaseSubroutineModel()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public SubroutineBlockModel getBaseSubroutineModel() {
|
public SubroutineBlockModel getBaseSubroutineModel() {
|
||||||
return modelM;
|
return modelM;
|
||||||
}
|
}
|
||||||
|
@ -429,10 +459,12 @@ public class OverlapCodeSubModel implements SubroutineBlockModel {
|
||||||
/**
|
/**
|
||||||
* @see ghidra.program.model.block.CodeBlockModel#allowsBlockOverlap()
|
* @see ghidra.program.model.block.CodeBlockModel#allowsBlockOverlap()
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean allowsBlockOverlap() {
|
public boolean allowsBlockOverlap() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean externalsIncluded() {
|
public boolean externalsIncluded() {
|
||||||
return modelM.externalsIncluded();
|
return modelM.externalsIncluded();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
|
|
|
@ -48,10 +48,11 @@ public class ArrayDataType extends DataTypeImpl implements Array {
|
||||||
* @param dataType the dataType of the elements in the array.
|
* @param dataType the dataType of the elements in the array.
|
||||||
* @param numElements the number of elements in the array.
|
* @param numElements the number of elements in the array.
|
||||||
* @param elementLength the length of an individual element in the array.
|
* @param elementLength the length of an individual element in the array.
|
||||||
|
* @param dtm datatype manager or null
|
||||||
*/
|
*/
|
||||||
public ArrayDataType(DataType dataType, int numElements, int elementLength,
|
public ArrayDataType(DataType dataType, int numElements, int elementLength,
|
||||||
DataTypeManager dtm) {
|
DataTypeManager dtm) {
|
||||||
super(CategoryPath.ROOT, "array", dtm);
|
super(dataType.getCategoryPath(), "array", dtm);
|
||||||
validate(dataType);
|
validate(dataType);
|
||||||
if (dataType.getDataTypeManager() != dtm) {
|
if (dataType.getDataTypeManager() != dtm) {
|
||||||
dataType = dataType.clone(dtm);
|
dataType = dataType.clone(dtm);
|
||||||
|
@ -100,9 +101,6 @@ public class ArrayDataType extends DataTypeImpl implements Array {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEquivalent(DataType obj) {
|
public boolean isEquivalent(DataType obj) {
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (obj == this) {
|
if (obj == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import utilities.util.ArrayUtilities;
|
||||||
* for use within data structures. The length (i.e., storage size) of this bitfield datatype is
|
* for use within data structures. The length (i.e., storage size) of this bitfield datatype is
|
||||||
* the minimum number of bytes required to contain the bitfield at its specified offset.
|
* the minimum number of bytes required to contain the bitfield at its specified offset.
|
||||||
* The effective bit-size of a bitfield will be limited by the size of the base
|
* The effective bit-size of a bitfield will be limited by the size of the base
|
||||||
* datatype whose size may be controlled by its' associated datatype manager and data organization
|
* datatype whose size may be controlled by its associated datatype manager and data organization
|
||||||
* (e.g., {@link IntegerDataType}).
|
* (e.g., {@link IntegerDataType}).
|
||||||
* <p>
|
* <p>
|
||||||
* NOTE: Instantiation of this datatype implementation is intended for internal use only.
|
* NOTE: Instantiation of this datatype implementation is intended for internal use only.
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.framework.ShutdownHookRegistry;
|
import ghidra.framework.ShutdownHookRegistry;
|
||||||
import ghidra.framework.ShutdownPriority;
|
import ghidra.framework.ShutdownPriority;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
|
|
@ -73,11 +73,24 @@ public interface Composite extends DataType {
|
||||||
public void setDescription(String desc);
|
public void setDescription(String desc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of component data types in this data type.
|
* Gets the number of component data types in this composite.
|
||||||
* @return the number of components that make up this data prototype
|
* The count will include all undefined filler components which may be present
|
||||||
|
* within an unaligned structure. Structures do not include the
|
||||||
|
* optional trailing flexible array component in this count
|
||||||
|
* (see {@link Structure#hasFlexibleArrayComponent()}).
|
||||||
|
* @return the number of components that make up this composite
|
||||||
*/
|
*/
|
||||||
public abstract int getNumComponents();
|
public abstract int getNumComponents();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of explicitly defined components in this composite.
|
||||||
|
* For Unions and aligned Structures this is equivalent to {@link #getNumComponents()}
|
||||||
|
* since they do not contain undefined components. The count will exclude all undefined
|
||||||
|
* filler components which may be present within an unaligned structure.
|
||||||
|
* @return the number of explicitly defined components in this composite
|
||||||
|
*/
|
||||||
|
public abstract int getNumDefinedComponents();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the component of this data type with the indicated ordinal.
|
* Returns the component of this data type with the indicated ordinal.
|
||||||
* @param ordinal the component's ordinal (zero based).
|
* @param ordinal the component's ordinal (zero based).
|
||||||
|
@ -87,11 +100,25 @@ public interface Composite extends DataType {
|
||||||
public abstract DataTypeComponent getComponent(int ordinal);
|
public abstract DataTypeComponent getComponent(int ordinal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of Data Type Components that make up this data type.
|
* Returns an array of Data Type Components that make up this composite including
|
||||||
* Returns an array of length 0 if there are no subcomponents.
|
* undefined filler components which may be present within an unaligned structure.
|
||||||
|
* The number of components corresponds to {@link #getNumComponents()}.
|
||||||
|
* @return list all components
|
||||||
*/
|
*/
|
||||||
public abstract DataTypeComponent[] getComponents();
|
public abstract DataTypeComponent[] getComponents();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of Data Type Components that make up this composite excluding
|
||||||
|
* undefined filler components which may be present within an unaligned structure.
|
||||||
|
* The number of components corresponds to {@link #getNumComponents()}. For Unions and
|
||||||
|
* aligned Structures this is equivalent to {@link #getComponents()}
|
||||||
|
* since they do not contain undefined components. Structures do not include the
|
||||||
|
* optional trailing flexible array component in this list
|
||||||
|
* (see {@link Structure#getFlexibleArrayComponent()}).
|
||||||
|
* @return list all explicitly defined components
|
||||||
|
*/
|
||||||
|
public abstract DataTypeComponent[] getDefinedComponents();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new datatype to the end of this composite. This is the preferred method
|
* Adds a new datatype to the end of this composite. This is the preferred method
|
||||||
* to use for adding components to an aligned structure for fixed-length dataTypes.
|
* to use for adding components to an aligned structure for fixed-length dataTypes.
|
||||||
|
@ -102,7 +129,7 @@ public interface Composite extends DataType {
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||||
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent add(DataType dataType);
|
public DataTypeComponent add(DataType dataType) throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new datatype to the end of this composite. This is the preferred method
|
* Adds a new datatype to the end of this composite. This is the preferred method
|
||||||
|
@ -118,7 +145,7 @@ public interface Composite extends DataType {
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||||
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent add(DataType dataType, int length);
|
public DataTypeComponent add(DataType dataType, int length) throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new datatype to the end of this composite. This is the preferred method
|
* Adds a new datatype to the end of this composite. This is the preferred method
|
||||||
|
@ -132,7 +159,8 @@ public interface Composite extends DataType {
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||||
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent add(DataType dataType, String name, String comment);
|
public DataTypeComponent add(DataType dataType, String name, String comment)
|
||||||
|
throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new bitfield to the end of this composite. This method is intended
|
* Adds a new bitfield to the end of this composite. This method is intended
|
||||||
|
@ -166,7 +194,8 @@ public interface Composite extends DataType {
|
||||||
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
* For example, suppose dt1 contains dt2. Therefore it is not valid
|
||||||
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
* to add dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent add(DataType dataType, int length, String name, String comment);
|
public DataTypeComponent add(DataType dataType, int length, String name, String comment)
|
||||||
|
throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||||
|
@ -181,7 +210,7 @@ public interface Composite extends DataType {
|
||||||
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent insert(int ordinal, DataType dataType);
|
public DataTypeComponent insert(int ordinal, DataType dataType) throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||||
|
@ -199,7 +228,8 @@ public interface Composite extends DataType {
|
||||||
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
* to insert dt1 to dt2 since this would cause a cyclic dependency.
|
||||||
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent insert(int ordinal, DataType dataType, int length);
|
public DataTypeComponent insert(int ordinal, DataType dataType, int length)
|
||||||
|
throws IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new datatype at the specified ordinal position in this composite.
|
* Inserts a new datatype at the specified ordinal position in this composite.
|
||||||
|
@ -220,7 +250,7 @@ public interface Composite extends DataType {
|
||||||
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
||||||
*/
|
*/
|
||||||
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
|
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
|
||||||
String comment);
|
String comment) throws ArrayIndexOutOfBoundsException, IllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the component at the given ordinal position.
|
* Deletes the component at the given ordinal position.
|
||||||
|
@ -229,7 +259,7 @@ public interface Composite extends DataType {
|
||||||
* @param ordinal the ordinal of the component to be deleted.
|
* @param ordinal the ordinal of the component to be deleted.
|
||||||
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
* @throws ArrayIndexOutOfBoundsException if component ordinal is out of bounds
|
||||||
*/
|
*/
|
||||||
public void delete(int ordinal);
|
public void delete(int ordinal) throws ArrayIndexOutOfBoundsException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the components at the given ordinal positions.
|
* Deletes the components at the given ordinal positions.
|
||||||
|
@ -238,7 +268,7 @@ public interface Composite extends DataType {
|
||||||
* @param ordinals the ordinals of the component to be deleted.
|
* @param ordinals the ordinals of the component to be deleted.
|
||||||
* @throws ArrayIndexOutOfBoundsException if any specified component ordinal is out of bounds
|
* @throws ArrayIndexOutOfBoundsException if any specified component ordinal is out of bounds
|
||||||
*/
|
*/
|
||||||
public void delete(int[] ordinals);
|
public void delete(int[] ordinals) throws ArrayIndexOutOfBoundsException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a data type is part of this data type. A data type could
|
* Check if a data type is part of this data type. A data type could
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class CompositeAlignmentHelper {
|
||||||
int allComponentsLCM = 1;
|
int allComponentsLCM = 1;
|
||||||
int packingAlignment = composite.getPackingValue();
|
int packingAlignment = composite.getPackingValue();
|
||||||
|
|
||||||
DataTypeComponent[] dataTypeComponents = composite.getComponents();
|
DataTypeComponent[] dataTypeComponents = composite.getDefinedComponents();
|
||||||
for (DataTypeComponent dataTypeComponent : dataTypeComponents) {
|
for (DataTypeComponent dataTypeComponent : dataTypeComponents) {
|
||||||
int impartedAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
|
int impartedAlignment = CompositeAlignmentHelper.getPackedAlignment(dataOrganization,
|
||||||
packingAlignment, dataTypeComponent);
|
packingAlignment, dataTypeComponent);
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
@ -25,26 +24,35 @@ import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.NotYetImplementedException;
|
import ghidra.util.exception.NotYetImplementedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common implementation methods for structure and union
|
* Common implementation methods for structure and union
|
||||||
*/
|
*/
|
||||||
public abstract class CompositeDataTypeImpl extends GenericDataType implements Composite {
|
public abstract class CompositeDataTypeImpl extends GenericDataType implements Composite {
|
||||||
private final static long serialVersionUID = 1;
|
private final static long serialVersionUID = 1;
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
protected boolean aligned = false; //WARNING, changing the initial value for this will cause
|
protected boolean aligned = false; // WARNING, changing the initial value for this will cause
|
||||||
// subtle errors - One I know of is in the StructureDataType
|
// subtle errors - One I know of is in the StructureDataType
|
||||||
// copyComponent method. It has built in assumptions about this.
|
// copyComponent method. It has built in assumptions about this.
|
||||||
|
|
||||||
protected AlignmentType alignmentType = AlignmentType.DEFAULT_ALIGNED;
|
protected AlignmentType alignmentType = AlignmentType.DEFAULT_ALIGNED;
|
||||||
protected int packingValue = NOT_PACKING;
|
protected int packingValue = NOT_PACKING;
|
||||||
protected int externalAlignment = DEFAULT_ALIGNMENT_VALUE;
|
protected int externalAlignment = DEFAULT_ALIGNMENT_VALUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty CompositeDataType with the specified name.
|
* Construct a new composite with the given name
|
||||||
* @param path the category path indicating where this data type is located.
|
*
|
||||||
* @param name the data type's name
|
* @param path the category path indicating where this
|
||||||
* @param dataTypeManager the data type manager associated with this data type. This can be null.
|
* data type is located.
|
||||||
* Also, the data type manager may not contain this actual data type.
|
* @param name the name of the new structure
|
||||||
|
* @param universalID the id for the data type
|
||||||
|
* @param sourceArchive the source archive for this data type
|
||||||
|
* @param lastChangeTime the last time this data type was changed
|
||||||
|
* @param lastChangeTimeInSourceArchive the last time this data type was changed
|
||||||
|
* in its source archive.
|
||||||
|
* @param dtm the data type manager associated with
|
||||||
|
* this data type. This can be null. Also,
|
||||||
|
* the data type manager may not yet
|
||||||
|
* contain this actual data type.
|
||||||
*/
|
*/
|
||||||
CompositeDataTypeImpl(CategoryPath path, String name, UniversalID universalID,
|
CompositeDataTypeImpl(CategoryPath path, String name, UniversalID universalID,
|
||||||
SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive,
|
SourceArchive sourceArchive, long lastChangeTime, long lastChangeTimeInSourceArchive,
|
||||||
|
@ -60,13 +68,15 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the preferred length for a new component. For Unions and internally aligned
|
* Get the preferred length for a new component. For Unions and internally
|
||||||
* structures the preferred component length for a fixed-length dataType will be the
|
* aligned structures the preferred component length for a fixed-length dataType
|
||||||
* length of that dataType. Otherwise the length returned will be no larger than the
|
* will be the length of that dataType. Otherwise the length returned will be no
|
||||||
* specified length.
|
* larger than the specified length.
|
||||||
|
*
|
||||||
* @param dataType new component datatype
|
* @param dataType new component datatype
|
||||||
* @param length constrained length or -1 to force use of dataType size. Dynamic types
|
* @param length constrained length or -1 to force use of dataType size.
|
||||||
* such as string must have a positive length specified.
|
* Dynamic types such as string must have a positive length
|
||||||
|
* specified.
|
||||||
* @return preferred component length
|
* @return preferred component length
|
||||||
*/
|
*/
|
||||||
protected int getPreferredComponentLength(DataType dataType, int length) {
|
protected int getPreferredComponentLength(DataType dataType, int length) {
|
||||||
|
@ -98,15 +108,15 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method throws an exception if the indicated data type is an ancestor
|
* This method throws an exception if the indicated data type is an ancestor of
|
||||||
* of this data type. In other words, the specified data type has a component
|
* this data type. In other words, the specified data type has a component or
|
||||||
* or sub-component containing this data type.
|
* sub-component containing this data type.
|
||||||
|
*
|
||||||
* @param dataType the data type
|
* @param dataType the data type
|
||||||
* @throws IllegalArgumentException if the data type is an ancestor of this
|
* @throws IllegalArgumentException if the data type is an ancestor of this data
|
||||||
* data type.
|
* type.
|
||||||
*/
|
*/
|
||||||
protected void checkAncestry(DataType dataType) {
|
protected void checkAncestry(DataType dataType) throws IllegalArgumentException {
|
||||||
// TODO: cyclic checks are easily bypassed by renaming multiple composite instances
|
|
||||||
if (this.equals(dataType)) {
|
if (this.equals(dataType)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"Data type " + getDisplayName() + " can't contain itself.");
|
"Data type " + getDisplayName() + " can't contain itself.");
|
||||||
|
@ -118,8 +128,9 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method throws an exception if the indicated data type is not
|
* This method throws an exception if the indicated data type is not a valid
|
||||||
* a valid data type for a component of this composite data type.
|
* data type for a component of this composite data type.
|
||||||
|
*
|
||||||
* @param dataType the data type to be checked.
|
* @param dataType the data type to be checked.
|
||||||
* @throws IllegalArgumentException if the data type is invalid.
|
* @throws IllegalArgumentException if the data type is invalid.
|
||||||
*/
|
*/
|
||||||
|
@ -143,11 +154,12 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle replacement of datatype which may impact bitfield datatype.
|
* Handle replacement of datatype which may impact bitfield datatype.
|
||||||
|
*
|
||||||
* @param bitfieldComponent bitfield component
|
* @param bitfieldComponent bitfield component
|
||||||
* @param oldDt affected datatype which has been removed or replaced
|
* @param oldDt affected datatype which has been removed or replaced
|
||||||
* @param newDt replacement datatype
|
* @param newDt replacement datatype
|
||||||
* @return true if bitfield component was modified
|
* @return true if bitfield component was modified
|
||||||
* @throws InvalidDataTypeException if new datatype is not
|
* @throws InvalidDataTypeException if new datatype is not
|
||||||
*/
|
*/
|
||||||
protected boolean updateBitFieldDataType(DataTypeComponentImpl bitfieldComponent,
|
protected boolean updateBitFieldDataType(DataTypeComponentImpl bitfieldComponent,
|
||||||
DataType oldDt, DataType newDt) throws InvalidDataTypeException {
|
DataType oldDt, DataType newDt) throws InvalidDataTypeException {
|
||||||
|
@ -323,7 +335,8 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify any parent data types that this composite data type's alignment has changed.
|
* Notify any parent data types that this composite data type's alignment has
|
||||||
|
* changed.
|
||||||
*/
|
*/
|
||||||
protected void notifyAlignmentChanged() {
|
protected void notifyAlignmentChanged() {
|
||||||
DataType[] parents = getParents();
|
DataType[] parents = getParents();
|
||||||
|
@ -336,10 +349,11 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the internal alignment of components within this composite based on the current
|
* Adjusts the internal alignment of components within this composite based on
|
||||||
* settings of the internal alignment, packing, alignment type and minimum alignment value.
|
* the current settings of the internal alignment, packing, alignment type and
|
||||||
* This method should be called whenever any of the above settings are changed or whenever
|
* minimum alignment value. This method should be called whenever any of the
|
||||||
* a components data type is changed or a component is added or removed.
|
* above settings are changed or whenever a components data type is changed or a
|
||||||
|
* component is added or removed.
|
||||||
*/
|
*/
|
||||||
protected abstract void adjustInternalAlignment();
|
protected abstract void adjustInternalAlignment();
|
||||||
|
|
||||||
|
@ -373,11 +387,14 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump all components for use in {@link #toString()} representation.
|
* Dump all components for use in {@link #toString()} representation.
|
||||||
|
*
|
||||||
* @param buffer string buffer
|
* @param buffer string buffer
|
||||||
* @param pad padding to be used with each component output line
|
* @param pad padding to be used with each component output line
|
||||||
*/
|
*/
|
||||||
protected void dumpComponents(StringBuilder buffer, String pad) {
|
protected void dumpComponents(StringBuilder buffer, String pad) {
|
||||||
for (DataTypeComponent dtc : getComponents()) {
|
// limit output of filler components for unaligned structures
|
||||||
|
DataTypeComponent[] components = getDefinedComponents();
|
||||||
|
for (DataTypeComponent dtc : components) {
|
||||||
DataType dataType = dtc.getDataType();
|
DataType dataType = dtc.getDataType();
|
||||||
buffer.append(pad + dtc.getOffset());
|
buffer.append(pad + dtc.getOffset());
|
||||||
buffer.append(pad + dataType.getName());
|
buffer.append(pad + dataType.getName());
|
||||||
|
|
|
@ -558,14 +558,7 @@ public class DataOrganizationImpl implements DataOrganization {
|
||||||
|
|
||||||
// Check each component and get the least common multiple of their forced minimum alignments.
|
// Check each component and get the least common multiple of their forced minimum alignments.
|
||||||
int componentForcedLCM = 0;
|
int componentForcedLCM = 0;
|
||||||
DataTypeComponent[] dataTypeComponents;
|
for (DataTypeComponent dataTypeComponent : composite.getDefinedComponents()) {
|
||||||
if (composite instanceof Structure) {
|
|
||||||
dataTypeComponents = ((Structure) composite).getDefinedComponents();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dataTypeComponents = composite.getComponents();
|
|
||||||
}
|
|
||||||
for (DataTypeComponent dataTypeComponent : dataTypeComponents) {
|
|
||||||
if (dataTypeComponent.isBitFieldComponent()) {
|
if (dataTypeComponent.isBitFieldComponent()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.docking.settings.SettingsDefinition;
|
import ghidra.docking.settings.SettingsDefinition;
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
@ -261,6 +260,9 @@ public interface DataType {
|
||||||
/**
|
/**
|
||||||
* Returns true if the given dataType is equivalent to this dataType. The
|
* Returns true if the given dataType is equivalent to this dataType. The
|
||||||
* precise meaning of "equivalent" is dataType dependent.
|
* precise meaning of "equivalent" is dataType dependent.
|
||||||
|
* <br>
|
||||||
|
* NOTE: if invoked by a DB object or manager it should be invoked on the
|
||||||
|
* DataTypeDB object passing the other datatype as the argument.
|
||||||
* @param dt the dataType being tested for equivalence.
|
* @param dt the dataType being tested for equivalence.
|
||||||
* @return true if the if the given dataType is equivalent to this dataType.
|
* @return true if the if the given dataType is equivalent to this dataType.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public abstract class DataTypeConflictHandler {
|
public abstract class DataTypeConflictHandler {
|
||||||
|
@ -61,13 +59,6 @@ public abstract class DataTypeConflictHandler {
|
||||||
RENAME_AND_ADD, USE_EXISTING, REPLACE_EXISTING;
|
RENAME_AND_ADD, USE_EXISTING, REPLACE_EXISTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Due to the locking concerns which can arise with a DataTypeConflictHandler,
|
|
||||||
* definition of new implementations must be done here.
|
|
||||||
*/
|
|
||||||
private DataTypeConflictHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static DataTypeConflictHandler DEFAULT_HANDLER = new DataTypeConflictHandler() {
|
public final static DataTypeConflictHandler DEFAULT_HANDLER = new DataTypeConflictHandler() {
|
||||||
@Override
|
@Override
|
||||||
public ConflictResult resolveConflict(DataType addedDataType, DataType existingDataType) {
|
public ConflictResult resolveConflict(DataType addedDataType, DataType existingDataType) {
|
||||||
|
@ -168,281 +159,52 @@ public abstract class DataTypeConflictHandler {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This {@link DataTypeConflictHandler conflict handler} attempts to match conflicting
|
* This {@link DataTypeConflictHandler conflict handler} behaves similar to
|
||||||
* {@link Composite composite data types} (structure or union) when they have compatible
|
* the {@link #DEFAULT_HANDLER} with the difference being that a
|
||||||
* data layouts. (Data types that are exactly equiv will not be subjected to conflict
|
* empty composite (see {@link Composite#isNotYetDefined()}) will be
|
||||||
* handling and will never reach here)
|
* replaced by a similar non-empty composite type. Alignment (e.g., packing)
|
||||||
* <p>
|
* is not considered when determining conflict resolution.
|
||||||
* A default/empty sized structure, or structures with the same size are candidates
|
* <br>
|
||||||
* for matching.
|
* For datatypes originating from a source archive with matching ID, the
|
||||||
* <p>
|
* replacment strategy will utilize the implementation with the
|
||||||
* Structures that have a subset of the other's field definition are candidates for matching.
|
* latest timestamp.
|
||||||
* <p>
|
* <br>
|
||||||
* When a candidate data type is matched with an existing data type, this conflict handler
|
* Unlike the {@link #DEFAULT_HANDLER}, follow-on dependency datatype
|
||||||
* will specify that the new data type is:<p>
|
* resolutions will retain the same conflict resolution strategy.
|
||||||
* <ul>
|
|
||||||
* <li>discarded and replaced by the existing data type ({@link ConflictResult#USE_EXISTING})
|
|
||||||
* <li>used to overwrite the existing data type ({@link ConflictResult#REPLACE_EXISTING})
|
|
||||||
* </ul>
|
|
||||||
* or the candidate data type was <b>NOT</b> matched with an existing data type, and the new data type is:<p>
|
|
||||||
* <ul>
|
|
||||||
* <li>kept, but renamed with a .conflictNNNN suffix to make it unique ({@link ConflictResult#RENAME_AND_ADD})
|
|
||||||
* </ul>
|
|
||||||
* <b>NOTE:</b> structures with alignment (instead of being statically laid out) are not
|
|
||||||
* treated specially and will not match other aligned or non-aligned structures.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public final static DataTypeConflictHandler REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER =
|
public final static DataTypeConflictHandler REPLACE_EMPTY_STRUCTS_OR_RENAME_AND_ADD_HANDLER =
|
||||||
new DataTypeConflictHandler() {
|
new DataTypeConflictHandler() {
|
||||||
|
|
||||||
/**
|
private ConflictResult resolveConflictReplaceEmpty(DataType addedDataType,
|
||||||
* Returns true if src can overwrite the target composite based on size
|
DataType existingDataType) {
|
||||||
* @param src
|
if (addedDataType.isNotYetDefined()) {
|
||||||
* @param target
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean isSizeCompatible(Composite src, Composite target) {
|
|
||||||
return (target.getLength() <= 1) || (src.getLength() == target.getLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the {@link Composite composite} is empty (to get around the lying that
|
|
||||||
* {@link Composite#getLength()} does.)
|
|
||||||
* @param composite
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean isCompositeEmpty(Composite composite) {
|
|
||||||
return composite.getLength() <= 1 && composite.getNumComponents() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the given composite is filled with default values (all components are default).
|
|
||||||
* @param composite composite to check
|
|
||||||
* @return true if default and false otherwise
|
|
||||||
*/
|
|
||||||
private boolean isCompositeDefault(Composite composite) {
|
|
||||||
if (composite.getLength() == composite.getNumComponents()) {
|
|
||||||
DataTypeComponent[] comps = composite.getComponents();
|
|
||||||
boolean isDefault = true;
|
|
||||||
for (int i = 0; i < comps.length; i++) {
|
|
||||||
if (comps[i].getDataType() != DataType.DEFAULT) {
|
|
||||||
isDefault = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isDefault) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isCompositePart(Composite full, Composite part,
|
|
||||||
Map<DataType, DataType> visitedDataTypes) {
|
|
||||||
if (full instanceof Structure && part instanceof Structure) {
|
|
||||||
return isStructurePart((Structure) full, (Structure) part, visitedDataTypes);
|
|
||||||
}
|
|
||||||
else if (full instanceof Union && part instanceof Union) {
|
|
||||||
return isUnionPart((Union) full, (Union) part, visitedDataTypes);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if one union is a subset of another union.
|
|
||||||
* <p>
|
|
||||||
* Each component of the candidate partial union must be present in the
|
|
||||||
* 'full' union and must be 'equiv'.
|
|
||||||
* <p>
|
|
||||||
* Order of components is ignored, except for unnamed components, which receive
|
|
||||||
* a default name created using their ordinal position.
|
|
||||||
*
|
|
||||||
* @param full {@link Union} datatype that is expected to be a superset of the next param.
|
|
||||||
* @param part {@link Union} datatype that is expected to be a subset of the previous param.
|
|
||||||
* @param visitedDataTypes identity map of datatypes to prevent loops.
|
|
||||||
* @return true if part is a subset (or equal) to full.
|
|
||||||
*/
|
|
||||||
private boolean isUnionPart(Union full, Union part,
|
|
||||||
Map<DataType, DataType> visitedDataTypes) {
|
|
||||||
if (full.getLength() < part.getLength()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, DataTypeComponent> fullComponentsByName = new HashMap<>();
|
|
||||||
for (DataTypeComponent dtc : full.getComponents()) {
|
|
||||||
String name = dtc.getFieldName();
|
|
||||||
if (name == null) {
|
|
||||||
name = dtc.getDefaultFieldName();
|
|
||||||
}
|
|
||||||
fullComponentsByName.put(name, dtc);
|
|
||||||
}
|
|
||||||
for (DataTypeComponent dtc : part.getComponents()) {
|
|
||||||
String name = dtc.getFieldName();
|
|
||||||
if (name == null) {
|
|
||||||
name = dtc.getDefaultFieldName();
|
|
||||||
}
|
|
||||||
DataTypeComponent fullDTC = fullComponentsByName.get(name);
|
|
||||||
if (fullDTC == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DataType partDT = dtc.getDataType();
|
|
||||||
DataType fullDT = fullDTC.getDataType();
|
|
||||||
if (doRelaxedCompare(partDT, fullDT,
|
|
||||||
visitedDataTypes) == ConflictResult.RENAME_AND_ADD) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns true if one structure is a partial definition of another structure.
|
|
||||||
* <p>
|
|
||||||
* Each defined component in the candidate partial structure must be present
|
|
||||||
* in the 'full' structure and must be equiv.
|
|
||||||
* <p>
|
|
||||||
* The order and sparseness of the candidate partial structure is not important,
|
|
||||||
* only that all of its defined components are present in the full structure.
|
|
||||||
* <p>
|
|
||||||
*/
|
|
||||||
private boolean isStructurePart(Structure full, Structure part,
|
|
||||||
Map<DataType, DataType> visitedDataTypes) {
|
|
||||||
// Both structures should be equal in length
|
|
||||||
if (full.getLength() != part.getLength()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean[] fullCompsUsedFlag = new boolean[full.getComponents().length];
|
|
||||||
DataTypeComponent[] partComps = part.getDefinedComponents();
|
|
||||||
|
|
||||||
// Find a match in the full structure's component list for each
|
|
||||||
// component in the partial structure.
|
|
||||||
// Use resolveConflict() == USE_EXISTING to test for equiv in addition to
|
|
||||||
// isEquiv().
|
|
||||||
// Ensure that two components in the partial struct don't map to the same
|
|
||||||
// component in the full structure.
|
|
||||||
for (int i = 0; i < partComps.length; i++) {
|
|
||||||
DataTypeComponent partDTC = partComps[i];
|
|
||||||
DataTypeComponent fullDTCAt = full.getComponentAt(partDTC.getOffset());
|
|
||||||
int fullOrd = fullDTCAt.getOrdinal();
|
|
||||||
if (fullCompsUsedFlag[fullOrd]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DataType partDT = partDTC.getDataType();
|
|
||||||
DataType fullDT = fullDTCAt.getDataType();
|
|
||||||
if (doRelaxedCompare(partDT, fullDT,
|
|
||||||
visitedDataTypes) == ConflictResult.RENAME_AND_ADD) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fullCompsUsedFlag[fullOrd] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strict compare will compare its parameters.
|
|
||||||
* The contents of these datatypes (ie. contents of structs, pointers, arrays)
|
|
||||||
* will be compared with relaxed typedef checking.
|
|
||||||
*/
|
|
||||||
private ConflictResult doStrictCompare(DataType addedDataType,
|
|
||||||
DataType existingDataType, Map<DataType, DataType> visitedDataTypes) {
|
|
||||||
visitedDataTypes.put(existingDataType, addedDataType);
|
|
||||||
if (existingDataType.isEquivalent(addedDataType)) {
|
|
||||||
return ConflictResult.USE_EXISTING;
|
return ConflictResult.USE_EXISTING;
|
||||||
}
|
}
|
||||||
else if (existingDataType instanceof Composite &&
|
if (existingDataType.isNotYetDefined()) {
|
||||||
addedDataType instanceof Composite) {
|
return ConflictResult.REPLACE_EXISTING;
|
||||||
Composite existingComposite = (Composite) existingDataType;
|
|
||||||
Composite addedComposite = (Composite) addedDataType;
|
|
||||||
|
|
||||||
// Check to see if we are adding a default/empty data type
|
|
||||||
if ((isCompositeEmpty(addedComposite) || isCompositeDefault(addedComposite)) &&
|
|
||||||
isSizeCompatible(existingComposite, addedComposite)) {
|
|
||||||
return ConflictResult.USE_EXISTING;
|
|
||||||
}
|
|
||||||
// Check to see if the existing type is a default/empty data type
|
|
||||||
if ((isCompositeEmpty(existingComposite) ||
|
|
||||||
isCompositeDefault(existingComposite)) &&
|
|
||||||
isSizeCompatible(addedComposite, existingComposite)) {
|
|
||||||
return ConflictResult.REPLACE_EXISTING;
|
|
||||||
}
|
|
||||||
// Check to see if the added type is part of the existing type first to
|
|
||||||
// generate more USE_EXISTINGS when possible.
|
|
||||||
if (isCompositePart(existingComposite, addedComposite, visitedDataTypes)) {
|
|
||||||
return ConflictResult.USE_EXISTING;
|
|
||||||
}
|
|
||||||
// Check to see if the existing type is a part of the added type
|
|
||||||
if (isCompositePart(addedComposite, existingComposite, visitedDataTypes)) {
|
|
||||||
return ConflictResult.REPLACE_EXISTING;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (existingDataType instanceof TypeDef && addedDataType instanceof TypeDef) {
|
|
||||||
TypeDef addedTypeDef = (TypeDef) addedDataType;
|
|
||||||
TypeDef existingTypeDef = (TypeDef) existingDataType;
|
|
||||||
return doRelaxedCompare(addedTypeDef.getBaseDataType(),
|
|
||||||
existingTypeDef.getBaseDataType(), visitedDataTypes);
|
|
||||||
}
|
|
||||||
else if (existingDataType instanceof Array && addedDataType instanceof Array) {
|
|
||||||
Array addedArray = (Array) addedDataType;
|
|
||||||
Array existingArray = (Array) existingDataType;
|
|
||||||
|
|
||||||
if (addedArray.getNumElements() != existingArray.getNumElements() ||
|
|
||||||
addedArray.getElementLength() != existingArray.getElementLength()) {
|
|
||||||
return ConflictResult.RENAME_AND_ADD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return doRelaxedCompare(addedArray.getDataType(), existingArray.getDataType(),
|
|
||||||
visitedDataTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConflictResult.RENAME_AND_ADD;
|
return ConflictResult.RENAME_AND_ADD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Relaxed compare will take liberties in skipping typedefs to try to compare
|
|
||||||
* the types that the typedef are hiding. This is useful when comparing types
|
|
||||||
* that were embedded in differently compiled files, where you might end up with
|
|
||||||
* a raw basetype in one file and a typedef to a basetype in another file.
|
|
||||||
*/
|
|
||||||
private ConflictResult doRelaxedCompare(DataType addedDataType,
|
|
||||||
DataType existingDataType, Map<DataType, DataType> visitedDataTypes) {
|
|
||||||
|
|
||||||
if (existingDataType instanceof Pointer && addedDataType instanceof Pointer) {
|
|
||||||
DataType ptrAddedDataType = ((Pointer) addedDataType).getDataType();
|
|
||||||
DataType ptrExistingDataType = ((Pointer) existingDataType).getDataType();
|
|
||||||
// only descend into the pointed-to-type if we haven't looked at it before.
|
|
||||||
// if you don't do this, you will have a stack-overflow issue when a struct
|
|
||||||
// has a pointer to its same type.
|
|
||||||
if (!visitedDataTypes.containsKey(ptrExistingDataType)) {
|
|
||||||
visitedDataTypes.put(ptrExistingDataType, ptrAddedDataType);
|
|
||||||
addedDataType = ptrAddedDataType;
|
|
||||||
existingDataType = ptrExistingDataType;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// unwrap typedefs, possibly asymmetrically. (ie. only unwrap added vs. existing)
|
|
||||||
if (addedDataType instanceof TypeDef) {
|
|
||||||
addedDataType = ((TypeDef) addedDataType).getBaseDataType();
|
|
||||||
}
|
|
||||||
if (existingDataType instanceof TypeDef) {
|
|
||||||
existingDataType = ((TypeDef) existingDataType).getBaseDataType();
|
|
||||||
}
|
|
||||||
return doStrictCompare(addedDataType, existingDataType, visitedDataTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConflictResult resolveConflict(DataType addedDataType,
|
public ConflictResult resolveConflict(DataType addedDataType,
|
||||||
DataType existingDataType) {
|
DataType existingDataType) {
|
||||||
IdentityHashMap<DataType, DataType> visitedDataTypes = new IdentityHashMap<>();
|
if (addedDataType instanceof Structure) {
|
||||||
return doStrictCompare(addedDataType, existingDataType, visitedDataTypes);
|
if (existingDataType instanceof Structure) {
|
||||||
|
return resolveConflictReplaceEmpty(addedDataType, existingDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (addedDataType instanceof Union) {
|
||||||
|
if (existingDataType instanceof Union) {
|
||||||
|
return resolveConflictReplaceEmpty(addedDataType, existingDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ConflictResult.RENAME_AND_ADD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldUpdate(DataType sourceDataType, DataType localDataType) {
|
public boolean shouldUpdate(DataType sourceDataType, DataType localDataType) {
|
||||||
return false;
|
return sourceDataType.getLastChangeTime() > localDataType.getLastChangeTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.Iterator;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.*;
|
import ghidra.docking.settings.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@ package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,6 +97,18 @@ public interface DataTypeManager {
|
||||||
*/
|
*/
|
||||||
public DataType addDataType(DataType dataType, DataTypeConflictHandler handler);
|
public DataType addDataType(DataType dataType, DataTypeConflictHandler handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sequentially adds a collection of datatypes to this data manager.
|
||||||
|
* This method provides the added benefit of equivalence caching
|
||||||
|
* for improved performance.
|
||||||
|
* @param dataTypes collection of datatypes
|
||||||
|
* @param handler conflict handler
|
||||||
|
* @param monitor task monitor
|
||||||
|
* @throws CancelledException if monitor is cancelled
|
||||||
|
*/
|
||||||
|
public void addDataTypes(Collection<DataType> dataTypes, DataTypeConflictHandler handler,
|
||||||
|
TaskMonitor monitor) throws CancelledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an iterator over all the dataTypes in this manager
|
* Returns an iterator over all the dataTypes in this manager
|
||||||
* @return an iterator over all the dataTypes in this manager
|
* @return an iterator over all the dataTypes in this manager
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The listener interface for notification of changes to a DataTypeManager
|
* The listener interface for notification of changes to a DataTypeManager
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adapter for a Category change listener.
|
* Adapter for a Category change listener.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||||
import ghidra.util.datastruct.WeakSet;
|
import ghidra.util.datastruct.WeakSet;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -352,7 +351,7 @@ public class DataTypeWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsComposite(Composite container, Composite contained) {
|
private boolean containsComposite(Composite container, Composite contained) {
|
||||||
for (DataTypeComponent component : container.getComponents()) {
|
for (DataTypeComponent component : container.getDefinedComponents()) {
|
||||||
DataType dt = getBaseArrayTypedefType(component.getDataType());
|
DataType dt = getBaseArrayTypedefType(component.getDataType());
|
||||||
if (dt instanceof Composite && dt.getName().equals(contained.getName()) &&
|
if (dt instanceof Composite && dt.getName().equals(contained.getName()) &&
|
||||||
dt.isEquivalent(contained)) {
|
dt.isEquivalent(contained)) {
|
||||||
|
|
|
@ -18,7 +18,6 @@ package ghidra.program.model.data;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.docking.settings.SettingsDefinition;
|
import ghidra.docking.settings.SettingsDefinition;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.program.model.data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.docking.settings.Settings;
|
import ghidra.docking.settings.Settings;
|
||||||
import ghidra.program.database.data.DataTypeUtilities;
|
import ghidra.program.database.data.DataTypeUtilities;
|
||||||
import ghidra.program.model.lang.PrototypeModel;
|
import ghidra.program.model.lang.PrototypeModel;
|
||||||
|
@ -304,10 +303,10 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct
|
||||||
if (dt == this) {
|
if (dt == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(dt instanceof FunctionSignature)) {
|
if (!(dt instanceof FunctionDefinition)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return isEquivalentSignature((FunctionSignature) dt);
|
return isEquivalentSignature((FunctionDefinition) dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -409,4 +408,9 @@ public class FunctionDefinitionDataType extends GenericDataType implements Funct
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getPrototypeString(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.model.data;
|
package ghidra.program.model.data;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
|
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.InvalidNameException;
|
||||||
import ghidra.util.UniversalID;
|
import ghidra.util.UniversalID;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue