Merge branch 'GP-5619_emteere_VariousSpeedImprovements_VERSION2'

This commit is contained in:
ghidra1 2025-05-08 14:57:36 -04:00
commit df505c40a3
25 changed files with 691 additions and 763 deletions

View file

@ -18,6 +18,7 @@ package ghidra.util.datastruct;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.*; import java.util.*;
import java.util.function.Function;
/** /**
* <code>ObjectClass</code> provides a fixed-size long-key-based object cache. * <code>ObjectClass</code> provides a fixed-size long-key-based object cache.
@ -27,12 +28,14 @@ import java.util.*;
* <p> * <p>
* The weak cache is keyed, while the hard cache simply maintains the presence of * The weak cache is keyed, while the hard cache simply maintains the presence of
* an object in the weak cache. * an object in the weak cache.
*
* @param <T> Object type held by cache
*/ */
public class ObjectCache { public class ObjectCache<T> {
private Map<Long, KeyedSoftReference<?>> hashTable; private Map<Long, KeyedSoftReference<T>> hashTable;
private ReferenceQueue<Object> refQueue; private ReferenceQueue<T> refQueue;
private LinkedList<Object> hardCache; private LinkedList<T> hardCache;
private int hardCacheSize; private int hardCacheSize;
/** /**
@ -61,10 +64,10 @@ public class ObjectCache {
* @param key object key * @param key object key
* @return cached object * @return cached object
*/ */
public synchronized Object get(long key) { public synchronized T get(long key) {
WeakReference<?> ref = hashTable.get(key); WeakReference<T> ref = hashTable.get(key);
if (ref != null) { if (ref != null) {
Object obj = ref.get(); T obj = ref.get();
if (obj == null) { if (obj == null) {
hashTable.remove(key); hashTable.remove(key);
} }
@ -74,6 +77,30 @@ public class ObjectCache {
return null; 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<Long, T> 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
* @return the hard cache size * @return the hard cache size
@ -98,9 +125,9 @@ public class ObjectCache {
* @param key object key * @param key object key
* @param obj the object * @param obj the object
*/ */
public synchronized void put(long key, Object obj) { public synchronized void put(long key, T obj) {
processQueue(); processQueue();
KeyedSoftReference<?> ref = new KeyedSoftReference<>(key, obj, refQueue); KeyedSoftReference<T> ref = new KeyedSoftReference<>(key, obj, refQueue);
hashTable.put(key, ref); hashTable.put(key, ref);
addToHardCache(obj); addToHardCache(obj);
} }
@ -112,7 +139,7 @@ public class ObjectCache {
*/ */
public synchronized void remove(long key) { public synchronized void remove(long key) {
processQueue(); processQueue();
KeyedSoftReference<?> ref = hashTable.get(key); KeyedSoftReference<T> ref = hashTable.get(key);
if (ref != null) { if (ref != null) {
ref.clear(); ref.clear();
hashTable.remove(key); hashTable.remove(key);
@ -123,7 +150,7 @@ public class ObjectCache {
* Add the specified object to the hard cache. * Add the specified object to the hard cache.
* @param obj object * @param obj object
*/ */
private void addToHardCache(Object obj) { private void addToHardCache(T obj) {
hardCache.addLast(obj); hardCache.addLast(obj);
if (hardCache.size() > hardCacheSize) { if (hardCache.size() > hardCacheSize) {
hardCache.removeFirst(); hardCache.removeFirst();
@ -134,8 +161,8 @@ public class ObjectCache {
* Cleanup weak cache * Cleanup weak cache
*/ */
private void processQueue() { private void processQueue() {
KeyedSoftReference<?> ref; KeyedSoftReference<? extends T> ref;
while ((ref = (KeyedSoftReference<?>) refQueue.poll()) != null) { while ((ref = (KeyedSoftReference<? extends T>) refQueue.poll()) != null) {
hashTable.remove(ref.getKey()); hashTable.remove(ref.getKey());
} }
} }

View file

@ -33,6 +33,7 @@ public abstract class DatabaseObject {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private final DBObjectCache cache; private final DBObjectCache cache;
private volatile int invalidateCount; private volatile int invalidateCount;
private boolean refreshing = false;
/** /**
* Constructs a new DatabaseObject and adds it to the specified cache. * 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. * 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 * 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 * record changes. A common situation where this can occur is an undo/redo operation
* against the underlying database. The methods {@link #checkIsValid()}, {@link #checkDeleted()}, * 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 * Check whether this object is still valid. If the object is invalid and not deleted, the
* to refresh itself using the specified record. If the refresh fails, the object will be marked * object will attempt to refresh itself using the specified record. If the refresh fails,
* as deleted and removed from cache. If this object is already marked as deleted, the record * the object will be marked as deleted and removed from cache. If this object is already
* can not be used to refresh the object. * marked as deleted a refresh will not be perormed.
* <P>
* 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 * @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) { protected boolean checkIsValid(DBRecord record) {
if (isInvalid()) { if (isInvalid()) {
setValid();// prevent checkIsValid recursion during refresh if (refreshing) {
if (!refresh(record)) { // 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) { if (cache != null) {
cache.delete(key); cache.delete(key);
} }
setDeleted(); setDeleted();
setInvalid(); return false;
}
finally {
refreshing = false;
} }
} }
return !deleted; 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 * 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. * @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) { protected boolean validate(Lock lock) {
if (!isInvalid()) { if (!isInvalid()) {
return true; return !deleted;
} }
lock.acquire(); lock.acquire();
try { try {

View file

@ -3207,8 +3207,8 @@ public class CodeManager implements ErrorHandler, ManagerDB {
lock.acquire(); lock.acquire();
try { try {
cache.invalidate(); cache.invalidate();
lengthMgr.invalidateCache(); lengthMgr.invalidate();
compositeMgr.invalidateCache(); compositeMgr.invalidate();
protoMgr.clearCache(); protoMgr.clearCache();
} }
finally { finally {

View file

@ -65,7 +65,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
* @param cacheKey the cache key (dataComponent does not use the address) * @param cacheKey the cache key (dataComponent does not use the address)
* @param address min address of this code unit * @param address min address of this code unit
* @param addr index for min address * @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, public CodeUnitDB(CodeManager codeMgr, DBObjectCache<? extends CodeUnitDB> cache, long cacheKey,
Address address, long addr, int length) { Address address, long addr, int length) {
@ -95,21 +95,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
return !hasBeenDeleted(record); 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. * 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 * 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 @Override
public void addMnemonicReference(Address refAddr, RefType refType, SourceType sourceType) { public void addMnemonicReference(Address refAddr, RefType refType, SourceType sourceType) {
refreshIfNeeded(); validate(lock);
refMgr.addMemoryReference(address, refAddr, refType, sourceType, MNEMONIC); refMgr.addMemoryReference(address, refAddr, refType, sourceType, MNEMONIC);
} }
@ -134,13 +119,12 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void addOperandReference(int opIndex, Address refAddr, RefType type, public void addOperandReference(int opIndex, Address refAddr, RefType type,
SourceType sourceType) { SourceType sourceType) {
refreshIfNeeded(); validate(lock);
refMgr.addMemoryReference(address, refAddr, type, sourceType, opIndex); refMgr.addMemoryReference(address, refAddr, type, sourceType, opIndex);
} }
@Override @Override
public int compareTo(Address a) { public int compareTo(Address a) {
refreshIfNeeded();
if (contains(a)) { if (contains(a)) {
return 0; return 0;
} }
@ -149,13 +133,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public boolean contains(Address testAddr) { public boolean contains(Address testAddr) {
refreshIfNeeded(); validate(lock);
return address.compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0; return address.compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0;
} }
@Override @Override
public String getAddressString(boolean showBlockName, boolean pad) { public String getAddressString(boolean showBlockName, boolean pad) {
refreshIfNeeded(); validate(lock);
Address cuAddress = address; Address cuAddress = address;
String addressString = cuAddress.toString(false, pad); String addressString = cuAddress.toString(false, pad);
if (showBlockName) { if (showBlockName) {
@ -231,7 +215,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public ExternalReference getExternalReference(int opIndex) { public ExternalReference getExternalReference(int opIndex) {
refreshIfNeeded(); validate(lock);
Reference[] refs = refMgr.getReferencesFrom(address, opIndex); Reference[] refs = refMgr.getReferencesFrom(address, opIndex);
for (Reference element : refs) { for (Reference element : refs) {
if (element.isExternalReference()) { if (element.isExternalReference()) {
@ -249,7 +233,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
throw NoValueException.noValueException; throw NoValueException.noValueException;
} }
try { try {
refreshIfNeeded(); validate(lock);
return pm.getInt(address); return pm.getInt(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -259,7 +243,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public String getLabel() { public String getLabel() {
refreshIfNeeded(); validate(lock);
SymbolTable st = codeMgr.getSymbolTable(); SymbolTable st = codeMgr.getSymbolTable();
Symbol symbol = st.getPrimarySymbol(address); Symbol symbol = st.getPrimarySymbol(address);
if (symbol != null) { if (symbol != null) {
@ -279,7 +263,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public Address getMaxAddress() { public Address getMaxAddress() {
refreshIfNeeded(); validate(lock);
if (endAddr == null) { if (endAddr == null) {
endAddr = getLength() == 0 ? address : address.add(getLength() - 1); endAddr = getLength() == 0 ? address : address.add(getLength() - 1);
} }
@ -288,20 +272,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public Address getMinAddress() { public Address getMinAddress() {
refreshIfNeeded(); validate(lock);
return address; return address;
} }
@Override @Override
public Address getAddress() { public Address getAddress() {
// TODO: Not sure why this method exists? validate(lock);
refreshIfNeeded();
return address; return address;
} }
@Override @Override
public Reference[] getMnemonicReferences() { public Reference[] getMnemonicReferences() {
refreshIfNeeded(); validate(lock);
return refMgr.getReferencesFrom(address, MNEMONIC); return refMgr.getReferencesFrom(address, MNEMONIC);
} }
@ -311,7 +294,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
ObjectPropertyMap<?> pm = upm.getObjectPropertyMap(name); ObjectPropertyMap<?> pm = upm.getObjectPropertyMap(name);
if (pm != null) { if (pm != null) {
try { try {
refreshIfNeeded(); validate(lock);
return pm.get(address); return pm.get(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -322,19 +305,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public Reference[] getOperandReferences(int opIndex) { public Reference[] getOperandReferences(int opIndex) {
refreshIfNeeded(); validate(lock);
return refMgr.getReferencesFrom(address, opIndex); return refMgr.getReferencesFrom(address, opIndex);
} }
@Override @Override
public Reference getPrimaryReference(int index) { public Reference getPrimaryReference(int index) {
refreshIfNeeded(); validate(lock);
return refMgr.getPrimaryReferenceFrom(address, index); return refMgr.getPrimaryReferenceFrom(address, index);
} }
@Override @Override
public Symbol getPrimarySymbol() { public Symbol getPrimarySymbol() {
refreshIfNeeded(); validate(lock);
SymbolTable st = codeMgr.getSymbolTable(); SymbolTable st = codeMgr.getSymbolTable();
return st.getPrimarySymbol(address); return st.getPrimarySymbol(address);
} }
@ -346,13 +329,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public Reference[] getReferencesFrom() { public Reference[] getReferencesFrom() {
refreshIfNeeded(); validate(lock);
return refMgr.getReferencesFrom(address); return refMgr.getReferencesFrom(address);
} }
@Override @Override
public ReferenceIterator getReferenceIteratorTo() { public ReferenceIterator getReferenceIteratorTo() {
refreshIfNeeded(); validate(lock);
return program.getReferenceManager().getReferencesTo(address); return program.getReferenceManager().getReferencesTo(address);
} }
@ -362,7 +345,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
StringPropertyMap pm = upm.getStringPropertyMap(name); StringPropertyMap pm = upm.getStringPropertyMap(name);
if (pm != null) { if (pm != null) {
try { try {
refreshIfNeeded(); validate(lock);
return pm.getString(address); return pm.getString(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -373,7 +356,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public Symbol[] getSymbols() { public Symbol[] getSymbols() {
refreshIfNeeded(); validate(lock);
SymbolTable st = codeMgr.getSymbolTable(); SymbolTable st = codeMgr.getSymbolTable();
return st.getSymbols(address); return st.getSymbols(address);
} }
@ -384,7 +367,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
VoidPropertyMap pm = upm.getVoidPropertyMap(name); VoidPropertyMap pm = upm.getVoidPropertyMap(name);
if (pm != null) { if (pm != null) {
try { try {
refreshIfNeeded(); validate(lock);
return pm.hasProperty(address); return pm.hasProperty(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -399,7 +382,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
PropertyMap<?> pm = upm.getPropertyMap(name); PropertyMap<?> pm = upm.getPropertyMap(name);
if (pm != null) { if (pm != null) {
try { try {
refreshIfNeeded(); validate(lock);
return pm.hasProperty(address); return pm.hasProperty(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -424,7 +407,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void removeMnemonicReference(Address refAddr) { public void removeMnemonicReference(Address refAddr) {
refreshIfNeeded(); validate(lock);
Reference ref = refMgr.getReference(address, refAddr, MNEMONIC); Reference ref = refMgr.getReference(address, refAddr, MNEMONIC);
if (ref != null) { if (ref != null) {
program.getReferenceManager().delete(ref); program.getReferenceManager().delete(ref);
@ -433,7 +416,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void removeOperandReference(int opIndex, Address refAddr) { public void removeOperandReference(int opIndex, Address refAddr) {
refreshIfNeeded(); validate(lock);
Reference ref = refMgr.getReference(address, refAddr, opIndex); Reference ref = refMgr.getReference(address, refAddr, opIndex);
if (ref != null) { if (ref != null) {
program.getReferenceManager().delete(ref); program.getReferenceManager().delete(ref);
@ -446,7 +429,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
PropertyMap<?> pm = upm.getPropertyMap(name); PropertyMap<?> pm = upm.getPropertyMap(name);
if (pm != null) { if (pm != null) {
try { try {
refreshIfNeeded(); validate(lock);
pm.remove(address); pm.remove(address);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -607,7 +590,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void setStackReference(int opIndex, int offset, SourceType sourceType, RefType refType) { public void setStackReference(int opIndex, int offset, SourceType sourceType, RefType refType) {
refreshIfNeeded(); validate(lock);
validateOpIndex(opIndex); validateOpIndex(opIndex);
refMgr.addStackReference(address, opIndex, offset, refType, sourceType); refMgr.addStackReference(address, opIndex, offset, refType, sourceType);
} }
@ -615,14 +598,14 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void setRegisterReference(int opIndex, Register reg, SourceType sourceType, public void setRegisterReference(int opIndex, Register reg, SourceType sourceType,
RefType refType) { RefType refType) {
refreshIfNeeded(); validate(lock);
validateOpIndex(opIndex); validateOpIndex(opIndex);
refMgr.addRegisterReference(address, opIndex, reg, refType, sourceType); refMgr.addRegisterReference(address, opIndex, reg, refType, sourceType);
} }
@Override @Override
public int getBytes(byte[] b, int offset) { public int getBytes(byte[] b, int offset) {
refreshIfNeeded(); validate(lock);
byte localBytes[] = populateByteArray(); byte localBytes[] = populateByteArray();
if (offset >= 0 && (offset + b.length) <= localBytes.length) { if (offset >= 0 && (offset + b.length) <= localBytes.length) {
System.arraycopy(localBytes, offset, b, 0, b.length); System.arraycopy(localBytes, offset, b, 0, b.length);
@ -639,10 +622,10 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public byte[] getBytes() throws MemoryAccessException { public byte[] getBytes() throws MemoryAccessException {
refreshIfNeeded(); validate(lock);
byte localBytes[] = populateByteArray(); byte localBytes[] = populateByteArray();
int locallen = getLength(); int locallen = getLength();
if (localBytes.length >= locallen ) { if (localBytes.length >= locallen) {
byte[] b = new byte[locallen]; byte[] b = new byte[locallen];
System.arraycopy(localBytes, 0, b, 0, b.length); System.arraycopy(localBytes, 0, b, 0, b.length);
return b; return b;
@ -659,7 +642,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public byte getByte(int offset) throws MemoryAccessException { public byte getByte(int offset) throws MemoryAccessException {
refreshIfNeeded(); validate(lock);
byte localBytes[] = populateByteArray(); byte localBytes[] = populateByteArray();
if (offset >= 0 && offset < localBytes.length) { if (offset >= 0 && offset < localBytes.length) {
return localBytes[offset]; return localBytes[offset];
@ -680,31 +663,31 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public BigInteger getValue(Register register, boolean signed) { public BigInteger getValue(Register register, boolean signed) {
refreshIfNeeded(); validate(lock);
return programContext.getValue(register, address, signed); return programContext.getValue(register, address, signed);
} }
@Override @Override
public RegisterValue getRegisterValue(Register register) { public RegisterValue getRegisterValue(Register register) {
refreshIfNeeded(); validate(lock);
return programContext.getRegisterValue(register, address); return programContext.getRegisterValue(register, address);
} }
@Override @Override
public void setValue(Register register, BigInteger value) throws ContextChangeException { public void setValue(Register register, BigInteger value) throws ContextChangeException {
refreshIfNeeded(); validate(lock);
programContext.setValue(register, address, address, value); programContext.setValue(register, address, address, value);
} }
@Override @Override
public void clearRegister(Register register) throws ContextChangeException { public void clearRegister(Register register) throws ContextChangeException {
refreshIfNeeded(); validate(lock);
programContext.setValue(register, address, address, null); programContext.setValue(register, address, address, null);
} }
@Override @Override
public void setRegisterValue(RegisterValue value) throws ContextChangeException { public void setRegisterValue(RegisterValue value) throws ContextChangeException {
refreshIfNeeded(); validate(lock);
programContext.setRegisterValue(address, address, value); programContext.setRegisterValue(address, address, value);
} }
@ -725,7 +708,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public boolean hasValue(Register register) { public boolean hasValue(Register register) {
refreshIfNeeded(); validate(lock);
return programContext.getValue(register, address, false) != null; return programContext.getValue(register, address, false) != null;
} }
@ -818,7 +801,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC
@Override @Override
public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException { public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException {
refreshIfNeeded(); validate(lock);
byte[] codeUnitBytes = getBytes(); byte[] codeUnitBytes = getBytes();
System.arraycopy(codeUnitBytes, 0, buffer, bufferOffset, System.arraycopy(codeUnitBytes, 0, buffer, bufferOffset,
Math.min(buffer.length, getLength())); Math.min(buffer.length, getLength()));

View file

@ -197,7 +197,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public void addValueReference(Address refAddr, RefType type) { public void addValueReference(Address refAddr, RefType type) {
refreshIfNeeded(); validate(lock);
refMgr.addMemoryReference(address, refAddr, type, SourceType.USER_DEFINED, refMgr.addMemoryReference(address, refAddr, type, SourceType.USER_DEFINED,
CodeManager.DATA_OP_INDEX); CodeManager.DATA_OP_INDEX);
} }
@ -379,19 +379,19 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { public boolean isChangeAllowed(SettingsDefinition settingsDefinition) {
refreshIfNeeded(); validate(lock);
return dataMgr.isChangeAllowed(this, settingsDefinition); return dataMgr.isChangeAllowed(this, settingsDefinition);
} }
@Override @Override
public void clearSetting(String name) { public void clearSetting(String name) {
refreshIfNeeded(); validate(lock);
dataMgr.clearSetting(this, name); dataMgr.clearSetting(this, name);
} }
@Override @Override
public Long getLong(String name) { public Long getLong(String name) {
refreshIfNeeded(); validate(lock);
Long value = dataMgr.getLongSettingsValue(this, name); Long value = dataMgr.getLongSettingsValue(this, name);
if (value == null) { if (value == null) {
value = getDefaultSettings().getLong(name); value = getDefaultSettings().getLong(name);
@ -401,13 +401,13 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public String[] getNames() { public String[] getNames() {
refreshIfNeeded(); validate(lock);
return dataMgr.getInstanceSettingsNames(this); return dataMgr.getInstanceSettingsNames(this);
} }
@Override @Override
public String getString(String name) { public String getString(String name) {
refreshIfNeeded(); validate(lock);
String value = dataMgr.getStringSettingsValue(this, name); String value = dataMgr.getStringSettingsValue(this, name);
if (value == null) { if (value == null) {
value = getDefaultSettings().getString(name); value = getDefaultSettings().getString(name);
@ -417,7 +417,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public Object getValue(String name) { public Object getValue(String name) {
refreshIfNeeded(); validate(lock);
Object value = dataMgr.getSettings(this, name); Object value = dataMgr.getSettings(this, name);
if (value == null) { if (value == null) {
value = getDefaultSettings().getValue(name); value = getDefaultSettings().getValue(name);
@ -427,19 +427,19 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public void setLong(String name, long value) { public void setLong(String name, long value) {
refreshIfNeeded(); validate(lock);
dataMgr.setLongSettingsValue(this, name, value); dataMgr.setLongSettingsValue(this, name, value);
} }
@Override @Override
public void setString(String name, String value) { public void setString(String name, String value) {
refreshIfNeeded(); validate(lock);
dataMgr.setStringSettingsValue(this, name, value); dataMgr.setStringSettingsValue(this, name, value);
} }
@Override @Override
public void setValue(String name, Object value) { public void setValue(String name, Object value) {
refreshIfNeeded(); validate(lock);
dataMgr.setSettings(this, name, value); dataMgr.setSettings(this, name, value);
} }
@ -650,7 +650,7 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public String getPathName() { public String getPathName() {
refreshIfNeeded(); validate(lock);
Address cuAddress = address; Address cuAddress = address;
SymbolTable st = program.getSymbolTable(); SymbolTable st = program.getSymbolTable();
Symbol symbol = st.getPrimarySymbol(cuAddress); Symbol symbol = st.getPrimarySymbol(cuAddress);
@ -763,13 +763,13 @@ class DataDB extends CodeUnitDB implements Data {
@Override @Override
public void clearAllSettings() { public void clearAllSettings() {
refreshIfNeeded(); validate(lock);
dataMgr.clearAllSettings(this); dataMgr.clearAllSettings(this);
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
refreshIfNeeded(); validate(lock);
return dataMgr.isEmptySetting(this); return dataMgr.isEmptySetting(this);
} }

View file

@ -54,9 +54,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
private FlowOverride flowOverride; private FlowOverride flowOverride;
private int lengthOverride; private int lengthOverride;
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0]; 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 volatile boolean clearingFallThroughs = false;
private ParserContext parserContext; private ParserContext parserContext; // uses lazy initialization
private String mnemonicString; // uses lazy initialization
/** /**
* Construct a new InstructionDB. * Construct a new InstructionDB.
@ -80,6 +82,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
protected boolean refresh(DBRecord record) { protected boolean refresh(DBRecord record) {
parserContext = null; parserContext = null;
mnemonicString = null;
return super.refresh(record); return super.refresh(record);
} }
@ -240,12 +243,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
return null; return null;
} }
if (this.isInDelaySlot()) { if (isInDelaySlot()) {
// If this instruction is within delay-slot, return a null fall-from address if // If this instruction is within delay-slot, return a null fall-from address if
// previous instruction (i.e., instruction with delay slot, found above) // 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. // does not have a fallthrough and this instruction has a ref or label on it.
if (!instr.hasFallthrough() && if (!instr.hasFallthrough() &&
program.getSymbolTable().hasSymbol(this.getMinAddress())) { program.getSymbolTable().hasSymbol(getMinAddress())) {
return null; return null;
} }
// Return previous instruction's address (i.e., instruction with delay slot, found above) // 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 @Override
public Address getFallThrough() { public Address getFallThrough() {
lock.acquire();
try {
checkIsValid();
if (isFallThroughOverridden()) { if (isFallThroughOverridden()) {
return getFallThroughReference(); return getFallThroughReference();
} }
return getDefaultFallThrough(); return getDefaultFallThrough();
} }
finally {
lock.release();
}
}
@Override @Override
public Address[] getFlows() { public Address[] getFlows() {
refreshIfNeeded(); validate(lock);
Reference[] refs = refMgr.getFlowReferencesFrom(address); Reference[] refs = refMgr.getFlowReferencesFrom(address);
if (refs.length == 0) { if (refs.length == 0) {
return EMPTY_ADDR_ARRAY; return EMPTY_ADDR_ARRAY;
@ -313,6 +309,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Address[] getDefaultFlows() { public Address[] getDefaultFlows() {
validate(lock);
Address[] flows = proto.getFlows(this); Address[] flows = proto.getFlows(this);
if (flowOverride == FlowOverride.RETURN && flows.length == 1) { if (flowOverride == FlowOverride.RETURN && flows.length == 1) {
return EMPTY_ADDR_ARRAY; return EMPTY_ADDR_ARRAY;
@ -322,17 +319,23 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public FlowType getFlowType() { public FlowType getFlowType() {
validate(lock);
return FlowOverride.getModifiedFlowType(proto.getFlowType(this), flowOverride); return FlowOverride.getModifiedFlowType(proto.getFlowType(this), flowOverride);
} }
@Override @Override
public Instruction getNext() { public Instruction getNext() {
refreshIfNeeded(); validate(lock);
return codeMgr.getInstructionAfter(address); return codeMgr.getInstructionAfter(address);
} }
@Override @Override
public RefType getOperandRefType(int opIndex) { public RefType getOperandRefType(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return null;
}
// always reflects current flowOverride // always reflects current flowOverride
lock.acquire(); lock.acquire();
try { try {
@ -346,6 +349,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public String getSeparator(int opIndex) { public String getSeparator(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return null;
}
lock.acquire(); lock.acquire();
try { try {
return proto.getSeparator(opIndex, this); return proto.getSeparator(opIndex, this);
@ -424,12 +432,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Object[] getOpObjects(int opIndex) { public Object[] getOpObjects(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return EMPTY_OBJ_ARRAY;
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
if (opIndex < 0 || opIndex >= getNumOperands()) {
return new Object[0];
}
return proto.getOpObjects(opIndex, this); return proto.getOpObjects(opIndex, this);
} }
finally { finally {
@ -439,7 +449,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Instruction getPrevious() { public Instruction getPrevious() {
refreshIfNeeded(); validate(lock);
return codeMgr.getInstructionBefore(address); return codeMgr.getInstructionBefore(address);
} }
@ -450,12 +460,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Register getRegister(int opIndex) { public Register getRegister(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return null;
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
if (opIndex < 0) {
return null;
}
return proto.getRegister(opIndex, this); return proto.getRegister(opIndex, this);
} }
finally { finally {
@ -494,6 +506,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Address getAddress(int opIndex) { public Address getAddress(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return null;
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
@ -501,9 +518,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (ref != null) { if (ref != null) {
return ref.getToAddress(); return ref.getToAddress();
} }
if (opIndex < 0) {
return null;
}
int opType = proto.getOpType(opIndex, this); int opType = proto.getOpType(opIndex, this);
if (OperandType.isAddress(opType)) { if (OperandType.isAddress(opType)) {
@ -550,10 +565,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public String getMnemonicString() { public String getMnemonicString() {
validate(lock);
String curMnemonic = mnemonicString;
if (curMnemonic != null) {
return curMnemonic;
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
return proto.getMnemonic(this); mnemonicString = proto.getMnemonic(this);
return mnemonicString;
} }
finally { finally {
lock.release(); lock.release();
@ -567,12 +588,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Scalar getScalar(int opIndex) { public Scalar getScalar(int opIndex) {
if (opIndex < 0 || opIndex >= getNumOperands()) {
return null;
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid(); checkIsValid();
if (opIndex < 0) {
return null;
}
return proto.getScalar(opIndex, this); return proto.getScalar(opIndex, this);
} }
finally { finally {
@ -693,6 +716,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public boolean isFallThroughOverridden() { public boolean isFallThroughOverridden() {
validate(lock);
return (flags & FALLTHROUGH_SET_MASK) != 0; return (flags & FALLTHROUGH_SET_MASK) != 0;
} }
@ -705,7 +729,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
if (clearingFallThroughs) { if (clearingFallThroughs) {
return; return;
} }
refreshIfNeeded(); validate(lock);
clearingFallThroughs = true; clearingFallThroughs = true;
try { try {
boolean fallThroughPreserved = false; boolean fallThroughPreserved = false;
@ -894,7 +918,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public boolean isLengthOverridden() { public boolean isLengthOverridden() {
refreshIfNeeded(); validate(lock);
return lengthOverride != 0; return lengthOverride != 0;
} }
@ -911,14 +935,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public Address getDefaultFallThrough() { public Address getDefaultFallThrough() {
lock.acquire(); FlowType myFlowType = getFlowType(); // getFlowType will validate
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()) { if (myFlowType.hasFallthrough()) {
try { try {
return getAddress().addNoWrap(proto.getFallThroughOffset(this)); return address.addNoWrap(getDefaultFallThroughOffset());
} }
catch (AddressOverflowException e) { catch (AddressOverflowException e) {
// ignore // ignore
@ -926,15 +946,15 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
} }
return null; return null;
} }
finally {
lock.release();
}
}
@Override @Override
public int getDefaultFallThroughOffset() { public int getDefaultFallThroughOffset() {
if (proto.getDelaySlotByteCount() <= 0) {
return getLength();
}
lock.acquire(); lock.acquire();
try { try {
checkIsValid();
return proto.getFallThroughOffset(this); return proto.getFallThroughOffset(this);
} }
finally { finally {
@ -944,18 +964,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public boolean hasFallthrough() { public boolean hasFallthrough() {
lock.acquire();
try {
checkIsValid();
if (isFallThroughOverridden()) { if (isFallThroughOverridden()) {
return getFallThrough() != null; // fall-through destination stored as reference return getFallThrough() != null; // fall-through destination stored as reference
} }
return getFlowType().hasFallthrough(); return getFlowType().hasFallthrough();
} }
finally {
lock.release();
}
}
@Override @Override
public boolean isFallthrough() { public boolean isFallthrough() {
@ -977,6 +990,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio
@Override @Override
public ParserContext getParserContext() throws MemoryAccessException { public ParserContext getParserContext() throws MemoryAccessException {
// NOTE: It is assumed this is invoked and used within a locked block
if (parserContext == null) { if (parserContext == null) {
parserContext = proto.getParserContext(this, this); parserContext = proto.getParserContext(this, this);
} }

View file

@ -34,9 +34,10 @@ import ghidra.util.exception.DuplicateNameException;
*/ */
class ArrayDB extends DataTypeDB implements Array { class ArrayDB extends DataTypeDB implements Array {
private volatile String displayName;
private ArrayDBAdapter adapter; private ArrayDBAdapter adapter;
private int elementLength; // lazy initialization
private int elementLength = -1; // lazy initialization
private String displayName; // lazy initialization
/** /**
* Constructor * Constructor

View file

@ -56,11 +56,18 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
this.dataMgr = dataMgr; this.dataMgr = dataMgr;
this.record = record; this.record = record;
this.lock = dataMgr.lock; 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.
* <P>
* NOTE: This must only be invoked while in a locked-state
*/
protected void refreshName() { protected void refreshName() {
name = doGetName(); name = null;
} }
/** /**
@ -144,9 +151,22 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
@Override @Override
public final String getName() { public final String getName() {
validate(lock); String n = name;
if (n != null && !isInvalid()) {
return n;
}
lock.acquire();
try {
checkIsValid();
if (name == null) {
name = doGetName();
}
return name; return name;
} }
finally {
lock.release();
}
}
@Override @Override
public Class<?> getValueClass(Settings settings) { public Class<?> getValueClass(Settings settings) {

View file

@ -46,6 +46,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Function;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.classfinder.ClassTranslator; import ghidra.util.classfinder.ClassTranslator;
import ghidra.util.datastruct.FixedSizeHashMap; import ghidra.util.datastruct.FixedSizeHashMap;
@ -4129,6 +4130,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
* @return calling convention name if found else unknown * @return calling convention name if found else unknown
*/ */
public String getCallingConventionName(byte id) { 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(); lock.acquire();
try { try {
String callingConvention = callingConventionAdapter.getCallingConventionName(id); String callingConvention = callingConventionAdapter.getCallingConventionName(id);

View file

@ -46,11 +46,12 @@ class EnumDB extends DataTypeDB implements Enum {
private EnumDBAdapter adapter; private EnumDBAdapter adapter;
private EnumValueDBAdapter valueAdapter; private EnumValueDBAdapter valueAdapter;
private Map<String, Long> nameMap; // name to value // Lazy fields whose initialization is triggered by nameMap=null (see initializeIfNeeded)
private SortedMap<Long, List<String>> valueMap; // value to names private Map<String, Long> nameMap; // lazy initialization, name to value
private Map<String, String> commentMap; // name to comment private SortedMap<Long, List<String>> valueMap; // lazy initialization, value to names
private List<BitGroup> bitGroups; private Map<String, String> commentMap; // lazy initialization, name to comment
private EnumSignedState signedState = null; private List<BitGroup> bitGroups; // lazy initialization
private EnumSignedState signedState = null; // lazy initialization
EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter, EnumDB(DataTypeManagerDB dataMgr, DBObjectCache<DataTypeDB> cache, EnumDBAdapter adapter,
EnumValueDBAdapter valueAdapter, DBRecord record) { EnumValueDBAdapter valueAdapter, DBRecord record) {

View file

@ -37,7 +37,8 @@ class PointerDB extends DataTypeDB implements Pointer {
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF }; new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private PointerDBAdapter adapter; private PointerDBAdapter adapter;
private String displayName;
private String displayName; // lazy initialization
/** /**
* <code>isEquivalentActive</code> is used to break cyclical recursion when * <code>isEquivalentActive</code> is used to break cyclical recursion when

View file

@ -37,7 +37,7 @@ class StructureDB extends CompositeDB implements StructureInternal {
private int structLength; private int structLength;
private int structAlignment; // reflects stored alignment, -1 if not yet stored 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 int numComponents; // If packed, this does not include the undefined components.
private List<DataTypeComponentDB> components; private List<DataTypeComponentDB> components;
@ -73,7 +73,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL);
structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL); structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL);
computedAlignment = -1; 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); : record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL);
if (oldFlexArrayRecord != null) { if (oldFlexArrayRecord != null) {

View file

@ -35,7 +35,8 @@ import ghidra.util.exception.DuplicateNameException;
class TypedefDB extends DataTypeDB implements TypeDef { class TypedefDB extends DataTypeDB implements TypeDef {
private TypedefDBAdapter adapter; private TypedefDBAdapter adapter;
private SettingsDefinition[] settingsDef;
private SettingsDefinition[] settingsDef; // lazy initialization
/** /**
* Construct TypeDefDB instance * Construct TypeDefDB instance

View file

@ -33,7 +33,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
private int unionLength; private int unionLength;
private int unionAlignment; // reflects stored alignment, -1 if not yet stored 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<DataTypeComponentDB> components; private List<DataTypeComponentDB> components;

View file

@ -120,34 +120,23 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public boolean isThunk() { public boolean isThunk() {
manager.lock.acquire(); validate(manager.lock);
try {
checkIsValid();
return thunkedFunction != null; return thunkedFunction != null;
} }
finally {
manager.lock.release();
}
}
@Override @Override
public Function getThunkedFunction(boolean recursive) { public Function getThunkedFunction(boolean recursive) {
manager.lock.acquire(); validate(manager.lock);
try { FunctionDB localThunkFunc = thunkedFunction;
checkIsValid(); if (!recursive || localThunkFunc == null) {
if (!recursive || thunkedFunction == null) { return localThunkFunc;
return thunkedFunction;
} }
FunctionDB endFunction = thunkedFunction; FunctionDB endFunction = localThunkFunc;
while (endFunction.thunkedFunction != null) { while ((localThunkFunc = endFunction.thunkedFunction) != null) {
endFunction = endFunction.thunkedFunction; endFunction = localThunkFunc;
} }
return endFunction; return endFunction;
} }
finally {
manager.lock.release();
}
}
@Override @Override
public void setThunkedFunction(Function referencedFunction) { public void setThunkedFunction(Function referencedFunction) {
@ -332,15 +321,9 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public Address getEntryPoint() { public Address getEntryPoint() {
manager.lock.acquire(); validate(manager.lock);
try {
checkIsValid();
return entryPoint; return entryPoint;
} }
finally {
manager.lock.release();
}
}
@Override @Override
public AddressSetView getBody() { public AddressSetView getBody() {
@ -378,12 +361,18 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public ReturnParameterDB getReturn() { 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(); manager.lock.acquire();
try { try {
checkIsValid(); // NOTE: lock required to avoid returning null returnParam
if (thunkedFunction != null) {
return thunkedFunction.getReturn();
}
loadVariables(); loadVariables();
return returnParam; return returnParam;
} }
@ -634,33 +623,23 @@ public class FunctionDB extends DatabaseObject implements Function {
*/ */
@Override @Override
public StackFrame getStackFrame() { public StackFrame getStackFrame() {
manager.lock.acquire(); validate(manager.lock);
try { FunctionDB localThunkFunc = thunkedFunction;
checkIsValid(); if (localThunkFunc != null) {
if (thunkedFunction != null) { return thunkedFunction.getStackFrame();
return thunkedFunction.frame;
} }
return frame; return frame;
} }
finally {
manager.lock.release();
}
}
@Override @Override
public int getStackPurgeSize() { public int getStackPurgeSize() {
manager.lock.acquire(); validate(manager.lock);
try { FunctionDB localThunkFunc = thunkedFunction;
checkIsValid(); if (localThunkFunc != null) {
if (thunkedFunction != null) { return localThunkFunc.getStackPurgeSize();
return thunkedFunction.getStackPurgeSize();
} }
return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL); return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL);
} }
finally {
manager.lock.release();
}
}
@Override @Override
public void setStackPurgeSize(int change) { public void setStackPurgeSize(int change) {
@ -693,20 +672,12 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public boolean isStackPurgeSizeValid() { public boolean isStackPurgeSizeValid() {
manager.lock.acquire(); validate(manager.lock);
try { FunctionDB localThunkFunc = thunkedFunction;
checkIsValid(); if (localThunkFunc != null) {
if (thunkedFunction != null) { return localThunkFunc.isStackPurgeSizeValid();
return thunkedFunction.isStackPurgeSizeValid();
}
if (getStackPurgeSize() > 0xffffff) {
return false;
}
return true;
}
finally {
manager.lock.release();
} }
return getStackPurgeSize() <= 0xffffff;
} }
@Override @Override
@ -2417,19 +2388,14 @@ public class FunctionDB extends DatabaseObject implements Function {
* @return true if the indicated flag is set * @return true if the indicated flag is set
*/ */
private boolean isFunctionFlagSet(byte functionFlagIndicator) { private boolean isFunctionFlagSet(byte functionFlagIndicator) {
manager.lock.acquire(); validate(manager.lock);
try { FunctionDB localThunkFunc = thunkedFunction;
checkIsValid(); if (localThunkFunc != null) {
if (thunkedFunction != null) { return localThunkFunc.isFunctionFlagSet(functionFlagIndicator);
return thunkedFunction.isFunctionFlagSet(functionFlagIndicator);
} }
byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL); byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL);
return ((flags & functionFlagIndicator) != 0); return ((flags & functionFlagIndicator) != 0);
} }
finally {
manager.lock.release();
}
}
/** /**
* Sets the indicated function flag to true or false. * Sets the indicated function flag to true or false.
@ -2529,29 +2495,20 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public String getCallingConventionName() { public String getCallingConventionName() {
manager.lock.acquire(); if (!validate(manager.lock)) {
try { return UNKNOWN_CALLING_CONVENTION_STRING;
if (!checkIsValid()) {
return null;
} }
if (thunkedFunction != null) { FunctionDB localThunkFunc = thunkedFunction;
return thunkedFunction.getCallingConventionName(); if (localThunkFunc != null) {
return localThunkFunc.getCallingConventionName();
} }
byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL);
if (callingConventionID == DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID) { // NOTE: If ID is invalid unknown calling convention name will be returned
return Function.UNKNOWN_CALLING_CONVENTION_STRING;
}
if (callingConventionID == DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID) {
return Function.DEFAULT_CALLING_CONVENTION_STRING;
}
return program.getDataTypeManager().getCallingConventionName(callingConventionID); return program.getDataTypeManager().getCallingConventionName(callingConventionID);
} }
finally {
manager.lock.release();
}
}
private String getRealCallingConventionName() { private String getRealCallingConventionName() {
// NOTE: Method only invoked from locked-block
if (thunkedFunction != null) { if (thunkedFunction != null) {
return thunkedFunction.getRealCallingConventionName(); return thunkedFunction.getRealCallingConventionName();
} }
@ -2651,11 +2608,12 @@ public class FunctionDB extends DatabaseObject implements Function {
@Override @Override
public String getCallFixup() { public String getCallFixup() {
manager.lock.acquire(); if (!validate(manager.lock)) {
try { return null;
checkIsValid(); }
if (thunkedFunction != null) { FunctionDB localThunkFunc = thunkedFunction;
return thunkedFunction.getCallFixup(); if (localThunkFunc != null) {
return localThunkFunc.getCallFixup();
} }
StringPropertyMap callFixupMap = manager.getCallFixupMap(false); StringPropertyMap callFixupMap = manager.getCallFixupMap(false);
if (callFixupMap == null) { if (callFixupMap == null) {
@ -2663,10 +2621,6 @@ public class FunctionDB extends DatabaseObject implements Function {
} }
return callFixupMap.getString(entryPoint); return callFixupMap.getString(entryPoint);
} }
finally {
manager.lock.release();
}
}
@Override @Override
public void setCallFixup(String name) { public void setCallFixup(String name) {

View file

@ -81,12 +81,6 @@ public class FunctionManagerDB implements FunctionManager {
return false; 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; private long lastFuncID = -1;
Lock lock; Lock lock;
@ -825,7 +819,6 @@ public class FunctionManagerDB implements FunctionManager {
lock.acquire(); lock.acquire();
try { try {
functionTagManager.invalidateCache(); functionTagManager.invalidateCache();
callFixupMap = null;
lastFuncID = -1; lastFuncID = -1;
cache.invalidate(); cache.invalidate();
} }
@ -835,11 +828,8 @@ public class FunctionManagerDB implements FunctionManager {
} }
StringPropertyMap getCallFixupMap(boolean create) { StringPropertyMap getCallFixupMap(boolean create) {
if (callFixupMap != null) {
return callFixupMap;
}
PropertyMapManager usrPropertyManager = program.getUsrPropertyManager(); PropertyMapManager usrPropertyManager = program.getUsrPropertyManager();
callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP); StringPropertyMap callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP);
if (callFixupMap == null && create) { if (callFixupMap == null && create) {
try { try {
callFixupMap = usrPropertyManager.createStringPropertyMap(CALLFIXUP_MAP); callFixupMap = usrPropertyManager.createStringPropertyMap(CALLFIXUP_MAP);

View file

@ -19,8 +19,8 @@
package ghidra.program.database.properties; package ghidra.program.database.properties;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.*;
import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap;
import db.*; import db.*;
import ghidra.framework.data.OpenMode; import ghidra.framework.data.OpenMode;
@ -49,7 +49,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
private AddressMap addrMap; private AddressMap addrMap;
private ChangeManager changeMgr; private ChangeManager changeMgr;
private PropertiesDBAdapter propertiesDBAdapter; private PropertiesDBAdapter propertiesDBAdapter;
private TreeMap<String, PropertyMapDB<?>> propertyMapCache; private ConcurrentHashMap<String, PropertyMapDB<?>> propertyMapCache;
private Lock lock; private Lock lock;
static final int CURRENT_PROPERTIES_TABLE_VERSION = 0; static final int CURRENT_PROPERTIES_TABLE_VERSION = 0;
@ -70,11 +70,9 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
static final Schema PROPERTIES_SCHEMA; static final Schema PROPERTIES_SCHEMA;
static { static {
PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.INSTANCE, PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.INSTANCE,
"Name", new Field[] { ByteField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE }, "Name", new Field[] { ByteField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE },
new String[] { "Type", "Object Class", "Version" }); new String[] { "Type", "Object Class", "Version" });
} }
/** /**
@ -100,7 +98,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
dbHandle.createTable(PROPERTIES_TABLE_NAME, PROPERTIES_SCHEMA); dbHandle.createTable(PROPERTIES_TABLE_NAME, PROPERTIES_SCHEMA);
} }
findAdapters(handle); findAdapters(handle);
propertyMapCache = new TreeMap<String, PropertyMapDB<?>>(); propertyMapCache = new ConcurrentHashMap<String, PropertyMapDB<?>>();
loadPropertyMaps(openMode, monitor); loadPropertyMaps(openMode, monitor);
} }
@ -119,7 +117,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public void invalidateCache(boolean all) throws IOException { public void invalidateCache(boolean all) throws IOException {
lock.acquire(); lock.acquire();
try { 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); loadPropertyMaps(null, TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
@ -144,12 +146,23 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
throws VersionException, CancelledException { throws VersionException, CancelledException {
try { try {
VersionException ve = null; VersionException ve = null;
Set<String> oldMapNames = new HashSet<>(propertyMapCache.keySet());
RecordIterator iter = propertiesDBAdapter.getRecords(); RecordIterator iter = propertiesDBAdapter.getRecords();
while (iter.hasNext()) { while (iter.hasNext()) {
DBRecord rec = iter.next(); DBRecord rec = iter.next();
String name = rec.getKeyField().getString();
byte propertyType = rec.getByteValue(PROPERTY_TYPE_COL); 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 { try {
switch (propertyType) { switch (propertyType) {
case INT_PROPERTY_TYPE: case INT_PROPERTY_TYPE:
@ -208,6 +221,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
} }
propertyMapCache.put(name, pm); propertyMapCache.put(name, pm);
} }
// Remove obsolete maps from cache
for (String obsoleteMapName : oldMapNames) {
propertyMapCache.remove(obsoleteMapName);
}
if (ve != null) { if (ve != null) {
throw ve; throw ve;
} }
@ -333,7 +352,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally { finally {
lock.release(); lock.release();
} }
} }
@Override @Override
@ -367,7 +385,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally { finally {
lock.release(); lock.release();
} }
} }
/** /**
@ -406,7 +423,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
finally { finally {
lock.release(); lock.release();
} }
} }
/** /**
@ -416,15 +432,8 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public PropertyMap<?> getPropertyMap(String propertyName) { public PropertyMap<?> getPropertyMap(String propertyName) {
lock.acquire();
try {
return propertyMapCache.get(propertyName); return propertyMapCache.get(propertyName);
} }
finally {
lock.release();
}
}
/** /**
* Returns the IntPropertyMap associated with the given name. * Returns the IntPropertyMap associated with the given name.
@ -434,20 +443,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public IntPropertyMap getIntPropertyMap(String propertyName) { public IntPropertyMap getIntPropertyMap(String propertyName) {
lock.acquire();
try {
PropertyMapDB<?> pm = propertyMapCache.get(propertyName); PropertyMapDB<?> pm = propertyMapCache.get(propertyName);
if (pm == null || pm instanceof IntPropertyMap) { if (pm == null || pm instanceof IntPropertyMap) {
return (IntPropertyMap) pm; return (IntPropertyMap) pm;
} }
throw new TypeMismatchException("Property " + propertyName + " is not int type"); throw new TypeMismatchException("Property " + propertyName + " is not int type");
} }
finally {
lock.release();
}
}
/** /**
* Returns the LongPropertyMap associated with the given name. * Returns the LongPropertyMap associated with the given name.
@ -457,19 +458,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public LongPropertyMap getLongPropertyMap(String propertyName) { public LongPropertyMap getLongPropertyMap(String propertyName) {
lock.acquire();
try {
PropertyMapDB<?> pm = propertyMapCache.get(propertyName); PropertyMapDB<?> pm = propertyMapCache.get(propertyName);
if (pm == null || pm instanceof LongPropertyMap) { if (pm == null || pm instanceof LongPropertyMap) {
return (LongPropertyMap) pm; return (LongPropertyMap) pm;
} }
throw new TypeMismatchException("Property " + propertyName + " is not long type"); throw new TypeMismatchException("Property " + propertyName + " is not long type");
} }
finally {
lock.release();
}
}
/** /**
* Returns the StringPropertyMap associated with the given name. * Returns the StringPropertyMap associated with the given name.
@ -479,18 +473,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public StringPropertyMap getStringPropertyMap(String propertyName) { public StringPropertyMap getStringPropertyMap(String propertyName) {
lock.acquire();
try {
PropertyMapDB<?> pm = propertyMapCache.get(propertyName); PropertyMapDB<?> pm = propertyMapCache.get(propertyName);
if (pm == null || pm instanceof StringPropertyMap) { if (pm == null || pm instanceof StringPropertyMap) {
return (StringPropertyMap) pm; return (StringPropertyMap) pm;
} }
throw new TypeMismatchException("Property " + propertyName + " is not String type"); throw new TypeMismatchException("Property " + propertyName + " is not String type");
}
finally {
lock.release();
}
} }
/** /**
@ -501,18 +488,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public ObjectPropertyMap<?> getObjectPropertyMap(String propertyName) { public ObjectPropertyMap<?> getObjectPropertyMap(String propertyName) {
lock.acquire();
try {
PropertyMapDB<?> pm = propertyMapCache.get(propertyName); PropertyMapDB<?> pm = propertyMapCache.get(propertyName);
if (pm == null || pm instanceof ObjectPropertyMap) { if (pm == null || pm instanceof ObjectPropertyMap) {
return (ObjectPropertyMap<?>) pm; return (ObjectPropertyMap<?>) pm;
} }
throw new TypeMismatchException("Property " + propertyName + " is not object type"); throw new TypeMismatchException("Property " + propertyName + " is not object type");
}
finally {
lock.release();
}
} }
/** /**
@ -523,18 +503,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
*/ */
@Override @Override
public VoidPropertyMap getVoidPropertyMap(String propertyName) { public VoidPropertyMap getVoidPropertyMap(String propertyName) {
lock.acquire();
try {
PropertyMapDB<?> pm = propertyMapCache.get(propertyName); PropertyMapDB<?> pm = propertyMapCache.get(propertyName);
if (pm == null || pm instanceof VoidPropertyMap) { if (pm == null || pm instanceof VoidPropertyMap) {
return (VoidPropertyMap) pm; return (VoidPropertyMap) pm;
} }
throw new TypeMismatchException("Property " + propertyName + " is not Void type"); throw new TypeMismatchException("Property " + propertyName + " is not Void type");
} }
finally {
lock.release();
}
}
@Override @Override
public boolean removePropertyMap(String propertyName) { public boolean removePropertyMap(String propertyName) {
@ -564,12 +538,15 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public Iterator<String> propertyManagers() { public Iterator<String> propertyManagers() {
lock.acquire(); lock.acquire();
try { try {
return propertyMapCache.keySet().iterator(); // NOTE: infrequent use expected
return propertyMapCache.keySet()
.stream()
.sorted() // Sort keys in natural order
.iterator();
} }
finally { finally {
lock.release(); lock.release();
} }
} }
@Override @Override
@ -579,7 +556,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
for (PropertyMapDB<?> pm : propertyMapCache.values()) { for (PropertyMapDB<?> pm : propertyMapCache.values()) {
pm.remove(addr); pm.remove(addr);
} }
} }
finally { finally {
lock.release(); lock.release();
@ -621,7 +597,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB {
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
removeAll(startAddr, endAddr, monitor); removeAll(startAddr, endAddr, monitor);
} }
} }

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties; package ghidra.program.database.properties;
import java.io.IOException; import java.io.IOException;
import java.util.function.Function;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPropertyMap { public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPropertyMap {
/**
* A single non-capturing lambda record reader function is used to avoid the possibility of
* multiple synthetic class instantiations.
*/
private Function<Long, Integer> 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. * Construct a integer property map.
* @param dbHandle database handle. * @param dbHandle database handle.
@ -58,27 +77,22 @@ public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPrope
public void add(Address addr, int value) { public void add(Address addr, int value) {
lock.acquire(); lock.acquire();
try { try {
checkDeleted();
Integer oldValue = null; Integer oldValue = null;
long addrKey = addrMap.getKey(addr, true);
long key = addrMap.getKey(addr, true);
if (propertyTable == null) { if (propertyTable == null) {
createTable(IntField.INSTANCE); createTable(IntField.INSTANCE);
} }
else { else {
oldValue = (Integer) cache.get(key); oldValue = cache.get(addrKey);
if (oldValue == null) { if (oldValue == null) {
DBRecord rec = propertyTable.getRecord(key); oldValue = valueReader.apply(addrKey);
if (rec != null) {
oldValue = rec.getIntValue(PROPERTY_VALUE_COL);
} }
} }
} DBRecord rec = schema.createRecord(addrKey);
DBRecord rec = schema.createRecord(key);
rec.setIntValue(PROPERTY_VALUE_COL, value); rec.setIntValue(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec); propertyTable.putRecord(rec);
cache.put(key, value); cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value); changeMgr.setPropertyChanged(name, addr, oldValue, value);
} }
@ -92,44 +106,25 @@ public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPrope
@Override @Override
public int getInt(Address addr) throws NoValueException { public int getInt(Address addr) throws NoValueException {
if (propertyTable == null) { Integer value = get(addr);
if (value == null) {
throw NO_VALUE_EXCEPTION; throw NO_VALUE_EXCEPTION;
} }
return value;
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;
} }
@Override @Override
public Integer get(Address addr) { public Integer get(Address addr) {
try { validate(lock);
return getInt(addr); Table table = propertyTable;
} if (table == null) {
catch (NoValueException e) {
return null; return null;
} }
long addrKey = addrMap.getKey(addr, false);
if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
return null;
}
return cache.computeIfAbsent(addrKey, valueReader);
} }
} }

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties; package ghidra.program.database.properties;
import java.io.IOException; import java.io.IOException;
import java.util.function.Function;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class LongPropertyMapDB extends PropertyMapDB<Long> implements LongPropertyMap { public class LongPropertyMapDB extends PropertyMapDB<Long> 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<Long, Long> 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. * Construct a long property map.
* @param dbHandle database handle. * @param dbHandle database handle.
@ -56,26 +75,24 @@ public class LongPropertyMapDB extends PropertyMapDB<Long> implements LongProper
@Override @Override
public void add(Address addr, long value) { public void add(Address addr, long value) {
Long oldValue = null;
lock.acquire(); lock.acquire();
try { try {
long key = addrMap.getKey(addr, true); checkDeleted();
Long oldValue = null;
long addrKey = addrMap.getKey(addr, true);
if (propertyTable == null) { if (propertyTable == null) {
createTable(LongField.INSTANCE); createTable(LongField.INSTANCE);
} }
else { else {
oldValue = (Long) cache.get(key); oldValue = cache.get(addrKey);
if (oldValue == null) { if (oldValue == null) {
DBRecord rec = propertyTable.getRecord(key); oldValue = valueReader.apply(addrKey);
if (rec != null) {
oldValue = rec.getLongValue(PROPERTY_VALUE_COL);
} }
} }
} DBRecord rec = schema.createRecord(addrKey);
DBRecord rec = schema.createRecord(key);
rec.setLongValue(PROPERTY_VALUE_COL, value); rec.setLongValue(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec); propertyTable.putRecord(rec);
cache.put(key, value); cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value); changeMgr.setPropertyChanged(name, addr, oldValue, value);
} }
catch (IOException e) { catch (IOException e) {
@ -89,44 +106,25 @@ public class LongPropertyMapDB extends PropertyMapDB<Long> implements LongProper
@Override @Override
public long getLong(Address addr) throws NoValueException { public long getLong(Address addr) throws NoValueException {
if (propertyTable == null) { Long value = get(addr);
if (value == null) {
throw NO_VALUE_EXCEPTION; throw NO_VALUE_EXCEPTION;
} }
return value;
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;
} }
@Override @Override
public Long get(Address addr) { public Long get(Address addr) {
try { validate(lock);
return getLong(addr); Table table = propertyTable;
} if (table == null) {
catch (NoValueException e) {
return null; return null;
} }
long addrKey = addrMap.getKey(addr, false);
if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
return null;
}
return cache.computeIfAbsent(addrKey, valueReader);
} }
} }

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties; package ghidra.program.database.properties;
import java.io.IOException; import java.io.IOException;
import java.util.function.Function;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
@ -42,6 +43,44 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
private int saveableObjectVersion; private int saveableObjectVersion;
private boolean supportsPrivate; 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<Long, T> 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. * Construct an Saveable object property map.
* @param dbHandle database handle. * @param dbHandle database handle.
@ -256,11 +295,19 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
public void add(Address addr, T value) { public void add(Address addr, T value) {
lock.acquire(); lock.acquire();
try { try {
checkDeleted();
if (!saveableObjectClass.isAssignableFrom(value.getClass())) { if (!saveableObjectClass.isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("value is not " + saveableObjectClass.getName()); throw new IllegalArgumentException("value is not " + saveableObjectClass.getName());
} }
long key = addrMap.getKey(addr, true); long addrKey = addrMap.getKey(addr, true);
T oldValue = get(addr);
T oldValue = null;
if (propertyTable != null) {
oldValue = cache.get(addrKey);
if (oldValue == null) {
oldValue = valueReader.apply(addrKey);
}
}
String tableName = getTableName(); String tableName = getTableName();
Schema s; Schema s;
@ -271,7 +318,7 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
s = objStorage.getSchema(value.getSchemaVersion()); s = objStorage.getSchema(value.getSchemaVersion());
checkSchema(s); checkSchema(s);
createPropertyTable(tableName, s); createPropertyTable(tableName, s);
rec = schema.createRecord(key); rec = schema.createRecord(addrKey);
objStorage.save(rec); objStorage.save(rec);
} }
else { // GenericSaveable else { // GenericSaveable
@ -281,11 +328,11 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
checkSchema(s); checkSchema(s);
createPropertyTable(tableName, s); createPropertyTable(tableName, s);
rec = originalRec.copy(); rec = originalRec.copy();
rec.setKey(key); rec.setKey(addrKey);
} }
propertyTable.putRecord(rec); propertyTable.putRecord(rec);
cache.put(key, value); cache.put(addrKey, value);
if (!isPrivate(value)) { if (!isPrivate(value)) {
changeMgr.setPropertyChanged(name, addr, oldValue, value); changeMgr.setPropertyChanged(name, addr, oldValue, value);
@ -326,57 +373,18 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
return saveableObjectClass; return saveableObjectClass;
} }
@SuppressWarnings("unchecked")
@Override @Override
public T get(Address addr) { public T get(Address addr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
long addrKey = addrMap.getKey(addr, false);
T obj = null; if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
lock.acquire();
try {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY) {
return null; return null;
} }
obj = (T) cache.get(key); return cache.computeIfAbsent(addrKey, valueReader);
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);
}
}
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;
} }
/** /**

View file

@ -21,6 +21,7 @@ import java.util.NoSuchElementException;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
import ghidra.framework.data.OpenMode; import ghidra.framework.data.OpenMode;
import ghidra.program.database.DatabaseObject;
import ghidra.program.database.map.*; import ghidra.program.database.map.*;
import ghidra.program.database.util.DatabaseTableUtils; import ghidra.program.database.util.DatabaseTableUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -37,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
* The map is stored within a database table. * The map is stored within a database table.
* @param <T> property value type * @param <T> property value type
*/ */
public abstract class PropertyMapDB<T> implements PropertyMap<T> { public abstract class PropertyMapDB<T> extends DatabaseObject implements PropertyMap<T> {
private static final String PROPERTY_TABLE_PREFIX = "Property Map - "; private static final String PROPERTY_TABLE_PREFIX = "Property Map - ";
@ -63,7 +64,7 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
protected AddressMap addrMap; protected AddressMap addrMap;
protected String name; protected String name;
protected ObjectCache cache = new ObjectCache(DEFAULT_CACHE_SIZE); protected ObjectCache<T> cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
protected Lock lock; protected Lock lock;
/** /**
@ -76,6 +77,7 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
*/ */
PropertyMapDB(DBHandle dbHandle, ErrorHandler errHandler, ChangeManager changeMgr, PropertyMapDB(DBHandle dbHandle, ErrorHandler errHandler, ChangeManager changeMgr,
AddressMap addrMap, String name) { AddressMap addrMap, String name) {
super(null, 0); // DatabaseObject cache is not used
this.dbHandle = dbHandle; this.dbHandle = dbHandle;
this.errHandler = errHandler; this.errHandler = errHandler;
@ -92,6 +94,12 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
} }
} }
// 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. * 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 * @param openMode the mode that the program was openned in or null if instantiated during
@ -180,22 +188,24 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
/** /**
* Create the default propertyTable. * Create the default propertyTable.
* This method may be called by add property methods if propertyTable * This method may be called by add property methods if propertyTable is null.
* is null. * @param valueField property value field type (null corresponds to void map table)
* @param valueField property value field type
* @throws IOException if IO error occurs * @throws IOException if IO error occurs
*/ */
protected void createTable(Field valueField) throws IOException { protected void createTable(Field valueField) throws IOException {
schema = getTableSchema(valueField);
propertyTable = dbHandle.createTable(getTableName(), schema);
}
private static Schema getTableSchema(Field valueField) {
if (valueField != null) { if (valueField != null) {
// Create default table schema with a value column and an long Address key // Create default table schema with a value column and an long Address key
Field[] fields = new Field[] { valueField }; 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 // Table contains only a long Address key (i.e., void property map)
schema = new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES); return new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES);
}
propertyTable = dbHandle.createTable(getTableName(), schema);
} }
@Override @Override
@ -227,8 +237,10 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
public void delete() throws IOException { public void delete() throws IOException {
lock.acquire(); lock.acquire();
try { try {
checkDeleted();
setDeleted();
if (propertyTable != null) { if (propertyTable != null) {
cache = null; cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
dbHandle.deleteTable(getTableName()); dbHandle.deleteTable(getTableName());
propertyTable = null; propertyTable = null;
} }
@ -240,12 +252,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public boolean intersects(Address startAddr, Address endAddr) { public boolean intersects(Address startAddr, Address endAddr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return false; return false;
} }
try { try {
AddressKeyIterator iter = AddressKeyIterator iter =
new AddressKeyIterator(propertyTable, addrMap, startAddr, endAddr, startAddr, true); new AddressKeyIterator(table, addrMap, startAddr, endAddr, startAddr, true);
return iter.hasNext(); return iter.hasNext();
} }
catch (IOException e) { catch (IOException e) {
@ -256,12 +270,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public boolean intersects(AddressSetView set) { public boolean intersects(AddressSetView set) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return false; return false;
} }
try { try {
AddressKeyIterator iter = AddressKeyIterator iter =
new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true); new AddressKeyIterator(table, addrMap, set, set.getMinAddress(), true);
return iter.hasNext(); return iter.hasNext();
} }
catch (IOException e) { catch (IOException e) {
@ -272,13 +288,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public boolean removeRange(Address startAddr, Address endAddr) { public boolean removeRange(Address startAddr, Address endAddr) {
lock.acquire();
try {
checkDeleted();
if (propertyTable == null) { if (propertyTable == null) {
return false; return false;
} }
lock.acquire();
try {
if (AddressRecordDeleter.deleteRecords(propertyTable, addrMap, startAddr, endAddr)) { if (AddressRecordDeleter.deleteRecords(propertyTable, addrMap, startAddr, endAddr)) {
cache = new ObjectCache(DEFAULT_CACHE_SIZE); cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
return true; return true;
} }
} }
@ -293,15 +310,16 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public boolean remove(Address addr) { public boolean remove(Address addr) {
if (propertyTable == null) {
return false;
}
lock.acquire(); lock.acquire();
boolean result = false; boolean result = false;
try { try {
long key = addrMap.getKey(addr, false); checkDeleted();
cache.remove(key); if (propertyTable == null) {
result = propertyTable.deleteRecord(key); return false;
}
long addrKey = addrMap.getKey(addr, false);
cache.remove(addrKey);
result = propertyTable.deleteRecord(addrKey);
changeMgr.setPropertyChanged(name, addr, null, null); changeMgr.setPropertyChanged(name, addr, null, null);
} }
catch (IOException e) { catch (IOException e) {
@ -315,33 +333,32 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public boolean hasProperty(Address addr) { public boolean hasProperty(Address addr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return false; return false;
} }
lock.acquire();
boolean result = false;
try { try {
long key = addrMap.getKey(addr, false); long addrKey = addrMap.getKey(addr, false);
if (key != AddressMap.INVALID_ADDRESS_KEY) { if (addrKey != AddressMap.INVALID_ADDRESS_KEY) {
result = cache.contains(key) || propertyTable.hasRecord(key); return cache.contains(addrKey) || table.hasRecord(addrKey);
} }
} }
catch (IOException e) { catch (IOException e) {
errHandler.dbError(e); errHandler.dbError(e);
} }
finally { return false;
lock.release();
}
return result;
} }
@Override @Override
public Address getNextPropertyAddress(Address addr) { public Address getNextPropertyAddress(Address addr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
try { try {
AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, false); AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, false);
return addrMap.decodeAddress(iter.next()); return addrMap.decodeAddress(iter.next());
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
@ -355,11 +372,13 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public Address getPreviousPropertyAddress(Address addr) { public Address getPreviousPropertyAddress(Address addr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
try { try {
AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, true); AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, true);
return addrMap.decodeAddress(iter.previous()); return addrMap.decodeAddress(iter.previous());
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
@ -373,11 +392,13 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public Address getFirstPropertyAddress() { public Address getFirstPropertyAddress() {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
try { try {
AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, true); AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, true);
return addrMap.decodeAddress(iter.next()); return addrMap.decodeAddress(iter.next());
} }
catch (NoSuchElementException e) { catch (NoSuchElementException e) {
@ -391,11 +412,13 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public Address getLastPropertyAddress() { public Address getLastPropertyAddress() {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
try { try {
AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, AddressKeyIterator iter = new AddressKeyIterator(table, addrMap,
addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false); addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false);
return addrMap.decodeAddress(iter.previous()); return addrMap.decodeAddress(iter.previous());
} }
@ -410,7 +433,9 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public int getSize() { 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<T> implements PropertyMap<T> {
*/ */
public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart) public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart)
throws IOException { 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; return AddressKeyIterator.EMPTY_ITERATOR;
} }
if (atStart) { 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<T> implements PropertyMap<T> {
*/ */
public AddressKeyIterator getAddressKeyIterator(Address start, boolean before) public AddressKeyIterator getAddressKeyIterator(Address start, boolean before)
throws IOException { throws IOException {
validate(lock);
if (propertyTable == null) { Table table = propertyTable;
if (table == null) {
return AddressKeyIterator.EMPTY_ITERATOR; 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<T> implements PropertyMap<T> {
*/ */
public AddressKeyIterator getAddressKeyIterator(Address start, Address end, boolean atStart) public AddressKeyIterator getAddressKeyIterator(Address start, Address end, boolean atStart)
throws IOException { throws IOException {
validate(lock);
if (propertyTable == null) { Table table = propertyTable;
if (table == null) {
return AddressKeyIterator.EMPTY_ITERATOR; return AddressKeyIterator.EMPTY_ITERATOR;
} }
if (atStart) { 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 @Override
@ -495,12 +524,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public AddressIterator getPropertyIterator() { public AddressIterator getPropertyIterator() {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return new EmptyAddressIterator(); return new EmptyAddressIterator();
} }
AddressKeyIterator keyIter = null; AddressKeyIterator keyIter = null;
try { try {
keyIter = new AddressKeyIterator(propertyTable, addrMap, true); keyIter = new AddressKeyIterator(table, addrMap, true);
} }
catch (IOException e) { catch (IOException e) {
errHandler.dbError(e); errHandler.dbError(e);
@ -510,13 +541,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public AddressIterator getPropertyIterator(AddressSetView asv) { public AddressIterator getPropertyIterator(AddressSetView asv) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return new EmptyAddressIterator(); return new EmptyAddressIterator();
} }
AddressKeyIterator keyIter = null; AddressKeyIterator keyIter = null;
try { try {
keyIter = keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true);
new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true);
} }
catch (IOException e) { catch (IOException e) {
errHandler.dbError(e); errHandler.dbError(e);
@ -526,18 +558,18 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward) { 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; return AddressIterator.EMPTY_ITERATOR;
} }
AddressKeyIterator keyIter = null; AddressKeyIterator keyIter = null;
try { try {
if (forward) { if (forward) {
keyIter = keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true);
new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true);
} }
else { else {
keyIter = keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMaxAddress(), false);
new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMaxAddress(), false);
} }
} }
catch (IOException e) { catch (IOException e) {
@ -548,12 +580,14 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
@Override @Override
public AddressIterator getPropertyIterator(Address start, boolean forward) { public AddressIterator getPropertyIterator(Address start, boolean forward) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return new EmptyAddressIterator(); return new EmptyAddressIterator();
} }
AddressKeyIterator keyIter = null; AddressKeyIterator keyIter = null;
try { try {
keyIter = new AddressKeyIterator(propertyTable, addrMap, start, forward); keyIter = new AddressKeyIterator(table, addrMap, start, forward);
} }
catch (IOException e) { catch (IOException e) {
errHandler.dbError(e); errHandler.dbError(e);
@ -564,23 +598,36 @@ public abstract class PropertyMapDB<T> implements PropertyMap<T> {
/** /**
* Invalidates the cache. * Invalidates the cache.
*/ */
public void invalidateCache() { public void invalidate() {
lock.acquire(); lock.acquire();
try { try {
propertyTable = dbHandle.getTable(getTableName()); setInvalid();
cache = new ObjectCache(DEFAULT_CACHE_SIZE);
} }
finally { finally {
lock.release(); 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 @Override
public void moveRange(Address start, Address end, Address newStart) { public void moveRange(Address start, Address end, Address newStart) {
lock.acquire(); lock.acquire();
try { try {
cache = new ObjectCache(DEFAULT_CACHE_SIZE); checkDeleted();
cache = new ObjectCache<>(DEFAULT_CACHE_SIZE);
if (propertyTable != null) { if (propertyTable != null) {
try { try {
DatabaseTableUtils.updateAddressKey(propertyTable, addrMap, start, end, DatabaseTableUtils.updateAddressKey(propertyTable, addrMap, start, end,

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties; package ghidra.program.database.properties;
import java.io.IOException; import java.io.IOException;
import java.util.function.Function;
import db.*; import db.*;
import db.util.ErrorHandler; import db.util.ErrorHandler;
@ -34,6 +35,24 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class StringPropertyMapDB extends PropertyMapDB<String> implements StringPropertyMap { public class StringPropertyMapDB extends PropertyMapDB<String> implements StringPropertyMap {
/**
* A single non-capturing lambda record reader function is used to avoid the possibility of
* multiple synthetic class instantiations.
*/
private Function<Long, String> 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. * Construct an String property map.
* @param dbHandle database handle. * @param dbHandle database handle.
@ -59,25 +78,24 @@ public class StringPropertyMapDB extends PropertyMapDB<String> implements String
public void add(Address addr, String value) { public void add(Address addr, String value) {
lock.acquire(); lock.acquire();
try { try {
long key = addrMap.getKey(addr, true); checkDeleted();
long addrKey = addrMap.getKey(addr, true);
String oldValue = null; String oldValue = null;
if (propertyTable == null) { if (propertyTable == null) {
createTable(StringField.INSTANCE); createTable(StringField.INSTANCE);
} }
else { else {
oldValue = (String) cache.get(key); oldValue = cache.get(addrKey);
if (oldValue == null) { if (oldValue == null) {
DBRecord rec = propertyTable.getRecord(key); oldValue = valueReader.apply(addrKey);
if (rec != null) {
oldValue = rec.getString(PROPERTY_VALUE_COL);
} }
} }
} DBRecord rec = schema.createRecord(addrKey);
DBRecord rec = schema.createRecord(key);
rec.setString(PROPERTY_VALUE_COL, value); rec.setString(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec); propertyTable.putRecord(rec);
cache.put(key, value); cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value); changeMgr.setPropertyChanged(name, addr, oldValue, value);
} }
catch (IOException e) { catch (IOException e) {
@ -91,38 +109,16 @@ public class StringPropertyMapDB extends PropertyMapDB<String> implements String
@Override @Override
public String getString(Address addr) { public String getString(Address addr) {
if (propertyTable == null) { validate(lock);
Table table = propertyTable;
if (table == null) {
return null; return null;
} }
long addrKey = addrMap.getKey(addr, false);
String str = null; if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
lock.acquire();
try {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY) {
return null; return null;
} }
str = (String) cache.get(key); return cache.computeIfAbsent(addrKey, valueReader);
if (str != null) {
return str;
}
DBRecord rec = propertyTable.getRecord(key);
if (rec == null) {
return null;
}
str = rec.getString(PROPERTY_VALUE_COL);
}
catch (IOException e) {
errHandler.dbError(e);
}
finally {
lock.release();
}
return str;
} }
@Override @Override

View file

@ -37,7 +37,7 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class VoidPropertyMapDB extends PropertyMapDB<Boolean> implements VoidPropertyMap { public class VoidPropertyMapDB extends PropertyMapDB<Boolean> implements VoidPropertyMap {
private static Object VOID_OBJECT = new Object(); private static Boolean TRUE = true;
/** /**
* Construct an void object property map. * Construct an void object property map.
@ -64,15 +64,17 @@ public class VoidPropertyMapDB extends PropertyMapDB<Boolean> implements VoidPro
public void add(Address addr) { public void add(Address addr) {
lock.acquire(); lock.acquire();
try { try {
long key = addrMap.getKey(addr, true); checkDeleted();
long addrKey = addrMap.getKey(addr, true);
Boolean oldValue = hasProperty(addr); Boolean oldValue = hasProperty(addr);
if (propertyTable == null) { if (propertyTable == null) {
createTable(null); createTable(null);
} }
DBRecord rec = schema.createRecord(key); DBRecord rec = schema.createRecord(addrKey);
propertyTable.putRecord(rec); propertyTable.putRecord(rec);
cache.put(key, VOID_OBJECT); cache.put(addrKey, TRUE);
changeMgr.setPropertyChanged(name, addr, oldValue, true); changeMgr.setPropertyChanged(name, addr, oldValue, true);
} }
catch (IOException e) { catch (IOException e) {

View file

@ -56,19 +56,11 @@ public class GenericAddress implements Address {
this.addrSpace = addrSpace; this.addrSpace = addrSpace;
} }
/**
*
* @see ghidra.program.model.address.Address#getAddress(java.lang.String)
*/
@Override @Override
public Address getAddress(String addrString) throws AddressFormatException { public Address getAddress(String addrString) throws AddressFormatException {
return addrSpace.getAddress(addrString); return addrSpace.getAddress(addrString);
} }
/**
*
* @see ghidra.program.model.address.Address#getNewAddress(long)
*/
@Override @Override
public Address getNewAddress(long byteOffset) { public Address getNewAddress(long byteOffset) {
return addrSpace.getAddress(byteOffset); return addrSpace.getAddress(byteOffset);
@ -86,10 +78,6 @@ public class GenericAddress implements Address {
return addrSpace.getTruncatedAddress(addrOffset, isAddressableWordOffset); return addrSpace.getTruncatedAddress(addrOffset, isAddressableWordOffset);
} }
/**
*
* @see ghidra.program.model.address.Address#getOffset()
*/
@Override @Override
public long getOffset() { public long getOffset() {
return offset; return offset;
@ -100,9 +88,6 @@ public class GenericAddress implements Address {
return addrSpace.getAddressableWordOffset(offset); return addrSpace.getAddressableWordOffset(offset);
} }
/**
* @see ghidra.program.model.address.Address#getUnsignedOffset()
*/
@Override @Override
public long getUnsignedOffset() { public long getUnsignedOffset() {
// TODO: Validity of offset within space is not verified // TODO: Validity of offset within space is not verified
@ -117,37 +102,21 @@ public class GenericAddress implements Address {
return spaceSize + offset; return spaceSize + offset;
} }
/**
*
* @see ghidra.program.model.address.Address#getAddressSpace()
*/
@Override @Override
public AddressSpace getAddressSpace() { public AddressSpace getAddressSpace() {
return addrSpace; return addrSpace;
} }
/**
*
* @see ghidra.program.model.address.Address#getSize()
*/
@Override @Override
public int getSize() { public int getSize() {
return addrSpace.getSize(); return addrSpace.getSize();
} }
/**
*
* @see ghidra.program.model.address.Address#subtract(ghidra.program.model.address.Address)
*/
@Override @Override
public long subtract(Address addr) { public long subtract(Address addr) {
return addrSpace.subtract(this, addr); return addrSpace.subtract(this, addr);
} }
/**
*
* @see ghidra.program.model.address.Address#subtractWrap(long)
*/
@Override @Override
public Address subtractWrap(long displacement) { public Address subtractWrap(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -155,9 +124,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrap(this, displacement); return addrSpace.subtractWrap(this, displacement);
} }
/**
* @see ghidra.program.model.address.Address#subtractWrapSpace(long)
*/
@Override @Override
public Address subtractWrapSpace(long displacement) { public Address subtractWrapSpace(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -165,10 +131,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrapSpace(this, displacement); return addrSpace.subtractWrapSpace(this, displacement);
} }
/**
*
* @see ghidra.program.model.address.Address#subtractNoWrap(long)
*/
@Override @Override
public Address subtractNoWrap(long displacement) throws AddressOverflowException { public Address subtractNoWrap(long displacement) throws AddressOverflowException {
if (displacement == 0) if (displacement == 0)
@ -176,10 +138,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractNoWrap(this, displacement); return addrSpace.subtractNoWrap(this, displacement);
} }
/**
*
* @see ghidra.program.model.address.Address#subtract(long)
*/
@Override @Override
public Address subtract(long displacement) { public Address subtract(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -187,9 +145,6 @@ public class GenericAddress implements Address {
return addrSpace.subtract(this, displacement); return addrSpace.subtract(this, displacement);
} }
/**
* @see ghidra.program.model.address.Address#addWrap(long)
*/
@Override @Override
public Address addWrap(long displacement) { public Address addWrap(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -197,9 +152,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrap(this, displacement); return addrSpace.addWrap(this, displacement);
} }
/**
* @see ghidra.program.model.address.Address#addWrapSpace(long)
*/
@Override @Override
public Address addWrapSpace(long displacement) { public Address addWrapSpace(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -207,10 +159,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrapSpace(this, displacement); return addrSpace.addWrapSpace(this, displacement);
} }
/**
*
* @see ghidra.program.model.address.Address#addNoWrap(long)
*/
@Override @Override
public Address addNoWrap(long displacement) throws AddressOverflowException { public Address addNoWrap(long displacement) throws AddressOverflowException {
if (displacement == 0) if (displacement == 0)
@ -226,10 +174,6 @@ public class GenericAddress implements Address {
return addrSpace.addNoWrap(this, displacement); return addrSpace.addNoWrap(this, displacement);
} }
/**
*
* @see ghidra.program.model.address.Address#add(long)
*/
@Override @Override
public Address add(long displacement) { public Address add(long displacement) {
if (displacement == 0) if (displacement == 0)
@ -237,10 +181,6 @@ public class GenericAddress implements Address {
return addrSpace.add(this, displacement); return addrSpace.add(this, displacement);
} }
/**
*
* @see ghidra.program.model.address.Address#isSuccessor(ghidra.program.model.address.Address)
*/
@Override @Override
public boolean isSuccessor(Address addr) { public boolean isSuccessor(Address addr) {
return addrSpace.isSuccessor(this, addr); return addrSpace.isSuccessor(this, addr);
@ -259,26 +199,17 @@ public class GenericAddress implements Address {
return Long.compareUnsigned(offset, otherOffset); return Long.compareUnsigned(offset, otherOffset);
} }
/**
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
return true; return true;
} }
if (!(o instanceof GenericAddress)) { if (!(o instanceof GenericAddress addr)) {
return false; return false;
} }
GenericAddress addr = (GenericAddress) o; return offset == addr.offset && addrSpace.equals(addr.getAddressSpace());
return addrSpace.equals(addr.getAddressSpace()) && offset == addr.offset;
} }
/**
*
* @see java.lang.Object#hashCode()
*/
@Override @Override
public int hashCode() { public int hashCode() {
int hash1 = addrSpace.hashCode(); int hash1 = addrSpace.hashCode();
@ -286,18 +217,11 @@ public class GenericAddress implements Address {
return (hash1 << 16) ^ hash3; return (hash1 << 16) ^ hash3;
} }
/**
* @see java.lang.Object#toString()
*/
@Override @Override
public String toString() { public String toString() {
return toString(addrSpace.showSpaceName(), MINIMUM_DIGITS); return toString(addrSpace.showSpaceName(), MINIMUM_DIGITS);
} }
/**
*
* @see ghidra.program.model.address.Address#toString(java.lang.String)
*/
@Override @Override
public String toString(String prefix) { public String toString(String prefix) {
boolean showSpace = prefix.length() == 0 && addrSpace.showSpaceName(); boolean showSpace = prefix.length() == 0 && addrSpace.showSpaceName();
@ -368,19 +292,11 @@ public class GenericAddress implements Address {
return buf.toString(); return buf.toString();
} }
/**
*
* @see ghidra.program.model.address.Address#hasSameAddressSpace(ghidra.program.model.address.Address)
*/
@Override @Override
public boolean hasSameAddressSpace(Address addr) { public boolean hasSameAddressSpace(Address addr) {
return addrSpace.equals(addr.getAddressSpace()); return addrSpace.equals(addr.getAddressSpace());
} }
/**
*
* @see ghidra.program.model.address.Address#next()
*/
@Override @Override
public Address next() { public Address next() {
if (addrSpace.getMaxAddress().getOffset() == offset) { if (addrSpace.getMaxAddress().getOffset() == offset) {
@ -389,9 +305,6 @@ public class GenericAddress implements Address {
return addrSpace.addWrap(this, 1); return addrSpace.addWrap(this, 1);
} }
/**
* @see ghidra.program.model.address.Address#previous()
*/
@Override @Override
public Address previous() { public Address previous() {
if (addrSpace.getMinAddress().getOffset() == offset) { if (addrSpace.getMinAddress().getOffset() == offset) {
@ -400,9 +313,6 @@ public class GenericAddress implements Address {
return addrSpace.subtractWrap(this, 1); return addrSpace.subtractWrap(this, 1);
} }
/**
* @see ghidra.program.model.address.Address#getPhysicalAddress()
*/
@Override @Override
public Address getPhysicalAddress() { public Address getPhysicalAddress() {
AddressSpace physical = addrSpace.getPhysicalSpace(); AddressSpace physical = addrSpace.getPhysicalSpace();
@ -415,17 +325,11 @@ public class GenericAddress implements Address {
return null; return null;
} }
/**
* @see ghidra.program.model.address.Address#getPointerSize()
*/
@Override @Override
public int getPointerSize() { public int getPointerSize() {
return addrSpace.getPointerSize(); return addrSpace.getPointerSize();
} }
/**
* @see ghidra.program.model.address.Address#isMemoryAddress()
*/
@Override @Override
public boolean isMemoryAddress() { public boolean isMemoryAddress() {
return addrSpace.isMemorySpace(); return addrSpace.isMemorySpace();
@ -441,57 +345,36 @@ public class GenericAddress implements Address {
return addrSpace.isNonLoadedMemorySpace(); return addrSpace.isNonLoadedMemorySpace();
} }
/**
* @see ghidra.program.model.address.Address#isHashAddress()
*/
@Override @Override
public boolean isHashAddress() { public boolean isHashAddress() {
return addrSpace.isHashSpace(); return addrSpace.isHashSpace();
} }
/**
* @see ghidra.program.model.address.Address#isStackAddress()
*/
@Override @Override
public boolean isStackAddress() { public boolean isStackAddress() {
return addrSpace.isStackSpace(); return addrSpace.isStackSpace();
} }
/**
* @see ghidra.program.model.address.Address#isUniqueAddress()
*/
@Override @Override
public boolean isUniqueAddress() { public boolean isUniqueAddress() {
return addrSpace.isUniqueSpace(); return addrSpace.isUniqueSpace();
} }
/**
* @see ghidra.program.model.address.Address#isConstantAddress()
*/
@Override @Override
public boolean isConstantAddress() { public boolean isConstantAddress() {
return addrSpace.isConstantSpace(); return addrSpace.isConstantSpace();
} }
/**
* @see ghidra.program.model.address.Address#isVariableAddress()
*/
@Override @Override
public boolean isVariableAddress() { public boolean isVariableAddress() {
return addrSpace.isVariableSpace(); return addrSpace.isVariableSpace();
} }
/**
* @see ghidra.program.model.address.Address#isRegisterAddress()
*/
@Override @Override
public boolean isRegisterAddress() { public boolean isRegisterAddress() {
return addrSpace.isRegisterSpace(); return addrSpace.isRegisterSpace();
} }
/**
* @see ghidra.program.model.address.Address#isExternalAddress()
*/
@Override @Override
public boolean isExternalAddress() { public boolean isExternalAddress() {
return addrSpace.isExternalSpace(); return addrSpace.isExternalSpace();

View file

@ -141,7 +141,7 @@ public interface PropertyMapManager {
public boolean removePropertyMap(String propertyName); 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<String> propertyManagers(); public Iterator<String> propertyManagers();