From 5a9b0268de9e1948e7d0f690133ab693c46621a3 Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Mon, 28 Apr 2025 12:57:04 +0000 Subject: [PATCH] GP-5619 Various FunctionDB/InstructionDB locking related speed improvements. Revised DatabaseObject checkIsValid implementation. --- .../ghidra/util/datastruct/ObjectCache.java | 57 ++++-- .../program/database/DatabaseObject.java | 47 +++-- .../program/database/code/CodeManager.java | 4 +- .../program/database/code/CodeUnitDB.java | 101 ++++----- .../ghidra/program/database/code/DataDB.java | 30 +-- .../program/database/code/InstructionDB.java | 130 ++++++------ .../ghidra/program/database/data/ArrayDB.java | 9 +- .../program/database/data/DataTypeDB.java | 28 ++- .../database/data/DataTypeManagerDB.java | 7 + .../ghidra/program/database/data/EnumDB.java | 11 +- .../program/database/data/PointerDB.java | 7 +- .../program/database/data/StructureDB.java | 8 +- .../program/database/data/TypedefDB.java | 7 +- .../ghidra/program/database/data/UnionDB.java | 2 +- .../program/database/function/FunctionDB.java | 170 ++++++--------- .../database/function/FunctionManagerDB.java | 12 +- .../properties/DBPropertyMapManager.java | 137 +++++-------- .../database/properties/IntPropertyMapDB.java | 81 ++++---- .../properties/LongPropertyMapDB.java | 80 ++++---- .../properties/ObjectPropertyMapDB.java | 114 ++++++----- .../database/properties/PropertyMapDB.java | 193 +++++++++++------- .../properties/StringPropertyMapDB.java | 74 ++++--- .../properties/VoidPropertyMapDB.java | 14 +- .../program/model/address/GenericAddress.java | 125 +----------- .../model/util/PropertyMapManager.java | 6 +- 25 files changed, 691 insertions(+), 763 deletions(-) diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java index bf6883701b..80c5663b6d 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/datastruct/ObjectCache.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,6 +18,7 @@ package ghidra.util.datastruct; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.*; +import java.util.function.Function; /** * ObjectClass provides a fixed-size long-key-based object cache. @@ -27,12 +28,14 @@ import java.util.*; *

* The weak cache is keyed, while the hard cache simply maintains the presence of * an object in the weak cache. + * + * @param Object type held by cache */ -public class ObjectCache { +public class ObjectCache { - private Map> hashTable; - private ReferenceQueue refQueue; - private LinkedList hardCache; + private Map> hashTable; + private ReferenceQueue refQueue; + private LinkedList hardCache; private int hardCacheSize; /** @@ -61,10 +64,10 @@ public class ObjectCache { * @param key object key * @return cached object */ - public synchronized Object get(long key) { - WeakReference ref = hashTable.get(key); + public synchronized T get(long key) { + WeakReference ref = hashTable.get(key); if (ref != null) { - Object obj = ref.get(); + T obj = ref.get(); if (obj == null) { hashTable.remove(key); } @@ -74,6 +77,30 @@ public class ObjectCache { return null; } + /** + * Get the current cached object which corresponds to specified {@code key} if contained in + * cache, otherwise the {@code mappingFunction} will be invoked to instantiate a new object + * where that object will be added to the cache and returned. If the {@code mappingFunction} + * returns null nothing will be added to the cache and null will be returned by this method. + * + * @param key object key + * @param mappingFunction function used to obtain a new object if not currently present + * in cache. + * @return cached object + */ + public synchronized T computeIfAbsent(long key, Function mappingFunction) { + Objects.requireNonNull(mappingFunction); + T oldValue = get(key); + if (oldValue != null) { + return oldValue; + } + T newValue = mappingFunction.apply(key); + if (newValue != null) { + put(key, newValue); + } + return newValue; + } + /** * Return the hard cache size * @return the hard cache size @@ -98,9 +125,9 @@ public class ObjectCache { * @param key object key * @param obj the object */ - public synchronized void put(long key, Object obj) { + public synchronized void put(long key, T obj) { processQueue(); - KeyedSoftReference ref = new KeyedSoftReference<>(key, obj, refQueue); + KeyedSoftReference ref = new KeyedSoftReference<>(key, obj, refQueue); hashTable.put(key, ref); addToHardCache(obj); } @@ -112,7 +139,7 @@ public class ObjectCache { */ public synchronized void remove(long key) { processQueue(); - KeyedSoftReference ref = hashTable.get(key); + KeyedSoftReference ref = hashTable.get(key); if (ref != null) { ref.clear(); hashTable.remove(key); @@ -123,7 +150,7 @@ public class ObjectCache { * Add the specified object to the hard cache. * @param obj object */ - private void addToHardCache(Object obj) { + private void addToHardCache(T obj) { hardCache.addLast(obj); if (hardCache.size() > hardCacheSize) { hardCache.removeFirst(); @@ -134,8 +161,8 @@ public class ObjectCache { * Cleanup weak cache */ private void processQueue() { - KeyedSoftReference ref; - while ((ref = (KeyedSoftReference) refQueue.poll()) != null) { + KeyedSoftReference ref; + while ((ref = (KeyedSoftReference) refQueue.poll()) != null) { hashTable.remove(ref.getKey()); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java index dd4eeb2e34..5062576c2a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/DatabaseObject.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,6 +33,7 @@ public abstract class DatabaseObject { @SuppressWarnings("rawtypes") private final DBObjectCache cache; private volatile int invalidateCount; + private boolean refreshing = false; /** * Constructs a new DatabaseObject and adds it to the specified cache. @@ -91,6 +92,7 @@ public abstract class DatabaseObject { /** * Returns true if object is currently invalid and must be validated prior to further use. + * A deleted object will be considered valid since it cannot be refreshed. * An invalid object may result from a cache invalidation which corresponds to wide-spread * record changes. A common situation where this can occur is an undo/redo operation * against the underlying database. The methods {@link #checkIsValid()}, {@link #checkDeleted()}, @@ -128,23 +130,41 @@ public abstract class DatabaseObject { } /** - * Check whether this object is still valid. If the object is invalid, the object will attempt - * to refresh itself using the specified record. If the refresh fails, the object will be marked - * as deleted and removed from cache. If this object is already marked as deleted, the record - * can not be used to refresh the object. + * Check whether this object is still valid. If the object is invalid and not deleted, the + * object will attempt to refresh itself using the specified record. If the refresh fails, + * the object will be marked as deleted and removed from cache. If this object is already + * marked as deleted a refresh will not be perormed. + *

+ * This method may invoke {@link #refresh(DBRecord)} to perform a refresh. It is important + * that such a refresh avoid recursing back into this method. * * @param record optional record which may be used to refresh invalid object - * @return true if the object is valid. + * @return true if the object is valid or false if it has been deleted. */ protected boolean checkIsValid(DBRecord record) { if (isInvalid()) { - setValid();// prevent checkIsValid recursion during refresh - if (!refresh(record)) { + if (refreshing) { + // NOTE: We need to correct such recursion cases which should be + // avoided since object is not in a valid state until refresh completed. + return !deleted; + } + refreshing = true; + try { + if (refresh(record)) { + // Object is valid + setValid(); + return true; + } + + // Object has been deleted if (cache != null) { cache.delete(key); } setDeleted(); - setInvalid(); + return false; + } + finally { + refreshing = false; } } return !deleted; @@ -152,14 +172,15 @@ public abstract class DatabaseObject { /** * This method provides a cheap (lock free) way to test if an object is valid. If this object is - * invalid, then the lock will be used to refresh as needed. + * invalid and not deleted, then the lock will be used to refresh as needed. A deleted object + * will not be refreshed. * * @param lock the lock that will be used if the object needs to be refreshed. - * @return true if object is valid, else false if deleted + * @return true if object is valid or false if deleted */ protected boolean validate(Lock lock) { if (!isInvalid()) { - return true; + return !deleted; } lock.acquire(); try { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java index daaec38a4d..3f5b051788 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java @@ -3207,8 +3207,8 @@ public class CodeManager implements ErrorHandler, ManagerDB { lock.acquire(); try { cache.invalidate(); - lengthMgr.invalidateCache(); - compositeMgr.invalidateCache(); + lengthMgr.invalidate(); + compositeMgr.invalidate(); protoMgr.clearCache(); } finally { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java index e6f7f1936c..18c62d4d68 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeUnitDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -65,7 +65,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC * @param cacheKey the cache key (dataComponent does not use the address) * @param address min address of this code unit * @param addr index for min address - * @param the length of the codeunit. + * @param length the length of the codeunit. */ public CodeUnitDB(CodeManager codeMgr, DBObjectCache cache, long cacheKey, Address address, long addr, int length) { @@ -95,21 +95,6 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC return !hasBeenDeleted(record); } - /** - * Check this code units validity when the lock/checkIsValid is not used and refresh if necessary. - */ - protected void refreshIfNeeded() { - if (isInvalid()) { - lock.acquire(); - try { - refresh(); - } - finally { - lock.release(); - } - } - } - /** * Perform any refresh necessary and determine if this code unit has been deleted. * If a record has been provided, it may be used to facilitate a refresh without @@ -122,7 +107,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void addMnemonicReference(Address refAddr, RefType refType, SourceType sourceType) { - refreshIfNeeded(); + validate(lock); refMgr.addMemoryReference(address, refAddr, refType, sourceType, MNEMONIC); } @@ -134,13 +119,12 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void addOperandReference(int opIndex, Address refAddr, RefType type, SourceType sourceType) { - refreshIfNeeded(); + validate(lock); refMgr.addMemoryReference(address, refAddr, type, sourceType, opIndex); } @Override public int compareTo(Address a) { - refreshIfNeeded(); if (contains(a)) { return 0; } @@ -149,13 +133,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public boolean contains(Address testAddr) { - refreshIfNeeded(); + validate(lock); return address.compareTo(testAddr) <= 0 && testAddr.compareTo(getMaxAddress()) <= 0; } @Override public String getAddressString(boolean showBlockName, boolean pad) { - refreshIfNeeded(); + validate(lock); Address cuAddress = address; String addressString = cuAddress.toString(false, pad); if (showBlockName) { @@ -231,7 +215,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public ExternalReference getExternalReference(int opIndex) { - refreshIfNeeded(); + validate(lock); Reference[] refs = refMgr.getReferencesFrom(address, opIndex); for (Reference element : refs) { if (element.isExternalReference()) { @@ -249,7 +233,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC throw NoValueException.noValueException; } try { - refreshIfNeeded(); + validate(lock); return pm.getInt(address); } catch (ConcurrentModificationException e) { @@ -259,7 +243,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public String getLabel() { - refreshIfNeeded(); + validate(lock); SymbolTable st = codeMgr.getSymbolTable(); Symbol symbol = st.getPrimarySymbol(address); if (symbol != null) { @@ -279,7 +263,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public Address getMaxAddress() { - refreshIfNeeded(); + validate(lock); if (endAddr == null) { endAddr = getLength() == 0 ? address : address.add(getLength() - 1); } @@ -288,20 +272,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public Address getMinAddress() { - refreshIfNeeded(); + validate(lock); return address; } @Override public Address getAddress() { - // TODO: Not sure why this method exists? - refreshIfNeeded(); + validate(lock); return address; } @Override public Reference[] getMnemonicReferences() { - refreshIfNeeded(); + validate(lock); return refMgr.getReferencesFrom(address, MNEMONIC); } @@ -311,7 +294,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC ObjectPropertyMap pm = upm.getObjectPropertyMap(name); if (pm != null) { try { - refreshIfNeeded(); + validate(lock); return pm.get(address); } catch (ConcurrentModificationException e) { @@ -322,19 +305,19 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public Reference[] getOperandReferences(int opIndex) { - refreshIfNeeded(); + validate(lock); return refMgr.getReferencesFrom(address, opIndex); } @Override public Reference getPrimaryReference(int index) { - refreshIfNeeded(); + validate(lock); return refMgr.getPrimaryReferenceFrom(address, index); } @Override public Symbol getPrimarySymbol() { - refreshIfNeeded(); + validate(lock); SymbolTable st = codeMgr.getSymbolTable(); return st.getPrimarySymbol(address); } @@ -346,13 +329,13 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public Reference[] getReferencesFrom() { - refreshIfNeeded(); + validate(lock); return refMgr.getReferencesFrom(address); } @Override public ReferenceIterator getReferenceIteratorTo() { - refreshIfNeeded(); + validate(lock); return program.getReferenceManager().getReferencesTo(address); } @@ -362,7 +345,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC StringPropertyMap pm = upm.getStringPropertyMap(name); if (pm != null) { try { - refreshIfNeeded(); + validate(lock); return pm.getString(address); } catch (ConcurrentModificationException e) { @@ -373,7 +356,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public Symbol[] getSymbols() { - refreshIfNeeded(); + validate(lock); SymbolTable st = codeMgr.getSymbolTable(); return st.getSymbols(address); } @@ -384,7 +367,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC VoidPropertyMap pm = upm.getVoidPropertyMap(name); if (pm != null) { try { - refreshIfNeeded(); + validate(lock); return pm.hasProperty(address); } catch (ConcurrentModificationException e) { @@ -399,7 +382,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC PropertyMap pm = upm.getPropertyMap(name); if (pm != null) { try { - refreshIfNeeded(); + validate(lock); return pm.hasProperty(address); } catch (ConcurrentModificationException e) { @@ -424,7 +407,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void removeMnemonicReference(Address refAddr) { - refreshIfNeeded(); + validate(lock); Reference ref = refMgr.getReference(address, refAddr, MNEMONIC); if (ref != null) { program.getReferenceManager().delete(ref); @@ -433,7 +416,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void removeOperandReference(int opIndex, Address refAddr) { - refreshIfNeeded(); + validate(lock); Reference ref = refMgr.getReference(address, refAddr, opIndex); if (ref != null) { program.getReferenceManager().delete(ref); @@ -446,7 +429,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC PropertyMap pm = upm.getPropertyMap(name); if (pm != null) { try { - refreshIfNeeded(); + validate(lock); pm.remove(address); } catch (ConcurrentModificationException e) { @@ -607,7 +590,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void setStackReference(int opIndex, int offset, SourceType sourceType, RefType refType) { - refreshIfNeeded(); + validate(lock); validateOpIndex(opIndex); refMgr.addStackReference(address, opIndex, offset, refType, sourceType); } @@ -615,20 +598,20 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void setRegisterReference(int opIndex, Register reg, SourceType sourceType, RefType refType) { - refreshIfNeeded(); + validate(lock); validateOpIndex(opIndex); refMgr.addRegisterReference(address, opIndex, reg, refType, sourceType); } @Override public int getBytes(byte[] b, int offset) { - refreshIfNeeded(); + validate(lock); byte localBytes[] = populateByteArray(); if (offset >= 0 && (offset + b.length) <= localBytes.length) { System.arraycopy(localBytes, offset, b, 0, b.length); return b.length; } - + try { return program.getMemory().getBytes(address.add(offset), b); } @@ -639,15 +622,15 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public byte[] getBytes() throws MemoryAccessException { - refreshIfNeeded(); + validate(lock); byte localBytes[] = populateByteArray(); int locallen = getLength(); - if (localBytes.length >= locallen ) { + if (localBytes.length >= locallen) { byte[] b = new byte[locallen]; System.arraycopy(localBytes, 0, b, 0, b.length); return b; } - + int len = getLength(); byte[] b = new byte[len]; @@ -659,7 +642,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public byte getByte(int offset) throws MemoryAccessException { - refreshIfNeeded(); + validate(lock); byte localBytes[] = populateByteArray(); if (offset >= 0 && offset < localBytes.length) { return localBytes[offset]; @@ -680,31 +663,31 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public BigInteger getValue(Register register, boolean signed) { - refreshIfNeeded(); + validate(lock); return programContext.getValue(register, address, signed); } @Override public RegisterValue getRegisterValue(Register register) { - refreshIfNeeded(); + validate(lock); return programContext.getRegisterValue(register, address); } @Override public void setValue(Register register, BigInteger value) throws ContextChangeException { - refreshIfNeeded(); + validate(lock); programContext.setValue(register, address, address, value); } @Override public void clearRegister(Register register) throws ContextChangeException { - refreshIfNeeded(); + validate(lock); programContext.setValue(register, address, address, null); } @Override public void setRegisterValue(RegisterValue value) throws ContextChangeException { - refreshIfNeeded(); + validate(lock); programContext.setRegisterValue(address, address, value); } @@ -725,7 +708,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public boolean hasValue(Register register) { - refreshIfNeeded(); + validate(lock); return programContext.getValue(register, address, false) != null; } @@ -818,7 +801,7 @@ abstract class CodeUnitDB extends DatabaseObject implements CodeUnit, ProcessorC @Override public void getBytesInCodeUnit(byte[] buffer, int bufferOffset) throws MemoryAccessException { - refreshIfNeeded(); + validate(lock); byte[] codeUnitBytes = getBytes(); System.arraycopy(codeUnitBytes, 0, buffer, bufferOffset, Math.min(buffer.length, getLength())); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java index e4fa0f0dda..5e02659908 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/DataDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -197,7 +197,7 @@ class DataDB extends CodeUnitDB implements Data { @Override public void addValueReference(Address refAddr, RefType type) { - refreshIfNeeded(); + validate(lock); refMgr.addMemoryReference(address, refAddr, type, SourceType.USER_DEFINED, CodeManager.DATA_OP_INDEX); } @@ -379,19 +379,19 @@ class DataDB extends CodeUnitDB implements Data { @Override public boolean isChangeAllowed(SettingsDefinition settingsDefinition) { - refreshIfNeeded(); + validate(lock); return dataMgr.isChangeAllowed(this, settingsDefinition); } @Override public void clearSetting(String name) { - refreshIfNeeded(); + validate(lock); dataMgr.clearSetting(this, name); } @Override public Long getLong(String name) { - refreshIfNeeded(); + validate(lock); Long value = dataMgr.getLongSettingsValue(this, name); if (value == null) { value = getDefaultSettings().getLong(name); @@ -401,13 +401,13 @@ class DataDB extends CodeUnitDB implements Data { @Override public String[] getNames() { - refreshIfNeeded(); + validate(lock); return dataMgr.getInstanceSettingsNames(this); } @Override public String getString(String name) { - refreshIfNeeded(); + validate(lock); String value = dataMgr.getStringSettingsValue(this, name); if (value == null) { value = getDefaultSettings().getString(name); @@ -417,7 +417,7 @@ class DataDB extends CodeUnitDB implements Data { @Override public Object getValue(String name) { - refreshIfNeeded(); + validate(lock); Object value = dataMgr.getSettings(this, name); if (value == null) { value = getDefaultSettings().getValue(name); @@ -427,19 +427,19 @@ class DataDB extends CodeUnitDB implements Data { @Override public void setLong(String name, long value) { - refreshIfNeeded(); + validate(lock); dataMgr.setLongSettingsValue(this, name, value); } @Override public void setString(String name, String value) { - refreshIfNeeded(); + validate(lock); dataMgr.setStringSettingsValue(this, name, value); } @Override public void setValue(String name, Object value) { - refreshIfNeeded(); + validate(lock); dataMgr.setSettings(this, name, value); } @@ -650,7 +650,7 @@ class DataDB extends CodeUnitDB implements Data { @Override public String getPathName() { - refreshIfNeeded(); + validate(lock); Address cuAddress = address; SymbolTable st = program.getSymbolTable(); Symbol symbol = st.getPrimarySymbol(cuAddress); @@ -763,13 +763,13 @@ class DataDB extends CodeUnitDB implements Data { @Override public void clearAllSettings() { - refreshIfNeeded(); + validate(lock); dataMgr.clearAllSettings(this); } @Override public boolean isEmpty() { - refreshIfNeeded(); + validate(lock); return dataMgr.isEmptySetting(this); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java index 973447512c..d0886c8e00 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/InstructionDB.java @@ -54,9 +54,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio private FlowOverride flowOverride; private int lengthOverride; private final static Address[] EMPTY_ADDR_ARRAY = new Address[0]; + private final static Object[] EMPTY_OBJ_ARRAY = new Object[0]; private volatile boolean clearingFallThroughs = false; - private ParserContext parserContext; + private ParserContext parserContext; // uses lazy initialization + private String mnemonicString; // uses lazy initialization /** * Construct a new InstructionDB. @@ -80,6 +82,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override protected boolean refresh(DBRecord record) { parserContext = null; + mnemonicString = null; return super.refresh(record); } @@ -240,12 +243,12 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio return null; } - if (this.isInDelaySlot()) { + if (isInDelaySlot()) { // If this instruction is within delay-slot, return a null fall-from address if // previous instruction (i.e., instruction with delay slot, found above) // does not have a fallthrough and this instruction has a ref or label on it. if (!instr.hasFallthrough() && - program.getSymbolTable().hasSymbol(this.getMinAddress())) { + program.getSymbolTable().hasSymbol(getMinAddress())) { return null; } // Return previous instruction's address (i.e., instruction with delay slot, found above) @@ -276,22 +279,15 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Address getFallThrough() { - lock.acquire(); - try { - checkIsValid(); - if (isFallThroughOverridden()) { - return getFallThroughReference(); - } - return getDefaultFallThrough(); - } - finally { - lock.release(); + if (isFallThroughOverridden()) { + return getFallThroughReference(); } + return getDefaultFallThrough(); } @Override public Address[] getFlows() { - refreshIfNeeded(); + validate(lock); Reference[] refs = refMgr.getFlowReferencesFrom(address); if (refs.length == 0) { return EMPTY_ADDR_ARRAY; @@ -313,6 +309,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Address[] getDefaultFlows() { + validate(lock); Address[] flows = proto.getFlows(this); if (flowOverride == FlowOverride.RETURN && flows.length == 1) { return EMPTY_ADDR_ARRAY; @@ -322,17 +319,23 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public FlowType getFlowType() { + validate(lock); return FlowOverride.getModifiedFlowType(proto.getFlowType(this), flowOverride); } @Override public Instruction getNext() { - refreshIfNeeded(); + validate(lock); return codeMgr.getInstructionAfter(address); } @Override public RefType getOperandRefType(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return null; + } + // always reflects current flowOverride lock.acquire(); try { @@ -346,6 +349,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public String getSeparator(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return null; + } + lock.acquire(); try { return proto.getSeparator(opIndex, this); @@ -424,12 +432,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Object[] getOpObjects(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return EMPTY_OBJ_ARRAY; + } + lock.acquire(); try { checkIsValid(); - if (opIndex < 0 || opIndex >= getNumOperands()) { - return new Object[0]; - } return proto.getOpObjects(opIndex, this); } finally { @@ -439,7 +449,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Instruction getPrevious() { - refreshIfNeeded(); + validate(lock); return codeMgr.getInstructionBefore(address); } @@ -450,12 +460,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Register getRegister(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return null; + } + lock.acquire(); try { checkIsValid(); - if (opIndex < 0) { - return null; - } return proto.getRegister(opIndex, this); } finally { @@ -494,6 +506,11 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Address getAddress(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return null; + } + lock.acquire(); try { checkIsValid(); @@ -501,9 +518,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio if (ref != null) { return ref.getToAddress(); } - if (opIndex < 0) { - return null; - } + int opType = proto.getOpType(opIndex, this); if (OperandType.isAddress(opType)) { @@ -550,10 +565,16 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public String getMnemonicString() { + validate(lock); + String curMnemonic = mnemonicString; + if (curMnemonic != null) { + return curMnemonic; + } lock.acquire(); try { checkIsValid(); - return proto.getMnemonic(this); + mnemonicString = proto.getMnemonic(this); + return mnemonicString; } finally { lock.release(); @@ -567,12 +588,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Scalar getScalar(int opIndex) { + + if (opIndex < 0 || opIndex >= getNumOperands()) { + return null; + } + lock.acquire(); try { checkIsValid(); - if (opIndex < 0) { - return null; - } return proto.getScalar(opIndex, this); } finally { @@ -693,6 +716,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public boolean isFallThroughOverridden() { + validate(lock); return (flags & FALLTHROUGH_SET_MASK) != 0; } @@ -705,7 +729,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio if (clearingFallThroughs) { return; } - refreshIfNeeded(); + validate(lock); clearingFallThroughs = true; try { boolean fallThroughPreserved = false; @@ -887,14 +911,14 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio Address defaultFallThrough = getDefaultFallThrough(); if (defaultFallThrough != null) { refMgr.addMemoryReference(address, defaultFallThrough, RefType.FALL_THROUGH, - SourceType.USER_DEFINED, Reference.MNEMONIC); + SourceType.USER_DEFINED, Reference.MNEMONIC); } } } @Override public boolean isLengthOverridden() { - refreshIfNeeded(); + validate(lock); return lengthOverride != 0; } @@ -911,30 +935,26 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public Address getDefaultFallThrough() { - lock.acquire(); - try { - // TODO: This used to be in proto. We need to override the proto's flowtype. - // This could be pushed back into the proto if we could override the flowType there. - FlowType myFlowType = getFlowType(); - if (myFlowType.hasFallthrough()) { - try { - return getAddress().addNoWrap(proto.getFallThroughOffset(this)); - } - catch (AddressOverflowException e) { - // ignore - } + FlowType myFlowType = getFlowType(); // getFlowType will validate + if (myFlowType.hasFallthrough()) { + try { + return address.addNoWrap(getDefaultFallThroughOffset()); + } + catch (AddressOverflowException e) { + // ignore } - return null; - } - finally { - lock.release(); } + return null; } @Override public int getDefaultFallThroughOffset() { + if (proto.getDelaySlotByteCount() <= 0) { + return getLength(); + } lock.acquire(); try { + checkIsValid(); return proto.getFallThroughOffset(this); } finally { @@ -944,17 +964,10 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public boolean hasFallthrough() { - lock.acquire(); - try { - checkIsValid(); - if (isFallThroughOverridden()) { - return getFallThrough() != null; // fall-through destination stored as reference - } - return getFlowType().hasFallthrough(); - } - finally { - lock.release(); + if (isFallThroughOverridden()) { + return getFallThrough() != null; // fall-through destination stored as reference } + return getFlowType().hasFallthrough(); } @Override @@ -977,6 +990,7 @@ public class InstructionDB extends CodeUnitDB implements Instruction, Instructio @Override public ParserContext getParserContext() throws MemoryAccessException { + // NOTE: It is assumed this is invoked and used within a locked block if (parserContext == null) { parserContext = proto.getParserContext(this, this); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java index 9b7ca2151b..0f95035c91 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/ArrayDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -34,9 +34,10 @@ import ghidra.util.exception.DuplicateNameException; */ class ArrayDB extends DataTypeDB implements Array { - private volatile String displayName; private ArrayDBAdapter adapter; - private int elementLength; // lazy initialization + + private int elementLength = -1; // lazy initialization + private String displayName; // lazy initialization /** * Constructor diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java index 1ed67894c6..c5c880313c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeDB.java @@ -56,11 +56,18 @@ abstract class DataTypeDB extends DatabaseObject implements DataType { this.dataMgr = dataMgr; this.record = record; this.lock = dataMgr.lock; - refreshName(); } + /** + * Clears the current name so that the next invocation of {@link #getName()} will + * force its update via {@link #doGetName()}. It is important that {@link #doGetName()} + * does not get invoked during a {@link #refresh()} to avoid problematic recursion during the + * refresh caused by recursive use of {@link #checkIsValid()} on the same object. + *

+ * NOTE: This must only be invoked while in a locked-state + */ protected void refreshName() { - name = doGetName(); + name = null; } /** @@ -144,8 +151,21 @@ abstract class DataTypeDB extends DatabaseObject implements DataType { @Override public final String getName() { - validate(lock); - return name; + String n = name; + if (n != null && !isInvalid()) { + return n; + } + lock.acquire(); + try { + checkIsValid(); + if (name == null) { + name = doGetName(); + } + return name; + } + finally { + lock.release(); + } } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java index c8ff6beaf9..de02ba55a9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/DataTypeManagerDB.java @@ -46,6 +46,7 @@ import ghidra.program.model.data.*; import ghidra.program.model.data.DataTypeConflictHandler.ConflictResult; import ghidra.program.model.data.Enum; import ghidra.program.model.lang.*; +import ghidra.program.model.listing.Function; import ghidra.util.*; import ghidra.util.classfinder.ClassTranslator; import ghidra.util.datastruct.FixedSizeHashMap; @@ -4129,6 +4130,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager { * @return calling convention name if found else unknown */ public String getCallingConventionName(byte id) { + if (id == DEFAULT_CALLING_CONVENTION_ID) { + return Function.DEFAULT_CALLING_CONVENTION_STRING; + } + else if (id == UNKNOWN_CALLING_CONVENTION_ID) { + return CompilerSpec.CALLING_CONVENTION_unknown; + } lock.acquire(); try { String callingConvention = callingConventionAdapter.getCallingConventionName(id); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java index 3b40e07df3..ab52133756 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java @@ -46,11 +46,12 @@ class EnumDB extends DataTypeDB implements Enum { private EnumDBAdapter adapter; private EnumValueDBAdapter valueAdapter; - private Map nameMap; // name to value - private SortedMap> valueMap; // value to names - private Map commentMap; // name to comment - private List bitGroups; - private EnumSignedState signedState = null; + // Lazy fields whose initialization is triggered by nameMap=null (see initializeIfNeeded) + private Map nameMap; // lazy initialization, name to value + private SortedMap> valueMap; // lazy initialization, value to names + private Map commentMap; // lazy initialization, name to comment + private List bitGroups; // lazy initialization + private EnumSignedState signedState = null; // lazy initialization EnumDB(DataTypeManagerDB dataMgr, DBObjectCache cache, EnumDBAdapter adapter, EnumValueDBAdapter valueAdapter, DBRecord record) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java index 21aa3c398c..fe3240f72d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,7 +37,8 @@ class PointerDB extends DataTypeDB implements Pointer { new SettingsDefinition[] { MutabilitySettingsDefinition.DEF }; private PointerDBAdapter adapter; - private String displayName; + + private String displayName; // lazy initialization /** * isEquivalentActive is used to break cyclical recursion when diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java index 531adf473a..32b2579f1d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/StructureDB.java @@ -37,7 +37,7 @@ class StructureDB extends CompositeDB implements StructureInternal { private int structLength; private int structAlignment; // reflects stored alignment, -1 if not yet stored - private int computedAlignment = -1; // cached alignment if not yet stored + private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed private int numComponents; // If packed, this does not include the undefined components. private List components; @@ -73,7 +73,11 @@ class StructureDB extends CompositeDB implements StructureInternal { structLength = record.getIntValue(CompositeDBAdapter.COMPOSITE_LENGTH_COL); structAlignment = record.getIntValue(CompositeDBAdapter.COMPOSITE_ALIGNMENT_COL); computedAlignment = -1; - numComponents = isPackingEnabled() ? components.size() + + boolean packingDisabled = + record.getIntValue(CompositeDBAdapter.COMPOSITE_PACKING_COL) < DEFAULT_PACKING; + + numComponents = !packingDisabled ? components.size() : record.getIntValue(CompositeDBAdapter.COMPOSITE_NUM_COMPONENTS_COL); if (oldFlexArrayRecord != null) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java index de955d5a9f..7ff5430054 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/TypedefDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -35,7 +35,8 @@ import ghidra.util.exception.DuplicateNameException; class TypedefDB extends DataTypeDB implements TypeDef { private TypedefDBAdapter adapter; - private SettingsDefinition[] settingsDef; + + private SettingsDefinition[] settingsDef; // lazy initialization /** * Construct TypeDefDB instance diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java index eef1b30cae..a9d9d2f740 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/UnionDB.java @@ -33,7 +33,7 @@ class UnionDB extends CompositeDB implements UnionInternal { private int unionLength; private int unionAlignment; // reflects stored alignment, -1 if not yet stored - private int computedAlignment = -1; // cached alignment if not yet stored + private int computedAlignment = -1; // lazy, cached alignment, -1 if not yet computed private List components; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java index 21bbe7ff53..740bb7d1cb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionDB.java @@ -120,33 +120,22 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public boolean isThunk() { - manager.lock.acquire(); - try { - checkIsValid(); - return thunkedFunction != null; - } - finally { - manager.lock.release(); - } + validate(manager.lock); + return thunkedFunction != null; } @Override public Function getThunkedFunction(boolean recursive) { - manager.lock.acquire(); - try { - checkIsValid(); - if (!recursive || thunkedFunction == null) { - return thunkedFunction; - } - FunctionDB endFunction = thunkedFunction; - while (endFunction.thunkedFunction != null) { - endFunction = endFunction.thunkedFunction; - } - return endFunction; + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (!recursive || localThunkFunc == null) { + return localThunkFunc; } - finally { - manager.lock.release(); + FunctionDB endFunction = localThunkFunc; + while ((localThunkFunc = endFunction.thunkedFunction) != null) { + endFunction = localThunkFunc; } + return endFunction; } @Override @@ -332,14 +321,8 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public Address getEntryPoint() { - manager.lock.acquire(); - try { - checkIsValid(); - return entryPoint; - } - finally { - manager.lock.release(); - } + validate(manager.lock); + return entryPoint; } @Override @@ -378,12 +361,18 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public ReturnParameterDB getReturn() { + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.getReturn(); + } + ReturnParameterDB rp = returnParam; + if (rp != null) { + return rp; + } manager.lock.acquire(); try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.getReturn(); - } + // NOTE: lock required to avoid returning null returnParam loadVariables(); return returnParam; } @@ -634,32 +623,22 @@ public class FunctionDB extends DatabaseObject implements Function { */ @Override public StackFrame getStackFrame() { - manager.lock.acquire(); - try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.frame; - } - return frame; - } - finally { - manager.lock.release(); + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return thunkedFunction.getStackFrame(); } + return frame; } @Override public int getStackPurgeSize() { - manager.lock.acquire(); - try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.getStackPurgeSize(); - } - return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL); - } - finally { - manager.lock.release(); + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.getStackPurgeSize(); } + return rec.getIntValue(FunctionAdapter.STACK_PURGE_COL); } @Override @@ -693,20 +672,12 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public boolean isStackPurgeSizeValid() { - manager.lock.acquire(); - try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.isStackPurgeSizeValid(); - } - if (getStackPurgeSize() > 0xffffff) { - return false; - } - return true; - } - finally { - manager.lock.release(); + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.isStackPurgeSizeValid(); } + return getStackPurgeSize() <= 0xffffff; } @Override @@ -2417,18 +2388,13 @@ public class FunctionDB extends DatabaseObject implements Function { * @return true if the indicated flag is set */ private boolean isFunctionFlagSet(byte functionFlagIndicator) { - manager.lock.acquire(); - try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.isFunctionFlagSet(functionFlagIndicator); - } - byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL); - return ((flags & functionFlagIndicator) != 0); - } - finally { - manager.lock.release(); + validate(manager.lock); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.isFunctionFlagSet(functionFlagIndicator); } + byte flags = rec.getByteValue(FunctionAdapter.FUNCTION_FLAGS_COL); + return ((flags & functionFlagIndicator) != 0); } /** @@ -2529,29 +2495,20 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public String getCallingConventionName() { - manager.lock.acquire(); - try { - if (!checkIsValid()) { - return null; - } - if (thunkedFunction != null) { - return thunkedFunction.getCallingConventionName(); - } - byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); - if (callingConventionID == DataTypeManagerDB.UNKNOWN_CALLING_CONVENTION_ID) { - return Function.UNKNOWN_CALLING_CONVENTION_STRING; - } - if (callingConventionID == DataTypeManagerDB.DEFAULT_CALLING_CONVENTION_ID) { - return Function.DEFAULT_CALLING_CONVENTION_STRING; - } - return program.getDataTypeManager().getCallingConventionName(callingConventionID); + if (!validate(manager.lock)) { + return UNKNOWN_CALLING_CONVENTION_STRING; } - finally { - manager.lock.release(); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.getCallingConventionName(); } + byte callingConventionID = rec.getByteValue(FunctionAdapter.CALLING_CONVENTION_ID_COL); + // NOTE: If ID is invalid unknown calling convention name will be returned + return program.getDataTypeManager().getCallingConventionName(callingConventionID); } private String getRealCallingConventionName() { + // NOTE: Method only invoked from locked-block if (thunkedFunction != null) { return thunkedFunction.getRealCallingConventionName(); } @@ -2651,21 +2608,18 @@ public class FunctionDB extends DatabaseObject implements Function { @Override public String getCallFixup() { - manager.lock.acquire(); - try { - checkIsValid(); - if (thunkedFunction != null) { - return thunkedFunction.getCallFixup(); - } - StringPropertyMap callFixupMap = manager.getCallFixupMap(false); - if (callFixupMap == null) { - return null; - } - return callFixupMap.getString(entryPoint); + if (!validate(manager.lock)) { + return null; } - finally { - manager.lock.release(); + FunctionDB localThunkFunc = thunkedFunction; + if (localThunkFunc != null) { + return localThunkFunc.getCallFixup(); } + StringPropertyMap callFixupMap = manager.getCallFixupMap(false); + if (callFixupMap == null) { + return null; + } + return callFixupMap.getString(entryPoint); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java index 8e18df055d..ee1ef8ffae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/function/FunctionManagerDB.java @@ -81,12 +81,6 @@ public class FunctionManagerDB implements FunctionManager { return false; }; - /** - * TODO: use of StringPropertyMap for callFixupMap lacks the ability to listen for changes - * which may be made to the map directly (e.g., diff/merge) - */ - private StringPropertyMap callFixupMap; - private long lastFuncID = -1; Lock lock; @@ -825,7 +819,6 @@ public class FunctionManagerDB implements FunctionManager { lock.acquire(); try { functionTagManager.invalidateCache(); - callFixupMap = null; lastFuncID = -1; cache.invalidate(); } @@ -835,11 +828,8 @@ public class FunctionManagerDB implements FunctionManager { } StringPropertyMap getCallFixupMap(boolean create) { - if (callFixupMap != null) { - return callFixupMap; - } PropertyMapManager usrPropertyManager = program.getUsrPropertyManager(); - callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP); + StringPropertyMap callFixupMap = usrPropertyManager.getStringPropertyMap(CALLFIXUP_MAP); if (callFixupMap == null && create) { try { callFixupMap = usrPropertyManager.createStringPropertyMap(CALLFIXUP_MAP); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java index 80e8552abb..dce0a9cc31 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/DBPropertyMapManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,8 +19,8 @@ package ghidra.program.database.properties; import java.io.IOException; -import java.util.Iterator; -import java.util.TreeMap; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import db.*; import ghidra.framework.data.OpenMode; @@ -49,7 +49,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { private AddressMap addrMap; private ChangeManager changeMgr; private PropertiesDBAdapter propertiesDBAdapter; - private TreeMap> propertyMapCache; + private ConcurrentHashMap> propertyMapCache; private Lock lock; static final int CURRENT_PROPERTIES_TABLE_VERSION = 0; @@ -70,11 +70,9 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { static final Schema PROPERTIES_SCHEMA; static { - PROPERTIES_SCHEMA = new Schema(CURRENT_PROPERTIES_TABLE_VERSION, StringField.INSTANCE, "Name", new Field[] { ByteField.INSTANCE, StringField.INSTANCE, IntField.INSTANCE }, new String[] { "Type", "Object Class", "Version" }); - } /** @@ -100,7 +98,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { dbHandle.createTable(PROPERTIES_TABLE_NAME, PROPERTIES_SCHEMA); } findAdapters(handle); - propertyMapCache = new TreeMap>(); + propertyMapCache = new ConcurrentHashMap>(); loadPropertyMaps(openMode, monitor); } @@ -119,7 +117,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { public void invalidateCache(boolean all) throws IOException { lock.acquire(); try { - propertyMapCache.clear(); + for (PropertyMapDB map : propertyMapCache.values()) { + map.invalidate(); + } + // NOTE: Reconciling maps immediately allows use to reliably use cache without + // requiring map re-validation. loadPropertyMaps(null, TaskMonitor.DUMMY); } catch (CancelledException e) { @@ -144,12 +146,23 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { throws VersionException, CancelledException { try { VersionException ve = null; + Set oldMapNames = new HashSet<>(propertyMapCache.keySet()); RecordIterator iter = propertiesDBAdapter.getRecords(); while (iter.hasNext()) { DBRecord rec = iter.next(); - String name = rec.getKeyField().getString(); byte propertyType = rec.getByteValue(PROPERTY_TYPE_COL); - PropertyMapDB pm = null; + String name = rec.getKeyField().getString(); + oldMapNames.remove(name); + + // Check for pre-existing map + PropertyMapDB pm = propertyMapCache.get(name); + if (pm != null) { + if (pm.validate(lock)) { + continue; // keep the map we already have + } + propertyMapCache.remove(name); + } + try { switch (propertyType) { case INT_PROPERTY_TYPE: @@ -208,6 +221,12 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { } propertyMapCache.put(name, pm); } + + // Remove obsolete maps from cache + for (String obsoleteMapName : oldMapNames) { + propertyMapCache.remove(obsoleteMapName); + } + if (ve != null) { throw ve; } @@ -333,7 +352,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { finally { lock.release(); } - } @Override @@ -367,7 +385,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { finally { lock.release(); } - } /** @@ -406,7 +423,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { finally { lock.release(); } - } /** @@ -416,14 +432,7 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public PropertyMap getPropertyMap(String propertyName) { - lock.acquire(); - try { - return propertyMapCache.get(propertyName); - } - finally { - lock.release(); - } - + return propertyMapCache.get(propertyName); } /** @@ -434,19 +443,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public IntPropertyMap getIntPropertyMap(String propertyName) { - - lock.acquire(); - try { - PropertyMapDB pm = propertyMapCache.get(propertyName); - if (pm == null || pm instanceof IntPropertyMap) { - return (IntPropertyMap) pm; - } - throw new TypeMismatchException("Property " + propertyName + " is not int type"); + PropertyMapDB pm = propertyMapCache.get(propertyName); + if (pm == null || pm instanceof IntPropertyMap) { + return (IntPropertyMap) pm; } - finally { - lock.release(); - } - + throw new TypeMismatchException("Property " + propertyName + " is not int type"); } /** @@ -457,18 +458,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public LongPropertyMap getLongPropertyMap(String propertyName) { - lock.acquire(); - try { - PropertyMapDB pm = propertyMapCache.get(propertyName); - if (pm == null || pm instanceof LongPropertyMap) { - return (LongPropertyMap) pm; - } - throw new TypeMismatchException("Property " + propertyName + " is not long type"); + PropertyMapDB pm = propertyMapCache.get(propertyName); + if (pm == null || pm instanceof LongPropertyMap) { + return (LongPropertyMap) pm; } - finally { - lock.release(); - } - + throw new TypeMismatchException("Property " + propertyName + " is not long type"); } /** @@ -479,18 +473,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public StringPropertyMap getStringPropertyMap(String propertyName) { - lock.acquire(); - try { - PropertyMapDB pm = propertyMapCache.get(propertyName); - if (pm == null || pm instanceof StringPropertyMap) { - return (StringPropertyMap) pm; - } - throw new TypeMismatchException("Property " + propertyName + " is not String type"); - - } - finally { - lock.release(); + PropertyMapDB pm = propertyMapCache.get(propertyName); + if (pm == null || pm instanceof StringPropertyMap) { + return (StringPropertyMap) pm; } + throw new TypeMismatchException("Property " + propertyName + " is not String type"); } /** @@ -501,18 +488,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public ObjectPropertyMap getObjectPropertyMap(String propertyName) { - lock.acquire(); - try { - PropertyMapDB pm = propertyMapCache.get(propertyName); - if (pm == null || pm instanceof ObjectPropertyMap) { - return (ObjectPropertyMap) pm; - } - throw new TypeMismatchException("Property " + propertyName + " is not object type"); - - } - finally { - lock.release(); + PropertyMapDB pm = propertyMapCache.get(propertyName); + if (pm == null || pm instanceof ObjectPropertyMap) { + return (ObjectPropertyMap) pm; } + throw new TypeMismatchException("Property " + propertyName + " is not object type"); } /** @@ -523,17 +503,11 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { */ @Override public VoidPropertyMap getVoidPropertyMap(String propertyName) { - lock.acquire(); - try { - PropertyMapDB pm = propertyMapCache.get(propertyName); - if (pm == null || pm instanceof VoidPropertyMap) { - return (VoidPropertyMap) pm; - } - throw new TypeMismatchException("Property " + propertyName + " is not Void type"); - } - finally { - lock.release(); + PropertyMapDB pm = propertyMapCache.get(propertyName); + if (pm == null || pm instanceof VoidPropertyMap) { + return (VoidPropertyMap) pm; } + throw new TypeMismatchException("Property " + propertyName + " is not Void type"); } @Override @@ -564,12 +538,15 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { public Iterator propertyManagers() { lock.acquire(); try { - return propertyMapCache.keySet().iterator(); + // NOTE: infrequent use expected + return propertyMapCache.keySet() + .stream() + .sorted() // Sort keys in natural order + .iterator(); } finally { lock.release(); } - } @Override @@ -579,7 +556,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { for (PropertyMapDB pm : propertyMapCache.values()) { pm.remove(addr); } - } finally { lock.release(); @@ -621,7 +597,6 @@ public class DBPropertyMapManager implements PropertyMapManager, ManagerDB { public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) throws CancelledException { removeAll(startAddr, endAddr, monitor); - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java index 7510e89cb6..9bf86e1e5a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/IntPropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.program.database.properties; import java.io.IOException; +import java.util.function.Function; import db.*; import db.util.ErrorHandler; @@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor; */ public class IntPropertyMapDB extends PropertyMapDB implements IntPropertyMap { + /** + * A single non-capturing lambda record reader function is used to avoid the possibility of + * multiple synthetic class instantiations. + */ + private Function valueReader = addrKey -> { + Table table = propertyTable; + DBRecord rec = null; + try { + if (table != null) { + rec = table.getRecord(addrKey); + } + } + catch (IOException e) { + errHandler.dbError(e); + } + return rec != null ? rec.getIntValue(PROPERTY_VALUE_COL) : null; + }; + /** * Construct a integer property map. * @param dbHandle database handle. @@ -58,27 +77,22 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPrope public void add(Address addr, int value) { lock.acquire(); try { + checkDeleted(); Integer oldValue = null; - - long key = addrMap.getKey(addr, true); - + long addrKey = addrMap.getKey(addr, true); if (propertyTable == null) { createTable(IntField.INSTANCE); } else { - oldValue = (Integer) cache.get(key); + oldValue = cache.get(addrKey); if (oldValue == null) { - DBRecord rec = propertyTable.getRecord(key); - if (rec != null) { - oldValue = rec.getIntValue(PROPERTY_VALUE_COL); - } + oldValue = valueReader.apply(addrKey); } } - DBRecord rec = schema.createRecord(key); - + DBRecord rec = schema.createRecord(addrKey); rec.setIntValue(PROPERTY_VALUE_COL, value); propertyTable.putRecord(rec); - cache.put(key, value); + cache.put(addrKey, value); changeMgr.setPropertyChanged(name, addr, oldValue, value); } @@ -92,44 +106,25 @@ public class IntPropertyMapDB extends PropertyMapDB implements IntPrope @Override public int getInt(Address addr) throws NoValueException { - if (propertyTable == null) { + Integer value = get(addr); + if (value == null) { throw NO_VALUE_EXCEPTION; } - - lock.acquire(); - try { - long key = addrMap.getKey(addr, false); - if (key == AddressMap.INVALID_ADDRESS_KEY) { - return 0; - } - Object obj = cache.get(key); - if (obj != null) { - return ((Integer) obj).intValue(); - } - - DBRecord rec = propertyTable.getRecord(key); - if (rec == null) { - throw NO_VALUE_EXCEPTION; - } - return rec.getIntValue(PROPERTY_VALUE_COL); - } - catch (IOException e) { - errHandler.dbError(e); - } - finally { - lock.release(); - } - return 0; + return value; } @Override public Integer get(Address addr) { - try { - return getInt(addr); - } - catch (NoValueException e) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } + long addrKey = addrMap.getKey(addr, false); + if (addrKey == AddressMap.INVALID_ADDRESS_KEY) { + return null; + } + return cache.computeIfAbsent(addrKey, valueReader); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java index d6e7a7f75d..50882cbff0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/LongPropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.program.database.properties; import java.io.IOException; +import java.util.function.Function; import db.*; import db.util.ErrorHandler; @@ -33,6 +34,24 @@ import ghidra.util.task.TaskMonitor; */ public class LongPropertyMapDB extends PropertyMapDB implements LongPropertyMap { + /** + * A single record reader function is used to avoid the use of a lambda form which could + * cause multiple synthetic class instantiations. + */ + private Function valueReader = addrKey -> { + Table table = propertyTable; + DBRecord rec = null; + try { + if (table != null) { + rec = table.getRecord(addrKey); + } + } + catch (IOException e) { + errHandler.dbError(e); + } + return rec != null ? rec.getLongValue(PROPERTY_VALUE_COL) : null; + }; + /** * Construct a long property map. * @param dbHandle database handle. @@ -56,26 +75,24 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongProper @Override public void add(Address addr, long value) { - Long oldValue = null; lock.acquire(); try { - long key = addrMap.getKey(addr, true); + checkDeleted(); + Long oldValue = null; + long addrKey = addrMap.getKey(addr, true); if (propertyTable == null) { createTable(LongField.INSTANCE); } else { - oldValue = (Long) cache.get(key); + oldValue = cache.get(addrKey); if (oldValue == null) { - DBRecord rec = propertyTable.getRecord(key); - if (rec != null) { - oldValue = rec.getLongValue(PROPERTY_VALUE_COL); - } + oldValue = valueReader.apply(addrKey); } } - DBRecord rec = schema.createRecord(key); + DBRecord rec = schema.createRecord(addrKey); rec.setLongValue(PROPERTY_VALUE_COL, value); propertyTable.putRecord(rec); - cache.put(key, value); + cache.put(addrKey, value); changeMgr.setPropertyChanged(name, addr, oldValue, value); } catch (IOException e) { @@ -89,44 +106,25 @@ public class LongPropertyMapDB extends PropertyMapDB implements LongProper @Override public long getLong(Address addr) throws NoValueException { - if (propertyTable == null) { + Long value = get(addr); + if (value == null) { throw NO_VALUE_EXCEPTION; } - - lock.acquire(); - try { - long key = addrMap.getKey(addr, false); - if (key == AddressMap.INVALID_ADDRESS_KEY) { - return 0; - } - Object obj = cache.get(key); - if (obj != null) { - return ((Long) obj).longValue(); - } - - DBRecord rec = propertyTable.getRecord(key); - if (rec == null) { - throw NO_VALUE_EXCEPTION; - } - return rec.getLongValue(PROPERTY_VALUE_COL); - } - catch (IOException e) { - errHandler.dbError(e); - } - finally { - lock.release(); - } - return 0; + return value; } @Override public Long get(Address addr) { - try { - return getLong(addr); - } - catch (NoValueException e) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } + long addrKey = addrMap.getKey(addr, false); + if (addrKey == AddressMap.INVALID_ADDRESS_KEY) { + return null; + } + return cache.computeIfAbsent(addrKey, valueReader); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java index 36f8e0500b..24bf94bbd9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/ObjectPropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.program.database.properties; import java.io.IOException; +import java.util.function.Function; import db.*; import db.util.ErrorHandler; @@ -42,6 +43,44 @@ public class ObjectPropertyMapDB extends PropertyMapDB private int saveableObjectVersion; private boolean supportsPrivate; + /** + * A single non-capturing lambda record reader function is used to avoid the possibility of + * multiple synthetic class instantiations. + */ + @SuppressWarnings("unchecked") + private Function valueReader = addrKey -> { + Table table = propertyTable; + DBRecord rec = null; + try { + if (table != null) { + rec = table.getRecord(addrKey); + } + if (rec == null) { + return null; + } + ObjectStorageAdapterDB objStorage = new ObjectStorageAdapterDB(rec); + if (saveableObjectClass == GenericSaveable.class) { + return (T) new GenericSaveable(rec, propertyTable.getSchema()); + } + T obj = saveableObjectClass.getDeclaredConstructor().newInstance(); + obj.restore(objStorage); + return obj; + } + catch (IOException e) { + errHandler.dbError(e); + } + catch (RuntimeException e) { + throw e; + } + catch (InstantiationException e) { + errHandler.dbError(new IOException("Could not instantiate " + e.getMessage())); + } + catch (Exception e) { + errHandler.dbError(new IOException(e.getMessage())); + } + return null; + }; + /** * Construct an Saveable object property map. * @param dbHandle database handle. @@ -256,11 +295,19 @@ public class ObjectPropertyMapDB extends PropertyMapDB public void add(Address addr, T value) { lock.acquire(); try { + checkDeleted(); if (!saveableObjectClass.isAssignableFrom(value.getClass())) { throw new IllegalArgumentException("value is not " + saveableObjectClass.getName()); } - long key = addrMap.getKey(addr, true); - T oldValue = get(addr); + long addrKey = addrMap.getKey(addr, true); + + T oldValue = null; + if (propertyTable != null) { + oldValue = cache.get(addrKey); + if (oldValue == null) { + oldValue = valueReader.apply(addrKey); + } + } String tableName = getTableName(); Schema s; @@ -271,7 +318,7 @@ public class ObjectPropertyMapDB extends PropertyMapDB s = objStorage.getSchema(value.getSchemaVersion()); checkSchema(s); createPropertyTable(tableName, s); - rec = schema.createRecord(key); + rec = schema.createRecord(addrKey); objStorage.save(rec); } else { // GenericSaveable @@ -281,11 +328,11 @@ public class ObjectPropertyMapDB extends PropertyMapDB checkSchema(s); createPropertyTable(tableName, s); rec = originalRec.copy(); - rec.setKey(key); + rec.setKey(addrKey); } propertyTable.putRecord(rec); - cache.put(key, value); + cache.put(addrKey, value); if (!isPrivate(value)) { changeMgr.setPropertyChanged(name, addr, oldValue, value); @@ -326,57 +373,18 @@ public class ObjectPropertyMapDB extends PropertyMapDB return saveableObjectClass; } - @SuppressWarnings("unchecked") @Override public T get(Address addr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } - - T obj = null; - - lock.acquire(); - try { - long key = addrMap.getKey(addr, false); - if (key == AddressMap.INVALID_ADDRESS_KEY) { - return null; - } - obj = (T) cache.get(key); - if (obj != null) { - return obj; - } - - DBRecord rec = propertyTable.getRecord(key); - if (rec == null) { - return null; - } - ObjectStorageAdapterDB objStorage = new ObjectStorageAdapterDB(rec); - if (saveableObjectClass == GenericSaveable.class) { - obj = (T) new GenericSaveable(rec, propertyTable.getSchema()); - } - else { - obj = saveableObjectClass.getDeclaredConstructor().newInstance(); - obj.restore(objStorage); - } + long addrKey = addrMap.getKey(addr, false); + if (addrKey == AddressMap.INVALID_ADDRESS_KEY) { + return null; } - catch (IOException e) { - errHandler.dbError(e); - } - catch (RuntimeException e) { - throw e; - } - catch (InstantiationException e) { - errHandler.dbError(new IOException("Could not instantiate " + e.getMessage())); - } - catch (Exception e) { - errHandler.dbError(new IOException(e.getMessage())); - - } - finally { - lock.release(); - } - - return obj; + return cache.computeIfAbsent(addrKey, valueReader); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java index 72075228d9..38f33ad473 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/PropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,6 +21,7 @@ import java.util.NoSuchElementException; import db.*; import db.util.ErrorHandler; import ghidra.framework.data.OpenMode; +import ghidra.program.database.DatabaseObject; import ghidra.program.database.map.*; import ghidra.program.database.util.DatabaseTableUtils; import ghidra.program.model.address.*; @@ -37,7 +38,7 @@ import ghidra.util.task.TaskMonitor; * The map is stored within a database table. * @param property value type */ -public abstract class PropertyMapDB implements PropertyMap { +public abstract class PropertyMapDB extends DatabaseObject implements PropertyMap { private static final String PROPERTY_TABLE_PREFIX = "Property Map - "; @@ -63,7 +64,7 @@ public abstract class PropertyMapDB implements PropertyMap { protected AddressMap addrMap; protected String name; - protected ObjectCache cache = new ObjectCache(DEFAULT_CACHE_SIZE); + protected ObjectCache cache = new ObjectCache<>(DEFAULT_CACHE_SIZE); protected Lock lock; /** @@ -76,6 +77,7 @@ public abstract class PropertyMapDB implements PropertyMap { */ PropertyMapDB(DBHandle dbHandle, ErrorHandler errHandler, ChangeManager changeMgr, AddressMap addrMap, String name) { + super(null, 0); // DatabaseObject cache is not used this.dbHandle = dbHandle; this.errHandler = errHandler; @@ -92,6 +94,12 @@ public abstract class PropertyMapDB implements PropertyMap { } } + // Expose validate method to DBPropertyMapManager + @Override + protected boolean validate(Lock lck) { + return super.validate(lck); + } + /** * Check if a table upgrade should be performed or a version error thrown. * @param openMode the mode that the program was openned in or null if instantiated during @@ -180,22 +188,24 @@ public abstract class PropertyMapDB implements PropertyMap { /** * Create the default propertyTable. - * This method may be called by add property methods if propertyTable - * is null. - * @param valueField property value field type + * This method may be called by add property methods if propertyTable is null. + * @param valueField property value field type (null corresponds to void map table) * @throws IOException if IO error occurs */ protected void createTable(Field valueField) throws IOException { + schema = getTableSchema(valueField); + propertyTable = dbHandle.createTable(getTableName(), schema); + } + + private static Schema getTableSchema(Field valueField) { if (valueField != null) { // Create default table schema with a value column and an long Address key Field[] fields = new Field[] { valueField }; - schema = new Schema(0, "Address", fields, SCHEMA_FIELD_NAMES); + return new Schema(0, "Address", fields, SCHEMA_FIELD_NAMES); } - else { - // Table contains only a long Address key - schema = new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES); - } - propertyTable = dbHandle.createTable(getTableName(), schema); + + // Table contains only a long Address key (i.e., void property map) + return new Schema(0, "Address", NO_SCHEMA_FIELDS, NO_SCHEMA_FIELD_NAMES); } @Override @@ -227,8 +237,10 @@ public abstract class PropertyMapDB implements PropertyMap { public void delete() throws IOException { lock.acquire(); try { + checkDeleted(); + setDeleted(); if (propertyTable != null) { - cache = null; + cache = new ObjectCache<>(DEFAULT_CACHE_SIZE); dbHandle.deleteTable(getTableName()); propertyTable = null; } @@ -240,12 +252,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public boolean intersects(Address startAddr, Address endAddr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return false; } try { AddressKeyIterator iter = - new AddressKeyIterator(propertyTable, addrMap, startAddr, endAddr, startAddr, true); + new AddressKeyIterator(table, addrMap, startAddr, endAddr, startAddr, true); return iter.hasNext(); } catch (IOException e) { @@ -256,12 +270,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public boolean intersects(AddressSetView set) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return false; } try { AddressKeyIterator iter = - new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true); + new AddressKeyIterator(table, addrMap, set, set.getMinAddress(), true); return iter.hasNext(); } catch (IOException e) { @@ -272,13 +288,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public boolean removeRange(Address startAddr, Address endAddr) { - if (propertyTable == null) { - return false; - } lock.acquire(); try { + checkDeleted(); + if (propertyTable == null) { + return false; + } if (AddressRecordDeleter.deleteRecords(propertyTable, addrMap, startAddr, endAddr)) { - cache = new ObjectCache(DEFAULT_CACHE_SIZE); + cache = new ObjectCache<>(DEFAULT_CACHE_SIZE); return true; } } @@ -293,15 +310,16 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public boolean remove(Address addr) { - if (propertyTable == null) { - return false; - } lock.acquire(); boolean result = false; try { - long key = addrMap.getKey(addr, false); - cache.remove(key); - result = propertyTable.deleteRecord(key); + checkDeleted(); + if (propertyTable == null) { + return false; + } + long addrKey = addrMap.getKey(addr, false); + cache.remove(addrKey); + result = propertyTable.deleteRecord(addrKey); changeMgr.setPropertyChanged(name, addr, null, null); } catch (IOException e) { @@ -315,33 +333,32 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public boolean hasProperty(Address addr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return false; } - lock.acquire(); - boolean result = false; try { - long key = addrMap.getKey(addr, false); - if (key != AddressMap.INVALID_ADDRESS_KEY) { - result = cache.contains(key) || propertyTable.hasRecord(key); + long addrKey = addrMap.getKey(addr, false); + if (addrKey != AddressMap.INVALID_ADDRESS_KEY) { + return cache.contains(addrKey) || table.hasRecord(addrKey); } } catch (IOException e) { errHandler.dbError(e); } - finally { - lock.release(); - } - return result; + return false; } @Override public Address getNextPropertyAddress(Address addr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } try { - AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, false); + AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, false); return addrMap.decodeAddress(iter.next()); } catch (NoSuchElementException e) { @@ -355,11 +372,13 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public Address getPreviousPropertyAddress(Address addr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } try { - AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, addr, true); + AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addr, true); return addrMap.decodeAddress(iter.previous()); } catch (NoSuchElementException e) { @@ -373,11 +392,13 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public Address getFirstPropertyAddress() { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } try { - AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, true); + AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, true); return addrMap.decodeAddress(iter.next()); } catch (NoSuchElementException e) { @@ -391,11 +412,13 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public Address getLastPropertyAddress() { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } try { - AddressKeyIterator iter = new AddressKeyIterator(propertyTable, addrMap, + AddressKeyIterator iter = new AddressKeyIterator(table, addrMap, addrMap.getAddressFactory().getAddressSet().getMaxAddress(), false); return addrMap.decodeAddress(iter.previous()); } @@ -410,7 +433,9 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public int getSize() { - return propertyTable != null ? propertyTable.getRecordCount() : 0; + validate(lock); + Table table = propertyTable; + return table != null ? table.getRecordCount() : 0; } /** @@ -423,13 +448,15 @@ public abstract class PropertyMapDB implements PropertyMap { */ public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart) throws IOException { - if (propertyTable == null || (set != null && set.isEmpty())) { + validate(lock); + Table table = propertyTable; + if (table == null || (set != null && set.isEmpty())) { return AddressKeyIterator.EMPTY_ITERATOR; } if (atStart) { - return new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true); + return new AddressKeyIterator(table, addrMap, set, set.getMinAddress(), true); } - return new AddressKeyIterator(propertyTable, addrMap, set, set.getMaxAddress(), false); + return new AddressKeyIterator(table, addrMap, set, set.getMaxAddress(), false); } /** @@ -441,11 +468,12 @@ public abstract class PropertyMapDB implements PropertyMap { */ public AddressKeyIterator getAddressKeyIterator(Address start, boolean before) throws IOException { - - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return AddressKeyIterator.EMPTY_ITERATOR; } - return new AddressKeyIterator(propertyTable, addrMap, start, before); + return new AddressKeyIterator(table, addrMap, start, before); } /** @@ -459,14 +487,15 @@ public abstract class PropertyMapDB implements PropertyMap { */ public AddressKeyIterator getAddressKeyIterator(Address start, Address end, boolean atStart) throws IOException { - - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return AddressKeyIterator.EMPTY_ITERATOR; } if (atStart) { - return new AddressKeyIterator(propertyTable, addrMap, start, end, start, true); + return new AddressKeyIterator(table, addrMap, start, end, start, true); } - return new AddressKeyIterator(propertyTable, addrMap, start, end, end, false); + return new AddressKeyIterator(table, addrMap, start, end, end, false); } @Override @@ -495,12 +524,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public AddressIterator getPropertyIterator() { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return new EmptyAddressIterator(); } AddressKeyIterator keyIter = null; try { - keyIter = new AddressKeyIterator(propertyTable, addrMap, true); + keyIter = new AddressKeyIterator(table, addrMap, true); } catch (IOException e) { errHandler.dbError(e); @@ -510,13 +541,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public AddressIterator getPropertyIterator(AddressSetView asv) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return new EmptyAddressIterator(); } AddressKeyIterator keyIter = null; try { - keyIter = - new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true); + keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true); } catch (IOException e) { errHandler.dbError(e); @@ -526,18 +558,18 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward) { - if (propertyTable == null || (asv != null && asv.isEmpty())) { + validate(lock); + Table table = propertyTable; + if (table == null || (asv != null && asv.isEmpty())) { return AddressIterator.EMPTY_ITERATOR; } AddressKeyIterator keyIter = null; try { if (forward) { - keyIter = - new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMinAddress(), true); + keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMinAddress(), true); } else { - keyIter = - new AddressKeyIterator(propertyTable, addrMap, asv, asv.getMaxAddress(), false); + keyIter = new AddressKeyIterator(table, addrMap, asv, asv.getMaxAddress(), false); } } catch (IOException e) { @@ -548,12 +580,14 @@ public abstract class PropertyMapDB implements PropertyMap { @Override public AddressIterator getPropertyIterator(Address start, boolean forward) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return new EmptyAddressIterator(); } AddressKeyIterator keyIter = null; try { - keyIter = new AddressKeyIterator(propertyTable, addrMap, start, forward); + keyIter = new AddressKeyIterator(table, addrMap, start, forward); } catch (IOException e) { errHandler.dbError(e); @@ -564,23 +598,36 @@ public abstract class PropertyMapDB implements PropertyMap { /** * Invalidates the cache. */ - public void invalidateCache() { + public void invalidate() { lock.acquire(); try { - propertyTable = dbHandle.getTable(getTableName()); - cache = new ObjectCache(DEFAULT_CACHE_SIZE); + setInvalid(); } finally { lock.release(); } + } + @Override + protected boolean refresh() { + cache = new ObjectCache<>(DEFAULT_CACHE_SIZE); + propertyTable = dbHandle.getTable(getTableName()); + if (propertyTable != null) { + if (!propertyTable.getSchema().equals(schema)) { + propertyTable = null; + return false; + } + } + // Must assume lazy table and empty map + return true; } @Override public void moveRange(Address start, Address end, Address newStart) { lock.acquire(); try { - cache = new ObjectCache(DEFAULT_CACHE_SIZE); + checkDeleted(); + cache = new ObjectCache<>(DEFAULT_CACHE_SIZE); if (propertyTable != null) { try { DatabaseTableUtils.updateAddressKey(propertyTable, addrMap, start, end, diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java index 5a83ce3976..1afa5a204f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/StringPropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,6 +16,7 @@ package ghidra.program.database.properties; import java.io.IOException; +import java.util.function.Function; import db.*; import db.util.ErrorHandler; @@ -34,6 +35,24 @@ import ghidra.util.task.TaskMonitor; */ public class StringPropertyMapDB extends PropertyMapDB implements StringPropertyMap { + /** + * A single non-capturing lambda record reader function is used to avoid the possibility of + * multiple synthetic class instantiations. + */ + private Function valueReader = addrKey -> { + Table table = propertyTable; + DBRecord rec = null; + try { + if (table != null) { + rec = table.getRecord(addrKey); + } + } + catch (IOException e) { + errHandler.dbError(e); + } + return rec != null ? rec.getString(PROPERTY_VALUE_COL) : null; + }; + /** * Construct an String property map. * @param dbHandle database handle. @@ -59,25 +78,24 @@ public class StringPropertyMapDB extends PropertyMapDB implements String public void add(Address addr, String value) { lock.acquire(); try { - long key = addrMap.getKey(addr, true); + checkDeleted(); + + long addrKey = addrMap.getKey(addr, true); String oldValue = null; if (propertyTable == null) { createTable(StringField.INSTANCE); } else { - oldValue = (String) cache.get(key); + oldValue = cache.get(addrKey); if (oldValue == null) { - DBRecord rec = propertyTable.getRecord(key); - if (rec != null) { - oldValue = rec.getString(PROPERTY_VALUE_COL); - } + oldValue = valueReader.apply(addrKey); } } - DBRecord rec = schema.createRecord(key); + DBRecord rec = schema.createRecord(addrKey); rec.setString(PROPERTY_VALUE_COL, value); propertyTable.putRecord(rec); - cache.put(key, value); + cache.put(addrKey, value); changeMgr.setPropertyChanged(name, addr, oldValue, value); } catch (IOException e) { @@ -91,38 +109,16 @@ public class StringPropertyMapDB extends PropertyMapDB implements String @Override public String getString(Address addr) { - if (propertyTable == null) { + validate(lock); + Table table = propertyTable; + if (table == null) { return null; } - - String str = null; - - lock.acquire(); - try { - long key = addrMap.getKey(addr, false); - if (key == AddressMap.INVALID_ADDRESS_KEY) { - return null; - } - str = (String) cache.get(key); - if (str != null) { - return str; - } - - DBRecord rec = propertyTable.getRecord(key); - if (rec == null) { - return null; - } - str = rec.getString(PROPERTY_VALUE_COL); + long addrKey = addrMap.getKey(addr, false); + if (addrKey == AddressMap.INVALID_ADDRESS_KEY) { + return null; } - catch (IOException e) { - errHandler.dbError(e); - - } - finally { - lock.release(); - } - - return str; + return cache.computeIfAbsent(addrKey, valueReader); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java index e1458ad43f..018745d470 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/properties/VoidPropertyMapDB.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -37,7 +37,7 @@ import ghidra.util.task.TaskMonitor; */ public class VoidPropertyMapDB extends PropertyMapDB implements VoidPropertyMap { - private static Object VOID_OBJECT = new Object(); + private static Boolean TRUE = true; /** * Construct an void object property map. @@ -64,15 +64,17 @@ public class VoidPropertyMapDB extends PropertyMapDB implements VoidPro public void add(Address addr) { lock.acquire(); try { - long key = addrMap.getKey(addr, true); + checkDeleted(); + + long addrKey = addrMap.getKey(addr, true); Boolean oldValue = hasProperty(addr); if (propertyTable == null) { createTable(null); } - DBRecord rec = schema.createRecord(key); + DBRecord rec = schema.createRecord(addrKey); propertyTable.putRecord(rec); - cache.put(key, VOID_OBJECT); + cache.put(addrKey, TRUE); changeMgr.setPropertyChanged(name, addr, oldValue, true); } catch (IOException e) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java index b004567d5e..7fe8f1d451 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GenericAddress.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -56,19 +56,11 @@ public class GenericAddress implements Address { this.addrSpace = addrSpace; } - /** - * - * @see ghidra.program.model.address.Address#getAddress(java.lang.String) - */ @Override public Address getAddress(String addrString) throws AddressFormatException { return addrSpace.getAddress(addrString); } - /** - * - * @see ghidra.program.model.address.Address#getNewAddress(long) - */ @Override public Address getNewAddress(long byteOffset) { return addrSpace.getAddress(byteOffset); @@ -86,10 +78,6 @@ public class GenericAddress implements Address { return addrSpace.getTruncatedAddress(addrOffset, isAddressableWordOffset); } - /** - * - * @see ghidra.program.model.address.Address#getOffset() - */ @Override public long getOffset() { return offset; @@ -100,9 +88,6 @@ public class GenericAddress implements Address { return addrSpace.getAddressableWordOffset(offset); } - /** - * @see ghidra.program.model.address.Address#getUnsignedOffset() - */ @Override public long getUnsignedOffset() { // TODO: Validity of offset within space is not verified @@ -117,37 +102,21 @@ public class GenericAddress implements Address { return spaceSize + offset; } - /** - * - * @see ghidra.program.model.address.Address#getAddressSpace() - */ @Override public AddressSpace getAddressSpace() { return addrSpace; } - /** - * - * @see ghidra.program.model.address.Address#getSize() - */ @Override public int getSize() { return addrSpace.getSize(); } - /** - * - * @see ghidra.program.model.address.Address#subtract(ghidra.program.model.address.Address) - */ @Override public long subtract(Address addr) { return addrSpace.subtract(this, addr); } - /** - * - * @see ghidra.program.model.address.Address#subtractWrap(long) - */ @Override public Address subtractWrap(long displacement) { if (displacement == 0) @@ -155,9 +124,6 @@ public class GenericAddress implements Address { return addrSpace.subtractWrap(this, displacement); } - /** - * @see ghidra.program.model.address.Address#subtractWrapSpace(long) - */ @Override public Address subtractWrapSpace(long displacement) { if (displacement == 0) @@ -165,10 +131,6 @@ public class GenericAddress implements Address { return addrSpace.subtractWrapSpace(this, displacement); } - /** - * - * @see ghidra.program.model.address.Address#subtractNoWrap(long) - */ @Override public Address subtractNoWrap(long displacement) throws AddressOverflowException { if (displacement == 0) @@ -176,10 +138,6 @@ public class GenericAddress implements Address { return addrSpace.subtractNoWrap(this, displacement); } - /** - * - * @see ghidra.program.model.address.Address#subtract(long) - */ @Override public Address subtract(long displacement) { if (displacement == 0) @@ -187,9 +145,6 @@ public class GenericAddress implements Address { return addrSpace.subtract(this, displacement); } - /** - * @see ghidra.program.model.address.Address#addWrap(long) - */ @Override public Address addWrap(long displacement) { if (displacement == 0) @@ -197,9 +152,6 @@ public class GenericAddress implements Address { return addrSpace.addWrap(this, displacement); } - /** - * @see ghidra.program.model.address.Address#addWrapSpace(long) - */ @Override public Address addWrapSpace(long displacement) { if (displacement == 0) @@ -207,10 +159,6 @@ public class GenericAddress implements Address { return addrSpace.addWrapSpace(this, displacement); } - /** - * - * @see ghidra.program.model.address.Address#addNoWrap(long) - */ @Override public Address addNoWrap(long displacement) throws AddressOverflowException { if (displacement == 0) @@ -226,10 +174,6 @@ public class GenericAddress implements Address { return addrSpace.addNoWrap(this, displacement); } - /** - * - * @see ghidra.program.model.address.Address#add(long) - */ @Override public Address add(long displacement) { if (displacement == 0) @@ -237,10 +181,6 @@ public class GenericAddress implements Address { return addrSpace.add(this, displacement); } - /** - * - * @see ghidra.program.model.address.Address#isSuccessor(ghidra.program.model.address.Address) - */ @Override public boolean isSuccessor(Address addr) { return addrSpace.isSuccessor(this, addr); @@ -259,26 +199,17 @@ public class GenericAddress implements Address { return Long.compareUnsigned(offset, otherOffset); } - /** - * - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof GenericAddress)) { + if (!(o instanceof GenericAddress addr)) { return false; } - GenericAddress addr = (GenericAddress) o; - return addrSpace.equals(addr.getAddressSpace()) && offset == addr.offset; + return offset == addr.offset && addrSpace.equals(addr.getAddressSpace()); } - /** - * - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { int hash1 = addrSpace.hashCode(); @@ -286,18 +217,11 @@ public class GenericAddress implements Address { return (hash1 << 16) ^ hash3; } - /** - * @see java.lang.Object#toString() - */ @Override public String toString() { return toString(addrSpace.showSpaceName(), MINIMUM_DIGITS); } - /** - * - * @see ghidra.program.model.address.Address#toString(java.lang.String) - */ @Override public String toString(String prefix) { boolean showSpace = prefix.length() == 0 && addrSpace.showSpaceName(); @@ -368,19 +292,11 @@ public class GenericAddress implements Address { return buf.toString(); } - /** - * - * @see ghidra.program.model.address.Address#hasSameAddressSpace(ghidra.program.model.address.Address) - */ @Override public boolean hasSameAddressSpace(Address addr) { return addrSpace.equals(addr.getAddressSpace()); } - /** - * - * @see ghidra.program.model.address.Address#next() - */ @Override public Address next() { if (addrSpace.getMaxAddress().getOffset() == offset) { @@ -389,9 +305,6 @@ public class GenericAddress implements Address { return addrSpace.addWrap(this, 1); } - /** - * @see ghidra.program.model.address.Address#previous() - */ @Override public Address previous() { if (addrSpace.getMinAddress().getOffset() == offset) { @@ -400,9 +313,6 @@ public class GenericAddress implements Address { return addrSpace.subtractWrap(this, 1); } - /** - * @see ghidra.program.model.address.Address#getPhysicalAddress() - */ @Override public Address getPhysicalAddress() { AddressSpace physical = addrSpace.getPhysicalSpace(); @@ -415,17 +325,11 @@ public class GenericAddress implements Address { return null; } - /** - * @see ghidra.program.model.address.Address#getPointerSize() - */ @Override public int getPointerSize() { return addrSpace.getPointerSize(); } - /** - * @see ghidra.program.model.address.Address#isMemoryAddress() - */ @Override public boolean isMemoryAddress() { return addrSpace.isMemorySpace(); @@ -441,57 +345,36 @@ public class GenericAddress implements Address { return addrSpace.isNonLoadedMemorySpace(); } - /** - * @see ghidra.program.model.address.Address#isHashAddress() - */ @Override public boolean isHashAddress() { return addrSpace.isHashSpace(); } - /** - * @see ghidra.program.model.address.Address#isStackAddress() - */ @Override public boolean isStackAddress() { return addrSpace.isStackSpace(); } - /** - * @see ghidra.program.model.address.Address#isUniqueAddress() - */ @Override public boolean isUniqueAddress() { return addrSpace.isUniqueSpace(); } - /** - * @see ghidra.program.model.address.Address#isConstantAddress() - */ @Override public boolean isConstantAddress() { return addrSpace.isConstantSpace(); } - /** - * @see ghidra.program.model.address.Address#isVariableAddress() - */ @Override public boolean isVariableAddress() { return addrSpace.isVariableSpace(); } - /** - * @see ghidra.program.model.address.Address#isRegisterAddress() - */ @Override public boolean isRegisterAddress() { return addrSpace.isRegisterSpace(); } - /** - * @see ghidra.program.model.address.Address#isExternalAddress() - */ @Override public boolean isExternalAddress() { return addrSpace.isExternalSpace(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java index 7684bdbccf..62ca480e3a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/util/PropertyMapManager.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -141,7 +141,7 @@ public interface PropertyMapManager { public boolean removePropertyMap(String propertyName); /** - * Returns an iterator over the names of all existing PropertyMaps. + * Returns an iterator over the names of all existing PropertyMaps sorted by name. */ public Iterator propertyManagers();