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.framework.plugintool.PluginTool;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.test.*; import ghidra.test.*;
@ -1264,6 +1265,8 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
assertNotNull("Expected data type: " + dtName, d); assertNotNull("Expected data type: " + dtName, d);
assertTrue("Expected data type: " + dtName, expectedDataType.isInstance(d.getDataType())); assertTrue("Expected data type: " + dtName, expectedDataType.isInstance(d.getDataType()));
DataType dt = d.getDataType();
boolean onByteWordData = true; boolean onByteWordData = true;
boolean onFloatDoubleData = true; boolean onFloatDoubleData = true;
boolean onCharData = true; boolean onCharData = true;
@ -1280,7 +1283,7 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
// || expectedDataType.equals(StringDataType.class) // || expectedDataType.equals(StringDataType.class)
// || expectedDataType.equals(UnicodeDataType.class)); // || expectedDataType.equals(UnicodeDataType.class));
boolean hasSettings = d.getDataType().getSettingsDefinitions().length != 0; boolean hasSettings = dt.getSettingsDefinitions().length != 0;
String caseName = "On " + dtName + " at: " + getCurrentLocation(); String caseName = "On " + dtName + " at: " + getCurrentLocation();
@ -1294,7 +1297,8 @@ public abstract class AbstractDataActionTest extends AbstractGhidraHeadedIntegra
checkAction(actions, CREATE_STRUCTURE, hasSelection, caseName); checkAction(actions, CREATE_STRUCTURE, hasSelection, caseName);
checkAction(actions, EDIT_DATA_TYPE, 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, CREATE_ARRAY, true, caseName);
checkAction(actions, DEFAULT_DATA_SETTINGS, checkAction(actions, DEFAULT_DATA_SETTINGS,
(!hasSelection || isSelectionJustSingleDataInstance(sel, d)) && hasSettings, caseName); (!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(); super();
} }
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
program = createDefaultProgram("Test", ProgramBuilder._TOY, this); program = createDefaultProgram("Test", ProgramBuilder._TOY, this);
dataMgr = program.getDataTypeManager(); dataMgr = program.getDataTypeManager();
transactionID = program.startTransaction("Test"); transactionID = program.startTransaction("Test");
} }
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
program.endTransaction(transactionID, false); program.endTransaction(transactionID, false);
program.release(this); program.release(this);
} }
@Test @Test
public void testCreateEnum() throws Exception { public void testCreateEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0); enumm.add("Red", 0);
enumm.add("Green", 1); enumm.add("Green", 1);
@ -88,29 +88,48 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertNotNull(c.getDataType("Color")); assertNotNull(c.getDataType("Color"));
} }
@Test @Test
public void testRemoveValue() throws Exception { public void testRemoveValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0); enumm.add("Red", 0);
enumm.add("Green", 1); enumm.add("Green", 1);
enumm.add("Blue", 2); enumm.add("Blue", 2);
enumm.add("blue", 2);
Category root = dataMgr.getRootCategory(); Category root = dataMgr.getRootCategory();
Category c = root.createCategory("enumms"); Category c = root.createCategory("enumms");
enumm.setCategoryPath(c.getCategoryPath()); enumm.setCategoryPath(c.getCategoryPath());
Enum enummDT = (Enum) dataMgr.resolve(enumm, null); 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 @Test
public void testAddValue() throws Exception { public void testAddValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 0); enumm.add("Red", 0);
enumm.add("Green", 1); enumm.add("Green", 1);
enumm.add("Blue", 2); 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 root = dataMgr.getRootCategory();
Category c = root.createCategory("enumms"); Category c = root.createCategory("enumms");
@ -119,12 +138,15 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
Enum enummDT = (Enum) dataMgr.resolve(enumm, null); Enum enummDT = (Enum) dataMgr.resolve(enumm, null);
enummDT.add("Purple", 7); enummDT.add("Purple", 7);
assertEquals(4, enummDT.getCount()); assertEquals(5, enummDT.getCount());
assertEquals(7, enummDT.getValue("Purple")); 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 @Test
public void testEditValue() throws Exception { public void testEditValue() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -148,8 +170,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(2, listener.getCount()); assertEquals(2, listener.getCount());
} }
@Test @Test
public void testCloneRetainIdentity() throws Exception { public void testCloneRetainIdentity() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -168,8 +190,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(copyDT.isEquivalent(c2)); assertTrue(copyDT.isEquivalent(c2));
} }
@Test @Test
public void testCopyNoRetainIdentity() throws Exception { public void testCopyNoRetainIdentity() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -188,8 +210,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(copyDT.isEquivalent(c2)); assertTrue(copyDT.isEquivalent(c2));
} }
@Test @Test
public void testRemoveEnum() throws Exception { public void testRemoveEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -208,8 +230,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
} }
@Test @Test
public void testMoveEnum() throws Exception { public void testMoveEnum() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -224,8 +246,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertNull(c.getDataType(enumm.getName())); assertNull(c.getDataType(enumm.getName()));
} }
@Test @Test
public void testResolve() throws Exception { public void testResolve() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -239,8 +261,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(enummDT, dataMgr.getDataType(id)); assertEquals(enummDT, dataMgr.getDataType(id));
} }
@Test @Test
public void testReplace() throws Exception { public void testReplace() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);
@ -275,8 +297,8 @@ public class EnumTest extends AbstractGhidraHeadedIntegrationTest {
} }
} }
@Test @Test
public void testIsEquivalent() throws Exception { public void testIsEquivalent() throws Exception {
Enum enumm = new EnumDataType("Color", 1); Enum enumm = new EnumDataType("Color", 1);
enumm.add("Red", 10); enumm.add("Red", 10);
enumm.add("Green", 15); enumm.add("Green", 15);

View file

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

View file

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

View file

@ -32,7 +32,7 @@ import ghidra.util.exception.DuplicateNameException;
*/ */
public abstract class BuiltIn extends DataTypeImpl implements BuiltInDataType { 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 }; new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private SettingsDefinition[] settingDefs; private SettingsDefinition[] settingDefs;

View file

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

View file

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