diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java
index bf6883701b..80c5663b6d 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java
@@ -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.
@@ -18,6 +18,7 @@ package ghidra.util.datastruct;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
+import java.util.function.Function;
/**
* ObjectClass
provides a fixed-size long-key-based object cache.
@@ -27,12 +28,14 @@ import java.util.*;
*
* The weak cache is keyed, while the hard cache simply maintains the presence of
* an object in the weak cache.
+ *
+ * @param Object type held by cache
*/
-public class ObjectCache {
+public class ObjectCache {
- private Map> hashTable;
- private ReferenceQueue refQueue;
- private LinkedList hardCache;
+ private Map> hashTable;
+ private ReferenceQueue refQueue;
+ private LinkedList hardCache;
private int hardCacheSize;
/**
@@ -61,10 +64,10 @@ public class ObjectCache {
* @param key object key
* @return cached object
*/
- public synchronized Object get(long key) {
- WeakReference> ref = hashTable.get(key);
+ public synchronized T get(long key) {
+ WeakReference ref = hashTable.get(key);
if (ref != null) {
- Object obj = ref.get();
+ T obj = ref.get();
if (obj == null) {
hashTable.remove(key);
}
@@ -74,6 +77,30 @@ public class ObjectCache {
return null;
}
+ /**
+ * Get the current cached object which corresponds to specified {@code key} if contained in
+ * cache, otherwise the {@code mappingFunction} will be invoked to instantiate a new object
+ * where that object will be added to the cache and returned. If the {@code mappingFunction}
+ * returns null nothing will be added to the cache and null will be returned by this method.
+ *
+ * @param key object key
+ * @param mappingFunction function used to obtain a new object if not currently present
+ * in cache.
+ * @return cached object
+ */
+ public synchronized T computeIfAbsent(long key, Function mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+ T oldValue = get(key);
+ if (oldValue != null) {
+ return oldValue;
+ }
+ T newValue = mappingFunction.apply(key);
+ if (newValue != null) {
+ put(key, newValue);
+ }
+ return newValue;
+ }
+
/**
* Return the hard cache size
* @return the hard cache size
@@ -98,9 +125,9 @@ public class ObjectCache {
* @param key object key
* @param obj the object
*/
- public synchronized void put(long key, Object obj) {
+ public synchronized void put(long key, T obj) {
processQueue();
- KeyedSoftReference> ref = new KeyedSoftReference<>(key, obj, refQueue);
+ KeyedSoftReference ref = new KeyedSoftReference<>(key, obj, refQueue);
hashTable.put(key, ref);
addToHardCache(obj);
}
@@ -112,7 +139,7 @@ public class ObjectCache {
*/
public synchronized void remove(long key) {
processQueue();
- KeyedSoftReference> ref = hashTable.get(key);
+ KeyedSoftReference ref = hashTable.get(key);
if (ref != null) {
ref.clear();
hashTable.remove(key);
@@ -123,7 +150,7 @@ public class ObjectCache {
* Add the specified object to the hard cache.
* @param obj object
*/
- private void addToHardCache(Object obj) {
+ private void addToHardCache(T obj) {
hardCache.addLast(obj);
if (hardCache.size() > hardCacheSize) {
hardCache.removeFirst();
@@ -134,8 +161,8 @@ public class ObjectCache {
* Cleanup weak cache
*/
private void processQueue() {
- KeyedSoftReference> ref;
- while ((ref = (KeyedSoftReference>) refQueue.poll()) != null) {
+ KeyedSoftReference extends T> ref;
+ while ((ref = (KeyedSoftReference extends T>) refQueue.poll()) != null) {
hashTable.remove(ref.getKey());
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java
index dd4eeb2e34..5062576c2a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java
@@ -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.
@@ -33,6 +33,7 @@ public abstract class DatabaseObject {
@SuppressWarnings("rawtypes")
private final DBObjectCache cache;
private volatile int invalidateCount;
+ private boolean refreshing = false;
/**
* Constructs a new DatabaseObject and adds it to the specified cache.
@@ -91,6 +92,7 @@ public abstract class DatabaseObject {
/**
* Returns true if object is currently invalid and must be validated prior to further use.
+ * A deleted object will be considered valid since it cannot be refreshed.
* An invalid object may result from a cache invalidation which corresponds to wide-spread
* record changes. A common situation where this can occur is an undo/redo operation
* against the underlying database. The methods {@link #checkIsValid()}, {@link #checkDeleted()},
@@ -128,23 +130,41 @@ public abstract class DatabaseObject {
}
/**
- * Check whether this object is still valid. If the object is invalid, the object will attempt
- * to refresh itself using the specified record. If the refresh fails, the object will be marked
- * as deleted and removed from cache. If this object is already marked as deleted, the record
- * can not be used to refresh the object.
+ * Check whether this object is still valid. If the object is invalid and not deleted, the
+ * object will attempt to refresh itself using the specified record. If the refresh fails,
+ * the object will be marked as deleted and removed from cache. If this object is already
+ * marked as deleted a refresh will not be perormed.
+ *
+ * This method may invoke {@link #refresh(DBRecord)} to perform a refresh. It is important
+ * that such a refresh avoid recursing back into this method.
*
* @param record optional record which may be used to refresh invalid object
- * @return true if the object is valid.
+ * @return true if the object is valid or false if it has been deleted.
*/
protected boolean checkIsValid(DBRecord record) {
if (isInvalid()) {
- setValid();// prevent checkIsValid recursion during refresh
- if (!refresh(record)) {
+ if (refreshing) {
+ // NOTE: We need to correct such recursion cases which should be
+ // avoided since object is not in a valid state until refresh completed.
+ return !deleted;
+ }
+ refreshing = true;
+ try {
+ if (refresh(record)) {
+ // Object is valid
+ setValid();
+ return true;
+ }
+
+ // Object has been deleted
if (cache != null) {
cache.delete(key);
}
setDeleted();
- setInvalid();
+ return false;
+ }
+ finally {
+ refreshing = false;
}
}
return !deleted;
@@ -152,14 +172,15 @@ public abstract class DatabaseObject {
/**
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
- * invalid, then the lock will be used to refresh as needed.
+ * invalid and not deleted, then the lock will be used to refresh as needed. A deleted object
+ * will not be refreshed.
*
* @param lock the lock that will be used if the object needs to be refreshed.
- * @return true if object is valid, else false if deleted
+ * @return true if object is valid or false if deleted
*/
protected boolean validate(Lock lock) {
if (!isInvalid()) {
- return true;
+ return !deleted;
}
lock.acquire();
try {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java
index daaec38a4d..3f5b051788 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java
@@ -3207,8 +3207,8 @@ public class CodeManager implements ErrorHandler, ManagerDB {
lock.acquire();
try {
cache.invalidate();
- lengthMgr.invalidateCache();
- compositeMgr.invalidateCache();
+ lengthMgr.invalidate();
+ compositeMgr.invalidate();
protoMgr.clearCache();
}
finally {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java
index e6f7f1936c..18c62d4d68 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java
@@ -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.
@@ -65,7 +65,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
* @param cacheKey the cache key (dataComponent does not use the address)
* @param address min address of this code unit
* @param addr index for min address
- * @param the length of the codeunit.
+ * @param length the length of the codeunit.
*/
public CodeUnitDB(CodeManager codeMgr, DBObjectCache extends CodeUnitDB> cache, long cacheKey,
Address address, long addr, int length) {
@@ -95,21 +95,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
return !hasBeenDeleted(record);
}
- /**
- * Check this code units validity when the lock/checkIsValid is not used and refresh if necessary.
- */
- protected void refreshIfNeeded() {
- if (isInvalid()) {
- lock.acquire();
- try {
- refresh();
- }
- finally {
- lock.release();
- }
- }
- }
-
/**
* Perform any refresh necessary and determine if this code unit has been deleted.
* If a record has been provided, it may be used to facilitate a refresh without
@@ -122,7 +107,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void addMnemonicReference(Address refAddr, RefType refType, SourceType sourceType) {
- refreshIfNeeded();
+ validate(lock);
refMgr.addMemoryReference(address, refAddr, refType, sourceType, MNEMONIC);
}
@@ -134,13 +119,12 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void addOperandReference(int opIndex, Address refAddr, RefType type,
SourceType sourceType) {
- refreshIfNeeded();
+ validate(lock);
refMgr.addMemoryReference(address, refAddr, type, sourceType, opIndex);
}
@Override
public int compareTo(Address a) {
- refreshIfNeeded();
if (contains(a)) {
return 0;
}
@@ -149,13 +133,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public boolean contains(Address testAddr) {
- refreshIfNeeded();
+ validate(lock);
return address.compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
}
@Override
public String getAddressString(boolean showBlockName, boolean pad) {
- refreshIfNeeded();
+ validate(lock);
Address cuAddress = address;
String addressString = cuAddress.toString(false, pad);
if (showBlockName) {
@@ -231,7 +215,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public ExternalReference getExternalReference(int opIndex) {
- refreshIfNeeded();
+ validate(lock);
Reference[] refs = refMgr.getReferencesFrom(address, opIndex);
for (Reference element : refs) {
if (element.isExternalReference()) {
@@ -249,7 +233,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
throw NoValueException.noValueException;
}
try {
- refreshIfNeeded();
+ validate(lock);
return pm.getInt(address);
}
catch (ConcurrentModificationException e) {
@@ -259,7 +243,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public String getLabel() {
- refreshIfNeeded();
+ validate(lock);
SymbolTable st = codeMgr.getSymbolTable();
Symbol symbol = st.getPrimarySymbol(address);
if (symbol != null) {
@@ -279,7 +263,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public Address getMaxAddress() {
- refreshIfNeeded();
+ validate(lock);
if (endAddr == null) {
endAddr = getLength() == 0 ? address : address.add(getLength() - 1);
}
@@ -288,20 +272,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public Address getMinAddress() {
- refreshIfNeeded();
+ validate(lock);
return address;
}
@Override
public Address getAddress() {
- // TODO: Not sure why this method exists?
- refreshIfNeeded();
+ validate(lock);
return address;
}
@Override
public Reference[] getMnemonicReferences() {
- refreshIfNeeded();
+ validate(lock);
return refMgr.getReferencesFrom(address, MNEMONIC);
}
@@ -311,7 +294,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
ObjectPropertyMap> pm = upm.getObjectPropertyMap(name);
if (pm != null) {
try {
- refreshIfNeeded();
+ validate(lock);
return pm.get(address);
}
catch (ConcurrentModificationException e) {
@@ -322,19 +305,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public Reference[] getOperandReferences(int opIndex) {
- refreshIfNeeded();
+ validate(lock);
return refMgr.getReferencesFrom(address, opIndex);
}
@Override
public Reference getPrimaryReference(int index) {
- refreshIfNeeded();
+ validate(lock);
return refMgr.getPrimaryReferenceFrom(address, index);
}
@Override
public Symbol getPrimarySymbol() {
- refreshIfNeeded();
+ validate(lock);
SymbolTable st = codeMgr.getSymbolTable();
return st.getPrimarySymbol(address);
}
@@ -346,13 +329,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public Reference[] getReferencesFrom() {
- refreshIfNeeded();
+ validate(lock);
return refMgr.getReferencesFrom(address);
}
@Override
public ReferenceIterator getReferenceIteratorTo() {
- refreshIfNeeded();
+ validate(lock);
return program.getReferenceManager().getReferencesTo(address);
}
@@ -362,7 +345,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
StringPropertyMap pm = upm.getStringPropertyMap(name);
if (pm != null) {
try {
- refreshIfNeeded();
+ validate(lock);
return pm.getString(address);
}
catch (ConcurrentModificationException e) {
@@ -373,7 +356,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public Symbol[] getSymbols() {
- refreshIfNeeded();
+ validate(lock);
SymbolTable st = codeMgr.getSymbolTable();
return st.getSymbols(address);
}
@@ -384,7 +367,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
VoidPropertyMap pm = upm.getVoidPropertyMap(name);
if (pm != null) {
try {
- refreshIfNeeded();
+ validate(lock);
return pm.hasProperty(address);
}
catch (ConcurrentModificationException e) {
@@ -399,7 +382,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
PropertyMap> pm = upm.getPropertyMap(name);
if (pm != null) {
try {
- refreshIfNeeded();
+ validate(lock);
return pm.hasProperty(address);
}
catch (ConcurrentModificationException e) {
@@ -424,7 +407,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void removeMnemonicReference(Address refAddr) {
- refreshIfNeeded();
+ validate(lock);
Reference ref = refMgr.getReference(address, refAddr, MNEMONIC);
if (ref != null) {
program.getReferenceManager().delete(ref);
@@ -433,7 +416,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void removeOperandReference(int opIndex, Address refAddr) {
- refreshIfNeeded();
+ validate(lock);
Reference ref = refMgr.getReference(address, refAddr, opIndex);
if (ref != null) {
program.getReferenceManager().delete(ref);
@@ -446,7 +429,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
PropertyMap> pm = upm.getPropertyMap(name);
if (pm != null) {
try {
- refreshIfNeeded();
+ validate(lock);
pm.remove(address);
}
catch (ConcurrentModificationException e) {
@@ -607,7 +590,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void setStackReference(int opIndex, int offset, SourceType sourceType, RefType refType) {
- refreshIfNeeded();
+ validate(lock);
validateOpIndex(opIndex);
refMgr.addStackReference(address, opIndex, offset, refType, sourceType);
}
@@ -615,20 +598,20 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void setRegisterReference(int opIndex, Register reg, SourceType sourceType,
RefType refType) {
- refreshIfNeeded();
+ validate(lock);
validateOpIndex(opIndex);
refMgr.addRegisterReference(address, opIndex, reg, refType, sourceType);
}
@Override
public int getBytes(byte[] b, int offset) {
- refreshIfNeeded();
+ validate(lock);
byte localBytes[] = populateByteArray();
if (offset >= 0 && (offset + b.length) <= localBytes.length) {
System.arraycopy(localBytes, offset, b, 0, b.length);
return b.length;
}
-
+
try {
return program.getMemory().getBytes(address.add(offset), b);
}
@@ -639,15 +622,15 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public byte[] getBytes() throws MemoryAccessException {
- refreshIfNeeded();
+ validate(lock);
byte localBytes[] = populateByteArray();
int locallen = getLength();
- if (localBytes.length >= locallen ) {
+ if (localBytes.length >= locallen) {
byte[] b = new byte[locallen];
System.arraycopy(localBytes, 0, b, 0, b.length);
return b;
}
-
+
int len = getLength();
byte[] b = new byte[len];
@@ -659,7 +642,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public byte getByte(int offset) throws MemoryAccessException {
- refreshIfNeeded();
+ validate(lock);
byte localBytes[] = populateByteArray();
if (offset >= 0 && offset < localBytes.length) {
return localBytes[offset];
@@ -680,31 +663,31 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public BigInteger getValue(Register register, boolean signed) {
- refreshIfNeeded();
+ validate(lock);
return programContext.getValue(register, address, signed);
}
@Override
public RegisterValue getRegisterValue(Register register) {
- refreshIfNeeded();
+ validate(lock);
return programContext.getRegisterValue(register, address);
}
@Override
public void setValue(Register register, BigInteger value) throws ContextChangeException {
- refreshIfNeeded();
+ validate(lock);
programContext.setValue(register, address, address, value);
}
@Override
public void clearRegister(Register register) throws ContextChangeException {
- refreshIfNeeded();
+ validate(lock);
programContext.setValue(register, address, address, null);
}
@Override
public void setRegisterValue(RegisterValue value) throws ContextChangeException {
- refreshIfNeeded();
+ validate(lock);
programContext.setRegisterValue(address, address, value);
}
@@ -725,7 +708,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public boolean hasValue(Register register) {
- refreshIfNeeded();
+ validate(lock);
return programContext.getValue(register, address, false) != null;
}
@@ -818,7 +801,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override
public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException {
- refreshIfNeeded();
+ validate(lock);
byte[] codeUnitBytes = getBytes();
System.arraycopy(codeUnitBytes, 0, buffer, bufferOffset,
Math.min(buffer.length, getLength()));
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java
index e4fa0f0dda..5e02659908 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java
@@ -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.
@@ -197,7 +197,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public void addValueReference(Address refAddr, RefType type) {
- refreshIfNeeded();
+ validate(lock);
refMgr.addMemoryReference(address, refAddr, type, SourceType.USER_DEFINED,
CodeManager.DATA_OP_INDEX);
}
@@ -379,19 +379,19 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public boolean isChangeAllowed(SettingsDefinition settingsDefinition) {
- refreshIfNeeded();
+ validate(lock);
return dataMgr.isChangeAllowed(this, settingsDefinition);
}
@Override
public void clearSetting(String name) {
- refreshIfNeeded();
+ validate(lock);
dataMgr.clearSetting(this, name);
}
@Override
public Long getLong(String name) {
- refreshIfNeeded();
+ validate(lock);
Long value = dataMgr.getLongSettingsValue(this, name);
if (value == null) {
value = getDefaultSettings().getLong(name);
@@ -401,13 +401,13 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public String[] getNames() {
- refreshIfNeeded();
+ validate(lock);
return dataMgr.getInstanceSettingsNames(this);
}
@Override
public String getString(String name) {
- refreshIfNeeded();
+ validate(lock);
String value = dataMgr.getStringSettingsValue(this, name);
if (value == null) {
value = getDefaultSettings().getString(name);
@@ -417,7 +417,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public Object getValue(String name) {
- refreshIfNeeded();
+ validate(lock);
Object value = dataMgr.getSettings(this, name);
if (value == null) {
value = getDefaultSettings().getValue(name);
@@ -427,19 +427,19 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public void setLong(String name, long value) {
- refreshIfNeeded();
+ validate(lock);
dataMgr.setLongSettingsValue(this, name, value);
}
@Override
public void setString(String name, String value) {
- refreshIfNeeded();
+ validate(lock);
dataMgr.setStringSettingsValue(this, name, value);
}
@Override
public void setValue(String name, Object value) {
- refreshIfNeeded();
+ validate(lock);
dataMgr.setSettings(this, name, value);
}
@@ -650,7 +650,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public String getPathName() {
- refreshIfNeeded();
+ validate(lock);
Address cuAddress = address;
SymbolTable st = program.getSymbolTable();
Symbol symbol = st.getPrimarySymbol(cuAddress);
@@ -763,13 +763,13 @@ class DataDB extends CodeUnitDB implements Data {
@Override
public void clearAllSettings() {
- refreshIfNeeded();
+ validate(lock);
dataMgr.clearAllSettings(this);
}
@Override
public boolean isEmpty() {
- refreshIfNeeded();
+ validate(lock);
return dataMgr.isEmptySetting(this);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java
index 973447512c..d0886c8e00 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java
@@ -54,9 +54,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
private FlowOverride flowOverride;
private int lengthOverride;
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
+ private final static Object[] EMPTY_OBJ_ARRAY = new Object[0];
private volatile boolean clearingFallThroughs = false;
- private ParserContext parserContext;
+ private ParserContext parserContext; // uses lazy initialization
+ private String mnemonicString; // uses lazy initialization
/**
* Construct a new InstructionDB.
@@ -80,6 +82,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
protected boolean refresh(DBRecord record) {
parserContext = null;
+ mnemonicString = null;
return super.refresh(record);
}
@@ -240,12 +243,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return null;
}
- if (this.isInDelaySlot()) {
+ if (isInDelaySlot()) {
// If this instruction is within delay-slot, return a null fall-from address if
// previous instruction (i.e., instruction with delay slot, found above)
// does not have a fallthrough and this instruction has a ref or label on it.
if (!instr.hasFallthrough() &&
- program.getSymbolTable().hasSymbol(this.getMinAddress())) {
+ program.getSymbolTable().hasSymbol(getMinAddress())) {
return null;
}
// Return previous instruction's address (i.e., instruction with delay slot, found above)
@@ -276,22 +279,15 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Address getFallThrough() {
- lock.acquire();
- try {
- checkIsValid();
- if (isFallThroughOverridden()) {
- return getFallThroughReference();
- }
- return getDefaultFallThrough();
- }
- finally {
- lock.release();
+ if (isFallThroughOverridden()) {
+ return getFallThroughReference();
}
+ return getDefaultFallThrough();
}
@Override
public Address[] getFlows() {
- refreshIfNeeded();
+ validate(lock);
Reference[] refs = refMgr.getFlowReferencesFrom(address);
if (refs.length == 0) {
return EMPTY_ADDR_ARRAY;
@@ -313,6 +309,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Address[] getDefaultFlows() {
+ validate(lock);
Address[] flows = proto.getFlows(this);
if (flowOverride == FlowOverride.RETURN && flows.length == 1) {
return EMPTY_ADDR_ARRAY;
@@ -322,17 +319,23 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public FlowType getFlowType() {
+ validate(lock);
return FlowOverride.getModifiedFlowType(proto.getFlowType(this), flowOverride);
}
@Override
public Instruction getNext() {
- refreshIfNeeded();
+ validate(lock);
return codeMgr.getInstructionAfter(address);
}
@Override
public RefType getOperandRefType(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return null;
+ }
+
// always reflects current flowOverride
lock.acquire();
try {
@@ -346,6 +349,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public String getSeparator(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return null;
+ }
+
lock.acquire();
try {
return proto.getSeparator(opIndex, this);
@@ -424,12 +432,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Object[] getOpObjects(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return EMPTY_OBJ_ARRAY;
+ }
+
lock.acquire();
try {
checkIsValid();
- if (opIndex < 0 || opIndex >= getNumOperands()) {
- return new Object[0];
- }
return proto.getOpObjects(opIndex, this);
}
finally {
@@ -439,7 +449,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Instruction getPrevious() {
- refreshIfNeeded();
+ validate(lock);
return codeMgr.getInstructionBefore(address);
}
@@ -450,12 +460,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Register getRegister(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return null;
+ }
+
lock.acquire();
try {
checkIsValid();
- if (opIndex < 0) {
- return null;
- }
return proto.getRegister(opIndex, this);
}
finally {
@@ -494,6 +506,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Address getAddress(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return null;
+ }
+
lock.acquire();
try {
checkIsValid();
@@ -501,9 +518,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (ref != null) {
return ref.getToAddress();
}
- if (opIndex < 0) {
- return null;
- }
+
int opType = proto.getOpType(opIndex, this);
if (OperandType.isAddress(opType)) {
@@ -550,10 +565,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public String getMnemonicString() {
+ validate(lock);
+ String curMnemonic = mnemonicString;
+ if (curMnemonic != null) {
+ return curMnemonic;
+ }
lock.acquire();
try {
checkIsValid();
- return proto.getMnemonic(this);
+ mnemonicString = proto.getMnemonic(this);
+ return mnemonicString;
}
finally {
lock.release();
@@ -567,12 +588,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Scalar getScalar(int opIndex) {
+
+ if (opIndex < 0 || opIndex >= getNumOperands()) {
+ return null;
+ }
+
lock.acquire();
try {
checkIsValid();
- if (opIndex < 0) {
- return null;
- }
return proto.getScalar(opIndex, this);
}
finally {
@@ -693,6 +716,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public boolean isFallThroughOverridden() {
+ validate(lock);
return (flags & FALLTHROUGH_SET_MASK) != 0;
}
@@ -705,7 +729,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (clearingFallThroughs) {
return;
}
- refreshIfNeeded();
+ validate(lock);
clearingFallThroughs = true;
try {
boolean fallThroughPreserved = false;
@@ -887,14 +911,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
Address defaultFallThrough = getDefaultFallThrough();
if (defaultFallThrough != null) {
refMgr.addMemoryReference(address, defaultFallThrough, RefType.FALL_THROUGH,
- SourceType.USER_DEFINED, Reference.MNEMONIC);
+ SourceType.USER_DEFINED, Reference.MNEMONIC);
}
}
}
@Override
public boolean isLengthOverridden() {
- refreshIfNeeded();
+ validate(lock);
return lengthOverride != 0;
}
@@ -911,30 +935,26 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public Address getDefaultFallThrough() {
- lock.acquire();
- try {
- // TODO: This used to be in proto. We need to override the proto's flowtype.
- // This could be pushed back into the proto if we could override the flowType there.
- FlowType myFlowType = getFlowType();
- if (myFlowType.hasFallthrough()) {
- try {
- return getAddress().addNoWrap(proto.getFallThroughOffset(this));
- }
- catch (AddressOverflowException e) {
- // ignore
- }
+ FlowType myFlowType = getFlowType(); // getFlowType will validate
+ if (myFlowType.hasFallthrough()) {
+ try {
+ return address.addNoWrap(getDefaultFallThroughOffset());
+ }
+ catch (AddressOverflowException e) {
+ // ignore
}
- return null;
- }
- finally {
- lock.release();
}
+ return null;
}
@Override
public int getDefaultFallThroughOffset() {
+ if (proto.getDelaySlotByteCount() <= 0) {
+ return getLength();
+ }
lock.acquire();
try {
+ checkIsValid();
return proto.getFallThroughOffset(this);
}
finally {
@@ -944,17 +964,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public boolean hasFallthrough() {
- lock.acquire();
- try {
- checkIsValid();
- if (isFallThroughOverridden()) {
- return getFallThrough() != null; // fall-through destination stored as reference
- }
- return getFlowType().hasFallthrough();
- }
- finally {
- lock.release();
+ if (isFallThroughOverridden()) {
+ return getFallThrough() != null; // fall-through destination stored as reference
}
+ return getFlowType().hasFallthrough();
}
@Override
@@ -977,6 +990,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override
public ParserContext getParserContext() throws MemoryAccessException {
+ // NOTE: It is assumed this is invoked and used within a locked block
if (parserContext == null) {
parserContext = proto.getParserContext(this, this);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java
index 9b7ca2151b..0f95035c91 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java
@@ -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.
@@ -34,9 +34,10 @@ import ghidra.util.exception.DuplicateNameException;
*/
class ArrayDB extends DataTypeDB implements Array {
- private volatile String displayName;
private ArrayDBAdapter adapter;
- private int elementLength; // lazy initialization
+
+ private int elementLength = -1; // lazy initialization
+ private String displayName; // lazy initialization
/**
* Constructor
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java
index 1ed67894c6..c5c880313c 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java
@@ -56,11 +56,18 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
this.dataMgr = dataMgr;
this.record = record;
this.lock = dataMgr.lock;
- refreshName();
}
+ /**
+ * Clears the current name so that the next invocation of {@link #getName()} will
+ * force its update via {@link #doGetName()}. It is important that {@link #doGetName()}
+ * does not get invoked during a {@link #refresh()} to avoid problematic recursion during the
+ * refresh caused by recursive use of {@link #checkIsValid()} on the same object.
+ *
+ * NOTE: This must only be invoked while in a locked-state
+ */
protected void refreshName() {
- name = doGetName();
+ name = null;
}
/**
@@ -144,8 +151,21 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
@Override
public final String getName() {
- validate(lock);
- return name;
+ String n = name;
+ if (n != null && !isInvalid()) {
+ return n;
+ }
+ lock.acquire();
+ try {
+ checkIsValid();
+ if (name == null) {
+ name = doGetName();
+ }
+ return name;
+ }
+ finally {
+ lock.release();
+ }
}
@Override
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
index c8ff6beaf9..de02ba55a9 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java
@@ -46,6 +46,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
import ghidra.program.model.data.Enum;
import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.Function;
import ghidra.util.*;
import ghidra.util.classfinder.ClassTranslator;
import ghidra.util.datastruct.FixedSizeHashMap;
@@ -4129,6 +4130,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
* @return calling convention name if found else unknown
*/
public String getCallingConventionName(byte id) {
+ if (id == DEFAULT_CALLING_CONVENTION_ID) {
+ return Function.DEFAULT_CALLING_CONVENTION_STRING;
+ }
+ else if (id == UNKNOWN_CALLING_CONVENTION_ID) {
+ return CompilerSpec.CALLING_CONVENTION_unknown;
+ }
lock.acquire();
try {
String callingConvention = callingConventionAdapter.getCallingConventionName(id);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java
index 3b40e07df3..ab52133756 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java
@@ -46,11 +46,12 @@ class EnumDB extends DataTypeDB implements Enum {
private EnumDBAdapter adapter;
private EnumValueDBAdapter valueAdapter;
- private Map nameMap; // name to value
- private SortedMap> valueMap; // value to names
- private Map commentMap; // name to comment
- private List bitGroups;
- private EnumSignedState signedState = null;
+ // Lazy fields whose initialization is triggered by nameMap=null (see initializeIfNeeded)
+ private Map nameMap; // lazy initialization, name to value
+ private SortedMap> valueMap; // lazy initialization, value to names
+ private Map commentMap; // lazy initialization, name to comment
+ private List bitGroups; // lazy initialization
+ private EnumSignedState signedState = null; // lazy initialization
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache cache, EnumDBAdapter adapter,
EnumValueDBAdapter valueAdapter, DBRecord record) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java
index 21aa3c398c..fe3240f72d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java
@@ -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 @@ class PointerDB extends DataTypeDB implements Pointer {
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private PointerDBAdapter adapter;
- private String displayName;
+
+ private String displayName; // lazy initialization
/**
* isEquivalentActive
is used to break cyclical recursion when
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
index 531adf473a..32b2579f1d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java
@@ -37,7 +37,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
private int structLength;
private int structAlignment; // reflects stored alignment, -1 if not yet stored
- private int computedAlignment = -1; // cached alignment if not yet stored
+ private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed
private int numComponents; // If packed, this does not include the undefined components.
private List components;
@@ -73,7 +73,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL);
structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL);
computedAlignment = -1;
- numComponents = isPackingEnabled() ? components.size()
+
+ boolean packingDisabled =
+ record.getIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL) < DEFAULT_PACKING;
+
+ numComponents = !packingDisabled ? components.size()
: record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL);
if (oldFlexArrayRecord != null) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java
index de955d5a9f..7ff5430054 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java
@@ -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.
@@ -35,7 +35,8 @@ import ghidra.util.exception.DuplicateNameException;
class TypedefDB extends DataTypeDB implements TypeDef {
private TypedefDBAdapter adapter;
- private SettingsDefinition[] settingsDef;
+
+ private SettingsDefinition[] settingsDef; // lazy initialization
/**
* Construct TypeDefDB instance
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java
index eef1b30cae..a9d9d2f740 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java
@@ -33,7 +33,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
private int unionLength;
private int unionAlignment; // reflects stored alignment, -1 if not yet stored
- private int computedAlignment = -1; // cached alignment if not yet stored
+ private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed
private List components;
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java
index 21bbe7ff53..740bb7d1cb 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java
@@ -120,33 +120,22 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public boolean isThunk() {
- manager.lock.acquire();
- try {
- checkIsValid();
- return thunkedFunction != null;
- }
- finally {
- manager.lock.release();
- }
+ validate(manager.lock);
+ return thunkedFunction != null;
}
@Override
public Function getThunkedFunction(boolean recursive) {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (!recursive || thunkedFunction == null) {
- return thunkedFunction;
- }
- FunctionDB endFunction = thunkedFunction;
- while (endFunction.thunkedFunction != null) {
- endFunction = endFunction.thunkedFunction;
- }
- return endFunction;
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (!recursive || localThunkFunc == null) {
+ return localThunkFunc;
}
- finally {
- manager.lock.release();
+ FunctionDB endFunction = localThunkFunc;
+ while ((localThunkFunc = endFunction.thunkedFunction) != null) {
+ endFunction = localThunkFunc;
}
+ return endFunction;
}
@Override
@@ -332,14 +321,8 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public Address getEntryPoint() {
- manager.lock.acquire();
- try {
- checkIsValid();
- return entryPoint;
- }
- finally {
- manager.lock.release();
- }
+ validate(manager.lock);
+ return entryPoint;
}
@Override
@@ -378,12 +361,18 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public ReturnParameterDB getReturn() {
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.getReturn();
+ }
+ ReturnParameterDB rp = returnParam;
+ if (rp != null) {
+ return rp;
+ }
manager.lock.acquire();
try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.getReturn();
- }
+ // NOTE: lock required to avoid returning null returnParam
loadVariables();
return returnParam;
}
@@ -634,32 +623,22 @@ public class FunctionDB extends DatabaseObject implements Function {
*/
@Override
public StackFrame getStackFrame() {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.frame;
- }
- return frame;
- }
- finally {
- manager.lock.release();
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return thunkedFunction.getStackFrame();
}
+ return frame;
}
@Override
public int getStackPurgeSize() {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.getStackPurgeSize();
- }
- return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL);
- }
- finally {
- manager.lock.release();
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.getStackPurgeSize();
}
+ return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL);
}
@Override
@@ -693,20 +672,12 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public boolean isStackPurgeSizeValid() {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.isStackPurgeSizeValid();
- }
- if (getStackPurgeSize() > 0xffffff) {
- return false;
- }
- return true;
- }
- finally {
- manager.lock.release();
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.isStackPurgeSizeValid();
}
+ return getStackPurgeSize() <= 0xffffff;
}
@Override
@@ -2417,18 +2388,13 @@ public class FunctionDB extends DatabaseObject implements Function {
* @return true if the indicated flag is set
*/
private boolean isFunctionFlagSet(byte functionFlagIndicator) {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.isFunctionFlagSet(functionFlagIndicator);
- }
- byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL);
- return ((flags & functionFlagIndicator) != 0);
- }
- finally {
- manager.lock.release();
+ validate(manager.lock);
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.isFunctionFlagSet(functionFlagIndicator);
}
+ byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL);
+ return ((flags & functionFlagIndicator) != 0);
}
/**
@@ -2529,29 +2495,20 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public String getCallingConventionName() {
- manager.lock.acquire();
- try {
- if (!checkIsValid()) {
- return null;
- }
- if (thunkedFunction != null) {
- return thunkedFunction.getCallingConventionName();
- }
- byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL);
- if (callingConventionID == DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID) {
- return Function.UNKNOWN_CALLING_CONVENTION_STRING;
- }
- if (callingConventionID == DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID) {
- return Function.DEFAULT_CALLING_CONVENTION_STRING;
- }
- return program.getDataTypeManager().getCallingConventionName(callingConventionID);
+ if (!validate(manager.lock)) {
+ return UNKNOWN_CALLING_CONVENTION_STRING;
}
- finally {
- manager.lock.release();
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.getCallingConventionName();
}
+ byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL);
+ // NOTE: If ID is invalid unknown calling convention name will be returned
+ return program.getDataTypeManager().getCallingConventionName(callingConventionID);
}
private String getRealCallingConventionName() {
+ // NOTE: Method only invoked from locked-block
if (thunkedFunction != null) {
return thunkedFunction.getRealCallingConventionName();
}
@@ -2651,21 +2608,18 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override
public String getCallFixup() {
- manager.lock.acquire();
- try {
- checkIsValid();
- if (thunkedFunction != null) {
- return thunkedFunction.getCallFixup();
- }
- StringPropertyMap callFixupMap = manager.getCallFixupMap(false);
- if (callFixupMap == null) {
- return null;
- }
- return callFixupMap.getString(entryPoint);
+ if (!validate(manager.lock)) {
+ return null;
}
- finally {
- manager.lock.release();
+ FunctionDB localThunkFunc = thunkedFunction;
+ if (localThunkFunc != null) {
+ return localThunkFunc.getCallFixup();
}
+ StringPropertyMap callFixupMap = manager.getCallFixupMap(false);
+ if (callFixupMap == null) {
+ return null;
+ }
+ return callFixupMap.getString(entryPoint);
}
@Override
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java
index 8e18df055d..ee1ef8ffae 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java
@@ -81,12 +81,6 @@ public class FunctionManagerDB implements FunctionManager {
return false;
};
- /**
- * TODO: use of StringPropertyMap for callFixupMap lacks the ability to listen for changes
- * which may be made to the map directly (e.g., diff/merge)
- */
- private StringPropertyMap callFixupMap;
-
private long lastFuncID = -1;
Lock lock;
@@ -825,7 +819,6 @@ public class FunctionManagerDB implements FunctionManager {
lock.acquire();
try {
functionTagManager.invalidateCache();
- callFixupMap = null;
lastFuncID = -1;
cache.invalidate();
}
@@ -835,11 +828,8 @@ public class FunctionManagerDB implements FunctionManager {
}
StringPropertyMap getCallFixupMap(boolean create) {
- if (callFixupMap != null) {
- return callFixupMap;
- }
PropertyMapManager usrPropertyManager = program.getUsrPropertyManager();
- callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP);
+ StringPropertyMap callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP);
if (callFixupMap == null && create) {
try {
callFixupMap = usrPropertyManager.createStringPropertyMap(CALLFIXUP_MAP);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java
index 80e8552abb..dce0a9cc31 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java
@@ -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.
@@ -19,8 +19,8 @@
package ghidra.program.database.properties;
import java.io.IOException;
-import java.util.Iterator;
-import java.util.TreeMap;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import db.*;
import ghidra.framework.data.OpenMode;
@@ -49,7 +49,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
private AddressMap addrMap;
private ChangeManager changeMgr;
private PropertiesDBAdapter propertiesDBAdapter;
- private TreeMap> propertyMapCache;
+ private ConcurrentHashMap> propertyMapCache;
private Lock lock;
static final int CURRENT_PROPERTIES_TABLE_VERSION = 0;
@@ -70,11 +70,9 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
static final Schema PROPERTIES_SCHEMA;
static {
-
PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.INSTANCE,
"Name", new Field[] { ByteField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE },
new String[] { "Type", "Object Class", "Version" });
-
}
/**
@@ -100,7 +98,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
dbHandle.createTable(PROPERTIES_TABLE_NAME, PROPERTIES_SCHEMA);
}
findAdapters(handle);
- propertyMapCache = new TreeMap>();
+ propertyMapCache = new ConcurrentHashMap>();
loadPropertyMaps(openMode, monitor);
}
@@ -119,7 +117,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public void invalidateCache(boolean all) throws IOException {
lock.acquire();
try {
- propertyMapCache.clear();
+ for (PropertyMapDB> map : propertyMapCache.values()) {
+ map.invalidate();
+ }
+ // NOTE: Reconciling maps immediately allows use to reliably use cache without
+ // requiring map re-validation.
loadPropertyMaps(null, TaskMonitor.DUMMY);
}
catch (CancelledException e) {
@@ -144,12 +146,23 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
throws VersionException, CancelledException {
try {
VersionException ve = null;
+ Set oldMapNames = new HashSet<>(propertyMapCache.keySet());
RecordIterator iter = propertiesDBAdapter.getRecords();
while (iter.hasNext()) {
DBRecord rec = iter.next();
- String name = rec.getKeyField().getString();
byte propertyType = rec.getByteValue(PROPERTY_TYPE_COL);
- PropertyMapDB> pm = null;
+ String name = rec.getKeyField().getString();
+ oldMapNames.remove(name);
+
+ // Check for pre-existing map
+ PropertyMapDB> pm = propertyMapCache.get(name);
+ if (pm != null) {
+ if (pm.validate(lock)) {
+ continue; // keep the map we already have
+ }
+ propertyMapCache.remove(name);
+ }
+
try {
switch (propertyType) {
case INT_PROPERTY_TYPE:
@@ -208,6 +221,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
}
propertyMapCache.put(name, pm);
}
+
+ // Remove obsolete maps from cache
+ for (String obsoleteMapName : oldMapNames) {
+ propertyMapCache.remove(obsoleteMapName);
+ }
+
if (ve != null) {
throw ve;
}
@@ -333,7 +352,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally {
lock.release();
}
-
}
@Override
@@ -367,7 +385,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally {
lock.release();
}
-
}
/**
@@ -406,7 +423,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally {
lock.release();
}
-
}
/**
@@ -416,14 +432,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public PropertyMap> getPropertyMap(String propertyName) {
- lock.acquire();
- try {
- return propertyMapCache.get(propertyName);
- }
- finally {
- lock.release();
- }
-
+ return propertyMapCache.get(propertyName);
}
/**
@@ -434,19 +443,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public IntPropertyMap getIntPropertyMap(String propertyName) {
-
- lock.acquire();
- try {
- PropertyMapDB> pm = propertyMapCache.get(propertyName);
- if (pm == null || pm instanceof IntPropertyMap) {
- return (IntPropertyMap) pm;
- }
- throw new TypeMismatchException("Property " + propertyName + " is not int type");
+ PropertyMapDB> pm = propertyMapCache.get(propertyName);
+ if (pm == null || pm instanceof IntPropertyMap) {
+ return (IntPropertyMap) pm;
}
- finally {
- lock.release();
- }
-
+ throw new TypeMismatchException("Property " + propertyName + " is not int type");
}
/**
@@ -457,18 +458,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public LongPropertyMap getLongPropertyMap(String propertyName) {
- lock.acquire();
- try {
- PropertyMapDB> pm = propertyMapCache.get(propertyName);
- if (pm == null || pm instanceof LongPropertyMap) {
- return (LongPropertyMap) pm;
- }
- throw new TypeMismatchException("Property " + propertyName + " is not long type");
+ PropertyMapDB> pm = propertyMapCache.get(propertyName);
+ if (pm == null || pm instanceof LongPropertyMap) {
+ return (LongPropertyMap) pm;
}
- finally {
- lock.release();
- }
-
+ throw new TypeMismatchException("Property " + propertyName + " is not long type");
}
/**
@@ -479,18 +473,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public StringPropertyMap getStringPropertyMap(String propertyName) {
- lock.acquire();
- try {
- PropertyMapDB> pm = propertyMapCache.get(propertyName);
- if (pm == null || pm instanceof StringPropertyMap) {
- return (StringPropertyMap) pm;
- }
- throw new TypeMismatchException("Property " + propertyName + " is not String type");
-
- }
- finally {
- lock.release();
+ PropertyMapDB> pm = propertyMapCache.get(propertyName);
+ if (pm == null || pm instanceof StringPropertyMap) {
+ return (StringPropertyMap) pm;
}
+ throw new TypeMismatchException("Property " + propertyName + " is not String type");
}
/**
@@ -501,18 +488,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public ObjectPropertyMap> getObjectPropertyMap(String propertyName) {
- lock.acquire();
- try {
- PropertyMapDB> pm = propertyMapCache.get(propertyName);
- if (pm == null || pm instanceof ObjectPropertyMap) {
- return (ObjectPropertyMap>) pm;
- }
- throw new TypeMismatchException("Property " + propertyName + " is not object type");
-
- }
- finally {
- lock.release();
+ PropertyMapDB> pm = propertyMapCache.get(propertyName);
+ if (pm == null || pm instanceof ObjectPropertyMap) {
+ return (ObjectPropertyMap>) pm;
}
+ throw new TypeMismatchException("Property " + propertyName + " is not object type");
}
/**
@@ -523,17 +503,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/
@Override
public VoidPropertyMap getVoidPropertyMap(String propertyName) {
- lock.acquire();
- try {
- PropertyMapDB> pm = propertyMapCache.get(propertyName);
- if (pm == null || pm instanceof VoidPropertyMap) {
- return (VoidPropertyMap) pm;
- }
- throw new TypeMismatchException("Property " + propertyName + " is not Void type");
- }
- finally {
- lock.release();
+ PropertyMapDB> pm = propertyMapCache.get(propertyName);
+ if (pm == null || pm instanceof VoidPropertyMap) {
+ return (VoidPropertyMap) pm;
}
+ throw new TypeMismatchException("Property " + propertyName + " is not Void type");
}
@Override
@@ -564,12 +538,15 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public Iterator propertyManagers() {
lock.acquire();
try {
- return propertyMapCache.keySet().iterator();
+ // NOTE: infrequent use expected
+ return propertyMapCache.keySet()
+ .stream()
+ .sorted() // Sort keys in natural order
+ .iterator();
}
finally {
lock.release();
}
-
}
@Override
@@ -579,7 +556,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
for (PropertyMapDB> pm : propertyMapCache.values()) {
pm.remove(addr);
}
-
}
finally {
lock.release();
@@ -621,7 +597,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException {
removeAll(startAddr, endAddr, monitor);
-
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java
index 7510e89cb6..9bf86e1e5a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java
@@ -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.
@@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
+import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor;
*/
public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap {
+ /**
+ * A single non-capturing lambda record reader function is used to avoid the possibility of
+ * multiple synthetic class instantiations.
+ */
+ private Function valueReader = addrKey -> {
+ Table table = propertyTable;
+ DBRecord rec = null;
+ try {
+ if (table != null) {
+ rec = table.getRecord(addrKey);
+ }
+ }
+ catch (IOException e) {
+ errHandler.dbError(e);
+ }
+ return rec != null ? rec.getIntValue(PROPERTY_VALUE_COL) : null;
+ };
+
/**
* Construct a integer property map.
* @param dbHandle database handle.
@@ -58,27 +77,22 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPrope
public void add(Address addr, int value) {
lock.acquire();
try {
+ checkDeleted();
Integer oldValue = null;
-
- long key = addrMap.getKey(addr, true);
-
+ long addrKey = addrMap.getKey(addr, true);
if (propertyTable == null) {
createTable(IntField.INSTANCE);
}
else {
- oldValue = (Integer) cache.get(key);
+ oldValue = cache.get(addrKey);
if (oldValue == null) {
- DBRecord rec = propertyTable.getRecord(key);
- if (rec != null) {
- oldValue = rec.getIntValue(PROPERTY_VALUE_COL);
- }
+ oldValue = valueReader.apply(addrKey);
}
}
- DBRecord rec = schema.createRecord(key);
-
+ DBRecord rec = schema.createRecord(addrKey);
rec.setIntValue(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec);
- cache.put(key, value);
+ cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value);
}
@@ -92,44 +106,25 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPrope
@Override
public int getInt(Address addr) throws NoValueException {
- if (propertyTable == null) {
+ Integer value = get(addr);
+ if (value == null) {
throw NO_VALUE_EXCEPTION;
}
-
- lock.acquire();
- try {
- long key = addrMap.getKey(addr, false);
- if (key == AddressMap.INVALID_ADDRESS_KEY) {
- return 0;
- }
- Object obj = cache.get(key);
- if (obj != null) {
- return ((Integer) obj).intValue();
- }
-
- DBRecord rec = propertyTable.getRecord(key);
- if (rec == null) {
- throw NO_VALUE_EXCEPTION;
- }
- return rec.getIntValue(PROPERTY_VALUE_COL);
- }
- catch (IOException e) {
- errHandler.dbError(e);
- }
- finally {
- lock.release();
- }
- return 0;
+ return value;
}
@Override
public Integer get(Address addr) {
- try {
- return getInt(addr);
- }
- catch (NoValueException e) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
+ long addrKey = addrMap.getKey(addr, false);
+ if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
+ return null;
+ }
+ return cache.computeIfAbsent(addrKey, valueReader);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java
index d6e7a7f75d..50882cbff0 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java
@@ -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.
@@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
+import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor;
*/
public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap {
+ /**
+ * A single record reader function is used to avoid the use of a lambda form which could
+ * cause multiple synthetic class instantiations.
+ */
+ private Function valueReader = addrKey -> {
+ Table table = propertyTable;
+ DBRecord rec = null;
+ try {
+ if (table != null) {
+ rec = table.getRecord(addrKey);
+ }
+ }
+ catch (IOException e) {
+ errHandler.dbError(e);
+ }
+ return rec != null ? rec.getLongValue(PROPERTY_VALUE_COL) : null;
+ };
+
/**
* Construct a long property map.
* @param dbHandle database handle.
@@ -56,26 +75,24 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongProper
@Override
public void add(Address addr, long value) {
- Long oldValue = null;
lock.acquire();
try {
- long key = addrMap.getKey(addr, true);
+ checkDeleted();
+ Long oldValue = null;
+ long addrKey = addrMap.getKey(addr, true);
if (propertyTable == null) {
createTable(LongField.INSTANCE);
}
else {
- oldValue = (Long) cache.get(key);
+ oldValue = cache.get(addrKey);
if (oldValue == null) {
- DBRecord rec = propertyTable.getRecord(key);
- if (rec != null) {
- oldValue = rec.getLongValue(PROPERTY_VALUE_COL);
- }
+ oldValue = valueReader.apply(addrKey);
}
}
- DBRecord rec = schema.createRecord(key);
+ DBRecord rec = schema.createRecord(addrKey);
rec.setLongValue(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec);
- cache.put(key, value);
+ cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value);
}
catch (IOException e) {
@@ -89,44 +106,25 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongProper
@Override
public long getLong(Address addr) throws NoValueException {
- if (propertyTable == null) {
+ Long value = get(addr);
+ if (value == null) {
throw NO_VALUE_EXCEPTION;
}
-
- lock.acquire();
- try {
- long key = addrMap.getKey(addr, false);
- if (key == AddressMap.INVALID_ADDRESS_KEY) {
- return 0;
- }
- Object obj = cache.get(key);
- if (obj != null) {
- return ((Long) obj).longValue();
- }
-
- DBRecord rec = propertyTable.getRecord(key);
- if (rec == null) {
- throw NO_VALUE_EXCEPTION;
- }
- return rec.getLongValue(PROPERTY_VALUE_COL);
- }
- catch (IOException e) {
- errHandler.dbError(e);
- }
- finally {
- lock.release();
- }
- return 0;
+ return value;
}
@Override
public Long get(Address addr) {
- try {
- return getLong(addr);
- }
- catch (NoValueException e) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
+ long addrKey = addrMap.getKey(addr, false);
+ if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
+ return null;
+ }
+ return cache.computeIfAbsent(addrKey, valueReader);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
index 36f8e0500b..24bf94bbd9 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java
@@ -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.
@@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
+import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@@ -42,6 +43,44 @@ public class ObjectPropertyMapDB extends PropertyMapDB
private int saveableObjectVersion;
private boolean supportsPrivate;
+ /**
+ * A single non-capturing lambda record reader function is used to avoid the possibility of
+ * multiple synthetic class instantiations.
+ */
+ @SuppressWarnings("unchecked")
+ private Function valueReader = addrKey -> {
+ Table table = propertyTable;
+ DBRecord rec = null;
+ try {
+ if (table != null) {
+ rec = table.getRecord(addrKey);
+ }
+ if (rec == null) {
+ return null;
+ }
+ ObjectStorageAdapterDB objStorage = new ObjectStorageAdapterDB(rec);
+ if (saveableObjectClass == GenericSaveable.class) {
+ return (T) new GenericSaveable(rec, propertyTable.getSchema());
+ }
+ T obj = saveableObjectClass.getDeclaredConstructor().newInstance();
+ obj.restore(objStorage);
+ return obj;
+ }
+ catch (IOException e) {
+ errHandler.dbError(e);
+ }
+ catch (RuntimeException e) {
+ throw e;
+ }
+ catch (InstantiationException e) {
+ errHandler.dbError(new IOException("Could not instantiate " + e.getMessage()));
+ }
+ catch (Exception e) {
+ errHandler.dbError(new IOException(e.getMessage()));
+ }
+ return null;
+ };
+
/**
* Construct an Saveable object property map.
* @param dbHandle database handle.
@@ -256,11 +295,19 @@ public class ObjectPropertyMapDB extends PropertyMapDB
public void add(Address addr, T value) {
lock.acquire();
try {
+ checkDeleted();
if (!saveableObjectClass.isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("value is not " + saveableObjectClass.getName());
}
- long key = addrMap.getKey(addr, true);
- T oldValue = get(addr);
+ long addrKey = addrMap.getKey(addr, true);
+
+ T oldValue = null;
+ if (propertyTable != null) {
+ oldValue = cache.get(addrKey);
+ if (oldValue == null) {
+ oldValue = valueReader.apply(addrKey);
+ }
+ }
String tableName = getTableName();
Schema s;
@@ -271,7 +318,7 @@ public class ObjectPropertyMapDB extends PropertyMapDB
s = objStorage.getSchema(value.getSchemaVersion());
checkSchema(s);
createPropertyTable(tableName, s);
- rec = schema.createRecord(key);
+ rec = schema.createRecord(addrKey);
objStorage.save(rec);
}
else { // GenericSaveable
@@ -281,11 +328,11 @@ public class ObjectPropertyMapDB extends PropertyMapDB
checkSchema(s);
createPropertyTable(tableName, s);
rec = originalRec.copy();
- rec.setKey(key);
+ rec.setKey(addrKey);
}
propertyTable.putRecord(rec);
- cache.put(key, value);
+ cache.put(addrKey, value);
if (!isPrivate(value)) {
changeMgr.setPropertyChanged(name, addr, oldValue, value);
@@ -326,57 +373,18 @@ public class ObjectPropertyMapDB extends PropertyMapDB
return saveableObjectClass;
}
- @SuppressWarnings("unchecked")
@Override
public T get(Address addr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
-
- T obj = null;
-
- lock.acquire();
- try {
- long key = addrMap.getKey(addr, false);
- if (key == AddressMap.INVALID_ADDRESS_KEY) {
- return null;
- }
- obj = (T) cache.get(key);
- if (obj != null) {
- return obj;
- }
-
- DBRecord rec = propertyTable.getRecord(key);
- if (rec == null) {
- return null;
- }
- ObjectStorageAdapterDB objStorage = new ObjectStorageAdapterDB(rec);
- if (saveableObjectClass == GenericSaveable.class) {
- obj = (T) new GenericSaveable(rec, propertyTable.getSchema());
- }
- else {
- obj = saveableObjectClass.getDeclaredConstructor().newInstance();
- obj.restore(objStorage);
- }
+ long addrKey = addrMap.getKey(addr, false);
+ if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
+ return null;
}
- catch (IOException e) {
- errHandler.dbError(e);
- }
- catch (RuntimeException e) {
- throw e;
- }
- catch (InstantiationException e) {
- errHandler.dbError(new IOException("Could not instantiate " + e.getMessage()));
- }
- catch (Exception e) {
- errHandler.dbError(new IOException(e.getMessage()));
-
- }
- finally {
- lock.release();
- }
-
- return obj;
+ return cache.computeIfAbsent(addrKey, valueReader);
}
/**
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java
index 72075228d9..38f33ad473 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java
@@ -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.
@@ -21,6 +21,7 @@ import java.util.NoSuchElementException;
import db.*;
import db.util.ErrorHandler;
import ghidra.framework.data.OpenMode;
+import ghidra.program.database.DatabaseObject;
import ghidra.program.database.map.*;
import ghidra.program.database.util.DatabaseTableUtils;
import ghidra.program.model.address.*;
@@ -37,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
* The map is stored within a database table.
* @param property value type
*/
-public abstract class PropertyMapDB implements PropertyMap {
+public abstract class PropertyMapDB extends DatabaseObject implements PropertyMap {
private static final String PROPERTY_TABLE_PREFIX = "Property Map - ";
@@ -63,7 +64,7 @@ public abstract class PropertyMapDB implements PropertyMap {
protected AddressMap addrMap;
protected String name;
- protected ObjectCache cache = new ObjectCache(DEFAULT_CACHE_SIZE);
+ protected ObjectCache cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
protected Lock lock;
/**
@@ -76,6 +77,7 @@ public abstract class PropertyMapDB implements PropertyMap {
*/
PropertyMapDB(DBHandle dbHandle, ErrorHandler errHandler, ChangeManager changeMgr,
AddressMap addrMap, String name) {
+ super(null, 0); // DatabaseObject cache is not used
this.dbHandle = dbHandle;
this.errHandler = errHandler;
@@ -92,6 +94,12 @@ public abstract class PropertyMapDB implements PropertyMap {
}
}
+ // Expose validate method to DBPropertyMapManager
+ @Override
+ protected boolean validate(Lock lck) {
+ return super.validate(lck);
+ }
+
/**
* Check if a table upgrade should be performed or a version error thrown.
* @param openMode the mode that the program was openned in or null if instantiated during
@@ -180,22 +188,24 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* Create the default propertyTable.
- * This method may be called by add property methods if propertyTable
- * is null.
- * @param valueField property value field type
+ * This method may be called by add property methods if propertyTable is null.
+ * @param valueField property value field type (null corresponds to void map table)
* @throws IOException if IO error occurs
*/
protected void createTable(Field valueField) throws IOException {
+ schema = getTableSchema(valueField);
+ propertyTable = dbHandle.createTable(getTableName(), schema);
+ }
+
+ private static Schema getTableSchema(Field valueField) {
if (valueField != null) {
// Create default table schema with a value column and an long Address key
Field[] fields = new Field[] { valueField };
- schema = new Schema(0, "Address", fields, SCHEMA_FIELD_NAMES);
+ return new Schema(0, "Address", fields, SCHEMA_FIELD_NAMES);
}
- else {
- // Table contains only a long Address key
- schema = new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES);
- }
- propertyTable = dbHandle.createTable(getTableName(), schema);
+
+ // Table contains only a long Address key (i.e., void property map)
+ return new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES);
}
@Override
@@ -227,8 +237,10 @@ public abstract class PropertyMapDB implements PropertyMap {
public void delete() throws IOException {
lock.acquire();
try {
+ checkDeleted();
+ setDeleted();
if (propertyTable != null) {
- cache = null;
+ cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
dbHandle.deleteTable(getTableName());
propertyTable = null;
}
@@ -240,12 +252,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public boolean intersects(Address startAddr, Address endAddr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return false;
}
try {
AddressKeyIterator iter =
- new AddressKeyIterator(propertyTable, addrMap, startAddr, endAddr, startAddr, true);
+ new AddressKeyIterator(table, addrMap, startAddr, endAddr, startAddr, true);
return iter.hasNext();
}
catch (IOException e) {
@@ -256,12 +270,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public boolean intersects(AddressSetView set) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return false;
}
try {
AddressKeyIterator iter =
- new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true);
+ new AddressKeyIterator(table, addrMap, set, set.getMinAddress(), true);
return iter.hasNext();
}
catch (IOException e) {
@@ -272,13 +288,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public boolean removeRange(Address startAddr, Address endAddr) {
- if (propertyTable == null) {
- return false;
- }
lock.acquire();
try {
+ checkDeleted();
+ if (propertyTable == null) {
+ return false;
+ }
if (AddressRecordDeleter.deleteRecords(propertyTable, addrMap, startAddr, endAddr)) {
- cache = new ObjectCache(DEFAULT_CACHE_SIZE);
+ cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
return true;
}
}
@@ -293,15 +310,16 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public boolean remove(Address addr) {
- if (propertyTable == null) {
- return false;
- }
lock.acquire();
boolean result = false;
try {
- long key = addrMap.getKey(addr, false);
- cache.remove(key);
- result = propertyTable.deleteRecord(key);
+ checkDeleted();
+ if (propertyTable == null) {
+ return false;
+ }
+ long addrKey = addrMap.getKey(addr, false);
+ cache.remove(addrKey);
+ result = propertyTable.deleteRecord(addrKey);
changeMgr.setPropertyChanged(name, addr, null, null);
}
catch (IOException e) {
@@ -315,33 +333,32 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public boolean hasProperty(Address addr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return false;
}
- lock.acquire();
- boolean result = false;
try {
- long key = addrMap.getKey(addr, false);
- if (key != AddressMap.INVALID_ADDRESS_KEY) {
- result = cache.contains(key) || propertyTable.hasRecord(key);
+ long addrKey = addrMap.getKey(addr, false);
+ if (addrKey != AddressMap.INVALID_ADDRESS_KEY) {
+ return cache.contains(addrKey) || table.hasRecord(addrKey);
}
}
catch (IOException e) {
errHandler.dbError(e);
}
- finally {
- lock.release();
- }
- return result;
+ return false;
}
@Override
public Address getNextPropertyAddress(Address addr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
try {
- AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, false);
+ AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, false);
return addrMap.decodeAddress(iter.next());
}
catch (NoSuchElementException e) {
@@ -355,11 +372,13 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public Address getPreviousPropertyAddress(Address addr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
try {
- AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, true);
+ AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, true);
return addrMap.decodeAddress(iter.previous());
}
catch (NoSuchElementException e) {
@@ -373,11 +392,13 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public Address getFirstPropertyAddress() {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
try {
- AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, true);
+ AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, true);
return addrMap.decodeAddress(iter.next());
}
catch (NoSuchElementException e) {
@@ -391,11 +412,13 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public Address getLastPropertyAddress() {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
try {
- AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap,
+ AddressKeyIterator iter = new AddressKeyIterator(table, addrMap,
addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false);
return addrMap.decodeAddress(iter.previous());
}
@@ -410,7 +433,9 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public int getSize() {
- return propertyTable != null ? propertyTable.getRecordCount() : 0;
+ validate(lock);
+ Table table = propertyTable;
+ return table != null ? table.getRecordCount() : 0;
}
/**
@@ -423,13 +448,15 @@ public abstract class PropertyMapDB implements PropertyMap {
*/
public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart)
throws IOException {
- if (propertyTable == null || (set != null && set.isEmpty())) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null || (set != null && set.isEmpty())) {
return AddressKeyIterator.EMPTY_ITERATOR;
}
if (atStart) {
- return new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true);
+ return new AddressKeyIterator(table, addrMap, set, set.getMinAddress(), true);
}
- return new AddressKeyIterator(propertyTable, addrMap, set, set.getMaxAddress(), false);
+ return new AddressKeyIterator(table, addrMap, set, set.getMaxAddress(), false);
}
/**
@@ -441,11 +468,12 @@ public abstract class PropertyMapDB implements PropertyMap {
*/
public AddressKeyIterator getAddressKeyIterator(Address start, boolean before)
throws IOException {
-
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return AddressKeyIterator.EMPTY_ITERATOR;
}
- return new AddressKeyIterator(propertyTable, addrMap, start, before);
+ return new AddressKeyIterator(table, addrMap, start, before);
}
/**
@@ -459,14 +487,15 @@ public abstract class PropertyMapDB implements PropertyMap {
*/
public AddressKeyIterator getAddressKeyIterator(Address start, Address end, boolean atStart)
throws IOException {
-
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return AddressKeyIterator.EMPTY_ITERATOR;
}
if (atStart) {
- return new AddressKeyIterator(propertyTable, addrMap, start, end, start, true);
+ return new AddressKeyIterator(table, addrMap, start, end, start, true);
}
- return new AddressKeyIterator(propertyTable, addrMap, start, end, end, false);
+ return new AddressKeyIterator(table, addrMap, start, end, end, false);
}
@Override
@@ -495,12 +524,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public AddressIterator getPropertyIterator() {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return new EmptyAddressIterator();
}
AddressKeyIterator keyIter = null;
try {
- keyIter = new AddressKeyIterator(propertyTable, addrMap, true);
+ keyIter = new AddressKeyIterator(table, addrMap, true);
}
catch (IOException e) {
errHandler.dbError(e);
@@ -510,13 +541,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public AddressIterator getPropertyIterator(AddressSetView asv) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return new EmptyAddressIterator();
}
AddressKeyIterator keyIter = null;
try {
- keyIter =
- new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true);
+ keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true);
}
catch (IOException e) {
errHandler.dbError(e);
@@ -526,18 +558,18 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward) {
- if (propertyTable == null || (asv != null && asv.isEmpty())) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null || (asv != null && asv.isEmpty())) {
return AddressIterator.EMPTY_ITERATOR;
}
AddressKeyIterator keyIter = null;
try {
if (forward) {
- keyIter =
- new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true);
+ keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true);
}
else {
- keyIter =
- new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMaxAddress(), false);
+ keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMaxAddress(), false);
}
}
catch (IOException e) {
@@ -548,12 +580,14 @@ public abstract class PropertyMapDB implements PropertyMap {
@Override
public AddressIterator getPropertyIterator(Address start, boolean forward) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return new EmptyAddressIterator();
}
AddressKeyIterator keyIter = null;
try {
- keyIter = new AddressKeyIterator(propertyTable, addrMap, start, forward);
+ keyIter = new AddressKeyIterator(table, addrMap, start, forward);
}
catch (IOException e) {
errHandler.dbError(e);
@@ -564,23 +598,36 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* Invalidates the cache.
*/
- public void invalidateCache() {
+ public void invalidate() {
lock.acquire();
try {
- propertyTable = dbHandle.getTable(getTableName());
- cache = new ObjectCache(DEFAULT_CACHE_SIZE);
+ setInvalid();
}
finally {
lock.release();
}
+ }
+ @Override
+ protected boolean refresh() {
+ cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
+ propertyTable = dbHandle.getTable(getTableName());
+ if (propertyTable != null) {
+ if (!propertyTable.getSchema().equals(schema)) {
+ propertyTable = null;
+ return false;
+ }
+ }
+ // Must assume lazy table and empty map
+ return true;
}
@Override
public void moveRange(Address start, Address end, Address newStart) {
lock.acquire();
try {
- cache = new ObjectCache(DEFAULT_CACHE_SIZE);
+ checkDeleted();
+ cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
if (propertyTable != null) {
try {
DatabaseTableUtils.updateAddressKey(propertyTable, addrMap, start, end,
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java
index 5a83ce3976..1afa5a204f 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java
@@ -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.
@@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
+import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@@ -34,6 +35,24 @@ import ghidra.util.task.TaskMonitor;
*/
public class StringPropertyMapDB extends PropertyMapDB implements StringPropertyMap {
+ /**
+ * A single non-capturing lambda record reader function is used to avoid the possibility of
+ * multiple synthetic class instantiations.
+ */
+ private Function valueReader = addrKey -> {
+ Table table = propertyTable;
+ DBRecord rec = null;
+ try {
+ if (table != null) {
+ rec = table.getRecord(addrKey);
+ }
+ }
+ catch (IOException e) {
+ errHandler.dbError(e);
+ }
+ return rec != null ? rec.getString(PROPERTY_VALUE_COL) : null;
+ };
+
/**
* Construct an String property map.
* @param dbHandle database handle.
@@ -59,25 +78,24 @@ public class StringPropertyMapDB extends PropertyMapDB implements String
public void add(Address addr, String value) {
lock.acquire();
try {
- long key = addrMap.getKey(addr, true);
+ checkDeleted();
+
+ long addrKey = addrMap.getKey(addr, true);
String oldValue = null;
if (propertyTable == null) {
createTable(StringField.INSTANCE);
}
else {
- oldValue = (String) cache.get(key);
+ oldValue = cache.get(addrKey);
if (oldValue == null) {
- DBRecord rec = propertyTable.getRecord(key);
- if (rec != null) {
- oldValue = rec.getString(PROPERTY_VALUE_COL);
- }
+ oldValue = valueReader.apply(addrKey);
}
}
- DBRecord rec = schema.createRecord(key);
+ DBRecord rec = schema.createRecord(addrKey);
rec.setString(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec);
- cache.put(key, value);
+ cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value);
}
catch (IOException e) {
@@ -91,38 +109,16 @@ public class StringPropertyMapDB extends PropertyMapDB implements String
@Override
public String getString(Address addr) {
- if (propertyTable == null) {
+ validate(lock);
+ Table table = propertyTable;
+ if (table == null) {
return null;
}
-
- String str = null;
-
- lock.acquire();
- try {
- long key = addrMap.getKey(addr, false);
- if (key == AddressMap.INVALID_ADDRESS_KEY) {
- return null;
- }
- str = (String) cache.get(key);
- if (str != null) {
- return str;
- }
-
- DBRecord rec = propertyTable.getRecord(key);
- if (rec == null) {
- return null;
- }
- str = rec.getString(PROPERTY_VALUE_COL);
+ long addrKey = addrMap.getKey(addr, false);
+ if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
+ return null;
}
- catch (IOException e) {
- errHandler.dbError(e);
-
- }
- finally {
- lock.release();
- }
-
- return str;
+ return cache.computeIfAbsent(addrKey, valueReader);
}
@Override
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java
index e1458ad43f..018745d470 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java
@@ -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,7 @@ import ghidra.util.task.TaskMonitor;
*/
public class VoidPropertyMapDB extends PropertyMapDB implements VoidPropertyMap {
- private static Object VOID_OBJECT = new Object();
+ private static Boolean TRUE = true;
/**
* Construct an void object property map.
@@ -64,15 +64,17 @@ public class VoidPropertyMapDB extends PropertyMapDB implements VoidPro
public void add(Address addr) {
lock.acquire();
try {
- long key = addrMap.getKey(addr, true);
+ checkDeleted();
+
+ long addrKey = addrMap.getKey(addr, true);
Boolean oldValue = hasProperty(addr);
if (propertyTable == null) {
createTable(null);
}
- DBRecord rec = schema.createRecord(key);
+ DBRecord rec = schema.createRecord(addrKey);
propertyTable.putRecord(rec);
- cache.put(key, VOID_OBJECT);
+ cache.put(addrKey, TRUE);
changeMgr.setPropertyChanged(name, addr, oldValue, true);
}
catch (IOException e) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java
index b004567d5e..7fe8f1d451 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java
@@ -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.
@@ -56,19 +56,11 @@ public class GenericAddress implements Address {
this.addrSpace = addrSpace;
}
- /**
- *
- * @see ghidra.program.model.address.Address#getAddress(java.lang.String)
- */
@Override
public Address getAddress(String addrString) throws AddressFormatException {
return addrSpace.getAddress(addrString);
}
- /**
- *
- * @see ghidra.program.model.address.Address#getNewAddress(long)
- */
@Override
public Address getNewAddress(long byteOffset) {
return addrSpace.getAddress(byteOffset);
@@ -86,10 +78,6 @@ public class GenericAddress implements Address {
return addrSpace.getTruncatedAddress(addrOffset, isAddressableWordOffset);
}
- /**
- *
- * @see ghidra.program.model.address.Address#getOffset()
- */
@Override
public long getOffset() {
return offset;
@@ -100,9 +88,6 @@ public class GenericAddress implements Address {
return addrSpace.getAddressableWordOffset(offset);
}
- /**
- * @see ghidra.program.model.address.Address#getUnsignedOffset()
- */
@Override
public long getUnsignedOffset() {
// TODO: Validity of offset within space is not verified
@@ -117,37 +102,21 @@ public class GenericAddress implements Address {
return spaceSize + offset;
}
- /**
- *
- * @see ghidra.program.model.address.Address#getAddressSpace()
- */
@Override
public AddressSpace getAddressSpace() {
return addrSpace;
}
- /**
- *
- * @see ghidra.program.model.address.Address#getSize()
- */
@Override
public int getSize() {
return addrSpace.getSize();
}
- /**
- *
- * @see ghidra.program.model.address.Address#subtract(ghidra.program.model.address.Address)
- */
@Override
public long subtract(Address addr) {
return addrSpace.subtract(this, addr);
}
- /**
- *
- * @see ghidra.program.model.address.Address#subtractWrap(long)
- */
@Override
public Address subtractWrap(long displacement) {
if (displacement == 0)
@@ -155,9 +124,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrap(this, displacement);
}
- /**
- * @see ghidra.program.model.address.Address#subtractWrapSpace(long)
- */
@Override
public Address subtractWrapSpace(long displacement) {
if (displacement == 0)
@@ -165,10 +131,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrapSpace(this, displacement);
}
- /**
- *
- * @see ghidra.program.model.address.Address#subtractNoWrap(long)
- */
@Override
public Address subtractNoWrap(long displacement) throws AddressOverflowException {
if (displacement == 0)
@@ -176,10 +138,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractNoWrap(this, displacement);
}
- /**
- *
- * @see ghidra.program.model.address.Address#subtract(long)
- */
@Override
public Address subtract(long displacement) {
if (displacement == 0)
@@ -187,9 +145,6 @@ public class GenericAddress implements Address {
return addrSpace.subtract(this, displacement);
}
- /**
- * @see ghidra.program.model.address.Address#addWrap(long)
- */
@Override
public Address addWrap(long displacement) {
if (displacement == 0)
@@ -197,9 +152,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrap(this, displacement);
}
- /**
- * @see ghidra.program.model.address.Address#addWrapSpace(long)
- */
@Override
public Address addWrapSpace(long displacement) {
if (displacement == 0)
@@ -207,10 +159,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrapSpace(this, displacement);
}
- /**
- *
- * @see ghidra.program.model.address.Address#addNoWrap(long)
- */
@Override
public Address addNoWrap(long displacement) throws AddressOverflowException {
if (displacement == 0)
@@ -226,10 +174,6 @@ public class GenericAddress implements Address {
return addrSpace.addNoWrap(this, displacement);
}
- /**
- *
- * @see ghidra.program.model.address.Address#add(long)
- */
@Override
public Address add(long displacement) {
if (displacement == 0)
@@ -237,10 +181,6 @@ public class GenericAddress implements Address {
return addrSpace.add(this, displacement);
}
- /**
- *
- * @see ghidra.program.model.address.Address#isSuccessor(ghidra.program.model.address.Address)
- */
@Override
public boolean isSuccessor(Address addr) {
return addrSpace.isSuccessor(this, addr);
@@ -259,26 +199,17 @@ public class GenericAddress implements Address {
return Long.compareUnsigned(offset, otherOffset);
}
- /**
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (!(o instanceof GenericAddress)) {
+ if (!(o instanceof GenericAddress addr)) {
return false;
}
- GenericAddress addr = (GenericAddress) o;
- return addrSpace.equals(addr.getAddressSpace()) && offset == addr.offset;
+ return offset == addr.offset && addrSpace.equals(addr.getAddressSpace());
}
- /**
- *
- * @see java.lang.Object#hashCode()
- */
@Override
public int hashCode() {
int hash1 = addrSpace.hashCode();
@@ -286,18 +217,11 @@ public class GenericAddress implements Address {
return (hash1 << 16) ^ hash3;
}
- /**
- * @see java.lang.Object#toString()
- */
@Override
public String toString() {
return toString(addrSpace.showSpaceName(), MINIMUM_DIGITS);
}
- /**
- *
- * @see ghidra.program.model.address.Address#toString(java.lang.String)
- */
@Override
public String toString(String prefix) {
boolean showSpace = prefix.length() == 0 && addrSpace.showSpaceName();
@@ -368,19 +292,11 @@ public class GenericAddress implements Address {
return buf.toString();
}
- /**
- *
- * @see ghidra.program.model.address.Address#hasSameAddressSpace(ghidra.program.model.address.Address)
- */
@Override
public boolean hasSameAddressSpace(Address addr) {
return addrSpace.equals(addr.getAddressSpace());
}
- /**
- *
- * @see ghidra.program.model.address.Address#next()
- */
@Override
public Address next() {
if (addrSpace.getMaxAddress().getOffset() == offset) {
@@ -389,9 +305,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrap(this, 1);
}
- /**
- * @see ghidra.program.model.address.Address#previous()
- */
@Override
public Address previous() {
if (addrSpace.getMinAddress().getOffset() == offset) {
@@ -400,9 +313,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrap(this, 1);
}
- /**
- * @see ghidra.program.model.address.Address#getPhysicalAddress()
- */
@Override
public Address getPhysicalAddress() {
AddressSpace physical = addrSpace.getPhysicalSpace();
@@ -415,17 +325,11 @@ public class GenericAddress implements Address {
return null;
}
- /**
- * @see ghidra.program.model.address.Address#getPointerSize()
- */
@Override
public int getPointerSize() {
return addrSpace.getPointerSize();
}
- /**
- * @see ghidra.program.model.address.Address#isMemoryAddress()
- */
@Override
public boolean isMemoryAddress() {
return addrSpace.isMemorySpace();
@@ -441,57 +345,36 @@ public class GenericAddress implements Address {
return addrSpace.isNonLoadedMemorySpace();
}
- /**
- * @see ghidra.program.model.address.Address#isHashAddress()
- */
@Override
public boolean isHashAddress() {
return addrSpace.isHashSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isStackAddress()
- */
@Override
public boolean isStackAddress() {
return addrSpace.isStackSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isUniqueAddress()
- */
@Override
public boolean isUniqueAddress() {
return addrSpace.isUniqueSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isConstantAddress()
- */
@Override
public boolean isConstantAddress() {
return addrSpace.isConstantSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isVariableAddress()
- */
@Override
public boolean isVariableAddress() {
return addrSpace.isVariableSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isRegisterAddress()
- */
@Override
public boolean isRegisterAddress() {
return addrSpace.isRegisterSpace();
}
- /**
- * @see ghidra.program.model.address.Address#isExternalAddress()
- */
@Override
public boolean isExternalAddress() {
return addrSpace.isExternalSpace();
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java
index 7684bdbccf..62ca480e3a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java
@@ -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.
@@ -141,7 +141,7 @@ public interface PropertyMapManager {
public boolean removePropertyMap(String propertyName);
/**
- * Returns an iterator over the names of all existing PropertyMaps.
+ * Returns an iterator over the names of all existing PropertyMaps sorted by name.
*/
public Iterator propertyManagers();