GP-3564 Add composite get component by name and removed

DuplicateNameException when renaming components.  Added GUI editor
warning when duplicate naming is used.
This commit is contained in:
ghidra1
2026-03-20 16:25:04 -04:00
parent f45091fd3f
commit 74fee41d58
44 changed files with 1408 additions and 1192 deletions
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,13 +15,14 @@
*/
package ghidra.app.cmd.data;
import java.util.Objects;
import ghidra.framework.cmd.Command;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
/**
* Command to rename a component in a data type.
* Command to rename a component in a {@link Composite} data type.
*
*/
public class RenameDataFieldCmd implements Command<Program> {
@@ -47,20 +48,20 @@ public class RenameDataFieldCmd implements Command<Program> {
statusMsg = "Null data type";
return false;
}
try {
comp.setFieldName(newName);
return true;
String name = InternalDataTypeComponent.cleanupFieldName(newName);
if (!Objects.equals(name, comp.getFieldName())) {
comp = comp.setFieldName(newName);
if (!Objects.equals(name, comp.getFieldName())) {
statusMsg = "Unable to rename component";
return false;
}
}
catch (DuplicateNameException e) {
statusMsg = "Type name already exists: " + newName;
}
return false;
return true;
}
/*
* (non-Javadoc)
* @see ghidra.framework.cmd.Command#getStatusMsg()
*/
@Override
public String getStatusMsg() {
return statusMsg;
@@ -802,13 +802,9 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
DataTypeComponent dtc = getComponent(startRowIndex);
// Set the field name and comment the same as before
try {
dtc.setFieldName(oldDtc.getFieldName());
}
catch (DuplicateNameException e) {
Msg.showError(this, null, "Unexcected Exception", "Exception applying field name", e);
}
dtc.setFieldName(oldDtc.getFieldName());
dtc.setComment(oldDtc.getComment());
fixSelection();
selectionChanged();
return dtc;
@@ -1165,32 +1161,32 @@ public abstract class CompEditorModel<T extends Composite> extends CompositeEdit
}
}
@Override
public void validateComponentName(int rowIndex, String name) throws UsrException {
if (nameExistsElsewhere(name, rowIndex)) {
throw new InvalidNameException("Name \"" + name + "\" already exists.");
}
}
@Override
public boolean setComponentName(int rowIndex, String name) throws InvalidNameException {
String oldName = getComponent(rowIndex).getFieldName();
if (Objects.equals(oldName, name)) {
name = InternalDataTypeComponent.cleanupFieldName(name); // will trim name if needed
DataTypeComponent component = getComponent(rowIndex);
if (Objects.equals(name, component.getDefaultFieldName())) {
name = null;
}
if (Objects.equals(name, component.getFieldName())) {
return false;
}
if (nameExistsElsewhere(name, rowIndex)) {
throw new InvalidNameException("Name \"" + name + "\" already exists.");
if (viewComposite.findComponent(name) != null) {
// Warn user and confirm rename when duplicate name is used
if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialog(null,
"Duplicate Field Name",
"Duplicate field name. Proceed with rename?",
"Rename!", OptionDialog.WARNING_MESSAGE)) {
return false;
}
}
return viewDTM.withTransaction("Set Component Name", () -> {
try {
getComponent(rowIndex).setFieldName(name); // setFieldName handles trimming
return true;
}
catch (DuplicateNameException exc) {
throw new InvalidNameException(exc.getMessage());
}
String newName = name;
return viewDTM.withTransaction("Set Field Name", () -> {
getComponent(rowIndex).setFieldName(newName);
return true;
});
}
@@ -1097,36 +1097,6 @@ abstract public class CompositeEditorModel<T extends Composite> extends Composit
return null;
}
/**
* Check for any data member in the composite with the specified name
* other than the component at the specified index.
*
* @param name the component name to look for.
* @param rowIndex index of the row (component).
*
* @return true if the name exists elsewhere.
*/
protected boolean nameExistsElsewhere(String name, int rowIndex) {
if (name != null) {
name = name.trim();
if (name.length() == 0) {
return false;
}
int numComponents = getNumComponents();
for (int i = 0; i < rowIndex && i < numComponents; i++) {
if (name.equals(getComponent(i).getFieldName())) {
return true;
}
}
for (int i = rowIndex + 1; i < numComponents; i++) {
if (name.equals(getComponent(i).getFieldName())) {
return true;
}
}
}
return false;
}
/**
* Determine if the data type is a valid one to place into the current structure being edited.
* If invalid, an exception will be thrown.
@@ -1145,8 +1145,8 @@ public abstract class CompositeEditorPanel<T extends Composite, M extends Compos
@Override
public boolean stopCellEditing() {
try {
model.validateComponentName(table.getEditingRow(),
((JTextComponent) getComponent()).getText());
String newName = ((JTextComponent) getComponent()).getText();
model.validateComponentName(table.getEditingRow(), newName);
fireEditingStopped();
return true;
}
@@ -1009,12 +1009,7 @@ class StructureEditorModel extends CompEditorModel<Structure> {
DataTypeComponent comp = getComponent(startRowIndex);
// Set the field name and comment the same as before
try {
comp.setFieldName(fieldName);
}
catch (DuplicateNameException exc) {
Msg.showError(this, null, null, null);
}
comp.setFieldName(fieldName);
comp.setComment(comment);
// Create any needed undefined data types.
@@ -1377,13 +1372,8 @@ class StructureEditorModel extends CompEditorModel<Structure> {
DataTypeComponent comp = getComponent(rowIndex);
// Set the field name and comment the same as before if unspecified
try {
if (comp.getFieldName() == null) {
comp.setFieldName(fieldName);
}
}
catch (DuplicateNameException exc) {
Msg.showError(this, null, null, null);
if (comp.getFieldName() == null) {
comp.setFieldName(fieldName);
}
if (StringUtils.isBlank(comp.getComment())) {
comp.setComment(comment);
@@ -19,12 +19,14 @@ import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.Date;
import java.util.Objects;
import javax.swing.*;
import org.apache.commons.lang3.StringUtils;
import docking.DialogComponentProvider;
import docking.widgets.OptionDialog;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.DataTypeSelectionEditor;
import ghidra.framework.cmd.Command;
@@ -439,9 +441,8 @@ public class EditDataFieldDialog extends DialogComponentProvider {
// replace that default type with a type that will force a record to be get created.
// We need a record to exist in order to set a comment or name.
DataType newtype = new Undefined1DataType();
DataTypeComponent newDtc =
struct.replaceAtOffset(dtc.getOffset(), newtype, 1, "tempName",
"Created by Edit Data Field action");
DataTypeComponent newDtc = struct.replaceAtOffset(dtc.getOffset(), newtype, 1,
"tempName", "Created by Edit Data Field action");
DataType oldDt = dtc.getDataType();
DataType editorDt = dataTypeEditor.getCellEditorValueAsDataType();
@@ -459,15 +460,28 @@ public class EditDataFieldDialog extends DialogComponentProvider {
return true;
}
String newName = InternalDataTypeComponent.cleanupFieldName(getNewFieldName());
DataTypeComponent dtc = composite.getComponent(ordinal);
try {
dtc.setFieldName(getNewFieldName());
if (Objects.equals(newName, dtc.getDefaultFieldName())) {
newName = null;
}
if (Objects.equals(newName, dtc.getFieldName())) {
return true;
}
catch (DuplicateNameException e) {
statusMessage = "Duplicate field name";
return false;
if (newName != null && composite.findComponent(newName) != null) {
// Warn user and confirm rename when duplicate name is used
if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialog(getComponent(),
"Duplicate Field Name",
"Duplicate field name. Proceed with rename?",
"Rename!", OptionDialog.WARNING_MESSAGE)) {
return false;
}
}
dtc.setFieldName(newName);
return true;
}
private boolean updateComment() {
@@ -512,8 +526,7 @@ public class EditDataFieldDialog extends DialogComponentProvider {
return;
}
DataTypeInstance dti =
DataTypeInstance.getDataTypeInstance(resolvedDt, -1, false);
DataTypeInstance dti = DataTypeInstance.getDataTypeInstance(resolvedDt, -1, false);
DataType dataType = dti.getDataType();
int length = dti.getLength();
String fieldName = dtc.getFieldName();
@@ -40,7 +40,6 @@ import ghidra.program.database.DatabaseObject;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@@ -707,10 +706,7 @@ public class StackEditorModel extends CompositeEditorModel<StackFrameDataType> {
@Override
public void validateComponentName(int currentIndex, String name) throws UsrException {
if (SymbolUtilities.containsInvalidChars(name)) {
throw new InvalidInputException(
"Symbol name \"" + name + "\"contains invalid characters.");
}
viewComposite.checkNameChange(currentIndex, name);
}
@Override
@@ -31,9 +31,11 @@ import ghidra.program.model.lang.ProgramArchitecture;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.*;
import ghidra.util.exception.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.InvalidInputException;
/**
* {@link StackFrameDataType} provides a {@link Structure} representation of a {@link StackFrame}
@@ -419,19 +421,15 @@ class StackFrameDataType implements Structure {
* @param name the new name. Null indicates the default name.
* @return true if name change was successful, else false
* @throws IndexOutOfBoundsException if specified ordinal is out of range
* @throws IllegalArgumentException if name is invalid
* @throws InvalidNameException if an invalid or duplicate name is specified
*/
public boolean setName(int ordinal, String name) throws IndexOutOfBoundsException {
public boolean setName(int ordinal, String name)
throws IndexOutOfBoundsException, InvalidNameException {
StackComponentWrapper comp = getComponent(ordinal);
String fieldName = comp.getFieldName();
if (name != null) {
name = name.trim();
if (name.length() == 0 || isDefaultName(name)) {
name = null;
}
}
name = checkNameChange(ordinal, name);
if (SystemUtilities.isEqual(name, fieldName)) {
return false;
@@ -445,17 +443,53 @@ class StackFrameDataType implements Structure {
comp = replace(comp.getOrdinal(), DataType.DEFAULT, 1, name, null);
}
else {
try {
comp.dtc.setFieldName(name);
}
catch (DuplicateNameException e) {
// FIXME: Inconsistent API / how should names be validated and on which methods?
return false;
}
comp.dtc.setFieldName(name);
}
return true;
}
/**
* Preliminary check if a component may be renamed to the specified name.
*
* @param ordinal the ordinal
* @param name the new name. Null indicates the default name.
* @return name which will actually be applied after cleaning
* @throws IndexOutOfBoundsException if specified ordinal is out of range
* @throws InvalidNameException if an invalid or duplicate name is specified
*/
public String checkNameChange(int ordinal, String name) throws InvalidNameException {
if (name != null) {
name = name.trim();
if (name.length() == 0 || isDefaultName(name)) {
name = null;
}
}
if (name != null) {
try {
SymbolUtilities.validateName(name);
}
catch (InvalidInputException e) {
throw new InvalidNameException(e.getMessage());
}
}
StackComponentWrapper comp = getComponent(ordinal);
String oldFieldName = comp.getFieldName();
if (SystemUtilities.isEqual(name, oldFieldName)) {
return oldFieldName;
}
SymbolTable symbolTable = function.getProgram().getSymbolTable();
if (name != null && (wrappedStruct.findComponent(name) != null ||
!symbolTable.getSymbols(name, function).isEmpty())) {
// Stack or symbol table storage has a conflicting variable or symbol with the same name
throw new InvalidNameException("Name conflicts with another variable: " + name);
}
return name;
}
/**
* Sets the comment at the specified ordinal.
*
@@ -834,7 +868,7 @@ class StackFrameDataType implements Structure {
* Unsupported method. Must use {@link StackFrameDataType#setComment(int, String)}.
*/
@Override
public void setComment(String comment) {
public DataTypeComponent setComment(String comment) {
throw new UnsupportedOperationException();
}
@@ -920,7 +954,7 @@ class StackFrameDataType implements Structure {
* Unsupported method. Must use {@link StackFrameDataType#setName(int, String)}.
*/
@Override
public void setFieldName(String fieldName) throws DuplicateNameException {
public DataTypeComponent setFieldName(String fieldName) {
throw new UnsupportedOperationException();
}
@@ -1070,6 +1104,11 @@ class StackFrameDataType implements Structure {
return new StackComponentWrapper(dtc);
}
@Override
public DataTypeComponent findComponent(String name) {
throw new UnsupportedOperationException();
}
@Override
public void clearComponent(int ordinal) throws IndexOutOfBoundsException {
wrappedStruct.clearComponent(ordinal);
@@ -1193,6 +1232,19 @@ class StackFrameDataType implements Structure {
name = null;
}
if (name != null) {
try {
SymbolUtilities.validateName(name);
}
catch (InvalidInputException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
// NOTE: We don't check for duplicate name since this method is intended for stack
// frame internal use only where any required conflict check should already have been
// performed
DataTypeComponent dtc = wrappedStruct.replace(ordinal, dataType, length, name, comment);
checkForStackGrowth();
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,18 +17,18 @@ package ghidra.app.util;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Objects;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import docking.DialogComponentProvider;
import docking.widgets.OptionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeComponent;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.exception.DuplicateNameException;
public class EditFieldNameDialog extends DialogComponentProvider {
@@ -77,36 +77,39 @@ public class EditFieldNameDialog extends DialogComponentProvider {
@Override
protected void okCallback() {
String newName = fieldName.getText().trim();
if (newName.equals(getCurrentFieldName())) {
String newName = InternalDataTypeComponent.cleanupFieldName(fieldName.getText());
if (Objects.equals(newName, dtComp.getDefaultFieldName())) {
newName = null;
}
if (Objects.equals(newName, dtComp.getFieldName())) {
close();
return;
}
boolean success = false;
DataType parent = dtComp.getParent();
if (newName != null && parent instanceof Composite composite &&
composite.findComponent(newName) != null) {
// Warn user and confirm rename when duplicate name is used
if (OptionDialog.OPTION_ONE != OptionDialog.showOptionDialog(getComponent(),
"Duplicate Field Name",
"Duplicate field name. Proceed with rename?",
"Rename!", OptionDialog.WARNING_MESSAGE)) {
return;
}
}
int txId = program.startTransaction("Edit Field Name");
try {
dtComp.setFieldName(newName);
DataType parent = dtComp.getParent();
if (parent != null) {
long timeNow = System.currentTimeMillis();
parent.setLastChangeTime(timeNow);
}
success = true;
}
catch (DuplicateNameException e) {
setStatusText(e.getMessage());
}
finally {
program.endTransaction(txId, true);
}
if (success) {
dtComp = null;
program = null;
close();
}
dtComp = null;
program = null;
close();
}
public void editField(DataTypeComponent dataTypeComponent, Program p) {
@@ -23,7 +23,6 @@ import ghidra.docking.settings.SettingsImpl;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorState;
import ghidra.pcode.memstate.MemoryState;
import ghidra.pcode.utils.Utils;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.model.address.*;
@@ -296,10 +295,9 @@ public abstract class PCodeTestAbstractControlBlock {
}
protected int getStructureComponent(Structure testInfoStruct, String fieldName) {
for (DataTypeComponent component : testInfoStruct.getDefinedComponents()) {
if (fieldName.equals(component.getFieldName())) {
return component.getOffset();
}
DataTypeComponent component = testInfoStruct.findComponent(fieldName);
if (component != null) {
return component.getOffset();
}
throw new RuntimeException(fieldName + " field not found within " +
testInfoStruct.getName() + " structure definition at " + infoStructAddr.toString(true));
@@ -932,13 +932,8 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"),
"IntStruct");
try {
DataTypeComponent dtc = s.getComponent(0);
dtc.setFieldName("Field One");
}
catch (DuplicateNameException e) {
Assert.fail("Got duplicate name exception!");
}
DataTypeComponent dtc = s.getComponent(0);
dtc.setFieldName("Field One");
}
@Override
@@ -950,14 +945,9 @@ public class DataTypeMerge1Test extends AbstractDataTypeMergeTest {
(Structure) dtm.getDataType(new CategoryPath("/Category1/Category2/Category3"),
"IntStruct");
try {
DataTypeComponent dtc = s.getComponent(2);
dtc.setFieldName("My Field Three");
dtc.setComment("my comments for Field 3");
}
catch (DuplicateNameException e) {
Assert.fail("Got duplicate name exception!");
}
DataTypeComponent dtc = s.getComponent(2);
dtc.setFieldName("My Field Three");
dtc.setComment("my comments for Field 3");
}
});
executeMerge(DataTypeMergeManager.OPTION_MY);
@@ -23,7 +23,6 @@ import org.junit.Test;
import ghidra.program.database.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.util.exception.DuplicateNameException;
/**
* More data type merge tests.
@@ -122,17 +121,10 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
Union union = (Union) dt;
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Union union = (Union) dt;
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
}
});
executeMerge();
@@ -190,17 +182,11 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
Union union = (Union) dt;
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Union union = (Union) dt;
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
}
});
executeMerge();
@@ -1209,26 +1195,18 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
Union union = (Union) dt;
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_DLL_Table", s);
Pointer p = PointerDataType.getPointer(td, 4);
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(p);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Union union = (Union) dt;
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
TypeDef td = new TypedefDataType(new CategoryPath("/Category1"), "TD_DLL_Table", s);
Pointer p = PointerDataType.getPointer(td, 4);
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(p);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1287,23 +1265,16 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
DataType dt =
dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
Union union = (Union) dt;
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Union union = (Union) dt;
union.add(new FloatDataType(), "Float Field", "my comments");
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1375,28 +1346,20 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
union.add(new FloatDataType(), "Float Field", "my comments");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1447,28 +1410,21 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1529,28 +1485,21 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
union.add(enumm);
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1601,30 +1550,23 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
union.add(td);
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
union.add(td);
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1704,33 +1646,26 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
Pointer p = PointerDataType.getPointer(td, 4);// TD_MyEnum *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * * *
union.add(p);
union.add(new FloatDataType(), "Float Field", "my comments");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
Pointer p = PointerDataType.getPointer(td, 4);// TD_MyEnum *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * * *
union.add(p);
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -1790,37 +1725,29 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
Union union =
(Union) dtm.getDataType(new CategoryPath("/Category1/Category2"), "CoolUnion");
try {
DataTypeComponent dtc = union.add(new FloatDataType());
dtc.setComment("my comments");
dtc.setFieldName("Float Field");
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
Pointer p = PointerDataType.getPointer(td, 4);// TD_MyEnum *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * * *
union.add(new FloatDataType(), "Float Field", "my comments");
// create an array of TD_MyEnum * * *
Array array = new ArrayDataType(p, 5, p.getLength());
dtc = union.add(array);
dtc.setComment("an array");
dtc.setFieldName("array field name");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
Enum enumm = new EnumDataType(new CategoryPath("/Category1"), "MyEnum", 1);
enumm.add("one", 1);
enumm.add("two", 2);
enumm.add("three", 3);
TypeDef td =
new TypedefDataType(new CategoryPath("/Category1"), "TD_MyEnum", enumm);
Pointer p = PointerDataType.getPointer(td, 4);// TD_MyEnum *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * *
p = PointerDataType.getPointer(p, 4);// TD_MyEnum * * *
union =
new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
catch (DuplicateNameException e) {
Assert.fail("Got Duplicate name exception!");
}
// create an array of TD_MyEnum * * *
Array array = new ArrayDataType(p, 5, p.getLength());
union.add(array, "array field name", "an array");
Structure s = (Structure) dtm.getDataType(CategoryPath.ROOT, "DLL_Table");
s.add(new WordDataType());
union = new UnionDataType(new CategoryPath("/Category1/Category2"), "AnotherUnion");
union.add(s);
union.add(new ByteDataType());
dtm.addDataType(union, DataTypeConflictHandler.DEFAULT_HANDLER);
}
});
executeMerge();
@@ -550,11 +550,6 @@ public class StackEditorCellEditTest extends AbstractStackEditorTest {
triggerActionInCellEditor(KeyEvent.VK_UP);
waitForSwing();
rowNum--;
// DataTypeComponent dtc = model.getComponent(rowNum);
// if (dtc.getOffset() < ((StackEditorModel) model).getParameterOffset()) {
// assertNotEditingField();
// break;
// }
assertIsEditingField(rowNum, colNum);
}
while (rowNum > 0) {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -47,7 +47,9 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
DataType dataTypeCopy = composite.copy(null);
Composite compositeCopy = (Composite) dataTypeCopy;
int fieldIndex = 0;
setName(compositeCopy, fieldIndex);
DataTypeComponent component = compositeCopy.getComponent(fieldIndex);
component.setFieldName("newName");
HTMLDataTypeRepresentation representation = ToolTipUtils.getHTMLRepresentation(composite);
HTMLDataTypeRepresentation otherRepresentation =
@@ -789,8 +791,8 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
assertEquals("ccc ddd", l3a.get(1));
//[a bbbbbbbb, bbbbbbbbbb, bbbbbbbbbb, bbbbbb c]
List<String> l4 = HTMLDataTypeRepresentation.breakLongLineAtWordBoundaries(
"a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb c", 10);
List<String> l4 = HTMLDataTypeRepresentation
.breakLongLineAtWordBoundaries("a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb c", 10);
assertEquals(4, l4.size());
assertEquals("a bbbbbbbb", l4.get(0));
assertEquals("bbbbbbbbbb", l4.get(1));
@@ -1219,18 +1221,6 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
}
}
private void setName(Composite c, int fieldIndex) {
DataTypeComponent component = c.getComponent(fieldIndex);
try {
component.setFieldName("newName");
}
catch (DuplicateNameException e) {
// shouldn't happen
e.printStackTrace();
Assert.fail("Unexpected duplicate name");
}
}
private void assertCompositeHeaderEquals(HTMLDataTypeRepresentation[] diffedRepresentations) {
List<ValidatableLine> headerLines =
((CompositeDataTypeHTMLRepresentation) diffedRepresentations[0]).headerContent;
@@ -116,6 +116,22 @@ public class StructureDataTypeTest extends AbstractGenericTest {
assertEquals("Comment4", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
dtc = struct.add(ByteDataType.dataType, "field3", "new comment");
assertEquals(8, dtc.getOffset());
assertEquals(4, dtc.getOrdinal());
assertEquals("field3", dtc.getFieldName()); // non-duplicate name was imposed
assertEquals("new comment", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertNotNull(struct.findComponent("field3")); // which one is returned is arbitrary
dtc = struct.add(ByteDataType.dataType, "field3 1", "new comment");
assertEquals(9, dtc.getOffset());
assertEquals(5, dtc.getOrdinal());
assertEquals("field3_1", dtc.getFieldName()); // non-duplicate name was imposed
assertEquals("new comment", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertEquals(dtc, struct.findComponent("field3_1"));
}
@Test
@@ -1306,6 +1322,22 @@ public class StructureDataTypeTest extends AbstractGenericTest {
assertEquals(8, comps[3].getOffset());
}
@Test
public void testDelete() {
assertNotNull(struct.findComponent("field1"));
struct.delete(0);
assertNull(struct.findComponent("field1"));
assertEquals(7, struct.getLength());
assertEquals(3, struct.getNumComponents());
DataTypeComponent[] comps = struct.getDefinedComponents();
assertEquals(DWordDataType.class, comps[1].getDataType().getClass());
assertEquals(2, comps[1].getOffset());
}
@Test
public void testDeleteManyBF() throws InvalidDataTypeException {
@@ -1356,16 +1388,6 @@ public class StructureDataTypeTest extends AbstractGenericTest {
assertEquals(5, comps[1].getOffset());
}
@Test
public void testDelete() {
struct.delete(1);
assertEquals(6, struct.getLength());
assertEquals(3, struct.getNumComponents());
DataTypeComponent[] comps = struct.getDefinedComponents();
assertEquals(DWordDataType.class, comps[1].getDataType().getClass());
assertEquals(1, comps[1].getOffset());
}
@Test
public void testDeleteBF() throws InvalidDataTypeException {
@@ -1409,8 +1431,12 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf2"));
struct.delete(3);
assertNull(struct.findComponent("bf2"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
@@ -1426,8 +1452,12 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf3"));
struct.delete(3);
assertNull(struct.findComponent("bf3"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
@@ -1443,8 +1473,12 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf4"));
struct.delete(4);
assertNull(struct.findComponent("bf4"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
@@ -1461,8 +1495,12 @@ public class StructureDataTypeTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf1"));
struct.delete(2);
assertNull(struct.findComponent("bf1"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/TestStruct\n" +
"pack(disabled)\n" +
@@ -1731,6 +1769,21 @@ public class StructureDataTypeTest extends AbstractGenericTest {
}
@Test
public void testGetComponentByName() {
DataTypeComponent dtc = struct.findComponent("field1");
assertNotNull(dtc);
assertEquals("field1", dtc.getFieldName());
dtc = struct.findComponent("field3");
assertNotNull(dtc);
assertEquals("field3", dtc.getFieldName());
dtc = struct.findComponent("field4");
assertNotNull(dtc);
assertEquals("field4", dtc.getFieldName());
}
@Test
public void testGetComponentAt() {
/**
@@ -2629,6 +2682,66 @@ public class StructureDataTypeTest extends AbstractGenericTest {
}
}
@Test
public void testFieldNameWhitespaceConvertedToUnderscores() {
StructureDataType newStruct = new StructureDataType("Test", 0);
DataTypeComponent component = newStruct.add(new ByteDataType(), " name with spaces", null);
assertEquals("name_with_spaces", component.getFieldName());
component = newStruct.getComponent(0);
component.setFieldName(" name in db with spaces ");
assertEquals("name_in_db_with_spaces", component.getFieldName());
component = newStruct.add(new ByteDataType(), " another test ", null);
assertEquals("another_test", component.getFieldName());
newStruct.insert(0, new ByteDataType(), 1, " insert test ", "");
component = newStruct.getComponent(0);
assertEquals("insert_test", component.getFieldName());
newStruct.replace(0, new ByteDataType(), 1, " insert test ", "");
component = newStruct.getComponent(0);
assertEquals("insert_test", component.getFieldName());
}
@Test
public void testDefaultFieldNames() {
StructureDataType newStruct = new StructureDataType("Test", 0);
DataTypeComponent component = newStruct.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
component = newStruct.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
component = newStruct.getComponent(0);
assertNull(component.getFieldName());
component.setFieldName(" ");
assertNull(component.getFieldName());
component = newStruct.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
component = newStruct.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
newStruct.insert(0, new ByteDataType(), 1, null, "");
component = newStruct.getComponent(0);
assertNull(component.getFieldName());
newStruct.insert(0, new ByteDataType(), 1, " ", "");
component = newStruct.getComponent(0);
assertNull(component.getFieldName());
newStruct.replace(0, new ByteDataType(), 1, null, "");
component = newStruct.getComponent(0);
assertNull(component.getFieldName());
newStruct.replace(0, new ByteDataType(), 1, " ", "");
component = newStruct.getComponent(0);
assertNull(component.getFieldName());
}
protected DataTypeManager createBigEndianDataTypeManager() {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
dataOrg.setBigEndian(true);
@@ -592,6 +592,61 @@ public class UnionDataTypeTest extends AbstractGenericTest {
}
}
@Test
public void testFieldNameWhitespaceConvertedToUnderscores() {
UnionDataType newUnion = new UnionDataType("Test");
DataTypeComponent component = newUnion.add(new ByteDataType(), " name with spaces", null);
assertEquals("name_with_spaces", component.getFieldName());
component = newUnion.getComponent(0);
component.setFieldName(" name in db with spaces ");
assertEquals("name_in_db_with_spaces", component.getFieldName());
component = newUnion.add(new ByteDataType(), " another test ", null);
assertEquals("another_test", component.getFieldName());
newUnion.insert(0, new ByteDataType(), 1, " insert test ", "");
component = newUnion.getComponent(0);
assertEquals("insert_test", component.getFieldName());
newUnion.insert(1, new ByteDataType(), 1, " insert test ", "");
component = newUnion.getComponent(1);
assertEquals("insert_test", component.getFieldName());
}
@Test
public void testDefaultFieldNames() {
UnionDataType newUnion = new UnionDataType("Test");
DataTypeComponent component = newUnion.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
component = newUnion.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
assertEquals("field1", component.getDefaultFieldName());
component = newUnion.getComponent(0);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
component.setFieldName(" ");
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
component = newUnion.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
assertEquals("field2", component.getDefaultFieldName());
component = newUnion.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
assertEquals("field3", component.getDefaultFieldName());
newUnion.insert(0, new ByteDataType(), 1, null, "");
component = newUnion.getComponent(0);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
}
protected DataTypeManager createBigEndianDataTypeManager() {
DataOrganizationImpl dataOrg = DataOrganizationImpl.getDefaultOrganization(null);
dataOrg.setBigEndian(true);
@@ -1225,13 +1225,7 @@ public class DefaultCompositeMember extends CompositeMember {
for (int i = 0; i < count; i++) {
DataTypeComponent component = composite.getComponent(i);
if (oldFieldName.equals(component.getFieldName())) {
try {
component.setFieldName(newFieldName);
}
catch (DuplicateNameException e) {
Msg.error(this, "Failed to rename temporary component name: " +
getDataTypeName() + "." + oldFieldName + " -> " + newFieldName);
}
component.setFieldName(newFieldName);
break;
}
}
@@ -1356,9 +1350,8 @@ public class DefaultCompositeMember extends CompositeMember {
* @throws CancelledException if monitor is cancelled
*/
public static boolean applyDataTypeMembers(Composite composite, boolean packingDisabled,
boolean isClass, int preferredCompositeSize,
List<? extends PdbMember> members, Consumer<String> errorConsumer, TaskMonitor monitor)
throws CancelledException {
boolean isClass, int preferredCompositeSize, List<? extends PdbMember> members,
Consumer<String> errorConsumer, TaskMonitor monitor) throws CancelledException {
Composite editComposite = composite;
@@ -19,7 +19,7 @@ import java.io.IOException;
import db.*;
import ghidra.framework.data.OpenMode;
import ghidra.util.StringUtilities;
import ghidra.program.model.data.InternalDataTypeComponent;
import ghidra.util.exception.VersionException;
/**
@@ -61,19 +61,20 @@ abstract class ComponentDBAdapter {
* @param length the total length of this component.
* @param ordinal the component's ordinal.
* @param offset the component's offset.
* @param name the component's name. This name will be subject to revision based upon
* {@link #cleanUpFieldName(String)} method use.
* @param fieldName the component's name (may be null). This method may sanitize the name
* (see {@link InternalDataTypeComponent#cleanupFieldName(String)}) before storing.
* {@link InternalDataTypeComponent#cleanupFieldName(String)} method use.
* @param comment a comment about this component
* @return the component data type record.
* @throws IOException if there is a problem accessing the database.
*/
abstract DBRecord createRecord(long dataTypeID, long parentID, int length, int ordinal,
int offset, String name, String comment) throws IOException;
int offset, String fieldName, String comment) throws IOException;
/**
* Gets the record for the indicated component data type.
* @param componentID the ID of the component data type to retrieve.
* @return the component record
* @return the component record or null if not found
* @throws IOException if there is a problem accessing the database.
*/
abstract DBRecord getRecord(long componentID) throws IOException;
@@ -89,8 +90,8 @@ abstract class ComponentDBAdapter {
/**
* Updates the component data type table with the provided record.
* <p>
* IMPORTANT: Any modification of field name should be subject to {@link #cleanUpFieldName(String)}
* use first.
* IMPORTANT: Any modification of field name should be subject to
* {@link InternalDataTypeComponent#cleanupFieldName(String)} use first.
*
* @param record the new record
* @throws IOException if there is a problem accessing the database.
@@ -17,6 +17,8 @@ package ghidra.program.database.data;
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import db.*;
import ghidra.program.model.data.InternalDataTypeComponent;
import ghidra.util.exception.VersionException;
@@ -73,15 +75,19 @@ class ComponentDBAdapterV0 extends ComponentDBAdapter {
@Override
DBRecord createRecord(long dataTypeID, long parentID, int length, int ordinal, int offset,
String name, String comment) throws IOException {
String fieldName, String comment) throws IOException {
long key =
DataTypeManagerDB.createKey(DataTypeManagerDB.COMPONENT, componentTable.getKey());
if (StringUtils.isBlank(comment)) {
comment = null;
}
fieldName = InternalDataTypeComponent.cleanupFieldName(fieldName);
DBRecord record = ComponentDBAdapter.COMPONENT_SCHEMA.createRecord(key);
record.setLongValue(ComponentDBAdapter.COMPONENT_PARENT_ID_COL, parentID);
record.setLongValue(ComponentDBAdapter.COMPONENT_OFFSET_COL, offset);
record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL, dataTypeID);
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL,
InternalDataTypeComponent.cleanupFieldName(name));
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, fieldName);
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
record.setIntValue(ComponentDBAdapter.COMPONENT_SIZE_COL, length);
record.setIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL, ordinal);
@@ -18,6 +18,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.function.Consumer;
import db.DBRecord;
import ghidra.docking.settings.Settings;
@@ -62,6 +63,111 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
*/
protected abstract void initialize();
protected DataTypeComponentDB createComponent(long dtID, int length, int ordinal, int offset,
String componentName, String comment) {
DBRecord rec;
try {
rec = componentAdapter.createRecord(dtID, key, length, ordinal, offset, componentName,
comment);
return new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
}
catch (IOException e) {
dataMgr.dbError(e); // throws RuntimeException
}
// Will never reach here
throw new AssertionError();
}
@Override
public abstract DataTypeComponentDB getComponent(int ordinal) throws IndexOutOfBoundsException;
private DataTypeComponentDB getValidatedComponent(DataTypeComponentDB component)
throws IOException {
// Verify that component is still valid for this composite
DBRecord rec = componentAdapter.getRecord(component.getKey());
if (rec == null || rec.getLongValue(ComponentDBAdapter.COMPONENT_PARENT_ID_COL) != key) {
throw new ConcurrentModificationException("Component has been deleted.");
}
// Verify specified component instance is
DataTypeComponentDB myDtc = getComponent(component.getOrdinal());
if (myDtc != component) {
// supplied instance is stale - it should exist in our defined component list
myDtc = getComponent(rec.getIntValue(ComponentDBAdapter.COMPONENT_ORDINAL_COL));
}
return myDtc;
}
/**
* Set the name on a possibly stale component in support of the
* {@link DataTypeComponent#setFieldName(String)} method.
* <p>
* If the field name is empty it will be set to null,
* which is the default field name. The field name may be sanitized to convert all whitespace
* characters to an underscore. If a name conflict occurs with another component, a one-up
* number suffix will be added to avoid duplication.
*
* @param component data type component which has this composite as its parent.
* @param name new field name or null
* @return updated component instance
*/
protected DataTypeComponentDB setFieldName(DataTypeComponentDB component, String name) {
lock.acquire();
try {
checkDeleted();
if (component.getRecord() == null) {
return component; // unable to change undefined component
}
// Verify specified component instance is
DataTypeComponentDB myDtc = getValidatedComponent(component);
if (myDtc.doSetFieldName(name)) {
dataMgr.dataTypeChanged(this, false);
}
return myDtc; // return modified component instance
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
return component; // unchanged
}
/**
* Set the comment on a possibly stale component in support of the
* {@link DataTypeComponent#setComment(String)} method.
*
* @param component data type component which has this composite as its parent.
* @param comment comment string
* @return updated component instance
*/
protected DataTypeComponentDB setComment(DataTypeComponentDB component, String comment) {
lock.acquire();
try {
checkDeleted();
if (component.getRecord() == null) {
return component; // unable to change undefined component
}
// Verify specified component instance is
DataTypeComponentDB myDtc = getValidatedComponent(component);
if (myDtc.doSetComment(comment)) {
dataMgr.dataTypeChanged(this, false);
}
return myDtc; // return modified component instance
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
return component; // unchanged
}
@Override
public final int getAlignedLength() {
return getLength();
@@ -329,9 +435,20 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
compositeAdapter.updateRecord(record, true);
}
protected void removeComponentRecord(long compKey) throws IOException {
componentAdapter.removeRecord(compKey);
dataMgr.getSettingsAdapter().removeAllSettingsRecords(compKey);
/**
* Removes a defined component without any alteration to other components or the components
* list, removes parent association for component datatype and remove name-map entry.
* @param dtc datatype component
* @throws IOException if an IO error occurs
*/
protected void doDelete(DataTypeComponentDB dtc) throws IOException {
dtc.getDataType().removeParent(this);
// Remove component record
long dtcKey = dtc.getKey();
componentAdapter.removeRecord(dtcKey);
dataMgr.getSettingsAdapter().removeAllSettingsRecords(dtcKey);
}
/**
@@ -673,6 +790,8 @@ abstract class CompositeDB extends DataTypeDB implements CompositeInternal {
@Override
public abstract DataTypeComponentDB[] getDefinedComponents();
abstract void forEachDefinedComponent(Consumer<DataTypeComponentDB> dtcConsumer);
@Override
protected void postPointerResolve(DataType definitionDt, DataTypeConflictHandler handler) {
Composite composite = (Composite) definitionDt;
@@ -16,14 +16,13 @@
package ghidra.program.database.data;
import java.io.IOException;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import db.DBRecord;
import ghidra.docking.settings.*;
import ghidra.program.model.data.*;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
* Database implementation for a DataTypeComponent. If this
@@ -215,14 +214,26 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
@Override
public void setComment(String comment) {
public DataTypeComponentDB setComment(String comment) {
if (record != null) {
return parent.setComment(this, comment);
}
return this;
}
boolean doSetComment(String comment) {
if (record != null) {
if (StringUtils.isBlank(comment)) {
comment = null;
}
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
updateRecord(true);
if (!Objects.equals(comment,
record.getString(ComponentDBAdapter.COMPONENT_COMMENT_COL))) {
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
updateRecord(true); // updates DB and timestamp without notification
return true;
}
}
return false;
}
@Override
@@ -236,12 +247,27 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
@Override
public void setFieldName(String name) throws DuplicateNameException {
public DataTypeComponentDB setFieldName(String name) {
if (record != null) {
String fieldName = InternalDataTypeComponent.cleanupFieldName(name);
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, fieldName);
updateRecord(true);
// Parent must first validate instance before setting field name
return parent.setFieldName(this, name);
}
return this;
}
boolean doSetFieldName(String name) {
if (record != null) {
String oldName = record.getString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL);
// Cleanup invalid names and make unique within this composite
String fieldName = InternalDataTypeComponent.cleanupFieldName(name);
if (!Objects.equals(oldName, fieldName)) {
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, fieldName);
updateRecord(true); // updates DB and timestamp without notification
return true;
}
}
return false;
}
@Override
@@ -261,8 +287,8 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
if (offset != dtc.getOffset() || getLength() != dtc.getLength() ||
ordinal != dtc.getOrdinal() ||
!SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) ||
!SystemUtilities.isEqual(getComment(), dtc.getComment())) {
!Objects.equals(getFieldName(), dtc.getFieldName()) ||
!Objects.equals(getComment(), dtc.getComment())) {
return false;
}
if (!(myDt instanceof Pointer) && !myDt.getPathName().equals(otherDt.getPathName())) {
@@ -307,8 +333,8 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
(myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false;
// Components don't need to have matching offset when structure has packing enabled
if ((!isPacked && (offset != dtc.getOffset())) ||
!SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) ||
!SystemUtilities.isEqual(getComment(), dtc.getComment())) {
!Objects.equals(getFieldName(), dtc.getFieldName()) ||
!Objects.equals(getComment(), dtc.getComment())) {
return false;
}
@@ -407,27 +433,36 @@ class DataTypeComponentDB implements InternalDataTypeComponent {
}
/**
* Perform special-case component update that does not result in size or alignment changes.
* @param name new component name
* Perform special-case component update that does not result in size or alignment changes
* and does not modify last change time.
* @param fieldName new component name or null. If not null a unique component name will be forced.
* @param dt new resolved datatype
* @param comment new comment
*/
void update(String name, DataType dt, String comment) {
void update(String fieldName, DataType dt, String comment) {
if (record != null) {
String oldName = record.getString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL);
fieldName = InternalDataTypeComponent.cleanupFieldName(fieldName);
if (!Objects.equals(oldName, fieldName)) {
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, fieldName);
}
if (StringUtils.isBlank(comment)) {
comment = null;
}
String fieldName = InternalDataTypeComponent.cleanupFieldName(name);
record.setString(ComponentDBAdapter.COMPONENT_FIELD_NAME_COL, fieldName);
record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL, dataMgr.getResolvedID(dt));
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL, comment);
record.setString(ComponentDBAdapter.COMPONENT_COMMENT_COL,
StringUtils.isBlank(comment) ? null : comment);
updateRecord(false);
}
}
@Override
public void setDataType(DataType newDt) {
// intended for internal use only - note exsiting settings should be preserved
// Internal Interface use only - existing settings should be preserved
if (record != null) {
record.setLongValue(ComponentDBAdapter.COMPONENT_DT_ID_COL,
dataMgr.getResolvedID(newDt));
@@ -2646,6 +2646,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
status = compositeAdapter.removeRecord(dataTypeID);
break;
case COMPONENT:
// NOTE: This is only used when completely removing a composite datatype
status = componentAdapter.removeRecord(dataTypeID);
break;
case TYPEDEF:
@@ -15,7 +15,7 @@
*/
package ghidra.program.database.data;
import com.google.common.base.Predicate;
import java.util.function.Predicate;
import ghidra.docking.settings.*;
import ghidra.program.model.data.*;
@@ -88,7 +88,7 @@ class DataTypeSettingsDB implements Settings {
return false;
}
if (allowedSettingPredicate != null &&
!allowedSettingPredicate.apply(settingsDefinition.getStorageKey())) {
!allowedSettingPredicate.test(settingsDefinition.getStorageKey())) {
return false;
}
return true;
@@ -118,7 +118,7 @@ class DataTypeSettingsDB implements Settings {
return false;
}
if (name != null && allowedSettingPredicate != null &&
!allowedSettingPredicate.apply(name)) {
!allowedSettingPredicate.test(name)) {
Msg.warn(this, "Ignored disallowed setting '" + name + "'");
return false;
}
@@ -17,6 +17,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import db.DBRecord;
import db.Field;
@@ -182,8 +183,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
}
private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment,
boolean validatePackAndNotify)
private DataTypeComponent doAdd(DataType dataType, int length, String name,
String comment, boolean validatePackAndNotify)
throws DataTypeDependencyException, IllegalArgumentException {
lock.acquire();
@@ -204,9 +205,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
else {
int componentLength = getPreferredComponentLength(dataType, length);
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType),
key, componentLength, numComponents, structLength, name, comment);
dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
dtc = createComponent(dataMgr.getResolvedID(dataType), componentLength,
numComponents, structLength, name, comment);
dataType.addParent(this);
components.add(dtc);
}
@@ -261,6 +263,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
else {
index = backupToFirstComponentContainingOffset(index, len);
index = afterNonZeroComponentsAtOffset(index, len);
}
int definedComponentCount = components.size();
if (index >= 0 && index < definedComponentCount) {
@@ -370,9 +373,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
length = getPreferredComponentLength(dataType, length);
int offset = getComponent(ordinal).getOffset();
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key,
length, ordinal, offset, name, comment);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(dataType), length,
ordinal, offset, name, comment);
dataType.addParent(this);
shiftOffsets(idx, 1, dtc.getLength());
components.add(idx, dtc);
@@ -383,13 +387,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
catch (DataTypeDependencyException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
return null;
}
@Override
@@ -544,9 +544,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
BitFieldDataType bitfieldDt =
new BitFieldDBDataType(baseDataType, bitSize, storageBitOffset);
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(bitfieldDt), key,
DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(bitfieldDt),
bitfieldDt.getStorageSize(), ordinal, revisedOffset, componentName, comment);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
bitfieldDt.addParent(this); // has no affect
components.add(startIndex, dtc);
@@ -554,13 +554,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
notifySizeChanged(false);
return dtc;
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
return null;
}
@Override
@@ -597,7 +593,8 @@ class StructureDB extends CompositeDB implements StructureInternal {
/**
* Removes a defined component at the specified index from the components list without any
* alteration to other components and removes parent association for component datatype.
* alteration to other components, removes parent association for component datatype and
* remove name-map entry.
* @param index defined component index
* @return the defined component which was removed.
* @throws IOException if an IO error occurs
@@ -608,17 +605,6 @@ class StructureDB extends CompositeDB implements StructureInternal {
return dtc;
}
/**
* Removes a defined component without any alteration to other components or the components
* list and removes parent association for component datatype.
* @param dtc datatype component
* @throws IOException if an IO error occurs
*/
private void doDelete(DataTypeComponentDB dtc) throws IOException {
dtc.getDataType().removeParent(this);
removeComponentRecord(dtc.getKey());
}
/**
* Removes a defined component at the specified index.
* <p>
@@ -1301,6 +1287,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
}
@Override
void forEachDefinedComponent(Consumer<DataTypeComponentDB> dtcConsumer) {
components.forEach(dtcConsumer);
}
@Override
public final DataTypeComponent insertAtOffset(int offset, DataType dataType, int length)
throws IllegalArgumentException {
@@ -1384,10 +1375,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
return new DataTypeComponentDB(dataMgr, this, ordinal, offset);
}
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dataType), key,
length, ordinal, offset, name, comment);
length = getPreferredComponentLength(dataType, length);
DataTypeComponentDB dtc = createComponent(dataMgr.getResolvedID(dataType), length,
ordinal, offset, name, comment);
dataType.addParent(this);
DataTypeComponentDB dtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
shiftOffsets(index, 1 + additionalShift, length + additionalShift);
components.add(index, dtc);
repack(false, false);
@@ -1397,13 +1389,9 @@ class StructureDB extends CompositeDB implements StructureInternal {
catch (DataTypeDependencyException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
return null;
}
/**
@@ -1442,6 +1430,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
return oldComponent;
}
// Note: a unique componentName will be imposed if not null
DataTypeComponent replaceComponent =
replaceComponents(replacedComponents, dataType, offset, length, componentName, comment);
@@ -1729,13 +1718,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
for (int i = 0; i < otherComponents.length; i++) {
resolvedDts[i] = doCheckedResolve(otherComponents[i].getDataType());
}
for (DataTypeComponentDB dtc : components) {
dtc.getDataType().removeParent(this);
long compKey = dtc.getKey();
componentAdapter.removeRecord(compKey);
dataMgr.getSettingsAdapter().removeAllSettingsRecords(compKey);
}
// Remove all existing components
for (DataTypeComponentDB dtc : components) {
doDelete(dtc);
}
components.clear();
numComponents = 0;
structLength = 0;
@@ -1799,8 +1786,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
}
}
private void doReplaceWithNonPacked(Structure struct, DataType[] resolvedDts)
throws IOException {
private void doReplaceWithNonPacked(Structure struct, DataType[] resolvedDts) {
// caller responsible for record updates
@@ -1836,17 +1822,16 @@ class StructureDB extends CompositeDB implements StructureInternal {
length = getPreferredComponentLength(dt, -1, maxLength);
}
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(dt), key, length,
DataTypeComponentDB newDtc = createComponent(dataMgr.getResolvedID(dt), length,
dtc.getOrdinal(), dtc.getOffset(), dtc.getFieldName(), dtc.getComment());
dt.addParent(this);
DataTypeComponentDB newDtc =
new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
components.add(newDtc);
}
}
private void doCopy(Structure struct, DataTypeComponent[] definedComponents,
DataType[] resolvedDts) throws IOException {
DataType[] resolvedDts) {
// simple replication of struct - caller must perform record updates
structLength = struct.isZeroLength() ? 0 : struct.getLength();
@@ -1857,12 +1842,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i];
DataType dt = resolvedDts[i]; // ancestry check already performed by caller
DBRecord rec =
componentAdapter.createRecord(dataMgr.getResolvedID(dt), key, dtc.getLength(),
dtc.getOrdinal(), dtc.getOffset(), dtc.getFieldName(), dtc.getComment());
DataTypeComponentDB newDtc = createComponent(dataMgr.getResolvedID(dt), dtc.getLength(),
dtc.getOrdinal(), dtc.getOffset(), dtc.getFieldName(), dtc.getComment());
dt.addParent(this);
DataTypeComponentDB newDtc =
new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
components.add(newDtc);
}
}
@@ -2345,10 +2329,10 @@ class StructureDB extends CompositeDB implements StructureInternal {
DataTypeComponentDB newDtc = null;
if (!clearOnly) {
// insert new component
DBRecord rec = componentAdapter.createRecord(dataMgr.getResolvedID(resolvedDataType),
key, length, newOrdinal, newOffset, name, comment);
newDtc = createComponent(dataMgr.getResolvedID(resolvedDataType), length, newOrdinal,
newOffset, name, comment);
resolvedDataType.addParent(this);
newDtc = new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
components.add(index, newDtc);
}
@@ -2477,15 +2461,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
try {
checkDeleted();
for (DataTypeComponentDB dtc : components) {
dtc.getDataType().removeParent(this);
try {
long compKey = dtc.getKey();
componentAdapter.removeRecord(compKey);
dataMgr.getSettingsAdapter().removeAllSettingsRecords(compKey);
}
catch (IOException e) {
dataMgr.dbError(e);
}
doDelete(dtc);
}
components.clear();
structLength = 0;
@@ -17,6 +17,7 @@ package ghidra.program.database.data;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import db.DBRecord;
import db.Field;
@@ -61,7 +62,9 @@ class UnionDB extends CompositeDB implements UnionInternal {
Field[] ids = componentAdapter.getComponentIdsInComposite(key);
for (Field id : ids) {
DBRecord rec = componentAdapter.getRecord(id.getLongValue());
components.add(new DataTypeComponentDB(dataMgr, componentAdapter, this, rec));
DataTypeComponentDB component =
new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
components.add(component);
}
}
catch (IOException e) {
@@ -122,8 +125,8 @@ class UnionDB extends CompositeDB implements UnionInternal {
return length;
}
private DataTypeComponent doAdd(DataType dataType, int length, String name, String comment,
boolean validateAlignAndNotify) throws DataTypeDependencyException {
private DataTypeComponent doAdd(DataType dataType, int length, String name,
String comment, boolean validateAlignAndNotify) throws DataTypeDependencyException {
dataType = validateDataType(dataType);
@@ -145,19 +148,6 @@ class UnionDB extends CompositeDB implements UnionInternal {
return dtc;
}
private DataTypeComponentDB createComponent(long dtID, int length, int ordinal, int offset,
String name, String comment) {
DBRecord rec;
try {
rec = componentAdapter.createRecord(dtID, key, length, ordinal, offset, name, comment);
return new DataTypeComponentDB(dataMgr, componentAdapter, this, rec);
}
catch (IOException e) {
dataMgr.dbError(e);
}
return null;
}
@Override
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
String comment) throws IllegalArgumentException {
@@ -222,8 +212,8 @@ class UnionDB extends CompositeDB implements UnionInternal {
getComputedAlignment(true); // ensure previous alignment has been stored
DataTypeComponentDB dtc = components.remove(ordinal);
dtc.getDataType().removeParent(this);
removeComponentRecord(dtc.getKey());
doDelete(dtc);
shiftOrdinals(ordinal, -1);
if (!repack(false, true)) {
@@ -265,8 +255,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
int ordinal = dtc.getOrdinal();
if (ordinals.contains(ordinal)) {
// component removed - delete record
dtc.getDataType().removeParent(this);
removeComponentRecord(dtc.getKey());
doDelete(dtc);
--ordinalAdjustment;
}
else {
@@ -347,9 +336,9 @@ class UnionDB extends CompositeDB implements UnionInternal {
}
for (DataTypeComponentDB dtc : components) {
dtc.getDataType().removeParent(this);
removeComponentRecord(dtc.getKey());
doDelete(dtc);
}
components.clear();
unionAlignment = -1;
computedAlignment = -1;
@@ -358,7 +347,8 @@ class UnionDB extends CompositeDB implements UnionInternal {
for (int i = 0; i < otherComponents.length; i++) {
DataTypeComponent dtc = otherComponents[i];
doAdd(resolvedDts[i], dtc.getLength(), dtc.getFieldName(), dtc.getComment(), false);
doAdd(resolvedDts[i], dtc.getLength(), dtc.getFieldName(), dtc.getComment(),
false);
}
repack(false, false);
@@ -427,7 +417,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
}
@Override
public DataTypeComponent getComponent(int ordinal) {
public DataTypeComponentDB getComponent(int ordinal) {
lock.acquire();
try {
checkIsValid();
@@ -458,6 +448,11 @@ class UnionDB extends CompositeDB implements UnionInternal {
return getComponents();
}
@Override
void forEachDefinedComponent(Consumer<DataTypeComponentDB> dtcConsumer) {
components.forEach(dtcConsumer);
}
@Override
public DataType copy(DataTypeManager dtm) {
UnionDataType union = new UnionDataType(getCategoryPath(), getName(), dtm);
@@ -18,7 +18,6 @@ package ghidra.program.database.data.merge;
import java.util.List;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* DataType merger for structures.
@@ -215,12 +214,7 @@ public class StructureMerger extends DataTypeMerger<Structure> {
otherName));
}
if (workingName == null && otherName != null) {
try {
workingComp.setFieldName(otherName);
}
catch (DuplicateNameException e) {
// This exception is going away soon, so ignore it for now
}
workingComp.setFieldName(otherName);
}
}
@@ -16,7 +16,6 @@
package ghidra.program.database.data.merge;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
/**
* Datatype merger for Unions
@@ -77,13 +76,8 @@ public class UnionMerger extends DataTypeMerger<Union> {
}
private void appySameTypeComponent(DataTypeComponent resultComp, DataTypeComponent component) {
try {
resultComp.setFieldName(component.getFieldName());
resultComp.setComment(join(resultComp.getComment(), component.getComment()));
}
catch (DuplicateNameException e) {
// can't happen, we already looked for a component with the same name
}
resultComp.setFieldName(component.getFieldName());
resultComp.setComment(join(resultComp.getComment(), component.getComment()));
}
private void applySameNamedComponent(DataTypeComponent comp1, DataTypeComponent comp2)
@@ -117,7 +117,7 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
}
@Override
public void setComment(String comment) {
public DataTypeComponent setComment(String comment) {
throw new UnsupportedOperationException();
}
@@ -127,7 +127,7 @@ public class AlignedStructureInspector extends AlignedStructurePacker {
}
@Override
public void setFieldName(String fieldName) throws DuplicateNameException {
public DataTypeComponent setFieldName(String fieldName) {
throw new UnsupportedOperationException();
}
@@ -15,7 +15,7 @@
*/
package ghidra.program.model.data;
import java.util.Set;
import java.util.*;
/**
* Interface for common methods in Structure and Union
@@ -55,6 +55,61 @@ public interface Composite extends DataType {
*/
public abstract DataTypeComponent getComponent(int ordinal) throws IndexOutOfBoundsException;
/**
* Find the first component which has the specified case-sensitive field name.
* Note that multiple components may be specified with the same name, if this is a possibility
* the {@link #findComponents(String)} method should be used. Only components with an explicit
* non-default field name will be considered. The name specified may be sanitized to be
* consistent with those permitted by {@link Composite} data types.
*
* @param fieldName field name
* @return first data type component whose field name matches or null if not found
*/
public default DataTypeComponent findComponent(String fieldName) {
if (getNumDefinedComponents() == 0) {
return null;
}
fieldName = InternalDataTypeComponent.cleanupFieldName(fieldName);
if (fieldName == null) {
return null;
}
for (DataTypeComponent dtc : getDefinedComponents()) {
if (fieldName.equals(dtc.getFieldName())) {
return dtc;
}
}
return null;
}
/**
* Find all components which have the specified case-sensitive field name.
* Note that multiple components may be specified with the same name, if this is a possibility
* the {@link #findComponents(String)} method should be used. Only components with an explicit
* non-default field name will be considered. The name specified may be sanitized to be
* consistent with those permitted by {@link Composite} data types.
*
* @param name field name
* @return all data type components whose field name matches or empty list if not found
*/
public default List<DataTypeComponent> findComponents(String name) {
if (getNumDefinedComponents() == 0) {
return List.of();
}
List<DataTypeComponent> list = new ArrayList<>();
for (DataTypeComponent dtc : getDefinedComponents()) {
if (name.equals(dtc.getFieldName())) {
list.add(dtc);
}
}
return list;
}
/**
* Returns an array of Data Type Components that make up this composite including
* undefined filler components which may be present within a Structure which has packing disabled.
@@ -113,15 +168,16 @@ public interface Composite extends DataType {
* automatically to provide the proper alignment.
*
* @param dataType the datatype to add.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component. (may be null)
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not
* allowed to be added to this composite data type.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* 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 componentName, String comment)
throws IllegalArgumentException;
/**
@@ -132,8 +188,9 @@ public interface Composite extends DataType {
*
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the bitfield size in bits
* @param componentName the field name to associate with this component.
* @param comment the comment to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component. (may be null)
* @return the componentDataType created whose associated data type will
* be BitFieldDataType.
* @throws InvalidDataTypeException if the specified data type is
@@ -151,16 +208,17 @@ public interface Composite extends DataType {
* @param dataType the datatype to add.
* @param length the length to associate with the datatype.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @param componentName the field name to associate with this component (may be null).
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component (may be null).
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not
* allowed to be added to this composite data type or an invalid length is specified.
* For example, suppose dt1 contains dt2. Therefore it is not valid
* to add dt1 to dt2 since this would cause a cyclic dependency.
*/
public DataTypeComponent add(DataType dataType, int length, String name, String comment)
throws IllegalArgumentException;
public DataTypeComponent add(DataType dataType, int length, String componentName,
String comment) throws IllegalArgumentException;
/**
* Inserts a new datatype at the specified ordinal position in this composite.
@@ -211,8 +269,9 @@ public interface Composite extends DataType {
* @param dataType the datatype to insert.
* @param length the length to associate with the datatype.
* For fixed length types a length &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param comment the comment to associate with this component.
* @param componentName the field name to associate with this component (may be null).
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component. (may be null)
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not
* allowed to be inserted into this composite data type or an invalid length
@@ -221,8 +280,9 @@ public interface Composite extends DataType {
* to insert dt1 to dt2 since this would cause a cyclic dependency.
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent insert(int ordinal, DataType dataType, int length, String name,
String comment) throws IndexOutOfBoundsException, IllegalArgumentException;
public DataTypeComponent insert(int ordinal, DataType dataType, int length,
String componentName, String comment)
throws IndexOutOfBoundsException, IllegalArgumentException;
/**
* Deletes the component at the given ordinal position.
@@ -15,6 +15,8 @@
*/
package ghidra.program.model.data;
import java.util.function.Consumer;
import ghidra.docking.settings.Settings;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.mem.MemBuffer;
@@ -62,6 +64,13 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
description = "";
}
protected DataTypeComponentImpl createComponent(DataType dataType, int length, int ordinal,
int offset, String fieldName, String comment) {
return new DataTypeComponentImpl(dataType, this, length, ordinal,
offset, fieldName, comment);
}
@Override
public final int getAlignedLength() {
return getLength();
@@ -441,6 +450,8 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
@Override
public abstract int getAlignment();
abstract void forEachDefinedComponent(Consumer<DataTypeComponentImpl> dtcConsumer);
@Override
public String toString() {
return CompositeInternal.toString(this);
@@ -260,5 +260,4 @@ public interface CompositeInternal extends Composite {
buf.append(")");
return buf.toString();
}
}
@@ -16,7 +16,6 @@
package ghidra.program.model.data;
import ghidra.docking.settings.Settings;
import ghidra.util.exception.DuplicateNameException;
/**
* DataTypeComponents are holders for the dataTypes that make up composite (Structures
@@ -98,9 +97,14 @@ public interface DataTypeComponent {
/**
* Sets the comment for the component.
* <P>
* NOTE: Since a datatype component instance is intended to be immutable a new component
* instance is returned which will reflect the modified component.
*
* @param comment this components comment or null to clear comment.
* @return a new component instance which will reflect the change.
*/
public void setComment(String comment);
public DataTypeComponent setComment(String comment);
/**
* Get this component's field name within its parent.
@@ -112,17 +116,17 @@ public interface DataTypeComponent {
/**
* Sets the field name. If the field name is empty it will be set to null,
* which is the default field name. An exception is thrown if one of the
* parent's other components already has the specified field name.
* which is the default field name. The field name may be sanitized to convert all whitespace
* characters to an underscore. If a name conflict occurs with another component, a one-up
* number suffix will be added to avoid duplication.
* <P>
* NOTE: Since a datatype component instance is intended to be immutable a new component
* instance is returned which will reflect the modified component.
*
* @param fieldName the new field name for this component.
*
* @throws DuplicateNameException This is actually never thrown anymore. All the other ways
* of naming fields did not perform this check and it would cause quite a bit of churn to
* add that exception to all the other methods that affect field names. So to be consistent,
* we no longer do the check in this method.
* @return a new component instance which will reflect the change.
*/
public void setFieldName(String fieldName) throws DuplicateNameException;
public DataTypeComponent setFieldName(String fieldName);
/**
* Returns a default field name for this component. Used only if a field name is not set.
@@ -16,13 +16,13 @@
package ghidra.program.model.data;
import java.io.Serializable;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsImpl;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateNameException;
/**
@@ -32,7 +32,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
private final static long serialVersionUID = 1;
private DataType dataType;
private CompositeDataTypeImpl parent; // parent prototype containing us
private CompositeDataTypeImpl parent; // composite containing us (may be null in certain use cases)
private int offset; // offset in parent
private int ordinal; // position in parent
private SettingsImpl defaultSettings;
@@ -48,12 +48,11 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
* @param length the length of the dataType in this component.
* @param ordinal the index within its parent.
* @param offset the byte offset within the parent
* @param fieldName the name associated with this component
* @param comment the comment associated with this component
* @param fieldName the name associated with this component or null
* @param comment the comment associated with this component or null
*/
public DataTypeComponentImpl(DataType dataType, CompositeDataTypeImpl parent, int length,
int ordinal, int offset, String fieldName, String comment) {
this.parent = parent;
this.ordinal = ordinal;
this.offset = offset;
@@ -67,7 +66,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
}
/**
* Create a new DataTypeComponent
* Create a new DataTypeComponent without a name
* @param dataType the dataType for this component
* @param parent the dataType that this component belongs to
* @param length the length of the dataType in this component.
@@ -119,8 +118,9 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
}
@Override
public void setComment(String comment) {
public DataTypeComponentImpl setComment(String comment) {
this.comment = StringUtils.isBlank(comment) ? null : comment;
return this;
}
@Override
@@ -132,8 +132,10 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
}
@Override
public void setFieldName(String name) throws DuplicateNameException {
this.fieldName = InternalDataTypeComponent.cleanupFieldName(name);
public DataTypeComponentImpl setFieldName(String name) {
// Cleanup invalid names and make unique within its parent
fieldName = InternalDataTypeComponent.cleanupFieldName(name);
return this;
}
public static void checkDefaultFieldName(String fieldName) throws DuplicateNameException {
@@ -174,9 +176,9 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
* @param newComment new comment
*/
void update(String name, DataType newDataType, String newComment) {
this.fieldName = InternalDataTypeComponent.cleanupFieldName(name);
this.dataType = newDataType;
this.comment = StringUtils.isBlank(newComment) ? null : newComment;
setFieldName(name);
setComment(newComment);
}
@Override
@@ -220,7 +222,7 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
@Override
public Settings getDefaultSettings() {
if (defaultSettings == null) {
if (defaultSettings == null && parent != null) {
DataTypeManager dataMgr = parent.getDataTypeManager();
boolean immutableSettings =
dataMgr == null || !dataMgr.allowsDefaultComponentSettings();
@@ -251,8 +253,8 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
if (offset != dtc.getOffset() || getLength() != dtc.getLength() ||
ordinal != dtc.getOrdinal() ||
!SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) ||
!SystemUtilities.isEqual(getComment(), dtc.getComment())) {
!Objects.equals(getFieldName(), dtc.getFieldName()) ||
!Objects.equals(getComment(), dtc.getComment())) {
return false;
}
if (!(myDt instanceof Pointer)) {
@@ -291,8 +293,8 @@ public class DataTypeComponentImpl implements InternalDataTypeComponent, Seriali
(myParent instanceof Composite) ? ((Composite) myParent).isPackingEnabled() : false;
// Components don't need to have matching offset when they are aligned
if ((!aligned && (offset != dtc.getOffset())) ||
!SystemUtilities.isEqual(getFieldName(), dtc.getFieldName()) ||
!SystemUtilities.isEqual(getComment(), dtc.getComment())) {
!Objects.equals(getFieldName(), dtc.getFieldName()) ||
!Objects.equals(getComment(), dtc.getComment())) {
return false;
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -126,9 +126,8 @@ public class DialogResourceDataType extends DynamicDataType {
private int addDlgTemplateStructure(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset, boolean ex) {
tempOffset =
addComp(ex ? dlgTemplateExStructure() : dlgTemplateStructure(), ex ? 26 : 18,
"Dialog Template Structure", memBuffer.getAddress(), comps, tempOffset);
tempOffset = addComp(ex ? dlgTemplateExStructure() : dlgTemplateStructure(), ex ? 26 : 18,
"Dialog Template Structure", memBuffer.getAddress(), comps, tempOffset);
return tempOffset;
}
@@ -139,15 +138,13 @@ public class DialogResourceDataType extends DynamicDataType {
short dialogMenuInfo = memBuffer.getShort(tempOffset);
if (dialogMenuInfo == 0x0000) { //if 0x0000 - no menu
tempOffset =
addComp(createArrayOfShorts(1), 2, "Dialog Menu",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(1), 2, "Dialog Menu",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//if 0xFFFF - one more item that is resource number of the menu
else if (dialogMenuInfo == 0xFFFF) {
tempOffset =
addComp(createArrayOfShorts(2), 4, "Dialog Menu",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(2), 4, "Dialog Menu",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//array is unicode name of menu in executable file
else {
@@ -164,15 +161,13 @@ public class DialogResourceDataType extends DynamicDataType {
//if 0x0000 - use predefined class
if (dialogClassInfo == 0x0000) {
tempOffset =
addComp(createArrayOfShorts(1), 2, "Dialog Class",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(1), 2, "Dialog Class",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//if 0xFFFF - one more item that is ordinal value of system window class
else if (dialogClassInfo == 0xFFFF) {
tempOffset =
addComp(createArrayOfShorts(2), 4, "Dialog Class",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(2), 4, "Dialog Class",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//array is unicode name of menu in executable file
else {
@@ -188,9 +183,8 @@ public class DialogResourceDataType extends DynamicDataType {
short dialogTitleInfo = memBuffer.getShort(tempOffset);
//if 0x0000 - Dialog has no title
if (dialogTitleInfo == 0x0000) {
tempOffset =
addComp(createArrayOfShorts(1), 2, "Dialog Title",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(1), 2, "Dialog Title",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//array is unicode name of menu in executable file
else {
@@ -206,22 +200,21 @@ public class DialogResourceDataType extends DynamicDataType {
private int addDialogFontComponents(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset, boolean ex) {
//add Dialog Font size
tempOffset =
addComp(new ShortDataType(), 2, "Dialog Font Size",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(new ShortDataType(), 2, "Dialog Font Size",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
if (ex) {
//add Dialog Font weight
tempOffset = addComp(new WordDataType(), 2, "Dialog Font Weight", memBuffer.getAddress().add(tempOffset),
comps, tempOffset);
tempOffset = addComp(new WordDataType(), 2, "Dialog Font Weight",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
//add Dialog Font Italic
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Italic", memBuffer.getAddress().add(tempOffset),
comps, tempOffset);
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Italic",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
//add Dialog Font Charset
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Charset", memBuffer.getAddress().add(tempOffset),
comps, tempOffset);
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Charset",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
//add Dialog Font Style array
@@ -235,9 +228,8 @@ public class DialogResourceDataType extends DynamicDataType {
int tempOffset, boolean ex) {
if ((memBuffer.getAddress().add(tempOffset).getOffset() % 4) != 0) {
tempOffset =
addComp(new AlignmentDataType(), 2, "Alignment",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(new AlignmentDataType(), 2, "Alignment",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
tempOffset =
addComp(ex ? dlgItemTemplateExStructure() : dlgItemTemplateStructure(), ex ? 24 : 18,
@@ -247,16 +239,15 @@ public class DialogResourceDataType extends DynamicDataType {
}
//adds Item class array - 1st after component after each DLGITEMTEMPLATE structure
private int addItemClassArray(MemBuffer memBuffer, List<DataTypeComponent> comps, int tempOffset)
throws MemoryAccessException {
private int addItemClassArray(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset) throws MemoryAccessException {
short itemClassInfo = memBuffer.getShort(tempOffset);
if ((itemClassInfo & 0xffff) == 0xffff) {
short classType = memBuffer.getShort(tempOffset + 2);
tempOffset =
addComp(createArrayOfShorts(2), 4, "Item Class Type or Name" + "(" +
getItemType(Integer.valueOf(classType)) + ")",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(2), 4,
"Item Class Type or Name" + "(" + getItemType(Integer.valueOf(classType)) + ")",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
else {
tempOffset = addUnicodeString(memBuffer, comps, tempOffset, "Item Class Type or Name");
@@ -265,14 +256,13 @@ public class DialogResourceDataType extends DynamicDataType {
}
//adds Item title array - 2nd after each DLGITEMTEMPLATE structure
private int addItemTitleArray(MemBuffer memBuffer, List<DataTypeComponent> comps, int tempOffset)
throws MemoryAccessException {
private int addItemTitleArray(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset) throws MemoryAccessException {
short itemTitleInfo = memBuffer.getShort(tempOffset);
if ((itemTitleInfo & 0xffff) == 0xffff) {
tempOffset =
addComp(createArrayOfShorts(2), 4, "Item Title or Resource ID",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(2), 4, "Item Title or Resource ID",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
else {
tempOffset =
@@ -287,15 +277,13 @@ public class DialogResourceDataType extends DynamicDataType {
short itemDataLength = memBuffer.getShort(tempOffset);
if (itemDataLength == 0x0000) {
tempOffset =
addComp(createArrayOfShorts(1), 2, "Item Data",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(createArrayOfShorts(1), 2, "Item Data",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
else {
tempOffset =
addComp(new ArrayDataType(ByteDataType.dataType, itemDataLength, 1),
itemDataLength, "Item Data", memBuffer.getAddress().add(tempOffset), comps,
tempOffset);
addComp(new ArrayDataType(ByteDataType.dataType, itemDataLength, 1), itemDataLength,
"Item Data", memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
return tempOffset;
}
@@ -303,154 +291,56 @@ public class DialogResourceDataType extends DynamicDataType {
//This is always the first structure in the dialog resource
private StructureDataType dlgTemplateExStructure() {
StructureDataType struct = new StructureDataType("DLGTEMPLATEEX", 0);
struct.add(WordDataType.dataType);
struct.add(WordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(WordDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
try {
struct.getComponent(0).setFieldName("dlgVer");
struct.getComponent(1).setFieldName("signature");
struct.getComponent(2).setFieldName("helpId");
struct.getComponent(3).setFieldName("exStyle");
struct.getComponent(4).setFieldName("style");
struct.getComponent(5).setFieldName("cDlgItems");
struct.getComponent(6).setFieldName("x");
struct.getComponent(7).setFieldName("y");
struct.getComponent(8).setFieldName("cx");
struct.getComponent(9).setFieldName("cy");
}
catch (DuplicateNameException e) {
Msg.debug(this, "Unexpected exception building DLGTEMPLATEEX", e);
}
struct.getComponent(0).setComment("version (must be 1)");
struct.getComponent(1).setComment("signature (must be 0xffff)");
struct.getComponent(2).setComment("help context identifier");
struct.getComponent(3).setComment("extended styles for a window");
struct.getComponent(4).setComment("style of dialog box");
struct.getComponent(5).setComment("number of items in dialog box");
struct.getComponent(6).setComment("x-coordinate of upper-left corner of dialog");
struct.getComponent(7).setComment("y-coordinate of upper-left corner of dialog");
struct.getComponent(8).setComment("width of dialog box");
struct.getComponent(9).setComment("height of dialog box");
struct.add(WordDataType.dataType, "dlgVer", "version (must be 1)");
struct.add(WordDataType.dataType, "signature", "signature (must be 0xffff)");
struct.add(DWordDataType.dataType, "helpId", "help context identifier");
struct.add(DWordDataType.dataType, "exStyle", "extended styles for a window");
struct.add(DWordDataType.dataType, "style", "style of dialog box");
struct.add(WordDataType.dataType, "cDlgItems", "number of items in dialog box");
struct.add(ShortDataType.dataType, "x", "x-coordinate of upper-left corner of dialog");
struct.add(ShortDataType.dataType, "y", "y-coordinate of upper-left corner of dialog");
struct.add(ShortDataType.dataType, "cx", "width of dialog box");
struct.add(ShortDataType.dataType, "cy", "height of dialog box");
return struct;
}
//This is always the first structure in the dialog resource
private StructureDataType dlgTemplateStructure() {
StructureDataType struct = new StructureDataType("DLGTEMPLATE", 0);
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(WordDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
try {
struct.getComponent(0).setFieldName("style");
struct.getComponent(1).setFieldName("dwExtendedStyle");
struct.getComponent(2).setFieldName("cdit");
struct.getComponent(3).setFieldName("x");
struct.getComponent(4).setFieldName("y");
struct.getComponent(5).setFieldName("cx");
struct.getComponent(6).setFieldName("cy");
}
catch (DuplicateNameException e) {
Msg.debug(this, "Unexpected exception building DLGTEMPLATE", e);
}
struct.getComponent(0).setComment("style of dialog box");
struct.getComponent(1).setComment("extended styles for a window");
struct.getComponent(2).setComment("number of items in dialog box");
struct.getComponent(3).setComment("x-coordinate of upper-left corner of dialog");
struct.getComponent(4).setComment("y-coordinate of upper-left corner of dialog");
struct.getComponent(5).setComment("width of dialog box");
struct.getComponent(6).setComment("height of dialog box");
struct.add(DWordDataType.dataType, "style", "style of dialog box");
struct.add(DWordDataType.dataType, "dwExtendedStyle", "extended styles for a window");
struct.add(WordDataType.dataType, "cdit", "number of items in dialog box");
struct.add(ShortDataType.dataType, "x", "x-coordinate of upper-left corner of dialog");
struct.add(ShortDataType.dataType, "y", "y-coordinate of upper-left corner of dialog");
struct.add(ShortDataType.dataType, "cx", "width of dialog box");
struct.add(ShortDataType.dataType, "cy", "height of dialog box");
return struct;
}
//Each control item has one of these structures
private StructureDataType dlgItemTemplateExStructure() {
StructureDataType struct = new StructureDataType("DLGITEMTEMPLATEEX", 0);
try {
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(DWordDataType.dataType);
struct.getComponent(0).setFieldName("helpID");
struct.getComponent(1).setFieldName("exStyle");
struct.getComponent(2).setFieldName("style");
struct.getComponent(3).setFieldName("x");
struct.getComponent(4).setFieldName("y");
struct.getComponent(5).setFieldName("cx");
struct.getComponent(6).setFieldName("cy");
struct.getComponent(7).setFieldName("id");
}
catch (DuplicateNameException e) {
Msg.debug(this, "Unexpected exception building DLGITEMTEMPLATEEX", e);
}
struct.getComponent(0).setComment("help context identifier");
struct.getComponent(1).setComment("extended styles for a window");
struct.getComponent(2).setComment("style of control");
struct.getComponent(3).setComment("x-coordinate of upper-left corner of control");
struct.getComponent(4).setComment("y-coordinate of upper-left corner of control");
struct.getComponent(5).setComment("width of control");
struct.getComponent(6).setComment("height of control");
struct.getComponent(7).setComment("control identifier");
struct.add(DWordDataType.dataType, "helpID", "help context identifier");
struct.add(DWordDataType.dataType, "exStyle", "extended styles for a window");
struct.add(DWordDataType.dataType, "style", "style of control");
struct.add(ShortDataType.dataType, "x", "x-coordinate of upper-left corner of control");
struct.add(ShortDataType.dataType, "y", "y-coordinate of upper-left corner of control");
struct.add(ShortDataType.dataType, "cx", "width of control");
struct.add(ShortDataType.dataType, "cy", "height of control");
struct.add(DWordDataType.dataType, "id", "control identifier");
return struct;
}
//Each control item has one of these structures
private StructureDataType dlgItemTemplateStructure() {
StructureDataType struct = new StructureDataType("DLGITEMTEMPLATE", 0);
try {
struct.add(DWordDataType.dataType);
struct.add(DWordDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(ShortDataType.dataType);
struct.add(WordDataType.dataType);
struct.getComponent(0).setFieldName("style");
struct.getComponent(1).setFieldName("dwExtendedStyle");
struct.getComponent(2).setFieldName("x");
struct.getComponent(3).setFieldName("y");
struct.getComponent(4).setFieldName("cx");
struct.getComponent(5).setFieldName("cy");
struct.getComponent(6).setFieldName("id");
}
catch (DuplicateNameException e) {
Msg.debug(this, "Unexpected exception building DLGITEMTEMPLATE", e);
}
struct.getComponent(0).setComment("style of control");
struct.getComponent(1).setComment("extended styles for a window");
struct.getComponent(2).setComment("x-coordinate of upper-left corner of control");
struct.getComponent(3).setComment("y-coordinate of upper-left corner of control");
struct.getComponent(4).setComment("width of control");
struct.getComponent(5).setComment("height of control");
struct.getComponent(6).setComment("control identifier");
struct.add(DWordDataType.dataType, "style", "style of dialog box");
struct.add(DWordDataType.dataType, "dwExtendedStyle", "extended styles for a window");
struct.add(ShortDataType.dataType, "x", "x-coordinate of upper-left corner of control");
struct.add(ShortDataType.dataType, "y", "y-coordinate of upper-left corner of control");
struct.add(ShortDataType.dataType, "cx", "width of control");
struct.add(ShortDataType.dataType, "cy", "height of control");
struct.add(DWordDataType.dataType, "id", "control identifier");
return struct;
}
@@ -459,16 +349,15 @@ public class DialogResourceDataType extends DynamicDataType {
return itemTypeMap.get(value);
}
private int addUnicodeString(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset, String title) {
private int addUnicodeString(MemBuffer memBuffer, List<DataTypeComponent> comps, int tempOffset,
String title) {
byte[] tempBytes = new byte[1024];
memBuffer.getBytes(tempBytes, tempOffset);
int strLength = findUnicodeLength(tempBytes);
if (strLength >= 2) {
tempOffset =
addComp(UnicodeDataType.dataType, strLength, title,
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(UnicodeDataType.dataType, strLength, title,
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
return tempOffset;
@@ -495,9 +384,8 @@ public class DialogResourceDataType extends DynamicDataType {
private int addComp(DataType dataType, int len, String fieldName, Address address,
List<DataTypeComponent> comps, int currentOffset) {
if (len > 0) {
ReadOnlyDataTypeComponent readOnlyDataTypeComponent =
new ReadOnlyDataTypeComponent(dataType, this, len, comps.size(), currentOffset,
fieldName, null);
ReadOnlyDataTypeComponent readOnlyDataTypeComponent = new ReadOnlyDataTypeComponent(
dataType, this, len, comps.size(), currentOffset, fieldName, null);
comps.add(readOnlyDataTypeComponent);
currentOffset += len;
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -131,9 +131,8 @@ public class MenuResourceDataType extends DynamicDataType {
}
//once verified as valid, lay down the initial structure
tempOffset =
addComp(menuItemTemplateHeaderStructure(), 4, "Menu Item Template Header Structure",
memBuffer.getAddress(), comps, tempOffset);
tempOffset = addComp(menuItemTemplateHeaderStructure(), 4,
"Menu Item Template Header Structure", memBuffer.getAddress(), comps, tempOffset);
return tempOffset;
}
@@ -141,21 +140,8 @@ public class MenuResourceDataType extends DynamicDataType {
//This is always the first structure in the menu resource
private StructureDataType menuItemTemplateHeaderStructure() {
StructureDataType struct = new StructureDataType("MENUITEM_TEMPLATE_HEADER", 0);
struct.add(WordDataType.dataType);
struct.add(WordDataType.dataType);
try {
struct.getComponent(0).setFieldName("versionNumber");
struct.getComponent(1).setFieldName("offset");
}
catch (DuplicateNameException e) {
Msg.debug(this, "Unexpected exception building MENUITEM_TEMPLATE_HEADER", e);
}
struct.getComponent(0).setComment("Version number of menu");
struct.getComponent(1).setComment("Menu items offset.");
struct.add(WordDataType.dataType, "versionNumber", "Version number of menu");
struct.add(WordDataType.dataType, "offset", "Menu items offset.");
return struct;
}
@@ -165,19 +151,16 @@ public class MenuResourceDataType extends DynamicDataType {
//If it is a popup there is only an option field, no ID field
if ((mtOption & MF_POPUP) == MF_POPUP) {
tempOffset =
addComp(WordDataType.dataType, 2, "mtOption", memBuffer.getAddress(), comps,
tempOffset);
tempOffset = addComp(WordDataType.dataType, 2, "mtOption", memBuffer.getAddress(),
comps, tempOffset);
}
//If it is anything else it has option and id fields
else {
tempOffset =
addComp(WordDataType.dataType, 2, "mtOption", memBuffer.getAddress(), comps,
tempOffset);
tempOffset = addComp(WordDataType.dataType, 2, "mtOption", memBuffer.getAddress(),
comps, tempOffset);
tempOffset =
addComp(WordDataType.dataType, 2, "mtID", memBuffer.getAddress().add(tempOffset),
comps, tempOffset);
tempOffset = addComp(WordDataType.dataType, 2, "mtID",
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
}
@@ -189,25 +172,23 @@ public class MenuResourceDataType extends DynamicDataType {
private int addComp(DataType dataType, int len, String fieldName, Address address,
List<DataTypeComponent> comps, int currentOffset) {
if (len > 0) {
ReadOnlyDataTypeComponent readOnlyDataTypeComponent =
new ReadOnlyDataTypeComponent(dataType, this, len, comps.size(), currentOffset,
fieldName, null);
ReadOnlyDataTypeComponent readOnlyDataTypeComponent = new ReadOnlyDataTypeComponent(
dataType, this, len, comps.size(), currentOffset, fieldName, null);
comps.add(readOnlyDataTypeComponent);
currentOffset += len;
}
return currentOffset;
}
private int addUnicodeString(MemBuffer memBuffer, List<DataTypeComponent> comps,
int tempOffset, String title) {
private int addUnicodeString(MemBuffer memBuffer, List<DataTypeComponent> comps, int tempOffset,
String title) {
byte[] tempBytes = new byte[1024];
memBuffer.getBytes(tempBytes, tempOffset);
int strLength = findUnicodeLength(tempBytes);
if (strLength >= 2) {
tempOffset =
addComp(UnicodeDataType.dataType, strLength, title,
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
tempOffset = addComp(UnicodeDataType.dataType, strLength, title,
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
return tempOffset;
}
@@ -101,8 +101,9 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl
}
@Override
public void setComment(String comment) {
public DataTypeComponent setComment(String comment) {
// ignore - read-only
return this;
}
@Override
@@ -114,8 +115,9 @@ public class ReadOnlyDataTypeComponent implements DataTypeComponent, Serializabl
}
@Override
public void setFieldName(String fieldName) throws DuplicateNameException {
public DataTypeComponent setFieldName(String fieldName) {
// ignore - read-only
return this;
}
@Override
@@ -162,7 +162,8 @@ public interface Structure extends Composite {
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the declared bitfield size in bits. The effective bit size may be adjusted
* based upon the specified baseDataType.
* @param componentName the field name to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component.
* @return the bitfield component created whose associated data type will be BitFieldDataType.
* @throws InvalidDataTypeException if the specified baseDataType is not a valid base type for
@@ -206,7 +207,8 @@ public interface Structure extends Composite {
* viewed as big-endian. The final offset may be reduced based upon the minimal
* storage size determined during insertion.
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param componentName the field name to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param bitSize the bitfield size in bits. A bitSize of 0 may be specified although its name
* will be ignored.
* @param comment the comment to associate with this component.
@@ -257,7 +259,8 @@ public interface Structure extends Composite {
* structure an {@link Undefined1DataType} will be used in its place.
* @param length the length to associate with the dataType. For fixed length types a length
* &lt;= 0 will use the length of the resolved dataType.
* @param name the field name to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component.
* @return the componentDataType created.
* @throws IllegalArgumentException if the specified data type is not allowed to be inserted
@@ -265,7 +268,8 @@ public interface Structure extends Composite {
* suppose dt1 contains dt2. Therefore it is not valid to insert dt1 to dt2 since
* this would cause a cyclic dependency.
*/
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length, String name,
public DataTypeComponent insertAtOffset(int offset, DataType dataType, int length,
String componentName,
String comment) throws IllegalArgumentException;
/**
@@ -383,14 +387,16 @@ public interface Structure extends Composite {
* @param length component length for containing the specified dataType. A positive length is required
* for sizable {@link Dynamic} datatypes and should be specified as -1 for fixed-length
* datatypes to rely on their resolved size.
* @param name the field name to associate with this component or null.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component or null.
* @return the new component.
* @throws IllegalArgumentException may be caused by: 1) invalid offset specified, 2) invalid datatype or
* associated length specified, or 3) insufficient space for replacement.
* @throws IndexOutOfBoundsException if component ordinal is out of bounds
*/
public DataTypeComponent replace(int ordinal, DataType dataType, int length, String name,
public DataTypeComponent replace(int ordinal, DataType dataType, int length,
String componentName,
String comment) throws IndexOutOfBoundsException, IllegalArgumentException;
/**
@@ -428,13 +434,15 @@ public interface Structure extends Composite {
* @param length component length for containing the specified dataType. A positive length is required
* for sizable {@link Dynamic} datatypes and should be specified as -1 for fixed-length
* datatypes to rely on their resolved size.
* @param name the field name to associate with this component or null.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component or null.
* @return the new component.
* @throws IllegalArgumentException may be caused by: 1) invalid offset specified, 2) invalid datatype or
* associated length specified, or 3) insufficient space for replacement.
*/
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length, String name,
public DataTypeComponent replaceAtOffset(int offset, DataType dataType, int length,
String componentName,
String comment) throws IllegalArgumentException;
/**
@@ -16,6 +16,7 @@
package ghidra.program.model.data;
import java.util.*;
import java.util.function.Consumer;
import ghidra.docking.settings.Settings;
import ghidra.program.database.data.DataTypeUtilities;
@@ -26,13 +27,8 @@ import ghidra.util.exception.AssertException;
/**
* Basic implementation of the structure data type.
* NOTES:
* <ul>
* <li>Implementation is not thread safe when being modified.</li>
* <li>For a structure to treated as having a zero-length (see {@link #isZeroLength()}) it </li>
*
* </ul>
*
* <p>
* NOTE: Implementation is not thread safe.
*/
public class StructureDataType extends CompositeDataTypeImpl implements StructureInternal {
@@ -386,6 +382,7 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
}
if (nextOrdinal != null && nextOrdinal == ordinal) {
// defined component removed
if (dtc.isBitFieldComponent()) {
// defer reconciling bitfield space to repack
@@ -394,12 +391,13 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
else {
offsetAdjustment -= dtc.getLength();
}
dtc.getDataType().removeParent(this);
--ordinalAdjustment;
lastDefinedOrdinal = ordinal;
nextOrdinal = sortedOrdinals.higher(ordinal);
}
else {
if (ordinalAdjustment != 0) {
shiftOffset(dtc, ordinalAdjustment, offsetAdjustment);
}
@@ -565,11 +563,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
return new DataTypeComponentImpl(DataType.DEFAULT, this, 1, ordinal, offset);
}
DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal,
length = getPreferredComponentLength(dataType, length);
DataTypeComponentImpl dtc = createComponent(dataType, length, ordinal,
offset, componentName, comment);
dataType.addParent(this);
shiftOffsets(index, 1 + additionalShift, length + additionalShift);
components.add(index, dtc);
repack(false);
notifySizeChanged();
return dtc;
@@ -622,12 +623,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
DataTypeComponentImpl dtc;
if (dataType == DataType.DEFAULT) {
// assume non-packed structure - structre will grow by 1-byte below
// assume non-packed structure - will grow by 1-byte below
dtc = new DataTypeComponentImpl(DataType.DEFAULT, this, 1, numComponents, structLength);
}
else {
int componentLength = getPreferredComponentLength(dataType, length);
dtc = new DataTypeComponentImpl(dataType, this, componentLength, numComponents,
dtc = createComponent(dataType, componentLength, numComponents,
structLength, componentName, comment);
dataType.addParent(this);
components.add(dtc);
@@ -667,9 +668,14 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
else {
index = backupToFirstComponentContainingOffset(index, len);
index = afterNonZeroComponentsAtOffset(index, len);
}
int definedComponentCount = components.size();
if (index >= 0 && index < definedComponentCount) {
// Process deleted components
components.subList(index, components.size()).forEach(dtc -> {
dtc.getDataType().removeParent(this);
});
components = components.subList(0, index);
}
}
@@ -743,11 +749,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
length = getPreferredComponentLength(dataType, length);
int offset = (getComponent(ordinal)).getOffset();
DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal,
DataTypeComponentImpl dtc = createComponent(dataType, length, ordinal,
offset, componentName, comment);
dataType.addParent(this);
shiftOffsets(idx, 1, dtc.getLength());
components.add(idx, dtc);
repack(false);
notifySizeChanged();
return dtc;
@@ -901,11 +908,11 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
BitFieldDataType bitfieldDt = new BitFieldDataType(baseDataType, bitSize, storageBitOffset);
DataTypeComponentImpl dtc = new DataTypeComponentImpl(bitfieldDt, this,
DataTypeComponentImpl dtc = createComponent(bitfieldDt,
bitfieldDt.getStorageSize(), ordinal, revisedOffset, componentName, comment);
bitfieldDt.addParent(this); // currently has no affect
components.add(startIndex, dtc);
adjustNonPackedComponents();
notifySizeChanged();
return dtc;
@@ -1259,6 +1266,9 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
/**
* Replaces the internal components of this structure with components of the given structure
* including packing and alignment settings.
* <p>
* NOTE: unlike adding new components which will guarantee component name uniqueness, this
* method will preserve component names.
*
* @param dataType the structure to get the component information from.
* @throws IllegalArgumentException if any of the component data types are not allowed to
@@ -1274,6 +1284,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
try {
// Remove all existing components
for (DataTypeComponentImpl dtc : components) {
dtc.getDataType().removeParent(this);
}
components.clear();
numComponents = 0;
structLength = 0;
@@ -1341,8 +1357,11 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
length = getPreferredComponentLength(dt, -1, maxLength);
}
components.add(new DataTypeComponentImpl(dt, this, length, dtc.getOrdinal(),
dtc.getOffset(), dtc.getFieldName(), dtc.getComment()));
// Note: original component name is preserved
DataTypeComponentImpl newDtc = createComponent(dt, length, dtc.getOrdinal(),
dtc.getOffset(), dtc.getFieldName(), dtc.getComment());
dt.addParent(this);
components.add(newDtc);
}
}
@@ -1431,7 +1450,12 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
@Override
public DataTypeComponent[] getDefinedComponents() {
return components.toArray(new DataTypeComponent[components.size()]);
return components.toArray(new DataTypeComponentImpl[components.size()]);
}
@Override
void forEachDefinedComponent(Consumer<DataTypeComponentImpl> dtcConsumer) {
components.forEach(dtcConsumer);
}
@Override
@@ -1481,7 +1505,8 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
}
@Override
public final DataTypeComponent replace(int index, DataType dataType, int length) {
public final DataTypeComponent replace(int index, DataType dataType, int length)
throws IndexOutOfBoundsException, IllegalArgumentException {
return replace(index, dataType, length, null, null);
}
@@ -1792,9 +1817,10 @@ public class StructureDataType extends CompositeDataTypeImpl implements Structur
DataTypeComponentImpl newDtc = null;
if (!clearOnly) {
// insert new component
newDtc = new DataTypeComponentImpl(dataType, this, length, newOrdinal, newOffset,
fieldName, comment);
newDtc =
createComponent(dataType, length, newOrdinal, newOffset, fieldName, comment);
components.add(index, newDtc);
dataType.addParent(this);
}
// adjust ordinals of trailing components - defer if packing is enabled
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -37,7 +37,8 @@ public interface Union extends Composite {
* @param baseDataType the bitfield base datatype (certain restrictions apply).
* @param bitSize the declared bitfield size in bits. The effective bit size may be
* adjusted based upon the specified baseDataType.
* @param componentName the field name to associate with this component.
* @param componentName the field name to associate with this component. (may be null)
* The name may be sanitized to convert all whitespace characters to an underscore.
* @param comment the comment to associate with this component.
* @return the bitfield component created whose associated data type will
* be BitFieldDataType.
@@ -16,6 +16,7 @@
package ghidra.program.model.data;
import java.util.*;
import java.util.function.Consumer;
import ghidra.docking.settings.Settings;
import ghidra.program.database.data.DataTypeUtilities;
@@ -24,7 +25,8 @@ import ghidra.util.UniversalID;
/**
* Basic implementation of the union data type.
* NOTE: Implementation is not thread safe when being modified.
* <p>
* NOTE: Implementation is not thread safe.
*/
public class UnionDataType extends CompositeDataTypeImpl implements UnionInternal {
@@ -116,6 +118,11 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
return getComponents();
}
@Override
void forEachDefinedComponent(Consumer<DataTypeComponentImpl> dtcConsumer) {
components.forEach(dtcConsumer);
}
@Override
public int getNumComponents() {
return components.size();
@@ -134,6 +141,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
int oldAlignment = getAlignment();
DataTypeComponent dtc = doAdd(dataType, length, componentName, comment);
if (!repack(true) && isPackingEnabled() && oldAlignment != getAlignment()) {
notifyAlignmentChanged();
}
@@ -163,8 +171,8 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
return length;
}
DataTypeComponentImpl doAdd(DataType dataType, int length, String componentName, String comment)
throws DataTypeDependencyException {
DataTypeComponentImpl doAdd(DataType dataType, int length, String componentName,
String comment) throws DataTypeDependencyException {
dataType = validateDataType(dataType);
@@ -175,8 +183,9 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
length = getPreferredComponentLength(dataType, length);
DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length,
DataTypeComponentImpl dtc = createComponent(dataType, length,
components.size(), 0, componentName, comment);
dataType.addParent(this);
components.add(dtc);
@@ -198,7 +207,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
length = getPreferredComponentLength(dataType, length);
DataTypeComponentImpl dtc = new DataTypeComponentImpl(dataType, this, length, ordinal,
DataTypeComponentImpl dtc = createComponent(dataType, length, ordinal,
0, componentName, comment);
dataType.addParent(this);
shiftOrdinals(ordinal, 1);
@@ -311,6 +320,7 @@ public class UnionDataType extends CompositeDataTypeImpl implements UnionInterna
if (ordinals.contains(ordinal)) {
// component removed
--ordinalAdjustment;
dtc.getDataType().removeParent(this);
}
else {
if (ordinalAdjustment != 0) {
@@ -134,6 +134,7 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals("field1", dtc.getFieldName());
assertEquals("Comment1", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertEquals(dtc, struct.findComponent("field1"));
dtc = struct.getComponent(1);
assertEquals(1, dtc.getOffset());
@@ -148,6 +149,7 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals("field3", dtc.getFieldName());
assertEquals(null, dtc.getComment());
assertEquals(DWordDataType.class, dtc.getDataType().getClass());
assertEquals(dtc, struct.findComponent("field3"));
dtc = struct.getComponent(3);
assertEquals(7, dtc.getOffset());
@@ -155,7 +157,23 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals("field4", dtc.getFieldName());
assertEquals("Comment4", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertEquals(dtc, struct.findComponent("field4"));
dtc = struct.add(ByteDataType.dataType, "field3", "new comment");
assertEquals(8, dtc.getOffset());
assertEquals(4, dtc.getOrdinal());
assertEquals("field3", dtc.getFieldName());
assertEquals("new comment", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertNotNull(struct.findComponent("field3")); // which one is returned is arbitrary
dtc = struct.add(ByteDataType.dataType, "field3 1", "new comment"); // cleanup required
assertEquals(9, dtc.getOffset());
assertEquals(5, dtc.getOrdinal());
assertEquals("field3_1", dtc.getFieldName());
assertEquals("new comment", dtc.getComment());
assertEquals(ByteDataType.class, dtc.getDataType().getClass());
assertEquals(dtc, struct.findComponent("field3_1")); // name gets modified
}
@Test
@@ -1869,7 +1887,7 @@ public class StructureDBTest extends AbstractGenericTest {
}
@Test
public void testDelete() throws InvalidDataTypeException {
public void testDeleteBF() throws InvalidDataTypeException {
struct.insertBitFieldAt(2, 4, 0, IntegerDataType.dataType, 3, "bf1", "bf1Comment");
struct.insertBitFieldAt(2, 4, 3, IntegerDataType.dataType, 3, "bf2", "bf2Comment");
@@ -1911,8 +1929,12 @@ public class StructureDBTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf2"));
struct.delete(3);
assertNull(struct.findComponent("bf2"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
@@ -1928,8 +1950,12 @@ public class StructureDBTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf3"));
struct.delete(3);
assertNull(struct.findComponent("bf3"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
@@ -1945,8 +1971,12 @@ public class StructureDBTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf4"));
struct.delete(4);
assertNull(struct.findComponent("bf4"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
@@ -1963,8 +1993,12 @@ public class StructureDBTest extends AbstractGenericTest {
"Length: 11 Alignment: 1", struct);
//@formatter:on
assertNotNull(struct.findComponent("bf1"));
struct.delete(2);
assertNull(struct.findComponent("bf1"));
//@formatter:off
CompositeTestUtils.assertExpectedComposite(this, "/Test\n" +
"pack(disabled)\n" +
@@ -2092,7 +2126,7 @@ public class StructureDBTest extends AbstractGenericTest {
}
@Test
public void testDeleteComponent() {
public void testDataTypeDeleted() {
Structure s = new StructureDataType("test1", 0);
s.add(new ByteDataType());
s.add(new FloatDataType());
@@ -2149,6 +2183,21 @@ public class StructureDBTest extends AbstractGenericTest {
assertEquals(0, s.getNumComponents());
}
@Test
public void testGetComponentByName() {
DataTypeComponent dtc = struct.findComponent("field1");
assertNotNull(dtc);
assertEquals("field1", dtc.getFieldName());
dtc = struct.findComponent("field3");
assertNotNull(dtc);
assertEquals("field3", dtc.getFieldName());
dtc = struct.findComponent("field4");
assertNotNull(dtc);
assertEquals("field4", dtc.getFieldName());
}
@Test
public void testGetComponentAt() {
/**
@@ -745,4 +745,63 @@ public class UnionDBTest extends AbstractGenericTest {
"Should be able to insert a union typedef array pointer into the pointer's union.");
}
}
@Test
public void testFieldNameWhitespaceConvertedToUnderscores() {
UnionDataType newUnion = new UnionDataType("Test");
DataTypeComponent component = newUnion.add(new ByteDataType(), " name with spaces", null);
assertEquals("name_with_spaces", component.getFieldName());
union = (UnionDB) dataMgr.resolve(newUnion, null);
component = union.getComponent(0);
component.setFieldName(" name in db with spaces ");
assertEquals("name_in_db_with_spaces", component.getFieldName());
component = union.add(new ByteDataType(), " another test ", null);
assertEquals("another_test", component.getFieldName());
union.insert(0, new ByteDataType(), 1, " insert test ", "");
component = union.getComponent(0);
assertEquals("insert_test", component.getFieldName());
union.insert(1, new ByteDataType(), 1, " insert test ", "");
component = union.getComponent(1);
assertEquals("insert_test", component.getFieldName());
}
@Test
public void testDefaultFieldNames() {
UnionDataType newUnion = new UnionDataType("Test");
DataTypeComponent component = newUnion.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
union = (UnionDB) dataMgr.resolve(newUnion, null);
component = union.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
assertEquals("field1", component.getDefaultFieldName());
component = union.getComponent(0);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
component.setFieldName(" ");
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
component = union.add(new ByteDataType(), null, null);
assertNull(component.getFieldName());
assertEquals("field2", component.getDefaultFieldName());
component = union.add(new ByteDataType(), " ", null);
assertNull(component.getFieldName());
assertEquals("field3", component.getDefaultFieldName());
union.insert(0, new ByteDataType(), 1, null, "");
component = union.getComponent(0);
assertNull(component.getFieldName());
assertEquals("field0", component.getDefaultFieldName());
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -77,7 +77,7 @@ public class CompositeTestUtils {
if (mismatch) {
Msg.error(test, "Expected composite:\n" + expectedDump);
Msg.error(test, "Result composite:\n" + result);
fail("Failed to parse expected composite (see log)");
fail("Composite mismatch (see log)");
}
}