GT-3415 Added mutability setting to enum datatypes as well as some code

cleanup.
This commit is contained in:
ghidra1 2019-12-20 16:42:44 -05:00
parent ccabecec3a
commit 3ea8770bae
8 changed files with 347 additions and 320 deletions

View File

@ -46,6 +46,7 @@ import ghidra.docking.settings.SettingsDefinition;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*;
import ghidra.program.util.*;
import ghidra.test.*;
@ -1264,6 +1265,8 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
assertNotNull("Expected data type: " + dtName, d);
assertTrue("Expected data type: " + dtName, expectedDataType.isInstance(d.getDataType()));
DataType dt = d.getDataType();
boolean onByteWordData = true;
boolean onFloatDoubleData = true;
boolean onCharData = true;
@ -1280,7 +1283,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
// || expectedDataType.equals(StringDataType.class)
// || expectedDataType.equals(UnicodeDataType.class));
boolean hasSettings = d.getDataType().getSettingsDefinitions().length != 0;
boolean hasSettings = dt.getSettingsDefinitions().length != 0;
String caseName = "On " + dtName + " at: " + getCurrentLocation();
@ -1294,7 +1297,8 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
checkAction(actions, CREATE_STRUCTURE, hasSelection, caseName);
checkAction(actions, EDIT_DATA_TYPE,
pdata != null && (pdata.isStructure() || pdata.isUnion()), caseName);
(pdata != null && (pdata.isStructure() || pdata.isUnion())) || (dt instanceof Enum),
caseName);
checkAction(actions, CREATE_ARRAY, true, caseName);
checkAction(actions, DEFAULT_DATA_SETTINGS,
(!hasSelection || isSelectionJustSingleDataInstance(sel, d)) && hasSettings, caseName);

View File

@ -0,0 +1,65 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.data;
import org.junit.Before;
import org.junit.Test;
import ghidra.program.model.data.EnumDataType;
public class EnumDataActionTest extends AbstractDataActionTest {
private EnumDataType testEnum;
private DataPlugin dataPlugin;
private TestEnumDataAction enumDataAction;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
testEnum = new EnumDataType("TEST_ENUM", 2);
testEnum.add("A", 0);
testEnum.add("B", 1);
testEnum.add("C", 2);
testEnum.add("D", 3);
testEnum.add("E", 3);
dataPlugin = getPlugin(tool, DataPlugin.class);
enumDataAction = new TestEnumDataAction(dataPlugin);
tool.addAction(enumDataAction);
}
@Test
public void testAllEnumDataSettings() throws Exception {
String actionName = enumDataAction.getName();
manipulateAllSettings(false, true, false, actionName);
manipulateAllSettings(true, true, true, actionName);
manipulateAllSettings(false, false, false, actionName);
manipulateAllSettings(true, false, false, actionName);
}
class TestEnumDataAction extends DataAction {
public TestEnumDataAction(DataPlugin plugin) {
super(testEnum, plugin);
}
}
}

View File

