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.WeakReference;
import java.util.*;
import java.util.function.Function;
/**
* <code>ObjectClass</code> provides a fixed-size long-key-based object cache.
@ -27,12 +28,14 @@ import java.util.*;
* <p>
* The weak cache is keyed, while the hard cache simply maintains the presence of
* 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 ReferenceQueue<Object> refQueue;
private LinkedList<Object> hardCache;
private Map<Long, KeyedSoftReference<T>> hashTable;
private ReferenceQueue<T> refQueue;
private LinkedList<T> hardCache;
private int hardCacheSize;
/**
@ -61,10 +64,10 @@ public class ObjectCache {
* @param key object key
* @return cached object
*/
public synchronized Object get(long key) {
WeakReference<?> ref = hashTable.get(key);
public synchronized T get(long key) {
WeakReference<T> ref = hashTable.get(key);
if (ref != null) {
Object obj = ref.get();
T obj = ref.get();
if (obj == null) {
hashTable.remove(key);
}
@ -74,6 +77,30 @@ public class ObjectCache {
return null;
}
/**
* Get the current cached object which corresponds to specified {@code key} if contained in
* cache, otherwise the {@code mappingFunction} will be invoked to instantiate a new object
* where that object will be added to the cache and returned. If the {@code mappingFunction}
* returns null nothing will be added to the cache and null will be returned by this method.
*
* @param key object key
* @param mappingFunction function used to obtain a new object if not currently present
* in cache.
* @return cached object
*/
public synchronized T computeIfAbsent(long key, Function<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
@ -98,9 +125,9 @@ public class ObjectCache {
* @param key object key
* @param obj the object
*/
public synchronized void put(long key, Object obj) {
public synchronized void put(long key, T obj) {
processQueue();
KeyedSoftReference<?> ref = new KeyedSoftReference<>(key, obj, refQueue);
KeyedSoftReference<T> ref = new KeyedSoftReference<>(key, obj, refQueue);
hashTable.put(key, ref);
addToHardCache(obj);
}
@ -112,7 +139,7 @@ public class ObjectCache {
*/
public synchronized void remove(long key) {
processQueue();
KeyedSoftReference<?> ref = hashTable.get(key);
KeyedSoftReference<T> ref = hashTable.get(key);
if (ref != null) {
ref.clear();
hashTable.remove(key);
@ -123,7 +150,7 @@ public class ObjectCache {
* Add the specified object to the hard cache.
* @param obj object
*/
private void addToHardCache(Object obj) {
private void addToHardCache(T obj) {
hardCache.addLast(obj);
if (hardCache.size() > hardCacheSize) {
hardCache.removeFirst();
@ -134,8 +161,8 @@ public class ObjectCache {
* Cleanup weak cache
*/
private void processQueue() {
KeyedSoftReference<?> ref;
while ((ref = (KeyedSoftReference<?>) refQueue.poll()) != null) {
KeyedSoftReference<? extends T> ref;
while ((ref = (KeyedSoftReference<? extends T>) refQueue.poll()) != null) {
hashTable.remove(ref.getKey());
}
}

View file

@ -33,6 +33,7 @@ public abstract class DatabaseObject {
@SuppressWarnings("rawtypes")
private final DBObjectCache cache;
private volatile int invalidateCount;
private boolean refreshing = false;
/**
* Constructs a new DatabaseObject and adds it to the specified cache.
@ -91,6 +92,7 @@ public abstract class DatabaseObject {
/**
* Returns true if object is currently invalid and must be validated prior to further use.
* A deleted object will be considered valid since it cannot be refreshed.
* An invalid object may result from a cache invalidation which corresponds to wide-spread
* record changes. A common situation where this can occur is an undo/redo operation
* against the underlying database. The methods {@link #checkIsValid()}, {@link #checkDeleted()},
@ -128,23 +130,41 @@ public abstract class DatabaseObject {
}
/**
* Check whether this object is still valid. If the object is invalid, the object will attempt
* to refresh itself using the specified record. If the refresh fails, the object will be marked
* as deleted and removed from cache. If this object is already marked as deleted, the record
* can not be used to refresh the object.
* Check whether this object is still valid. If the object is invalid and not deleted, the
* object will attempt to refresh itself using the specified record. If the refresh fails,
* the object will be marked as deleted and removed from cache. If this object is already
* marked as deleted a refresh will not be perormed.
* <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
* @return true if the object is valid.
* @return true if the object is valid or false if it has been deleted.
*/
protected boolean checkIsValid(DBRecord record) {
if (isInvalid()) {
setValid();// prevent checkIsValid recursion during refresh
if (!refresh(record)) {
if (refreshing) {
// NOTE: We need to correct such recursion cases which should be
// avoided since object is not in a valid state until refresh completed.
return !deleted;
}
refreshing = true;
try {
if (refresh(record)) {
// Object is valid
setValid();
return true;
}
// Object has been deleted
if (cache != null) {
cache.delete(key);
}
setDeleted();
setInvalid();
return false;
}
finally {
refreshing = false;
}
}
return !deleted;
@ -152,14 +172,15 @@ public abstract class DatabaseObject {
/**
* This method provides a cheap (lock free) way to test if an object is valid. If this object is
* invalid, then the lock will be used to refresh as needed.
* invalid and not deleted, then the lock will be used to refresh as needed. A deleted object
* will not be refreshed.
*
* @param lock the lock that will be used if the object needs to be refreshed.
* @return true if object is valid, else false if deleted
* @return true if object is valid or false if deleted
*/
protected boolean validate(Lock lock) {
if (!isInvalid()) {
return true;
return !deleted;
}
lock.acquire();
try {

View file

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

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

View file

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

View file

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

View file

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

View file

@ -56,11 +56,18 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
this.dataMgr = dataMgr;
this.record = record;
this.lock = dataMgr.lock;
refreshName();
}
/**
* Clears the current name so that the next invocation of {@link #getName()} will
* force its update via {@link #doGetName()}. It is important that {@link #doGetName()}
* does not get invoked during a {@link #refresh()} to avoid problematic recursion during the
* refresh caused by recursive use of {@link #checkIsValid()} on the same object.
* <P>
* NOTE: This must only be invoked while in a locked-state
*/
protected void refreshName() {
name = doGetName();
name = null;
}
/**
@ -144,8 +151,21 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
@Override
public final String getName() {
validate(lock);
return name;
String n = name;
if (n != null && !isInvalid()) {
return n;
}
lock.acquire();
try {
checkIsValid();
if (name == null) {
name = doGetName();
}
return name;
}
finally {
lock.release();
}
}
@Override

View file

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

View file

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

View file

@ -37,7 +37,8 @@ class PointerDB extends DataTypeDB implements Pointer {
new SettingsDefinition[] { MutabilitySettingsDefinition.DEF };
private PointerDBAdapter adapter;
private String displayName;
private String displayName; // lazy initialization
/**
* <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 structAlignment; // reflects stored alignment, -1 if not yet stored
private int computedAlignment = -1; // cached alignment if not yet stored
private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed
private int numComponents; // If packed, this does not include the undefined components.
private List<DataTypeComponentDB> components;
@ -73,7 +73,11 @@ class StructureDB extends CompositeDB implements StructureInternal {
structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL);
structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL);
computedAlignment = -1;
numComponents = isPackingEnabled() ? components.size()
boolean packingDisabled =
record.getIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL) < DEFAULT_PACKING;
numComponents = !packingDisabled ? components.size()
: record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL);
if (oldFlexArrayRecord != null) {

View file

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

View file

@ -33,7 +33,7 @@ class UnionDB extends CompositeDB implements UnionInternal {
private int unionLength;
private int unionAlignment; // reflects stored alignment, -1 if not yet stored
private int computedAlignment = -1; // cached alignment if not yet stored
private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed
private List<DataTypeComponentDB> components;

View file

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

View file

@ -81,12 +81,6 @@ public class FunctionManagerDB implements FunctionManager {
return false;
};
/**
* TODO: use of StringPropertyMap for callFixupMap lacks the ability to listen for changes
* which may be made to the map directly (e.g., diff/merge)
*/
private StringPropertyMap callFixupMap;
private long lastFuncID = -1;
Lock lock;
@ -825,7 +819,6 @@ public class FunctionManagerDB implements FunctionManager {
lock.acquire();
try {
functionTagManager.invalidateCache();
callFixupMap = null;
lastFuncID = -1;
cache.invalidate();
}
@ -835,11 +828,8 @@ public class FunctionManagerDB implements FunctionManager {
}
StringPropertyMap getCallFixupMap(boolean create) {
if (callFixupMap != null) {
return callFixupMap;
}
PropertyMapManager usrPropertyManager = program.getUsrPropertyManager();
callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP);
StringPropertyMap callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP);
if (callFixupMap == null && create) {
try {
callFixupMap = usrPropertyManager.createStringPropertyMap(CALLFIXUP_MAP);

View file

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

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor;
*/
public class IntPropertyMapDB extends PropertyMapDB<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.
* @param dbHandle database handle.
@ -58,27 +77,22 @@ public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPrope
public void add(Address addr, int value) {
lock.acquire();
try {
checkDeleted();
Integer oldValue = null;
long key = addrMap.getKey(addr, true);
long addrKey = addrMap.getKey(addr, true);
if (propertyTable == null) {
createTable(IntField.INSTANCE);
}
else {
oldValue = (Integer) cache.get(key);
oldValue = cache.get(addrKey);
if (oldValue == null) {
DBRecord rec = propertyTable.getRecord(key);
if (rec != null) {
oldValue = rec.getIntValue(PROPERTY_VALUE_COL);
}
oldValue = valueReader.apply(addrKey);
}
}
DBRecord rec = schema.createRecord(key);
DBRecord rec = schema.createRecord(addrKey);
rec.setIntValue(PROPERTY_VALUE_COL, value);
propertyTable.putRecord(rec);
cache.put(key, value);
cache.put(addrKey, value);
changeMgr.setPropertyChanged(name, addr, oldValue, value);
}
@ -92,44 +106,25 @@ public class IntPropertyMapDB extends PropertyMapDB<Integer> implements IntPrope
@Override
public int getInt(Address addr) throws NoValueException {
if (propertyTable == null) {
Integer value = get(addr);
if (value == null) {
throw NO_VALUE_EXCEPTION;
}
lock.acquire();
try {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY) {
return 0;
}
Object obj = cache.get(key);
if (obj != null) {
return ((Integer) obj).intValue();
}
DBRecord rec = propertyTable.getRecord(key);
if (rec == null) {
throw NO_VALUE_EXCEPTION;
}
return rec.getIntValue(PROPERTY_VALUE_COL);
}
catch (IOException e) {
errHandler.dbError(e);
}
finally {
lock.release();
}
return 0;
return value;
}
@Override
public Integer get(Address addr) {
try {
return getInt(addr);
}
catch (NoValueException e) {
validate(lock);
Table table = propertyTable;
if (table == null) {
return null;
}
long addrKey = addrMap.getKey(addr, false);
if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
return null;
}
return cache.computeIfAbsent(addrKey, valueReader);
}
}

View file

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

View file

@ -16,6 +16,7 @@
package ghidra.program.database.properties;
import java.io.IOException;
import java.util.function.Function;
import db.*;
import db.util.ErrorHandler;
@ -42,6 +43,44 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
private int saveableObjectVersion;
private boolean supportsPrivate;
/**
* A single non-capturing lambda record reader function is used to avoid the possibility of
* multiple synthetic class instantiations.
*/
@SuppressWarnings("unchecked")
private Function<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.
* @param dbHandle database handle.
@ -256,11 +295,19 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
public void add(Address addr, T value) {
lock.acquire();
try {
checkDeleted();
if (!saveableObjectClass.isAssignableFrom(value.getClass())) {
throw new IllegalArgumentException("value is not " + saveableObjectClass.getName());
}
long key = addrMap.getKey(addr, true);
T oldValue = get(addr);
long addrKey = addrMap.getKey(addr, true);
T oldValue = null;
if (propertyTable != null) {
oldValue = cache.get(addrKey);
if (oldValue == null) {
oldValue = valueReader.apply(addrKey);
}
}
String tableName = getTableName();
Schema s;
@ -271,7 +318,7 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
s = objStorage.getSchema(value.getSchemaVersion());
checkSchema(s);
createPropertyTable(tableName, s);
rec = schema.createRecord(key);
rec = schema.createRecord(addrKey);
objStorage.save(rec);
}
else { // GenericSaveable
@ -281,11 +328,11 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
checkSchema(s);
createPropertyTable(tableName, s);
rec = originalRec.copy();
rec.setKey(key);
rec.setKey(addrKey);
}
propertyTable.putRecord(rec);
cache.put(key, value);
cache.put(addrKey, value);
if (!isPrivate(value)) {
changeMgr.setPropertyChanged(name, addr, oldValue, value);
@ -326,57 +373,18 @@ public class ObjectPropertyMapDB<T extends Saveable> extends PropertyMapDB<T>
return saveableObjectClass;
}
@SuppressWarnings("unchecked")
@Override
public T get(Address addr) {
if (propertyTable == null) {
validate(lock);
Table table = propertyTable;
if (table == null) {
return null;
}
T obj = null;
lock.acquire();
try {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY) {
return null;
}
obj = (T) cache.get(key);
if (obj != null) {
return obj;
}
DBRecord rec = propertyTable.getRecord(key);
if (rec == null) {
return null;
}
ObjectStorageAdapterDB objStorage = new ObjectStorageAdapterDB(rec);
if (saveableObjectClass == GenericSaveable.class) {
obj = (T) new GenericSaveable(rec, propertyTable.getSchema());
}
else {
obj = saveableObjectClass.getDeclaredConstructor().newInstance();
obj.restore(objStorage);
}
long addrKey = addrMap.getKey(addr, false);
if (addrKey == AddressMap.INVALID_ADDRESS_KEY) {
return null;
}
catch (IOException e) {
errHandler.dbError(e);
}
catch (RuntimeException e) {
throw e;
}
catch (InstantiationException e) {
errHandler.dbError(new IOException("Could not instantiate " + e.getMessage()));
}
catch (Exception e) {
errHandler.dbError(new IOException(e.getMessage()));
}
finally {
lock.release();
}
return obj;
return cache.computeIfAbsent(addrKey, valueReader);
}
/**

View file

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

View file

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

View file

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

View file

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

View file

@ -141,7 +141,7 @@ public interface PropertyMapManager {
public boolean removePropertyMap(String propertyName);
/**
* Returns an iterator over the names of all existing PropertyMaps.
* Returns an iterator over the names of all existing PropertyMaps sorted by name.
*/
public Iterator<String> propertyManagers();