@ -44,22 +44,22 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
super();
}
@Before
public void setUp() throws Exception {
@Before
public void setUp() throws Exception {
program = createDefaultProgram("Test", ProgramBuilder._TOY, this);
dataMgr = program.getDataTypeManager();
transactionID = program.startTransaction("Test");
}
@After
public void tearDown() throws Exception {
@After
public void tearDown() throws Exception {
program.endTransaction(transactionID, false);
program.release(this);
}
@Test
public void testCreateEnum() throws Exception {
@Test
public void testCreateEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0);
enumm.add("Green", 1);
@ -88,29 +88,48 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertNotNull(c.getDataType("Color"));
}
@Test
public void testRemoveValue() throws Exception {
@Test
public void testRemoveValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0);
enumm.add("Green", 1);
enumm.add("Blue", 2);
enumm.add("blue", 2);
Category root = dataMgr.getRootCategory();
Category c = root.createCategory("enumms");
enumm.setCategoryPath(c.getCategoryPath());
Enum enummDT = (Enum) dataMgr.resolve(enumm, null);
assertEquals(3, enummDT.getCount());
enummDT.remove("Green");
assertEquals(2, enummDT.getCount());
assertArrayEquals(new long[] { 0, 1, 2 }, enumm.getValues());
assertEquals(4, enumm.getCount());
enummDT.remove("Green");
enummDT.remove("blue");
assertEquals(2, enummDT.getCount());
assertArrayEquals(new long[] { 0, 2 }, enummDT.getValues());
assertEquals(2, enummDT.getValue("Blue"));
try {
enummDT.getValue("blue");
fail("expected NoSuchElementException");
}
catch (NoSuchElementException e) {
// expected
}
}
@Test
public void testAddValue() throws Exception {
@Test
public void testAddValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0);
enumm.add("Green", 1);
enumm.add("Blue", 2);
enumm.add("blue", 2);
assertArrayEquals(new long[] { 0, 1, 2 }, enumm.getValues());
assertEquals(4, enumm.getCount());
Category root = dataMgr.getRootCategory();
Category c = root.createCategory("enumms");
@ -119,12 +138,15 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
Enum enummDT = (Enum) dataMgr.resolve(enumm, null);
enummDT.add("Purple", 7);
assertEquals(4, enummDT.getCount());
assertEquals(5, enummDT.getCount());
assertEquals(7, enummDT.getValue("Purple"));
assertEquals(2, enummDT.getValue("Blue"));
assertEquals(2, enummDT.getValue("blue"));
assertArrayEquals(new long[] { 0, 1, 2, 7 }, enummDT.getValues());
}
@Test
public void testEditValue() throws Exception {
@Test
public void testEditValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -148,8 +170,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(2, listener.getCount());
}
@Test
public void testCloneRetainIdentity() throws Exception {
@Test
public void testCloneRetainIdentity() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -168,8 +190,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(copyDT.isEquivalent(c2));
}
@Test
public void testCopyNoRetainIdentity() throws Exception {
@Test
public void testCopyNoRetainIdentity() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -188,8 +210,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(copyDT.isEquivalent(c2));
}
@Test
public void testRemoveEnum() throws Exception {
@Test
public void testRemoveEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -208,8 +230,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
}
@Test
public void testMoveEnum() throws Exception {
@Test
public void testMoveEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -224,8 +246,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertNull(c.getDataType(enumm.getName()));
}
@Test
public void testResolve() throws Exception {
@Test
public void testResolve() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -239,8 +261,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(enummDT, dataMgr.getDataType(id));
}
@Test
public void testReplace() throws Exception {
@Test
public void testReplace() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);
@ -275,8 +297,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
}
}
@Test
public void testIsEquivalent() throws Exception {
@Test
public void testIsEquivalent() throws Exception {
Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10);
enumm.add("Green", 15);

View File

@ -21,6 +21,7 @@ import java.util.*;
import db.Record;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.DBObjectCache;
import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
@ -28,9 +29,6 @@ import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.UniversalID;
import ghidra.util.datastruct.LongObjectHashtable;
import ghidra.util.datastruct.ObjectLongHashtable;
import ghidra.util.exception.NoValueException;
/**
* Database implementation for the enumerated data type.
@ -38,10 +36,13 @@ import ghidra.util.exception.NoValueException;
*/
class EnumDB extends DataTypeDB implements Enum {
private static final SettingsDefinition[] ENUM_SETTINGS_DEFINITIONS =
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private EnumDBAdapter adapter;
private EnumValueDBAdapter valueAdapter;
private ObjectLongHashtable<String> nameMap;
private LongObjectHashtable<String> valueMap;
private Map<String, Long> nameMap; // name to value
private Map<Long, List<String>> valueMap; // value to names
private List<BitGroup> bitGroups;
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter,
@ -49,9 +50,6 @@ class EnumDB extends DataTypeDB implements Enum {
super(dataMgr, cache, record);
this.adapter = adapter;
this.valueAdapter = valueAdapter;
nameMap = new ObjectLongHashtable<>();
valueMap = new LongObjectHashtable<>();
initialize();
}
@Override
@ -64,71 +62,109 @@ class EnumDB extends DataTypeDB implements Enum {
return record.getString(EnumDBAdapter.ENUM_NAME_COL);
}
@Override
public SettingsDefinition[] getSettingsDefinitions() {
return ENUM_SETTINGS_DEFINITIONS;
}
private void initializeIfNeeded() {
if (nameMap != null) {
return;
}
try {
initialize();
}
catch (IOException e) {
dataMgr.dbError(e);
}
}
private void initialize() throws IOException {
bitGroups = null;
nameMap.removeAll();
valueMap.removeAll();
nameMap = new HashMap<>();
valueMap = new HashMap<>();
long[] ids = valueAdapter.getValueIdsInEnum(key);
for (int i = 0; i < ids.length; i++) {
Record rec = valueAdapter.getRecord(ids[i]);
String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL);
long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL);
nameMap.put(valueName, value);
valueMap.put(value, valueName);
addToCache(valueName, value);
}
}
/**
* @see ghidra.program.model.data.Enum#getValue(java.lang.String)
*/
private void addToCache(String valueName, long value) {
nameMap.put(valueName, value);
List<String> list = valueMap.computeIfAbsent(value, v -> new ArrayList<>());
list.add(valueName);
}
private boolean removeFromCache(String valueName) {
Long value = nameMap.remove(valueName);
if (value == null) {
return false;
}
List<String> list = valueMap.get(value);
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
if (valueName.equals(iter.next())) {
iter.remove();
break;
}
}
if (list.isEmpty()) {
valueMap.remove(value);
}
return true;
}
@Override
public long getValue(String name) throws NoSuchElementException {
public long getValue(String valueName) throws NoSuchElementException {
lock.acquire();
try {
checkIsValid();
return nameMap.get(name);
}
catch (NoValueException e) {
throw new NoSuchElementException(name + " does not exist in this enum");
initializeIfNeeded();
Long value = nameMap.get(valueName);
if (value == null) {
throw new NoSuchElementException("No value for " + valueName);
}
return value;
}
finally {
lock.release();
}
}
/**
* @see ghidra.program.model.data.Enum#getName(long)
*/
@Override
public String getName(long value) {
lock.acquire();
try {
checkIsValid();
return valueMap.get(value);
initializeIfNeeded();
List<String> list = valueMap.get(value);
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
finally {
lock.release();
}
}
/**
* @see ghidra.program.model.data.DataType#isDynamicallySized()
*/
@Override
public boolean isDynamicallySized() {
return false;
}
/**
* @see ghidra.program.model.data.Enum#getValues()
*/
@Override
public long[] getValues() {
lock.acquire();
try {
checkIsValid();
long[] values = valueMap.getKeys();
initializeIfNeeded();
long[] values = valueMap.keySet().stream().mapToLong(Long::longValue).toArray();
Arrays.sort(values);
return values;
}
@ -137,31 +173,25 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.Enum#getNames()
*/
@Override
public String[] getNames() {
lock.acquire();
try {
checkIsValid();
String[] names = nameMap.getKeys(new String[nameMap.size()]);
Arrays.sort(names);
return names;
initializeIfNeeded();
return nameMap.keySet().toArray(new String[nameMap.size()]);
}
finally {
lock.release();
}
}
/**
* @see ghidra.program.model.data.Enum#getCount()
*/
@Override
public int getCount() {
lock.acquire();
try {
checkIsValid();
initializeIfNeeded();
return nameMap.size();
}
finally {
@ -169,58 +199,62 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.Enum#add(java.lang.String, long)
*/
@Override
public void add(String name, long value) {
public void add(String valueName, long value) {
lock.acquire();
bitGroups = null;
try {
checkDeleted();
if (nameMap.contains(name)) {
throw new IllegalArgumentException(name + " already exists in this enum");
}
try {
valueAdapter.createRecord(key, name, value);
nameMap.put(name, value);
if (!valueMap.contains(value)) {
valueMap.put(value, name);
}
adapter.updateRecord(record, true);
dataMgr.dataTypeChanged(this);
}
catch (IOException e) {
dataMgr.dbError(e);
checkValue(value);
initializeIfNeeded();
if (nameMap.containsKey(valueName)) {
throw new IllegalArgumentException(valueName + " already exists in this enum");
}
bitGroups = null;
valueAdapter.createRecord(key, valueName, value);
adapter.updateRecord(record, true);
addToCache(valueName, value);
dataMgr.dataTypeChanged(this);
}
catch (IOException e) {
dataMgr.dbError(e);
}
finally {
lock.release();
}
}
/**
* @see ghidra.program.model.data.Enum#remove(java.lang.String)
*/
private void checkValue(long value) {
int length = getLength();
if (length == 8) {
return; // all long values permitted
}
// compute maximum enum value as a positive value: (2^length)-1
long max = (1L << (getLength() * 8)) - 1;
if (value > max) {
throw new IllegalArgumentException(
getName() + " enum value 0x" + Long.toHexString(value) +
" is outside the range of 0x0 to 0x" + Long.toHexString(max));
}
}
@Override
public void remove(String name) {
public void remove(String valueName) {
lock.acquire();
bitGroups = null;
try {
checkDeleted();
if (!nameMap.contains(name)) {
initializeIfNeeded();
if (!removeFromCache(valueName)) {
return;
}
long value = nameMap.get(name);
nameMap.remove(name);
if (name.equals(valueMap.get(value))) {
valueMap.remove(value);
}
bitGroups = null;
long[] ids = valueAdapter.getValueIdsInEnum(key);
for (int i = 0; i < ids.length; i++) {
Record rec = valueAdapter.getRecord(ids[i]);
if (name.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) {
if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) {
valueAdapter.removeRecord(ids[i]);
break;
}
@ -231,17 +265,11 @@ class EnumDB extends DataTypeDB implements Enum {
catch (IOException e) {
dataMgr.dbError(e);
}
catch (NoValueException e) {
// can't happen
}
finally {
lock.release();
}
}
/*
* @see ghidra.program.model.data.Enum#replace(ghidra.program.model.data.Enum)
*/
@Override
public void replaceWith(DataType dataType) {
if (!(dataType instanceof Enum)) {
@ -249,30 +277,33 @@ class EnumDB extends DataTypeDB implements Enum {
}
Enum enumm = (Enum) dataType;
lock.acquire();
bitGroups = null;
try {
checkDeleted();
int oldLength = getLength();
nameMap.removeAll();
valueMap.removeAll();
bitGroups = null;
nameMap = new HashMap<>();
valueMap = new HashMap<>();
long[] ids = valueAdapter.getValueIdsInEnum(key);
for (int i = 0; i < ids.length; i++) {
valueAdapter.removeRecord(ids[i]);
}
String[] names = enumm.getNames();
for (int i = 0; i < names.length; i++) {
if (nameMap.contains(names[i])) {
throw new IllegalArgumentException(names[i] + " already exists in this Enum");
}
long value = enumm.getValue(names[i]);
valueAdapter.createRecord(key, names[i], value);
nameMap.put(names[i], value);
valueMap.put(value, names[i]);
int oldLength = getLength();
int newLength = enumm.getLength();
if (oldLength != newLength) {
record.setByteValue(EnumDBAdapter.ENUM_SIZE_COL, (byte) newLength);
adapter.updateRecord(record, true);
}
int newLength = enumm.getLength();
record.setByteValue(EnumDBAdapter.ENUM_SIZE_COL, (byte) newLength);
adapter.updateRecord(record, true);
String[] names = enumm.getNames();
for (int i = 0; i < names.length; i++) {
long value = enumm.getValue(names[i]);
valueAdapter.createRecord(key, names[i], value);
adapter.updateRecord(record, true);
addToCache(names[i], value);
}
if (oldLength != newLength) {
notifySizeChanged();
@ -308,9 +339,6 @@ class EnumDB extends DataTypeDB implements Enum {
return enumDataType;
}
/**
* @see ghidra.program.model.data.DataType#getMnemonic(ghidra.docking.settings.Settings)
*/
@Override
public String getMnemonic(Settings settings) {
lock.acquire();
@ -323,9 +351,6 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.DataType#getLength()
*/
@Override
public int getLength() {
lock.acquire();
@ -338,9 +363,6 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.DataType#getDescription()
*/
@Override
public String getDescription() {
lock.acquire();
@ -354,9 +376,6 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.Enum#setDescription(java.lang.String)
*/
@Override
public void setDescription(String description) {
lock.acquire();
@ -374,9 +393,6 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
/**
* @see ghidra.program.model.data.DataType#getValue(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int)
*/
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
lock.acquire();
@ -412,9 +428,6 @@ class EnumDB extends DataTypeDB implements Enum {
return Scalar.class;
}
/**
* @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int)
*/
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
lock.acquire();
@ -501,9 +514,6 @@ class EnumDB extends DataTypeDB implements Enum {
return valueName;
}
/**
* @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType)
*/
@Override
public boolean isEquivalent(DataType dt) {
if (dt == this) {
@ -513,7 +523,6 @@ class EnumDB extends DataTypeDB implements Enum {
return false;
}
checkIsValid();
Enum enumm = (Enum) dt;
if (!DataTypeUtilities.equalsIgnoreConflict(getName(), enumm.getName()) ||
getLength() != enumm.getLength() || getCount() != enumm.getCount()) {
@ -536,16 +545,15 @@ class EnumDB extends DataTypeDB implements Enum {
return true;
}
/**
* @see ghidra.program.database.DatabaseObject#refresh()
*/
@Override
protected boolean refresh() {
try {
nameMap = null;
valueMap = null;
bitGroups = null;
Record rec = adapter.getRecord(key);
if (rec != null) {
record = rec;
initialize();
return super.refresh();
}
}
@ -555,9 +563,6 @@ class EnumDB extends DataTypeDB implements Enum {
return false;
}
/**
* @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
// not applicable
@ -575,17 +580,11 @@ class EnumDB extends DataTypeDB implements Enum {
adapter.updateRecord(record, true);
}
/**
* @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeDeleted(DataType dt) {
// not applicable
}
/**
* @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String)
*/
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// not applicable

View File

@ -33,9 +33,11 @@ import ghidra.util.exception.DuplicateNameException;
*/
class PointerDB extends DataTypeDB implements Pointer {
private static final SettingsDefinition[] POINTER_SETTINGS_DEFINITIONS =
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private PointerDBAdapter adapter;
private String displayName;
private SettingsDefinition[] settingsDef;
/**
* Constructor
@ -74,9 +76,6 @@ class PointerDB extends DataTypeDB implements Pointer {
return pointerName;
}
/**
* @see ghidra.program.model.data.Pointer#getDataType()
*/
@Override
public DataType getDataType() {
lock.acquire();
@ -91,18 +90,7 @@ class PointerDB extends DataTypeDB implements Pointer {
@Override
public SettingsDefinition[] getSettingsDefinitions() {
lock.acquire();
try {
checkIsValid();
if (settingsDef == null) {
DataType dt = newPointer(getDataType());
settingsDef = dt.getSettingsDefinitions();
}
return settingsDef;
}
finally {
lock.release();
}
return POINTER_SETTINGS_DEFINITIONS;
}
@Override
@ -126,9 +114,6 @@ class PointerDB extends DataTypeDB implements Pointer {
return false;
}
/**
* @see ghidra.program.model.data.DataType#clone(ghidra.program.model.data.DataTypeManager)
*/
@Override
public final DataType clone(DataTypeManager dtm) {
if (dtm == getDataTypeManager()) {
@ -143,9 +128,6 @@ class PointerDB extends DataTypeDB implements Pointer {
return clone(dtm);
}
/**
* @see ghidra.program.model.data.DataType#getName()
*/
@Override
public String getDisplayName() {
validate(lock);
@ -166,9 +148,6 @@ class PointerDB extends DataTypeDB implements Pointer {
return localDisplayName;
}
/**
* @see ghidra.program.model.data.DataType#getMnemonic(ghidra.docking.settings.Settings)
*/
@Override
public String getMnemonic(Settings settings) {
lock.acquire();
@ -185,9 +164,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.Pointer#isDynamicallySized()
*/
@Override
public boolean isDynamicallySized() {
lock.acquire();
@ -200,9 +176,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#getLength()
*/
@Override
public int getLength() {
lock.acquire();
@ -219,9 +192,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#getDescription()
*/
@Override
public String getDescription() {
lock.acquire();
@ -250,9 +220,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#getValue(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int)
*/
@Override
public Object getValue(MemBuffer buf, Settings settings, int length) {
lock.acquire();
@ -278,9 +245,6 @@ class PointerDB extends DataTypeDB implements Pointer {
return Address.class;
}
/**
* @see ghidra.program.model.data.DataType#getRepresentation(ghidra.program.model.mem.MemBuffer, ghidra.docking.settings.Settings, int)
*/
@Override
public String getRepresentation(MemBuffer buf, Settings settings, int length) {
lock.acquire();
@ -298,9 +262,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#isEquivalent(ghidra.program.model.data.DataType)
*/
@Override
public boolean isEquivalent(DataType dt) {
if (dt == null) {
@ -339,9 +300,6 @@ class PointerDB extends DataTypeDB implements Pointer {
otherDataType.getPathName());
}
/**
* @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
if (newDt == this) {
@ -371,9 +329,6 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeDeleted(DataType dt) {
if (getDataType() == dt) {
@ -440,26 +395,17 @@ class PointerDB extends DataTypeDB implements Pointer {
}
}
/**
* @see ghidra.program.model.data.DataType#setCategoryPath(ghidra.program.model.data.CategoryPath)
*/
@Override
public void setCategoryPath(CategoryPath path) throws DuplicateNameException {
// not permitted to move - follows base type (see updatePath)
}
/**
* @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType)
*/
@Override
public boolean dependsOn(DataType dt) {
DataType myDt = getDataType();
return (myDt != null && (myDt == dt || myDt.dependsOn(dt)));
}
/**
* @see ghidra.program.model.data.Pointer#newPointer(ghidra.program.model.data.DataType)
*/
@Override
public Pointer newPointer(DataType dataType) {
if (isDynamicallySized()) {

View File

@ -32,7 +32,7 @@ import ghidra.util.exception.DuplicateNameException;
*/
public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType {
private static SettingsDefinition[] STANDARD_SETTINGS_DEFINITIONS =
private static final SettingsDefinition[] STANDARD_SETTINGS_DEFINITIONS =
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private SettingsDefinition[] settingDefs;

View File

@ -20,17 +20,20 @@ import java.util.*;
import ghidra.app.plugin.core.datamgr.archive.SourceArchive;
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.UniversalID;
import ghidra.util.datastruct.LongObjectHashtable;
import ghidra.util.datastruct.ObjectLongHashtable;
import ghidra.util.exception.NoValueException;
public class EnumDataType extends GenericDataType implements Enum {
private ObjectLongHashtable<String> defs;
private static final SettingsDefinition[] ENUM_SETTINGS_DEFINITIONS =
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private Map<String, Long> nameMap; // name to value
private Map<Long, List<String>> valueMap; // value to names
private int length;
private String description;
private List<BitGroup> bitGroups;
@ -45,7 +48,11 @@ public class EnumDataType extends GenericDataType implements Enum {
public EnumDataType(CategoryPath path, String name, int length, DataTypeManager dtm) {
super(path, name, dtm);
defs = new ObjectLongHashtable<>();
if (length < 1 || length > 8) {
throw new IllegalArgumentException("unsupported enum length: " + length);
}
nameMap = new HashMap<>();
valueMap = new HashMap<>();
this.length = length;
}
@ -54,13 +61,19 @@ public class EnumDataType extends GenericDataType implements Enum {
DataTypeManager dtm) {
super(path, name, universalID, sourceArchive, lastChangeTime, lastChangeTimeInSourceArchive,
dtm);
defs = new ObjectLongHashtable<>();
if (length < 1 || length > 8) {
throw new IllegalArgumentException("unsupported enum length: " + length);
}
nameMap = new HashMap<>();
valueMap = new HashMap<>();
this.length = length;
}
/**
* @see ghidra.program.model.data.DataType#isDynamicallySized()
*/
@Override
public SettingsDefinition[] getSettingsDefinitions() {
return ENUM_SETTINGS_DEFINITIONS;
}
@Override
public boolean isDynamicallySized() {
return false;
@ -68,91 +81,96 @@ public class EnumDataType extends GenericDataType implements Enum {
@Override
public long getValue(String valueName) throws NoSuchElementException {
try {
return defs.get(valueName);
}
catch (NoValueException e) {
Long value = nameMap.get(valueName);
if (value == null) {
throw new NoSuchElementException("No value for " + valueName);
}
return value;
}
@Override
public String getName(long value) {
String[] names = defs.getKeys(new String[defs.size()]);
for (String name1 : names) {
try {
long nameValue = defs.get(name1);
if (nameValue == value) {
return name1;
}
}
catch (NoValueException e) {
// can't happen
}
List<String> list = valueMap.get(value);
if (list == null || list.isEmpty()) {
return null;
}
return null;
return list.get(0);
}
@Override
public long[] getValues() {
String[] names = defs.getKeys(new String[defs.size()]);
LongObjectHashtable<String> keyTable = new LongObjectHashtable<>();
for (String name1 : names) {
try {
long value = defs.get(name1);
keyTable.put(value, name1);
}
catch (NoValueException e) {
// can't happen
}
}
long[] values = keyTable.getKeys();
long[] values = valueMap.keySet().stream().mapToLong(Long::longValue).toArray();
Arrays.sort(values);
return values;
}
@Override
public String[] getNames() {
String[] names = defs.getKeys(new String[defs.size()]);
Arrays.sort(names);
return names;
return nameMap.keySet().toArray(new String[nameMap.size()]);
}
@Override
public int getCount() {
return defs.size();
return nameMap.size();
}
@Override
public void add(String valueName, long value) {
bitGroups = null;
checkValue(value);
if (defs.contains(valueName)) {
try {
if (defs.get(valueName) == value) {
return;
}
}
catch (NoValueException e) {
}
throw new IllegalArgumentException(name + " enum value " + value + " already assigned");
if (nameMap.containsKey(valueName)) {
throw new IllegalArgumentException(name + " already exists in this enum");
}
defs.put(valueName, value);
nameMap.put(valueName, value);
List<String> list = valueMap.get(value);
if (list == null) {
list = new ArrayList<>();
valueMap.put(value, list);
}
list.add(valueName);
}
private void checkValue(long value) {
long max = (1L << (length * 8)) - 1;
if (max > 0 && value > max) {
throw new IllegalArgumentException(name + " enum value 0x" + Long.toHexString(value) +
" is outside the range of 0x0 to 0x" + Long.toHexString(max));
if (length == 8) {
return; // all long values permitted
}
// compute maximum enum value as a positive value: (2^length)-1
long max = (1L << (getLength() * 8)) - 1;
if (value > max) {
throw new IllegalArgumentException(
getName() + " enum value 0x" + Long.toHexString(value) +
" is outside the range of 0x0 to 0x" + Long.toHexString(max));
}
}
private boolean isTooBig(int testLength, long value) {
if (length == 8) {
return false; // all long values permitted
}
// compute maximum enum value as a positive value: (2^length)-1
long max = (1L << (testLength * 8)) - 1;
return value > max;
}
@Override
public void remove(String valueName) {
bitGroups = null;
defs.remove(valueName);
Long value = nameMap.get(valueName);
if (value != null) {
nameMap.remove(valueName);
List<String> list = valueMap.get(value);
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
if (valueName.equals(iter.next())) {
iter.remove();
break;
}
}
if (list.isEmpty()) {
valueMap.remove(value);
}
}
}
@Override
@ -200,14 +218,6 @@ public class EnumDataType extends GenericDataType implements Enum {
this.length = length;
}
private boolean isTooBig(int testLength, long value) {
long max = (1L << (testLength * 8)) - 1;
if (max > 0 && value > max) {
return true;
}
return false;
}
@Override
public String getDescription() {
return description == null ? "" : description;
@ -368,46 +378,36 @@ public class EnumDataType extends GenericDataType implements Enum {
throw new IllegalArgumentException();
}
Enum enumm = (Enum) dataType;
defs.removeAll();
nameMap = new HashMap<>();
valueMap = new HashMap<>();
setLength(enumm.getLength());
String[] names = enumm.getNames();
for (int i = 0; i < names.length; i++) {
defs.put(names[i], enumm.getValue(names[i]));
add(names[i], enumm.getValue(names[i]));
}
stateChanged(null);
}
/**
* @see ghidra.program.model.data.DataType#dataTypeSizeChanged(ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeSizeChanged(DataType dt) {
// not applicable
}
/**
* @see ghidra.program.model.data.DataType#dataTypeDeleted(ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeDeleted(DataType dt) {
// not applicable
}
/**
* @see ghidra.program.model.data.DataType#dataTypeNameChanged(ghidra.program.model.data.DataType, java.lang.String)
*/
@Override
public void dataTypeNameChanged(DataType dt, String oldName) {
// not applicable
}
/**
* @see ghidra.program.model.data.DataType#dataTypeReplaced(ghidra.program.model.data.DataType, ghidra.program.model.data.DataType)
*/
@Override
public void dataTypeReplaced(DataType oldDt, DataType newDt) {
// not applicable
}
/**
* @see ghidra.program.model.data.DataType#dependsOn(ghidra.program.model.data.DataType)
*/
@Override
public boolean dependsOn(DataType dt) {
return false;

View File

@ -17,15 +17,12 @@ package ghidra.program.model.data;
import org.junit.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.ByteMemBufferImpl;
import ghidra.util.BigEndianDataConverter;
import ghidra.util.UniversalIdGenerator;
import mockit.Expectations;
import mockit.Mocked;
public class EnumDataTypeTest {
@Mocked
MemBuffer memBuffer;
@Before
public void setUp() {
@ -33,33 +30,27 @@ public class EnumDataTypeTest {
}
@Test
public void testNegativeValue() throws MemoryAccessException {
new Expectations() {
{
memBuffer.getInt(anyInt);
result = 0xffffffff;
}
};
public void testNegativeValue() {
EnumDataType enumDt = new EnumDataType("Test", 4);
enumDt.add("bob", 0xffffffffL);
ByteMemBufferImpl memBuffer = new ByteMemBufferImpl(Address.NO_ADDRESS,
BigEndianDataConverter.INSTANCE.getBytes(-1), true);
Assert.assertEquals("bob", enumDt.getRepresentation(memBuffer, null, 0));
}
@Test
public void testUpperBitLongValue() throws MemoryAccessException {
new Expectations() {
{
memBuffer.getInt(anyInt);
result = 0x80000000;
}
};
public void testUpperBitLongValue() {
EnumDataType enumDt = new EnumDataType("Test", 4);
enumDt.add("bob", 0x80000000L);
ByteMemBufferImpl memBuffer = new ByteMemBufferImpl(Address.NO_ADDRESS,
BigEndianDataConverter.INSTANCE.getBytes(Integer.MIN_VALUE), true);
Assert.assertEquals("bob", enumDt.getRepresentation(memBuffer, null, 0));
}